From df4926d1797e02541662eeab9060625452704abf Mon Sep 17 00:00:00 2001 From: Stroopwafe1 <48443491+Stroopwafe1@users.noreply.github.com> Date: Fri, 27 Jun 2025 12:09:44 +0200 Subject: [PATCH 0001/1733] Feat: Add script to install leantime --- ct/leantime.sh | 79 ++++++++++++++++++++ frontend/public/json/leantime.json | 35 +++++++++ install/leantime-install.sh | 113 +++++++++++++++++++++++++++++ 3 files changed, 227 insertions(+) create mode 100644 ct/leantime.sh create mode 100644 frontend/public/json/leantime.json create mode 100644 install/leantime-install.sh diff --git a/ct/leantime.sh b/ct/leantime.sh new file mode 100644 index 000000000..b084ef8ad --- /dev/null +++ b/ct/leantime.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Stroopwafe1 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://leantime.io + +# App Default Values +# Name of the app (e.g. Google, Adventurelog, Apache-Guacamole" +APP="Leantime" +# Tags for Proxmox VE, maximum 2 pcs., no spaces allowed, separated by a semicolon ; (e.g. database | adblock;dhcp) +var_tags="${var_tags:-productivity}" +# Number of cores (1-X) (e.g. 4) - default are 2 +var_cpu="${var_cpu:-2}" +# Amount of used RAM in MB (e.g. 2048 or 4096) +var_ram="${var_ram:-2048}" +# Amount of used disk space in GB (e.g. 4 or 10) +var_disk="${var_disk:-20}" +# Default OS (e.g. debian, ubuntu, alpine) +var_os="${var_os:-debian}" +# Default OS version (e.g. 12 for debian, 24.04 for ubuntu, 3.20 for alpine) +var_version="${var_version:-12}" +# 1 = unprivileged container, 0 = privileged container +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + # Check if installation is present | -f for file, -d for folder + if [[ ! -d /opt/${APP} ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + # Crawling the new version and checking whether an update is required + RELEASE=$(curl -fsSL https://api.github.com/repos/Leantime/leantime/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then + # Creating Backup + msg_info "Creating Backup" + mariadb-dump leantime >"/opt/${APP}_db_backup_$(date +%F).sql" + tar -czf "/opt/${APP}_backup_$(date +%F).tar.gz" /opt/leantime + msg_ok "Backup Created" + + # Execute Update + msg_info "Updating $APP to v${RELEASE}" + curl -fsSL -o "${RELEASE}.tar.gz" "https://github.com/Leantime/leantime/archive/refs/tags/${RELEASE}.tar.gz" + tar xf "${RELEASE}.tar.gz" --strip-components=1 -C "/opt/${APP}" + msg_ok "Updated $APP to v${RELEASE}" + + # Cleaning up + msg_info "Cleaning Up" + rm -rf "${RELEASE}.tar.gz" + msg_ok "Cleanup Completed" + + # Last Action + echo "${RELEASE}" >/opt/${APP}_version.txt + msg_ok "Update Successful" + else + msg_ok "No update required. ${APP} is already at v${RELEASE}" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}/install${CL}" diff --git a/frontend/public/json/leantime.json b/frontend/public/json/leantime.json new file mode 100644 index 000000000..b45947970 --- /dev/null +++ b/frontend/public/json/leantime.json @@ -0,0 +1,35 @@ +{ + "name": "Leantime", + "slug": "leantime", + "categories": [ + 12 + ], + "date_created": "2025-06-27", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 80, + "documentation": "https://docs.leantime.io/", + "config_path": "/opt/Leantime/config/.env", + "website": "https://leantime.io", + "logo": "https://assets.leantime.io/wp-content/uploads/2019/02/iconOnly-700.png", + "description": "Leantime is a goals focused project management system for non-project managers. Building with ADHD, Autism, and dyslexia in mind. ", + "install_methods": [ + { + "type": "default", + "script": "ct/leantime.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 20, + "os": "Debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} diff --git a/install/leantime-install.sh b/install/leantime-install.sh new file mode 100644 index 000000000..c7d60c7c3 --- /dev/null +++ b/install/leantime-install.sh @@ -0,0 +1,113 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Stroopwafe1 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://leantime.io + +# Import Functions und Setup +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +PHP_VERSION=8.4 +PHP_MODULE=mysql +PHP_APACHE=YES +PHP_FPM=YES + +msg_info "Installing Apache2" +$STD apt-get install -y \ + apache2 +msg_ok "Installed Apache2" + +setup_php + +msg_info "Installing Apache2 mod for PHP" +$STD apt-get install -y \ + "libapache2-mod-php${PHP_VERSION}" +msg_ok "Installed Apache2 mod" + +setup_mariadb + +msg_ok "Installed Dependencies" + +# Template: MySQL Database +msg_info "Setting up Database" +systemctl enable -q --now mariadb +DB_NAME=leantime +DB_USER=leantime +DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) +$STD mysql -u root -e "CREATE DATABASE $DB_NAME;" +$STD mysql -u root -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED WITH mysql_native_password AS PASSWORD('$DB_PASS');" +$STD mysql -u root -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" +{ + echo "${APPLICATION} Credentials" + echo "Database User: $DB_USER" + echo "Database Password: $DB_PASS" + echo "Database Name: $DB_NAME" +} >>~/"$APPLICATION".creds +msg_ok "Set up Database" + +# Setup App +msg_info "Setup ${APPLICATION}" +APACHE_LOG_DIR=/var/log/apache2 +RELEASE=$(curl -fsSL https://api.github.com/repos/Leantime/leantime/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') +curl -fsSL -o "${RELEASE}.tar.gz" "https://github.com/Leantime/leantime/releases/download/${RELEASE}/Leantime-${RELEASE}.tar.gz" +mkdir -p "/opt/${APPLICATION}" +mkdir -p /etc/apache2/sites-enabled +tar xf "${RELEASE}.tar.gz" --strip-components=1 -C "/opt/${APPLICATION}" +chown -R www-data:www-data "/opt/${APPLICATION}" +chmod -R 750 "/opt/${APPLICATION}" + +cat </etc/apache2/sites-enabled/000-default.conf + + ServerAdmin webmaster@localhost + DocumentRoot /opt/${APPLICATION}/public + DirectoryIndex index.php index.html index.cgi index.pl index.xhtml + Options +ExecCGI + + + Options FollowSymLinks + Require all granted + AllowOverride All + + + + Require all granted + + + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + +EOF + +mv "/opt/${APPLICATION}/config/sample.env" "/opt/${APPLICATION}/config/.env" +sed -i -e "s|^LEAN_DB_DATABASE.*|LEAN_DB_DATABASE = '$DB_NAME'|" \ + -e "s|^LEAN_DB_USER.*|LEAN_DB_USER = '$DB_USER'|" \ + -e "s|^LEAN_DB_PASSWORD.*|LEAN_DB_PASSWORD = '$DB_PASS'|" \ + -e "s|^LEAN_SESSION_PASSWORD.*|LEAN_SESSION_PASSWORD = '$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13)'|" \ + "/opt/${APPLICATION}/config/.env" + +a2enmod -q proxy_fcgi setenvif rewrite +a2enconf -q "php${PHP_VERSION}-fpm" + +sed -i -e "s/^;extension.\(curl\|fileinfo\|gd\|intl\|ldap\|mbstring\|exif\|mysqli\|odbc\|openssl\|pdo_mysql\)/extension=\1/g" "/etc/php/${PHP_VERSION}/apache2/php.ini" + +systemctl restart apache2 + +echo "${RELEASE}" >/opt/"${APPLICATION}"_version.txt +msg_ok "Setup ${APPLICATION}" + +motd_ssh +customize + +# Cleanup +msg_info "Cleaning up" +rm -f "${RELEASE}".tar.gz +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 9f01464234db7db6123202b4d6bdbd72be6b9c24 Mon Sep 17 00:00:00 2001 From: Stroopwafe1 <48443491+Stroopwafe1@users.noreply.github.com> Date: Fri, 27 Jun 2025 18:25:15 +0200 Subject: [PATCH 0002/1733] Chore: Implement feedback --- ct/leantime.sh | 42 +++++------------------------- frontend/public/json/leantime.json | 2 +- install/leantime-install.sh | 10 +------ 3 files changed, 8 insertions(+), 46 deletions(-) diff --git a/ct/leantime.sh b/ct/leantime.sh index b084ef8ad..cc58a7656 100644 --- a/ct/leantime.sh +++ b/ct/leantime.sh @@ -1,27 +1,18 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: Stroopwafe1 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://leantime.io -# App Default Values -# Name of the app (e.g. Google, Adventurelog, Apache-Guacamole" APP="Leantime" -# Tags for Proxmox VE, maximum 2 pcs., no spaces allowed, separated by a semicolon ; (e.g. database | adblock;dhcp) var_tags="${var_tags:-productivity}" -# Number of cores (1-X) (e.g. 4) - default are 2 var_cpu="${var_cpu:-2}" -# Amount of used RAM in MB (e.g. 2048 or 4096) var_ram="${var_ram:-2048}" -# Amount of used disk space in GB (e.g. 4 or 10) var_disk="${var_disk:-20}" -# Default OS (e.g. debian, ubuntu, alpine) var_os="${var_os:-debian}" -# Default OS version (e.g. 12 for debian, 24.04 for ubuntu, 3.20 for alpine) var_version="${var_version:-12}" -# 1 = unprivileged container, 0 = privileged container var_unprivileged="${var_unprivileged:-1}" header_info "$APP" @@ -40,32 +31,11 @@ function update_script() { exit fi - # Crawling the new version and checking whether an update is required - RELEASE=$(curl -fsSL https://api.github.com/repos/Leantime/leantime/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then - # Creating Backup - msg_info "Creating Backup" - mariadb-dump leantime >"/opt/${APP}_db_backup_$(date +%F).sql" - tar -czf "/opt/${APP}_backup_$(date +%F).tar.gz" /opt/leantime - msg_ok "Backup Created" - - # Execute Update - msg_info "Updating $APP to v${RELEASE}" - curl -fsSL -o "${RELEASE}.tar.gz" "https://github.com/Leantime/leantime/archive/refs/tags/${RELEASE}.tar.gz" - tar xf "${RELEASE}.tar.gz" --strip-components=1 -C "/opt/${APP}" - msg_ok "Updated $APP to v${RELEASE}" - - # Cleaning up - msg_info "Cleaning Up" - rm -rf "${RELEASE}.tar.gz" - msg_ok "Cleanup Completed" - - # Last Action - echo "${RELEASE}" >/opt/${APP}_version.txt - msg_ok "Update Successful" - else - msg_ok "No update required. ${APP} is already at v${RELEASE}" - fi + msg_info "Creating Backup" + mariadb-dump leantime >"/opt/${APP}_db_backup_$(date +%F).sql" + tar -czf "/opt/${APP}_backup_$(date +%F).tar.gz" "/opt/${APP}" + msg_ok "Backup Created" + fetch_and_deploy_gh_release "$APP" "Leantime/leantime" "prebuild" "latest" "/opt/${APP}" Leantime-v[0-9].[0-9].[0-9].tar.gz exit } diff --git a/frontend/public/json/leantime.json b/frontend/public/json/leantime.json index b45947970..e5e0fec81 100644 --- a/frontend/public/json/leantime.json +++ b/frontend/public/json/leantime.json @@ -12,7 +12,7 @@ "documentation": "https://docs.leantime.io/", "config_path": "/opt/Leantime/config/.env", "website": "https://leantime.io", - "logo": "https://assets.leantime.io/wp-content/uploads/2019/02/iconOnly-700.png", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/leantime.webp", "description": "Leantime is a goals focused project management system for non-project managers. Building with ADHD, Autism, and dyslexia in mind. ", "install_methods": [ { diff --git a/install/leantime-install.sh b/install/leantime-install.sh index c7d60c7c3..475a464aa 100644 --- a/install/leantime-install.sh +++ b/install/leantime-install.sh @@ -5,7 +5,6 @@ # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://leantime.io -# Import Functions und Setup source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 @@ -35,7 +34,6 @@ setup_mariadb msg_ok "Installed Dependencies" -# Template: MySQL Database msg_info "Setting up Database" systemctl enable -q --now mariadb DB_NAME=leantime @@ -55,11 +53,7 @@ msg_ok "Set up Database" # Setup App msg_info "Setup ${APPLICATION}" APACHE_LOG_DIR=/var/log/apache2 -RELEASE=$(curl -fsSL https://api.github.com/repos/Leantime/leantime/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') -curl -fsSL -o "${RELEASE}.tar.gz" "https://github.com/Leantime/leantime/releases/download/${RELEASE}/Leantime-${RELEASE}.tar.gz" -mkdir -p "/opt/${APPLICATION}" -mkdir -p /etc/apache2/sites-enabled -tar xf "${RELEASE}.tar.gz" --strip-components=1 -C "/opt/${APPLICATION}" +fetch_and_deploy_gh_release "$APPLICATION" "Leantime/leantime" "prebuild" "latest" "/opt/${APPLICATION}" Leantime-v[0-9].[0-9].[0-9].tar.gz chown -R www-data:www-data "/opt/${APPLICATION}" chmod -R 750 "/opt/${APPLICATION}" @@ -99,7 +93,6 @@ sed -i -e "s/^;extension.\(curl\|fileinfo\|gd\|intl\|ldap\|mbstring\|exif\|mysql systemctl restart apache2 -echo "${RELEASE}" >/opt/"${APPLICATION}"_version.txt msg_ok "Setup ${APPLICATION}" motd_ssh @@ -107,7 +100,6 @@ customize # Cleanup msg_info "Cleaning up" -rm -f "${RELEASE}".tar.gz $STD apt-get -y autoremove $STD apt-get -y autoclean msg_ok "Cleaned" From dc0e63e7810b3854285d526b92f6f0a437087485 Mon Sep 17 00:00:00 2001 From: Stroopwafe1 <48443491+Stroopwafe1@users.noreply.github.com> Date: Fri, 27 Jun 2025 20:42:22 +0200 Subject: [PATCH 0003/1733] Chore: Remove remaining comments --- install/leantime-install.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/install/leantime-install.sh b/install/leantime-install.sh index 475a464aa..cd577289c 100644 --- a/install/leantime-install.sh +++ b/install/leantime-install.sh @@ -50,7 +50,6 @@ $STD mysql -u root -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH } >>~/"$APPLICATION".creds msg_ok "Set up Database" -# Setup App msg_info "Setup ${APPLICATION}" APACHE_LOG_DIR=/var/log/apache2 fetch_and_deploy_gh_release "$APPLICATION" "Leantime/leantime" "prebuild" "latest" "/opt/${APPLICATION}" Leantime-v[0-9].[0-9].[0-9].tar.gz @@ -98,7 +97,6 @@ msg_ok "Setup ${APPLICATION}" motd_ssh customize -# Cleanup msg_info "Cleaning up" $STD apt-get -y autoremove $STD apt-get -y autoclean From 9989b15dda0871d0514e890df2a66c2586e13b7f Mon Sep 17 00:00:00 2001 From: Stroopwafe1 <48443491+Stroopwafe1@users.noreply.github.com> Date: Fri, 27 Jun 2025 20:45:56 +0200 Subject: [PATCH 0004/1733] Chore: Remove comment --- ct/leantime.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/ct/leantime.sh b/ct/leantime.sh index cc58a7656..f49c503a2 100644 --- a/ct/leantime.sh +++ b/ct/leantime.sh @@ -25,7 +25,6 @@ function update_script() { check_container_storage check_container_resources - # Check if installation is present | -f for file, -d for folder if [[ ! -d /opt/${APP} ]]; then msg_error "No ${APP} Installation Found!" exit From 3428070a0963b775575201f28c3ce98849748974 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20B=C3=A9dard-Couture?= Date: Sat, 28 Jun 2025 12:40:57 -0400 Subject: [PATCH 0005/1733] Adds script to update LXC services from the host, allowing to automatically increase resources if needed to build the service --- misc/build.func | 4 + tools/pve/update-lxcs-services.sh | 184 ++++++++++++++++++++++++++++++ 2 files changed, 188 insertions(+) create mode 100644 tools/pve/update-lxcs-services.sh diff --git a/misc/build.func b/misc/build.func index b75e5b38a..136b3de59 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1096,6 +1096,10 @@ start() { source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) if command -v pveversion >/dev/null 2>&1; then install_script + elif [ ! -z ${PHS_SILENT+x} ] && [[ "${PHS_SILENT}" == "1" ]]; then + VERBOSE="no" + set_std_mode + update_script else CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "${APP} LXC Update/Setting" --menu \ "Support/Update functions for ${APP} LXC. Choose an option:" \ diff --git a/tools/pve/update-lxcs-services.sh b/tools/pve/update-lxcs-services.sh new file mode 100644 index 000000000..e5bcca049 --- /dev/null +++ b/tools/pve/update-lxcs-services.sh @@ -0,0 +1,184 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) | Co-Author: remz1337 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE + +function header_info() { + clear + cat <<"EOF" + __ __ __ __ __ _ ________ _____ _ + / / / /___ ____/ /___ _/ /____ / / | |/ / ____/ / ___/___ ______ __(_)_______ _____ + / / / / __ \/ __ / __ `/ __/ _ \ / / | / / \__ \/ _ \/ ___/ | / / / ___/ _ \/ ___/ +/ /_/ / /_/ / /_/ / /_/ / /_/ __/ / /___/ / /___ ___/ / __/ / | |/ / / /__/ __(__ ) +\____/ .___/\__,_/\__,_/\__/\___/ /_____/_/|_\____/ /____/\___/_/ |___/_/\___/\___/____/ + /_/ + +EOF +} +set -eEuo pipefail +YW=$(echo "\033[33m") +BL=$(echo "\033[36m") +RD=$(echo "\033[01;31m") +CM='\xE2\x9C\x94\033' +GN=$(echo "\033[1;92m") +CL=$(echo "\033[m") +header_info +echo "Loading..." +whiptail --backtitle "Proxmox VE Helper Scripts" --title "Proxmox VE LXC Service Updater" --yesno "This Will Update LXC Services. Proceed?" 10 58 +NODE=$(hostname) +EXCLUDE_MENU=() +MSG_MAX_LENGTH=0 +while read -r TAG ITEM; do + OFFSET=2 + ((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=${#ITEM}+OFFSET + EXCLUDE_MENU+=("$TAG" "$ITEM " "OFF") +done < <(pct list | awk 'NR>1') +excluded_containers=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Containers on $NODE" --checklist "\nSelect containers to skip from updates:\n" 16 $((MSG_MAX_LENGTH + 23)) 6 "${EXCLUDE_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"') + +function needs_reboot() { + local container=$1 + local os=$(pct config "$container" | awk '/^ostype/ {print $2}') + local reboot_required_file="/var/run/reboot-required.pkgs" + if [ -f "$reboot_required_file" ]; then + if [[ "$os" == "ubuntu" || "$os" == "debian" ]]; then + if pct exec "$container" -- [ -s "$reboot_required_file" ]; then + return 0 + fi + fi + fi + return 1 +} + +function update_container_service() { + container=$1 + header_info + name=$(pct exec "$container" hostname) + os=$(pct config "$container" | awk '/^ostype/ {print $2}') + if [[ "$os" == "ubuntu" || "$os" == "debian" || "$os" == "fedora" ]]; then + disk_info=$(pct exec "$container" df /boot | awk 'NR==2{gsub("%","",$5); printf "%s %.1fG %.1fG %.1fG", $5, $3/1024/1024, $2/1024/1024, $4/1024/1024 }') + read -ra disk_info_array <<<"$disk_info" + echo -e "${BL}[Info]${GN} Updating ${BL}$container${CL} : ${GN}$name${CL} - ${YW}Boot Disk: ${disk_info_array[0]}% full [${disk_info_array[1]}/${disk_info_array[2]} used, ${disk_info_array[3]} free]${CL}\n" + else + echo -e "${BL}[Info]${GN} Updating ${BL}$container${CL} : ${GN}$name${CL} - ${YW}[No disk info for ${os}]${CL}\n" + fi + + #1) Detect service using the service name in the update command + pushd $(mktemp -d) >/dev/null + pct pull "$container" /usr/bin/update update 2>/dev/null + service=$(cat update | sed 's|.*/ct/||g' | sed 's|\.sh).*||g') + popd >/dev/null + + #1.1) If update script not detected, return + if [ -z "${service}" ]; then + echo -e "${YW}[WARN]${CL} Update script not found\n" + return + else + echo "${BL}[INFO]${CL} Detected service: ${GN}${service}${CL}\n" + fi + + #2) Extract service build/update resource requirements from config/installation file + script=$(curl -fsSL https://raw.githubusercontent.com/remz1337/ProxmoxVE/remz/ct/${service}.sh) + config=$(pct config "$container") + build_cpu=$(echo "$script" | { grep -m 1 "var_cpu" || test $? = 1; } | sed 's|.*=||g' | sed 's|"||g' | sed 's|.*var_cpu:-||g' | sed 's|}||g') + build_ram=$(echo "$script" | { grep -m 1 "var_ram" || test $? = 1; } | sed 's|.*=||g' | sed 's|"||g' | sed 's|.*var_ram:-||g' | sed 's|}||g') + run_cpu=$(echo "$script" | { grep -m 1 "pct set \$CTID -cores" || test $? = 1; } | sed 's|.*cores ||g') + run_ram=$(echo "$script" | { grep -m 1 "pct set \$CTID -memory" || test $? = 1; } | sed 's|.*memory ||g') + current_cpu=$(echo "$config" | grep -m 1 "cores:" | sed 's|cores: ||g') + current_ram=$(echo "$config" | grep -m 1 "memory:" | sed 's|memory: ||g') + + #Test if all values are valid (>0) + if [ -z "${run_cpu}" ] || [ "$run_cpu" -le 0 ]; then + #echo "No valid value found for run_cpu. Assuming same as current configuration." + run_cpu=$current_cpu + fi + + if [ -z "${run_ram}" ] || [ "$run_ram" -le 0 ]; then + #echo "No valid value found for run_ram. Assuming same as current configuration." + run_ram=$current_ram + fi + + if [ -z "${build_cpu}" ] || [ "$build_cpu" -le 0 ]; then + #echo "No valid value found for build_cpu. Assuming same as current configuration." + build_cpu=$current_cpu + fi + + if [ -z "${build_ram}" ] || [ "$build_ram" -le 0 ]; then + #echo "No valid value found for build_ram. Assuming same as current configuration." + build_ram=$current_ram + fi + + UPDATE_BUILD_RESOURCES=0 + if [ "$build_cpu" -gt "$run_cpu" ] || [ "$build_ram" -gt "$run_ram" ]; then + UPDATE_BUILD_RESOURCES=1 + fi + + #3) if build resources are different than run resources, then: + if [ "$UPDATE_BUILD_RESOURCES" -eq "1" ]; then + #pct shutdown "$container" + #sleep 2 + pct set "$container" --cores "$build_cpu" --memory "$build_ram" + pct restart "$container" + sleep 2 + fi + + #4) Update service, using the update command + UPDATE_CMD="export PHS_SILENT=1;update;" + case "$os" in + alpine) pct exec "$container" -- ash -c "$UPDATE_CMD" ;; + archlinux) pct exec "$container" -- bash -c "$UPDATE_CMD" ;; + fedora | rocky | centos | alma) pct exec "$container" -- bash -c "$UPDATE_CMD" ;; + ubuntu | debian | devuan) pct exec "$container" -- bash -c "$UPDATE_CMD" ;; + opensuse) pct exec "$container" -- bash -c "$UPDATE_CMD" ;; + esac + + #5) if build resources are different than run resources, then: + if [ "$UPDATE_BUILD_RESOURCES" -eq "1" ]; then + #pct shutdown "$container" + #sleep 2 + pct set "$container" --cores "$run_cpu" --memory "$run_ram" + #pct restart "$container" + #sleep 2 + fi +} + +containers_needing_reboot=() +header_info +for container in $(pct list | awk '{if(NR>1) print $1}'); do + if [[ " ${excluded_containers[@]} " =~ " $container " ]]; then + header_info + echo -e "${BL}[Info]${GN} Skipping ${BL}$container${CL}" + sleep 1 + else + status=$(pct status $container) + template=$(pct config $container | grep -q "template:" && echo "true" || echo "false") + if [ "$template" == "false" ] && [ "$status" == "status: stopped" ]; then + echo -e "${BL}[Info]${GN} Starting${BL} $container ${CL} \n" + pct start $container + echo -e "${BL}[Info]${GN} Waiting For${BL} $container${CL}${GN} To Start ${CL} \n" + sleep 5 + #update_container $container + update_container_service $container + echo -e "${BL}[Info]${GN} Shutting down${BL} $container ${CL} \n" + pct shutdown $container & + elif [ "$status" == "status: running" ]; then + #update_container $container + update_container_service $container + fi + if pct exec "$container" -- [ -e "/var/run/reboot-required" ]; then + # Get the container's hostname and add it to the list + container_hostname=$(pct exec "$container" hostname) + containers_needing_reboot+=("$container ($container_hostname)") + fi + fi +done +wait +header_info +echo -e "${GN}The process is complete, and the containers have been successfully updated.${CL}\n" +if [ "${#containers_needing_reboot[@]}" -gt 0 ]; then + echo -e "${RD}The following containers require a reboot:${CL}" + for container_name in "${containers_needing_reboot[@]}"; do + echo "$container_name" + done +fi +echo "" From 98973810d774dfa13f92d81ab3b42a52f3f84663 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20B=C3=A9dard-Couture?= Date: Sat, 28 Jun 2025 14:33:33 -0400 Subject: [PATCH 0006/1733] Add back keycloak following changes to latest keycloak release --- ct/keycloak.sh | 72 +++++++++++++++++++++++++++++ install/keycloak-install.sh | 90 +++++++++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+) create mode 100644 ct/keycloak.sh create mode 100644 install/keycloak-install.sh diff --git a/ct/keycloak.sh b/ct/keycloak.sh new file mode 100644 index 000000000..349fadb78 --- /dev/null +++ b/ct/keycloak.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) | Co-Author: remz1337 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://www.keycloak.org/ + +APP="Keycloak" +var_tags="${var_tags:-access-management}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" +var_postfix_sat="${var_postfix_sat:-yes}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -f /etc/systemd/system/keycloak.service ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + msg_info "Stopping ${APP}" + systemctl stop keycloak + msg_ok "Stopped ${APP}" + + msg_info "Updating packages" + apt-get update &>/dev/null + apt-get -y upgrade &>/dev/null + msg_ok "Updated packages" + + RELEASE=$(curl -fsSL https://api.github.com/repos/keycloak/keycloak/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + msg_info "Updating ${APP} to v$RELEASE" + cd /opt + wget -q https://github.com/keycloak/keycloak/releases/download/$RELEASE/keycloak-$RELEASE.tar.gz + mv keycloak keycloak.old + tar -xzf keycloak-$RELEASE.tar.gz + tar -czf keycloak_conf_backup.tar.gz keycloak.old/conf + mv keycloak_conf_backup.tar.gz keycloak-$RELEASE/conf + cp -r keycloak.old/providers keycloak-$RELEASE + cp -r keycloak.old/themes keycloak-$RELEASE + mv keycloak-$RELEASE keycloak + rm keycloak-$RELEASE.tar.gz + rm -rf keycloak.old + msg_ok "Updated ${APP} LXC" + + msg_info "Restating Keycloak" + systemctl restart keycloak + msg_ok "Restated Keycloak" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080/admin${CL}" +echo -e "${TAB}${GN}Temporary admin user:${BL}tmpadm${CL}" +echo -e "${TAB}${GN}Temporary admin password:${BL}admin123${CL}" +echo -e "${INFO}${YW} If you modified configurations files in `conf/`: Re-apply your changes to those files, otherwise leave them unchanged.${CL}" diff --git a/install/keycloak-install.sh b/install/keycloak-install.sh new file mode 100644 index 000000000..a0bf44bbd --- /dev/null +++ b/install/keycloak-install.sh @@ -0,0 +1,90 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: tteck (tteckster) | Co-Authors: Slaviša Arežina (tremor021), remz1337 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/keycloak/keycloak + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies (Patience)" +$STD apt-get install -y curl +$STD apt-get install -y sudo +$STD apt-get install -y mc +$STD apt-get install -y ca-certificates-java +msg_ok "Installed Dependencies" + +msg_info "Installing OpenJDK" +$STD apt install wget lsb-release -y +$STD wget https://packages.microsoft.com/config/debian/$(lsb_release -rs)/packages-microsoft-prod.deb -O packages-microsoft-prod.deb +$STD dpkg -i packages-microsoft-prod.deb +$STD apt update +$STD apt install -y msopenjdk-21 +sudo update-java-alternatives --set msopenjdk-21-amd64 +rm packages-microsoft-prod.deb +msg_ok "Installed OpenJDK" + +msg_info "Installing PostgreSQL" +$STD apt-get install -y postgresql +DB_NAME="keycloak" +DB_USER="keycloak" +DB_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" +$STD sudo -u postgres psql -c "CREATE USER $DB_USER WITH PASSWORD '$DB_PASS';" +$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8';" +$STD sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER;" +msg_ok "Installed PostgreSQL" + +msg_info "Installing Keycloak" +temp_file=$(mktemp) +RELEASE=$(curl -fsSL https://api.github.com/repos/keycloak/keycloak/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') +curl -fsSL "https://github.com/keycloak/keycloak/releases/download/$RELEASE/keycloak-$RELEASE.tar.gz" -o "$temp_file" +tar xzf $temp_file +mv keycloak-$RELEASE /opt/keycloak +msg_ok "Installed Keycloak" + +msg_info "Creating Service" +cat </etc/systemd/system/keycloak.service +[Unit] +Description=Keycloak Service +Requires=network.target +After=syslog.target network-online.target + +[Service] +Type=idle +User=root +WorkingDirectory=/opt/keycloak +ExecStart=/opt/keycloak/bin/kc.sh start +ExecStop=/opt/keycloak/bin/kc.sh stop +Restart=always +RestartSec=3 +Environment="JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64" +Environment="KC_DB=postgres" +Environment="KC_DB_USERNAME=$DB_USER" +Environment="KC_DB_PASSWORD=$DB_PASS" +Environment="KC_HTTP_ENABLED=true" +Environment="KC_BOOTSTRAP_ADMIN_USERNAME=tmpadm" +Environment="KC_BOOTSTRAP_ADMIN_PASSWORD=admin123" +# Comment following line and uncomment the next 2 if working behind a reverse proxy +Environment="KC_HOSTNAME_STRICT=false" +#Environment="KC_HOSTNAME=keycloak.example.com" +#Environment="KC_PROXY_HEADERS=xforwarded" +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now keycloak +msg_ok "Created Service" + +motd_ssh +customize + +msg_info "Cleaning up" +rm -f $temp_file +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 5ab98dedd942ab7239f5c6cc92c0cc5127e4fe86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20B=C3=A9dard-Couture?= Date: Wed, 2 Jul 2025 17:10:49 -0400 Subject: [PATCH 0007/1733] (WIP) Support updating multiple containers --- tools/pve/update-apps.sh | 88 ++++++++++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 30 deletions(-) diff --git a/tools/pve/update-apps.sh b/tools/pve/update-apps.sh index e7712e4ed..98e5e8ab0 100644 --- a/tools/pve/update-apps.sh +++ b/tools/pve/update-apps.sh @@ -1,9 +1,11 @@ #!/usr/bin/env bash # Copyright (c) 2021-2025 community-scripts ORG -# Author: BvdBerg01 +# Author: BvdBerg01 | Co-Author: remz1337 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + function header_info { clear cat <<"EOF" @@ -16,8 +18,6 @@ function header_info { EOF } -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) - header_info echo "Loading..." whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC Container Update" --yesno "This will update LXC container. Proceed?" 10 58 || exit @@ -45,8 +45,8 @@ while read -r container; do done <<< "$containers" CHOICE=$(whiptail --title "LXC Container Update" \ - --radiolist "Select LXC container to update:" 25 60 13 \ - "${menu_items[@]}" 3>&2 2>&1 1>&3) + --checklist "Select LXC containers to update:" 25 60 13 \ + "${menu_items[@]}" 3>&2 2>&1 1>&3 | tr -d '"') if [ -z "$CHOICE" ]; then whiptail --title "LXC Container Update" \ @@ -55,12 +55,21 @@ if [ -z "$CHOICE" ]; then fi header_info -if(whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC Container Update" --yesno "Do you want to create a backup from your container?" 10 58); then +BACKUP_CHOICE="no" +if(whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC Container Update" --yesno "Do you want to backup your containers before update?" 10 58); then + BACKUP_CHOICE="yes" +fi +UNATTENDED_UPDATE="no" +if(whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC Container Update" --yesno "Run updates unattended?" 10 58); then + UNATTENDED_UPDATE="yes" +fi + +if [ "$BACKUP_CHOICE" == "yes" ]; then STORAGES=$(awk '/^(\S+):/ {storage=$2} /content.*backup/ {print storage}' /etc/pve/storage.cfg) if [ -z "$STORAGES" ]; then - whiptail --msgbox "Geen opslag met 'backup' gevonden!" 8 40 + whiptail --msgbox "No storage with 'backup' found!" 8 40 exit 1 fi @@ -75,37 +84,56 @@ if(whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC Container Updat msg_error "No storage selected!" exit 1 fi +fi - msg_info "Creating backup" - vzdump $CHOICE --compress zstd --storage $STORAGE_CHOICE -notes-template "community-scripts backup updater" > /dev/null 2>&1 +function backup_container(){ + msg_info "Creating backup for container $1" + vzdump $1 --compress zstd --storage $STORAGE_CHOICE -notes-template "community-scripts backup updater" > /dev/null 2>&1 status=$? if [ $status -eq 0 ]; then - msg_ok "Backup created" - pct exec $CHOICE -- update --from-pve - exit_code=$? + msg_ok "Backup created" else - msg_error "Backup failed" + msg_error "Backup failed for container $1" + exit 1 fi +} -else - pct exec $CHOICE -- update --from-pve - exit_code=$? +UPDATE_CMD="update;" +if [ "$UNATTENDED_UPDATE" == "yes" ];then + UPDATE_CMD="export PHS_SILENT=1;update;" fi -if [ $exit_code -eq 0 ]; then - msg_ok "Update completed" -else - msg_info "Restoring LXC from backup" - pct stop $CHOICE - LXC_STORAGE=$(pct config $CHOICE | awk -F '[:,]' '/rootfs/ {print $2}') - pct restore $CHOICE /var/lib/vz/dump/vzdump-lxc-$CHOICE-*.tar.zst --storage $LXC_STORAGE --force > /dev/null 2>&1 - pct start $CHOICE - restorestatus=$? - if [ $restorestatus -eq 0 ]; then - msg_ok "Restored LXC from backup" - else - msg_error "Restored LXC from backup failed" +for container in $CHOICE; do + echo "Updating container:$container" + + if [ "BACKUP_CHOICE" == "yes" ];then + backup_container $container fi -fi + #CHECK FOR RESOURCES + + #pct exec $container -- update + pct exec "$container" -- "$UPDATE_CMD" + exit_code=$? + + if [ $exit_code -eq 0 ]; then + msg_ok "Update completed" + elif [ "BACKUP_CHOICE" == "yes" ];then + msg_info "Restoring LXC from backup" + pct stop $container + LXC_STORAGE=$(pct config $container | awk -F '[:,]' '/rootfs/ {print $2}') + pct restore $container /var/lib/vz/dump/vzdump-lxc-${container}-*.tar.zst --storage $LXC_STORAGE --force > /dev/null 2>&1 + pct start $container + restorestatus=$? + if [ $restorestatus -eq 0 ]; then + msg_ok "Restored LXC from backup" + else + msg_error "Restored LXC from backup failed" + exit 1 + fi + else + msg_error "Update failed for container $container. Exiting" + exit 1 + fi +done From f586a21e1e263bcd0e30f7a1aeb0f4678de39f2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20B=C3=A9dard-Couture?= Date: Wed, 2 Jul 2025 21:17:49 -0400 Subject: [PATCH 0008/1733] Add logic for unattended updates and reboot containers if needed (following user prompt) --- tools/pve/update-apps.sh | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/tools/pve/update-apps.sh b/tools/pve/update-apps.sh index 98e5e8ab0..43a17bfc4 100644 --- a/tools/pve/update-apps.sh +++ b/tools/pve/update-apps.sh @@ -104,6 +104,7 @@ if [ "$UNATTENDED_UPDATE" == "yes" ];then UPDATE_CMD="export PHS_SILENT=1;update;" fi +containers_needing_reboot=() for container in $CHOICE; do echo "Updating container:$container" @@ -113,10 +114,24 @@ for container in $CHOICE; do #CHECK FOR RESOURCES - #pct exec $container -- update - pct exec "$container" -- "$UPDATE_CMD" + os=$(pct config "$container" | awk '/^ostype/ {print $2}') + + #4) Update service, using the update command + case "$os" in + alpine) pct exec "$container" -- ash -c "$UPDATE_CMD" ;; + archlinux) pct exec "$container" -- bash -c "$UPDATE_CMD" ;; + fedora | rocky | centos | alma) pct exec "$container" -- bash -c "$UPDATE_CMD" ;; + ubuntu | debian | devuan) pct exec "$container" -- bash -c "$UPDATE_CMD" ;; + opensuse) pct exec "$container" -- bash -c "$UPDATE_CMD" ;; + esac exit_code=$? + if pct exec "$container" -- [ -e "/var/run/reboot-required" ]; then + # Get the container's hostname and add it to the list + container_hostname=$(pct exec "$container" hostname) + containers_needing_reboot+=("$container ($container_hostname)") + fi + if [ $exit_code -eq 0 ]; then msg_ok "Update completed" elif [ "BACKUP_CHOICE" == "yes" ];then @@ -137,3 +152,22 @@ for container in $CHOICE; do exit 1 fi done + +wait +header_info +echo -e "${GN}The process is complete, and the containers have been successfully updated.${CL}\n" +if [ "${#containers_needing_reboot[@]}" -gt 0 ]; then + echo -e "${RD}The following containers require a reboot:${CL}" + for container_name in "${containers_needing_reboot[@]}"; do + echo "$container_name" + done + echo -ne "${INFO} Do you wish to reboot these containers? " + read -r prompt + if [[ ${prompt,,} =~ ^(yes)$ ]]; then + echo -e "${CROSS}${HOLD} ${YWB}Rebooting containers.${CL}" + for container_name in "${containers_needing_reboot[@]}"; do + container=$(echo $container_name | cut -d " " -f 1) + pct reboot ${container} + done + fi +fi From b62b0ccc9b19900a8fb59bd221a237903b3ff290 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20B=C3=A9dard-Couture?= Date: Wed, 2 Jul 2025 21:25:41 -0400 Subject: [PATCH 0009/1733] Adds resources scaling logic --- tools/pve/update-apps.sh | 60 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/tools/pve/update-apps.sh b/tools/pve/update-apps.sh index 43a17bfc4..b086d2996 100644 --- a/tools/pve/update-apps.sh +++ b/tools/pve/update-apps.sh @@ -112,7 +112,60 @@ for container in $CHOICE; do backup_container $container fi - #CHECK FOR RESOURCES + #1) Detect service using the service name in the update command + pushd $(mktemp -d) >/dev/null + pct pull "$container" /usr/bin/update update 2>/dev/null + service=$(cat update | sed 's|.*/ct/||g' | sed 's|\.sh).*||g') + popd >/dev/null + + #1.1) If update script not detected, return + if [ -z "${service}" ]; then + echo -e "${YW}[WARN]${CL} Update script not found. Skipping to next container\n" + continue + else + echo "${BL}[INFO]${CL} Detected service: ${GN}${service}${CL}\n" + fi + + #2) Extract service build/update resource requirements from config/installation file + script=$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/${service}.sh) + config=$(pct config "$container") + build_cpu=$(echo "$script" | { grep -m 1 "var_cpu" || test $? = 1; } | sed 's|.*=||g' | sed 's|"||g' | sed 's|.*var_cpu:-||g' | sed 's|}||g') + build_ram=$(echo "$script" | { grep -m 1 "var_ram" || test $? = 1; } | sed 's|.*=||g' | sed 's|"||g' | sed 's|.*var_ram:-||g' | sed 's|}||g') + run_cpu=$(echo "$script" | { grep -m 1 "pct set \$CTID -cores" || test $? = 1; } | sed 's|.*cores ||g') + run_ram=$(echo "$script" | { grep -m 1 "pct set \$CTID -memory" || test $? = 1; } | sed 's|.*memory ||g') + current_cpu=$(echo "$config" | grep -m 1 "cores:" | sed 's|cores: ||g') + current_ram=$(echo "$config" | grep -m 1 "memory:" | sed 's|memory: ||g') + + #Test if all values are valid (>0) + if [ -z "${run_cpu}" ] || [ "$run_cpu" -le 0 ]; then + #echo "No valid value found for run_cpu. Assuming same as current configuration." + run_cpu=$current_cpu + fi + + if [ -z "${run_ram}" ] || [ "$run_ram" -le 0 ]; then + #echo "No valid value found for run_ram. Assuming same as current configuration." + run_ram=$current_ram + fi + + if [ -z "${build_cpu}" ] || [ "$build_cpu" -le 0 ]; then + #echo "No valid value found for build_cpu. Assuming same as current configuration." + build_cpu=$current_cpu + fi + + if [ -z "${build_ram}" ] || [ "$build_ram" -le 0 ]; then + #echo "No valid value found for build_ram. Assuming same as current configuration." + build_ram=$current_ram + fi + + UPDATE_BUILD_RESOURCES=0 + if [ "$build_cpu" -gt "$run_cpu" ] || [ "$build_ram" -gt "$run_ram" ]; then + UPDATE_BUILD_RESOURCES=1 + fi + + #3) if build resources are different than run resources, then: + if [ "$UPDATE_BUILD_RESOURCES" -eq "1" ]; then + pct set "$container" --cores "$build_cpu" --memory "$build_ram" + fi os=$(pct config "$container" | awk '/^ostype/ {print $2}') @@ -126,6 +179,11 @@ for container in $CHOICE; do esac exit_code=$? + #5) if build resources are different than run resources, then: + if [ "$UPDATE_BUILD_RESOURCES" -eq "1" ]; then + pct set "$container" --cores "$run_cpu" --memory "$run_ram" + fi + if pct exec "$container" -- [ -e "/var/run/reboot-required" ]; then # Get the container's hostname and add it to the list container_hostname=$(pct exec "$container" hostname) From 6c507a0f9064983e574d8ad6a4f859ac69bee0e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20B=C3=A9dard-Couture?= Date: Wed, 2 Jul 2025 21:34:49 -0400 Subject: [PATCH 0010/1733] Remove old script and cleanup some messages --- tools/pve/update-apps.sh | 8 +- tools/pve/update-lxcs-services.sh | 184 ------------------------------ 2 files changed, 4 insertions(+), 188 deletions(-) delete mode 100644 tools/pve/update-lxcs-services.sh diff --git a/tools/pve/update-apps.sh b/tools/pve/update-apps.sh index b086d2996..0c1a979ff 100644 --- a/tools/pve/update-apps.sh +++ b/tools/pve/update-apps.sh @@ -106,7 +106,7 @@ fi containers_needing_reboot=() for container in $CHOICE; do - echo "Updating container:$container" + msg_info "Updating container $container" if [ "BACKUP_CHOICE" == "yes" ];then backup_container $container @@ -120,10 +120,10 @@ for container in $CHOICE; do #1.1) If update script not detected, return if [ -z "${service}" ]; then - echo -e "${YW}[WARN]${CL} Update script not found. Skipping to next container\n" + echo -e "${YW}[WARN]${CL} Update script not found. Skipping to next container" continue else - echo "${BL}[INFO]${CL} Detected service: ${GN}${service}${CL}\n" + echo -e "${BL}[INFO]${CL} Detected service: ${GN}${service}${CL}" fi #2) Extract service build/update resource requirements from config/installation file @@ -191,7 +191,7 @@ for container in $CHOICE; do fi if [ $exit_code -eq 0 ]; then - msg_ok "Update completed" + msg_ok "Updated container $container" elif [ "BACKUP_CHOICE" == "yes" ];then msg_info "Restoring LXC from backup" pct stop $container diff --git a/tools/pve/update-lxcs-services.sh b/tools/pve/update-lxcs-services.sh deleted file mode 100644 index e5bcca049..000000000 --- a/tools/pve/update-lxcs-services.sh +++ /dev/null @@ -1,184 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) | Co-Author: remz1337 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE - -function header_info() { - clear - cat <<"EOF" - __ __ __ __ __ _ ________ _____ _ - / / / /___ ____/ /___ _/ /____ / / | |/ / ____/ / ___/___ ______ __(_)_______ _____ - / / / / __ \/ __ / __ `/ __/ _ \ / / | / / \__ \/ _ \/ ___/ | / / / ___/ _ \/ ___/ -/ /_/ / /_/ / /_/ / /_/ / /_/ __/ / /___/ / /___ ___/ / __/ / | |/ / / /__/ __(__ ) -\____/ .___/\__,_/\__,_/\__/\___/ /_____/_/|_\____/ /____/\___/_/ |___/_/\___/\___/____/ - /_/ - -EOF -} -set -eEuo pipefail -YW=$(echo "\033[33m") -BL=$(echo "\033[36m") -RD=$(echo "\033[01;31m") -CM='\xE2\x9C\x94\033' -GN=$(echo "\033[1;92m") -CL=$(echo "\033[m") -header_info -echo "Loading..." -whiptail --backtitle "Proxmox VE Helper Scripts" --title "Proxmox VE LXC Service Updater" --yesno "This Will Update LXC Services. Proceed?" 10 58 -NODE=$(hostname) -EXCLUDE_MENU=() -MSG_MAX_LENGTH=0 -while read -r TAG ITEM; do - OFFSET=2 - ((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=${#ITEM}+OFFSET - EXCLUDE_MENU+=("$TAG" "$ITEM " "OFF") -done < <(pct list | awk 'NR>1') -excluded_containers=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Containers on $NODE" --checklist "\nSelect containers to skip from updates:\n" 16 $((MSG_MAX_LENGTH + 23)) 6 "${EXCLUDE_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"') - -function needs_reboot() { - local container=$1 - local os=$(pct config "$container" | awk '/^ostype/ {print $2}') - local reboot_required_file="/var/run/reboot-required.pkgs" - if [ -f "$reboot_required_file" ]; then - if [[ "$os" == "ubuntu" || "$os" == "debian" ]]; then - if pct exec "$container" -- [ -s "$reboot_required_file" ]; then - return 0 - fi - fi - fi - return 1 -} - -function update_container_service() { - container=$1 - header_info - name=$(pct exec "$container" hostname) - os=$(pct config "$container" | awk '/^ostype/ {print $2}') - if [[ "$os" == "ubuntu" || "$os" == "debian" || "$os" == "fedora" ]]; then - disk_info=$(pct exec "$container" df /boot | awk 'NR==2{gsub("%","",$5); printf "%s %.1fG %.1fG %.1fG", $5, $3/1024/1024, $2/1024/1024, $4/1024/1024 }') - read -ra disk_info_array <<<"$disk_info" - echo -e "${BL}[Info]${GN} Updating ${BL}$container${CL} : ${GN}$name${CL} - ${YW}Boot Disk: ${disk_info_array[0]}% full [${disk_info_array[1]}/${disk_info_array[2]} used, ${disk_info_array[3]} free]${CL}\n" - else - echo -e "${BL}[Info]${GN} Updating ${BL}$container${CL} : ${GN}$name${CL} - ${YW}[No disk info for ${os}]${CL}\n" - fi - - #1) Detect service using the service name in the update command - pushd $(mktemp -d) >/dev/null - pct pull "$container" /usr/bin/update update 2>/dev/null - service=$(cat update | sed 's|.*/ct/||g' | sed 's|\.sh).*||g') - popd >/dev/null - - #1.1) If update script not detected, return - if [ -z "${service}" ]; then - echo -e "${YW}[WARN]${CL} Update script not found\n" - return - else - echo "${BL}[INFO]${CL} Detected service: ${GN}${service}${CL}\n" - fi - - #2) Extract service build/update resource requirements from config/installation file - script=$(curl -fsSL https://raw.githubusercontent.com/remz1337/ProxmoxVE/remz/ct/${service}.sh) - config=$(pct config "$container") - build_cpu=$(echo "$script" | { grep -m 1 "var_cpu" || test $? = 1; } | sed 's|.*=||g' | sed 's|"||g' | sed 's|.*var_cpu:-||g' | sed 's|}||g') - build_ram=$(echo "$script" | { grep -m 1 "var_ram" || test $? = 1; } | sed 's|.*=||g' | sed 's|"||g' | sed 's|.*var_ram:-||g' | sed 's|}||g') - run_cpu=$(echo "$script" | { grep -m 1 "pct set \$CTID -cores" || test $? = 1; } | sed 's|.*cores ||g') - run_ram=$(echo "$script" | { grep -m 1 "pct set \$CTID -memory" || test $? = 1; } | sed 's|.*memory ||g') - current_cpu=$(echo "$config" | grep -m 1 "cores:" | sed 's|cores: ||g') - current_ram=$(echo "$config" | grep -m 1 "memory:" | sed 's|memory: ||g') - - #Test if all values are valid (>0) - if [ -z "${run_cpu}" ] || [ "$run_cpu" -le 0 ]; then - #echo "No valid value found for run_cpu. Assuming same as current configuration." - run_cpu=$current_cpu - fi - - if [ -z "${run_ram}" ] || [ "$run_ram" -le 0 ]; then - #echo "No valid value found for run_ram. Assuming same as current configuration." - run_ram=$current_ram - fi - - if [ -z "${build_cpu}" ] || [ "$build_cpu" -le 0 ]; then - #echo "No valid value found for build_cpu. Assuming same as current configuration." - build_cpu=$current_cpu - fi - - if [ -z "${build_ram}" ] || [ "$build_ram" -le 0 ]; then - #echo "No valid value found for build_ram. Assuming same as current configuration." - build_ram=$current_ram - fi - - UPDATE_BUILD_RESOURCES=0 - if [ "$build_cpu" -gt "$run_cpu" ] || [ "$build_ram" -gt "$run_ram" ]; then - UPDATE_BUILD_RESOURCES=1 - fi - - #3) if build resources are different than run resources, then: - if [ "$UPDATE_BUILD_RESOURCES" -eq "1" ]; then - #pct shutdown "$container" - #sleep 2 - pct set "$container" --cores "$build_cpu" --memory "$build_ram" - pct restart "$container" - sleep 2 - fi - - #4) Update service, using the update command - UPDATE_CMD="export PHS_SILENT=1;update;" - case "$os" in - alpine) pct exec "$container" -- ash -c "$UPDATE_CMD" ;; - archlinux) pct exec "$container" -- bash -c "$UPDATE_CMD" ;; - fedora | rocky | centos | alma) pct exec "$container" -- bash -c "$UPDATE_CMD" ;; - ubuntu | debian | devuan) pct exec "$container" -- bash -c "$UPDATE_CMD" ;; - opensuse) pct exec "$container" -- bash -c "$UPDATE_CMD" ;; - esac - - #5) if build resources are different than run resources, then: - if [ "$UPDATE_BUILD_RESOURCES" -eq "1" ]; then - #pct shutdown "$container" - #sleep 2 - pct set "$container" --cores "$run_cpu" --memory "$run_ram" - #pct restart "$container" - #sleep 2 - fi -} - -containers_needing_reboot=() -header_info -for container in $(pct list | awk '{if(NR>1) print $1}'); do - if [[ " ${excluded_containers[@]} " =~ " $container " ]]; then - header_info - echo -e "${BL}[Info]${GN} Skipping ${BL}$container${CL}" - sleep 1 - else - status=$(pct status $container) - template=$(pct config $container | grep -q "template:" && echo "true" || echo "false") - if [ "$template" == "false" ] && [ "$status" == "status: stopped" ]; then - echo -e "${BL}[Info]${GN} Starting${BL} $container ${CL} \n" - pct start $container - echo -e "${BL}[Info]${GN} Waiting For${BL} $container${CL}${GN} To Start ${CL} \n" - sleep 5 - #update_container $container - update_container_service $container - echo -e "${BL}[Info]${GN} Shutting down${BL} $container ${CL} \n" - pct shutdown $container & - elif [ "$status" == "status: running" ]; then - #update_container $container - update_container_service $container - fi - if pct exec "$container" -- [ -e "/var/run/reboot-required" ]; then - # Get the container's hostname and add it to the list - container_hostname=$(pct exec "$container" hostname) - containers_needing_reboot+=("$container ($container_hostname)") - fi - fi -done -wait -header_info -echo -e "${GN}The process is complete, and the containers have been successfully updated.${CL}\n" -if [ "${#containers_needing_reboot[@]}" -gt 0 ]; then - echo -e "${RD}The following containers require a reboot:${CL}" - for container_name in "${containers_needing_reboot[@]}"; do - echo "$container_name" - done -fi -echo "" From 45a6418d598abde7cc91af66fd479a6e53b001cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20B=C3=A9dard-Couture?= Date: Thu, 3 Jul 2025 13:08:24 -0400 Subject: [PATCH 0011/1733] Temporary redirect of install urls for testing --- misc/build.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index b75e5b38a..7fd759a27 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1282,7 +1282,7 @@ EOF' pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y sudo curl mc gnupg2 >/dev/null" fi msg_ok "Customized LXC Container" - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/install/$var_install.sh)" $? + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/remz1337/ProxmoxVED/raw/branch/pr-keycloak/install/$var_install.sh)" $? } From c1bdeb065301d7eceae1900a9b35a71ebc271282 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20B=C3=A9dard-Couture?= Date: Thu, 3 Jul 2025 13:09:38 -0400 Subject: [PATCH 0012/1733] Implement feedback from PR review --- ct/keycloak.sh | 72 ++++++++++++++++++------------------- install/keycloak-install.sh | 47 ++++++++++++------------ 2 files changed, 61 insertions(+), 58 deletions(-) diff --git a/ct/keycloak.sh b/ct/keycloak.sh index 349fadb78..6761d5785 100644 --- a/ct/keycloak.sh +++ b/ct/keycloak.sh @@ -13,7 +13,6 @@ var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" -var_postfix_sat="${var_postfix_sat:-yes}" header_info "$APP" variables @@ -21,42 +20,43 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -f /etc/systemd/system/keycloak.service ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - msg_info "Stopping ${APP}" - systemctl stop keycloak - msg_ok "Stopped ${APP}" - - msg_info "Updating packages" - apt-get update &>/dev/null - apt-get -y upgrade &>/dev/null - msg_ok "Updated packages" - - RELEASE=$(curl -fsSL https://api.github.com/repos/keycloak/keycloak/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - msg_info "Updating ${APP} to v$RELEASE" - cd /opt - wget -q https://github.com/keycloak/keycloak/releases/download/$RELEASE/keycloak-$RELEASE.tar.gz - mv keycloak keycloak.old - tar -xzf keycloak-$RELEASE.tar.gz - tar -czf keycloak_conf_backup.tar.gz keycloak.old/conf - mv keycloak_conf_backup.tar.gz keycloak-$RELEASE/conf - cp -r keycloak.old/providers keycloak-$RELEASE - cp -r keycloak.old/themes keycloak-$RELEASE - mv keycloak-$RELEASE keycloak - rm keycloak-$RELEASE.tar.gz - rm -rf keycloak.old - msg_ok "Updated ${APP} LXC" - - msg_info "Restating Keycloak" - systemctl restart keycloak - msg_ok "Restated Keycloak" + header_info + check_container_storage + check_container_resources + if [[ ! -f /etc/systemd/system/keycloak.service ]]; then + msg_error "No ${APP} Installation Found!" exit + fi + + msg_info "Stopping ${APP}" + systemctl stop keycloak + msg_ok "Stopped ${APP}" + + msg_info "Updating packages" + apt-get update &>/dev/null + apt-get -y upgrade &>/dev/null + msg_ok "Updated packages" + + msg_info "Backup old Keycloak" + cd /opt + mv keycloak keycloak.old + tar -czf keycloak_conf_backup.tar.gz keycloak.old/conf + msg_ok "Backup done" + + fetch_and_deploy_gh_release "keycloak" "keycloak/keycloak" "tarball" "latest" "/opt/keycloak" + + msg_info "Updating ${APP}" + cd /opt + mv keycloak_conf_backup.tar.gz keycloak/conf + cp -r keycloak.old/providers keycloak + cp -r keycloak.old/themes keycloak + rm -rf keycloak.old + msg_ok "Updated ${APP} LXC" + + msg_info "Restating Keycloak" + systemctl restart keycloak + msg_ok "Restated Keycloak" + exit } start diff --git a/install/keycloak-install.sh b/install/keycloak-install.sh index a0bf44bbd..fd1ec8095 100644 --- a/install/keycloak-install.sh +++ b/install/keycloak-install.sh @@ -14,24 +14,25 @@ network_check update_os msg_info "Installing Dependencies (Patience)" -$STD apt-get install -y curl -$STD apt-get install -y sudo -$STD apt-get install -y mc -$STD apt-get install -y ca-certificates-java +#$STD apt-get install -y ca-certificates-java msg_ok "Installed Dependencies" -msg_info "Installing OpenJDK" -$STD apt install wget lsb-release -y -$STD wget https://packages.microsoft.com/config/debian/$(lsb_release -rs)/packages-microsoft-prod.deb -O packages-microsoft-prod.deb -$STD dpkg -i packages-microsoft-prod.deb -$STD apt update -$STD apt install -y msopenjdk-21 -sudo update-java-alternatives --set msopenjdk-21-amd64 -rm packages-microsoft-prod.deb -msg_ok "Installed OpenJDK" +#msg_info "Installing OpenJDK" +#$STD apt install wget lsb-release -y +#$STD wget https://packages.microsoft.com/config/debian/$(lsb_release -rs)/packages-microsoft-prod.deb -O packages-microsoft-prod.deb +#$STD dpkg -i packages-microsoft-prod.deb +#$STD apt update +#$STD apt install -y msopenjdk-21 +#sudo update-java-alternatives --set msopenjdk-21-amd64 +#rm packages-microsoft-prod.deb +#msg_ok "Installed OpenJDK" + +JAVA_VERSION=21 setup_java + msg_info "Installing PostgreSQL" -$STD apt-get install -y postgresql +#$STD apt-get install -y postgresql +PG_VERSION=16 setup_postgresql DB_NAME="keycloak" DB_USER="keycloak" DB_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" @@ -40,13 +41,15 @@ $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCO $STD sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER;" msg_ok "Installed PostgreSQL" -msg_info "Installing Keycloak" -temp_file=$(mktemp) -RELEASE=$(curl -fsSL https://api.github.com/repos/keycloak/keycloak/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') -curl -fsSL "https://github.com/keycloak/keycloak/releases/download/$RELEASE/keycloak-$RELEASE.tar.gz" -o "$temp_file" -tar xzf $temp_file -mv keycloak-$RELEASE /opt/keycloak -msg_ok "Installed Keycloak" +fetch_and_deploy_gh_release "keycloak" "keycloak/keycloak" "tarball" "latest" "/opt/keycloak" + +#msg_info "Installing Keycloak" +#temp_file=$(mktemp) +#RELEASE=$(curl -fsSL https://api.github.com/repos/keycloak/keycloak/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') +#curl -fsSL "https://github.com/keycloak/keycloak/releases/download/$RELEASE/keycloak-$RELEASE.tar.gz" -o "$temp_file" +#tar xzf $temp_file +#mv keycloak-$RELEASE /opt/keycloak +#msg_ok "Installed Keycloak" msg_info "Creating Service" cat </etc/systemd/system/keycloak.service @@ -84,7 +87,7 @@ motd_ssh customize msg_info "Cleaning up" -rm -f $temp_file +#rm -f $temp_file $STD apt-get -y autoremove $STD apt-get -y autoclean msg_ok "Cleaned" From 28a86e6703900ee7ebe7e6fff189735061a1b22f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20B=C3=A9dard-Couture?= Date: Thu, 3 Jul 2025 13:37:14 -0400 Subject: [PATCH 0013/1733] Clarify config change instructions --- ct/keycloak.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/keycloak.sh b/ct/keycloak.sh index 6761d5785..7ce18cd1d 100644 --- a/ct/keycloak.sh +++ b/ct/keycloak.sh @@ -69,4 +69,4 @@ echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080/admin${CL}" echo -e "${TAB}${GN}Temporary admin user:${BL}tmpadm${CL}" echo -e "${TAB}${GN}Temporary admin password:${BL}admin123${CL}" -echo -e "${INFO}${YW} If you modified configurations files in `conf/`: Re-apply your changes to those files, otherwise leave them unchanged.${CL}" +echo -e "${INFO}${YW} If you modified ${BL}cache-ispn.xml${YW}: Re-apply your changes to the new file, otherwise leave it unchanged.${CL}" From 60ba17032d3a87a67c31ee695bfcbf00116e1199 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20B=C3=A9dard-Couture?= Date: Thu, 3 Jul 2025 14:00:42 -0400 Subject: [PATCH 0014/1733] Temporary redirect of url to test on my fork --- ct/keycloak.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/keycloak.sh b/ct/keycloak.sh index 7ce18cd1d..e4f26d5b8 100644 --- a/ct/keycloak.sh +++ b/ct/keycloak.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/remz1337/ProxmoxVE/pr-keycloak/misc/build.func) # Copyright (c) 2021-2025 tteck # Author: tteck (tteckster) | Co-Author: remz1337 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE From fd87cf6c1d7a8b8a30fc52b137886f160e99aa2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20B=C3=A9dard-Couture?= Date: Thu, 3 Jul 2025 14:08:20 -0400 Subject: [PATCH 0015/1733] Temporary redirect url (fix) --- misc/build.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index 7fd759a27..ac7d163ec 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1282,7 +1282,7 @@ EOF' pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y sudo curl mc gnupg2 >/dev/null" fi msg_ok "Customized LXC Container" - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/remz1337/ProxmoxVED/raw/branch/pr-keycloak/install/$var_install.sh)" $? + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/remz1337/ProxmoxVED/pr-keycloak/install/$var_install.sh)" $? } From 875b7ab140c45077b6d5fd7a677d724683314972 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20B=C3=A9dard-Couture?= Date: Thu, 3 Jul 2025 14:26:38 -0400 Subject: [PATCH 0016/1733] Download keycloak binaries instead of source --- install/keycloak-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/keycloak-install.sh b/install/keycloak-install.sh index fd1ec8095..a990c3a90 100644 --- a/install/keycloak-install.sh +++ b/install/keycloak-install.sh @@ -41,7 +41,7 @@ $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCO $STD sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER;" msg_ok "Installed PostgreSQL" -fetch_and_deploy_gh_release "keycloak" "keycloak/keycloak" "tarball" "latest" "/opt/keycloak" +fetch_and_deploy_gh_release "keycloak" "keycloak/keycloak" "prebuild" "latest" "/opt/keycloak" #msg_info "Installing Keycloak" #temp_file=$(mktemp) From b080301e4059db7a32ce30503a93441769299e05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20B=C3=A9dard-Couture?= Date: Thu, 3 Jul 2025 14:35:33 -0400 Subject: [PATCH 0017/1733] Add pattern to fetch function since it's a prebuilt binary --- install/keycloak-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/keycloak-install.sh b/install/keycloak-install.sh index a990c3a90..89cb51bf3 100644 --- a/install/keycloak-install.sh +++ b/install/keycloak-install.sh @@ -41,7 +41,7 @@ $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCO $STD sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER;" msg_ok "Installed PostgreSQL" -fetch_and_deploy_gh_release "keycloak" "keycloak/keycloak" "prebuild" "latest" "/opt/keycloak" +fetch_and_deploy_gh_release "keycloak" "keycloak/keycloak" "prebuild" "latest" "/opt/keycloak" "keycloak-*.tar.gz" #msg_info "Installing Keycloak" #temp_file=$(mktemp) From 849dd9750115494a3d7175b2157f924c52c2821e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20B=C3=A9dard-Couture?= Date: Thu, 3 Jul 2025 14:46:23 -0400 Subject: [PATCH 0018/1733] Fix JAVA_HOME environment variable to use temurin JDK --- install/keycloak-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/keycloak-install.sh b/install/keycloak-install.sh index 89cb51bf3..b7ced7654 100644 --- a/install/keycloak-install.sh +++ b/install/keycloak-install.sh @@ -66,7 +66,7 @@ ExecStart=/opt/keycloak/bin/kc.sh start ExecStop=/opt/keycloak/bin/kc.sh stop Restart=always RestartSec=3 -Environment="JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64" +Environment="JAVA_HOME=/usr/lib/jvm/temurin-21-jdk-amd64" Environment="KC_DB=postgres" Environment="KC_DB_USERNAME=$DB_USER" Environment="KC_DB_PASSWORD=$DB_PASS" From d0b68432f864d807f0e7ae91f68625ec32fd77a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20B=C3=A9dard-Couture?= Date: Thu, 3 Jul 2025 14:49:38 -0400 Subject: [PATCH 0019/1733] Temporary fix url to test with my fork --- ct/keycloak.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/keycloak.sh b/ct/keycloak.sh index e4f26d5b8..f28e152d7 100644 --- a/ct/keycloak.sh +++ b/ct/keycloak.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/remz1337/ProxmoxVE/pr-keycloak/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/remz1337/ProxmoxVED/pr-keycloak/misc/build.func) # Copyright (c) 2021-2025 tteck # Author: tteck (tteckster) | Co-Author: remz1337 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE From c24c858562e221e3d2b834227c7e24acf4254e59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20B=C3=A9dard-Couture?= Date: Thu, 3 Jul 2025 14:55:51 -0400 Subject: [PATCH 0020/1733] Fix fetch command in update path to retrieve binaries and clean up old commented code --- ct/keycloak.sh | 2 +- install/keycloak-install.sh | 31 +++---------------------------- 2 files changed, 4 insertions(+), 29 deletions(-) diff --git a/ct/keycloak.sh b/ct/keycloak.sh index f28e152d7..8d897845c 100644 --- a/ct/keycloak.sh +++ b/ct/keycloak.sh @@ -43,7 +43,7 @@ function update_script() { tar -czf keycloak_conf_backup.tar.gz keycloak.old/conf msg_ok "Backup done" - fetch_and_deploy_gh_release "keycloak" "keycloak/keycloak" "tarball" "latest" "/opt/keycloak" + fetch_and_deploy_gh_release "keycloak" "keycloak/keycloak" "prebuild" "latest" "/opt/keycloak" "keycloak-*.tar.gz" msg_info "Updating ${APP}" cd /opt diff --git a/install/keycloak-install.sh b/install/keycloak-install.sh index b7ced7654..6d1deb162 100644 --- a/install/keycloak-install.sh +++ b/install/keycloak-install.sh @@ -13,44 +13,20 @@ setting_up_container network_check update_os -msg_info "Installing Dependencies (Patience)" -#$STD apt-get install -y ca-certificates-java -msg_ok "Installed Dependencies" - -#msg_info "Installing OpenJDK" -#$STD apt install wget lsb-release -y -#$STD wget https://packages.microsoft.com/config/debian/$(lsb_release -rs)/packages-microsoft-prod.deb -O packages-microsoft-prod.deb -#$STD dpkg -i packages-microsoft-prod.deb -#$STD apt update -#$STD apt install -y msopenjdk-21 -#sudo update-java-alternatives --set msopenjdk-21-amd64 -#rm packages-microsoft-prod.deb -#msg_ok "Installed OpenJDK" - JAVA_VERSION=21 setup_java - - -msg_info "Installing PostgreSQL" -#$STD apt-get install -y postgresql PG_VERSION=16 setup_postgresql + +msg_info "Configuring PostgreSQL user" DB_NAME="keycloak" DB_USER="keycloak" DB_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" $STD sudo -u postgres psql -c "CREATE USER $DB_USER WITH PASSWORD '$DB_PASS';" $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8';" $STD sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER;" -msg_ok "Installed PostgreSQL" +msg_ok "Configured PostgreSQL user" fetch_and_deploy_gh_release "keycloak" "keycloak/keycloak" "prebuild" "latest" "/opt/keycloak" "keycloak-*.tar.gz" -#msg_info "Installing Keycloak" -#temp_file=$(mktemp) -#RELEASE=$(curl -fsSL https://api.github.com/repos/keycloak/keycloak/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') -#curl -fsSL "https://github.com/keycloak/keycloak/releases/download/$RELEASE/keycloak-$RELEASE.tar.gz" -o "$temp_file" -#tar xzf $temp_file -#mv keycloak-$RELEASE /opt/keycloak -#msg_ok "Installed Keycloak" - msg_info "Creating Service" cat </etc/systemd/system/keycloak.service [Unit] @@ -87,7 +63,6 @@ motd_ssh customize msg_info "Cleaning up" -#rm -f $temp_file $STD apt-get -y autoremove $STD apt-get -y autoclean msg_ok "Cleaned" From 154e450a4464447f42e0e9c8f8de8f6adc95cf52 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Wed, 16 Jul 2025 17:20:10 -0400 Subject: [PATCH 0021/1733] New script: Tududi --- ct/tududi.sh | 71 +++++++++++++++++++++++++++++++ frontend/public/json/tududi.json | 40 +++++++++++++++++ install/tududi-install.sh | 73 ++++++++++++++++++++++++++++++++ 3 files changed, 184 insertions(+) create mode 100644 ct/tududi.sh create mode 100644 frontend/public/json/tududi.json create mode 100644 install/tududi-install.sh diff --git a/ct/tududi.sh b/ct/tududi.sh new file mode 100644 index 000000000..7aa6375ab --- /dev/null +++ b/ct/tududi.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash +source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: vhsdream +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://tududi.com + +APP="Tududi" +var_tags="${var_tags:-todo-app}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-1024}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/tududi ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + RELEASE=$(curl -fsSL https://api.github.com/repos/chrisvel/tududi/releases/latest | yq '.tag_name' | sed 's/^v//') + if [[ "${RELEASE}" != "$(cat ~/.tududi 2>/dev/null)" ]] || [[ ! -f ~/.tududi ]]; then + msg_info "Stopping Service" + systemctl stop tududi + msg_ok "Stopped Service" + + msg_info "Upadting ${APP}" + cp /opt/"$APP"/backend/.env /opt/"$APP".env + rm -rf /opt/"$APP" + fetch_and_deploy_gh_release "tududi" "chrisvel/tududi" + + cd /opt/"$APP" + $STD npm install + export NODE_ENV=production + $STD npm run frontend:build + cp -r ./dist ./backend/dist + cp -r ./public/locales ./backend/dist/locales + mv /opt/"$APP".env /opt/"$APP"/.env + msg_ok "Updated $APP" + + msg_info "Starting Service" + systemctl start tududi + msg_ok "Started Service" + + msg_ok "Updated Successfully" + else + msg_ok "Already up to date" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${ADVANCED}${BL}Create your initial user in${CL} ${BGN}/opt/${APP}${CL}${BL} in the LXC:${CL}" +echo -e "${TAB3}${BL}npm run user:create ${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3002${CL}" diff --git a/frontend/public/json/tududi.json b/frontend/public/json/tududi.json new file mode 100644 index 000000000..fb06bb9e7 --- /dev/null +++ b/frontend/public/json/tududi.json @@ -0,0 +1,40 @@ +{ + "name": "Tududi", + "slug": "tududi", + "categories": [ + 12 + ], + "date_created": "2025-07-07", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 3002, + "documentation": null, + "config_path": "/", + "website": "https://tududi.com/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/notesnook.webp", + "description": "Notesnook is a free (as in speech) & open-source note-taking app focused on user privacy & ease of use. To ensure zero knowledge principles, Notesnook encrypts everything on your device using XChaCha20-Poly1305 & Argon2.", + "install_methods": [ + { + "type": "default", + "script": "ct/tududi.sh", + "resources": { + "cpu": 2, + "ram": 1024, + "hdd": 4, + "os": "Debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Create users like this: `cd /opt/tududi` => `npm run user:create `", + "type": "info" + } + ] +} diff --git a/install/tududi-install.sh b/install/tududi-install.sh new file mode 100644 index 000000000..a9268d212 --- /dev/null +++ b/install/tududi-install.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash + +# Copyright (c) 2025 Community Scripts ORG +# Author: vhsdream +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://tududi.com/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt-get install -y sqlite3 +msg_ok "Installed Dependencies" + +NODE_VERSION="20" setup_nodejs + +msg_info "Installing Tududi" +fetch_and_deploy_gh_release "tududi" "chrisvel/tududi" +cd /opt/"$APPLICATION" +$STD npm install +export NODE_ENV=production +$STD npm run frontend:build +cp -r ./dist ./backend/dist +cp -r ./public/locales ./backend/dist/locales +msg_ok "Installed Tududi" + +msg_info "Creating config and database" +DB_LOCATION="/opt/tududi-db" +UPLOAD_DIR="/opt/tududi-uploads" +mkdir -p {"$DB_LOCATION","$UPLOAD_DIR"} +SECRET="$(openssl rand -hex 64)" +sed -e 's/^GOOGLE/# &/' \ + -e '/TUDUDI_SESSION/s/^# //' \ + -e '/NODE_ENV/s/^# //' \ + -e "s/your_session_secret_here/$SECRET/" \ + -e 's/development/production/' \ + -e "\$a\DB_FILE=$DB_LOCATION/production.sqlite3" \ + -e "\$a\UPLOAD_LOCATION=$UPLOAD_DIR" \ + /opt/tududi/backend/.env.example >/opt/tududi/backend/.env +export DB_FILE="$DB_LOCATION/production.sqlite3" +$STD npm run db:init +msg_ok "Created config and database" + +msg_info "Creating service" +cat </etc/systemd/system/tududi.service +[Unit] +Description=Tududi Service +After=network.target + +[Service] +Type=simple +WorkingDirectory=/opt/tududi +EnvironmentFile=/opt/tududi/backend/.env +ExecStart=/usr/bin/npm run start + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now tududi +msg_ok "Created service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 4fc97ca1df781cef4f6cc2372ff564ff4505568c Mon Sep 17 00:00:00 2001 From: vhsdream Date: Wed, 16 Jul 2025 17:33:42 -0400 Subject: [PATCH 0022/1733] Fix icon --- frontend/public/json/tududi.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/tududi.json b/frontend/public/json/tududi.json index fb06bb9e7..080f210eb 100644 --- a/frontend/public/json/tududi.json +++ b/frontend/public/json/tududi.json @@ -12,7 +12,7 @@ "documentation": null, "config_path": "/", "website": "https://tududi.com/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/notesnook.webp", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/tududi.webp", "description": "Notesnook is a free (as in speech) & open-source note-taking app focused on user privacy & ease of use. To ensure zero knowledge principles, Notesnook encrypts everything on your device using XChaCha20-Poly1305 & Argon2.", "install_methods": [ { From cd36c5f8620fad3ba962eea7291960af4fd3a4ca Mon Sep 17 00:00:00 2001 From: vhsdream Date: Wed, 16 Jul 2025 17:40:54 -0400 Subject: [PATCH 0023/1733] Fix description; copy favicons --- ct/tududi.sh | 1 + frontend/public/json/tududi.json | 2 +- install/tududi-install.sh | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ct/tududi.sh b/ct/tududi.sh index 7aa6375ab..ee1347139 100644 --- a/ct/tududi.sh +++ b/ct/tududi.sh @@ -45,6 +45,7 @@ function update_script() { $STD npm run frontend:build cp -r ./dist ./backend/dist cp -r ./public/locales ./backend/dist/locales + cp ./public/favicon.* ./backend/dist mv /opt/"$APP".env /opt/"$APP"/.env msg_ok "Updated $APP" diff --git a/frontend/public/json/tududi.json b/frontend/public/json/tududi.json index 080f210eb..e02f7c63c 100644 --- a/frontend/public/json/tududi.json +++ b/frontend/public/json/tududi.json @@ -13,7 +13,7 @@ "config_path": "/", "website": "https://tududi.com/", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/tududi.webp", - "description": "Notesnook is a free (as in speech) & open-source note-taking app focused on user privacy & ease of use. To ensure zero knowledge principles, Notesnook encrypts everything on your device using XChaCha20-Poly1305 & Argon2.", + "description": "Self-hosted task management with functional programming architecture, hierarchical organization, and multi-language support.", "install_methods": [ { "type": "default", diff --git a/install/tududi-install.sh b/install/tududi-install.sh index a9268d212..2b58d6eb5 100644 --- a/install/tududi-install.sh +++ b/install/tududi-install.sh @@ -27,6 +27,7 @@ export NODE_ENV=production $STD npm run frontend:build cp -r ./dist ./backend/dist cp -r ./public/locales ./backend/dist/locales +cp ./public/favicon.* ./backend/dist msg_ok "Installed Tududi" msg_info "Creating config and database" From d9818797eeada22542e8893580017a2d0a369ddc Mon Sep 17 00:00:00 2001 From: vhsdream Date: Wed, 16 Jul 2025 17:42:19 -0400 Subject: [PATCH 0024/1733] Add config path to JSON --- frontend/public/json/tududi.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/tududi.json b/frontend/public/json/tududi.json index e02f7c63c..e8da88404 100644 --- a/frontend/public/json/tududi.json +++ b/frontend/public/json/tududi.json @@ -10,7 +10,7 @@ "privileged": false, "interface_port": 3002, "documentation": null, - "config_path": "/", + "config_path": "/opt/tududi/backend/.env", "website": "https://tududi.com/", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/tududi.webp", "description": "Self-hosted task management with functional programming architecture, hierarchical organization, and multi-language support.", From 442c2b8a67ffb6db57c5699ac4e4e1a65473343a Mon Sep 17 00:00:00 2001 From: vhsdream Date: Wed, 16 Jul 2025 17:49:22 -0400 Subject: [PATCH 0025/1733] Use explicit path --- install/tududi-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/tududi-install.sh b/install/tududi-install.sh index 2b58d6eb5..17cd07a03 100644 --- a/install/tududi-install.sh +++ b/install/tududi-install.sh @@ -21,7 +21,7 @@ NODE_VERSION="20" setup_nodejs msg_info "Installing Tududi" fetch_and_deploy_gh_release "tududi" "chrisvel/tududi" -cd /opt/"$APPLICATION" +cd /opt/tududi $STD npm install export NODE_ENV=production $STD npm run frontend:build From be46cc130d77c133969bc1c2710ce942daf29ac5 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Wed, 16 Jul 2025 18:10:35 -0400 Subject: [PATCH 0026/1733] Alter post-installation text --- ct/tududi.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ct/tududi.sh b/ct/tududi.sh index ee1347139..431ea85ff 100644 --- a/ct/tududi.sh +++ b/ct/tududi.sh @@ -66,7 +66,6 @@ description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${ADVANCED}${BL}Create your initial user in${CL} ${BGN}/opt/${APP}${CL}${BL} in the LXC:${CL}" -echo -e "${TAB3}${BL}npm run user:create ${CL}" +echo -e "${ADVANCED}${BL}Create your initial user in${CL} ${BGN}/opt/${APP}${CL}${BL} in the LXC:${CL} ${RD}npm run user:create ${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3002${CL}" From c0b1b524913e59a01ef19a4b51ef5f3b1bcdcc21 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Wed, 16 Jul 2025 18:32:27 -0400 Subject: [PATCH 0027/1733] Increase RAM --- ct/tududi.sh | 2 +- frontend/public/json/tududi.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/tududi.sh b/ct/tududi.sh index 431ea85ff..81c538c40 100644 --- a/ct/tududi.sh +++ b/ct/tududi.sh @@ -8,7 +8,7 @@ source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/ APP="Tududi" var_tags="${var_tags:-todo-app}" var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-1024}" +var_ram="${var_ram:-2048}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-12}" diff --git a/frontend/public/json/tududi.json b/frontend/public/json/tududi.json index e8da88404..fd884ef59 100644 --- a/frontend/public/json/tududi.json +++ b/frontend/public/json/tududi.json @@ -20,7 +20,7 @@ "script": "ct/tududi.sh", "resources": { "cpu": 2, - "ram": 1024, + "ram": 2048, "hdd": 4, "os": "Debian", "version": "12" From 049fc60585f491929d36141c3d6878bfb47f2658 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 17 Jul 2025 08:43:37 +0200 Subject: [PATCH 0028/1733] Update auto-update-app-headers.yml --- .github/workflows/auto-update-app-headers.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/auto-update-app-headers.yml b/.github/workflows/auto-update-app-headers.yml index 18941379c..39ecb5ca7 100644 --- a/.github/workflows/auto-update-app-headers.yml +++ b/.github/workflows/auto-update-app-headers.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Generate a token id: generate-token - uses: actions/create-github-app-token@v1 + uses: actions/create-github-app-token@v2 with: app-id: ${{ vars.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} From d5b6b672edb771651fe571313c88c01239a79368 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 17 Jul 2025 08:47:28 +0200 Subject: [PATCH 0029/1733] Update auto-update-app-headers.yml --- .github/workflows/auto-update-app-headers.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/auto-update-app-headers.yml b/.github/workflows/auto-update-app-headers.yml index 39ecb5ca7..339ed4c4a 100644 --- a/.github/workflows/auto-update-app-headers.yml +++ b/.github/workflows/auto-update-app-headers.yml @@ -27,7 +27,7 @@ jobs: - name: Generate a token for PR approval and merge id: generate-token-merge - uses: actions/create-github-app-token@v1 + uses: actions/create-github-app-token@v2 with: app-id: ${{ secrets.APP_ID_APPROVE_AND_MERGE }} private-key: ${{ secrets.APP_KEY_APPROVE_AND_MERGE }} From 64569d5d67e4f20b3b8c1d20be10ecf1840f637d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 17 Jul 2025 14:07:03 +0200 Subject: [PATCH 0030/1733] fix --- frontend/public/json/cloudreve.json | 6 +++--- install/salt-install.sh | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/frontend/public/json/cloudreve.json b/frontend/public/json/cloudreve.json index ff754a105..798a07cc1 100644 --- a/frontend/public/json/cloudreve.json +++ b/frontend/public/json/cloudreve.json @@ -17,7 +17,7 @@ "install_methods": [ { "type": "default", - "script": "ct/librenms.sh", + "script": "ct/cloudreve.sh", "resources": { "cpu": 2, "ram": 2048, @@ -28,8 +28,8 @@ } ], "default_credentials": { - "username": "admin", - "password": "admin" + "username": null, + "password": null }, "notes": [] } diff --git a/install/salt-install.sh b/install/salt-install.sh index 1f59939b4..7c74b7c10 100644 --- a/install/salt-install.sh +++ b/install/salt-install.sh @@ -14,8 +14,7 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt-get install -y \ - jq +$STD apt-get install -y jq msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "salt" "saltstack/salt" "binary" "latest" "/opt/salt" "salt-master*_amd64.deb" From 838a73fb90f6e2b33d22029077e05afd2fa544c6 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 17 Jul 2025 14:28:06 +0200 Subject: [PATCH 0031/1733] fix json --- frontend/public/json/cloudreve.json | 31 +++++++++++++++++------------ 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/frontend/public/json/cloudreve.json b/frontend/public/json/cloudreve.json index 798a07cc1..060b2d83b 100644 --- a/frontend/public/json/cloudreve.json +++ b/frontend/public/json/cloudreve.json @@ -1,27 +1,27 @@ { - "name": "LibreNMS", - "slug": "librenms", + "name": "Cloudreve", + "slug": "cloudreve", "categories": [ - 9 + 12 ], - "date_created": "2025-03-24", + "date_created": "2025-07-17", "type": "ct", "updateable": false, "privileged": false, - "interface_port": 80, - "documentation": "https://docs.librenms.org/", - "website": "https://librenms.org/", + "interface_port": 5212, + "documentation": "https://docs.cloudreve.org/en/", + "website": "https://cloudreve.org/", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/cloudreve.webp", - "config_path": "/opt/librenms/config.php and /opt/librenms/.env", - "description": "LibreNMS is an open-source, community-driven network monitoring system that provides automatic discovery, alerting, and performance tracking for network devices. It supports a wide range of hardware and integrates with various notification and logging platforms.", + "config_path": "/opt/cloudreve/data/config.ini", + "description": "Cloudreve is an open-source, community-driven cloud storage system that provides file sharing, synchronization, and management features. It supports a wide range of storage backends and integrates with various notification and logging platforms.", "install_methods": [ { "type": "default", "script": "ct/cloudreve.sh", "resources": { - "cpu": 2, - "ram": 2048, - "hdd": 4, + "cpu": 1, + "ram": 1024, + "hdd": 10, "os": "Debian", "version": "12" } @@ -31,5 +31,10 @@ "username": null, "password": null }, - "notes": [] + "notes": [ + { + "text": "After Installation: Register your user -> Login -> Dashboard -> Accept Primary URL.", + "type": "info" + } + ] } From c61351b8680a1632ebea041459807a507a1f7204 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 17 Jul 2025 14:28:41 +0200 Subject: [PATCH 0032/1733] Update cloudreve.json --- frontend/public/json/cloudreve.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/cloudreve.json b/frontend/public/json/cloudreve.json index 060b2d83b..61fa2ce0b 100644 --- a/frontend/public/json/cloudreve.json +++ b/frontend/public/json/cloudreve.json @@ -34,7 +34,7 @@ "notes": [ { "text": "After Installation: Register your user -> Login -> Dashboard -> Accept Primary URL.", - "type": "info" + "type": "warn" } ] } From a727d30127f59fac12e46b11dfe56e7dfeef1a27 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 17 Jul 2025 14:35:10 +0200 Subject: [PATCH 0033/1733] Update cloudreve.sh --- ct/cloudreve.sh | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/ct/cloudreve.sh b/ct/cloudreve.sh index f64fea7a3..ce5ee2966 100644 --- a/ct/cloudreve.sh +++ b/ct/cloudreve.sh @@ -23,14 +23,28 @@ function update_script() { header_info check_container_storage check_container_resources - if [[ ! -d /var ]]; then + + if [[ ! -d /opt/cloudreve ]]; then msg_error "No ${APP} Installation Found!" exit fi - msg_info "Updating $APP LXC" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "Updated $APP LXC" + + RELEASE=$(curl -fsSL https://api.github.com/repos/cloudreve/cloudreve/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + if [[ "${RELEASE}" != "$(cat ~/.cloudreve 2>/dev/null)" ]] || [[ ! -f ~/.cloudreve ]]; then + msg_info "Stopping $APP" + systemctl stop cloudreve + msg_ok "Stopped $APP" + + fetch_and_deploy_gh_release "cloudreve" "cloudreve/cloudreve" "prebuild" "latest" "/opt/cloudreve" "*linux_amd64.tar.gz" + + msg_info "Starting $APP" + systemctl start cloudreve + msg_ok "Started $APP" + + msg_ok "Update Successful" + else + msg_ok "No update required. ${APP} is already at v${RELEASE}" + fi exit } From e90783865422fecb0c7d184920bbff2d376171a5 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 17 Jul 2025 15:22:02 +0200 Subject: [PATCH 0034/1733] Update linkstack-install.sh --- install/linkstack-install.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/install/linkstack-install.sh b/install/linkstack-install.sh index 1d6ce833a..87e8c14df 100644 --- a/install/linkstack-install.sh +++ b/install/linkstack-install.sh @@ -18,9 +18,7 @@ $STD apt-get install -y \ software-properties-common \ ca-certificates \ lsb-release \ - apt-transport-https \ - apache2 - unzip + apt-transport-https msg_ok "Installed dependencies" PHP_VERSION="8.2" PHP_MODULE="sqlite3, mysql, fileinfo" PHP_APACHE="YES" install_php From e77563e6bc73ed58c24d116566086adb85652f29 Mon Sep 17 00:00:00 2001 From: dadcoachengineer Date: Thu, 17 Jul 2025 11:01:14 -0500 Subject: [PATCH 0035/1733] Fixed curl command at line 137 for libusb --- install/frigate-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/frigate-install.sh b/install/frigate-install.sh index 557fd9d39..daf95b439 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -134,7 +134,7 @@ msg_info "Installing Coral Object Detection Model (Patience)" cd /opt/frigate export CCACHE_DIR=/root/.ccache export CCACHE_MAXSIZE=2G -curl -fsSL https://github.com/libusb/libusb/archive/v1.0.26.zip +curl -L -o v1.0.26.zip https://github.com/libusb/libusb/archive/v1.0.26.zip unzip -q v1.0.26.zip rm v1.0.26.zip cd libusb-1.0.26 From 3ab0eeabb9ab01f1ac1b9e0e5628947ff1952e4b Mon Sep 17 00:00:00 2001 From: vhsdream Date: Thu, 17 Jul 2025 13:08:57 -0400 Subject: [PATCH 0036/1733] Delete Reactive-Resume --- ct/reactive-resume.sh | 100 ------------ frontend/public/json/reactive-resume.json | 36 ----- install/reactive-resume-install.sh | 178 ---------------------- 3 files changed, 314 deletions(-) delete mode 100644 ct/reactive-resume.sh delete mode 100644 frontend/public/json/reactive-resume.json delete mode 100644 install/reactive-resume-install.sh diff --git a/ct/reactive-resume.sh b/ct/reactive-resume.sh deleted file mode 100644 index 52a2fdceb..000000000 --- a/ct/reactive-resume.sh +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: vhsdream -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://rxresume.org - -APP="Reactive-Resume" -var_tags="${var_tags:-documents}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-3072}" -var_disk="${var_disk:-8}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - - if [[ ! -f /etc/systemd/system/Reactive-Resume.service ]]; then - msg_error "No $APP Installation Found!" - exit - fi - RELEASE=$(curl -fsSL https://api.github.com/repos/lazy-media/Reactive-Resume/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') - if [[ ! -f "$HOME"/.reactive-resume ]] || [[ "$RELEASE" != "$(cat "$HOME"/.reactive-resume)" ]]; then - msg_info "Stopping services" - systemctl stop Reactive-Resume - msg_ok "Stopped services" - - msg_info "Updating $APP to v${RELEASE}" - cp /opt/"$APP"/.env /opt/rxresume.env - fetch_and_deploy_gh_release "Reactive-Resume" "lazy-media/Reactive-Resume" - cd /opt/"$APP" - export PUPPETEER_SKIP_DOWNLOAD="true" - export NEXT_TELEMETRY_DISABLED=1 - export CI="true" - export NODE_ENV="production" - $STD pnpm install --frozen-lockfile - $STD pnpm run build - $STD pnpm run prisma:generate - mv /opt/rxresume.env /opt/"$APP"/.env - msg_ok "Updated $APP to v$RELEASE" - - msg_info "Updating Minio" - systemctl stop minio - cd /tmp - curl -fsSL https://dl.min.io/server/minio/release/linux-amd64/minio.deb -o minio.deb - $STD dpkg -i minio.deb - msg_ok "Updated Minio" - - msg_info "Updating Browserless (Patience)" - systemctl stop browserless - cp /opt/browserless/.env /opt/browserless.env - rm -rf browserless - brwsr_tmp=$(mktemp) - TAG=$(curl -fsSL https://api.github.com/repos/browserless/browserless/tags?per_page=1 | grep "name" | awk '{print substr($2, 3, length($2)-4) }') - curl -fsSL https://github.com/browserless/browserless/archive/refs/tags/v"$TAG".zip -O "$brwsr_tmp" - $STD unzip "$brwsr_tmp" - mv browserless-"$TAG"/ /opt/browserless - cd /opt/browserless - $STD npm install - rm -rf src/routes/{chrome,edge,firefox,webkit} - $STD node_modules/playwright-core/cli.js install --with-deps chromium - $STD npm run build - $STD npm run build:function - $STD npm prune production - mv /opt/browserless.env /opt/browserless/.env - msg_ok "Updated Browserless" - - msg_info "Restarting services" - systemctl start minio Reactive-Resume browserless - msg_ok "Restarted services" - - msg_info "Cleaning Up" - rm -f /tmp/minio.deb - rm -f "$brwsr_tmp" - msg_ok "Cleanup Completed" - - msg_ok "Update Successful" - else - msg_ok "No update required. $APP is already at v$RELEASE" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" diff --git a/frontend/public/json/reactive-resume.json b/frontend/public/json/reactive-resume.json deleted file mode 100644 index 5c99acfb9..000000000 --- a/frontend/public/json/reactive-resume.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "Reactive Resume", - "slug": "reactive-resume", - "categories": [ - 12 - ], - "date_created": "2025-04-22", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 3000, - "documentation": "https://docs.rxresume.org/", - "website": "https://rxresume.org", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/png/reactive-resume-light.png", - "config_path": "/opt/reactive-resume/.env", - "description": "A one-of-a-kind resume builder that keeps your privacy in mind. Completely secure, customizable, portable, open-source and free forever.", - "install_methods": [ - { - "type": "default", - "script": "ct/reactive-resume.sh", - "resources": { - "cpu": 2, - "ram": 3072, - "hdd": 8, - "os": "Debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] -} - diff --git a/install/reactive-resume-install.sh b/install/reactive-resume-install.sh deleted file mode 100644 index 895ace024..000000000 --- a/install/reactive-resume-install.sh +++ /dev/null @@ -1,178 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: vhsdream -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://rxresume.org - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -cd /tmp -curl -fsSL https://dl.min.io/server/minio/release/linux-amd64/minio.deb -o minio.deb -$STD dpkg -i minio.deb -msg_ok "Installed Dependencies" - -PG_VERSION="16" PG_MODULES="common" setup_postgresql -NODE_VERSION="22" NODE_MODULE="pnpm@latest" setup_nodejs - -msg_info "Setting up Database" -DB_USER="rxresume" -DB_NAME="rxresume" -DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) -$STD sudo -u postgres psql -c "CREATE USER $DB_USER WITH ENCRYPTED PASSWORD '$DB_PASS';" -$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" -$STD sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE $DB_NAME to $DB_USER;" -$STD sudo -u postgres psql -c "ALTER USER $DB_USER WITH SUPERUSER;" -msg_ok "Set up Database" - -msg_info "Installing $APPLICATION" -MINIO_PASS=$(openssl rand -base64 48) -ACCESS_TOKEN=$(openssl rand -base64 48) -REFRESH_TOKEN=$(openssl rand -base64 48) -CHROME_TOKEN=$(openssl rand -hex 32) -LOCAL_IP=$(hostname -I | awk '{print $1}') -TAG=$(curl -fsSL https://api.github.com/repos/browserless/browserless/tags?per_page=1 | grep "name" | awk '{print substr($2, 3, length($2)-4) }') - -fetch_and_deploy_gh_release "Reactive-Resume" "lazy-media/Reactive-Resume" -cd /opt/"$APPLICATION" -# corepack enable -export CI="true" -export PUPPETEER_SKIP_DOWNLOAD="true" -export NODE_ENV="production" -export NEXT_TELEMETRY_DISABLED=1 -$STD pnpm install --frozen-lockfile -$STD pnpm run build -$STD pnpm install --prod --frozen-lockfile -$STD pnpm run prisma:generate -msg_ok "Installed $APPLICATION" - -msg_info "Installing Browserless (Patience)" -cd /tmp -curl -fsSL https://github.com/browserless/browserless/archive/refs/tags/v"$TAG".zip -o v"$TAG".zip -$STD unzip v"$TAG".zip -mv browserless-"$TAG" /opt/browserless -cd /opt/browserless -$STD npm install -rm -rf src/routes/{chrome,edge,firefox,webkit} -$STD node_modules/playwright-core/cli.js install --with-deps chromium -$STD npm run build -$STD npm run build:function -$STD npm prune production -msg_ok "Installed Browserless" - -msg_info "Configuring applications" -mkdir -p /opt/minio -cat </opt/minio/.env -MINIO_ROOT_USER="storageadmin" -MINIO_ROOT_PASSWORD="${MINIO_PASS}" -MINIO_VOLUMES=/opt/minio -MINIO_OPTS="--address :9000 --console-address 127.0.0.1:9001" -EOF -cat </opt/"$APPLICATION"/.env -NODE_ENV=production -PORT=3000 -PUBLIC_URL=http://${LOCAL_IP}:3000 -STORAGE_URL=http://${LOCAL_IP}:9000/rxresume -DATABASE_URL=postgresql://${DB_USER}:${DB_PASS}@localhost:5432/${DB_NAME}?schema=public -ACCESS_TOKEN_SECRET=${ACCESS_TOKEN} -REFRESH_TOKEN_SECRET=${REFRESH_TOKEN} -CHROME_PORT=8080 -CHROME_TOKEN=${CHROME_TOKEN} -CHROME_URL=ws://localhost:8080 -CHROME_IGNORE_HTTPS_ERRORS=true -MAIL_FROM=noreply@locahost -# SMTP_URL=smtp://username:password@smtp.server.mail:587 # -STORAGE_ENDPOINT=localhost -STORAGE_PORT=9000 -STORAGE_REGION=us-east-1 -STORAGE_BUCKET=rxresume -STORAGE_ACCESS_KEY=storageadmin -STORAGE_SECRET_KEY=${MINIO_PASS} -STORAGE_USE_SSL=false -STORAGE_SKIP_BUCKET_CHECK=false - -# GitHub (OAuth, Optional) -# GITHUB_CLIENT_ID= -# GITHUB_CLIENT_SECRET= -# GITHUB_CALLBACK_URL=http://localhost:5173/api/auth/github/callback - -# Google (OAuth, Optional) -# GOOGLE_CLIENT_ID= -# GOOGLE_CLIENT_SECRET= -# GOOGLE_CALLBACK_URL=http://localhost:5173/api/auth/google/callback -EOF -cat </opt/browserless/.env -DEBUG=browserless*,-**:verbose -HOST=localhost -PORT=8080 -TOKEN=${CHROME_TOKEN} -EOF -{ - echo "${APPLICATION} Credentials" - echo "Database User: $DB_USER" - echo "Database Password: $DB_PASS" - echo "Database Name: $DB_NAME" - echo "Minio Root Password: ${MINIO_PASS}" -} >>~/"$APPLICATION".creds -msg_ok "Configured applications" - -msg_info "Creating Services" -mkdir -p /etc/systemd/system/minio.service.d/ -cat </etc/systemd/system/minio.service.d/override.conf -[Service] -User=root -Group=root -WorkingDirectory=/usr/local/bin -EnvironmentFile=/opt/minio/.env -EOF - -cat </etc/systemd/system/"$APPLICATION".service -[Unit] -Description=${APPLICATION} Service -After=network.target postgresql.service minio.service -Wants=postgresql.service minio.service - -[Service] -WorkingDirectory=/opt/${APPLICATION} -EnvironmentFile=/opt/${APPLICATION}/.env -ExecStart=/usr/bin/pnpm run start -Restart=always - -[Install] -WantedBy=multi-user.target -EOF - -cat </etc/systemd/system/browserless.service -[Unit] -Description=Browserless service -After=network.target ${APPLICATION}.service - -[Service] -WorkingDirectory=/opt/browserless -EnvironmentFile=/opt/browserless/.env -ExecStart=/usr/bin/npm run start -Restart=unless-stopped - -[Install] -WantedBy=multi-user.target -EOF -systemctl daemon-reload -systemctl enable -q --now minio.service "$APPLICATION".service browserless.service -msg_ok "Created Services" - -motd_ssh -customize - -msg_info "Cleaning up" -rm -f /tmp/v"$TAG".zip -rm -f /tmp/minio.deb -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" From e0528ba540daf7d51e82606e58887ca9f035d158 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Thu, 17 Jul 2025 16:44:50 -0400 Subject: [PATCH 0037/1733] typo fix --- ct/tududi.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/tududi.sh b/ct/tududi.sh index 81c538c40..a4d6a4d49 100644 --- a/ct/tududi.sh +++ b/ct/tududi.sh @@ -34,7 +34,7 @@ function update_script() { systemctl stop tududi msg_ok "Stopped Service" - msg_info "Upadting ${APP}" + msg_info "Updating ${APP}" cp /opt/"$APP"/backend/.env /opt/"$APP".env rm -rf /opt/"$APP" fetch_and_deploy_gh_release "tududi" "chrisvel/tududi" From 063bed19c1640c92d627ba2e2245fd19f633bddc Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Sun, 20 Jul 2025 13:23:07 +0200 Subject: [PATCH 0038/1733] Update ots-install.sh --- install/ots-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/ots-install.sh b/install/ots-install.sh index 437e30101..2c6b5f24f 100644 --- a/install/ots-install.sh +++ b/install/ots-install.sh @@ -22,7 +22,7 @@ msg_ok "Installed Dependencies" msg_info "Installing OTS" fetch_and_deploy_gh_release "ots" "Luzifer/ots" "tarball" "latest" "/opt/ots" "ots_linux_amd64.tgz" -cat </opt/ots/env" +cat </opt/ots/env LISTEN=0.0.0.0:3000 REDIS_URL=redis://127.0.0.1:6379 SECRET_EXPIRY=604800 From ea6bffa79f0eea9d5bbf34c5d6220d5cf69bb580 Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Sun, 20 Jul 2025 13:23:52 +0200 Subject: [PATCH 0039/1733] Update ots-install.sh --- install/ots-install.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/install/ots-install.sh b/install/ots-install.sh index 2c6b5f24f..8ff327680 100644 --- a/install/ots-install.sh +++ b/install/ots-install.sh @@ -15,9 +15,7 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - caddy \ - apt-transport-https \ - ca-certificates + redis-server msg_ok "Installed Dependencies" msg_info "Installing OTS" From d2f96c89b7f0352b4f95cb25d2cb2eb0b512c77b Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Sun, 20 Jul 2025 14:06:34 +0200 Subject: [PATCH 0040/1733] Update ots-install.sh --- install/ots-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/ots-install.sh b/install/ots-install.sh index 8ff327680..7c243b370 100644 --- a/install/ots-install.sh +++ b/install/ots-install.sh @@ -19,7 +19,7 @@ $STD apt-get install -y \ msg_ok "Installed Dependencies" msg_info "Installing OTS" -fetch_and_deploy_gh_release "ots" "Luzifer/ots" "tarball" "latest" "/opt/ots" "ots_linux_amd64.tgz" +fetch_and_deploy_gh_release "ots" "Luzifer/ots" "prebuild" "latest" "/opt/ots" "ots_linux_amd64.tgz" cat </opt/ots/env LISTEN=0.0.0.0:3000 REDIS_URL=redis://127.0.0.1:6379 From 00c131f7aaf2b7378c7c90959a546fa57efb36a6 Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Sun, 20 Jul 2025 14:21:23 +0200 Subject: [PATCH 0041/1733] Update tools.func --- misc/tools.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/tools.func b/misc/tools.func index 6973e4c6d..92a5bac55 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -968,7 +968,7 @@ function fetch_and_deploy_gh_release() { $STD apt-get install -y unzip fi unzip -q "$tmpdir/$filename" -d "$unpack_tmp" - elif [[ "$filename" == *.tar.* ]]; then + elif [[ "$filename" == *.tar.* || "$filename" == *.tgz ]]; then tar -xf "$tmpdir/$filename" -C "$unpack_tmp" else msg_error "Unsupported archive format: $filename" From 8ba183c6319b80af4c7f2cb98612258d3824a17a Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Sun, 20 Jul 2025 15:14:27 +0200 Subject: [PATCH 0042/1733] Update ots-install.sh --- install/ots-install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install/ots-install.sh b/install/ots-install.sh index 7c243b370..514ce80d3 100644 --- a/install/ots-install.sh +++ b/install/ots-install.sh @@ -44,6 +44,7 @@ RestartSecs=5 [Install] WantedBy=multi-user.target EOF +systemctl enable -q --now ots msg_ok "Created Services" motd_ssh From af1c0726cbd357bb6b9f832e8d7d2b222d530c3a Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Sun, 20 Jul 2025 15:15:21 +0200 Subject: [PATCH 0043/1733] Update ots-install.sh --- install/ots-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/ots-install.sh b/install/ots-install.sh index 514ce80d3..da8e1d550 100644 --- a/install/ots-install.sh +++ b/install/ots-install.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # Copyright (c) 2021-2025 community-scripts ORG -# Author: MickLesk (CanbiZ) +# Author: bvberg01 # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/Luzifer/ots From 34113208ffd757a6106049a168ce466e50774878 Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Sun, 20 Jul 2025 15:40:27 +0200 Subject: [PATCH 0044/1733] Update ots.sh --- ct/ots.sh | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/ct/ots.sh b/ct/ots.sh index d089bd804..e3e03df99 100644 --- a/ct/ots.sh +++ b/ct/ots.sh @@ -1,12 +1,12 @@ #!/usr/bin/env bash source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG -# Author: BvdBerg01 +# Author: bvdberg01 # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://github.com/Luzifer/ots APP="OTS" -var_tags="${var_tags:-analytics}" +var_tags="${var_tags:-secrets-sharer}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-3}" @@ -27,10 +27,25 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - msg_info "Updating $APP LXC" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "Updated $APP LXC" + + RELEASE=$(curl -fsSL https://api.github.com/repos/Luzifer/ots/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then + + msg_info "Stopping ${APP} Service" + systemctl stop ots + msg_ok "Stopped ${APP} Service" + + msg_info "Updating ${APP} to v${RELEASE}" + fetch_and_deploy_gh_release "ots" "Luzifer/ots" "prebuild" "latest" "/opt/ots" "ots_linux_amd64.tgz" + msg_ok "Updated ${APP} to v${RELEASE}" + + msg_info "Stopping ${APP} Service" + systemctl start ots + msg_ok "Stopped ${APP} Service" + + else + msg_ok "No update required. ${APP} is already at ${RELEASE}" + fi exit } From 41b616ae2afa1d2c06084309786250f1d5055ed6 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 07:44:24 +0200 Subject: [PATCH 0045/1733] Update auto-update-app-headers.yml --- .github/workflows/auto-update-app-headers.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/auto-update-app-headers.yml b/.github/workflows/auto-update-app-headers.yml index 339ed4c4a..30bc5f429 100644 --- a/.github/workflows/auto-update-app-headers.yml +++ b/.github/workflows/auto-update-app-headers.yml @@ -24,13 +24,18 @@ jobs: with: app-id: ${{ vars.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} + owner: community-scripts + repositories: ProxmoxVED + - name: Generate a token for PR approval and merge id: generate-token-merge uses: actions/create-github-app-token@v2 with: - app-id: ${{ secrets.APP_ID_APPROVE_AND_MERGE }} - private-key: ${{ secrets.APP_KEY_APPROVE_AND_MERGE }} + app-id: ${{ vars.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + owner: community-scripts + repositories: ProxmoxVED # Step 1: Checkout repository - name: Checkout repository From a7e8207c41037d04d7cda0f5c86428af640e4704 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 21 Jul 2025 05:45:14 +0000 Subject: [PATCH 0046/1733] Update .app files --- ct/headers/cloudreve | 6 ++++++ ct/headers/leantime | 6 ++++++ ct/headers/nginxproxymanager | 6 ++++++ ct/headers/reactive-resume | 6 ------ ct/headers/salt | 6 ++++++ ct/headers/saltmaster | 6 ------ ct/headers/tududi | 6 ++++++ 7 files changed, 30 insertions(+), 12 deletions(-) create mode 100644 ct/headers/cloudreve create mode 100644 ct/headers/leantime create mode 100644 ct/headers/nginxproxymanager delete mode 100644 ct/headers/reactive-resume create mode 100644 ct/headers/salt delete mode 100644 ct/headers/saltmaster create mode 100644 ct/headers/tududi diff --git a/ct/headers/cloudreve b/ct/headers/cloudreve new file mode 100644 index 000000000..fb88b4ab2 --- /dev/null +++ b/ct/headers/cloudreve @@ -0,0 +1,6 @@ + ________ __ + / ____/ /___ __ ______/ /_______ _ _____ + / / / / __ \/ / / / __ / ___/ _ \ | / / _ \ +/ /___/ / /_/ / /_/ / /_/ / / / __/ |/ / __/ +\____/_/\____/\__,_/\__,_/_/ \___/|___/\___/ + diff --git a/ct/headers/leantime b/ct/headers/leantime new file mode 100644 index 000000000..048d615df --- /dev/null +++ b/ct/headers/leantime @@ -0,0 +1,6 @@ + __ __ _ + / / ___ ____ _____ / /_(_)___ ___ ___ + / / / _ \/ __ `/ __ \/ __/ / __ `__ \/ _ \ + / /___/ __/ /_/ / / / / /_/ / / / / / / __/ +/_____/\___/\__,_/_/ /_/\__/_/_/ /_/ /_/\___/ + diff --git a/ct/headers/nginxproxymanager b/ct/headers/nginxproxymanager new file mode 100644 index 000000000..d68d0c9d8 --- /dev/null +++ b/ct/headers/nginxproxymanager @@ -0,0 +1,6 @@ + _ __ _ ____ __ ___ + / | / /___ _(_)___ _ __ / __ \_________ _ ____ __ / |/ /___ _____ ____ _____ ____ _____ + / |/ / __ `/ / __ \| |/_/ / /_/ / ___/ __ \| |/_/ / / / / /|_/ / __ `/ __ \/ __ `/ __ `/ _ \/ ___/ + / /| / /_/ / / / / /> < / ____/ / / /_/ /> Date: Mon, 21 Jul 2025 07:48:43 +0200 Subject: [PATCH 0047/1733] fixes --- .../workflows/backup/update_json_date.yml.bak | 2 ++ .github/workflows/changelog-pr.yaml | 4 +++ .github/workflows/delete_new_script.yaml | 2 ++ .github/workflows/get-versions-from-gh.yaml | 2 ++ .../get-versions-from-newreleases.yaml | 4 +++ .github/workflows/live/changelog-pr.yml | 26 ++++++++++--------- .github/workflows/live/update-json-date.yml | 10 ++++--- 7 files changed, 34 insertions(+), 16 deletions(-) diff --git a/.github/workflows/backup/update_json_date.yml.bak b/.github/workflows/backup/update_json_date.yml.bak index cb9bc8559..236a14b80 100644 --- a/.github/workflows/backup/update_json_date.yml.bak +++ b/.github/workflows/backup/update_json_date.yml.bak @@ -21,6 +21,8 @@ jobs: with: app-id: ${{ vars.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} + owner: community-scripts + repositories: ProxmoxVED - name: Checkout repository uses: actions/checkout@v4 diff --git a/.github/workflows/changelog-pr.yaml b/.github/workflows/changelog-pr.yaml index ca187dd5c..300ce186d 100644 --- a/.github/workflows/changelog-pr.yaml +++ b/.github/workflows/changelog-pr.yaml @@ -23,6 +23,8 @@ jobs: with: app-id: ${{ vars.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} + owner: community-scripts + repositories: ProxmoxVED - name: Generate a token for PR approval and merge id: generate-token-merge @@ -30,6 +32,8 @@ jobs: with: app-id: ${{ secrets.APP_ID_APPROVE_AND_MERGE }} private-key: ${{ secrets.APP_KEY_APPROVE_AND_MERGE }} + owner: community-scripts + repositories: ProxmoxVED - name: Checkout code uses: actions/checkout@v4 diff --git a/.github/workflows/delete_new_script.yaml b/.github/workflows/delete_new_script.yaml index 9d9f97d61..468573c18 100644 --- a/.github/workflows/delete_new_script.yaml +++ b/.github/workflows/delete_new_script.yaml @@ -20,6 +20,8 @@ jobs: with: app-id: ${{ secrets.APP_ID_APPROVE_AND_MERGE }} private-key: ${{ secrets.APP_KEY_APPROVE_AND_MERGE }} + owner: community-scripts + repositories: ProxmoxVED - name: Extract Issue Title (Lowercase & Underscores) id: extract_title diff --git a/.github/workflows/get-versions-from-gh.yaml b/.github/workflows/get-versions-from-gh.yaml index d3cd7e366..d45532e21 100644 --- a/.github/workflows/get-versions-from-gh.yaml +++ b/.github/workflows/get-versions-from-gh.yaml @@ -28,6 +28,8 @@ jobs: with: app-id: ${{ vars.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} + owner: community-scripts + repositories: ProxmoxVED - name: Crawl from Github API env: diff --git a/.github/workflows/get-versions-from-newreleases.yaml b/.github/workflows/get-versions-from-newreleases.yaml index aabfae35e..9d06d6c6e 100644 --- a/.github/workflows/get-versions-from-newreleases.yaml +++ b/.github/workflows/get-versions-from-newreleases.yaml @@ -28,6 +28,8 @@ jobs: with: app-id: ${{ vars.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} + owner: community-scripts + repositories: ProxmoxVED - name: Generate a token for PR approval and merge id: generate-token-merge @@ -35,6 +37,8 @@ jobs: with: app-id: ${{ secrets.APP_ID_APPROVE_AND_MERGE }} private-key: ${{ secrets.APP_KEY_APPROVE_AND_MERGE }} + owner: community-scripts + repositories: ProxmoxVED - name: Crawl from newreleases.io env: diff --git a/.github/workflows/live/changelog-pr.yml b/.github/workflows/live/changelog-pr.yml index dc5bcd3d3..f3bb1cbd4 100644 --- a/.github/workflows/live/changelog-pr.yml +++ b/.github/workflows/live/changelog-pr.yml @@ -22,6 +22,8 @@ jobs: with: app-id: ${{ vars.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} + owner: community-scripts + repositories: ProxmoxVED - name: Checkout code uses: actions/checkout@v4 @@ -109,7 +111,7 @@ jobs: ); if (updateScriptsCategory) { - + const subCategory = updateScriptsCategory.subCategories.find(sub => sub.labels.some(label => prLabels.includes(label)) ); @@ -121,7 +123,7 @@ jobs: } } }); - + console.log(JSON.stringify(categorizedPRs, null, 2)); return categorizedPRs; @@ -147,30 +149,30 @@ jobs: const hasSubcategories = subCategories && subCategories.length > 0; const hasMainNotes = notes.length > 0; const hasSubNotes = hasSubcategories && subCategories.some(sub => sub.notes && sub.notes.length > 0); - - + + if (hasMainNotes || hasSubNotes) { newReleaseNotes += `### ${title}\n\n`; } - + if (hasMainNotes) { newReleaseNotes += ` ${notes.join("\n")}\n\n`; } if (hasSubcategories) { for (const { title: subTitle, notes: subNotes } of subCategories) { if (subNotes && subNotes.length > 0) { - newReleaseNotes += ` - #### ${subTitle}\n\n`; - newReleaseNotes += ` ${subNotes.join("\n ")}\n\n`; + newReleaseNotes += ` - #### ${subTitle}\n\n`; + newReleaseNotes += ` ${subNotes.join("\n ")}\n\n`; } } } - } - + } + const changelogContent = await fs.readFile(changelogPath, 'utf-8'); const changelogIncludesTodaysReleaseNotes = changelogContent.includes(`\n## ${today}`); - const regex = changelogIncludesTodaysReleaseNotes - ? new RegExp(`## ${today}.*(?=## ${latestDateInChangelog})`, "gs") + const regex = changelogIncludesTodaysReleaseNotes + ? new RegExp(`## ${today}.*(?=## ${latestDateInChangelog})`, "gs") : new RegExp(`(?=## ${latestDateInChangelog})`, "gs"); const newChangelogContent = changelogContent.replace(regex, newReleaseNotes); @@ -223,4 +225,4 @@ jobs: PR_NUMBER=$(gh pr list --head "${BRANCH_NAME}" --json number --jq '.[].number') if [ -n "$PR_NUMBER" ]; then gh pr review $PR_NUMBER --approve - fi \ No newline at end of file + fi diff --git a/.github/workflows/live/update-json-date.yml b/.github/workflows/live/update-json-date.yml index 7e9c24973..861c4ee0d 100644 --- a/.github/workflows/live/update-json-date.yml +++ b/.github/workflows/live/update-json-date.yml @@ -6,7 +6,7 @@ on: - main paths: - 'json/**.json' - workflow_dispatch: + workflow_dispatch: jobs: update-app-files: @@ -23,11 +23,13 @@ jobs: with: app-id: ${{ vars.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} + owner: community-scripts + repositories: ProxmoxVED - name: Generate dynamic branch name id: timestamp run: echo "BRANCH_NAME=pr-update-json-$(date +'%Y%m%d%H%M%S')" >> $GITHUB_ENV - + - name: Set up GH_TOKEN env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -102,8 +104,8 @@ jobs: - name: Commit and create PR if changes exist if: env.changed == 'true' run: | - - + + git commit -m "Update date in json" git checkout -b ${{ env.BRANCH_NAME }} git push origin ${{ env.BRANCH_NAME }} From 23119da919344e6fd4c162aafc40d2e3fb149024 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 07:50:03 +0200 Subject: [PATCH 0048/1733] rm cloudreve --- ct/cloudreve.sh | 58 ----------------------------- frontend/public/json/cloudreve.json | 40 -------------------- install/cloudreve-install.sh | 43 --------------------- 3 files changed, 141 deletions(-) delete mode 100644 ct/cloudreve.sh delete mode 100644 frontend/public/json/cloudreve.json delete mode 100644 install/cloudreve-install.sh diff --git a/ct/cloudreve.sh b/ct/cloudreve.sh deleted file mode 100644 index ce5ee2966..000000000 --- a/ct/cloudreve.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env bash -source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: MickLesk (CanbiZ) -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://cloudreve.org/ - -APP="Cloudreve" -var_tags="${var_tags:-cloud}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-1024}" -var_disk="${var_disk:-10}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - - if [[ ! -d /opt/cloudreve ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - RELEASE=$(curl -fsSL https://api.github.com/repos/cloudreve/cloudreve/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - if [[ "${RELEASE}" != "$(cat ~/.cloudreve 2>/dev/null)" ]] || [[ ! -f ~/.cloudreve ]]; then - msg_info "Stopping $APP" - systemctl stop cloudreve - msg_ok "Stopped $APP" - - fetch_and_deploy_gh_release "cloudreve" "cloudreve/cloudreve" "prebuild" "latest" "/opt/cloudreve" "*linux_amd64.tar.gz" - - msg_info "Starting $APP" - systemctl start cloudreve - msg_ok "Started $APP" - - msg_ok "Update Successful" - else - msg_ok "No update required. ${APP} is already at v${RELEASE}" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5212${CL}" diff --git a/frontend/public/json/cloudreve.json b/frontend/public/json/cloudreve.json deleted file mode 100644 index 61fa2ce0b..000000000 --- a/frontend/public/json/cloudreve.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "Cloudreve", - "slug": "cloudreve", - "categories": [ - 12 - ], - "date_created": "2025-07-17", - "type": "ct", - "updateable": false, - "privileged": false, - "interface_port": 5212, - "documentation": "https://docs.cloudreve.org/en/", - "website": "https://cloudreve.org/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/cloudreve.webp", - "config_path": "/opt/cloudreve/data/config.ini", - "description": "Cloudreve is an open-source, community-driven cloud storage system that provides file sharing, synchronization, and management features. It supports a wide range of storage backends and integrates with various notification and logging platforms.", - "install_methods": [ - { - "type": "default", - "script": "ct/cloudreve.sh", - "resources": { - "cpu": 1, - "ram": 1024, - "hdd": 10, - "os": "Debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "After Installation: Register your user -> Login -> Dashboard -> Accept Primary URL.", - "type": "warn" - } - ] -} diff --git a/install/cloudreve-install.sh b/install/cloudreve-install.sh deleted file mode 100644 index c7ef56fa2..000000000 --- a/install/cloudreve-install.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: MickLesk (CanbiZ) -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://cloudreve.org/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -fetch_and_deploy_gh_release "cloudreve" "cloudreve/cloudreve" "prebuild" "latest" "/opt/cloudreve" "*linux_amd64.tar.gz" - -msg_info "Setup Service" -cat </etc/systemd/system/cloudreve.service -[Unit] -Description=Cloudreve Service -After=network.target - -[Service] -Type=simple -ExecStart=/opt/cloudreve/cloudreve -Restart=on-failure -RestartSec=5 - -[Install] -WantedBy=multi-user.target -EOF - -systemctl enable -q --now cloudreve -msg_ok "Service Setup" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" From e23e84dd22a9a658c8928e3f9367406bf936f082 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 07:50:46 +0200 Subject: [PATCH 0049/1733] token v2 --- .github/workflows/backup/update_json_date.yml.bak | 2 +- .github/workflows/changelog-pr.yaml | 4 ++-- .github/workflows/delete_new_script.yaml | 2 +- .github/workflows/get-versions-from-gh.yaml | 2 +- .github/workflows/get-versions-from-newreleases.yaml | 4 ++-- .github/workflows/live/changelog-pr.yml | 2 +- .github/workflows/live/update-json-date.yml | 2 +- .github/workflows/move-to-main-repo.yaml | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/backup/update_json_date.yml.bak b/.github/workflows/backup/update_json_date.yml.bak index 236a14b80..710125285 100644 --- a/.github/workflows/backup/update_json_date.yml.bak +++ b/.github/workflows/backup/update_json_date.yml.bak @@ -17,7 +17,7 @@ jobs: steps: - name: Generate a token id: generate-token - uses: actions/create-github-app-token@v1 + uses: actions/create-github-app-token@v2 with: app-id: ${{ vars.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} diff --git a/.github/workflows/changelog-pr.yaml b/.github/workflows/changelog-pr.yaml index 300ce186d..80959d54e 100644 --- a/.github/workflows/changelog-pr.yaml +++ b/.github/workflows/changelog-pr.yaml @@ -19,7 +19,7 @@ jobs: steps: - name: Generate a token for PR creation id: generate-token - uses: actions/create-github-app-token@v1 + uses: actions/create-github-app-token@v2 with: app-id: ${{ vars.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} @@ -28,7 +28,7 @@ jobs: - name: Generate a token for PR approval and merge id: generate-token-merge - uses: actions/create-github-app-token@v1 + uses: actions/create-github-app-token@v2 with: app-id: ${{ secrets.APP_ID_APPROVE_AND_MERGE }} private-key: ${{ secrets.APP_KEY_APPROVE_AND_MERGE }} diff --git a/.github/workflows/delete_new_script.yaml b/.github/workflows/delete_new_script.yaml index 468573c18..d538437a5 100644 --- a/.github/workflows/delete_new_script.yaml +++ b/.github/workflows/delete_new_script.yaml @@ -16,7 +16,7 @@ jobs: - name: Generate a token for PR approval and merge id: generate-token-merge - uses: actions/create-github-app-token@v1 + uses: actions/create-github-app-token@v2 with: app-id: ${{ secrets.APP_ID_APPROVE_AND_MERGE }} private-key: ${{ secrets.APP_KEY_APPROVE_AND_MERGE }} diff --git a/.github/workflows/get-versions-from-gh.yaml b/.github/workflows/get-versions-from-gh.yaml index d45532e21..0cf632d11 100644 --- a/.github/workflows/get-versions-from-gh.yaml +++ b/.github/workflows/get-versions-from-gh.yaml @@ -24,7 +24,7 @@ jobs: - name: Generate a token id: generate-token - uses: actions/create-github-app-token@v1 + uses: actions/create-github-app-token@v2 with: app-id: ${{ vars.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} diff --git a/.github/workflows/get-versions-from-newreleases.yaml b/.github/workflows/get-versions-from-newreleases.yaml index 9d06d6c6e..634a0c1b4 100644 --- a/.github/workflows/get-versions-from-newreleases.yaml +++ b/.github/workflows/get-versions-from-newreleases.yaml @@ -24,7 +24,7 @@ jobs: - name: Generate a token id: generate-token - uses: actions/create-github-app-token@v1 + uses: actions/create-github-app-token@v2 with: app-id: ${{ vars.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} @@ -33,7 +33,7 @@ jobs: - name: Generate a token for PR approval and merge id: generate-token-merge - uses: actions/create-github-app-token@v1 + uses: actions/create-github-app-token@v2 with: app-id: ${{ secrets.APP_ID_APPROVE_AND_MERGE }} private-key: ${{ secrets.APP_KEY_APPROVE_AND_MERGE }} diff --git a/.github/workflows/live/changelog-pr.yml b/.github/workflows/live/changelog-pr.yml index f3bb1cbd4..87da55143 100644 --- a/.github/workflows/live/changelog-pr.yml +++ b/.github/workflows/live/changelog-pr.yml @@ -18,7 +18,7 @@ jobs: steps: - name: Generate a token id: generate-token - uses: actions/create-github-app-token@v1 + uses: actions/create-github-app-token@v2 with: app-id: ${{ vars.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} diff --git a/.github/workflows/live/update-json-date.yml b/.github/workflows/live/update-json-date.yml index 861c4ee0d..1bf965a44 100644 --- a/.github/workflows/live/update-json-date.yml +++ b/.github/workflows/live/update-json-date.yml @@ -19,7 +19,7 @@ jobs: steps: - name: Generate a token id: generate-token - uses: actions/create-github-app-token@v1 + uses: actions/create-github-app-token@v2 with: app-id: ${{ vars.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} diff --git a/.github/workflows/move-to-main-repo.yaml b/.github/workflows/move-to-main-repo.yaml index 9c9fb65dd..dfddeff73 100644 --- a/.github/workflows/move-to-main-repo.yaml +++ b/.github/workflows/move-to-main-repo.yaml @@ -18,7 +18,7 @@ jobs: steps: - name: Generate a token id: app-token - uses: actions/create-github-app-token@v1 + uses: actions/create-github-app-token@v2 with: app-id: ${{ vars.PUSH_MAIN_APP_ID }} private-key: ${{ secrets.PUSH_MAIN_APP_SECRET }} From 687890f663f923bca8ac7b8f6177e2c6a995a3f7 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 09:14:51 +0200 Subject: [PATCH 0050/1733] rm docker --- ct/docker.sh | 92 ------------------------------ install/docker-install.sh | 114 -------------------------------------- 2 files changed, 206 deletions(-) delete mode 100644 ct/docker.sh delete mode 100644 install/docker-install.sh diff --git a/ct/docker.sh b/ct/docker.sh deleted file mode 100644 index 123d0c7c2..000000000 --- a/ct/docker.sh +++ /dev/null @@ -1,92 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://www.docker.com/ - -APP="Docker" -var_tags="${var_tags:-docker}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-4}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - - get_latest_release() { - curl -fsSL https://api.github.com/repos/"$1"/releases/latest | grep '"tag_name":' | cut -d'"' -f4 - } - - msg_info "Updating base system" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "Base system updated" - - msg_info "Updating Docker Engine" - $STD apt-get install --only-upgrade -y docker-ce docker-ce-cli containerd.io - msg_ok "Docker Engine updated" - - if [[ -f /usr/local/lib/docker/cli-plugins/docker-compose ]]; then - COMPOSE_BIN="/usr/local/lib/docker/cli-plugins/docker-compose" - COMPOSE_NEW_VERSION=$(get_latest_release "docker/compose") - msg_info "Updating Docker Compose to $COMPOSE_NEW_VERSION" - curl -fsSL "https://github.com/docker/compose/releases/download/${COMPOSE_NEW_VERSION}/docker-compose-$(uname -s)-$(uname -m)" \ - -o "$COMPOSE_BIN" - chmod +x "$COMPOSE_BIN" - msg_ok "Docker Compose updated" - fi - - if docker ps -a --format '{{.Names}}' | grep -q '^portainer$'; then - msg_info "Updating Portainer" - docker pull portainer/portainer-ce:latest - docker stop portainer && docker rm portainer - docker volume create portainer_data >/dev/null 2>&1 - $STD docker run -d \ - -p 8000:8000 \ - -p 9443:9443 \ - --name=portainer \ - --restart=always \ - -v /var/run/docker.sock:/var/run/docker.sock \ - -v portainer_data:/data \ - portainer/portainer-ce:latest - msg_ok "Updated Portainer" - fi - - if docker ps -a --format '{{.Names}}' | grep -q '^portainer_agent$'; then - msg_info "Updating Portainer Agent" - docker pull portainer/agent:latest - docker stop portainer_agent && docker rm portainer_agent - $STD docker run -d \ - -p 9001:9001 \ - --name=portainer_agent \ - --restart=always \ - -v /var/run/docker.sock:/var/run/docker.sock \ - -v /var/lib/docker/volumes:/var/lib/docker/volumes \ - portainer/agent - msg_ok "Updated Portainer Agent" - fi - - msg_info "Cleaning up" - $STD apt-get -y autoremove - $STD apt-get -y autoclean - msg_ok "Cleanup complete" - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" diff --git a/install/docker-install.sh b/install/docker-install.sh deleted file mode 100644 index 238a84046..000000000 --- a/install/docker-install.sh +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://www.docker.com/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -get_latest_release() { - curl -fsSL https://api.github.com/repos/"$1"/releases/latest | grep '"tag_name":' | cut -d'"' -f4 -} - -DOCKER_LATEST_VERSION=$(get_latest_release "moby/moby") -PORTAINER_LATEST_VERSION=$(get_latest_release "portainer/portainer") -PORTAINER_AGENT_LATEST_VERSION=$(get_latest_release "portainer/agent") -DOCKER_COMPOSE_LATEST_VERSION=$(get_latest_release "docker/compose") - -msg_info "Installing Docker $DOCKER_LATEST_VERSION" -DOCKER_CONFIG_PATH='/etc/docker/daemon.json' -mkdir -p $(dirname $DOCKER_CONFIG_PATH) -echo -e '{\n "log-driver": "journald"\n}' >/etc/docker/daemon.json -$STD sh <(curl -fsSL https://get.docker.com) -msg_ok "Installed Docker $DOCKER_LATEST_VERSION" - -read -r -p "${TAB3}Install Docker Compose v2 plugin? " prompt_compose -if [[ ${prompt_compose,,} =~ ^(y|yes)$ ]]; then - msg_info "Installing Docker Compose $DOCKER_COMPOSE_LATEST_VERSION" - mkdir -p /usr/local/lib/docker/cli-plugins - curl -fsSL "https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_LATEST_VERSION}/docker-compose-$(uname -s)-$(uname -m)" \ - -o /usr/local/lib/docker/cli-plugins/docker-compose - chmod +x /usr/local/lib/docker/cli-plugins/docker-compose - msg_ok "Installed Docker Compose $DOCKER_COMPOSE_LATEST_VERSION" -fi - -read -r -p "${TAB3}Would you like to add Portainer (UI)? " prompt -if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then - msg_info "Installing Portainer $PORTAINER_LATEST_VERSION" - docker volume create portainer_data >/dev/null - $STD docker run -d \ - -p 8000:8000 \ - -p 9443:9443 \ - --name=portainer \ - --restart=always \ - -v /var/run/docker.sock:/var/run/docker.sock \ - -v portainer_data:/data \ - portainer/portainer-ce:latest - msg_ok "Installed Portainer $PORTAINER_LATEST_VERSION" -else - read -r -p "${TAB3}Would you like to install the Portainer Agent (for remote management)? " prompt_agent - if [[ ${prompt_agent,,} =~ ^(y|yes)$ ]]; then - msg_info "Installing Portainer Agent $PORTAINER_AGENT_LATEST_VERSION" - $STD docker run -d \ - -p 9001:9001 \ - --name portainer_agent \ - --restart=always \ - -v /var/run/docker.sock:/var/run/docker.sock \ - -v /var/lib/docker/volumes:/var/lib/docker/volumes \ - portainer/agent - msg_ok "Installed Portainer Agent $PORTAINER_AGENT_LATEST_VERSION" - fi -fi - -read -r -p "${TAB3}Expose Docker TCP socket (insecure) ? [n = No, l = Local only (127.0.0.1), a = All interfaces (0.0.0.0)] : " socket_choice -case "${socket_choice,,}" in - l) - socket="tcp://127.0.0.1:2375" - ;; - a) - socket="tcp://0.0.0.0:2375" - ;; - *) - socket="" - ;; -esac - -if [[ -n "$socket" ]]; then - msg_info "Enabling Docker TCP socket on $socket" - $STD apt-get install -y jq - - tmpfile=$(mktemp) - jq --arg sock "$socket" '. + { "hosts": ["unix:///var/run/docker.sock", $sock] }' /etc/docker/daemon.json > "$tmpfile" && mv "$tmpfile" /etc/docker/daemon.json - - mkdir -p /etc/systemd/system/docker.service.d - cat < /etc/systemd/system/docker.service.d/override.conf -[Service] -ExecStart= -ExecStart=/usr/bin/dockerd -EOF - - $STD systemctl daemon-reexec - $STD systemctl daemon-reload - - if systemctl restart docker; then - msg_ok "Docker TCP socket available on $socket" - else - msg_error "Docker failed to restart. Check journalctl -xeu docker.service" - exit 1 - fi -fi - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" From 3e562048a6f2a749cb0269128beb0d7f7b1b3694 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 21 Jul 2025 07:15:21 +0000 Subject: [PATCH 0051/1733] Update .app files --- ct/headers/cloudreve | 6 ------ ct/headers/docker | 6 ------ 2 files changed, 12 deletions(-) delete mode 100644 ct/headers/cloudreve delete mode 100644 ct/headers/docker diff --git a/ct/headers/cloudreve b/ct/headers/cloudreve deleted file mode 100644 index fb88b4ab2..000000000 --- a/ct/headers/cloudreve +++ /dev/null @@ -1,6 +0,0 @@ - ________ __ - / ____/ /___ __ ______/ /_______ _ _____ - / / / / __ \/ / / / __ / ___/ _ \ | / / _ \ -/ /___/ / /_/ / /_/ / /_/ / / / __/ |/ / __/ -\____/_/\____/\__,_/\__,_/_/ \___/|___/\___/ - diff --git a/ct/headers/docker b/ct/headers/docker deleted file mode 100644 index 907ffbaef..000000000 --- a/ct/headers/docker +++ /dev/null @@ -1,6 +0,0 @@ - ____ __ - / __ \____ _____/ /_____ _____ - / / / / __ \/ ___/ //_/ _ \/ ___/ - / /_/ / /_/ / /__/ ,< / __/ / -/_____/\____/\___/_/|_|\___/_/ - From 715bd48f9f7a2aed3fe8d7477369d031971a4b5c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 09:27:23 +0200 Subject: [PATCH 0052/1733] docspell --- ct/docspell.sh | 2 +- install/docspell-install.sh | 60 +++++++++---------------------------- 2 files changed, 15 insertions(+), 47 deletions(-) diff --git a/ct/docspell.sh b/ct/docspell.sh index 7fcdc0142..3ff1cd3bd 100644 --- a/ct/docspell.sh +++ b/ct/docspell.sh @@ -3,7 +3,7 @@ source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/ # Copyright (c) 2021-2025 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: +# Source: https://github.com/community-scripts/ProxmoxVE APP="Docspell" var_tags="${var_tags:-document}" diff --git a/install/docspell-install.sh b/install/docspell-install.sh index b9bb74ad8..14d908fc4 100644 --- a/install/docspell-install.sh +++ b/install/docspell-install.sh @@ -20,11 +20,8 @@ msg_ok "Setup Functions" msg_info "Installing Dependencies (Patience)" $STD apt-get install -y \ htop \ - gnupg2 \ ca-certificates \ - default-jdk \ apt-transport-https \ - ghostscript \ tesseract-ocr \ tesseract-ocr-deu \ tesseract-ocr-eng \ @@ -34,13 +31,13 @@ $STD apt-get install -y \ ocrmypdf msg_ok "Installed Dependencies" -msg_info "Setting up PostgreSQL Repository" -curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor -o /etc/apt/trusted.gpg.d/postgresql.gpg -echo "deb https://apt.postgresql.org/pub/repos/apt bookworm-pgdg main" >/etc/apt/sources.list.d/pgdg.list -$STD apt-get update -msg_ok "Set up PostgreSQL Repository" -msg_info "Install/Set up PostgreSQL Database" +setup_gs +JAVA_VERSION="21" setup_java +POSTGRES_VERSION="16" setup_postgres +setup_yq + +msg_info "Set up PostgreSQL Database" $STD apt-get install -y postgresql-16 DB_NAME=docspell_db DB_USER=docspell_usr @@ -58,23 +55,15 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" } >>~/docspell.creds msg_ok "Set up PostgreSQL Database" -msg_info "Setup Docspell (Patience)" -mkdir -p /opt/docspell -Docspell=$(curl -fsSL https://github.com/eikek/docspell/releases/latest -o - | grep "title>Release" | cut -d " " -f 5) -DocspellDSC=$(curl -fsSL https://github.com/docspell/dsc/releases/latest -o - | grep "title>Release" | cut -d " " -f 4 | sed 's/^v//') -cd /opt -curl -fsSL https://github.com/eikek/docspell/releases/download/v${Docspell}/docspell-joex_${Docspell}_all.deb -o docspell-joex_${Docspell}_all.deb -curl -fsSL https://github.com/eikek/docspell/releases/download/v${Docspell}/docspell-restserver_${Docspell}_all.deb -o docspell-restserver_${Docspell}_all.deb -$STD dpkg -i docspell-*.deb -curl -fsSL https://github.com/docspell/dsc/releases/download/v${DocspellDSC}/dsc_amd64-musl-${DocspellDSC} -o dsc_amd64-musl-${DocspellDSC} -mv dsc_amd* dsc -chmod +x dsc -mv dsc /usr/bin + +fetch_and_deploy_gh_release "docspell-joex" "eikek/docspell" "binary" "latest" "/opt/docspell-joex" "docspell-joex_*all.deb" +fetch_and_deploy_gh_release "docspell-restserver" "eikek/docspell" "binary" "latest" "/opt/docspell-restserver" "docspell-restserver_*all.deb" +fetch_and_deploy_gh_release "docspell-dsc" "docspell/dsc" "singlefile" "latest" "/usr/bin" "dsc" + + + +msg_info "Setup Docspell" ln -s /etc/docspell-joex /opt/docspell/docspell-joex && ln -s /etc/docspell-restserver /opt/docspell/docspell-restserver && ln -s /usr/bin/dsc /opt/docspell/dsc -curl -fsSL https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -o /usr/bin/yq -chmod +x /usr/bin/yq -#JOEX_CONF="/usr/share/docspell-joex/conf/docspell-joex.conf" -#SERVER_CONF="/usr/share/docspell-restserver/conf/docspell-server.conf" sed -i \ -e '11s|localhost|'"$LOCAL_IP"'|' \ -e '17s|localhost|'"$LOCAL_IP"'|' \ @@ -94,27 +83,6 @@ sed -i \ -e '358s|password = .*|password = "'"$DB_PASS"'"|' \ -e '401s|url = .*|url = "jdbc:postgresql://localhost:5432/'"$DB_NAME"'"|' \ /usr/share/docspell-restserver/conf/docspell-server.conf - -# sed -i 's|address = "localhost"|address = "0.0.0.0"|' "$JOEX_CONF" "$SERVER_CONF" -# sed -i -E '/backend\s*\{/,/\}/ { -# /jdbc\s*\{/,/\}/ { -# s|(url\s*=\s*).*|\1"jdbc:postgresql://localhost:5432/'"$DB_NAME"'"|; -# s|(user\s*=\s*).*|\1"'"$DB_USER"'"|; -# s|(password\s*=\s*).*|\1"'"$DB_PASS"'"|; -# } -# }' "$SERVER_CONF" -# sed -i -E '/postgresql\s*\{/,/\}/ { -# /jdbc\s*\{/,/\}/ { -# s|(url\s*=\s*).*|\1"jdbc:postgresql://localhost:5432/'"$DB_NAME"'"|; -# s|(user\s*=\s*).*|\1"'"$DB_USER"'"|; -# s|(password\s*=\s*).*|\1"'"$DB_PASS"'"|; -# } -# }' "$SERVER_CONF" -# sed -i -E '/jdbc\s*\{/,/\}/ { -# s|(url\s*=\s*).*|\1"jdbc:postgresql://localhost:5432/'"$DB_NAME"'"|; -# s|(user\s*=\s*).*|\1"'"$DB_USER"'"|; -# s|(password\s*=\s*).*|\1"'"$DB_PASS"'"|; -# }' "$JOEX_CONF" msg_ok "Setup Docspell" msg_info "Setup Apache Solr" From 436026dc1cef5730d80d7a1f19ddc517f345b2bd Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 11:44:41 +0200 Subject: [PATCH 0053/1733] Update build.func --- misc/build.func | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/misc/build.func b/misc/build.func index 50664ea81..35c623edf 100644 --- a/misc/build.func +++ b/misc/build.func @@ -77,15 +77,37 @@ root_check() { # This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported. pve_check() { - if ! pveversion | grep -Eq "pve-manager/8\.[0-4](\.[0-9]+)*"; then - msg_error "${CROSS}${RD}This version of Proxmox Virtual Environment is not supported" - echo -e "Requires Proxmox Virtual Environment Version 8.1 or later." - echo -e "Exiting..." - sleep 2 - exit + local PVE_VER + PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" + + # 8 Version Check + if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then + local MINOR="${BASH_REMATCH[1]}" + if (( MINOR < 1 || MINOR > 4 )); then + msg_error "This version of Proxmox Virtual Environment is not supported" + echo -e "Requires Proxmox Virtual Environment Version 8.1 – 8.4" + echo -e "Exiting..." + sleep 2 + exit 1 + fi + return 0 fi + + # 9 Beta Version Check + if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then + msg_custom "Detected Proxmox Virtual Environment $PVE_VER – Beta state, use with caution!" + return 0 + fi + + # All others (unsupported versions) + msg_error "This version of Proxmox Virtual Environment is not supported" + echo -e "Requires Proxmox Virtual Environment Version 8.1 – 8.4 or 9.x (Beta)" + echo -e "Exiting..." + sleep 2 + exit 1 } + # When a node is running tens of containers, it's possible to exceed the kernel's cryptographic key storage allocations. # These are tuneable, so verify if the currently deployment is approaching the limits, advise the user on how to tune the limits, and exit the script. # https://cleveruptime.com/docs/files/proc-key-users | https://docs.kernel.org/security/keys/core.html From 8a875044f04e1eb0ccfea39b7ce4f77d7f18b034 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 11:59:23 +0200 Subject: [PATCH 0054/1733] testing & remove some things --- install/docspell-install.sh | 16 +- misc/backup_07052025/alpine-install copy.func | 277 ---- misc/backup_07052025/api.func.bak | 189 --- misc/backup_07052025/backup.func | 139 -- misc/backup_07052025/build copy.func | 1407 ----------------- misc/backup_07052025/core.func.bak | 220 --- misc/backup_07052025/dialogue.bak | 850 ---------- misc/backup_07052025/install copy.func | 281 ---- misc/backup_07052025/msg.func | 98 -- 9 files changed, 8 insertions(+), 3469 deletions(-) delete mode 100644 misc/backup_07052025/alpine-install copy.func delete mode 100644 misc/backup_07052025/api.func.bak delete mode 100644 misc/backup_07052025/backup.func delete mode 100644 misc/backup_07052025/build copy.func delete mode 100644 misc/backup_07052025/core.func.bak delete mode 100644 misc/backup_07052025/dialogue.bak delete mode 100644 misc/backup_07052025/install copy.func delete mode 100644 misc/backup_07052025/msg.func diff --git a/install/docspell-install.sh b/install/docspell-install.sh index 14d908fc4..d76983d2b 100644 --- a/install/docspell-install.sh +++ b/install/docspell-install.sh @@ -22,19 +22,19 @@ $STD apt-get install -y \ htop \ ca-certificates \ apt-transport-https \ - tesseract-ocr \ - tesseract-ocr-deu \ - tesseract-ocr-eng \ - unpaper \ - unoconv \ - wkhtmltopdf \ - ocrmypdf + tesseract-ocr + #tesseract-ocr-deu \ + #tesseract-ocr-eng \ + #unpaper \ + #unoconv \ + #wkhtmltopdf \ + #ocrmypdf msg_ok "Installed Dependencies" setup_gs JAVA_VERSION="21" setup_java -POSTGRES_VERSION="16" setup_postgres +POSTGRES_VERSION="16" setup_postgresql setup_yq msg_info "Set up PostgreSQL Database" diff --git a/misc/backup_07052025/alpine-install copy.func b/misc/backup_07052025/alpine-install copy.func deleted file mode 100644 index 71e7c1d45..000000000 --- a/misc/backup_07052025/alpine-install copy.func +++ /dev/null @@ -1,277 +0,0 @@ -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) -# Co-Author: MickLesk -# License: MIT -# https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE - -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) -load_functions - -# This function sets color variables for formatting output in the terminal -# color() { -# # Colors -# YW=$(echo "\033[33m") -# YWB=$(echo "\033[93m") -# BL=$(echo "\033[36m") -# RD=$(echo "\033[01;31m") -# GN=$(echo "\033[1;92m") - -# # Formatting -# CL=$(echo "\033[m") -# BFR="\\r\\033[K" -# BOLD=$(echo "\033[1m") -# TAB=" " - -# # System -# RETRY_NUM=10 -# RETRY_EVERY=3 -# i=$RETRY_NUM - -# # Icons -# CM="${TAB}✔️${TAB}${CL}" -# CROSS="${TAB}✖️${TAB}${CL}" -# INFO="${TAB}💡${TAB}${CL}" -# NETWORK="${TAB}📡${TAB}${CL}" -# OS="${TAB}🖥️${TAB}${CL}" -# OSVERSION="${TAB}🌟${TAB}${CL}" -# HOSTNAME="${TAB}🏠${TAB}${CL}" -# GATEWAY="${TAB}🌐${TAB}${CL}" -# DEFAULT="${TAB}⚙️${TAB}${CL}" -# } - -# Function to set STD mode based on verbosity -set_std_mode() { - if [ "$VERBOSE" = "yes" ]; then - STD="" - else - STD="silent" - fi -} - -# Silent execution function -silent() { - "$@" >/dev/null 2>&1 -} - -# This function enables IPv6 if it's not disabled and sets verbose mode -verb_ip6() { - set_std_mode # Set STD mode based on VERBOSE - - if [ "$DISABLEIPV6" == "yes" ]; then - $STD sysctl -w net.ipv6.conf.all.disable_ipv6=1 - echo "net.ipv6.conf.all.disable_ipv6 = 1" >>/etc/sysctl.conf - $STD rc-update add sysctl default - fi -} - -# This function catches errors and handles them with the error handler function -catch_errors() { - unset SPINNER_PID - set -Eeuo pipefail - trap 'error_handler $LINENO "$BASH_COMMAND"' ERR -} - -# This function handles errors -error_handler() { - local exit_code="$?" - local line_number="$1" - local command="$2" - local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" - echo -e "\n$error_message\n" - [[ -n "${SPINNER_PID:-}" ]] && kill "$SPINNER_PID" &>/dev/null || true -} - -# # This function displays an informational message with logging support. -# declare -A MSG_INFO_SHOWN -# SPINNER_ACTIVE=0 -# SPINNER_PID="" -# SPINNER_MSG="" - -# trap 'stop_spinner' EXIT INT TERM HUP - -# start_spinner() { -# local msg="$1" -# local frames=(⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏) -# local spin_i=0 -# local interval=0.1 - -# SPINNER_MSG="$msg" -# printf "\r\e[2K" >&2 - -# { -# while [[ "$SPINNER_ACTIVE" -eq 1 ]]; do -# printf "\r\e[2K%s %b" "${frames[spin_i]}" "${YW}${SPINNER_MSG}${CL}" >&2 -# spin_i=$(((spin_i + 1) % ${#frames[@]})) -# sleep "$interval" -# done -# } & - -# SPINNER_PID=$! -# disown "$SPINNER_PID" -# } - -# stop_spinner() { -# if [[ ${SPINNER_PID+v} && -n "$SPINNER_PID" ]] && kill -0 "$SPINNER_PID" 2>/dev/null; then -# kill "$SPINNER_PID" 2>/dev/null -# sleep 0.1 -# kill -0 "$SPINNER_PID" 2>/dev/null && kill -9 "$SPINNER_PID" 2>/dev/null -# wait "$SPINNER_PID" 2>/dev/null || true -# fi -# SPINNER_ACTIVE=0 -# unset SPINNER_PID -# } - -# spinner_guard() { -# if [[ "$SPINNER_ACTIVE" -eq 1 ]] && [[ -n "$SPINNER_PID" ]]; then -# kill "$SPINNER_PID" 2>/dev/null -# wait "$SPINNER_PID" 2>/dev/null || true -# SPINNER_ACTIVE=0 -# unset SPINNER_PID -# fi -# } - -# msg_info() { -# local msg="$1" -# [[ -n "${MSG_INFO_SHOWN["$msg"]+x}" ]] && return -# MSG_INFO_SHOWN["$msg"]=1 - -# spinner_guard -# SPINNER_ACTIVE=1 -# start_spinner "$msg" -# } - -# msg_ok() { -# local msg="$1" -# stop_spinner -# printf "\r\e[2K%s %b\n" "${CM}" "${GN}${msg}${CL}" >&2 -# unset MSG_INFO_SHOWN["$msg"] -# } - -# msg_error() { -# stop_spinner -# local msg="$1" -# printf "\r\e[2K%s %b\n" "${CROSS}" "${RD}${msg}${CL}" >&2 -# #log_message "ERROR" "$msg" -# } - -# This function sets up the Container OS by generating the locale, setting the timezone, and checking the network connection -setting_up_container() { - msg_info "Setting up Container OS" - while [ $i -gt 0 ]; do - if [ "$(ip addr show | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | cut -d'/' -f1)" != "" ]; then - break - fi - echo 1>&2 -en "${CROSS}${RD} No Network! " - sleep $RETRY_EVERY - i=$((i - 1)) - done - - if [ "$(ip addr show | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | cut -d'/' -f1)" = "" ]; then - echo 1>&2 -e "\n${CROSS}${RD} No Network After $RETRY_NUM Tries${CL}" - echo -e "${NETWORK}Check Network Settings" - exit 1 - fi - msg_ok "Set up Container OS" - msg_ok "Network Connected: ${BL}$(ip addr show | grep 'inet ' | awk '{print $2}' | cut -d'/' -f1 | tail -n1)${CL}" -} - -# This function checks the network connection by pinging a known IP address and prompts the user to continue if the internet is not connected -network_check() { - set +e - trap - ERR - if ping -c 1 -W 1 1.1.1.1 &>/dev/null || ping -c 1 -W 1 8.8.8.8 &>/dev/null || ping -c 1 -W 1 9.9.9.9 &>/dev/null; then - msg_ok "Internet Connected" - else - msg_error "Internet NOT Connected" - read -r -p "Would you like to continue anyway? " prompt - if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then - echo -e "${INFO}${RD}Expect Issues Without Internet${CL}" - else - echo -e "${NETWORK}Check Network Settings" - exit 1 - fi - fi - RESOLVEDIP=$(getent hosts github.com | awk '{ print $1 }') - if [[ -z "$RESOLVEDIP" ]]; then msg_error "DNS Lookup Failure"; else msg_ok "DNS Resolved github.com to ${BL}$RESOLVEDIP${CL}"; fi - set -e - trap 'error_handler $LINENO "$BASH_COMMAND"' ERR -} - -# This function updates the Container OS by running apt-get update and upgrade -update_os() { - msg_info "Updating Container OS" - $STD apk update - $STD apk upgrade - msg_ok "Updated Container OS" - - msg_info "Installing core dependencies" - $STD apk update - $STD apk add newt curl openssh nano mc ncurses - msg_ok "Core dependencies installed" -} - -# This function modifies the message of the day (motd) and SSH settings -motd_ssh() { - echo "export TERM='xterm-256color'" >>/root/.bashrc - IP=$(ip -4 addr show eth0 | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) - - if [ -f "/etc/os-release" ]; then - OS_NAME=$(grep ^NAME /etc/os-release | cut -d= -f2 | tr -d '"') - OS_VERSION=$(grep ^VERSION_ID /etc/os-release | cut -d= -f2 | tr -d '"') - else - OS_NAME="Alpine Linux" - OS_VERSION="Unknown" - fi - - PROFILE_FILE="/etc/profile.d/00_lxc-details.sh" - echo "echo -e \"\"" >"$PROFILE_FILE" - echo -e "echo -e \"${BOLD}${YW}${APPLICATION} LXC Container - DEV Repository${CL}\"" >>"$PROFILE_FILE" - echo -e "echo -e \"${RD}WARNING: This is a DEVELOPMENT version (ProxmoxVED). Do NOT use in production!${CL}\"" >>"$PROFILE_FILE" - echo -e "echo -e \"${YW} OS: ${GN}${OS_NAME} - Version: ${OS_VERSION}${CL}\"" >>"$PROFILE_FILE" - echo -e "echo -e \"${YW} Hostname: ${GN}\$(hostname)${CL}\"" >>"$PROFILE_FILE" - echo -e "echo -e \"${YW} IP Address: ${GN}${IP}${CL}\"" >>"$PROFILE_FILE" - echo -e "echo -e \"${YW} Repository: ${GN}https://github.com/community-scripts/ProxmoxVED${CL}\"" >>"$PROFILE_FILE" - echo "echo \"\"" >>"$PROFILE_FILE" - - if [[ "${SSH_ROOT}" == "yes" ]]; then - $STD rc-update add sshd - sed -i "s/#PermitRootLogin prohibit-password/PermitRootLogin yes/g" /etc/ssh/sshd_config - $STD /etc/init.d/sshd start - fi -} - -# Validate Timezone for some LXC's -validate_tz() { - [[ -f "/usr/share/zoneinfo/$1" ]] -} - -# This function customizes the container and enables passwordless login for the root user -customize() { - if [[ "$PASSWORD" == "" ]]; then - msg_info "Customizing Container" - passwd -d root >/dev/null 2>&1 - - # Ensure agetty is available - apk add --no-cache --force-broken-world util-linux >/dev/null 2>&1 - - # Create persistent autologin boot script - mkdir -p /etc/local.d - cat <<'EOF' >/etc/local.d/autologin.start -#!/bin/sh -sed -i 's|^tty1::respawn:.*|tty1::respawn:/sbin/agetty --autologin root --noclear tty1 38400 linux|' /etc/inittab -kill -HUP 1 -EOF - touch /root/.hushlogin - - chmod +x /etc/local.d/autologin.start - rc-update add local >/dev/null 2>&1 - - # Apply autologin immediately for current session - /etc/local.d/autologin.start - - msg_ok "Customized Container" - fi - - echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update - chmod +x /usr/bin/update -} diff --git a/misc/backup_07052025/api.func.bak b/misc/backup_07052025/api.func.bak deleted file mode 100644 index 2da17c1ba..000000000 --- a/misc/backup_07052025/api.func.bak +++ /dev/null @@ -1,189 +0,0 @@ -# Copyright (c) 2021-2025 community-scripts ORG -# Author: michelroegl-brunner -# License: MIT | https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/LICENSE - -get_error_description() { - local exit_code="$1" - case "$exit_code" in - 0) echo " " ;; - 1) echo "General error: An unspecified error occurred." ;; - 2) echo "Incorrect shell usage or invalid command arguments." ;; - 3) echo "Unexecuted function or invalid shell condition." ;; - 4) echo "Error opening a file or invalid path." ;; - 5) echo "I/O error: An input/output failure occurred." ;; - 6) echo "No such device or address." ;; - 7) echo "Insufficient memory or resource exhaustion." ;; - 8) echo "Non-executable file or invalid file format." ;; - 9) echo "Failed child process execution." ;; - 18) echo "Connection to a remote server failed." ;; - 22) echo "Invalid argument or faulty network connection." ;; - 28) echo "No space left on device." ;; - 35) echo "Timeout while establishing a connection." ;; - 56) echo "Faulty TLS connection." ;; - 60) echo "SSL certificate error." ;; - 100) echo "LXC install error: Unexpected error in create_lxc.sh." ;; - 101) echo "LXC install error: No network connection detected." ;; - 200) echo "LXC creation failed." ;; - 201) echo "LXC error: Invalid Storage class." ;; - 202) echo "User aborted menu in create_lxc.sh." ;; - 203) echo "CTID not set in create_lxc.sh." ;; - 204) echo "PCT_OSTYPE not set in create_lxc.sh." ;; - 205) echo "CTID cannot be less than 100 in create_lxc.sh." ;; - 206) echo "CTID already in use in create_lxc.sh." ;; - 207) echo "Template not found in create_lxc.sh." ;; - 208) echo "Error downloading template in create_lxc.sh." ;; - 209) echo "Container creation failed, but template is intact in create_lxc.sh." ;; - 125) echo "Docker error: Container could not start." ;; - 126) echo "Command not executable: Incorrect permissions or missing dependencies." ;; - 127) echo "Command not found: Incorrect path or missing dependency." ;; - 128) echo "Invalid exit signal, e.g., incorrect Git command." ;; - 129) echo "Signal 1 (SIGHUP): Process terminated due to hangup." ;; - 130) echo "Signal 2 (SIGINT): Manual termination via Ctrl+C." ;; - 132) echo "Signal 4 (SIGILL): Illegal machine instruction." ;; - 133) echo "Signal 5 (SIGTRAP): Debugging error or invalid breakpoint signal." ;; - 134) echo "Signal 6 (SIGABRT): Program aborted itself." ;; - 135) echo "Signal 7 (SIGBUS): Memory error, invalid memory address." ;; - 137) echo "Signal 9 (SIGKILL): Process forcibly terminated (OOM-killer or 'kill -9')." ;; - 139) echo "Signal 11 (SIGSEGV): Segmentation fault, possibly due to invalid pointer access." ;; - 141) echo "Signal 13 (SIGPIPE): Pipe closed unexpectedly." ;; - 143) echo "Signal 15 (SIGTERM): Process terminated normally." ;; - 152) echo "Signal 24 (SIGXCPU): CPU time limit exceeded." ;; - 255) echo "Unknown critical error, often due to missing permissions or broken scripts." ;; - *) echo "Unknown error code ($exit_code)." ;; - esac -} - -post_to_api() { - - if ! command -v curl &>/dev/null; then - return - fi - - if [ "$DIAGNOSTICS" = "no" ]; then - return - fi - - if [ -z "$RANDOM_UUID" ]; then - return - fi - - local API_URL="http://api.community-scripts.org/dev/upload" - local pve_version="not found" - pve_version=$(pveversion | awk -F'[/ ]' '{print $2}') - - JSON_PAYLOAD=$( - cat </dev/null; then - return - fi - - if [ "$POST_UPDATE_DONE" = true ]; then - return 0 - fi - exit_code=${2:-1} - local API_URL="http://api.community-scripts.org/dev/upload/updatestatus" - local status="${1:-failed}" - if [[ "$status" == "failed" ]]; then - local exit_code="${2:-1}" - elif [[ "$status" == "success" ]]; then - local exit_code="${2:-0}" - fi - - if [[ -z "$exit_code" ]]; then - exit_code=1 - fi - - error=$(get_error_description "$exit_code") - - if [ -z "$error" ]; then - error="Unknown error" - fi - - JSON_PAYLOAD=$( - cat <, sonst Arbeitsverzeichnis oder APP_DIR gesetzt vom Script - base_dir="${APP_DIR:-/opt/$app_name}" - if [[ ! -d "$base_dir" ]]; then - msg_error "Cannot determine base directory for $app_name" - return 1 - fi - - local snapshot_base="${base_dir}-snapshot" - SNAPSHOT_DIR="${snapshot_base}-$(date +%F_%T | tr ':' '-')" - - msg_info "Creating snapshot for $app_name" - - mkdir -p "$SNAPSHOT_DIR" - cp -a "$base_dir" "$SNAPSHOT_DIR/base" || { - msg_error "Failed to backup base directory" - return 1 - } - - mkdir -p "$SNAPSHOT_DIR/systemd" - cp -a /etc/systemd/system/${app_name}-*.service "$SNAPSHOT_DIR/systemd/" 2>/dev/null || true - - [[ -f "/etc/default/$app_name" ]] && cp "/etc/default/$app_name" "$SNAPSHOT_DIR/" - [[ -f "$base_dir/${app_name}_version.txt" ]] && cp "$base_dir/${app_name}_version.txt" "$SNAPSHOT_DIR/" - - rotate_snapshots "$snapshot_base" - - msg_ok "Snapshot created at $SNAPSHOT_DIR" - return 0 -} - -rotate_snapshots() { - local snapshot_base=$1 - local snapshots - - # Sortiert nach Datum absteigend, behalte nur die 3 neuesten - mapfile -t snapshots < <(ls -dt ${snapshot_base}-* 2>/dev/null) - if ((${#snapshots[@]} > 3)); then - for ((i = 3; i < ${#snapshots[@]}; i++)); do - rm -rf "${snapshots[$i]}" - msg_info "Old snapshot removed: ${snapshots[$i]}" - done - fi -} - -rollback_snapshot() { - local app_name=$1 - local base_dir - - base_dir="${APP_DIR:-/opt/$app_name}" - if [[ -z "$SNAPSHOT_DIR" || ! -d "$SNAPSHOT_DIR" ]]; then - msg_error "No snapshot found. Cannot rollback." - return 1 - fi - - msg_info "Rolling back $app_name from snapshot" - - systemctl stop ${app_name}-* 2>/dev/null || true - - rm -rf "$base_dir" - cp -a "$SNAPSHOT_DIR/base" "$base_dir" || { - msg_error "Failed to restore base directory" - return 1 - } - - if [[ -d "$SNAPSHOT_DIR/systemd" ]]; then - cp "$SNAPSHOT_DIR/systemd/"*.service /etc/systemd/system/ 2>/dev/null || true - systemctl daemon-reload - fi - - [[ -f "$SNAPSHOT_DIR/$app_name" ]] && cp "$SNAPSHOT_DIR/$app_name" "/etc/default/$app_name" - [[ -f "$SNAPSHOT_DIR/${app_name}_version.txt" ]] && cp "$SNAPSHOT_DIR/${app_name}_version.txt" "$base_dir/" - - systemctl start ${app_name}-* 2>/dev/null || true - - msg_ok "Rollback for $app_name completed" - return 0 -} - -cleanup_snapshot() { - if [[ -n "$SNAPSHOT_DIR" && -d "$SNAPSHOT_DIR" ]]; then - rm -rf "$SNAPSHOT_DIR" - msg_ok "Cleaned up snapshot at $SNAPSHOT_DIR" - fi -} - -handle_failure() { - local app_name=$1 - local line=$2 - msg_error "Update failed at line $line. Rolling back..." - rollback_snapshot "$app_name" - exit 1 -} - -safe_run_update_script() { - local app_name="${APP:-paperless}" - - if ! create_snapshot "$app_name"; then - msg_error "Snapshot creation failed. Aborting update." - exit 1 - fi - - trap 'handle_failure "$app_name" $LINENO' ERR - set -eE - - update_script - - cleanup_snapshot -} - -wrap_update_script_with_snapshot() { - local original_func - original_func=$(declare -f update_script) || return 1 - - eval " - original_update_script() { - ${original_func#*\{} - } - update_script() { - local app_name=\"\${APP:-paperless}\" - if ! create_snapshot \"\$app_name\"; then - msg_error \"Snapshot creation failed. Aborting update.\" - exit 1 - fi - trap 'handle_failure \"\$app_name\" \$LINENO' ERR - set -eE - original_update_script - cleanup_snapshot - } - " -} diff --git a/misc/backup_07052025/build copy.func b/misc/backup_07052025/build copy.func deleted file mode 100644 index bf5af2c46..000000000 --- a/misc/backup_07052025/build copy.func +++ /dev/null @@ -1,1407 +0,0 @@ -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) -# Co-Author: MickLesk -# Co-Author: michelroegl-brunner -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE - -variables() { - NSAPP=$(echo "${APP,,}" | tr -d ' ') # This function sets the NSAPP variable by converting the value of the APP variable to lowercase and removing any spaces. - var_install="${NSAPP}-install" # sets the var_install variable by appending "-install" to the value of NSAPP. - INTEGER='^[0-9]+([.][0-9]+)?$' # it defines the INTEGER regular expression pattern. - PVEHOST_NAME=$(hostname) # gets the Proxmox Hostname and sets it to Uppercase - DIAGNOSTICS="yes" # sets the DIAGNOSTICS variable to "yes", used for the API call. - METHOD="default" # sets the METHOD variable to "default", used for the API call. - RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" # generates a random UUID and sets it to the RANDOM_UUID variable. -} - -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) - -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) -color - -# # This function sets various color variables using ANSI escape codes for formatting text in the terminal. -# color() { -# # Colors -# YW=$(echo "\033[33m") -# YWB=$(echo "\033[93m") -# BL=$(echo "\033[36m") -# RD=$(echo "\033[01;31m") -# BGN=$(echo "\033[4;92m") -# GN=$(echo "\033[1;92m") -# DGN=$(echo "\033[32m") - -# # Formatting -# CL=$(echo "\033[m") -# BOLD=$(echo "\033[1m") -# HOLD=" " -# TAB=" " - -# # Icons -# CM="${TAB}✔️${TAB}" -# CROSS="${TAB}✖️${TAB}" -# INFO="${TAB}💡${TAB}${CL}" -# OS="${TAB}🖥️${TAB}${CL}" -# OSVERSION="${TAB}🌟${TAB}${CL}" -# CONTAINERTYPE="${TAB}📦${TAB}${CL}" -# DISKSIZE="${TAB}💾${TAB}${CL}" -# CPUCORE="${TAB}🧠${TAB}${CL}" -# RAMSIZE="${TAB}🛠️${TAB}${CL}" -# SEARCH="${TAB}🔍${TAB}${CL}" -# VERBOSE_CROPPED="🔍${TAB}" -# VERIFYPW="${TAB}🔐${TAB}${CL}" -# CONTAINERID="${TAB}🆔${TAB}${CL}" -# HOSTNAME="${TAB}🏠${TAB}${CL}" -# BRIDGE="${TAB}🌉${TAB}${CL}" -# NETWORK="${TAB}📡${TAB}${CL}" -# GATEWAY="${TAB}🌐${TAB}${CL}" -# DISABLEIPV6="${TAB}🚫${TAB}${CL}" -# DEFAULT="${TAB}⚙️${TAB}${CL}" -# MACADDRESS="${TAB}🔗${TAB}${CL}" -# VLANTAG="${TAB}🏷️${TAB}${CL}" -# ROOTSSH="${TAB}🔑${TAB}${CL}" -# CREATING="${TAB}🚀${TAB}${CL}" -# ADVANCED="${TAB}🧩${TAB}${CL}" -# } - -# This function enables error handling in the script by setting options and defining a trap for the ERR signal. -catch_errors() { - set -Eeuo pipefail - trap 'error_handler $LINENO "$BASH_COMMAND"' ERR -} - -# This function is called when an error occurs. It receives the exit code, line number, and command that caused the error, and displays an error message. -error_handler() { - source /dev/stdin <<<$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) - if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi - printf "\e[?25h" - local exit_code="$?" - local line_number="$1" - local command="$2" - local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" - post_update_to_api "failed" "${command}" - echo -e "\n$error_message\n" -} - -# Check if the shell is using bash -shell_check() { - if [[ "$(basename "$SHELL")" != "bash" ]]; then - clear - msg_error "Your default shell is currently not set to Bash. To use these scripts, please switch to the Bash shell." - echo -e "\nExiting..." - sleep 2 - exit - fi -} - -# Run as root only -root_check() { - if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then - clear - msg_error "Please run this script as root." - echo -e "\nExiting..." - sleep 2 - exit - fi -} - -# This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported. -pve_check() { - if ! pveversion | grep -Eq "pve-manager/8\.[0-4](\.[0-9]+)*"; then - msg_error "${CROSS}${RD}This version of Proxmox Virtual Environment is not supported" - echo -e "Requires Proxmox Virtual Environment Version 8.1 or later." - echo -e "Exiting..." - sleep 2 - exit - fi -} - -# When a node is running tens of containers, it's possible to exceed the kernel's cryptographic key storage allocations. -# These are tuneable, so verify if the currently deployment is approaching the limits, advise the user on how to tune the limits, and exit the script. -# https://cleveruptime.com/docs/files/proc-key-users | https://docs.kernel.org/security/keys/core.html -maxkeys_check() { - # Read kernel parameters - per_user_maxkeys=$(cat /proc/sys/kernel/keys/maxkeys 2>/dev/null || echo 0) - per_user_maxbytes=$(cat /proc/sys/kernel/keys/maxbytes 2>/dev/null || echo 0) - - # Exit if kernel parameters are unavailable - if [[ "$per_user_maxkeys" -eq 0 || "$per_user_maxbytes" -eq 0 ]]; then - echo -e "${CROSS}${RD} Error: Unable to read kernel parameters. Ensure proper permissions.${CL}" - exit 1 - fi - - # Fetch key usage for user ID 100000 (typical for containers) - used_lxc_keys=$(awk '/100000:/ {print $2}' /proc/key-users 2>/dev/null || echo 0) - used_lxc_bytes=$(awk '/100000:/ {split($5, a, "/"); print a[1]}' /proc/key-users 2>/dev/null || echo 0) - - # Calculate thresholds and suggested new limits - threshold_keys=$((per_user_maxkeys - 100)) - threshold_bytes=$((per_user_maxbytes - 1000)) - new_limit_keys=$((per_user_maxkeys * 2)) - new_limit_bytes=$((per_user_maxbytes * 2)) - - # Check if key or byte usage is near limits - failure=0 - if [[ "$used_lxc_keys" -gt "$threshold_keys" ]]; then - echo -e "${CROSS}${RD} Warning: Key usage is near the limit (${used_lxc_keys}/${per_user_maxkeys}).${CL}" - echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxkeys=${new_limit_keys}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." - failure=1 - fi - if [[ "$used_lxc_bytes" -gt "$threshold_bytes" ]]; then - echo -e "${CROSS}${RD} Warning: Key byte usage is near the limit (${used_lxc_bytes}/${per_user_maxbytes}).${CL}" - echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxbytes=${new_limit_bytes}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." - failure=1 - fi - - # Provide next steps if issues are detected - if [[ "$failure" -eq 1 ]]; then - echo -e "${INFO} To apply changes, run: ${BOLD}service procps force-reload${CL}" - exit 1 - fi - - echo -e "${CM}${GN} All kernel key limits are within safe thresholds.${CL}" -} - -# This function checks the system architecture and exits if it's not "amd64". -arch_check() { - if [ "$(dpkg --print-architecture)" != "amd64" ]; then - echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" - echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" - echo -e "Exiting..." - sleep 2 - exit - fi -} - -# Function to get the current IP address based on the distribution -get_current_ip() { - if [ -f /etc/os-release ]; then - # Check for Debian/Ubuntu (uses hostname -I) - if grep -qE 'ID=debian|ID=ubuntu' /etc/os-release; then - CURRENT_IP=$(hostname -I | awk '{print $1}') - # Check for Alpine (uses ip command) - elif grep -q 'ID=alpine' /etc/os-release; then - CURRENT_IP=$(ip -4 addr show eth0 | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) - else - CURRENT_IP="Unknown" - fi - fi - echo "$CURRENT_IP" -} - -# Function to update the IP address in the MOTD file -update_motd_ip() { - MOTD_FILE="/etc/motd" - - if [ -f "$MOTD_FILE" ]; then - # Remove existing IP Address lines to prevent duplication - sed -i '/IP Address:/d' "$MOTD_FILE" - - IP=$(get_current_ip) - # Add the new IP address - echo -e "${TAB}${NETWORK}${YW} IP Address: ${GN}${IP}${CL}" >>"$MOTD_FILE" - fi -} - -# Function to download & save header files -get_header() { - local app_name=$(echo "${APP,,}" | tr -d ' ') - local header_url="https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/ct/headers/${app_name}" - local local_header_path="/usr/local/community-scripts/headers/${app_name}" - - mkdir -p "$(dirname "$local_header_path")" - - if [ ! -s "$local_header_path" ]; then - if ! curl -fsSL "$header_url" -o "$local_header_path"; then - return 1 - fi - fi - - cat "$local_header_path" 2>/dev/null || true -} -# This function sets the APP-Name into an ASCII Header in Slant, figlet needed on proxmox main node. -header_info() { - local app_name=$(echo "${APP,,}" | tr -d ' ') - local header_content - - # Download & save Header-File locally - header_content=$(get_header "$app_name") || header_content="" - - # Show ASCII-Header - clear - local term_width - term_width=$(tput cols 2>/dev/null || echo 120) - - if [ -n "$header_content" ]; then - echo "$header_content" - fi -} - -# This function checks if the script is running through SSH and prompts the user to confirm if they want to proceed or exit. -ssh_check() { - if [ -n "${SSH_CLIENT:+x}" ]; then - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's advisable to utilize the Proxmox shell rather than SSH, as there may be potential complications with variable retrieval. Proceed using SSH?" 10 72; then - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox --title "Proceed using SSH" "You've chosen to proceed using SSH. If any issues arise, please run the script in the Proxmox shell before creating a repository issue." 10 72 - else - clear - echo "Exiting due to SSH usage. Please consider using the Proxmox shell." - exit - fi - fi -} - -base_settings() { - # Default Settings - CT_TYPE="1" - DISK_SIZE="4" - CORE_COUNT="1" - RAM_SIZE="1024" - VERBOSE="${1:-no}" - PW="" - CT_ID=$NEXTID - HN=$NSAPP - BRG="vmbr0" - NET="dhcp" - GATE="" - APT_CACHER="" - APT_CACHER_IP="" - DISABLEIP6="no" - MTU="" - SD="" - NS="" - MAC="" - VLAN="" - SSH="no" - SSH_AUTHORIZED_KEY="" - TAGS="community-script;" - UDHCPC_FIX="" - - # Override default settings with variables from ct script - CT_TYPE=${var_unprivileged:-$CT_TYPE} - DISK_SIZE=${var_disk:-$DISK_SIZE} - CORE_COUNT=${var_cpu:-$CORE_COUNT} - RAM_SIZE=${var_ram:-$RAM_SIZE} - VERB=${var_verbose:-$VERBOSE} - TAGS="${TAGS}${var_tags:-}" - - # Since these 2 are only defined outside of default_settings function, we add a temporary fallback. TODO: To align everything, we should add these as constant variables (e.g. OSTYPE and OSVERSION), but that would currently require updating the default_settings function for all existing scripts - if [ -z "$var_os" ]; then - var_os="debian" - fi - if [ -z "$var_version" ]; then - var_version="12" - fi -} - -# This function displays the default values for various settings. -echo_default() { - # Convert CT_TYPE to description - CT_TYPE_DESC="Unprivileged" - if [ "$CT_TYPE" -eq 0 ]; then - CT_TYPE_DESC="Privileged" - fi - - # Output the selected values with icons - echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os${CL}" - echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" - echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}${CT_ID}${CL}" - if [ "$VERB" == "yes" ]; then - echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}Enabled${CL}" - fi - echo -e "${CREATING}${BOLD}${BL}Creating a ${APP} LXC using the above default settings${CL}" - echo -e " " -} - -# This function is called when the user decides to exit the script. It clears the screen and displays an exit message. -exit_script() { - [[ -n "${SPINNER_PID:-}" ]] && kill "$SPINNER_PID" &>/dev/null || true - clear - echo -e "\n${CROSS}${RD}User exited script${CL}\n" - exit -} - -# This function allows the user to configure advanced settings for the script. -advanced_settings() { - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox --title "Here is an instructional tip:" "To make a selection, use the Spacebar." 8 58 - # Setting Default Tag for Advanced Settings - TAGS="community-script;${var_tags:-}" - CT_DEFAULT_TYPE="${CT_TYPE}" - CT_TYPE="" - while [ -z "$CT_TYPE" ]; do - if [ "$CT_DEFAULT_TYPE" == "1" ]; then - if CT_TYPE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ - "1" "Unprivileged" ON \ - "0" "Privileged" OFF \ - 3>&1 1>&2 2>&3); then - if [ -n "$CT_TYPE" ]; then - CT_TYPE_DESC="Unprivileged" - if [ "$CT_TYPE" -eq 0 ]; then - CT_TYPE_DESC="Privileged" - fi - echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os${CL}" - echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" - fi - else - exit_script - fi - fi - if [ "$CT_DEFAULT_TYPE" == "0" ]; then - if CT_TYPE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ - "1" "Unprivileged" OFF \ - "0" "Privileged" ON \ - 3>&1 1>&2 2>&3); then - if [ -n "$CT_TYPE" ]; then - CT_TYPE_DESC="Unprivileged" - if [ "$CT_TYPE" -eq 0 ]; then - CT_TYPE_DESC="Privileged" - fi - echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os${CL}" - echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" - fi - else - exit_script - fi - fi - done - - while true; do - if PW1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --passwordbox "\nSet Root Password (needed for root ssh access)" 9 58 --title "PASSWORD (leave blank for automatic login)" 3>&1 1>&2 2>&3); then - if [[ ! -z "$PW1" ]]; then - if [[ "$PW1" == *" "* ]]; then - whiptail --msgbox "Password cannot contain spaces. Please try again." 8 58 - elif [ ${#PW1} -lt 5 ]; then - whiptail --msgbox "Password must be at least 5 characters long. Please try again." 8 58 - else - if PW2=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then - if [[ "$PW1" == "$PW2" ]]; then - PW="-password $PW1" - echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}" - break - else - whiptail --msgbox "Passwords do not match. Please try again." 8 58 - fi - else - exit_script - fi - fi - else - PW1="Automatic Login" - PW="" - echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}$PW1${CL}" - break - fi - else - exit_script - fi - done - - if CT_ID=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Container ID" 8 58 "$NEXTID" --title "CONTAINER ID" 3>&1 1>&2 2>&3); then - if [ -z "$CT_ID" ]; then - CT_ID="$NEXTID" - echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" - else - echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" - fi - else - exit - fi - - if CT_NAME=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 "$NSAPP" --title "HOSTNAME" 3>&1 1>&2 2>&3); then - if [ -z "$CT_NAME" ]; then - HN="$NSAPP" - else - HN=$(echo "${CT_NAME,,}" | tr -d ' ') - fi - echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" - else - exit_script - fi - - if DISK_SIZE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GB" 8 58 "$var_disk" --title "DISK SIZE" 3>&1 1>&2 2>&3); then - if [ -z "$DISK_SIZE" ]; then - DISK_SIZE="$var_disk" - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" - else - if ! [[ $DISK_SIZE =~ $INTEGER ]]; then - echo -e "{INFO}${HOLD}${RD} DISK SIZE MUST BE AN INTEGER NUMBER!${CL}" - advanced_settings - fi - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" - fi - else - exit_script - fi - - if CORE_COUNT=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 "$var_cpu" --title "CORE COUNT" 3>&1 1>&2 2>&3); then - if [ -z "$CORE_COUNT" ]; then - CORE_COUNT="$var_cpu" - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" - else - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" - fi - else - exit_script - fi - - if RAM_SIZE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 "$var_ram" --title "RAM" 3>&1 1>&2 2>&3); then - if [ -z "$RAM_SIZE" ]; then - RAM_SIZE="$var_ram" - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" - else - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" - fi - else - exit_script - fi - - if BRG=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" 3>&1 1>&2 2>&3); then - if [ -z "$BRG" ]; then - BRG="vmbr0" - echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" - else - echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" - fi - else - exit_script - fi - - while true; do - NET=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a Static IPv4 CIDR Address (/24)" 8 58 dhcp --title "IP ADDRESS" 3>&1 1>&2 2>&3) - exit_status=$? - if [ $exit_status -eq 0 ]; then - if [ "$NET" = "dhcp" ]; then - echo -e "${NETWORK}${BOLD}${DGN}IP Address: ${BGN}$NET${CL}" - break - else - if [[ "$NET" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then - echo -e "${NETWORK}${BOLD}${DGN}IP Address: ${BGN}$NET${CL}" - break - else - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "$NET is an invalid IPv4 CIDR address. Please enter a valid IPv4 CIDR address or 'dhcp'" 8 58 - fi - fi - else - exit_script - fi - done - - if [ "$NET" != "dhcp" ]; then - while true; do - GATE1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Enter gateway IP address" 8 58 --title "Gateway IP" 3>&1 1>&2 2>&3) - if [ -z "$GATE1" ]; then - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "Gateway IP address cannot be empty" 8 58 - elif [[ ! "$GATE1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "Invalid IP address format" 8 58 - else - GATE=",gw=$GATE1" - echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE1${CL}" - break - fi - done - else - GATE="" - echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}Default${CL}" - fi - - if [ "$var_os" == "alpine" ]; then - APT_CACHER="" - APT_CACHER_IP="" - else - if APT_CACHER_IP=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set APT-Cacher IP (leave blank for none)" 8 58 --title "APT-Cacher IP" 3>&1 1>&2 2>&3); then - APT_CACHER="${APT_CACHER_IP:+yes}" - echo -e "${NETWORK}${BOLD}${DGN}APT-Cacher IP Address: ${BGN}${APT_CACHER_IP:-Default}${CL}" - else - exit_script - fi - fi - - if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "IPv6" --yesno "Disable IPv6?" 10 58); then - DISABLEIP6="yes" - else - DISABLEIP6="no" - fi - echo -e "${DISABLEIPV6}${BOLD}${DGN}Disable IPv6: ${BGN}$DISABLEIP6${CL}" - - if MTU1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default [The MTU of your selected vmbr, default is 1500])" 8 58 --title "MTU SIZE" 3>&1 1>&2 2>&3); then - if [ -z "$MTU1" ]; then - MTU1="Default" - MTU="" - else - MTU=",mtu=$MTU1" - fi - echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" - else - exit_script - fi - - if SD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a DNS Search Domain (leave blank for HOST)" 8 58 --title "DNS Search Domain" 3>&1 1>&2 2>&3); then - if [ -z "$SD" ]; then - SX=Host - SD="" - else - SX=$SD - SD="-searchdomain=$SD" - fi - echo -e "${SEARCH}${BOLD}${DGN}DNS Search Domain: ${BGN}$SX${CL}" - else - exit_script - fi - - if NX=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a DNS Server IP (leave blank for HOST)" 8 58 --title "DNS SERVER IP" 3>&1 1>&2 2>&3); then - if [ -z "$NX" ]; then - NX=Host - NS="" - else - NS="-nameserver=$NX" - fi - echo -e "${NETWORK}${BOLD}${DGN}DNS Server IP Address: ${BGN}$NX${CL}" - else - exit_script - fi - - if [ "$var_os" == "alpine" ] && [ "$NET" == "dhcp" ] && [ "$NX" != "Host" ]; then - UDHCPC_FIX="yes" - else - UDHCPC_FIX="no" - fi - export UDHCPC_FIX - - if MAC1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a MAC Address(leave blank for generated MAC)" 8 58 --title "MAC ADDRESS" 3>&1 1>&2 2>&3); then - if [ -z "$MAC1" ]; then - MAC1="Default" - MAC="" - else - MAC=",hwaddr=$MAC1" - echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" - fi - else - exit_script - fi - - if VLAN1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for no VLAN)" 8 58 --title "VLAN" 3>&1 1>&2 2>&3); then - if [ -z "$VLAN1" ]; then - VLAN1="Default" - VLAN="" - else - VLAN=",tag=$VLAN1" - fi - echo -e "${VLANTAG}${BOLD}${DGN}Vlan: ${BGN}$VLAN1${CL}" - else - exit_script - fi - - if ADV_TAGS=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Custom Tags?[If you remove all, there will be no tags!]" 8 58 "${TAGS}" --title "Advanced Tags" 3>&1 1>&2 2>&3); then - if [ -n "${ADV_TAGS}" ]; then - ADV_TAGS=$(echo "$ADV_TAGS" | tr -d '[:space:]') - TAGS="${ADV_TAGS}" - else - TAGS=";" - fi - echo -e "${NETWORK}${BOLD}${DGN}Tags: ${BGN}$TAGS${CL}" - else - exit_script - fi - - if [[ "$PW" == -password* ]]; then - if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable Root SSH Access?" 10 58); then - SSH="yes" - else - SSH="no" - fi - echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}" - else - SSH="no" - echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}" - fi - - if [[ "${SSH}" == "yes" ]]; then - SSH_AUTHORIZED_KEY="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "SSH Authorized key for root (leave empty for none)" 8 58 --title "SSH Key" 3>&1 1>&2 2>&3)" - - if [[ -z "${SSH_AUTHORIZED_KEY}" ]]; then - echo "Warning: No SSH key provided." - fi - else - SSH_AUTHORIZED_KEY="" - fi - if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then - VERB="yes" - else - VERB="no" - fi - echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERB${CL}" - - if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create ${APP} LXC?" 10 58); then - echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}" - else - clear - header_info - echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings on node $PVEHOST_NAME${CL}" - advanced_settings - fi -} - -diagnostics_check() { - if ! [ -d "/usr/local/community-scripts" ]; then - mkdir -p /usr/local/community-scripts - fi - - if ! [ -f "/usr/local/community-scripts/diagnostics" ]; then - if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS" --yesno "Send Diagnostics of LXC Installation?\n\n(This only transmits data without user data, just RAM, CPU, LXC name, ...)" 10 58); then - cat </usr/local/community-scripts/diagnostics -DIAGNOSTICS=yes - -#This file is used to store the diagnostics settings for the Community-Scripts API. -#https://github.com/community-scripts/ProxmoxVED/discussions/1836 -#Your diagnostics will be sent to the Community-Scripts API for troubleshooting/statistical purposes. -#You can review the data at https://community-scripts.github.io/ProxmoxVE/data -#If you do not wish to send diagnostics, please set the variable 'DIAGNOSTICS' to "no" in /usr/local/community-scripts/diagnostics, or use the menue. -#This will disable the diagnostics feature. -#To send diagnostics, set the variable 'DIAGNOSTICS' to "yes" in /usr/local/community-scripts/diagnostics, or use the menue. -#This will enable the diagnostics feature. -#The following information will be sent: -#"ct_type" -#"disk_size" -#"core_count" -#"ram_size" -#"os_type" -#"os_version" -#"disableip6" -#"nsapp" -#"method" -#"pve_version" -#"status" -#If you have any concerns, please review the source code at /misc/build.func -EOF - DIAGNOSTICS="yes" - else - cat </usr/local/community-scripts/diagnostics -DIAGNOSTICS=no - -#This file is used to store the diagnostics settings for the Community-Scripts API. -#https://github.com/community-scripts/ProxmoxVED/discussions/1836 -#Your diagnostics will be sent to the Community-Scripts API for troubleshooting/statistical purposes. -#You can review the data at https://community-scripts.github.io/ProxmoxVE/data -#If you do not wish to send diagnostics, please set the variable 'DIAGNOSTICS' to "no" in /usr/local/community-scripts/diagnostics, or use the menue. -#This will disable the diagnostics feature. -#To send diagnostics, set the variable 'DIAGNOSTICS' to "yes" in /usr/local/community-scripts/diagnostics, or use the menue. -#This will enable the diagnostics feature. -#The following information will be sent: -#"ct_type" -#"disk_size" -#"core_count" -#"ram_size" -#"os_type" -#"os_version" -#"disableip6" -#"nsapp" -#"method" -#"pve_version" -#"status" -#If you have any concerns, please review the source code at /misc/build.func -EOF - DIAGNOSTICS="no" - fi - else - DIAGNOSTICS=$(awk -F '=' '/^DIAGNOSTICS/ {print $2}' /usr/local/community-scripts/diagnostics) - - fi - -} - -config_file() { - - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox --title "Default distribution for $APP" "${var_os} ${var_version} \n \nIf the default Linux distribution is not adhered to, script support will be discontinued. \n" 10 58 - - CONFIG_FILE="/opt/community-scripts/.settings" - - if [[ -f "/opt/community-scripts/${NSAPP}.conf" ]]; then - CONFIG_FILE="/opt/community-scripts/${NSAPP}.conf" - fi - - if CONFIG_FILE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set absolute path to config file" 8 58 "$CONFIG_FILE" --title "CONFIG FILE" 3>&1 1>&2 2>&3); then - if [[ ! -f "$CONFIG_FILE" ]]; then - echo -e "${CROSS}${RD}Config file not found, exiting script!.${CL}" - exit - else - echo -e "${INFO}${BOLD}${DGN}Using config File: ${BGN}$CONFIG_FILE${CL}" - base_settings - source "$CONFIG_FILE" - fi - fi - - if [[ "$var_os" == "debian" ]]; then - echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os${CL}" - if [[ "$var_version" == "11" ]]; then - echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" - elif [[ "$var_version" == "12" ]]; then - echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" - else - msg_error "Unknown setting for var_version, should be 11 or 12, was ${var_version}" - exit - fi - elif [[ "$var_os" == "ubuntu" ]]; then - echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os${CL}" - if [[ "$var_version" == "20.04" ]]; then - echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" - elif [[ "$var_version" == "22.04" ]]; then - echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" - elif [[ "$var_version" == "24.04" ]]; then - echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" - elif [[ "$var_version" == "24.10" ]]; then - echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" - else - msg_error "Unknown setting for var_version, should be 20.04, 22.04, 24.04 or 24.10, was ${var_version}" - exit - fi - else - msg_error "Unknown setting for var_os! should be debian or ubuntu, was ${var_os}" - exit - fi - - if [[ -n "$CT_ID" ]]; then - - if [[ "$CT_ID" =~ ^([0-9]{3,4})-([0-9]{3,4})$ ]]; then - MIN_ID=${BASH_REMATCH[1]} - MAX_ID=${BASH_REMATCH[2]} - - if ((MIN_ID >= MAX_ID)); then - msg_error "Invalid Container ID range. The first number must be smaller than the second number, was ${CT_ID}" - exit - fi - - LIST_OF_IDS=$(pvesh get /cluster/resources --type vm --output-format json | grep -oP '"vmid":\s*\K\d+') - - for ((ID = MIN_ID; ID <= MAX_ID; ID++)); do - if ! grep -q "^$ID$" <<<"$LIST_OF_IDS"; then - CT_ID=$ID - break - fi - done - - echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" - - elif [[ "$CT_ID" =~ ^[0-9]+$ ]]; then - - LIST_OF_IDS=$(pvesh get /cluster/resources --type vm --output-format json | grep -oP '"vmid":\s*\K\d+') - if ! grep -q "^$CT_ID$" <<<"$LIST_OF_IDS"; then - echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" - else - msg_error "Container ID $CT_ID already exists" - exit - fi - else - msg_error "Invalid Container ID format. Needs to be 0000-9999 or 0-9999, was ${CT_ID}" - exit - fi - fi - - if [[ "$CT_TYPE" -eq 0 ]]; then - CT_TYPE_DESC="Privileged" - elif [[ "$CT_TYPE" -eq 1 ]]; then - CT_TYPE_DESC="Unprivileged" - else - msg_error "Unknown setting for CT_TYPE, should be 1 or 0, was ${CT_TYPE}" - exit - fi - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" - - if [[ ! -z "$PW" ]]; then - - if [[ "$PW" == *" "* ]]; then - msg_error "Password cannot be empty" - exit - elif [[ ${#PW} -lt 5 ]]; then - msg_error "Password must be at least 5 characters long" - exit - else - echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}" - fi - PW="-password $PW" - else - PW="" - echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}Automatic Login${CL}" - fi - echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" - - if [[ ! -z "$HN" ]]; then - echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" - else - msg_error "Hostname cannot be empty" - exit - fi - - if [[ ! -z "$DISK_SIZE" ]]; then - if [[ "$DISK_SIZE" =~ ^-?[0-9]+$ ]]; then - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" - else - msg_error "DISK_SIZE must be an integer, was ${DISK_SIZE}" - exit - fi - else - msg_error "DISK_SIZE cannot be empty" - exit - fi - - if [[ ! -z "$CORE_COUNT" ]]; then - if [[ "$CORE_COUNT" =~ ^-?[0-9]+$ ]]; then - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" - else - msg_error "CORE_COUNT must be an integer, was ${CORE_COUNT}" - exit - fi - else - msg_error "CORE_COUNT cannot be empty" - exit - fi - - if [[ ! -z "$RAM_SIZE" ]]; then - if [[ "$RAM_SIZE" =~ ^-?[0-9]+$ ]]; then - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" - else - msg_error "RAM_SIZE must be an integer, was ${RAM_SIZE}" - exit - fi - else - msg_error "RAM_SIZE cannot be empty" - exit - fi - - if [[ ! -z "$BRG" ]]; then - if grep -q "^iface ${BRG}" /etc/network/interfaces; then - echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" - else - msg_error "Bridge '${BRG}' does not exist in /etc/network/interfaces" - exit - fi - else - msg_error "Bridge cannot be empty" - exit - fi - - local ip_cidr_regex='^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/([0-9]{1,2})$' - local ip_regex='^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$' - - if [[ ! -z $NET ]]; then - if [ "$NET" == "dhcp" ]; then - echo -e "${NETWORK}${BOLD}${DGN}IP Address: ${BGN}DHCP${CL}" - echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}Default${CL}" - elif - [[ "$NET" =~ $ip_cidr_regex ]] - then - echo -e "${NETWORK}${BOLD}${DGN}IP Address: ${BGN}$NET${CL}" - else - msg_error "Invalid IP Address format. Needs to be 0.0.0.0/0, was ${NET}" - exit - fi - fi - if [ ! -z "$GATE" ]; then - if [[ "$GATE" =~ $ip_regex ]]; then - echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE${CL}" - GATE=",gw=$GATE" - else - msg_error "Invalid IP Address format for Gateway. Needs to be 0.0.0.0, was ${GATE}" - exit - fi - else - msg_error "Gateway IP Address cannot be empty" - exit - fi - - if [[ ! -z "$APT_CACHER_IP" ]]; then - if [[ "$APT_CACHER_IP" =~ $ip_regex ]]; then - APT_CACHER="yes" - echo -e "${NETWORK}${BOLD}${DGN}APT-CACHER IP Address: ${BGN}$APT_CACHER_IP${CL}" - else - msg_error "Invalid IP Address format for APT-Cacher. Needs to be 0.0.0.0, was ${APT_CACHER_IP}" - exit - fi - fi - - if [[ "$DISABLEIP6" == "yes" ]]; then - echo -e "${DISABLEIPV6}${BOLD}${DGN}Disable IPv6: ${BGN}Yes${CL}" - elif [[ "$DISABLEIP6" == "no" ]]; then - echo -e "${DISABLEIPV6}${BOLD}${DGN}Disable IPv6: ${BGN}No${CL}" - else - msg_error "Disable IPv6 needs to be 'yes' or 'no'" - exit - fi - - if [[ ! -z "$MTU" ]]; then - if [[ "$MTU" =~ ^-?[0-9]+$ ]]; then - echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU${CL}" - MTU=",mtu=$MTU" - else - msg_error "MTU must be an integer, was ${MTU}" - exit - fi - else - MTU="" - echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" - - fi - - if [[ ! -z "$SD" ]]; then - echo -e "${SEARCH}${BOLD}${DGN}DNS Search Domain: ${BGN}$SD${CL}" - SD="-searchdomain=$SD" - else - SD="" - echo -e "${SEARCH}${BOLD}${DGN}DNS Search Domain: ${BGN}HOST${CL}" - fi - - if [[ ! -z "$NS" ]]; then - if [[ "$NS" =~ $ip_regex ]]; then - echo -e "${NETWORK}${BOLD}${DGN}DNS Server IP Address: ${BGN}$NS${CL}" - NS="-nameserver=$NS" - else - msg_error "Invalid IP Address format for DNS Server. Needs to be 0.0.0.0, was ${NS}" - exit - fi - else - NS="" - echo -e "${NETWORK}${BOLD}${DGN}DNS Server IP Address: ${BGN}HOST${CL}" - fi - - if [[ ! -z "$MAC" ]]; then - if [[ "$MAC" =~ ^([A-Fa-f0-9]{2}:){5}[A-Fa-f0-9]{2}$ ]]; then - echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" - MAC=",hwaddr=$MAC" - else - msg_error "MAC Address must be in the format xx:xx:xx:xx:xx:xx, was ${MAC}" - exit - fi - fi - - if [[ ! -z "$VLAN" ]]; then - if [[ "$VLAN" =~ ^-?[0-9]+$ ]]; then - echo -e "${VLANTAG}${BOLD}${DGN}Vlan: ${BGN}$VLAN${CL}" - VLAN=",tag=$VLAN" - else - msg_error "VLAN must be an integer, was ${VLAN}" - exit - fi - fi - - if [[ ! -z "$TAGS" ]]; then - echo -e "${NETWORK}${BOLD}${DGN}Tags: ${BGN}$TAGS${CL}" - fi - - if [[ "$SSH" == "yes" ]]; then - echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}" - if [[ ! -z "$SSH_AUTHORIZED_KEY" ]]; then - echo -e "${ROOTSSH}${BOLD}${DGN}SSH Authorized Key: ${BGN}********************${CL}" - else - echo -e "${ROOTSSH}${BOLD}${DGN}SSH Authorized Key: ${BGN}None${CL}" - fi - elif [[ "$SSH" == "no" ]]; then - echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}" - else - msg_error "SSH needs to be 'yes' or 'no', was ${SSH}" - exit - fi - - if [[ "$VERB" == "yes" ]]; then - echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERB${CL}" - elif [[ "$VERB" == "no" ]]; then - echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}No${CL}" - else - msg_error "Verbose Mode needs to be 'yes' or 'no', was ${VERB}" - exit - fi - - if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS WITH CONFIG FILE COMPLETE" --yesno "Ready to create ${APP} LXC?" 10 58); then - echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above settings${CL}" - else - clear - header_info - echo -e "${INFO}${HOLD} ${GN}Using Config File on node $PVEHOST_NAME${CL}" - config_file - fi - -} - -install_script() { - pve_check - shell_check - root_check - arch_check - ssh_check - maxkeys_check - diagnostics_check - - if systemctl is-active -q ping-instances.service; then - systemctl -q stop ping-instances.service - fi - NEXTID=$(pvesh get /cluster/nextid) - timezone=$(cat /etc/timezone) - header_info - while true; do - - CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SETTINGS" --menu "Choose an option:" \ - 18 60 6 \ - "1" "Default Settings" \ - "2" "Default Settings (with verbose)" \ - "3" "Advanced Settings" \ - "4" "Use Config File" \ - "5" "Diagnostic Settings" \ - "6" "Exit" --nocancel --default-item "1" 3>&1 1>&2 2>&3) - - if [ $? -ne 0 ]; then - echo -e "${CROSS}${RD} Menu canceled. Exiting.${CL}" - exit 0 - fi - - case $CHOICE in - 1) - header_info - echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME${CL}" - VERB="no" - METHOD="default" - base_settings "$VERB" - echo_default - break - ;; - 2) - header_info - echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME (${VERBOSE_CROPPED}Verbose)${CL}" - VERB="yes" - METHOD="default" - base_settings "$VERB" - echo_default - break - ;; - 3) - header_info - echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings on node $PVEHOST_NAME${CL}" - METHOD="advanced" - base_settings - advanced_settings - break - ;; - 4) - header_info - echo -e "${INFO}${HOLD} ${GN}Using Config File on node $PVEHOST_NAME${CL}" - METHOD="advanced" - base_settings - config_file - break - ;; - 5) - if [[ $DIAGNOSTICS == "yes" ]]; then - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS SETTINGS" --yesno "Send Diagnostics of LXC Installation?\n\nCurrent setting: ${DIAGNOSTICS}" 10 58 \ - --yes-button "No" --no-button "Back"; then - DIAGNOSTICS="no" - sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=no/' /usr/local/community-scripts/diagnostics - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS SETTINGS" --msgbox "Diagnostics settings changed to ${DIAGNOSTICS}." 8 58 - fi - else - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS SETTINGS" --yesno "Send Diagnostics of LXC Installation?\n\nCurrent setting: ${DIAGNOSTICS}" 10 58 \ - --yes-button "Yes" --no-button "Back"; then - DIAGNOSTICS="yes" - sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=yes/' /usr/local/community-scripts/diagnostics - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS SETTINGS" --msgbox "Diagnostics settings changed to ${DIAGNOSTICS}." 8 58 - fi - fi - - ;; - 6) - echo -e "${CROSS}${RD}Exiting.${CL}" - exit 0 - ;; - *) - echo -e "${CROSS}${RD}Invalid option, please try again.${CL}" - ;; - esac - done -} - -check_container_resources() { - # Check actual RAM & Cores - current_ram=$(free -m | awk 'NR==2{print $2}') - current_cpu=$(nproc) - - # Check whether the current RAM is less than the required RAM or the CPU cores are less than required - if [[ "$current_ram" -lt "$var_ram" ]] || [[ "$current_cpu" -lt "$var_cpu" ]]; then - echo -e "\n${INFO}${HOLD} ${GN}Required: ${var_cpu} CPU, ${var_ram}MB RAM ${CL}| ${RD}Current: ${current_cpu} CPU, ${current_ram}MB RAM${CL}" - echo -e "${YWB}Please ensure that the ${APP} LXC is configured with at least ${var_cpu} vCPU and ${var_ram} MB RAM for the build process.${CL}\n" - echo -ne "${INFO}${HOLD} May cause data loss! ${INFO} Continue update with under-provisioned LXC? " - read -r prompt - # Check if the input is 'yes', otherwise exit with status 1 - if [[ ! ${prompt,,} =~ ^(yes)$ ]]; then - echo -e "${CROSS}${HOLD} ${YWB}Exiting based on user input.${CL}" - exit 1 - fi - else - echo -e "" - fi -} - -check_container_storage() { - # Check if the /boot partition is more than 80% full - total_size=$(df /boot --output=size | tail -n 1) - local used_size=$(df /boot --output=used | tail -n 1) - usage=$((100 * used_size / total_size)) - if ((usage > 80)); then - # Prompt the user for confirmation to continue - echo -e "${INFO}${HOLD} ${YWB}Warning: Storage is dangerously low (${usage}%).${CL}" - echo -ne "Continue anyway? " - read -r prompt - # Check if the input is 'y' or 'yes', otherwise exit with status 1 - if [[ ! ${prompt,,} =~ ^(y|yes)$ ]]; then - echo -e "${CROSS}${HOLD}${YWB}Exiting based on user input.${CL}" - exit 1 - fi - fi -} - -start() { - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) - if command -v pveversion >/dev/null 2>&1; then - if ! (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "${APP} LXC" --yesno "This will create a New ${APP} LXC. Proceed?" 10 58); then - clear - exit_script - exit - fi - SPINNER_PID="" - install_script - else - CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "${APP} LXC Update/Setting" --menu \ - "Support/Update functions for ${APP} LXC. Choose an option:" \ - 12 60 3 \ - "1" "YES (Silent Mode)" \ - "2" "YES (Verbose Mode)" \ - "3" "NO (Cancel Update)" --nocancel --default-item "1" 3>&1 1>&2 2>&3) - - case "$CHOICE" in - 1) - VERB="no" - set_std_mode - ;; - 2) - VERB="yes" - set_std_mode - ;; - 3) - clear - exit_script - exit - ;; - esac - - SPINNER_PID="" - update_script - fi -} - -# This function collects user settings and integrates all the collected information. -build_container() { - # if [ "$VERB" == "yes" ]; then set -x; fi - - if [ "$CT_TYPE" == "1" ]; then - FEATURES="keyctl=1,nesting=1" - else - FEATURES="nesting=1" - fi - - if [[ $DIAGNOSTICS == "yes" ]]; then - post_to_api - fi - - TEMP_DIR=$(mktemp -d) - pushd "$TEMP_DIR" >/dev/null - if [ "$var_os" == "alpine" ]; then - export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-install.func)" - else - export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" - fi - export RANDOM_UUID="$RANDOM_UUID" - export CACHER="$APT_CACHER" - export CACHER_IP="$APT_CACHER_IP" - export tz="$timezone" - export DISABLEIPV6="$DISABLEIP6" - export APPLICATION="$APP" - export app="$NSAPP" - export PASSWORD="$PW" - export VERBOSE="$VERB" - export SSH_ROOT="${SSH}" - export SSH_AUTHORIZED_KEY - export CTID="$CT_ID" - export CTTYPE="$CT_TYPE" - export PCT_OSTYPE="$var_os" - export PCT_OSVERSION="$var_version" - export PCT_DISK_SIZE="$DISK_SIZE" - export PCT_OPTIONS=" - -features $FEATURES - -hostname $HN - -tags $TAGS - $SD - $NS - -net0 name=eth0,bridge=$BRG$MAC,ip=$NET$GATE$VLAN$MTU - -onboot 1 - -cores $CORE_COUNT - -memory $RAM_SIZE - -unprivileged $CT_TYPE - $PW - " - # This executes create_lxc.sh and creates the container and .conf file - bash -c "$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/create_lxc.sh)" $? - - LXC_CONFIG=/etc/pve/lxc/${CTID}.conf - if [ "$CT_TYPE" == "0" ]; then - cat <>"$LXC_CONFIG" -# USB passthrough -lxc.cgroup2.devices.allow: a -lxc.cap.drop: -lxc.cgroup2.devices.allow: c 188:* rwm -lxc.cgroup2.devices.allow: c 189:* rwm -lxc.mount.entry: /dev/serial/by-id dev/serial/by-id none bind,optional,create=dir -lxc.mount.entry: /dev/ttyUSB0 dev/ttyUSB0 none bind,optional,create=file -lxc.mount.entry: /dev/ttyUSB1 dev/ttyUSB1 none bind,optional,create=file -lxc.mount.entry: /dev/ttyACM0 dev/ttyACM0 none bind,optional,create=file -lxc.mount.entry: /dev/ttyACM1 dev/ttyACM1 none bind,optional,create=file -EOF - fi - - if [ "$CT_TYPE" == "0" ]; then - if [[ "$APP" == "immich" || "$APP" == "Channels" || "$APP" == "Emby" || "$APP" == "ErsatzTV" || "$APP" == "Frigate" || "$APP" == "Jellyfin" || "$APP" == "Plex" || "$APP" == "Scrypted" || "$APP" == "Tdarr" || "$APP" == "Unmanic" || "$APP" == "Ollama" || "$APP" == "FileFlows" ]]; then - cat <>$LXC_CONFIG -# VAAPI hardware transcoding -lxc.cgroup2.devices.allow: c 226:0 rwm -lxc.cgroup2.devices.allow: c 226:128 rwm -lxc.cgroup2.devices.allow: c 29:0 rwm -lxc.mount.entry: /dev/fb0 dev/fb0 none bind,optional,create=file -lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir -lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file -EOF - fi - else - if [[ "$APP" == "immich" || "$APP" == "Channels" || "$APP" == "Emby" || "$APP" == "ErsatzTV" || "$APP" == "Frigate" || "$APP" == "Jellyfin" || "$APP" == "Plex" || "$APP" == "Scrypted" || "$APP" == "Tdarr" || "$APP" == "Unmanic" || "$APP" == "Ollama" || "$APP" == "FileFlows" ]]; then - if [[ -e "/dev/dri/renderD128" ]]; then - if [[ -e "/dev/dri/card0" ]]; then - cat <>$LXC_CONFIG -# VAAPI hardware transcoding -dev0: /dev/dri/card0,gid=44 -dev1: /dev/dri/renderD128,gid=104 -EOF - else - cat <>"$LXC_CONFIG" -# VAAPI hardware transcoding -dev0: /dev/dri/card1,gid=44 -dev1: /dev/dri/renderD128,gid=104 -EOF - fi - fi - fi - fi - - # This starts the container and executes -install.sh - msg_info "Starting LXC Container" - pct start "$CTID" - msg_ok "Started LXC Container" - if [ "$var_os" == "alpine" ]; then - sleep 3 - pct exec "$CTID" -- /bin/sh -c 'cat </etc/apk/repositories -http://dl-cdn.alpinelinux.org/alpine/latest-stable/main -http://dl-cdn.alpinelinux.org/alpine/latest-stable/community -EOF' - pct exec "$CTID" -- ash -c "apk add bash >/dev/null" - fi - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/install/"$var_install".sh)" $? - -} - -# This function sets the description of the container. -description() { - IP=$(pct exec "$CTID" ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) - - # Generate LXC Description - DESCRIPTION=$( - cat < - - Logo - - -

${APP} LXC

- -

- - spend Coffee - -

- - - - GitHub - - - - Discussions - - - - Issues - - -EOF - ) - - # Set Description in LXC - pct set "$CTID" -description "$DESCRIPTION" - - if [[ -f /etc/systemd/system/ping-instances.service ]]; then - systemctl start ping-instances.service - fi - - post_update_to_api "done" "none" -} - -set_std_mode() { - if [ "$VERB" = "yes" ]; then - STD="" - else - STD="silent" - fi -} - -# Silent execution function -silent() { - if [ "$VERB" = "no" ]; then - "$@" >/dev/null 2>&1 || return 1 - else - "$@" || return 1 - fi -} - -api_exit_script() { - exit_code=$? # Capture the exit status of the last executed command - #200 exit codes indicate error in create_lxc.sh - #100 exit codes indicate error in install.func - - if [ $exit_code -ne 0 ]; then - case $exit_code in - 100) post_update_to_api "failed" "100: Unexpected error in create_lxc.sh" ;; - 101) post_update_to_api "failed" "101: No network connection detected in create_lxc.sh" ;; - 200) post_update_to_api "failed" "200: LXC creation failed in create_lxc.sh" ;; - 201) post_update_to_api "failed" "201: Invalid Storage class in create_lxc.sh" ;; - 202) post_update_to_api "failed" "202: User aborted menu in create_lxc.sh" ;; - 203) post_update_to_api "failed" "203: CTID not set in create_lxc.sh" ;; - 204) post_update_to_api "failed" "204: PCT_OSTYPE not set in create_lxc.sh" ;; - 205) post_update_to_api "failed" "205: CTID cannot be less than 100 in create_lxc.sh" ;; - 206) post_update_to_api "failed" "206: CTID already in use in create_lxc.sh" ;; - 207) post_update_to_api "failed" "207: Template not found in create_lxc.sh" ;; - 208) post_update_to_api "failed" "208: Error downloading template in create_lxc.sh" ;; - 209) post_update_to_api "failed" "209: Container creation failed, but template is intact in create_lxc.sh" ;; - *) post_update_to_api "failed" "Unknown error, exit code: $exit_code in create_lxc.sh" ;; - esac - fi -} - -trap 'api_exit_script' EXIT -trap 'post_update_to_api "failed" "$BASH_COMMAND"' ERR -trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT -trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM diff --git a/misc/backup_07052025/core.func.bak b/misc/backup_07052025/core.func.bak deleted file mode 100644 index de18842a9..000000000 --- a/misc/backup_07052025/core.func.bak +++ /dev/null @@ -1,220 +0,0 @@ -# Copyright (c) 2021-2025 community-scripts ORG -# Author: michelroegl-brunner -# License: MIT | https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/LICENSE - -color() { - # Colors - YW=$(echo "\033[33m") - YWB=$(echo "\033[93m") - BL=$(echo "\033[36m") - RD=$(echo "\033[01;31m") - BGN=$(echo "\033[4;92m") - GN=$(echo "\033[1;92m") - DGN=$(echo "\033[32m") - - # Formatting - CL=$(echo "\033[m") - BOLD=$(echo "\033[1m") - HOLD=" " - TAB=" " - - # Icons - CM="${TAB}✔️${TAB}" - CROSS="${TAB}✖️${TAB}${CL}" - INFO="${TAB}💡${TAB}${CL}" - OS="${TAB}🖥️${TAB}${CL}" - OSVERSION="${TAB}🌟${TAB}${CL}" - CONTAINERTYPE="${TAB}📦${TAB}${CL}" - DISKSIZE="${TAB}💾${TAB}${CL}" - CPUCORE="${TAB}🧠${TAB}${CL}" - RAMSIZE="${TAB}🛠️${TAB}${CL}" - SEARCH="${TAB}🔍${TAB}${CL}" - VERBOSE_CROPPED="🔍${TAB}" - VERIFYPW="${TAB}🔐${TAB}${CL}" - CONTAINERID="${TAB}🆔${TAB}${CL}" - HOSTNAME="${TAB}🏠${TAB}${CL}" - BRIDGE="${TAB}🌉${TAB}${CL}" - NETWORK="${TAB}📡${TAB}${CL}" - GATEWAY="${TAB}🌐${TAB}${CL}" - DISABLEIPV6="${TAB}🚫${TAB}${CL}" - DEFAULT="${TAB}⚙️${TAB}${CL}" - MACADDRESS="${TAB}🔗${TAB}${CL}" - VLANTAG="${TAB}🏷️${TAB}${CL}" - ROOTSSH="${TAB}🔑${TAB}${CL}" - CREATING="${TAB}🚀${TAB}${CL}" - ADVANCED="${TAB}🧩${TAB}${CL}" - FUSE="${TAB}🔧${TAB}${CL}" -} - -declare -A MSG_INFO_SHOWN -SPINNER_ACTIVE=0 -SPINNER_PID="" -SPINNER_MSG="" - -start_spinner() { - local msg="$1" - local frames=(⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏) - local spin_i=0 - local interval=0.1 - - SPINNER_MSG="$msg" - printf "\r\e[2K" >&2 - - { - while [[ "$SPINNER_ACTIVE" -eq 1 ]]; do - printf "\r\e[2K%s %b" "${frames[spin_i]}" "${YW}${SPINNER_MSG}${CL}" >&2 - spin_i=$(((spin_i + 1) % ${#frames[@]})) - sleep "$interval" - done - } & - - SPINNER_PID=$! - disown "$SPINNER_PID" -} - -stop_spinner() { - if [[ ${SPINNER_PID+v} && -n "$SPINNER_PID" ]] && kill -0 "$SPINNER_PID" 2>/dev/null; then - kill "$SPINNER_PID" 2>/dev/null - sleep 0.1 - kill -0 "$SPINNER_PID" 2>/dev/null && kill -9 "$SPINNER_PID" 2>/dev/null - wait "$SPINNER_PID" 2>/dev/null || true - fi - SPINNER_ACTIVE=0 - unset SPINNER_PID -} - -spinner_guard() { - if [[ "$SPINNER_ACTIVE" -eq 1 ]] && [[ -n "$SPINNER_PID" ]]; then - kill "$SPINNER_PID" 2>/dev/null - wait "$SPINNER_PID" 2>/dev/null || true - SPINNER_ACTIVE=0 - unset SPINNER_PID - fi -} - -log_message() { - local level="$1" - local message="$2" - local timestamp - local logdate - timestamp=$(date '+%Y-%m-%d %H:%M:%S') - logdate=$(date '+%Y-%m-%d') - - LOGDIR="/usr/local/community-scripts/logs" - mkdir -p "$LOGDIR" - - LOGFILE="${LOGDIR}/${logdate}_${NSAPP}.log" - echo "$timestamp - $level: $message" >>"$LOGFILE" -} - -msg_info() { - local msg="$1" - if [ "${SPINNER_ACTIVE:-0}" -eq 1 ]; then - return - fi - - SPINNER_ACTIVE=1 - start_spinner "$msg" -} - -msg_ok() { - if [ -n "${SPINNER_PID:-}" ] && ps -p "$SPINNER_PID" >/dev/null 2>&1; then - kill "$SPINNER_PID" >/dev/null 2>&1 - wait "$SPINNER_PID" 2>/dev/null || true - fi - - local msg="$1" - printf "\r\e[2K${CM}${GN}%b${CL}\n" "$msg" >&2 - unset SPINNER_PID - SPINNER_ACTIVE=0 - - log_message "OK" "$msg" -} - -msg_error() { - if [ -n "${SPINNER_PID:-}" ] && ps -p "$SPINNER_PID" >/dev/null 2>&1; then - kill "$SPINNER_PID" >/dev/null 2>&1 - wait "$SPINNER_PID" 2>/dev/null || true - fi - - local msg="$1" - printf "\r\e[2K${CROSS}${RD}%b${CL}\n" "$msg" >&2 - unset SPINNER_PID - SPINNER_ACTIVE=0 - log_message "ERROR" "$msg" -} - -shell_check() { - if [[ "$(basename "$SHELL")" != "bash" ]]; then - clear - msg_error "Your default shell is currently not set to Bash. To use these scripts, please switch to the Bash shell." - echo -e "\nExiting..." - sleep 2 - exit - fi -} - -root_check() { - if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then - clear - msg_error "Please run this script as root." - echo -e "\nExiting..." - sleep 2 - exit - fi -} - -pve_check() { - if ! pveversion | grep -Eq "pve-manager/8\.[1-9](\.[0-9]+)*"; then - msg_error "${CROSS}${RD}This version of Proxmox Virtual Environment is not supported" - echo -e "Requires Proxmox Virtual Environment Version 8.1 or later." - echo -e "Exiting..." - sleep 2 - exit - fi -} - -arch_check() { - if [ "$(dpkg --print-architecture)" != "amd64" ]; then - echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" - echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" - echo -e "Exiting..." - sleep 2 - exit - fi -} - -ssh_check() { - if command -v pveversion >/dev/null 2>&1; then - if [ -n "${SSH_CLIENT:+x}" ]; then - if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then - echo "you've been warned" - else - clear - exit - fi - fi - fi -} - -exit-script() { - clear - echo -e "\n${CROSS}${RD}User exited script${CL}\n" - exit -} - -set_std_mode() { - if [ "$VERB" = "yes" ]; then - STD="" - else - STD="silent" - fi -} - -silent() { - if [ "$VERB" = "no" ]; then - "$@" >>"$LOGFILE" 2>&1 - else - "$@" 2>&1 | tee -a "$LOGFILE" - fi -} diff --git a/misc/backup_07052025/dialogue.bak b/misc/backup_07052025/dialogue.bak deleted file mode 100644 index 497b5fe02..000000000 --- a/misc/backup_07052025/dialogue.bak +++ /dev/null @@ -1,850 +0,0 @@ - -# dialog_input() { -# local title="$1" -# local prompt="$2" -# local default="$3" -# local result -# apt-get install -y dialog -# result=$(dialog --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "$title" \ -# --extra-button --extra-label "Back" \ -# --ok-label "Next" --cancel-label "Exit" \ -# --inputbox "$prompt" 10 58 "$default" 2>&1 >/dev/tty) - -# local exitcode=$? - -# case $exitcode in -# 0) -# REPLY_RESULT="$result" -# return 0 -# ;; # OK -# 3) return 2 ;; # Back -# *) return 1 ;; # Cancel/Exit -# esac -# } - -# advanced_settings() { -# local step=1 - -# while true; do -# case $step in -# 1) -# show_intro_messages && ((step++)) -# ;; -# 2) -# select_distribution -# result=$? -# [[ $result -eq 0 ]] && ((step++)) -# [[ $result -eq 2 && $step -gt 1 ]] && ((step--)) -# [[ $result -eq 1 ]] && return -# ;; -# 3) -# select_version -# result=$? -# [[ $result -eq 0 ]] && ((step++)) -# [[ $result -eq 2 && $step -gt 1 ]] && ((step--)) -# [[ $result -eq 1 ]] && return -# ;; -# 4) -# select_container_type -# result=$? -# [[ $result -eq 0 ]] && ((step++)) -# [[ $result -eq 2 && $step -gt 1 ]] && ((step--)) -# [[ $result -eq 1 ]] && return -# ;; -# 5) -# set_root_password -# result=$? -# [[ $result -eq 0 ]] && ((step++)) -# [[ $result -eq 2 && $step -gt 1 ]] && ((step--)) -# [[ $result -eq 1 ]] && return -# ;; -# 6) -# set_container_id -# result=$? -# [[ $result -eq 0 ]] && ((step++)) -# [[ $result -eq 2 && $step -gt 1 ]] && ((step--)) -# [[ $result -eq 1 ]] && return -# ;; -# 7) -# set_hostname -# result=$? -# [[ $result -eq 0 ]] && ((step++)) -# [[ $result -eq 2 && $step -gt 1 ]] && ((step--)) -# [[ $result -eq 1 ]] && return -# ;; -# 8) -# set_disk_size -# result=$? -# [[ $result -eq 0 ]] && ((step++)) -# [[ $result -eq 2 && $step -gt 1 ]] && ((step--)) -# [[ $result -eq 1 ]] && return -# ;; -# 9) -# set_cpu_cores -# result=$? -# [[ $result -eq 0 ]] && ((step++)) -# [[ $result -eq 2 && $step -gt 1 ]] && ((step--)) -# [[ $result -eq 1 ]] && return -# ;; -# 10) -# set_ram_size -# result=$? -# [[ $result -eq 0 ]] && ((step++)) -# [[ $result -eq 2 && $step -gt 1 ]] && ((step--)) -# [[ $result -eq 1 ]] && return -# ;; -# 11) -# set_bridge -# result=$? -# [[ $result -eq 0 ]] && ((step++)) -# [[ $result -eq 2 && $step -gt 1 ]] && ((step--)) -# [[ $result -eq 1 ]] && return -# ;; -# 12) -# set_ip_address -# result=$? -# [[ $result -eq 0 ]] && ((step++)) -# [[ $result -eq 2 && $step -gt 1 ]] && ((step--)) -# [[ $result -eq 1 ]] && return -# ;; -# 13) -# set_gateway -# result=$? -# [[ $result -eq 0 ]] && ((step++)) -# [[ $result -eq 2 && $step -gt 1 ]] && ((step--)) -# [[ $result -eq 1 ]] && return -# ;; -# 14) -# set_apt_cacher -# result=$? -# [[ $result -eq 0 ]] && ((step++)) -# [[ $result -eq 2 && $step -gt 1 ]] && ((step--)) -# [[ $result -eq 1 ]] && return -# ;; -# 15) -# toggle_ipv6 -# result=$? -# [[ $result -eq 0 ]] && ((step++)) -# [[ $result -eq 2 && $step -gt 1 ]] && ((step--)) -# [[ $result -eq 1 ]] && return -# ;; -# 16) -# set_mtu -# result=$? -# [[ $result -eq 0 ]] && ((step++)) -# [[ $result -eq 2 && $step -gt 1 ]] && ((step--)) -# [[ $result -eq 1 ]] && return -# ;; -# 17) -# set_dns_search_domain -# result=$? -# [[ $result -eq 0 ]] && ((step++)) -# [[ $result -eq 2 && $step -gt 1 ]] && ((step--)) -# [[ $result -eq 1 ]] && return -# ;; -# 18) -# set_dns_server -# result=$? -# [[ $result -eq 0 ]] && ((step++)) -# [[ $result -eq 2 && $step -gt 1 ]] && ((step--)) -# [[ $result -eq 1 ]] && return -# ;; -# 19) -# set_mac_address -# result=$? -# [[ $result -eq 0 ]] && ((step++)) -# [[ $result -eq 2 && $step -gt 1 ]] && ((step--)) -# [[ $result -eq 1 ]] && return -# ;; -# 20) -# set_vlan -# result=$? -# [[ $result -eq 0 ]] && ((step++)) -# [[ $result -eq 2 && $step -gt 1 ]] && ((step--)) -# [[ $result -eq 1 ]] && return -# ;; -# 21) -# set_tags -# result=$? -# [[ $result -eq 0 ]] && ((step++)) -# [[ $result -eq 2 && $step -gt 1 ]] && ((step--)) -# [[ $result -eq 1 ]] && return -# ;; -# 22) -# set_ssh_access -# result=$? -# [[ $result -eq 0 ]] && ((step++)) -# [[ $result -eq 2 && $step -gt 1 ]] && ((step--)) -# [[ $result -eq 1 ]] && return -# ;; -# 23) -# set_fuse -# result=$? -# [[ $result -eq 0 ]] && ((step++)) -# [[ $result -eq 2 && $step -gt 1 ]] && ((step--)) -# [[ $result -eq 1 ]] && return -# ;; -# 24) -# set_verbose -# result=$? -# [[ $result -eq 0 ]] && ((step++)) -# [[ $result -eq 2 && $step -gt 1 ]] && ((step--)) -# [[ $result -eq 1 ]] && return -# ;; -# 25) -# confirm_creation -# result=$? -# [[ $result -eq 0 ]] && break -# [[ $result -eq 2 && $step -gt 1 ]] && ((step--)) -# [[ $result -eq 1 ]] && return -# ;; -# esac -# done -# } - -# show_intro_messages() { -# dialog --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "Instructional Tip" \ -# --msgbox "To make a selection, use the Spacebar." 8 58 || return 1 - -# dialog --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "Default distribution for $APP" \ -# --msgbox "Default is: ${var_os} ${var_version}\n\nIf the default Linux distribution is not adhered to, script support will be discontinued." 10 58 || return 1 -# return 0 -# } - -# select_distribution() { -# [[ "$var_os" == "alpine" ]] && return 0 - -# local default result exitcode -# default="${var_os:-debian}" -# var_os="" - -# local debian_flag ubuntu_flag -# [[ "$default" == "debian" ]] && debian_flag="on" || debian_flag="off" -# [[ "$default" == "ubuntu" ]] && ubuntu_flag="on" || ubuntu_flag="off" - -# while [[ -z "$var_os" ]]; do -# exec 3>&1 -# result=$(dialog --clear \ -# --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "DISTRIBUTION" \ -# --radiolist "Choose Distribution:" 15 60 4 \ -# "debian" "" "$debian_flag" \ -# "ubuntu" "" "$ubuntu_flag" \ -# --ok-label "Next" \ -# --cancel-label "Exit" \ -# --extra-button \ -# --extra-label "Back" \ -# 2>&1 1>&3) -# exitcode=$? -# exec 3>&- - -# case "$exitcode" in -# 0) -# if [[ "$result" =~ ^(debian|ubuntu)$ ]]; then -# var_os="$result" -# printf "%bOperating System: %b%s%b\n" "$OS$BOLD$DGN" "$BGN" "$var_os" "$CL" -# return 0 -# else -# printf "[DEBUG] No valid selection made (result='%s'), repeating...\n" "$result" -# fi -# ;; -# 3) -# return 2 -# ;; -# 1 | 255) -# return 1 -# ;; -# *) -# printf "[DEBUG] Unexpected exit code: %s\n" "$exitcode" >&2 -# return 1 -# ;; -# esac -# done -# } - -# select_version() { -# local default="${var_version}" -# var_version="" -# local list result exitcode - -# if [[ "$var_os" == "debian" ]]; then -# case "$default" in -# 11) list=("11" "Bullseye" on "12" "Bookworm" off) ;; -# 12) list=("11" "Bullseye" off "12" "Bookworm" on) ;; -# *) list=("11" "Bullseye" off "12" "Bookworm" off) ;; -# esac -# elif [[ "$var_os" == "ubuntu" ]]; then -# case "$default" in -# 20.04) list=("20.04" "Focal" on "22.04" "Jammy" off "24.04" "Noble" off "24.10" "Oracular" off) ;; -# 22.04) list=("20.04" "Focal" off "22.04" "Jammy" on "24.04" "Noble" off "24.10" "Oracular" off) ;; -# 24.04) list=("20.04" "Focal" off "22.04" "Jammy" off "24.04" "Noble" on "24.10" "Oracular" off) ;; -# 24.10) list=("20.04" "Focal" off "22.04" "Jammy" off "24.04" "Noble" off "24.10" "Oracular" on) ;; -# *) list=("20.04" "Focal" off "22.04" "Jammy" off "24.04" "Noble" off "24.10" "Oracular" off) ;; -# esac -# fi - -# result=$(dialog --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "VERSION" \ -# --radiolist "Choose Version:" 15 58 5 \ -# "${list[@]}" \ -# --ok-label "Next" \ -# --cancel-label "Exit" \ -# --extra-button --extra-label "Back" \ -# 3>&1 1>&2 2>&3) - -# exitcode=$? - -# case $exitcode in -# 0) -# var_version="$result" -# printf "%bVersion: %b%s%b\n" "$OSVERSION$BOLD$DGN" "$BGN" "$var_version" "$CL" -# return 0 -# ;; -# 3) return 2 ;; -# *) return 1 ;; -# esac -# } - -# select_container_type() { -# local default="${CT_TYPE}" -# CT_TYPE="" -# local list result exitcode - -# [[ "$default" == "1" ]] && list=("1" "Unprivileged" on "0" "Privileged" off) || list=("1" "Unprivileged" off "0" "Privileged" on) - -# result=$(dialog --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "CONTAINER TYPE" \ -# --radiolist "Choose Type:" 10 58 2 "${list[@]}" \ -# --extra-button --extra-label "Back" --ok-label "Next" --cancel-label "Exit" 3>&1 1>&2 2>&3) - -# exitcode=$? - -# case $exitcode in -# 0) -# CT_TYPE="$result" -# [[ "$CT_TYPE" == "0" ]] && desc="Privileged" || desc="Unprivileged" -# printf "%bContainer Type: %b%s%b\n" "$CONTAINERTYPE$BOLD$DGN" "$BGN" "$desc" "$CL" -# return 0 -# ;; -# 3) return 2 ;; -# *) return 1 ;; -# esac -# } -# set_root_password() { -# local pw1 pw2 exitcode - -# while true; do -# pw1=$(dialog --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "PASSWORD (leave blank for automatic login)" \ -# --insecure --passwordbox "\nSet Root Password (needed for root ssh access)" 10 58 \ -# --extra-button --extra-label "Back" --ok-label "Next" --cancel-label "Exit" 3>&1 1>&2 2>&3) -# exitcode=$? - -# case $exitcode in -# 0) -# if [[ -z "$pw1" ]]; then -# PW1="Automatic Login" -# PW="" -# printf "%bRoot Password: %b%s%b\n" "$VERIFYPW$BOLD$DGN" "$BGN" "$PW1" "$CL" -# return 0 -# fi -# if [[ "$pw1" == *" "* ]]; then -# dialog --msgbox "Password cannot contain spaces. Please try again." 8 58 -# continue -# fi -# if [[ ${#pw1} -lt 5 ]]; then -# dialog --msgbox "Password must be at least 5 characters long. Please try again." 8 58 -# continue -# fi -# pw2=$(dialog --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "PASSWORD VERIFICATION" \ -# --insecure --passwordbox "\nVerify Root Password" 10 58 \ -# --extra-button --extra-label "Back" --ok-label "Next" --cancel-label "Exit" 3>&1 1>&2 2>&3) -# exitcode=$? -# case $exitcode in -# 0) -# if [[ "$pw1" == "$pw2" ]]; then -# PW="-password $pw1" -# printf "%bRoot Password: %b********%b\n" "$VERIFYPW$BOLD$DGN" "$BGN" "$CL" -# return 0 -# else -# dialog --msgbox "Passwords do not match. Please try again." 8 58 -# continue -# fi -# ;; -# 3) return 2 ;; -# *) return 1 ;; -# esac -# ;; -# 3) return 2 ;; -# *) return 1 ;; -# esac -# done -# } - -# set_container_id() { -# local result exitcode -# result=$(dialog --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "CONTAINER ID" \ -# --inputbox "Set Container ID" 8 58 "$NEXTID" \ -# --extra-button --extra-label "Back" --ok-label "Next" --cancel-label "Exit" 3>&1 1>&2 2>&3) -# exitcode=$? - -# case $exitcode in -# 0) -# CT_ID="${result:-$NEXTID}" -# printf "%bContainer ID: %b%s%b\n" "$CONTAINERID$BOLD$DGN" "$BGN" "$CT_ID" "$CL" -# return 0 -# ;; -# 3) return 2 ;; -# *) return 1 ;; -# esac -# } - -# set_hostname() { -# local result exitcode -# result=$(dialog --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "HOSTNAME" \ -# --inputbox "Set Hostname" 8 58 "$NSAPP" \ -# --extra-button --extra-label "Back" --ok-label "Next" --cancel-label "Exit" 3>&1 1>&2 2>&3) -# exitcode=$? - -# case $exitcode in -# 0) -# if [[ -z "$result" ]]; then -# HN="$NSAPP" -# else -# HN=$(tr -d ' ' <<<"${result,,}") -# fi -# printf "%bHostname: %b%s%b\n" "$HOSTNAME$BOLD$DGN" "$BGN" "$HN" "$CL" -# return 0 -# ;; -# 3) return 2 ;; -# *) return 1 ;; -# esac -# } - -# set_disk_size() { -# local result exitcode -# result=$(dialog --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "DISK SIZE" \ -# --inputbox "Set Disk Size in GB" 8 58 "$var_disk" \ -# --extra-button --extra-label "Back" --ok-label "Next" --cancel-label "Exit" 3>&1 1>&2 2>&3) -# exitcode=$? - -# case $exitcode in -# 0) -# if [[ -z "$result" ]]; then -# DISK_SIZE="$var_disk" -# elif [[ "$result" =~ ^[0-9]+$ ]]; then -# DISK_SIZE="$result" -# else -# dialog --msgbox "Disk size must be an integer!" 8 58 -# return 2 -# fi -# printf "%bDisk Size: %b%s GB%b\n" "$DISKSIZE$BOLD$DGN" "$BGN" "$DISK_SIZE" "$CL" -# return 0 -# ;; -# 3) return 2 ;; -# *) return 1 ;; -# esac -# } - -# set_cpu_cores() { -# local result exitcode -# result=$(dialog --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "CORE COUNT" \ -# --inputbox "Allocate CPU Cores" 8 58 "$var_cpu" \ -# --extra-button --extra-label "Back" --ok-label "Next" --cancel-label "Exit" 3>&1 1>&2 2>&3) -# exitcode=$? - -# case $exitcode in -# 0) -# CORE_COUNT="${result:-$var_cpu}" -# printf "%bCPU Cores: %b%s%b\n" "$CPUCORE$BOLD$DGN" "$BGN" "$CORE_COUNT" "$CL" -# return 0 -# ;; -# 3) return 2 ;; -# *) return 1 ;; -# esac -# } - -# set_ram_size() { -# local result exitcode -# result=$(dialog --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "RAM" \ -# --inputbox "Allocate RAM in MiB" 8 58 "$var_ram" \ -# --extra-button --extra-label "Back" --ok-label "Next" --cancel-label "Exit" 3>&1 1>&2 2>&3) -# exitcode=$? - -# case $exitcode in -# 0) -# RAM_SIZE="${result:-$var_ram}" -# printf "%bRAM Size: %b%s MiB%b\n" "$RAMSIZE$BOLD$DGN" "$BGN" "$RAM_SIZE" "$CL" -# return 0 -# ;; -# 3) return 2 ;; -# *) return 1 ;; -# esac -# } - -# set_bridge() { -# local result exitcode -# result=$(dialog --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "BRIDGE" \ -# --inputbox "Set a Bridge" 8 58 "vmbr0" \ -# --extra-button --extra-label "Back" --ok-label "Next" --cancel-label "Exit" 3>&1 1>&2 2>&3) -# exitcode=$? - -# case $exitcode in -# 0) -# BRG="${result:-vmbr0}" -# printf "%bBridge: %b%s%b\n" "$BRIDGE$BOLD$DGN" "$BGN" "$BRG" "$CL" -# return 0 -# ;; -# 3) return 2 ;; -# *) return 1 ;; -# esac -# } - -# set_ip_address() { -# local result exitcode -# while true; do -# result=$(dialog --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "IP ADDRESS" \ -# --inputbox "Set a Static IPv4 CIDR Address (/24)" 8 58 "dhcp" \ -# --extra-button --extra-label "Back" --ok-label "Next" --cancel-label "Exit" 3>&1 1>&2 2>&3) -# exitcode=$? - -# case $exitcode in -# 0) -# if [[ "$result" == "dhcp" ]]; then -# NET="dhcp" -# printf "%bIP Address: %b%s%b\n" "$NETWORK$BOLD$DGN" "$BGN" "$NET" "$CL" -# return 0 -# elif [[ "$result" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then -# NET="$result" -# printf "%bIP Address: %b%s%b\n" "$NETWORK$BOLD$DGN" "$BGN" "$NET" "$CL" -# return 0 -# else -# dialog --msgbox "$result is an invalid IPv4 CIDR address. Please enter a valid address or 'dhcp'." 8 58 -# continue -# fi -# ;; -# 3) return 2 ;; -# *) return 1 ;; -# esac -# done -# } - -# set_gateway() { -# local result exitcode -# if [[ "$NET" == "dhcp" ]]; then -# GATE="" -# printf "%bGateway IP Address: %bDefault%b\n" "$GATEWAY$BOLD$DGN" "$BGN" "$CL" -# return 0 -# fi - -# while true; do -# result=$(dialog --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "Gateway IP" \ -# --inputbox "Enter gateway IP address" 8 58 \ -# --extra-button --extra-label "Back" --ok-label "Next" --cancel-label "Exit" 3>&1 1>&2 2>&3) -# exitcode=$? - -# case $exitcode in -# 0) -# if [[ -z "$result" ]]; then -# dialog --msgbox "Gateway IP address cannot be empty" 8 58 -# continue -# elif [[ "$result" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then -# GATE=",gw=$result" -# printf "%bGateway IP Address: %b%s%b\n" "$GATEWAY$BOLD$DGN" "$BGN" "$result" "$CL" -# return 0 -# else -# dialog --msgbox "Invalid IP address format" 8 58 -# fi -# ;; -# 3) return 2 ;; -# *) return 1 ;; -# esac -# done -# } - -# set_apt_cacher() { -# local result exitcode -# if [[ "$var_os" == "alpine" ]]; then -# APT_CACHER="" -# APT_CACHER_IP="" -# return 0 -# fi - -# result=$(dialog --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "APT-Cacher IP" \ -# --inputbox "Set APT-Cacher IP (leave blank for none)" 8 58 \ -# --extra-button --extra-label "Back" --ok-label "Next" --cancel-label "Exit" 3>&1 1>&2 2>&3) -# exitcode=$? - -# case $exitcode in -# 0) -# APT_CACHER_IP="$result" -# APT_CACHER="${APT_CACHER_IP:+yes}" -# printf "%bAPT-Cacher IP Address: %b%s%b\n" "$NETWORK$BOLD$DGN" "$BGN" "${APT_CACHER_IP:-Default}" "$CL" -# return 0 -# ;; -# 3) return 2 ;; -# *) return 1 ;; -# esac -# } - -# toggle_ipv6() { -# dialog --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "IPv6" \ -# --yesno "Disable IPv6?" 10 58 \ -# --extra-button --extra-label "Back" --ok-label "Yes" --cancel-label "No" -# case $? in -# 0) DISABLEIP6="yes" ;; -# 1) DISABLEIP6="no" ;; -# 3) return 2 ;; -# *) return 1 ;; -# esac -# printf "%bDisable IPv6: %b%s%b\n" "$DISABLEIPV6$BOLD$DGN" "$BGN" "$DISABLEIP6" "$CL" -# return 0 -# } -# set_mtu() { -# local result exitcode -# result=$(dialog --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "MTU SIZE" \ -# --inputbox "Set Interface MTU Size (leave blank for default [1500])" 8 58 "" \ -# --extra-button --extra-label "Back" --ok-label "Next" --cancel-label "Exit" 3>&1 1>&2 2>&3) -# exitcode=$? - -# case $exitcode in -# 0) -# if [[ -z "$result" ]]; then -# MTU1="Default" -# MTU="" -# else -# MTU1="$result" -# MTU=",mtu=$MTU1" -# fi -# printf "%bInterface MTU Size: %b%s%b\n" "$DEFAULT$BOLD$DGN" "$BGN" "$MTU1" "$CL" -# return 0 -# ;; -# 3) return 2 ;; -# *) return 1 ;; -# esac -# } - -# set_dns_search_domain() { -# local result exitcode -# result=$(dialog --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "DNS Search Domain" \ -# --inputbox "Set a DNS Search Domain (leave blank for HOST)" 8 58 "" \ -# --extra-button --extra-label "Back" --ok-label "Next" --cancel-label "Exit" 3>&1 1>&2 2>&3) -# exitcode=$? - -# case $exitcode in -# 0) -# if [[ -z "$result" ]]; then -# SX="Host" -# SD="" -# else -# SX="$result" -# SD="-searchdomain=$result" -# fi -# printf "%bDNS Search Domain: %b%s%b\n" "$SEARCH$BOLD$DGN" "$BGN" "$SX" "$CL" -# return 0 -# ;; -# 3) return 2 ;; -# *) return 1 ;; -# esac -# } - -# set_dns_server() { -# local result exitcode -# result=$(dialog --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "DNS SERVER IP" \ -# --inputbox "Set a DNS Server IP (leave blank for HOST)" 8 58 "" \ -# --extra-button --extra-label "Back" --ok-label "Next" --cancel-label "Exit" 3>&1 1>&2 2>&3) -# exitcode=$? - -# case $exitcode in -# 0) -# if [[ -z "$result" ]]; then -# NX="Host" -# NS="" -# else -# NX="$result" -# NS="-nameserver=$result" -# fi -# printf "%bDNS Server IP Address: %b%s%b\n" "$NETWORK$BOLD$DGN" "$BGN" "$NX" "$CL" -# return 0 -# ;; -# 3) return 2 ;; -# *) return 1 ;; -# esac -# } - -# set_mac_address() { -# local result exitcode -# result=$(dialog --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "MAC ADDRESS" \ -# --inputbox "Set a MAC Address (leave blank for generated MAC)" 8 58 "" \ -# --extra-button --extra-label "Back" --ok-label "Next" --cancel-label "Exit" 3>&1 1>&2 2>&3) -# exitcode=$? - -# case $exitcode in -# 0) -# if [[ -z "$result" ]]; then -# MAC1="Default" -# MAC="" -# else -# MAC1="$result" -# MAC=",hwaddr=$MAC1" -# fi -# printf "%bMAC Address: %b%s%b\n" "$MACADDRESS$BOLD$DGN" "$BGN" "$MAC1" "$CL" -# return 0 -# ;; -# 3) return 2 ;; -# *) return 1 ;; -# esac -# } - -# set_vlan() { -# local result exitcode -# result=$(dialog --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "VLAN" \ -# --inputbox "Set a VLAN (leave blank for no VLAN)" 8 58 "" \ -# --extra-button --extra-label "Back" --ok-label "Next" --cancel-label "Exit" 3>&1 1>&2 2>&3) -# exitcode=$? - -# case $exitcode in -# 0) -# if [[ -z "$result" ]]; then -# VLAN1="Default" -# VLAN="" -# else -# VLAN1="$result" -# VLAN=",tag=$VLAN1" -# fi -# printf "%bVlan: %b%s%b\n" "$VLANTAG$BOLD$DGN" "$BGN" "$VLAN1" "$CL" -# return 0 -# ;; -# 3) return 2 ;; -# *) return 1 ;; -# esac -# } - -# set_tags() { -# local result exitcode -# result=$(dialog --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "Advanced Tags" \ -# --inputbox "Set Custom Tags? [If you remove all, there will be no tags!]" 8 58 "$TAGS" \ -# --extra-button --extra-label "Back" --ok-label "Next" --cancel-label "Exit" 3>&1 1>&2 2>&3) -# exitcode=$? - -# case $exitcode in -# 0) -# if [[ -n "$result" ]]; then -# ADV_TAGS=$(tr -d '[:space:]' <<<"$result") -# TAGS="$ADV_TAGS" -# else -# TAGS=";" -# fi -# printf "%bTags: %b%s%b\n" "$NETWORK$BOLD$DGN" "$BGN" "$TAGS" "$CL" -# return 0 -# ;; -# 3) return 2 ;; -# *) return 1 ;; -# esac -# } - -# set_ssh_access() { -# local result exitcode - -# if [[ "$PW" == -password* ]]; then -# dialog --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "SSH ACCESS" \ -# --yesno "Enable Root SSH Access?" 10 58 \ -# --extra-button --extra-label "Back" --ok-label "Yes" --cancel-label "No" -# exitcode=$? -# case $exitcode in -# 0) SSH="yes" ;; -# 1) SSH="no" ;; -# 3) return 2 ;; -# *) return 1 ;; -# esac -# else -# SSH="no" -# fi - -# printf "%bRoot SSH Access: %b%s%b\n" "$ROOTSSH$BOLD$DGN" "$BGN" "$SSH" "$CL" - -# if [[ "$SSH" == "yes" ]]; then -# result=$(dialog --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "SSH Key" \ -# --inputbox "SSH Authorized key for root (leave empty for none)" 8 58 "" \ -# --extra-button --extra-label "Back" --ok-label "Next" --cancel-label "Exit" 3>&1 1>&2 2>&3) -# exitcode=$? -# case $exitcode in -# 0) -# SSH_AUTHORIZED_KEY="$result" -# return 0 -# ;; -# 3) return 2 ;; -# *) return 1 ;; -# esac -# else -# SSH_AUTHORIZED_KEY="" -# return 0 -# fi -# } - -# set_fuse() { -# dialog --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "FUSE Support" \ -# --yesno "Enable FUSE (Filesystem in Userspace) support in the container?" 10 58 \ -# --extra-button --extra-label "Back" --ok-label "Yes" --cancel-label "No" -# case $? in -# 0) ENABLE_FUSE="yes" ;; -# 1) ENABLE_FUSE="no" ;; -# 3) return 2 ;; -# *) return 1 ;; -# esac -# printf "%bFUSE (Filesystem in Userspace) Support: %b%s%b\n" "$FUSE$BOLD$DGN" "$BGN" "$ENABLE_FUSE" "$CL" -# return 0 -# } - -# set_verbose() { -# dialog --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "VERBOSE MODE" \ -# --yesno "Enable Verbose Mode?" 10 58 \ -# --extra-button --extra-label "Back" --ok-label "Yes" --cancel-label "No" -# case $? in -# 0) VERB="yes" ;; -# 1) VERB="no" ;; -# 3) return 2 ;; -# *) return 1 ;; -# esac -# printf "%bVerbose Mode: %b%s%b\n" "$SEARCH$BOLD$DGN" "$BGN" "$VERB" "$CL" -# return 0 -# } - -# confirm_creation() { -# dialog --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "ADVANCED SETTINGS COMPLETE" \ -# --yesno "Ready to create ${APP} LXC?" 10 58 \ -# --extra-button --extra-label "Back" --ok-label "Yes" --cancel-label "No" -# case $? in -# 0) -# printf "%bCreating a %s LXC using the above advanced settings%b\n" "$CREATING$BOLD$RD" "$APP" "$CL" -# return 0 -# ;; -# 3) return 2 ;; -# *) return 1 ;; -# esac -# } diff --git a/misc/backup_07052025/install copy.func b/misc/backup_07052025/install copy.func deleted file mode 100644 index d2042d5d7..000000000 --- a/misc/backup_07052025/install copy.func +++ /dev/null @@ -1,281 +0,0 @@ -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) -# Co-Author: MickLesk -# Co-Author: michelroegl-brunner -# License: MIT -# https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE - -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) -load_functions - -# color() { -# # Colors -# YW=$(echo "\033[33m") -# YWB=$(echo "\033[93m") -# BL=$(echo "\033[36m") -# RD=$(echo "\033[01;31m") -# GN=$(echo "\033[1;92m") - -# # Formatting -# CL=$(echo "\033[m") -# BFR="\\r\\033[K" -# BOLD=$(echo "\033[1m") -# HOLD=" " -# TAB=" " - -# # System -# RETRY_NUM=10 -# RETRY_EVERY=3 - -# # Icons -# CM="${TAB}✔️${TAB}${CL}" -# CROSS="${TAB}✖️${TAB}${CL}" -# INFO="${TAB}💡${TAB}${CL}" -# NETWORK="${TAB}📡${TAB}${CL}" -# OS="${TAB}🖥️${TAB}${CL}" -# OSVERSION="${TAB}🌟${TAB}${CL}" -# HOSTNAME="${TAB}🏠${TAB}${CL}" -# GATEWAY="${TAB}🌐${TAB}${CL}" -# DEFAULT="${TAB}⚙️${TAB}${CL}" -# } - -# Function to set STD mode based on verbosity -set_std_mode() { - if [ "$VERBOSE" = "yes" ]; then - STD="" - else - STD="silent" - fi -} - -# Silent execution function -silent() { - "$@" >/dev/null 2>&1 -} - -# This function enables IPv6 if it's not disabled and sets verbose mode -verb_ip6() { - set_std_mode # Set STD mode based on VERBOSE - - if [ "$DISABLEIPV6" == "yes" ]; then - echo "net.ipv6.conf.all.disable_ipv6 = 1" >>/etc/sysctl.conf - $STD sysctl -p - fi -} - -# This function sets error handling options and defines the error_handler function to handle errors -catch_errors() { - set -Eeuo pipefail - trap 'error_handler $LINENO "$BASH_COMMAND"' ERR -} - -# This function handles errors -error_handler() { - source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) - if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi - printf "\e[?25h" - local exit_code="$?" - local line_number="$1" - local command="$2" - local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" - echo -e "\n$error_message" - [[ -n "${SPINNER_PID:-}" ]] && kill "$SPINNER_PID" &>/dev/null || true - - if [[ "$line_number" -eq 50 ]]; then - echo -e "The silent function has suppressed the error, run the script with verbose mode enabled, which will provide more detailed output.\n" - post_update_to_api "failed" "No error message, script ran in silent mode" - else - post_update_to_api "failed" "${command}" - fi -} - -# # This function displays a spinner. -# spinner() { -# local frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏') -# local spin_i=0 -# local interval=0.1 -# printf "\e[?25l" - -# local color="${YWB}" - -# while true; do -# printf "\r ${color}%s${CL}" "${frames[spin_i]}" -# spin_i=$(((spin_i + 1) % ${#frames[@]})) -# sleep "$interval" -# done -# } - -# # This function displays an informational message with a yellow color. -# msg_info() { -# local msg="$1" -# echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" -# spinner & -# SPINNER_PID=$! -# } - -# # This function displays a success message with a green color. -# msg_ok() { -# if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi -# printf "\e[?25h" -# local msg="$1" -# echo -e "${BFR}${CM}${GN}${msg}${CL}" -# } - -# # This function displays a error message with a red color. -# msg_error() { -# if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi -# printf "\e[?25h" -# local msg="$1" -# echo -e "${BFR}${CROSS}${RD}${msg}${CL}" -# } - -# This function sets up the Container OS by generating the locale, setting the timezone, and checking the network connection -setting_up_container() { - msg_info "Setting up Container OS" - sed -i "/$LANG/ s/\(^# \)//" /etc/locale.gen - locale_line=$(grep -v '^#' /etc/locale.gen | grep -E '^[a-zA-Z]' | awk '{print $1}' | head -n 1) - echo "LANG=${locale_line}" >/etc/default/locale - locale-gen >/dev/null - export LANG=${locale_line} - echo $tz >/etc/timezone - ln -sf /usr/share/zoneinfo/$tz /etc/localtime - for ((i = RETRY_NUM; i > 0; i--)); do - if [ "$(hostname -I)" != "" ]; then - break - fi - echo 1>&2 -en "${CROSS}${RD} No Network! " - sleep $RETRY_EVERY - done - if [ "$(hostname -I)" = "" ]; then - echo 1>&2 -e "\n${CROSS}${RD} No Network After $RETRY_NUM Tries${CL}" - echo -e "${NETWORK}Check Network Settings" - exit 1 - fi - rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED - systemctl disable -q --now systemd-networkd-wait-online.service - msg_ok "Set up Container OS" - msg_ok "Network Connected: ${BL}$(hostname -I)" -} - -# This function checks the network connection by pinging a known IP address and prompts the user to continue if the internet is not connected -network_check() { - set +e - trap - ERR - ipv4_connected=false - ipv6_connected=false - sleep 1 - # Check IPv4 connectivity to Google, Cloudflare & Quad9 DNS servers. - if ping -c 1 -W 1 1.1.1.1 &>/dev/null || ping -c 1 -W 1 8.8.8.8 &>/dev/null || ping -c 1 -W 1 9.9.9.9 &>/dev/null; then - msg_ok "IPv4 Internet Connected" - ipv4_connected=true - else - msg_error "IPv4 Internet Not Connected" - fi - - # Check IPv6 connectivity to Google, Cloudflare & Quad9 DNS servers. - if ping6 -c 1 -W 1 2606:4700:4700::1111 &>/dev/null || ping6 -c 1 -W 1 2001:4860:4860::8888 &>/dev/null || ping6 -c 1 -W 1 2620:fe::fe &>/dev/null; then - msg_ok "IPv6 Internet Connected" - ipv6_connected=true - else - msg_error "IPv6 Internet Not Connected" - fi - - # If both IPv4 and IPv6 checks fail, prompt the user - if [[ $ipv4_connected == false && $ipv6_connected == false ]]; then - read -r -p "No Internet detected,would you like to continue anyway? " prompt - if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then - echo -e "${INFO}${RD}Expect Issues Without Internet${CL}" - else - echo -e "${NETWORK}Check Network Settings" - exit 1 - fi - fi - - RESOLVEDIP=$(getent hosts github.com | awk '{ print $1 }') - if [[ -z "$RESOLVEDIP" ]]; then msg_error "DNS Lookup Failure"; else msg_ok "DNS Resolved github.com to ${BL}$RESOLVEDIP${CL}"; fi - set -e - trap 'error_handler $LINENO "$BASH_COMMAND"' ERR -} - -# This function updates the Container OS by running apt-get update and upgrade -update_os() { - msg_info "Updating Container OS" - if [[ "$CACHER" == "yes" ]]; then - echo "Acquire::http::Proxy-Auto-Detect \"/usr/local/bin/apt-proxy-detect.sh\";" >/etc/apt/apt.conf.d/00aptproxy - cat </usr/local/bin/apt-proxy-detect.sh -#!/bin/bash -if nc -w1 -z "${CACHER_IP}" 3142; then - echo -n "http://${CACHER_IP}:3142" -else - echo -n "DIRECT" -fi -EOF - chmod +x /usr/local/bin/apt-proxy-detect.sh - fi - $STD apt-get update - $STD apt-get -o Dpkg::Options::="--force-confold" -y dist-upgrade - rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED - msg_ok "Updated Container OS" - - msg_info "Installing core dependencies" - $STD apt-get update - $STD apt-get install -y sudo curl mc gnupg2 - msg_ok "Core dependencies installed" - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) - -} - -# This function modifies the message of the day (motd) and SSH settings -motd_ssh() { - grep -qxF "export TERM='xterm-256color'" /root/.bashrc || echo "export TERM='xterm-256color'" >>/root/.bashrc - - if [ -f "/etc/os-release" ]; then - OS_NAME=$(grep ^NAME /etc/os-release | cut -d= -f2 | tr -d '"') - OS_VERSION=$(grep ^VERSION_ID /etc/os-release | cut -d= -f2 | tr -d '"') - elif [ -f "/etc/debian_version" ]; then - OS_NAME="Debian" - OS_VERSION=$(cat /etc/debian_version) - fi - - PROFILE_FILE="/etc/profile.d/00_lxc-details.sh" - echo "echo -e \"\"" >"$PROFILE_FILE" - echo -e "echo -e \"${BOLD}${YW}${APPLICATION} LXC Container - DEV Repository${CL}\"" >>"$PROFILE_FILE" - echo -e "echo -e \"${RD}WARNING: This is a DEVELOPMENT version (ProxmoxVED). Do NOT use in production!${CL}\"" >>"$PROFILE_FILE" - echo -e "echo -e \"${YW} OS: ${GN}${OS_NAME} - Version: ${OS_VERSION}${CL}\"" >>"$PROFILE_FILE" - echo -e "echo -e \"${YW} Hostname: ${GN}\$(hostname)${CL}\"" >>"$PROFILE_FILE" - echo -e "echo -e \"${YW} IP Address: ${GN}\$(hostname -I | awk '{print \$1}')${CL}\"" >>"$PROFILE_FILE" - echo -e "echo -e \"${YW} Repository: ${GN}https://github.com/community-scripts/ProxmoxVED${CL}\"" >>"$PROFILE_FILE" - echo "echo \"\"" >>"$PROFILE_FILE" - - chmod -x /etc/update-motd.d/* - - if [[ "${SSH_ROOT}" == "yes" ]]; then - sed -i "s/#PermitRootLogin prohibit-password/PermitRootLogin yes/g" /etc/ssh/sshd_config - systemctl restart sshd - fi -} - -# This function customizes the container by modifying the getty service and enabling auto-login for the root user -customize() { - if [[ "$PASSWORD" == "" ]]; then - msg_info "Customizing Container" - GETTY_OVERRIDE="/etc/systemd/system/container-getty@1.service.d/override.conf" - mkdir -p $(dirname $GETTY_OVERRIDE) - cat <$GETTY_OVERRIDE - [Service] - ExecStart= - ExecStart=-/sbin/agetty --autologin root --noclear --keep-baud tty%I 115200,38400,9600 \$TERM -EOF - systemctl daemon-reload - systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//') - msg_ok "Customized Container" - fi - echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update - chmod +x /usr/bin/update - if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then - mkdir -p /root/.ssh - echo "${SSH_AUTHORIZED_KEY}" >/root/.ssh/authorized_keys - chmod 700 /root/.ssh - chmod 600 /root/.ssh/authorized_keys - fi -} diff --git a/misc/backup_07052025/msg.func b/misc/backup_07052025/msg.func deleted file mode 100644 index 1257d0d2e..000000000 --- a/misc/backup_07052025/msg.func +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env bash - -# Spinner state -declare -A SPINNER_PIDS -declare -A SPINNER_MSGS -declare -A MSG_SHOWN - -# Color definitions (adjust as needed) -RD='\033[0;31m' -GN='\033[0;32m' -YW='\033[0;33m' -CL='\033[0m' -CM='✔' -CROSS='✘' - -# Trap cleanup -trap cleanup_spinners EXIT INT TERM HUP - -# Hash function for message ID -msg_hash() { - local input="$1" - echo -n "$input" | sha1sum | awk '{print $1}' -} - -# Start a spinner for a specific message -start_spinner_for_msg() { - local msg="$1" - local id - id=$(msg_hash "$msg") - - [[ -n "${MSG_SHOWN["$id"]+x}" ]] && return - MSG_SHOWN["$id"]=1 - SPINNER_MSGS["$id"]="$msg" - - local frames=(⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏) - local interval=0.1 - local spin_i=0 - - { - while true; do - printf "\r\e[2K%s %b" "${frames[spin_i]}" "${YW}${msg}${CL}" >&2 - spin_i=$(((spin_i + 1) % ${#frames[@]})) - sleep "$interval" - done - } & - - SPINNER_PIDS["$id"]=$! - disown "${SPINNER_PIDS["$id"]}" -} - -# Stop the spinner for a specific message -stop_spinner_for_msg() { - local msg="$1" - local id - id=$(msg_hash "$msg") - - if [[ -n "${SPINNER_PIDS["$id"]+x}" ]] && ps -p "${SPINNER_PIDS["$id"]}" >/dev/null 2>&1; then - kill "${SPINNER_PIDS["$id"]}" 2>/dev/null - wait "${SPINNER_PIDS["$id"]}" 2>/dev/null || true - fi - - unset SPINNER_PIDS["$id"] - unset SPINNER_MSGS["$id"] - unset MSG_SHOWN["$id"] -} - -# Cleanup all active spinners -cleanup_spinners() { - for id in "${!SPINNER_PIDS[@]}"; do - if ps -p "${SPINNER_PIDS[$id]}" >/dev/null 2>&1; then - kill "${SPINNER_PIDS[$id]}" 2>/dev/null - wait "${SPINNER_PIDS[$id]}" 2>/dev/null || true - fi - unset SPINNER_PIDS["$id"] - unset SPINNER_MSGS["$id"] - unset MSG_SHOWN["$id"] - done -} - -# Show info message with spinner -msg_info() { - local msg="$1" - start_spinner_for_msg "$msg" -} - -# End spinner and show success message -msg_ok() { - local msg="$1" - stop_spinner_for_msg "$msg" - printf "\r\e[2K%s %b\n" "${CM}" "${GN}${msg}${CL}" >&2 -} - -# End spinner and show error message -msg_error() { - local msg="$1" - stop_spinner_for_msg "$msg" - printf "\r\e[2K%s %b\n" "${CROSS}" "${RD}${msg}${CL}" >&2 -} From 1617f334d58af43f8b3ac2e39a7ac34c3c53c807 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 12:11:08 +0200 Subject: [PATCH 0055/1733] Create add-iptag.sh --- tools/pve/add-iptag.sh | 1359 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1359 insertions(+) create mode 100644 tools/pve/add-iptag.sh diff --git a/tools/pve/add-iptag.sh b/tools/pve/add-iptag.sh new file mode 100644 index 000000000..49bf1a900 --- /dev/null +++ b/tools/pve/add-iptag.sh @@ -0,0 +1,1359 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: MickLesk (Canbiz) && Desert_Gamer +# License: MIT + +function header_info { + clear + cat <<"EOF" + ___ ____ _____ +|_ _| _ \ _ |_ _|_ _ __ _ + | || |_) (_) | |/ _` |/ _` | + | || __/ _ | | (_| | (_| | +|___|_| (_) |_|\__,_|\__, | + |___/ +EOF +} + +clear +header_info +APP="IP-Tag" +hostname=$(hostname) + +# Color variables +YW=$(echo "\033[33m") +GN=$(echo "\033[1;92m") +RD=$(echo "\033[01;31m") +CL=$(echo "\033[m") +BFR="\\r\\033[K" +HOLD=" " +CM=" ✔️ ${CL}" +CROSS=" ✖️ ${CL}" + +# Error handler for displaying error messages +error_handler() { + if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then + kill $SPINNER_PID >/dev/null + fi + printf "\e[?25h" + local exit_code="$?" + local line_number="$1" + local command="$2" + local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" + echo -e "\n$error_message\n" +} + +# Spinner for progress indication +spinner() { + local frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏') + local spin_i=0 + local interval=0.1 + printf "\e[?25l" + + local color="${YWB}" + + while true; do + printf "\r ${color}%s${CL}" "${frames[spin_i]}" + spin_i=$(((spin_i + 1) % ${#frames[@]})) + sleep "$interval" + done +} + +# Info message +msg_info() { + local msg="$1" + echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" + spinner & + SPINNER_PID=$! +} + +# Success message +msg_ok() { + if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then + kill $SPINNER_PID >/dev/null + fi + printf "\e[?25h" + local msg="$1" + echo -e "${BFR}${CM}${GN}${msg}${CL}" +} + +# Error message +msg_error() { + if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then + kill $SPINNER_PID >/dev/null + fi + printf "\e[?25h" + local msg="$1" + echo -e "${BFR}${CROSS}${RD}${msg}${CL}" +} + +# Check if service exists +check_service_exists() { + if systemctl is-active --quiet iptag.service; then + return 0 + else + return 1 + fi +} + +# Migrate configuration from old path to new +migrate_config() { + local old_config="/opt/lxc-iptag" + local new_config="/opt/iptag/iptag.conf" + + if [[ -f "$old_config" ]]; then + msg_info "Migrating configuration from old path" + if cp "$old_config" "$new_config" &>/dev/null; then + rm -rf "$old_config" &>/dev/null + msg_ok "Configuration migrated and old config removed" + else + msg_error "Failed to migrate configuration" + fi + fi +} + +# Update existing installation +update_installation() { + msg_info "Updating IP-Tag Scripts" + systemctl stop iptag.service &>/dev/null + msg_ok "Stopped IP-Tag service" + + # Create directory if it doesn't exist + if [[ ! -d "/opt/iptag" ]]; then + mkdir -p /opt/iptag + fi + + # Create new config file (check if exists and ask user) + if [[ -f "/opt/iptag/iptag.conf" ]]; then + echo -e "\n${YW}Configuration file already exists.${CL}" + while true; do + read -p "Do you want to replace it with defaults? (y/n): " yn + case $yn in + [Yy]*) + msg_info "Replacing configuration file" + generate_config >/opt/iptag/iptag.conf + msg_ok "Configuration file replaced with defaults" + break + ;; + [Nn]*) + echo -e "${GN}✔️ Keeping existing configuration file${CL}" + break + ;; + *) + echo -e "${RD}Please answer yes or no.${CL}" + ;; + esac + done + else + msg_info "Creating new configuration file" + generate_config >/opt/iptag/iptag.conf + msg_ok "Created new configuration file at /opt/iptag/iptag.conf" + fi + + # Update main script + msg_info "Updating main script" + generate_main_script >/opt/iptag/iptag + chmod +x /opt/iptag/iptag + msg_ok "Updated main script" + + # Update service file + msg_info "Updating service file" + generate_service >/lib/systemd/system/iptag.service + msg_ok "Updated service file" + + msg_info "Creating manual run command" + cat <<'EOF' >/usr/local/bin/iptag-run +#!/usr/bin/env bash +CONFIG_FILE="/opt/iptag/iptag.conf" +SCRIPT_FILE="/opt/iptag/iptag" +if [[ ! -f "$SCRIPT_FILE" ]]; then + echo "❌ Main script not found: $SCRIPT_FILE" + exit 1 +fi +export FORCE_SINGLE_RUN=true +exec "$SCRIPT_FILE" +EOF + chmod +x /usr/local/bin/iptag-run + msg_ok "Created iptag-run executable - You can execute this manually by entering “iptag-run” in the Proxmox host, so the script is executed by hand." + + msg_info "Restarting service" + systemctl daemon-reload &>/dev/null + systemctl enable -q --now iptag.service &>/dev/null + msg_ok "Updated IP-Tag Scripts" +} + +# Generate configuration file content +generate_config() { + cat <&2 + fi +} + +# Color constants +readonly RED='\033[0;31m' +readonly GREEN='\033[0;32m' +readonly YELLOW='\033[0;33m' +readonly BLUE='\033[0;34m' +readonly PURPLE='\033[0;35m' +readonly CYAN='\033[0;36m' +readonly WHITE='\033[1;37m' +readonly GRAY='\033[0;37m' +readonly NC='\033[0m' # No Color + +# Logging functions with colors +log_success() { + echo -e "${GREEN}✓${NC} $*" +} + +log_info() { + echo -e "${BLUE}ℹ${NC} $*" +} + +log_warning() { + echo -e "${YELLOW}⚠${NC} $*" +} + +log_error() { + echo -e "${RED}✗${NC} $*" +} + +log_change() { + echo -e "${CYAN}~${NC} $*" +} + +log_unchanged() { + echo -e "${GRAY}=${NC} $*" +} + +# Check if IP is in CIDR +ip_in_cidr() { + local ip="$1" cidr="$2" + debug_log "ip_in_cidr: checking '$ip' against '$cidr'" + + # Manual CIDR check - более надёжный метод + debug_log "ip_in_cidr: using manual check (bypassing ipcalc)" + local network prefix + IFS='/' read -r network prefix <<< "$cidr" + + # Convert IP and network to integers for comparison + local ip_int net_int mask + IFS='.' read -r a b c d <<< "$ip" + ip_int=$(( (a << 24) + (b << 16) + (c << 8) + d )) + + IFS='.' read -r a b c d <<< "$network" + net_int=$(( (a << 24) + (b << 16) + (c << 8) + d )) + + # Create subnet mask + mask=$(( 0xFFFFFFFF << (32 - prefix) )) + + # Apply mask and compare + local ip_masked=$((ip_int & mask)) + local net_masked=$((net_int & mask)) + + debug_log "ip_in_cidr: IP=$ip ($ip_int), Network=$network ($net_int), Prefix=$prefix" + debug_log "ip_in_cidr: Mask=$mask (hex: $(printf '0x%08x' $mask))" + debug_log "ip_in_cidr: IP&Mask=$ip_masked ($(printf '%d.%d.%d.%d' $((ip_masked>>24&255)) $((ip_masked>>16&255)) $((ip_masked>>8&255)) $((ip_masked&255))))" + debug_log "ip_in_cidr: Net&Mask=$net_masked ($(printf '%d.%d.%d.%d' $((net_masked>>24&255)) $((net_masked>>16&255)) $((net_masked>>8&255)) $((net_masked&255))))" + + if (( ip_masked == net_masked )); then + debug_log "ip_in_cidr: manual check PASSED - IP is in CIDR" + return 0 + else + debug_log "ip_in_cidr: manual check FAILED - IP is NOT in CIDR" + return 1 + fi +} + +# Format IP address according to the configuration +format_ip_tag() { + local ip="$1" + [[ -z "$ip" ]] && return + local format="${TAG_FORMAT:-$DEFAULT_TAG_FORMAT}" + case "$format" in + "last_octet") echo "${ip##*.}" ;; + "last_two_octets") echo "${ip#*.*.}" ;; + *) echo "$ip" ;; + esac +} + +# Check if IP is in any CIDRs +ip_in_cidrs() { + local ip="$1" cidrs="$2" + [[ -z "$cidrs" ]] && return 1 + local IFS=' ' + debug_log "Checking IP '$ip' against CIDRs: '$cidrs'" + for cidr in $cidrs; do + debug_log "Testing IP '$ip' against CIDR '$cidr'" + if ip_in_cidr "$ip" "$cidr"; then + debug_log "IP '$ip' matches CIDR '$cidr' - PASSED" + return 0 + else + debug_log "IP '$ip' does not match CIDR '$cidr'" + fi + done + debug_log "IP '$ip' failed all CIDR checks" + return 1 +} + +# Check if IP is valid +is_valid_ipv4() { + local ip="$1" + [[ "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] || return 1 + + local IFS='.' parts + read -ra parts <<< "$ip" + for part in "${parts[@]}"; do + (( part >= 0 && part <= 255 )) || return 1 + done + return 0 +} + +# Get VM IPs using multiple methods with performance optimizations +get_vm_ips() { + local vmid=$1 ips="" + local vm_config="/etc/pve/qemu-server/${vmid}.conf" + [[ ! -f "$vm_config" ]] && return + + debug_log "vm $vmid: starting optimized IP detection" + + # Check if VM is running first (avoid expensive operations for stopped VMs) + local vm_status="" + if command -v qm >/dev/null 2>&1; then + vm_status=$(qm status "$vmid" 2>/dev/null | awk '{print $2}') + fi + + if [[ "$vm_status" != "running" ]]; then + debug_log "vm $vmid: not running (status: $vm_status), skipping expensive detection" + return + fi + + # Cache for this execution + local cache_file="/tmp/iptag_vm_${vmid}_cache" + local cache_ttl=60 # 60 seconds cache + + # Check cache first + if [[ -f "$cache_file" ]] && [[ $(($(date +%s) - $(stat -c %Y "$cache_file" 2>/dev/null || echo 0))) -lt $cache_ttl ]]; then + local cached_ips=$(cat "$cache_file" 2>/dev/null) + if [[ -n "$cached_ips" ]]; then + debug_log "vm $vmid: using cached IPs: $cached_ips" + echo "$cached_ips" + return + fi + fi + + # Method 1: Quick ARP table lookup (fastest) + local mac_addresses=$(grep -E "^net[0-9]+:" "$vm_config" | grep -oE "([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}" | head -3) + debug_log "vm $vmid: found MACs: $mac_addresses" + + # Quick ARP check without forced refresh (most common case) + for mac in $mac_addresses; do + local mac_lower=$(echo "$mac" | tr '[:upper:]' '[:lower:]') + local ip=$(ip neighbor show | grep "$mac_lower" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1) + if [[ -n "$ip" && "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then + debug_log "vm $vmid: found IP $ip via quick ARP for MAC $mac_lower" + ips+="$ip " + fi + done + + # Early exit if we found IPs via ARP + if [[ -n "$ips" ]]; then + local unique_ips=$(echo "$ips" | tr ' ' '\n' | sort -u | tr '\n' ' ') + unique_ips="${unique_ips% }" + debug_log "vm $vmid: early exit with IPs: '$unique_ips'" + echo "$unique_ips" > "$cache_file" + echo "$unique_ips" + return + fi + + # Method 2: QM guest agent (fast if available) + if command -v qm >/dev/null 2>&1; then + local qm_ips=$(timeout 3 qm guest cmd "$vmid" network-get-interfaces 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | grep -v "127.0.0.1" | head -2) + for qm_ip in $qm_ips; do + if [[ "$qm_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then + debug_log "vm $vmid: found IP $qm_ip via qm guest cmd" + ips+="$qm_ip " + fi + done + fi + + # Early exit if we found IPs via QM + if [[ -n "$ips" ]]; then + local unique_ips=$(echo "$ips" | tr ' ' '\n' | sort -u | tr '\n' ' ') + unique_ips="${unique_ips% }" + debug_log "vm $vmid: early exit with QM IPs: '$unique_ips'" + echo "$unique_ips" > "$cache_file" + echo "$unique_ips" + return + fi + + # Method 3: DHCP leases check (medium cost) + for mac in $mac_addresses; do + local mac_lower=$(echo "$mac" | tr '[:upper:]' '[:lower:]') + + for dhcp_file in "/var/lib/dhcp/dhcpd.leases" "/var/lib/dhcpcd5/dhcpcd.leases" "/tmp/dhcp.leases"; do + if [[ -f "$dhcp_file" ]]; then + local dhcp_ip=$(timeout 2 grep -A 10 "ethernet $mac_lower" "$dhcp_file" 2>/dev/null | grep "binding state active" -A 5 | grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}" | head -1) + if [[ -n "$dhcp_ip" && "$dhcp_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then + debug_log "vm $vmid: found IP $dhcp_ip via DHCP leases for MAC $mac_lower" + ips+="$dhcp_ip " + break 2 + fi + fi + done + done + + # Early exit if we found IPs via DHCP + if [[ -n "$ips" ]]; then + local unique_ips=$(echo "$ips" | tr ' ' '\n' | sort -u | tr '\n' ' ') + unique_ips="${unique_ips% }" + debug_log "vm $vmid: early exit with DHCP IPs: '$unique_ips'" + echo "$unique_ips" > "$cache_file" + echo "$unique_ips" + return + fi + + # Method 4: Limited network discovery (expensive - only if really needed) + debug_log "vm $vmid: falling back to limited network discovery" + + for mac in $mac_addresses; do + local mac_lower=$(echo "$mac" | tr '[:upper:]' '[:lower:]') + + # Get bridge interfaces + local bridges=$(grep -E "^net[0-9]+:" "$vm_config" | grep -oE "bridge=\w+" | cut -d= -f2 | head -1) + for bridge in $bridges; do + if [[ -n "$bridge" && -d "/sys/class/net/$bridge" ]]; then + # Get bridge IP range + local bridge_ip=$(ip addr show "$bridge" 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]+' | head -1) + if [[ -n "$bridge_ip" ]]; then + local network=$(echo "$bridge_ip" | cut -d'/' -f1) + debug_log "vm $vmid: limited scan on bridge $bridge network $bridge_ip" + + # Force ARP refresh with broadcast ping (limited) + IFS='.' read -r a b c d <<< "$network" + local broadcast="$a.$b.$c.255" + timeout 1 ping -c 1 -b "$broadcast" >/dev/null 2>&1 || true + + # Check ARP again after refresh + sleep 0.5 + local ip=$(ip neighbor show | grep "$mac_lower" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1) + if [[ -n "$ip" && "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then + debug_log "vm $vmid: found IP $ip via ARP after broadcast for MAC $mac_lower" + ips+="$ip " + break 2 + fi + + # Only do very limited ping scan (reduced range) + IFS='.' read -r a b c d <<< "$network" + local base_net="$a.$b.$c" + + # Try only most common ranges (much smaller than before) + for last_octet in {100..105} {200..205}; do + local test_ip="$base_net.$last_octet" + + # Very quick ping test (reduced timeout) + if timeout 0.2 ping -c 1 -W 1 "$test_ip" >/dev/null 2>&1; then + # Check if this IP corresponds to our MAC + sleep 0.1 + local found_mac=$(ip neighbor show "$test_ip" 2>/dev/null | grep -oE "([0-9a-f]{2}:){5}[0-9a-f]{2}") + if [[ "$found_mac" == "$mac_lower" ]]; then + debug_log "vm $vmid: found IP $test_ip via limited ping scan for MAC $mac_lower" + ips+="$test_ip " + break 2 + fi + fi + done + + # Skip extended scanning entirely (too expensive) + debug_log "vm $vmid: skipping extended scan to preserve CPU" + fi + fi + done + done + + # Method 5: Static configuration check (fast) + if [[ -z "$ips" ]]; then + debug_log "vm $vmid: checking for static IP configuration" + + # Check cloud-init configuration if exists + local cloudinit_file="/var/lib/vz/snippets/${vmid}-cloud-init.yml" + if [[ -f "$cloudinit_file" ]]; then + local static_ip=$(grep -E "addresses?:" "$cloudinit_file" 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1) + if [[ -n "$static_ip" && "$static_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then + debug_log "vm $vmid: found static IP $static_ip in cloud-init config" + ips+="$static_ip " + fi + fi + + # Check VM config for any IP hints + local config_ip=$(grep -E "(ip=|gw=)" "$vm_config" 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1) + if [[ -n "$config_ip" && "$config_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then + debug_log "vm $vmid: found IP hint $config_ip in VM config" + ips+="$config_ip " + fi + fi + + # Remove duplicates and cache result + local unique_ips=$(echo "$ips" | tr ' ' '\n' | sort -u | tr '\n' ' ') + unique_ips="${unique_ips% }" + + # Cache the result (even if empty) + echo "$unique_ips" > "$cache_file" + + debug_log "vm $vmid: final optimized IPs: '$unique_ips'" + echo "$unique_ips" +} + +# Update tags for container or VM +update_tags() { + local type="$1" vmid="$2" + local current_ips_full + + if [[ "$type" == "lxc" ]]; then + current_ips_full=$(get_lxc_ips "${vmid}") + while IFS= read -r line; do + [[ "$line" == tags:* ]] && current_tags_raw="${line#tags: }" && break + done < <(pct config "$vmid" 2>/dev/null) + else + current_ips_full=$(get_vm_ips "${vmid}") + local vm_config="/etc/pve/qemu-server/${vmid}.conf" + if [[ -f "$vm_config" ]]; then + local current_tags_raw=$(grep "^tags:" "$vm_config" 2>/dev/null | cut -d: -f2 | sed 's/^[[:space:]]*//') + fi + fi + + local current_tags=() next_tags=() current_ip_tags=() + if [[ -n "$current_tags_raw" ]]; then + mapfile -t current_tags < <(echo "$current_tags_raw" | sed 's/;/\n/g') + fi + + # Separate IP/numeric and user tags + for tag in "${current_tags[@]}"; do + if is_valid_ipv4 "${tag}" || [[ "$tag" =~ ^[0-9]+(\.[0-9]+)*$ ]]; then + current_ip_tags+=("${tag}") + else + next_tags+=("${tag}") + fi + done + + # Generate new IP tags from current IPs + local formatted_ips=() + debug_log "$type $vmid current_ips_full: '$current_ips_full'" + debug_log "$type $vmid CIDR_LIST: ${CIDR_LIST[*]}" + for ip in $current_ips_full; do + [[ -z "$ip" ]] && continue + debug_log "$type $vmid processing IP: '$ip'" + if is_valid_ipv4 "$ip"; then + debug_log "$type $vmid IP '$ip' is valid" + if ip_in_cidrs "$ip" "${CIDR_LIST[*]}"; then + debug_log "$type $vmid IP '$ip' passed CIDR check" + local formatted_ip=$(format_ip_tag "$ip") + debug_log "$type $vmid formatted '$ip' -> '$formatted_ip'" + [[ -n "$formatted_ip" ]] && formatted_ips+=("$formatted_ip") + else + debug_log "$type $vmid IP '$ip' failed CIDR check" + fi + else + debug_log "$type $vmid IP '$ip' is invalid" + fi + done + debug_log "$type $vmid final formatted_ips: ${formatted_ips[*]}" + + # If LXC and no IPs detected, do not touch tags at all + if [[ "$type" == "lxc" && ${#formatted_ips[@]} -eq 0 ]]; then + log_unchanged "LXC ${GRAY}${vmid}${NC}: No IP detected, tags unchanged" + return + fi + + # Add new IP tags + for new_ip in "${formatted_ips[@]}"; do + next_tags+=("$new_ip") + done + + # Update tags if there are changes + local old_tags_str=$(IFS=';'; echo "${current_tags[*]}") + local new_tags_str=$(IFS=';'; echo "${next_tags[*]}") + + debug_log "$type $vmid old_tags: '$old_tags_str'" + debug_log "$type $vmid new_tags: '$new_tags_str'" + debug_log "$type $vmid tags_equal: $([[ "$old_tags_str" == "$new_tags_str" ]] && echo true || echo false)" + + if [[ "$old_tags_str" != "$new_tags_str" ]]; then + # Determine what changed + local old_ip_tags_count=${#current_ip_tags[@]} + local new_ip_tags_count=${#formatted_ips[@]} + + # Build detailed change message + local change_details="" + + if [[ $old_ip_tags_count -eq 0 ]]; then + change_details="added ${new_ip_tags_count} IP tag(s): [${GREEN}${formatted_ips[*]}${NC}]" + else + # Compare old and new IP tags + local added_tags=() removed_tags=() common_tags=() + + # Find removed tags + for old_tag in "${current_ip_tags[@]}"; do + local found=false + for new_tag in "${formatted_ips[@]}"; do + if [[ "$old_tag" == "$new_tag" ]]; then + found=true + break + fi + done + if [[ "$found" == false ]]; then + removed_tags+=("$old_tag") + else + common_tags+=("$old_tag") + fi + done + + # Find added tags + for new_tag in "${formatted_ips[@]}"; do + local found=false + for old_tag in "${current_ip_tags[@]}"; do + if [[ "$new_tag" == "$old_tag" ]]; then + found=true + break + fi + done + if [[ "$found" == false ]]; then + added_tags+=("$new_tag") + fi + done + + # Build change message + local change_parts=() + if [[ ${#added_tags[@]} -gt 0 ]]; then + change_parts+=("added [${GREEN}${added_tags[*]}${NC}]") + fi + if [[ ${#removed_tags[@]} -gt 0 ]]; then + change_parts+=("removed [${YELLOW}${removed_tags[*]}${NC}]") + fi + if [[ ${#common_tags[@]} -gt 0 ]]; then + change_parts+=("kept [${GRAY}${common_tags[*]}${NC}]") + fi + + change_details=$(IFS=', '; echo "${change_parts[*]}") + fi + + log_change "${type^^} ${CYAN}${vmid}${NC}: ${change_details}" + + if [[ "$type" == "lxc" ]]; then + pct set "${vmid}" -tags "$(IFS=';'; echo "${next_tags[*]}")" &>/dev/null + else + local vm_config="/etc/pve/qemu-server/${vmid}.conf" + if [[ -f "$vm_config" ]]; then + sed -i '/^tags:/d' "$vm_config" + if [[ ${#next_tags[@]} -gt 0 ]]; then + echo "tags: $(IFS=';'; echo "${next_tags[*]}")" >> "$vm_config" + fi + fi + fi + else + # Tags unchanged + local ip_count=${#formatted_ips[@]} + local status_msg="" + + if [[ $ip_count -eq 0 ]]; then + status_msg="No IPs detected" + elif [[ $ip_count -eq 1 ]]; then + status_msg="IP tag [${GRAY}${formatted_ips[0]}${NC}] unchanged" + else + status_msg="${ip_count} IP tags [${GRAY}${formatted_ips[*]}${NC}] unchanged" + fi + + log_unchanged "${type^^} ${GRAY}${vmid}${NC}: ${status_msg}" + fi +} + +# Update all instances of specified type +update_all_tags() { + local type="$1" vmids count=0 + + if [[ "$type" == "lxc" ]]; then + vmids=($(pct list 2>/dev/null | grep -v VMID | awk '{print $1}')) + else + local all_vm_configs=($(ls /etc/pve/qemu-server/*.conf 2>/dev/null | sed 's/.*\/\([0-9]*\)\.conf/\1/' | sort -n)) + vmids=("${all_vm_configs[@]}") + fi + + count=${#vmids[@]} + [[ $count -eq 0 ]] && return + + # Display processing header with color + if [[ "$type" == "lxc" ]]; then + log_info "Processing ${WHITE}${count}${NC} LXC container(s) in parallel" + + # Clean up old cache files before processing LXC + cleanup_vm_cache + + # Process LXC containers in parallel for better performance + process_lxc_parallel "${vmids[@]}" + else + log_info "Processing ${WHITE}${count}${NC} virtual machine(s) in parallel" + + # Clean up old cache files before processing VMs + cleanup_vm_cache + + # Process VMs in parallel for better performance + process_vms_parallel "${vmids[@]}" + fi + + # Add completion message + if [[ "$type" == "lxc" ]]; then + log_success "Completed processing LXC containers" + else + log_success "Completed processing virtual machines" + fi +} + +# Check if status changed +check_status_changed() { + local type="$1" current + case "$type" in + "lxc") current=$(pct list 2>/dev/null | grep -v VMID) ;; + "vm") current=$(ls -la /etc/pve/qemu-server/*.conf 2>/dev/null) ;; + "fw") current=$(ip link show type bridge 2>/dev/null) ;; + esac + local last_var="last_${type}_status" + [[ "${!last_var}" == "$current" ]] && return 1 + eval "$last_var='$current'" + return 0 +} + +# Main check function +check() { + local current_time changes_detected=false + current_time=$(date +%s) + + local update_lxc=false + local update_vm=false + + # Periodic cache cleanup (every 10 minutes) + local time_since_last_cleanup=$((current_time - ${last_cleanup_time:-0})) + if [[ $time_since_last_cleanup -ge 600 ]]; then + cleanup_vm_cache + last_cleanup_time=$current_time + debug_log "Performed periodic cache cleanup" + fi + + # Check LXC status + local time_since_last_lxc_check=$((current_time - last_lxc_status_check_time)) + if [[ "${LXC_STATUS_CHECK_INTERVAL:-60}" -gt 0 ]] && \ + [[ "$time_since_last_lxc_check" -ge "${LXC_STATUS_CHECK_INTERVAL:-60}" ]]; then + last_lxc_status_check_time=$current_time + if check_status_changed "lxc"; then + update_lxc=true + log_warning "LXC status changes detected" + fi + fi + + # Check VM status + local time_since_last_vm_check=$((current_time - last_vm_status_check_time)) + if [[ "${VM_STATUS_CHECK_INTERVAL:-60}" -gt 0 ]] && \ + [[ "$time_since_last_vm_check" -ge "${VM_STATUS_CHECK_INTERVAL:-60}" ]]; then + last_vm_status_check_time=$current_time + if check_status_changed "vm"; then + update_vm=true + log_warning "VM status changes detected" + fi + fi + + # Check network interface changes + local time_since_last_fw_check=$((current_time - last_fw_net_interface_check_time)) + if [[ "${FW_NET_INTERFACE_CHECK_INTERVAL:-60}" -gt 0 ]] && \ + [[ "$time_since_last_fw_check" -ge "${FW_NET_INTERFACE_CHECK_INTERVAL:-60}" ]]; then + last_fw_net_interface_check_time=$current_time + if check_status_changed "fw"; then + update_lxc=true + update_vm=true + log_warning "Network interface changes detected" + fi + fi + + # Force update if interval exceeded + for type in "lxc" "vm"; do + local last_update_var="last_update_${type}_time" + local time_since_last_update=$((current_time - ${!last_update_var})) + if [[ $time_since_last_update -ge ${FORCE_UPDATE_INTERVAL:-1800} ]]; then + if [[ "$type" == "lxc" ]]; then + update_lxc=true + log_info "Scheduled LXC update (every $((FORCE_UPDATE_INTERVAL / 60)) minutes)" + else + update_vm=true + log_info "Scheduled VM update (every $((FORCE_UPDATE_INTERVAL / 60)) minutes)" + fi + eval "${last_update_var}=${current_time}" + fi + done + + # Final execution + $update_lxc && update_all_tags "lxc" + $update_vm && update_all_tags "vm" +} + +# Initialize time variables +declare -g last_lxc_status="" last_vm_status="" last_fw_status="" +declare -g last_lxc_status_check_time=0 last_vm_status_check_time=0 last_fw_net_interface_check_time=0 +declare -g last_update_lxc_time=0 last_update_vm_time=0 last_cleanup_time=0 + +# Main loop +main() { + # Display startup message + echo -e "\n${PURPLE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + log_success "IP-Tag service started successfully" + echo -e "${BLUE}ℹ${NC} Loop interval: ${WHITE}${LOOP_INTERVAL:-$DEFAULT_CHECK_INTERVAL}${NC} seconds" + echo -e "${BLUE}ℹ${NC} Debug mode: ${WHITE}${DEBUG:-false}${NC}" + echo -e "${BLUE}ℹ${NC} Tag format: ${WHITE}${TAG_FORMAT:-$DEFAULT_TAG_FORMAT}${NC}" + echo -e "${BLUE}ℹ${NC} Allowed CIDRs: ${WHITE}${CIDR_LIST[*]}${NC}" + echo -e "${PURPLE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}\n" + + if [[ "$FORCE_SINGLE_RUN" == "true" ]]; then + check + exit 0 + fi + + while true; do + check + sleep "${LOOP_INTERVAL:-300}" + done +} + + +# Cache cleanup function +cleanup_vm_cache() { + local cache_dir="/tmp" + local vm_cache_ttl=${VM_IP_CACHE_TTL:-120} + local lxc_cache_ttl=${LXC_IP_CACHE_TTL:-120} + local status_cache_ttl=${LXC_STATUS_CACHE_TTL:-30} + local current_time=$(date +%s) + + debug_log "Starting extreme cache cleanup" + + # Clean VM cache files + for cache_file in "$cache_dir"/iptag_vm_*_cache; do + if [[ -f "$cache_file" ]]; then + local file_time=$(stat -c %Y "$cache_file" 2>/dev/null || echo 0) + if [[ $((current_time - file_time)) -gt $vm_cache_ttl ]]; then + rm -f "$cache_file" 2>/dev/null + debug_log "Cleaned up expired VM cache file: $cache_file" + fi + fi + done + + # Clean LXC IP cache files + for cache_file in "$cache_dir"/iptag_lxc_*_cache; do + if [[ -f "$cache_file" ]]; then + local file_time=$(stat -c %Y "$cache_file" 2>/dev/null || echo 0) + if [[ $((current_time - file_time)) -gt $lxc_cache_ttl ]]; then + rm -f "$cache_file" 2>/dev/null + # Also clean meta files + rm -f "${cache_file}.meta" 2>/dev/null + debug_log "Cleaned up expired LXC cache file: $cache_file" + fi + fi + done + + # Clean LXC status cache files (shorter TTL) + for cache_file in "$cache_dir"/iptag_lxc_status_*_cache; do + if [[ -f "$cache_file" ]]; then + local file_time=$(stat -c %Y "$cache_file" 2>/dev/null || echo 0) + if [[ $((current_time - file_time)) -gt $status_cache_ttl ]]; then + rm -f "$cache_file" 2>/dev/null + debug_log "Cleaned up expired LXC status cache: $cache_file" + fi + fi + done + + # Clean LXC PID cache files (60 second TTL) + for cache_file in "$cache_dir"/iptag_lxc_pid_*_cache; do + if [[ -f "$cache_file" ]]; then + local file_time=$(stat -c %Y "$cache_file" 2>/dev/null || echo 0) + if [[ $((current_time - file_time)) -gt 60 ]]; then + rm -f "$cache_file" 2>/dev/null + debug_log "Cleaned up expired LXC PID cache: $cache_file" + fi + fi + done + + # Clean any orphaned meta files + for meta_file in "$cache_dir"/iptag_*.meta; do + if [[ -f "$meta_file" ]]; then + local base_file="${meta_file%.meta}" + if [[ ! -f "$base_file" ]]; then + rm -f "$meta_file" 2>/dev/null + debug_log "Cleaned up orphaned meta file: $meta_file" + fi + fi + done + + debug_log "Completed extreme cache cleanup" +} + +# Parallel VM processing function +process_vms_parallel() { + local vm_list=("$@") + local max_parallel=${MAX_PARALLEL_VM_CHECKS:-5} + local job_count=0 + local pids=() + local pid_start_times=() + + for vmid in "${vm_list[@]}"; do + if [[ $job_count -ge $max_parallel ]]; then + local pid_to_wait="${pids[0]}" + local start_time="${pid_start_times[0]}" + local waited=0 + while kill -0 "$pid_to_wait" 2>/dev/null && [[ $waited -lt 10 ]]; do + sleep 1 + ((waited++)) + done + if kill -0 "$pid_to_wait" 2>/dev/null; then + kill -9 "$pid_to_wait" 2>/dev/null + log_warning "VM parallel: killed stuck process $pid_to_wait after 10s timeout" + else + wait "$pid_to_wait" + fi + pids=("${pids[@]:1}") + pid_start_times=("${pid_start_times[@]:1}") + ((job_count--)) + fi + # Start background job + (update_tags "vm" "$vmid") & + pids+=($!) + pid_start_times+=("$(date +%s)") + ((job_count++)) + done + for i in "${!pids[@]}"; do + local pid="${pids[$i]}" + local waited=0 + while kill -0 "$pid" 2>/dev/null && [[ $waited -lt 10 ]]; do + sleep 1 + ((waited++)) + done + if kill -0 "$pid" 2>/dev/null; then + kill -9 "$pid" 2>/dev/null + log_warning "VM parallel: killed stuck process $pid after 10s timeout" + else + wait "$pid" + fi + done +} + +# Parallel LXC processing function +process_lxc_parallel() { + local lxc_list=("$@") + local max_parallel=${MAX_PARALLEL_LXC_CHECKS:-2} + local batch_size=${LXC_BATCH_SIZE:-20} + local job_count=0 + local pids=() + local pid_start_times=() + + debug_log "Starting parallel LXC processing: ${#lxc_list[@]} containers, max_parallel=$max_parallel" + + if [[ ${#lxc_list[@]} -gt 5 ]]; then + debug_log "Pre-loading LXC statuses for ${#lxc_list[@]} containers" + local all_statuses=$(pct list 2>/dev/null) + for vmid in "${lxc_list[@]}"; do + local status=$(echo "$all_statuses" | grep "^$vmid" | awk '{print $2}') + if [[ -n "$status" ]]; then + local status_cache_file="/tmp/iptag_lxc_status_${vmid}_cache" + echo "$status" > "$status_cache_file" 2>/dev/null & + fi + done + wait + debug_log "Completed batch status pre-loading" + fi + for vmid in "${lxc_list[@]}"; do + if [[ $job_count -ge $max_parallel ]]; then + local pid_to_wait="${pids[0]}" + local start_time="${pid_start_times[0]}" + local waited=0 + while kill -0 "$pid_to_wait" 2>/dev/null && [[ $waited -lt 10 ]]; do + sleep 1 + ((waited++)) + done + if kill -0 "$pid_to_wait" 2>/dev/null; then + kill -9 "$pid_to_wait" 2>/dev/null + log_warning "LXC parallel: killed stuck process $pid_to_wait after 10s timeout" + else + wait "$pid_to_wait" + fi + pids=("${pids[@]:1}") + pid_start_times=("${pid_start_times[@]:1}") + ((job_count--)) + fi + # Start background job with higher priority + (update_tags "lxc" "$vmid") & + pids+=($!) + pid_start_times+=("$(date +%s)") + ((job_count++)) + done + for i in "${!pids[@]}"; do + local pid="${pids[$i]}" + local waited=0 + while kill -0 "$pid" 2>/dev/null && [[ $waited -lt 10 ]]; do + sleep 1 + ((waited++)) + done + if kill -0 "$pid" 2>/dev/null; then + kill -9 "$pid" 2>/dev/null + log_warning "LXC parallel: killed stuck process $pid after 10s timeout" + else + wait "$pid" + fi + done + debug_log "Completed parallel LXC processing" +} + +# Optimized LXC IP detection with caching and alternative methods +get_lxc_ips() { + local vmid=$1 + local status_cache_file="/tmp/iptag_lxc_status_${vmid}_cache" + local status_cache_ttl=${LXC_STATUS_CACHE_TTL:-30} + + debug_log "lxc $vmid: starting extreme optimized IP detection" + + # Check status cache first (avoid expensive pct status calls) + local lxc_status="" + if [[ -f "$status_cache_file" ]] && [[ $(($(date +%s) - $(stat -c %Y "$status_cache_file" 2>/dev/null || echo 0))) -lt $status_cache_ttl ]]; then + lxc_status=$(cat "$status_cache_file" 2>/dev/null) + debug_log "lxc $vmid: using cached status: $lxc_status" + else + lxc_status=$(pct status "${vmid}" 2>/dev/null | awk '{print $2}') + echo "$lxc_status" > "$status_cache_file" 2>/dev/null + debug_log "lxc $vmid: fetched fresh status: $lxc_status" + fi + + if [[ "$lxc_status" != "running" ]]; then + debug_log "lxc $vmid: not running (status: $lxc_status)" + return + fi + + local ips="" + local method_used="" + + # EXTREME Method 1: Direct Proxmox config inspection (super fast) + debug_log "lxc $vmid: trying direct Proxmox config inspection" + local pve_lxc_config="/etc/pve/lxc/${vmid}.conf" + if [[ -f "$pve_lxc_config" ]]; then + local static_ip=$(grep -E "^net[0-9]+:" "$pve_lxc_config" 2>/dev/null | grep -oE 'ip=([0-9]{1,3}\.){3}[0-9]{1,3}' | cut -d'=' -f2 | head -1) + debug_log "lxc $vmid: [CONFIG] static_ip='$static_ip' (from $pve_lxc_config)" + if [[ -n "$static_ip" && "$static_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then + debug_log "lxc $vmid: found static IP $static_ip in Proxmox config" + ips="$static_ip" + method_used="proxmox_config" + fi + else + debug_log "lxc $vmid: [CONFIG] config file not found: $pve_lxc_config" + fi + + # EXTREME Method 2: Direct network namespace inspection (fastest dynamic) + if [[ -z "$ips" ]]; then + debug_log "lxc $vmid: trying optimized namespace inspection" + local ns_file="/var/lib/lxc/${vmid}/rootfs/proc/net/fib_trie" + if [[ -f "$ns_file" ]]; then + local ns_ip=$(timeout 1 grep -m1 -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' "$ns_file" 2>/dev/null | grep -v '127.0.0.1' | head -1) + debug_log "lxc $vmid: [NAMESPACE] ns_ip='$ns_ip'" + if [[ -n "$ns_ip" ]] && is_valid_ipv4 "$ns_ip"; then + debug_log "lxc $vmid: found IP $ns_ip via namespace inspection" + ips="$ns_ip" + method_used="namespace" + fi + else + debug_log "lxc $vmid: [NAMESPACE] ns_file not found: $ns_file" + fi + fi + + # EXTREME Method 3: Batch ARP table lookup (if namespace failed) + if [[ -z "$ips" ]]; then + debug_log "lxc $vmid: trying batch ARP lookup" + local bridge_name=""; local mac_addr="" + if [[ -f "$pve_lxc_config" ]]; then + bridge_name=$(grep -Eo 'bridge=[^,]+' "$pve_lxc_config" | head -1 | cut -d'=' -f2) + mac_addr=$(grep -Eo 'hwaddr=([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}' "$pve_lxc_config" | head -1 | cut -d'=' -f2) + debug_log "lxc $vmid: [ARP] bridge_name='$bridge_name' mac_addr='$mac_addr' (from $pve_lxc_config)" + fi + if [[ -z "$bridge_name" || -z "$mac_addr" ]]; then + local lxc_config="/var/lib/lxc/${vmid}/config" + if [[ -f "$lxc_config" ]]; then + [[ -z "$bridge_name" ]] && bridge_name=$(grep "lxc.net.0.link" "$lxc_config" 2>/dev/null | cut -d'=' -f2 | tr -d ' ') + [[ -z "$mac_addr" ]] && mac_addr=$(grep "lxc.net.0.hwaddr" "$lxc_config" 2>/dev/null | cut -d'=' -f2 | tr -d ' ') + debug_log "lxc $vmid: [ARP] bridge_name='$bridge_name' mac_addr='$mac_addr' (from $lxc_config)" + else + debug_log "lxc $vmid: [ARP] lxc config not found: $lxc_config" + fi + fi + if [[ -n "$bridge_name" && -n "$mac_addr" ]]; then + local bridge_ip=$(ip neighbor show dev "$bridge_name" 2>/dev/null | grep "$mac_addr" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1) + debug_log "lxc $vmid: [ARP] bridge_ip='$bridge_ip'" + if [[ -n "$bridge_ip" && "$bridge_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then + debug_log "lxc $vmid: found IP $bridge_ip via ARP table" + ips="$bridge_ip" + method_used="arp_table" + fi + fi + fi + + # EXTREME Method 4: Fast process namespace (if ARP failed) + if [[ -z "$ips" ]] && [[ "${LXC_SKIP_SLOW_METHODS:-true}" != "true" ]]; then + debug_log "lxc $vmid: trying fast process namespace" + local pid_cache_file="/tmp/iptag_lxc_pid_${vmid}_cache" + local container_pid="" + if [[ -f "$pid_cache_file" ]] && [[ $(($(date +%s) - $(stat -c %Y "$pid_cache_file" 2>/dev/null || echo 0))) -lt 60 ]]; then + container_pid=$(cat "$pid_cache_file" 2>/dev/null) + else + container_pid=$(pct list 2>/dev/null | grep "^$vmid" | awk '{print $3}') + [[ -n "$container_pid" && "$container_pid" != "-" ]] && echo "$container_pid" > "$pid_cache_file" + fi + debug_log "lxc $vmid: [PROCESS_NS] container_pid='$container_pid'" + if [[ -n "$container_pid" && "$container_pid" != "-" ]]; then + local ns_ip=$(timeout 1 nsenter -t "$container_pid" -n ip -4 addr show 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | grep -v '127.0.0.1' | head -1) + debug_log "lxc $vmid: [PROCESS_NS] ns_ip='$ns_ip'" + if [[ -n "$ns_ip" ]] && is_valid_ipv4 "$ns_ip"; then + debug_log "lxc $vmid: found IP $ns_ip via process namespace" + ips="$ns_ip" + method_used="process_ns" + fi + fi + fi + + # Fallback: always do lxc-attach/pct exec with timeout if nothing found + if [[ -z "$ips" && "${LXC_ALLOW_FORCED_COMMANDS:-true}" == "true" ]]; then + debug_log "lxc $vmid: trying fallback lxc-attach (forced)" + local attach_ip="" + attach_ip=$(timeout 7s lxc-attach -n "$vmid" -- ip -4 addr show 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | grep -v '127.0.0.1' | head -1) + local attach_status=$? + debug_log "lxc $vmid: [LXC_ATTACH] attach_ip='$attach_ip' status=$attach_status" + if [[ $attach_status -eq 124 ]]; then + debug_log "lxc $vmid: lxc-attach timed out after 7s" + fi + if [[ -n "$attach_ip" ]] && is_valid_ipv4 "$attach_ip"; then + debug_log "lxc $vmid: found IP $attach_ip via lxc-attach (forced)" + ips="$attach_ip" + method_used="lxc_attach_forced" + fi + fi + if [[ -z "$ips" && "${LXC_ALLOW_FORCED_COMMANDS:-true}" == "true" ]]; then + debug_log "lxc $vmid: trying fallback pct exec (forced)" + local pct_ip="" + pct_ip=$(timeout 7s pct exec "$vmid" -- ip -4 addr show 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | grep -v '127.0.0.1' | head -1) + local pct_status=$? + debug_log "lxc $vmid: [PCT_EXEC] pct_ip='$pct_ip' status=$pct_status" + if [[ $pct_status -eq 124 ]]; then + debug_log "lxc $vmid: pct exec timed out after 7s" + fi + if [[ -n "$pct_ip" ]] && is_valid_ipv4 "$pct_ip"; then + debug_log "lxc $vmid: found IP $pct_ip via pct exec (forced)" + ips="$pct_ip" + method_used="pct_exec_forced" + fi + fi + + debug_log "lxc $vmid: [RESULT] ips='$ips' method='$method_used'" + echo "$ips" +} + +main +EOF +} + +# Main installation process +if check_service_exists; then + while true; do + read -p "IP-Tag service is already installed. Do you want to update it? (y/n): " yn + case $yn in + [Yy]*) + update_installation + exit 0 + ;; + [Nn]*) + msg_error "Installation cancelled." + exit 0 + ;; + *) + msg_error "Please answer yes or no." + ;; + esac + done +fi + +while true; do + read -p "This will install ${APP} on ${hostname}. Proceed? (y/n): " yn + case $yn in + [Yy]*) + break + ;; + [Nn]*) + msg_error "Installation cancelled." + exit + ;; + *) + msg_error "Please answer yes or no." + ;; + esac +done + +if ! pveversion | grep -Eq "pve-manager/8\.[0-4](\.[0-9]+)*"; then + msg_error "This version of Proxmox Virtual Environment is not supported" + msg_error "⚠️ Requires Proxmox Virtual Environment Version 8.0 or later." + msg_error "Exiting..." + sleep 2 + exit +fi + +FILE_PATH="/usr/local/bin/iptag" +if [[ -f "$FILE_PATH" ]]; then + msg_info "The file already exists: '$FILE_PATH'. Skipping installation." + exit 0 +fi + +msg_info "Installing Dependencies" +apt-get update &>/dev/null +apt-get install -y ipcalc net-tools &>/dev/null +msg_ok "Installed Dependencies" + +msg_info "Setting up IP-Tag Scripts" +mkdir -p /opt/iptag +msg_ok "Setup IP-Tag Scripts" + +# Migrate config if needed +migrate_config + +msg_info "Setup Default Config" +if [[ ! -f /opt/iptag/iptag.conf ]]; then + generate_config >/opt/iptag/iptag.conf + msg_ok "Setup default config" +else + msg_ok "Default config already exists" +fi + +msg_info "Setup Main Function" +if [[ ! -f /opt/iptag/iptag ]]; then + generate_main_script >/opt/iptag/iptag + chmod +x /opt/iptag/iptag + msg_ok "Setup Main Function" +else + msg_ok "Main Function already exists" +fi + +msg_info "Creating Service" +if [[ ! -f /lib/systemd/system/iptag.service ]]; then + generate_service >/lib/systemd/system/iptag.service + msg_ok "Created Service" +else + msg_ok "Service already exists." +fi + +msg_ok "Setup IP-Tag Scripts" + +msg_info "Starting Service" +systemctl daemon-reload &>/dev/null +systemctl enable -q --now iptag.service &>/dev/null +msg_ok "Started Service" + +msg_info "Restarting Service with optimizations" +systemctl restart iptag.service &>/dev/null +msg_ok "Service restarted with CPU optimizations" + +msg_info "Creating manual run command" +cat <<'EOF' >/usr/local/bin/iptag-run +#!/usr/bin/env bash +CONFIG_FILE="/opt/iptag/iptag.conf" +SCRIPT_FILE="/opt/iptag/iptag" +if [[ ! -f "$SCRIPT_FILE" ]]; then + echo "❌ Main script not found: $SCRIPT_FILE" + exit 1 +fi +export FORCE_SINGLE_RUN=true +exec "$SCRIPT_FILE" +EOF +chmod +x /usr/local/bin/iptag-run +msg_ok "Created iptag-run executable - You can execute this manually by entering “iptag-run” in the Proxmox host, so the script is executed by hand." + +SPINNER_PID="" +echo -e "\n${APP} installation completed successfully! ${CL}\n" + +# Proper script termination +exit 0 From 1f21b4dc8613b362938f2100915f52f2a0753e55 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 12:12:23 +0200 Subject: [PATCH 0056/1733] Update add-iptag.sh --- tools/pve/add-iptag.sh | 124 +++++++++++++++++------------------------ 1 file changed, 51 insertions(+), 73 deletions(-) diff --git a/tools/pve/add-iptag.sh b/tools/pve/add-iptag.sh index 49bf1a900..6dbda4df4 100644 --- a/tools/pve/add-iptag.sh +++ b/tools/pve/add-iptag.sh @@ -194,12 +194,14 @@ CIDR_LIST=( 10.0.0.0/8 100.64.0.0/10 ) +# Enable or Disable IPv6 tagging +ENABLE_IPV6_TAGS=false # Tag format options: # - "full": full IP address (e.g., 192.168.0.100) # - "last_octet": only the last octet (e.g., 100) # - "last_two_octets": last two octets (e.g., 0.100) -TAG_FORMAT="last_two_octets" +TAG_FORMAT="full" # Interval settings (in seconds) - optimized for lower CPU usage LOOP_INTERVAL=300 @@ -1088,14 +1090,23 @@ process_lxc_parallel() { } # Optimized LXC IP detection with caching and alternative methods +# ------------------------------------------- +# Combined optimized LXC IP detection +# Keeps advanced debug logs & methods +# Adds IPv6 detection controlled by ENABLE_IPV6_TAGS +# ------------------------------------------- get_lxc_ips() { local vmid=$1 + local ips="" + local method_used="" + + # status cache for container state local status_cache_file="/tmp/iptag_lxc_status_${vmid}_cache" local status_cache_ttl=${LXC_STATUS_CACHE_TTL:-30} - debug_log "lxc $vmid: starting extreme optimized IP detection" + debug_log "lxc $vmid: starting combined IP detection" - # Check status cache first (avoid expensive pct status calls) + # ----- STATUS CHECK ----- local lxc_status="" if [[ -f "$status_cache_file" ]] && [[ $(($(date +%s) - $(stat -c %Y "$status_cache_file" 2>/dev/null || echo 0))) -lt $status_cache_ttl ]]; then lxc_status=$(cat "$status_cache_file" 2>/dev/null) @@ -1111,74 +1122,48 @@ get_lxc_ips() { return fi - local ips="" - local method_used="" - - # EXTREME Method 1: Direct Proxmox config inspection (super fast) - debug_log "lxc $vmid: trying direct Proxmox config inspection" + # ----- TRY CONFIG FOR STATIC IP ----- local pve_lxc_config="/etc/pve/lxc/${vmid}.conf" if [[ -f "$pve_lxc_config" ]]; then local static_ip=$(grep -E "^net[0-9]+:" "$pve_lxc_config" 2>/dev/null | grep -oE 'ip=([0-9]{1,3}\.){3}[0-9]{1,3}' | cut -d'=' -f2 | head -1) - debug_log "lxc $vmid: [CONFIG] static_ip='$static_ip' (from $pve_lxc_config)" + debug_log "lxc $vmid: [CONFIG] static_ip='$static_ip'" if [[ -n "$static_ip" && "$static_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then - debug_log "lxc $vmid: found static IP $static_ip in Proxmox config" ips="$static_ip" method_used="proxmox_config" fi - else - debug_log "lxc $vmid: [CONFIG] config file not found: $pve_lxc_config" fi - # EXTREME Method 2: Direct network namespace inspection (fastest dynamic) + # ----- NAMESPACE FAST PARSE ----- if [[ -z "$ips" ]]; then - debug_log "lxc $vmid: trying optimized namespace inspection" local ns_file="/var/lib/lxc/${vmid}/rootfs/proc/net/fib_trie" + debug_log "lxc $vmid: trying namespace fib_trie" if [[ -f "$ns_file" ]]; then local ns_ip=$(timeout 1 grep -m1 -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' "$ns_file" 2>/dev/null | grep -v '127.0.0.1' | head -1) - debug_log "lxc $vmid: [NAMESPACE] ns_ip='$ns_ip'" if [[ -n "$ns_ip" ]] && is_valid_ipv4 "$ns_ip"; then - debug_log "lxc $vmid: found IP $ns_ip via namespace inspection" ips="$ns_ip" - method_used="namespace" + method_used="namespace_fib" + debug_log "lxc $vmid: found IP via namespace: $ips" fi - else - debug_log "lxc $vmid: [NAMESPACE] ns_file not found: $ns_file" fi fi - # EXTREME Method 3: Batch ARP table lookup (if namespace failed) + # ----- ARP TABLE ----- if [[ -z "$ips" ]]; then - debug_log "lxc $vmid: trying batch ARP lookup" - local bridge_name=""; local mac_addr="" - if [[ -f "$pve_lxc_config" ]]; then - bridge_name=$(grep -Eo 'bridge=[^,]+' "$pve_lxc_config" | head -1 | cut -d'=' -f2) - mac_addr=$(grep -Eo 'hwaddr=([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}' "$pve_lxc_config" | head -1 | cut -d'=' -f2) - debug_log "lxc $vmid: [ARP] bridge_name='$bridge_name' mac_addr='$mac_addr' (from $pve_lxc_config)" - fi - if [[ -z "$bridge_name" || -z "$mac_addr" ]]; then - local lxc_config="/var/lib/lxc/${vmid}/config" - if [[ -f "$lxc_config" ]]; then - [[ -z "$bridge_name" ]] && bridge_name=$(grep "lxc.net.0.link" "$lxc_config" 2>/dev/null | cut -d'=' -f2 | tr -d ' ') - [[ -z "$mac_addr" ]] && mac_addr=$(grep "lxc.net.0.hwaddr" "$lxc_config" 2>/dev/null | cut -d'=' -f2 | tr -d ' ') - debug_log "lxc $vmid: [ARP] bridge_name='$bridge_name' mac_addr='$mac_addr' (from $lxc_config)" - else - debug_log "lxc $vmid: [ARP] lxc config not found: $lxc_config" - fi - fi - if [[ -n "$bridge_name" && -n "$mac_addr" ]]; then - local bridge_ip=$(ip neighbor show dev "$bridge_name" 2>/dev/null | grep "$mac_addr" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1) - debug_log "lxc $vmid: [ARP] bridge_ip='$bridge_ip'" + debug_log "lxc $vmid: trying ARP lookup" + local mac_addr=$(grep -Eo 'hwaddr=([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}' "$pve_lxc_config" | head -1 | cut -d'=' -f2 | tr 'A-F' 'a-f') + if [[ -n "$mac_addr" ]]; then + local bridge_ip=$(ip neighbor show | grep "$mac_addr" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1) if [[ -n "$bridge_ip" && "$bridge_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then - debug_log "lxc $vmid: found IP $bridge_ip via ARP table" ips="$bridge_ip" method_used="arp_table" + debug_log "lxc $vmid: found IP via ARP: $ips" fi fi fi - # EXTREME Method 4: Fast process namespace (if ARP failed) - if [[ -z "$ips" ]] && [[ "${LXC_SKIP_SLOW_METHODS:-true}" != "true" ]]; then - debug_log "lxc $vmid: trying fast process namespace" + # ----- PROCESS NAMESPACE (fast) ----- + if [[ -z "$ips" && "${LXC_SKIP_SLOW_METHODS:-true}" != "true" ]]; then + debug_log "lxc $vmid: trying process namespace" local pid_cache_file="/tmp/iptag_lxc_pid_${vmid}_cache" local container_pid="" if [[ -f "$pid_cache_file" ]] && [[ $(($(date +%s) - $(stat -c %Y "$pid_cache_file" 2>/dev/null || echo 0))) -lt 60 ]]; then @@ -1187,54 +1172,47 @@ get_lxc_ips() { container_pid=$(pct list 2>/dev/null | grep "^$vmid" | awk '{print $3}') [[ -n "$container_pid" && "$container_pid" != "-" ]] && echo "$container_pid" > "$pid_cache_file" fi - debug_log "lxc $vmid: [PROCESS_NS] container_pid='$container_pid'" if [[ -n "$container_pid" && "$container_pid" != "-" ]]; then local ns_ip=$(timeout 1 nsenter -t "$container_pid" -n ip -4 addr show 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | grep -v '127.0.0.1' | head -1) - debug_log "lxc $vmid: [PROCESS_NS] ns_ip='$ns_ip'" if [[ -n "$ns_ip" ]] && is_valid_ipv4 "$ns_ip"; then - debug_log "lxc $vmid: found IP $ns_ip via process namespace" ips="$ns_ip" method_used="process_ns" + debug_log "lxc $vmid: found IP via process namespace: $ips" fi fi fi - # Fallback: always do lxc-attach/pct exec with timeout if nothing found - if [[ -z "$ips" && "${LXC_ALLOW_FORCED_COMMANDS:-true}" == "true" ]]; then - debug_log "lxc $vmid: trying fallback lxc-attach (forced)" - local attach_ip="" - attach_ip=$(timeout 7s lxc-attach -n "$vmid" -- ip -4 addr show 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | grep -v '127.0.0.1' | head -1) - local attach_status=$? - debug_log "lxc $vmid: [LXC_ATTACH] attach_ip='$attach_ip' status=$attach_status" - if [[ $attach_status -eq 124 ]]; then - debug_log "lxc $vmid: lxc-attach timed out after 7s" - fi - if [[ -n "$attach_ip" ]] && is_valid_ipv4 "$attach_ip"; then - debug_log "lxc $vmid: found IP $attach_ip via lxc-attach (forced)" - ips="$attach_ip" - method_used="lxc_attach_forced" - fi - fi - if [[ -z "$ips" && "${LXC_ALLOW_FORCED_COMMANDS:-true}" == "true" ]]; then - debug_log "lxc $vmid: trying fallback pct exec (forced)" - local pct_ip="" - pct_ip=$(timeout 7s pct exec "$vmid" -- ip -4 addr show 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | grep -v '127.0.0.1' | head -1) - local pct_status=$? - debug_log "lxc $vmid: [PCT_EXEC] pct_ip='$pct_ip' status=$pct_status" - if [[ $pct_status -eq 124 ]]; then - debug_log "lxc $vmid: pct exec timed out after 7s" - fi + # ----- FORCED METHODS (attach/exec) ----- + if [[ -z "$ips" && "${LXC_ALLOW_FORCED_COMMANDS:-false}" == "true" ]]; then + debug_log "lxc $vmid: trying forced pct exec" + local pct_ip=$(timeout 7s pct exec "$vmid" -- ip -4 addr show 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | grep -v '127.0.0.1' | head -1) if [[ -n "$pct_ip" ]] && is_valid_ipv4 "$pct_ip"; then - debug_log "lxc $vmid: found IP $pct_ip via pct exec (forced)" ips="$pct_ip" method_used="pct_exec_forced" + debug_log "lxc $vmid: found IP via pct exec: $ips" fi fi + # ----- OPTIONAL IPv6 detection ----- + if [[ -z "$ips" && "${ENABLE_IPV6_TAGS,,}" == "true" ]]; then + debug_log "lxc $vmid: trying IPv6 neighbor lookup" + local mac_addr=$(grep -Eo 'hwaddr=([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}' "$pve_lxc_config" | head -1 | cut -d'=' -f2 | tr 'A-F' 'a-f') + if [[ -n "$mac_addr" ]]; then + local ipv6=$(ip -6 neighbor show | grep -i "$mac_addr" | grep -oE '([0-9a-fA-F:]+:+)+' | head -1) + if [[ -n "$ipv6" ]]; then + ips="$ipv6" + method_used="ipv6_neighbor" + debug_log "lxc $vmid: found IPv6: $ips" + fi + fi + fi + + # ----- FINAL RESULT ----- debug_log "lxc $vmid: [RESULT] ips='$ips' method='$method_used'" echo "$ips" } + main EOF } From 0aaf8cfd1c5656509ca6dddfe87d2e2db014556a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 12:52:50 +0200 Subject: [PATCH 0057/1733] Update add-iptag.sh --- tools/pve/add-iptag.sh | 66 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 59 insertions(+), 7 deletions(-) diff --git a/tools/pve/add-iptag.sh b/tools/pve/add-iptag.sh index 6dbda4df4..50dac8b9a 100644 --- a/tools/pve/add-iptag.sh +++ b/tools/pve/add-iptag.sh @@ -203,6 +203,12 @@ ENABLE_IPV6_TAGS=false # - "last_two_octets": last two octets (e.g., 0.100) TAG_FORMAT="full" +# IPv6 Tag format options: +# - "full": full IPv6 address +# - "short": compressed form (default) +# - "last_block": only last block +IPV6_TAG_FORMAT="short" + # Interval settings (in seconds) - optimized for lower CPU usage LOOP_INTERVAL=300 VM_STATUS_CHECK_INTERVAL=600 @@ -360,6 +366,33 @@ format_ip_tag() { esac } +format_ipv6_tag() { + local ip="$1" + [[ -z "$ip" ]] && return + local format="${IPV6_TAG_FORMAT:-short}" + + case "$format" in + "last_block") + # take last hex block + echo "${ip##*:}" + ;; + "full") + # return full as-is + echo "$ip" + ;; + "short"|"compressed") + # compress repeated zeros (::) automatically + # Linux ip command already returns compressed by default + echo "$ip" + ;; + *) + # fallback + echo "$ip" + ;; + esac +} + + # Check if IP is in any CIDRs ip_in_cidrs() { local ip="$1" cidrs="$2" @@ -626,22 +659,41 @@ update_tags() { for ip in $current_ips_full; do [[ -z "$ip" ]] && continue debug_log "$type $vmid processing IP: '$ip'" + if is_valid_ipv4 "$ip"; then - debug_log "$type $vmid IP '$ip' is valid" + debug_log "$type $vmid IP '$ip' is valid IPv4" + # Only check IPv4 against CIDR list if ip_in_cidrs "$ip" "${CIDR_LIST[*]}"; then - debug_log "$type $vmid IP '$ip' passed CIDR check" - local formatted_ip=$(format_ip_tag "$ip") - debug_log "$type $vmid formatted '$ip' -> '$formatted_ip'" - [[ -n "$formatted_ip" ]] && formatted_ips+=("$formatted_ip") + debug_log "$type $vmid IPv4 '$ip' passed CIDR check" + local formatted_ip4 + formatted_ip4=$(format_ip_tag "$ip") + debug_log "$type $vmid formatted IPv4 '$ip' -> '$formatted_ip4'" + [[ -n "$formatted_ip4" ]] && formatted_ips+=("$formatted_ip4") else - debug_log "$type $vmid IP '$ip' failed CIDR check" + debug_log "$type $vmid IPv4 '$ip' failed CIDR check, skipping" fi + + elif [[ "${ENABLE_IPV6_TAGS,,}" == "true" ]]; then + # IPv6 handling only if enabled + debug_log "$type $vmid IP '$ip' not IPv4, treating as IPv6" + # basic IPv6 validation + if [[ "$ip" =~ ^[0-9a-fA-F:]+$ ]]; then + debug_log "$type $vmid IPv6 '$ip' accepted" + local formatted_ip6 + formatted_ip6=$(format_ipv6_tag "$ip") + debug_log "$type $vmid formatted IPv6 '$ip' -> '$formatted_ip6'" + [[ -n "$formatted_ip6" ]] && formatted_ips+=("$formatted_ip6") + else + debug_log "$type $vmid value '$ip' not recognized as valid IPv6, skipping" + fi + else - debug_log "$type $vmid IP '$ip' is invalid" + debug_log "$type $vmid IP '$ip' is invalid or IPv6 not enabled" fi done debug_log "$type $vmid final formatted_ips: ${formatted_ips[*]}" + # If LXC and no IPs detected, do not touch tags at all if [[ "$type" == "lxc" && ${#formatted_ips[@]} -eq 0 ]]; then log_unchanged "LXC ${GRAY}${vmid}${NC}: No IP detected, tags unchanged" From ad62a8ad0def341cc8931c5ec68aad53d81a0e9d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 12:57:12 +0200 Subject: [PATCH 0058/1733] Update add-iptag.sh --- tools/pve/add-iptag.sh | 43 ++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/tools/pve/add-iptag.sh b/tools/pve/add-iptag.sh index 50dac8b9a..742a07a02 100644 --- a/tools/pve/add-iptag.sh +++ b/tools/pve/add-iptag.sh @@ -195,7 +195,7 @@ CIDR_LIST=( 100.64.0.0/10 ) # Enable or Disable IPv6 tagging -ENABLE_IPV6_TAGS=false +ENABLE_IPV6_TAGS=true # Tag format options: # - "full": full IP address (e.g., 192.168.0.100) @@ -228,8 +228,8 @@ MAX_PARALLEL_LXC_CHECKS=2 LXC_BATCH_SIZE=3 LXC_STATUS_CACHE_TTL=300 LXC_AGGRESSIVE_CACHING=true -LXC_SKIP_SLOW_METHODS=true -LXC_ALLOW_FORCED_COMMANDS=false +LXC_SKIP_SLOW_METHODS=false +LXC_ALLOW_FORCED_COMMANDS=true # Debug settings (set to true to enable debugging) DEBUG=false @@ -1246,15 +1246,34 @@ get_lxc_ips() { fi # ----- OPTIONAL IPv6 detection ----- - if [[ -z "$ips" && "${ENABLE_IPV6_TAGS,,}" == "true" ]]; then - debug_log "lxc $vmid: trying IPv6 neighbor lookup" - local mac_addr=$(grep -Eo 'hwaddr=([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}' "$pve_lxc_config" | head -1 | cut -d'=' -f2 | tr 'A-F' 'a-f') - if [[ -n "$mac_addr" ]]; then - local ipv6=$(ip -6 neighbor show | grep -i "$mac_addr" | grep -oE '([0-9a-fA-F:]+:+)+' | head -1) - if [[ -n "$ipv6" ]]; then - ips="$ipv6" - method_used="ipv6_neighbor" - debug_log "lxc $vmid: found IPv6: $ips" + if [[ "${ENABLE_IPV6_TAGS,,}" == "true" ]]; then + debug_log "lxc $vmid: IPv6 detection enabled" + + # 1. Try to get IPv6 from inside the container + local pct_ipv6=$(timeout 3 pct exec "$vmid" -- ip -6 addr show scope global 2>/dev/null \ + | grep -oE '([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}' \ + | grep -v '^fe80' | head -1) + if [[ -n "$pct_ipv6" ]]; then + debug_log "lxc $vmid: found IPv6 via pct exec: $pct_ipv6" + [[ -n "$ips" ]] && ips="$ips $pct_ipv6" || ips="$pct_ipv6" + method_used="${method_used:+$method_used,}ipv6_exec" + else + debug_log "lxc $vmid: no IPv6 from pct exec, fallback to neighbor table" + + # 2. Fallback: neighbor table + local mac_addr=$(grep -Eo 'hwaddr=([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}' "$pve_lxc_config" \ + | head -1 | cut -d'=' -f2 | tr 'A-F' 'a-f') + if [[ -n "$mac_addr" ]]; then + local ipv6_nb=$(ip -6 neighbor show | grep -i "$mac_addr" \ + | grep -oE '([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}' \ + | grep -v '^fe80' | head -1) + if [[ -n "$ipv6_nb" ]]; then + debug_log "lxc $vmid: found IPv6 via neighbor: $ipv6_nb" + [[ -n "$ips" ]] && ips="$ips $ipv6_nb" || ips="$ipv6_nb" + method_used="${method_used:+$method_used,}ipv6_neighbor" + else + debug_log "lxc $vmid: no IPv6 found in neighbor table" + fi fi fi fi From b5759dda481111b24d2e4b4576c7e41388af2112 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 13:14:10 +0200 Subject: [PATCH 0059/1733] rybbit --- install/docspell-install.sh | 2 +- install/rybbit-install.sh | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/install/docspell-install.sh b/install/docspell-install.sh index d76983d2b..8b963d18c 100644 --- a/install/docspell-install.sh +++ b/install/docspell-install.sh @@ -59,7 +59,7 @@ msg_ok "Set up PostgreSQL Database" fetch_and_deploy_gh_release "docspell-joex" "eikek/docspell" "binary" "latest" "/opt/docspell-joex" "docspell-joex_*all.deb" fetch_and_deploy_gh_release "docspell-restserver" "eikek/docspell" "binary" "latest" "/opt/docspell-restserver" "docspell-restserver_*all.deb" fetch_and_deploy_gh_release "docspell-dsc" "docspell/dsc" "singlefile" "latest" "/usr/bin" "dsc" - +fetch_and_deploy_gh_release "apache-solr" "apache/solr" "tarball" "latest" "/opt/docspell" msg_info "Setup Docspell" diff --git a/install/rybbit-install.sh b/install/rybbit-install.sh index 56f82bcd8..c7d913d98 100644 --- a/install/rybbit-install.sh +++ b/install/rybbit-install.sh @@ -48,6 +48,32 @@ msg_ok "Set up PostgreSQL Database" fetch_and_deploy_gh_release "rybbit" "rybbit-io/rybbit" "tarball" "latest" "/opt/rybbit" +cd /opt/rybbit/shared +npm install +npm run build + +cd /opt/rybbit/server +RUN npm ci +npm run build + +cd /opt/rybbit/client +npm ci --legacy-peer-deps +npm run build + +mv /opt/rybbit/.env.example /opt/rybbit/.env +sed -i "s|^POSTGRES_DB=.*|POSTGRES_DB=$DB_NAME|g" /opt/rybbit/.env +sed -i "s|^POSTGRES_USER=.*|POSTGRES_USER=$DB_USER|g" /opt/rybbit/.env +sed -i "s|^POSTGRES_PASSWORD=.*|POSTGRES_PASSWORD=$DB_PASS|g" /opt/rybbit/.env +sed -i "s|^DOMAIN_NAME=.*|DOMAIN_NAME=localhost|g" /opt/rybbit/.env +sed -i "s|^BASE_URL=.*|BASE_URL=\"http://localhost\"|g" /opt/rybbit/.env +msg_ok "Rybbit Installed" + +msg_info "Setting up Caddy" +mkdir -p /etc/caddy +cp /opt/rybbit/Caddyfile /etc/caddy/Caddyfile +systemctl enable -q --now caddy +msg_ok "Caddy Setup" + motd_ssh customize From 62e2b07dabf8ca362351b0ebaf44931e289486c3 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 13:18:43 +0200 Subject: [PATCH 0060/1733] Update viseron-install.sh --- install/viseron-install.sh | 41 +++++++++++++------------------------- 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/install/viseron-install.sh b/install/viseron-install.sh index 3f04fdf6a..cc30bcd9b 100644 --- a/install/viseron-install.sh +++ b/install/viseron-install.sh @@ -13,42 +13,29 @@ setting_up_container network_check update_os +PYTHON_VERSION="3.12" setup_uv + + msg_info "Installing Dependencies" $STD apt-get install -y \ -python3 python3-pip python3-venv \ -git curl wget \ -libgl1-mesa-glx libglib2.0-0 \ -libsm6 libxext6 libxrender-dev \ -libgstreamer1.0-0 libgstreamer-plugins-base1.0-0 \ -libgstreamer-plugins-bad1.0-0 gstreamer1.0-plugins-base \ -gstreamer1.0-plugins-good gstreamer1.0-plugins-bad \ -gstreamer1.0-plugins-ugly gstreamer1.0-libav \ -gstreamer1.0-tools gstreamer1.0-x gstreamer1.0-alsa \ -gstreamer1.0-gl gstreamer1.0-gtk3 gstreamer1.0-qt5 \ -gstreamer1.0-pulseaudio \ -libavcodec-dev libavformat-dev libswscale-dev \ -libv4l-dev libxvidcore-dev libx264-dev \ -libjpeg-dev libpng-dev libtiff-dev \ -libatlas-base-dev gfortran \ -libhdf5-dev libhdf5-serial-dev \ -libhdf5-103 libqtgui4 libqtwebkit4 libqt4-test python3-pyqt5 \ -libgtk-3-dev libcanberra-gtk3-module \ -libgirepository1.0-dev libcairo2-dev pkg-config \ -libcblas-dev libopenblas-dev liblapack-dev \ -libsm6 libxext6 libxrender-dev libxss1 \ -libgconf-2-4 libasound2 + python3 python3-pip python3-venv \ + python3-opencv \ + libgl1-mesa-glx libglib2.0-0 \ + libgstreamer1.0-0 libgstreamer-plugins-base1.0-0 \ + gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-libav msg_ok "Installed Dependencies" -msg_info "Setting up Python Environment" +msg_info "Setting up Python Environment with uv" cd /opt -python3 -m venv viseron +uv venv viseron source viseron/bin/activate -pip install --upgrade pip setuptools wheel -msg_ok "Python Environment Setup" +uv pip install --upgrade pip setuptools wheel +msg_ok "Python Environment Setup (uv)" msg_info "Installing Viseron" RELEASE=$(curl -s https://api.github.com/repos/roflcoopter/viseron/releases/latest | jq -r '.tag_name') -pip install viseron==${RELEASE#v} +uv pip install viseron==${RELEASE#v} +ln -s /opt/viseron/bin/viseron /usr/local/bin/viseron msg_ok "Installed Viseron $RELEASE" msg_info "Creating Configuration Directory" From ef67f58e2e925659438291ed0f24f208cbde396b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 13:19:03 +0200 Subject: [PATCH 0061/1733] Update rybbit-install.sh --- install/rybbit-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/rybbit-install.sh b/install/rybbit-install.sh index c7d913d98..4865a0d8e 100644 --- a/install/rybbit-install.sh +++ b/install/rybbit-install.sh @@ -53,7 +53,7 @@ npm install npm run build cd /opt/rybbit/server -RUN npm ci +npm ci npm run build cd /opt/rybbit/client From ddf9148dc0164b5333a2eb61f6d64d4399ae7e95 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 13:27:47 +0200 Subject: [PATCH 0062/1733] fixes --- install/viseron-install.sh | 1 - tools/pve/post-pve-install.sh | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/install/viseron-install.sh b/install/viseron-install.sh index cc30bcd9b..6964180d4 100644 --- a/install/viseron-install.sh +++ b/install/viseron-install.sh @@ -15,7 +15,6 @@ update_os PYTHON_VERSION="3.12" setup_uv - msg_info "Installing Dependencies" $STD apt-get install -y \ python3 python3-pip python3-venv \ diff --git a/tools/pve/post-pve-install.sh b/tools/pve/post-pve-install.sh index 5afc3bd02..79a5f8092 100644 --- a/tools/pve/post-pve-install.sh +++ b/tools/pve/post-pve-install.sh @@ -141,7 +141,7 @@ EOF yes) whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox --title "Support Subscriptions" "Supporting the software's development team is essential. Check their official website's Support Subscriptions for pricing. Without their dedicated work, we wouldn't have this exceptional software." 10 58 msg_info "Disabling subscription nag" - echo "DPkg::Post-Invoke { \"dpkg -V proxmox-widget-toolkit | grep -q '/proxmoxlib\.js$'; if [ \$? -eq 1 ]; then { echo 'Removing subscription nag from UI...'; sed -i '/.*data\.status.*{/{s/\!//;s/active/NoMoreNagging/}' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js; }; fi\"; };" >/etc/apt/apt.conf.d/no-nag-script + echo "DPkg::Post-Invoke { \"dpkg -V proxmox-widget-toolkit 2>/dev/null && [ -f /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js ] && echo 'Removing subscription nag from UI...' && sed -i '/data\.status/{s/\!/=/;s/active/NoMoreNagging/}' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js\"; };" >/etc/apt/apt.conf.d/no-nag-script apt --reinstall install proxmox-widget-toolkit &>/dev/null msg_ok "Disabled subscription nag (Delete browser cache)" ;; From f2c5bf774cb8448b2b38c2e39c53a1ad82b8e626 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 13:28:55 +0200 Subject: [PATCH 0063/1733] Update post-pve-install.sh --- tools/pve/post-pve-install.sh | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/tools/pve/post-pve-install.sh b/tools/pve/post-pve-install.sh index 79a5f8092..f241aca12 100644 --- a/tools/pve/post-pve-install.sh +++ b/tools/pve/post-pve-install.sh @@ -246,12 +246,38 @@ while true; do esac done -if ! pveversion | grep -Eq "pve-manager/8\.[0-4](\.[0-9]+)*"; then +# This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported. +pve_check() { + local PVE_VER + PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" + + # 8 Version Check + if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then + local MINOR="${BASH_REMATCH[1]}" + if (( MINOR < 1 || MINOR > 4 )); then + msg_error "This version of Proxmox Virtual Environment is not supported" + echo -e "Requires Proxmox Virtual Environment Version 8.1 – 8.4" + echo -e "Exiting..." + sleep 2 + exit 1 + fi + return 0 + fi + + # 9 Beta Version Check + if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then + msg_custom "Detected Proxmox Virtual Environment $PVE_VER – Beta state, use with caution!" + return 0 + fi + + # All others (unsupported versions) msg_error "This version of Proxmox Virtual Environment is not supported" - echo -e "Requires Proxmox Virtual Environment Version 8.0 or later." + echo -e "Requires Proxmox Virtual Environment Version 8.1 – 8.4 or 9.x (Beta)" echo -e "Exiting..." sleep 2 - exit -fi + exit 1 +} + +pve_check start_routines From 2a3b9454e7feb48642f6ce26e0867a334fdb4c74 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 13:30:23 +0200 Subject: [PATCH 0064/1733] Update viseron-install.sh --- install/viseron-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/viseron-install.sh b/install/viseron-install.sh index 6964180d4..080da234f 100644 --- a/install/viseron-install.sh +++ b/install/viseron-install.sh @@ -18,7 +18,7 @@ PYTHON_VERSION="3.12" setup_uv msg_info "Installing Dependencies" $STD apt-get install -y \ python3 python3-pip python3-venv \ - python3-opencv \ + python3-opencv \ jq \ libgl1-mesa-glx libglib2.0-0 \ libgstreamer1.0-0 libgstreamer-plugins-base1.0-0 \ gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-libav From 48395202ccf0465b4ea124201ae4c75755e3fa3d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 13:31:06 +0200 Subject: [PATCH 0065/1733] Update post-pve-install.sh --- tools/pve/post-pve-install.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/pve/post-pve-install.sh b/tools/pve/post-pve-install.sh index f241aca12..b6c704f0e 100644 --- a/tools/pve/post-pve-install.sh +++ b/tools/pve/post-pve-install.sh @@ -44,6 +44,12 @@ msg_error() { echo -e "${BFR} ${CROSS} ${RD}${msg}${CL}" } +msg_custom() { + local msg="$1" + echo -e "${BFR} ${YW}⚠ ${msg}${CL}" +} + + start_routines() { header_info From aebdae5dee9c01099a338d4dd580aa5b81e3936b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 13:39:49 +0200 Subject: [PATCH 0066/1733] Update viseron-install.sh --- install/viseron-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/viseron-install.sh b/install/viseron-install.sh index 080da234f..c96fab404 100644 --- a/install/viseron-install.sh +++ b/install/viseron-install.sh @@ -18,7 +18,7 @@ PYTHON_VERSION="3.12" setup_uv msg_info "Installing Dependencies" $STD apt-get install -y \ python3 python3-pip python3-venv \ - python3-opencv \ jq \ + python3-opencv jq \ libgl1-mesa-glx libglib2.0-0 \ libgstreamer1.0-0 libgstreamer-plugins-base1.0-0 \ gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-libav From 977e5a05eb94d8af70aeeda46c290ca9ebeae7e7 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 13:48:08 +0200 Subject: [PATCH 0067/1733] Update viseron-install.sh --- install/viseron-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/viseron-install.sh b/install/viseron-install.sh index c96fab404..13aea1b3c 100644 --- a/install/viseron-install.sh +++ b/install/viseron-install.sh @@ -33,7 +33,7 @@ msg_ok "Python Environment Setup (uv)" msg_info "Installing Viseron" RELEASE=$(curl -s https://api.github.com/repos/roflcoopter/viseron/releases/latest | jq -r '.tag_name') -uv pip install viseron==${RELEASE#v} +uv pip install https://github.com/roflcoopter/viseron/archive/refs/tags/${RELEASE}.tar.gz ln -s /opt/viseron/bin/viseron /usr/local/bin/viseron msg_ok "Installed Viseron $RELEASE" From 23c6236b0248971ae9f862f1d9ef1fbf48c68d21 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 13:48:28 +0200 Subject: [PATCH 0068/1733] Update viseron.sh --- ct/viseron.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/viseron.sh b/ct/viseron.sh index 8c12645ad..32cb237e7 100644 --- a/ct/viseron.sh +++ b/ct/viseron.sh @@ -8,7 +8,7 @@ var_ram="${var_ram:-2048}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-0}" +var_unprivileged="${var_unprivileged:-1}" header_info "$APP" From a07064067bad1154c9a28db02c3934ce34d1c244 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 14:09:26 +0200 Subject: [PATCH 0069/1733] error handling --- misc/build.func | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/misc/build.func b/misc/build.func index 35c623edf..07d33f950 100644 --- a/misc/build.func +++ b/misc/build.func @@ -35,14 +35,18 @@ catch_errors() { # This function is called when an error occurs. It receives the exit code, line number, and command that caused the error, and displays an error message. error_handler() { - source /dev/stdin <<<$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) - printf "\e[?25h" + local exit_code="$?" local line_number="$1" local command="$2" - local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" + printf "\e[?25h" + + + source /dev/stdin <<<$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) + local error_message="[ERROR] in line $line_number: exit code $exit_code: while executing command $command" post_update_to_api "failed" "${command}" echo -e "\n$error_message\n" + exit "$exit_code" if [[ -n "$CT_ID" ]]; then read -p "Remove this Container? " prompt @@ -53,6 +57,7 @@ error_handler() { fi fi } + # Check if the shell is using bash shell_check() { if [[ "$(basename "$SHELL")" != "bash" ]]; then @@ -1228,10 +1233,8 @@ build_container() { $PW " bash -c "$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/create_lxc.sh)" - RET=$? - if [[ $RET -ne 0 ]]; then - msg_error "rny: in line $LINENO: exit code $RET: while executing create_lxc.sh" - exit $RET + if [ $? -ne 0 ]; then + exit 200 fi LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" @@ -1394,7 +1397,10 @@ EOF' fi msg_ok "Customized LXC Container" - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/"$var_install".sh)" $? + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/"$var_install".sh)" + if [ $? -ne 0 ]; then + exit 100 + fi } # This function sets the description of the container. From d1976b23d84eca4b92b8bbeb01f0e316d5a51178 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 14:40:59 +0200 Subject: [PATCH 0070/1733] Update salt-install.sh --- install/salt-install.sh | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/install/salt-install.sh b/install/salt-install.sh index 7c74b7c10..4fe29a824 100644 --- a/install/salt-install.sh +++ b/install/salt-install.sh @@ -17,25 +17,23 @@ msg_info "Installing Dependencies" $STD apt-get install -y jq msg_ok "Installed Dependencies" -fetch_and_deploy_gh_release "salt" "saltstack/salt" "binary" "latest" "/opt/salt" "salt-master*_amd64.deb" +msg_info "Setup Salt Repo" +mkdir -p /etc/apt/keyrings +curl -fsSL https://packages.broadcom.com/artifactory/api/security/keypair/SaltProjectKey/public -o /etc/apt/keyrings/salt-archive-keyring.pgp +curl -fsSL https://github.com/saltstack/salt-install-guide/releases/latest/download/salt.sources -o /etc/apt/sources.list.d/salt.sources +$STD apt-get update +msg_ok "Setup Salt Repo" -# msg_info "Setup Salt repo" -# mkdir -p /etc/apt/keyrings -# curl -fsSL https://packages.broadcom.com/artifactory/api/security/keypair/SaltProjectKey/public -o /etc/apt/keyrings/salt-archive-keyring.pgp -# curl -fsSL https://github.com/saltstack/salt-install-guide/releases/latest/download/salt.sources -o /etc/apt/sources.list.d/salt.sources -# $STD apt-get update -# msg_ok "Setup Salt repo" - -# msg_info "Installing Salt Master" -# RELEASE=$(curl -fsSL https://api.github.com/repos/saltstack/salt/releases/latest | jq -r .tag_name | sed 's/^v//') -# cat </etc/apt/preferences.d/salt-pin-1001 -# Package: salt-* -# Pin: version ${RELEASE} -# Pin-Priority: 1001 -# EOF -# $STD apt-get install -y salt-master -# echo "${RELEASE}" >/~.salt -# msg_ok "Installed Salt Master" +msg_info "Installing Salt" +RELEASE=$(curl -fsSL https://api.github.com/repos/saltstack/salt/releases/latest | jq -r .tag_name | sed 's/^v//') +cat </etc/apt/preferences.d/salt-pin-1001 +Package: salt-* +Pin: version ${RELEASE} +Pin-Priority: 1001 +EOF +$STD apt-get install -y salt-master +echo "${RELEASE}" >/~.salt +msg_ok "Installed Salt" motd_ssh customize From 5d3ad7a459767192ff399612c47b50ab325c73b2 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 21 Jul 2025 14:43:13 +0200 Subject: [PATCH 0071/1733] Add TeamSpeak Server script --- ct/teamspeak-server.sh | 0 install/teamspeak-server-install.sh | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 ct/teamspeak-server.sh create mode 100644 install/teamspeak-server-install.sh diff --git a/ct/teamspeak-server.sh b/ct/teamspeak-server.sh new file mode 100644 index 000000000..e69de29bb diff --git a/install/teamspeak-server-install.sh b/install/teamspeak-server-install.sh new file mode 100644 index 000000000..e69de29bb From ca03967f6af573a43917f3fc66172c076f22bfa1 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 14:53:07 +0200 Subject: [PATCH 0072/1733] Update salt.sh --- ct/salt.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/salt.sh b/ct/salt.sh index 5efed33c6..c8a6cec70 100644 --- a/ct/salt.sh +++ b/ct/salt.sh @@ -30,12 +30,12 @@ function update_script() { fi RELEASE=$(curl -fsSL https://api.github.com/repos/saltstack/salt/releases/latest | jq -r .tag_name | sed 's/^v//') - if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then + if [[ ! -f /~.salt ]] || [[ "${RELEASE}" != "$(cat /~.salt)" ]]; then msg_info "Updating $APP to ${RELEASE}" sed -i "s/^\(Pin: version \).*/\1${RELEASE}/" /etc/apt/preferences.d/salt-pin-1001 $STD apt-get update $STD apt-get upgrade -y - echo "${RELEASE}" >/opt/${APP}_version.txt + echo "${RELEASE}" >/~.salt msg_ok "Updated ${APP} to ${RELEASE}" else msg_ok "${APP} is already up to date (${RELEASE})" From b5be5439b470e3da16f22009df912ff1ffc48af9 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 14:56:36 +0200 Subject: [PATCH 0073/1733] Update salt.json --- frontend/public/json/salt.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/public/json/salt.json b/frontend/public/json/salt.json index 4c162abab..41750a2f8 100644 --- a/frontend/public/json/salt.json +++ b/frontend/public/json/salt.json @@ -2,7 +2,7 @@ "name": "Salt", "slug": "salt", "categories": [ - 1 + 19 ], "date_created": "2025-07-02", "type": "ct", @@ -13,7 +13,7 @@ "documentation": "https://docs.saltproject.io/salt/install-guide/en/latest/", "website": "https://saltproject.io/", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/saltmaster.svg", - "description": "Saltmaster is an open-source self-hosted application.", + "description": "SaltStack Salt is a software for automating the management and configuration of IT infrastructure and applications. It is an event-driven automation tool and framework used to deploy, configure, and manage complex IT systems. Its primary functions include configuration management, where it ensures consistent configurations and manages operating system deployment and software installation. It also automates and orchestrates routine IT processes and can create self-aware, self-healing systems.", "install_methods": [ { "type": "default", From c05054fc1f634b065e1bc48f29d7409eabb2b545 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 15:03:17 +0200 Subject: [PATCH 0074/1733] Update build.func --- misc/build.func | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/misc/build.func b/misc/build.func index 07d33f950..a1f68f1ad 100644 --- a/misc/build.func +++ b/misc/build.func @@ -19,10 +19,12 @@ source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxV if command -v curl >/dev/null 2>&1; then source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + source /dev/stdin <<<$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) load_functions #echo "(build.func) Loaded core.func via curl" elif command -v wget >/dev/null 2>&1; then source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + source /dev/stdin <<<$(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) load_functions #echo "(build.func) Loaded core.func via wget" fi @@ -30,21 +32,18 @@ fi # This function enables error handling in the script by setting options and defining a trap for the ERR signal. catch_errors() { set -Eeo pipefail + shopt -s errtrace trap 'error_handler $LINENO "$BASH_COMMAND"' ERR } # This function is called when an error occurs. It receives the exit code, line number, and command that caused the error, and displays an error message. error_handler() { - local exit_code="$?" local line_number="$1" local command="$2" printf "\e[?25h" - - - source /dev/stdin <<<$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) local error_message="[ERROR] in line $line_number: exit code $exit_code: while executing command $command" - post_update_to_api "failed" "${command}" + post_update_to_api "failed" "$command" echo -e "\n$error_message\n" exit "$exit_code" From 4c730e542d085e469f51b5cc848ec51b7b0d2814 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 15:04:38 +0200 Subject: [PATCH 0075/1733] Update build.func --- misc/build.func | 2 -- 1 file changed, 2 deletions(-) diff --git a/misc/build.func b/misc/build.func index a1f68f1ad..a1494f92c 100644 --- a/misc/build.func +++ b/misc/build.func @@ -19,12 +19,10 @@ source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxV if command -v curl >/dev/null 2>&1; then source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) - source /dev/stdin <<<$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) load_functions #echo "(build.func) Loaded core.func via curl" elif command -v wget >/dev/null 2>&1; then source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) - source /dev/stdin <<<$(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) load_functions #echo "(build.func) Loaded core.func via wget" fi From 219e800870616e323967e6bafb68c809ef8f3fa3 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 15:05:37 +0200 Subject: [PATCH 0076/1733] Update build.func --- misc/build.func | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index a1494f92c..234b1034f 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1,3 +1,4 @@ +#!/usr/bin/env bash # Copyright (c) 2021-2025 tteck # Author: tteck (tteckster) # Co-Author: MickLesk @@ -30,7 +31,9 @@ fi # This function enables error handling in the script by setting options and defining a trap for the ERR signal. catch_errors() { set -Eeo pipefail - shopt -s errtrace + if [ -n "$BASH_VERSION" ]; then + shopt -s errtrace + fi trap 'error_handler $LINENO "$BASH_COMMAND"' ERR } From 24b5340ed8fc7083048947920ff9c2aaac844477 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 15:07:53 +0200 Subject: [PATCH 0077/1733] Update build.func --- misc/build.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index 234b1034f..79e580ef3 100644 --- a/misc/build.func +++ b/misc/build.func @@ -31,7 +31,7 @@ fi # This function enables error handling in the script by setting options and defining a trap for the ERR signal. catch_errors() { set -Eeo pipefail - if [ -n "$BASH_VERSION" ]; then + if [ -n "$BASH_VERSION" ] && command -v shopt >/dev/null 2>&1; then shopt -s errtrace fi trap 'error_handler $LINENO "$BASH_COMMAND"' ERR From 8a676cb2a33ca170d8ad3b0f6edfa676489749be Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 15:10:23 +0200 Subject: [PATCH 0078/1733] Update build.func --- misc/build.func | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/misc/build.func b/misc/build.func index 79e580ef3..1e8cd020c 100644 --- a/misc/build.func +++ b/misc/build.func @@ -31,9 +31,9 @@ fi # This function enables error handling in the script by setting options and defining a trap for the ERR signal. catch_errors() { set -Eeo pipefail - if [ -n "$BASH_VERSION" ] && command -v shopt >/dev/null 2>&1; then - shopt -s errtrace - fi + # if [ -n "$BASH_VERSION" ] && command -v shopt >/dev/null 2>&1; then + # shopt -s errtrace + # fi trap 'error_handler $LINENO "$BASH_COMMAND"' ERR } From b4eba58f3b36900da2c0be95aa15bc8e1029d789 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 15:16:33 +0200 Subject: [PATCH 0079/1733] Update build.func --- misc/build.func | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/misc/build.func b/misc/build.func index 1e8cd020c..dcd5a73fd 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1397,9 +1397,8 @@ EOF' fi msg_ok "Customized LXC Container" - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/"$var_install".sh)" - if [ $? -ne 0 ]; then - exit 100 + if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/"$var_install".sh)"; then + return fi } From f6174a8960c86528a7b3bb88fce3fb51121b13e5 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 21 Jul 2025 15:18:00 +0200 Subject: [PATCH 0080/1733] Update Teamspeak --- ct/teamspeak-server.sh | 62 +++++++++++++++++++++++++++++ install/teamspeak-server-install.sh | 55 +++++++++++++++++++++++++ 2 files changed, 117 insertions(+) diff --git a/ct/teamspeak-server.sh b/ct/teamspeak-server.sh index e69de29bb..cb0cb4a39 100644 --- a/ct/teamspeak-server.sh +++ b/ct/teamspeak-server.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: tremor021 (Slaviša Arežina) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://teamspeak.com/en/ + +APP="Teamspeak Server" +var_tags="${var_tags:-voice;communication}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/teamspeak-server ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + RELEASE=$(curl -fsSL https://teamspeak.com/en/downloads/#server | grep -oP 'teamspeak3-server_linux_amd64-\K[0-9]+\.[0-9]+\.[0-9]+' | head -1) + if [[ "${RELEASE}" != "$(cat ~/.teamspeak-server 2>/dev/null)" ]] || [[ ! -f ~/.teamspeak-server ]]; then + msg_info "Stopping Service" + systemctl stop teamspeak-server + msg_ok "Stopped Service" + + msg_info "Updating ${APP}" + curl -fsSL "https://files.teamspeak-services.com/releases/server/${RELEASE}/teamspeak3-server_linux_amd64-${RELEASE}.tar.bz2" -o ts3server.tar.bz2 + tar -xf ./ts3server.tar.bz2 + cp -ru teamspeak3-server_linux_amd64/* /opt/teamspeak-server/ + msg_ok "Updated $APP" + + msg_info "Starting Service" + systemctl start teamspeak-server + msg_ok "Started Service" + + msg_ok "Updated Successfully" + else + msg_ok "Already up to date" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${ADVANCED}${BL}Create your initial user in${CL} ${BGN}/opt/${APP}${CL}${BL} in the LXC:${CL} ${RD}npm run user:create ${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3002${CL}" diff --git a/install/teamspeak-server-install.sh b/install/teamspeak-server-install.sh index e69de29bb..2d6c97e26 100644 --- a/install/teamspeak-server-install.sh +++ b/install/teamspeak-server-install.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: tremor021 (Slaviša Arežina) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://teamspeak.com/en/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +RELEASE=$(curl -fsSL https://teamspeak.com/en/downloads/#server | grep -oP 'teamspeak3-server_linux_amd64-\K[0-9]+\.[0-9]+\.[0-9]+' | head -1) + +msg_info "Setting up Teamspeak Server" +curl -fsSL "https://files.teamspeak-services.com/releases/server/${RELEASE}/teamspeak3-server_linux_amd64-${RELEASE}.tar.bz2" -o ts3server.tar.bz2 +tar -xf ./ts3server.tar.bz2 +mv teamspeak3-server_linux_amd64/ /opt/teamspeak-server/ +touch /opt/teamspeak-server/.ts3server_license_accepted +msg_ok "Setup Teamspeak Server" + +msg_info "Creating service" +cat </etc/systemd/system/teamspeak-server.service +[Unit] +Description=TeamSpeak3 Server +Wants=network-online.target +After=network.target + +[Service] +WorkingDirectory=/opt/teamspeak +User=root +Type=forking +ExecStart=/opt/teamspeak-server/ts3server_startscript.sh start +ExecStop=/opt/teamspeak-server/ts3server_startscript.sh stop +ExecReload=/opt/teamspeak-server/ts3server_startscript.sh restart +Restart=always +RestartSec=15 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now teamspeak-server +msg_ok "Created service" + +motd_ssh +customize + +msg_info "Cleaning up" +rm -f ~/ts3server.tar.bz* +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From f4f67d8ea8035025c908cdd6b53064a120c25bcf Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 21 Jul 2025 15:23:56 +0200 Subject: [PATCH 0081/1733] Update Teamspeak --- ct/teamspeak-server.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/teamspeak-server.sh b/ct/teamspeak-server.sh index cb0cb4a39..50cdee999 100644 --- a/ct/teamspeak-server.sh +++ b/ct/teamspeak-server.sh @@ -5,7 +5,7 @@ source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/ # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://teamspeak.com/en/ -APP="Teamspeak Server" +APP="teamspeak-server" var_tags="${var_tags:-voice;communication}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-2048}" From c406dcfa99c166b3b72ac4bc093182ca9a2443bf Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 21 Jul 2025 15:32:20 +0200 Subject: [PATCH 0082/1733] Update Teamspeak --- ct/teamspeak-server.sh | 5 ++--- install/teamspeak-server-install.sh | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/ct/teamspeak-server.sh b/ct/teamspeak-server.sh index 50cdee999..073359202 100644 --- a/ct/teamspeak-server.sh +++ b/ct/teamspeak-server.sh @@ -5,7 +5,7 @@ source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/ # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://teamspeak.com/en/ -APP="teamspeak-server" +APP="Teamspeak-Server" var_tags="${var_tags:-voice;communication}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-2048}" @@ -57,6 +57,5 @@ description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${ADVANCED}${BL}Create your initial user in${CL} ${BGN}/opt/${APP}${CL}${BL} in the LXC:${CL} ${RD}npm run user:create ${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3002${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:7100${CL}" diff --git a/install/teamspeak-server-install.sh b/install/teamspeak-server-install.sh index 2d6c97e26..a02e71473 100644 --- a/install/teamspeak-server-install.sh +++ b/install/teamspeak-server-install.sh @@ -30,7 +30,7 @@ Wants=network-online.target After=network.target [Service] -WorkingDirectory=/opt/teamspeak +WorkingDirectory=/opt/teamspeak-server User=root Type=forking ExecStart=/opt/teamspeak-server/ts3server_startscript.sh start From a8c04a319f74f685bf5d9bf799c0c81549332f4f Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 21 Jul 2025 13:32:43 +0000 Subject: [PATCH 0083/1733] Update .app files --- ct/headers/teamspeak-server | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/teamspeak-server diff --git a/ct/headers/teamspeak-server b/ct/headers/teamspeak-server new file mode 100644 index 000000000..92c3a8e42 --- /dev/null +++ b/ct/headers/teamspeak-server @@ -0,0 +1,6 @@ + ______ __ _____ + /_ __/__ ____ _____ ___ _________ ___ ____ _/ /__ / ___/___ ______ _____ _____ + / / / _ \/ __ `/ __ `__ \/ ___/ __ \/ _ \/ __ `/ //_/_____\__ \/ _ \/ ___/ | / / _ \/ ___/ + / / / __/ /_/ / / / / / (__ ) /_/ / __/ /_/ / ,< /_____/__/ / __/ / | |/ / __/ / +/_/ \___/\__,_/_/ /_/ /_/____/ .___/\___/\__,_/_/|_| /____/\___/_/ |___/\___/_/ + /_/ From b6df57617b09c3f9c5938db0a3eb37dec1597c98 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 21 Jul 2025 15:45:33 +0200 Subject: [PATCH 0084/1733] Update Teamspeak --- install/teamspeak-server-install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install/teamspeak-server-install.sh b/install/teamspeak-server-install.sh index a02e71473..8fb45f2ce 100644 --- a/install/teamspeak-server-install.sh +++ b/install/teamspeak-server-install.sh @@ -20,6 +20,7 @@ curl -fsSL "https://files.teamspeak-services.com/releases/server/${RELEASE}/team tar -xf ./ts3server.tar.bz2 mv teamspeak3-server_linux_amd64/ /opt/teamspeak-server/ touch /opt/teamspeak-server/.ts3server_license_accepted +echo "${RELEASE}" >~/.teamspeak-server msg_ok "Setup Teamspeak Server" msg_info "Creating service" From 43816d79dc9d22e87b260c7bab2ea487bf57bc07 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 15:46:23 +0200 Subject: [PATCH 0085/1733] Update build.func --- misc/build.func | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/misc/build.func b/misc/build.func index dcd5a73fd..7b51e9962 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1390,16 +1390,17 @@ EOF' msg_warn "Skipping timezone setup – zone '$tz' not found in container" fi - pct exec "$CTID" -- bash -c "apt-get update && apt-get install -y sudo curl mc gnupg2" || { + pct exec "$CTID" -- bash -c "$STD apt-get update && $STD apt-get install -y sudo curl mc gnupg2" || { msg_error "apt-get base packages installation failed" exit 1 } fi msg_ok "Customized LXC Container" - if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/"$var_install".sh)"; then - return + if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/${var_install}.sh)"; then + exit $? fi + } # This function sets the description of the container. From 3de63d06f35a74b134731a1420f7305351aea501 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 15:56:18 +0200 Subject: [PATCH 0086/1733] Update build.func --- misc/build.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index 7b51e9962..e7dd0796b 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1390,7 +1390,7 @@ EOF' msg_warn "Skipping timezone setup – zone '$tz' not found in container" fi - pct exec "$CTID" -- bash -c "$STD apt-get update && $STD apt-get install -y sudo curl mc gnupg2" || { + pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y sudo curl mc gnupg2 >/dev/null" || { msg_error "apt-get base packages installation failed" exit 1 } From d54281bb875b4a5a60a406edbe77f799873077bb Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 21 Jul 2025 16:03:26 +0200 Subject: [PATCH 0087/1733] Add Teamspeak json --- ct/teamspeak-server.sh | 6 ++-- frontend/public/json/teamspeak-server.json | 39 ++++++++++++++++++++++ 2 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 frontend/public/json/teamspeak-server.json diff --git a/ct/teamspeak-server.sh b/ct/teamspeak-server.sh index 073359202..5f34dd6a0 100644 --- a/ct/teamspeak-server.sh +++ b/ct/teamspeak-server.sh @@ -8,8 +8,8 @@ source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/ APP="Teamspeak-Server" var_tags="${var_tags:-voice;communication}" var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-4}" +var_ram="${var_ram:-512}" +var_disk="${var_disk:-1}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" @@ -58,4 +58,4 @@ description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:7100${CL}" +echo -e "${TAB}${GATEWAY}${BGN}${IP}:9987${CL}" diff --git a/frontend/public/json/teamspeak-server.json b/frontend/public/json/teamspeak-server.json new file mode 100644 index 000000000..60e8de357 --- /dev/null +++ b/frontend/public/json/teamspeak-server.json @@ -0,0 +1,39 @@ +{ + "name": "Teamspeak Server", + "slug": "teamspeak-server", + "categories": [ + 24 + ], + "date_created": "2025-07-21", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 9987, + "documentation": "https://support.teamspeak.com/hc/en-us/categories/360000302017-TeamSpeak-3", + "website": "https://teamspeak.com/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/teamspeak-light.svgp", + "config_path": "", + "description": "TeamSpeak is a voice‑over‑IP (VoIP) application, primarily used by gamers and teams to chat in real‑time on dedicated servers. It delivers crystal‑clear, low‑latency voice communication.""install_methods": [ + { + "type": "default", + "script": "ct/teamspeak-server.sh", + "resources": { + "cpu": 1, + "ram": 512, + "hdd": 1, + "os": "debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Use `journalctl -u teamspeak-server.service` inside LXC console to check for admin credentials!", + "type": "info" + } + ] +} From 91b21ff24244bd2c3cedd9305774f53db7f80882 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 16:04:14 +0200 Subject: [PATCH 0088/1733] Update build.func --- misc/build.func | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/misc/build.func b/misc/build.func index e7dd0796b..fb60a543c 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1397,10 +1397,9 @@ EOF' fi msg_ok "Customized LXC Container" - if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/${var_install}.sh)"; then - exit $? + if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/"$var_install".sh)"; then + return fi - } # This function sets the description of the container. From bf3d258127ea29c72ade825070b95cd85cdabc2c Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 21 Jul 2025 16:12:15 +0200 Subject: [PATCH 0089/1733] Update Teamspeak --- ct/teamspeak-server.sh | 2 +- frontend/public/json/teamspeak-server.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/teamspeak-server.sh b/ct/teamspeak-server.sh index 5f34dd6a0..55f51b852 100644 --- a/ct/teamspeak-server.sh +++ b/ct/teamspeak-server.sh @@ -9,7 +9,7 @@ APP="Teamspeak-Server" var_tags="${var_tags:-voice;communication}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" -var_disk="${var_disk:-1}" +var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" diff --git a/frontend/public/json/teamspeak-server.json b/frontend/public/json/teamspeak-server.json index 60e8de357..98dbec48e 100644 --- a/frontend/public/json/teamspeak-server.json +++ b/frontend/public/json/teamspeak-server.json @@ -20,7 +20,7 @@ "resources": { "cpu": 1, "ram": 512, - "hdd": 1, + "hdd": 2, "os": "debian", "version": "12" } From c927b75da292e5f03f93fa7074bbad2584f7ecd7 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 16:15:10 +0200 Subject: [PATCH 0090/1733] Update build.func --- misc/build.func | 45 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/misc/build.func b/misc/build.func index fb60a543c..1dc9e741c 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1341,25 +1341,48 @@ EOF # This starts the container and executes -install.sh msg_info "Starting LXC Container" pct start "$CTID" - msg_ok "Started LXC Container" + + # wait for status 'running' + for i in {1..10}; do + if pct status "$CTID" | grep -q "status: running"; then + msg_ok "Startted LXC Container" + break + fi + sleep 1 + if [ "$i" -eq 10 ]; then + msg_error "LXC Container did not reach running state" + exit 1 + fi + done msg_info "Waiting for network in LXC container" for i in {1..10}; do if pct exec "$CTID" -- ping -c1 -W1 deb.debian.org >/dev/null 2>&1; then msg_ok "Network in LXC is reachable" break - else + fi + if [ "$i" -lt 10 ]; then msg_warn "No network yet in LXC (try $i/10) – waiting..." sleep 3 - fi - if [ $i -eq 10 ]; then - msg_error "No network in LXC after waiting. Setting fallback DNS..." - pct set "$CTID" --nameserver 1.1.1.1 - pct set "$CTID" --nameserver 8.8.8.8 - if ! pct exec "$CTID" -- ping -c1 -W1 deb.debian.org >/dev/null 2>&1; then - msg_error "Still no network/DNS in LXC! Aborting customization." - exit 1 - fi + else + msg_error "No network in LXC after waiting." + read -r -p "Set fallback DNS (1.1.1.1/8.8.8.8)? [y/N]: " choice + case "$choice" in + [yY]*) + pct set "$CTID" --nameserver 1.1.1.1 + pct set "$CTID" --nameserver 8.8.8.8 + if pct exec "$CTID" -- ping -c1 -W1 deb.debian.org >/dev/null 2>&1; then + msg_ok "Network reachable after DNS fallback" + else + msg_error "Still no network/DNS in LXC! Aborting customization." + exit 1 + fi + ;; + *) + msg_error "Aborted by user – no DNS fallback set." + exit 1 + ;; + esac fi done From af0b122bd8748bba2fa1b4a226c2b4264476834c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 18:33:34 +0200 Subject: [PATCH 0091/1733] Update rybbit-install.sh --- install/rybbit-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/install/rybbit-install.sh b/install/rybbit-install.sh index 4865a0d8e..62a825b9a 100644 --- a/install/rybbit-install.sh +++ b/install/rybbit-install.sh @@ -32,7 +32,6 @@ msg_info "Setting up PostgreSQL Database" DB_NAME=rybbit_db DB_USER=rybbit DB_PASS="$(openssl rand -base64 18 | cut -c1-13)" -APP_SECRET=$(openssl rand -base64 32) $STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" From 8ac6700b9450cd7c14e436f69783e9c89bc05ede Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 18:39:59 +0200 Subject: [PATCH 0092/1733] Update linkstack-install.sh --- install/linkstack-install.sh | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/install/linkstack-install.sh b/install/linkstack-install.sh index 87e8c14df..f9024516f 100644 --- a/install/linkstack-install.sh +++ b/install/linkstack-install.sh @@ -21,15 +21,14 @@ $STD apt-get install -y \ apt-transport-https msg_ok "Installed dependencies" -PHP_VERSION="8.2" PHP_MODULE="sqlite3, mysql, fileinfo" PHP_APACHE="YES" install_php +PHP_VERSION="8.3" PHP_MODULE="sqlite3, mysql, fileinfo" PHP_APACHE="YES" setup_php msg_info "Installing LinkStack" $STD a2enmod rewrite -ZIP_URL="https://github.com/linkstackorg/linkstack/releases/latest/download/linkstack.zip" -ZIP_FILE="/tmp/linkstack.zip" -curl -fsSL -o "$ZIP_FILE" "$ZIP_URL" -unzip -q "$ZIP_FILE" -d /var/www/html/linkstack +fetch_and_deploy_gh_release "linkstack" "linkstackorg/linkstack" "prebuild" "latest" "/var/www/html/linkstack" + +msg_info "Configuring LinkStack" chown -R www-data:www-data /var/www/html/linkstack chmod -R 755 /var/www/html/linkstack @@ -55,7 +54,6 @@ motd_ssh customize msg_info "Cleaning up" -$STD rm -f "$ZIP_FILE" $STD apt-get -y autoremove $STD apt-get -y autoclean msg_ok "Cleaned" From 22c51351fbfdc62a13bc04858e4917fc0e1a38e8 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 19:23:19 +0200 Subject: [PATCH 0093/1733] Update linkstack-install.sh --- install/linkstack-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/linkstack-install.sh b/install/linkstack-install.sh index f9024516f..a5b60fbe7 100644 --- a/install/linkstack-install.sh +++ b/install/linkstack-install.sh @@ -21,7 +21,7 @@ $STD apt-get install -y \ apt-transport-https msg_ok "Installed dependencies" -PHP_VERSION="8.3" PHP_MODULE="sqlite3, mysql, fileinfo" PHP_APACHE="YES" setup_php +PHP_VERSION="8.3" PHP_MODULE="sqlite3" PHP_APACHE="YES" setup_php msg_info "Installing LinkStack" $STD a2enmod rewrite From 4b243158bd7b45b850a40894e30b0799a0dda049 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 21 Jul 2025 19:30:17 +0200 Subject: [PATCH 0094/1733] Update linkstack-install.sh --- install/linkstack-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/linkstack-install.sh b/install/linkstack-install.sh index a5b60fbe7..afcd4b029 100644 --- a/install/linkstack-install.sh +++ b/install/linkstack-install.sh @@ -26,7 +26,7 @@ PHP_VERSION="8.3" PHP_MODULE="sqlite3" PHP_APACHE="YES" setup_php msg_info "Installing LinkStack" $STD a2enmod rewrite -fetch_and_deploy_gh_release "linkstack" "linkstackorg/linkstack" "prebuild" "latest" "/var/www/html/linkstack" +fetch_and_deploy_gh_release "linkstack" "linkstackorg/linkstack" "prebuild" "latest" "/var/www/html/linkstack" "linkstack.zip" msg_info "Configuring LinkStack" chown -R www-data:www-data /var/www/html/linkstack From ae2c84500ac0459a1b32ecedc00b6ba52611a82f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20B=C3=A9dard-Couture?= Date: Mon, 21 Jul 2025 14:35:06 -0400 Subject: [PATCH 0095/1733] Allow updating stopped containers and check the download of the install script needed to assess build resource requirements --- tools/pve/update-apps.sh | 40 +++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/tools/pve/update-apps.sh b/tools/pve/update-apps.sh index 0c1a979ff..f7727977d 100644 --- a/tools/pve/update-apps.sh +++ b/tools/pve/update-apps.sh @@ -38,8 +38,10 @@ while read -r container; do container_name=$(echo $container | awk '{print $2}') container_status=$(echo $container | awk '{print $3}') formatted_line=$(printf "$FORMAT" "$container_name" "$container_status") - IS_HELPERSCRIPT_LXC=$(pct exec $container_id -- [ -e /usr/bin/update ] && echo true || echo false) - if [ "$IS_HELPERSCRIPT_LXC" = true ]; then + #IS_HELPERSCRIPT_LXC=$(pct exec $container_id -- [ -e /usr/bin/update ] && echo true || echo false) + #if [ "$IS_HELPERSCRIPT_LXC" = true ]; then + detect_service $container + if [ -n "${service}" ]; then menu_items+=("$container_id" "$formatted_line" "OFF") fi done <<< "$containers" @@ -99,6 +101,13 @@ function backup_container(){ fi } +function detect_service(){ + pushd $(mktemp -d) >/dev/null + pct pull "$1" /usr/bin/update update 2>/dev/null + service=$(cat update | sed 's|.*/ct/||g' | sed 's|\.sh).*||g') + popd >/dev/null +} + UPDATE_CMD="update;" if [ "$UNATTENDED_UPDATE" == "yes" ];then UPDATE_CMD="export PHS_SILENT=1;update;" @@ -113,10 +122,7 @@ for container in $CHOICE; do fi #1) Detect service using the service name in the update command - pushd $(mktemp -d) >/dev/null - pct pull "$container" /usr/bin/update update 2>/dev/null - service=$(cat update | sed 's|.*/ct/||g' | sed 's|\.sh).*||g') - popd >/dev/null + detect_service $container #1.1) If update script not detected, return if [ -z "${service}" ]; then @@ -127,7 +133,14 @@ for container in $CHOICE; do fi #2) Extract service build/update resource requirements from config/installation file - script=$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/ct/${service}.sh) + script=$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${service}.sh) + + #2.1) Check if the script downloaded successfully + if [ $? -ne 0 ]; then + echo -e "${RD}[ERROR]${CL} Issue while downloading install script." + echo -e "${YW}[WARN]${CL} Unable to assess build resource requirements. Proceeding with current resources." + fi + config=$(pct config "$container") build_cpu=$(echo "$script" | { grep -m 1 "var_cpu" || test $? = 1; } | sed 's|.*=||g' | sed 's|"||g' | sed 's|.*var_cpu:-||g' | sed 's|}||g') build_ram=$(echo "$script" | { grep -m 1 "var_ram" || test $? = 1; } | sed 's|.*=||g' | sed 's|"||g' | sed 's|.*var_ram:-||g' | sed 's|}||g') @@ -168,6 +181,14 @@ for container in $CHOICE; do fi os=$(pct config "$container" | awk '/^ostype/ {print $2}') + status=$(pct status $container) + template=$(pct config $container | grep -q "template:" && echo "true" || echo "false") + if [ "$template" == "false" ] && [ "$status" == "status: stopped" ]; then + echo -e "${BL}[Info]${GN} Starting${BL} $container ${CL} \n" + pct start $container + echo -e "${BL}[Info]${GN} Waiting For${BL} $container${CL}${GN} To Start ${CL} \n" + sleep 5 + fi #4) Update service, using the update command case "$os" in @@ -179,6 +200,11 @@ for container in $CHOICE; do esac exit_code=$? + if [ "$template" == "false" ] && [ "$status" == "status: stopped" ]; then + echo -e "${BL}[Info]${GN} Shutting down${BL} $container ${CL} \n" + pct shutdown $container & + fi + #5) if build resources are different than run resources, then: if [ "$UPDATE_BUILD_RESOURCES" -eq "1" ]; then pct set "$container" --cores "$run_cpu" --memory "$run_ram" From 0f772b7df561b8887b7cb35b5841eedefa1f0a87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20B=C3=A9dard-Couture?= Date: Mon, 21 Jul 2025 14:42:55 -0400 Subject: [PATCH 0096/1733] Move functions to the top --- tools/pve/update-apps.sh | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/tools/pve/update-apps.sh b/tools/pve/update-apps.sh index f7727977d..1c3924306 100644 --- a/tools/pve/update-apps.sh +++ b/tools/pve/update-apps.sh @@ -18,6 +18,26 @@ function header_info { EOF } +function detect_service(){ + pushd $(mktemp -d) >/dev/null + pct pull "$1" /usr/bin/update update 2>/dev/null + service=$(cat update | sed 's|.*/ct/||g' | sed 's|\.sh).*||g') + popd >/dev/null +} + +function backup_container(){ + msg_info "Creating backup for container $1" + vzdump $1 --compress zstd --storage $STORAGE_CHOICE -notes-template "community-scripts backup updater" > /dev/null 2>&1 + status=$? + + if [ $status -eq 0 ]; then + msg_ok "Backup created" + else + msg_error "Backup failed for container $1" + exit 1 + fi +} + header_info echo "Loading..." whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC Container Update" --yesno "This will update LXC container. Proceed?" 10 58 || exit @@ -88,26 +108,6 @@ if [ "$BACKUP_CHOICE" == "yes" ]; then fi fi -function backup_container(){ - msg_info "Creating backup for container $1" - vzdump $1 --compress zstd --storage $STORAGE_CHOICE -notes-template "community-scripts backup updater" > /dev/null 2>&1 - status=$? - - if [ $status -eq 0 ]; then - msg_ok "Backup created" - else - msg_error "Backup failed for container $1" - exit 1 - fi -} - -function detect_service(){ - pushd $(mktemp -d) >/dev/null - pct pull "$1" /usr/bin/update update 2>/dev/null - service=$(cat update | sed 's|.*/ct/||g' | sed 's|\.sh).*||g') - popd >/dev/null -} - UPDATE_CMD="update;" if [ "$UNATTENDED_UPDATE" == "yes" ];then UPDATE_CMD="export PHS_SILENT=1;update;" From 9b1382ddcf513030ae4205e14ecadf082e69c163 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20B=C3=A9dard-Couture?= Date: Mon, 21 Jul 2025 15:02:40 -0400 Subject: [PATCH 0097/1733] Remove msg_info call to prevent spinner from overriding the whiptail menu --- tools/pve/update-apps.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/pve/update-apps.sh b/tools/pve/update-apps.sh index 1c3924306..e91677d3d 100644 --- a/tools/pve/update-apps.sh +++ b/tools/pve/update-apps.sh @@ -115,7 +115,7 @@ fi containers_needing_reboot=() for container in $CHOICE; do - msg_info "Updating container $container" + echo -e "${BL}[INFO]${CL} Updating container $container" if [ "BACKUP_CHOICE" == "yes" ];then backup_container $container @@ -138,7 +138,7 @@ for container in $CHOICE; do #2.1) Check if the script downloaded successfully if [ $? -ne 0 ]; then echo -e "${RD}[ERROR]${CL} Issue while downloading install script." - echo -e "${YW}[WARN]${CL} Unable to assess build resource requirements. Proceeding with current resources." + echo -e "${YW}[WARN]${CL} Unable to assess build resource requirements. Proceeding with current resources." fi config=$(pct config "$container") From 3e2924d2ed56f43f640c63fe9a3f9aab0ce860aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20B=C3=A9dard-Couture?= Date: Mon, 21 Jul 2025 15:30:07 -0400 Subject: [PATCH 0098/1733] Fix backup storage detection to support local dir --- tools/pve/update-apps.sh | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/tools/pve/update-apps.sh b/tools/pve/update-apps.sh index 1c3924306..adcbe20ce 100644 --- a/tools/pve/update-apps.sh +++ b/tools/pve/update-apps.sh @@ -38,6 +38,31 @@ function backup_container(){ fi } +function get_backup_storages(){ +STORAGES=$(awk ' +/^[a-z]+:/ { + if (name != "") { + if (has_backup || (!has_content && type == "dir")) print name + } + split($0, a, ":") + type = a[1] + name = a[2] + sub(/^ +/, "", name) + has_content = 0 + has_backup = 0 +} +/^ +content/ { + has_content = 1 + if ($0 ~ /backup/) has_backup = 1 +} +END { + if (name != "") { + if (has_backup || (!has_content && type == "dir")) print name + } +} +' /etc/pve/storage.cfg) +} + header_info echo "Loading..." whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC Container Update" --yesno "This will update LXC container. Proceed?" 10 58 || exit @@ -88,7 +113,8 @@ if(whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC Container Updat fi if [ "$BACKUP_CHOICE" == "yes" ]; then - STORAGES=$(awk '/^(\S+):/ {storage=$2} /content.*backup/ {print storage}' /etc/pve/storage.cfg) + #STORAGES=$(awk '/^(\S+):/ {storage=$2} /content.*backup/ {print storage}' /etc/pve/storage.cfg) + get_backup_storages if [ -z "$STORAGES" ]; then whiptail --msgbox "No storage with 'backup' found!" 8 40 From aab59bce4bebeb3c3f806be3f3140db6df193a48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20B=C3=A9dard-Couture?= Date: Mon, 21 Jul 2025 15:41:19 -0400 Subject: [PATCH 0099/1733] Fix missing $ to check variable value and allow backup of container --- tools/pve/update-apps.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/pve/update-apps.sh b/tools/pve/update-apps.sh index ef8fb9a7a..6268cf23b 100644 --- a/tools/pve/update-apps.sh +++ b/tools/pve/update-apps.sh @@ -143,7 +143,7 @@ containers_needing_reboot=() for container in $CHOICE; do echo -e "${BL}[INFO]${CL} Updating container $container" - if [ "BACKUP_CHOICE" == "yes" ];then + if [ "$BACKUP_CHOICE" == "yes" ];then backup_container $container fi From 503e9be156fbe31679a0f63a93f30dfb751d3a0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20B=C3=A9dard-Couture?= Date: Mon, 21 Jul 2025 16:24:31 -0400 Subject: [PATCH 0100/1733] Detect helper scripts based on tags (so it can detect even if the container is shutdown) --- tools/pve/update-apps.sh | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/tools/pve/update-apps.sh b/tools/pve/update-apps.sh index 6268cf23b..73ee0474c 100644 --- a/tools/pve/update-apps.sh +++ b/tools/pve/update-apps.sh @@ -77,18 +77,16 @@ fi menu_items=() FORMAT="%-10s %-15s %-10s" +TAGS="community-script|proxmox-helper-scripts" while read -r container; do - container_id=$(echo $container | awk '{print $1}') - container_name=$(echo $container | awk '{print $2}') - container_status=$(echo $container | awk '{print $3}') - formatted_line=$(printf "$FORMAT" "$container_name" "$container_status") - #IS_HELPERSCRIPT_LXC=$(pct exec $container_id -- [ -e /usr/bin/update ] && echo true || echo false) - #if [ "$IS_HELPERSCRIPT_LXC" = true ]; then - detect_service $container - if [ -n "${service}" ]; then - menu_items+=("$container_id" "$formatted_line" "OFF") - fi + container_id=$(echo $container | awk '{print $1}') + container_name=$(echo $container | awk '{print $2}') + container_status=$(echo $container | awk '{print $3}') + formatted_line=$(printf "$FORMAT" "$container_name" "$container_status") + if pct config $container | grep -E "^tags:.*(${TAGS}).*"; then + menu_items+=("$container_id" "$formatted_line" "OFF") + fi done <<< "$containers" CHOICE=$(whiptail --title "LXC Container Update" \ From 3652194d24bf82e88ef7f253e0bf64bcc9fb401e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20B=C3=A9dard-Couture?= Date: Mon, 21 Jul 2025 16:37:54 -0400 Subject: [PATCH 0101/1733] Fix tag check --- tools/pve/update-apps.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/pve/update-apps.sh b/tools/pve/update-apps.sh index 73ee0474c..c2ff6c8c3 100644 --- a/tools/pve/update-apps.sh +++ b/tools/pve/update-apps.sh @@ -84,7 +84,7 @@ while read -r container; do container_name=$(echo $container | awk '{print $2}') container_status=$(echo $container | awk '{print $3}') formatted_line=$(printf "$FORMAT" "$container_name" "$container_status") - if pct config $container | grep -E "^tags:.*(${TAGS}).*"; then + if pct config "$container_id" | grep -qE "^tags:.*(${TAGS}).*"; then menu_items+=("$container_id" "$formatted_line" "OFF") fi done <<< "$containers" From 787bd613609b257c0c1d4d3430c5defc077ce085 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20B=C3=A9dard-Couture?= Date: Mon, 21 Jul 2025 16:42:16 -0400 Subject: [PATCH 0102/1733] The detect_service function needs to have the container running --- tools/pve/update-apps.sh | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tools/pve/update-apps.sh b/tools/pve/update-apps.sh index c2ff6c8c3..adb7987ed 100644 --- a/tools/pve/update-apps.sh +++ b/tools/pve/update-apps.sh @@ -145,6 +145,16 @@ for container in $CHOICE; do backup_container $container fi + os=$(pct config "$container" | awk '/^ostype/ {print $2}') + status=$(pct status $container) + template=$(pct config $container | grep -q "template:" && echo "true" || echo "false") + if [ "$template" == "false" ] && [ "$status" == "status: stopped" ]; then + echo -e "${BL}[Info]${GN} Starting${BL} $container ${CL} \n" + pct start $container + echo -e "${BL}[Info]${GN} Waiting For${BL} $container${CL}${GN} To Start ${CL} \n" + sleep 5 + fi + #1) Detect service using the service name in the update command detect_service $container @@ -204,16 +214,6 @@ for container in $CHOICE; do pct set "$container" --cores "$build_cpu" --memory "$build_ram" fi - os=$(pct config "$container" | awk '/^ostype/ {print $2}') - status=$(pct status $container) - template=$(pct config $container | grep -q "template:" && echo "true" || echo "false") - if [ "$template" == "false" ] && [ "$status" == "status: stopped" ]; then - echo -e "${BL}[Info]${GN} Starting${BL} $container ${CL} \n" - pct start $container - echo -e "${BL}[Info]${GN} Waiting For${BL} $container${CL}${GN} To Start ${CL} \n" - sleep 5 - fi - #4) Update service, using the update command case "$os" in alpine) pct exec "$container" -- ash -c "$UPDATE_CMD" ;; From 518c66fc2b22a446b722ccd9576e8a8d024fdfb7 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 21 Jul 2025 23:21:38 +0200 Subject: [PATCH 0103/1733] Add Alpine-TeamSpeak --- ct/alpine-teamspeak-server.sh | 57 ++++++++++++++++++ install/alpine-teamspeak-server-install.sh | 69 ++++++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 ct/alpine-teamspeak-server.sh create mode 100644 install/alpine-teamspeak-server-install.sh diff --git a/ct/alpine-teamspeak-server.sh b/ct/alpine-teamspeak-server.sh new file mode 100644 index 000000000..ed7b0e431 --- /dev/null +++ b/ct/alpine-teamspeak-server.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/TwiN/gatus + +APP="Alpine-TeamSpeak-Server" +var_tags="${var_tags:-alpine;communication}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-256}" +var_disk="${var_disk:-2}" +var_os="${var_os:-alpine}" +var_version="${var_version:-3.22}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + + if [[ ! -d /opt/teamspeak-server ]]; then + msg_error "No ${APP} Installation Found!" + exit 1 + fi + + RELEASE=$(curl -fsSL https://teamspeak.com/en/downloads/#server | sed -n 's/.*teamspeak3-server_linux_amd64-\([0-9]\+\.[0-9]\+\.[0-9]\+\).*/\1/p' | head -1) + if [ "${RELEASE}" != "$(cat ~/.teamspeak-server)" ] || [ ! -f ~/.teamspeak-server ]; then + msg_info "Updating ${APP} LXC" + $STD apk -U upgrade + $STD service teamspeak stop + curl -fsSL "https://files.teamspeak-services.com/releases/server/${RELEASE}/teamspeak3-server_linux_amd64-${RELEASE}.tar.bz2" -o ts3server.tar.bz2 + tar -xf ./ts3server.tar.bz2 + cp -ru teamspeak3-server_linux_amd64/* /opt/teamspeak-server/ + rm -f ~/ts3server.tar.bz* + rm -rf teamspeak3-server_linux_amd64 + echo "${RELEASE}" >~/.teamspeak-server + $STD service teamspeak start + msg_ok "Updated Successfully" + else + msg_ok "No update required. ${APP} is already at ${RELEASE}" + fi + + exit 0 +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following IP:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}${IP}:9987${CL}" diff --git a/install/alpine-teamspeak-server-install.sh b/install/alpine-teamspeak-server-install.sh new file mode 100644 index 000000000..e767722b2 --- /dev/null +++ b/install/alpine-teamspeak-server-install.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/TwiN/gatus + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing dependencies" +$STD apk add --no-cache \ + ca-certificates \ + libstdc++ \ + libc6-compat +msg_ok "Installed dependencies" + +RELEASE=$(curl -fsSL https://teamspeak.com/en/downloads/#server | sed -n 's/.*teamspeak3-server_linux_amd64-\([0-9]\+\.[0-9]\+\.[0-9]\+\).*/\1/p' | head -1) + +msg_info "Installing Teamspeak Server v${RELEASE}" +mkdir -p /opt/teamspeak-server +cd /opt/teamspeak-server +curl -fsSL "https://files.teamspeak-services.com/releases/server/${RELEASE}/teamspeak3-server_linux_amd64-${RELEASE}.tar.bz2" -o ts3server.tar.bz2 +tar xf ts3server.tar.bz2 --strip-components=1 +mkdir -p logs data lib +mv *.so lib +touch data/ts3server.sqlitedb data/query_ip_blacklist.txt data/query_ip_whitelist.txt .ts3server_license_accepted +echo "${RELEASE}" >~/.teamspeak-server +msg_ok "Installed TeamSpeak Server v${RELEASE}" + +msg_info "Enabling TeamSpeak Server Service" +cat </etc/init.d/teamspeak +#!/sbin/openrc-run + +name="TeamSpeak Server" +description="TeamSpeak 3 Server" +command="/opt/teamspeak-server/ts3server_startscript.sh" +command_args="start" +output_log="/var/log/teamspeak.out.log" +error_log="/var/log/teamspeak.err.log" +command_background=true +pidfile="/run/teamspeak-server.pid" +directory="/opt/teamspeak-server" + +depend() { + need net + use dns +} +EOF +chmod +x /etc/init.d/teamspeak +$STD rc-update add teamspeak default +msg_ok "Enabled TeamSpeak Server Service" + +msg_info "Starting TeamSpeak Server" +$STD service gatus start +msg_ok "Started TeamSpeak Server" + +motd_ssh +customize + +msg_info "Cleaning up" +rm -r ts3server.tar.bz* LICENSE* CHANGELOG doc serverquerydocs tsdns redist +$STD apk cache clean +msg_ok "Cleaned" From f59b00a1029c71da87464e711a28de5c4e494f8a Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 21 Jul 2025 23:23:35 +0200 Subject: [PATCH 0104/1733] Update headers --- ct/alpine-teamspeak-server.sh | 4 ++-- install/alpine-teamspeak-server-install.sh | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ct/alpine-teamspeak-server.sh b/ct/alpine-teamspeak-server.sh index ed7b0e431..16bb80481 100644 --- a/ct/alpine-teamspeak-server.sh +++ b/ct/alpine-teamspeak-server.sh @@ -1,9 +1,9 @@ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG -# Author: Slaviša Arežina (tremor021) +# Author: tremor021 (Slaviša Arežina) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/TwiN/gatus +# Source: https://teamspeak.com/en/ APP="Alpine-TeamSpeak-Server" var_tags="${var_tags:-alpine;communication}" diff --git a/install/alpine-teamspeak-server-install.sh b/install/alpine-teamspeak-server-install.sh index e767722b2..af311467d 100644 --- a/install/alpine-teamspeak-server-install.sh +++ b/install/alpine-teamspeak-server-install.sh @@ -1,9 +1,9 @@ #!/usr/bin/env bash # Copyright (c) 2021-2025 community-scripts ORG -# Author: Slaviša Arežina (tremor021) +# Author: tremor021 (Slaviša Arežina) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/TwiN/gatus +# Source: https://teamspeak.com/en/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color @@ -57,7 +57,7 @@ $STD rc-update add teamspeak default msg_ok "Enabled TeamSpeak Server Service" msg_info "Starting TeamSpeak Server" -$STD service gatus start +$STD service teamspeak start msg_ok "Started TeamSpeak Server" motd_ssh From d962fb4150c624dc9efaf98dc8980507ca9fb882 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 21 Jul 2025 23:24:44 +0200 Subject: [PATCH 0105/1733] Update Alpine-TeamSpeak --- ct/alpine-teamspeak-server.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/alpine-teamspeak-server.sh b/ct/alpine-teamspeak-server.sh index 16bb80481..1fee6cdc3 100644 --- a/ct/alpine-teamspeak-server.sh +++ b/ct/alpine-teamspeak-server.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: tremor021 (Slaviša Arežina) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE From d346e9042b97488e1984be6bd0e1c04e64892f79 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 21 Jul 2025 21:25:05 +0000 Subject: [PATCH 0106/1733] Update .app files --- ct/headers/alpine-teamspeak-server | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/alpine-teamspeak-server diff --git a/ct/headers/alpine-teamspeak-server b/ct/headers/alpine-teamspeak-server new file mode 100644 index 000000000..cfa5aeb9f --- /dev/null +++ b/ct/headers/alpine-teamspeak-server @@ -0,0 +1,6 @@ + ___ __ _ ______ _____ __ _____ + / | / /___ (_)___ ___ /_ __/__ ____ _____ ___ / ___/____ ___ ____ _/ /__ / ___/___ ______ _____ _____ + / /| | / / __ \/ / __ \/ _ \______/ / / _ \/ __ `/ __ `__ \\__ \/ __ \/ _ \/ __ `/ //_/_____\__ \/ _ \/ ___/ | / / _ \/ ___/ + / ___ |/ / /_/ / / / / / __/_____/ / / __/ /_/ / / / / / /__/ / /_/ / __/ /_/ / ,< /_____/__/ / __/ / | |/ / __/ / +/_/ |_/_/ .___/_/_/ /_/\___/ /_/ \___/\__,_/_/ /_/ /_/____/ .___/\___/\__,_/_/|_| /____/\___/_/ |___/\___/_/ + /_/ /_/ From 49d9984841289aa2d165dac6f19f087e305f4a21 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 22 Jul 2025 09:28:50 +0200 Subject: [PATCH 0107/1733] finalize --- ct/linkstack.sh | 42 +++++++++++++++++++++++++++++------- install/linkstack-install.sh | 2 +- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/ct/linkstack.sh b/ct/linkstack.sh index 2a88277d7..db1ad4ef8 100644 --- a/ct/linkstack.sh +++ b/ct/linkstack.sh @@ -23,20 +23,46 @@ function update_script() { header_info check_container_storage check_container_resources - - if [[ ! -d /var ]]; then + + if [[ ! -f /.linkstack ]]; then msg_error "No ${APP} Installation Found!" exit fi - - msg_info "Updating $APP LXC" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "Updated $APP LXC" - + + RELEASE=$(curl -fsSL https://api.github.com/repos/linkstackorg/linkstack/releases/latest | jq -r '.tag_name | ltrimstr("v")') + if [[ "${RELEASE}" != "$(cat ~/.linkstack 2>/dev/null)" ]] || [[ ! -f ~/.linkstack ]]; then + msg_info "Stopping $APP" + systemctl stop apache2 + msg_ok "Stopped $APP" + + msg_info "Creating Backup" + BACKUP_FILE="/opt/linkstack_backup_$(date +%F).tar.gz" + $STD tar -czf "$BACKUP_FILE" /var/www/html/linkstack + msg_ok "Backup Created" + + PHP_VERSION="8.3" PHP_MODULE="sqlite3" PHP_APACHE="YES" setup_php + fetch_and_deploy_gh_release "linkstack" "linkstackorg/linkstack" "prebuild" "latest" "/var/www/html/linkstack" "linkstack.zip" + + msg_info "Updating $APP to v${RELEASE}" + chown -R www-data:www-data /var/www/html/linkstack + chmod -R 755 /var/www/html/linkstack + msg_ok "Updated $APP to v${RELEASE}" + + msg_info "Starting $APP" + systemctl start linkstack + msg_ok "Started $APP" + + msg_info "Cleaning Up" + rm -rf "$BACKUP_FILE" + msg_ok "Cleanup Completed" + msg_ok "Update Successful" + else + msg_ok "No update required. ${APP} is already at v${RELEASE}" + fi exit } + start build_container description diff --git a/install/linkstack-install.sh b/install/linkstack-install.sh index afcd4b029..72d303863 100644 --- a/install/linkstack-install.sh +++ b/install/linkstack-install.sh @@ -48,7 +48,7 @@ EOF $STD a2dissite 000-default.conf $STD a2ensite linkstack.conf $STD systemctl restart apache2 -msg_ok "Installed LinkStack" +msg_ok "Configured LinkStack" motd_ssh customize From aca09d00dded849d2a4a5bcb1f89c20f431134e3 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 22 Jul 2025 09:30:03 +0200 Subject: [PATCH 0108/1733] finalize --- ct/linkstack.sh | 4 ++-- install/linkstack-install.sh | 14 ++------------ 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/ct/linkstack.sh b/ct/linkstack.sh index db1ad4ef8..da7301b7b 100644 --- a/ct/linkstack.sh +++ b/ct/linkstack.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash -source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG -# Author: Omar Minaya +# Author: Omar Minaya | MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://linkstack.org/ diff --git a/install/linkstack-install.sh b/install/linkstack-install.sh index 72d303863..48c6b9a69 100644 --- a/install/linkstack-install.sh +++ b/install/linkstack-install.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # Copyright (c) 2021-2025 community-scripts ORG -# Author: Omar Minaya +# Author: Omar Minaya | MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://linkstack.org/ @@ -13,22 +13,12 @@ setting_up_container network_check update_os -msg_info "Installing dependencies" -$STD apt-get install -y \ - software-properties-common \ - ca-certificates \ - lsb-release \ - apt-transport-https -msg_ok "Installed dependencies" PHP_VERSION="8.3" PHP_MODULE="sqlite3" PHP_APACHE="YES" setup_php - -msg_info "Installing LinkStack" -$STD a2enmod rewrite - fetch_and_deploy_gh_release "linkstack" "linkstackorg/linkstack" "prebuild" "latest" "/var/www/html/linkstack" "linkstack.zip" msg_info "Configuring LinkStack" +$STD a2enmod rewrite chown -R www-data:www-data /var/www/html/linkstack chmod -R 755 /var/www/html/linkstack From db0c4495206e986f020cc112bd6022cb3798d8c5 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 22 Jul 2025 09:34:02 +0200 Subject: [PATCH 0109/1733] fix json --- ct/linkstack.sh | 4 +- frontend/public/json/linkstack.json | 82 ++++++++++++++--------------- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/ct/linkstack.sh b/ct/linkstack.sh index da7301b7b..2ee0eec4e 100644 --- a/ct/linkstack.sh +++ b/ct/linkstack.sh @@ -8,8 +8,8 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV APP="LinkStack" var_tags="${var_tags:-os}" var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-10}" +var_ram="${var_ram:-1024}" +var_disk="${var_disk:-5}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" diff --git a/frontend/public/json/linkstack.json b/frontend/public/json/linkstack.json index 7c7063451..33c6d980c 100644 --- a/frontend/public/json/linkstack.json +++ b/frontend/public/json/linkstack.json @@ -1,44 +1,44 @@ { - "name": "LinkStack", - "slug": "linkstack", - "categories": [ - 9 - ], - "date_created": "2025-05-21", - "type": "ct", - "updateable": false, - "privileged": false, - "config_path": "/var/www/html/linkstack/linkstack/.env", - "interface_port": 80, - "documentation": "https://docs.linkstack.org/", - "website": "https://linkstack.org/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/linkstack.webp", - "description": "LinkStack is an open-source, self-hosted alternative to Linktree, allowing users to create a customizable profile page to share multiple links, hosted on their own server.", - "install_methods": [ - { - "type": "default", - "script": "ct/linkstack.sh", - "resources": { - "cpu": 1, - "ram": 2048, - "hdd": 10, - "os": "Debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null + "name": "LinkStack", + "slug": "linkstack", + "categories": [ + 9 + ], + "date_created": "2025-07-22", + "type": "ct", + "updateable": true, + "privileged": false, + "config_path": "/var/www/html/linkstack/.env", + "interface_port": 80, + "documentation": "https://docs.linkstack.org/", + "website": "https://linkstack.org/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/linkstack.webp", + "description": "LinkStack is an open-source, self-hosted alternative to Linktree, allowing users to create a customizable profile page to share multiple links, hosted on their own server.", + "install_methods": [ + { + "type": "default", + "script": "ct/linkstack.sh", + "resources": { + "cpu": 1, + "ram": 1024, + "hdd": 5, + "os": "Debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "LinkStack can be updated via the user interface or with the command `update`.", + "type": "info" }, - "notes": [ - { - "text": "LinkStack can be updated via the user interface.", - "type": "info" - }, - { - "text": "Complete setup via the web interface at http:///. Check installation logs: `cat ~/linkstack-install.log`", - "type": "info" - } - ] + { + "text": "Complete setup via the web interface at http:///. Check installation logs: `cat ~/linkstack-install.log`", + "type": "info" + } + ] } From 8fb912a938938b12b2192b6f4936fff0c2e711de Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 22 Jul 2025 09:34:28 +0200 Subject: [PATCH 0110/1733] fix path for apache2 --- install/linkstack-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/linkstack-install.sh b/install/linkstack-install.sh index 48c6b9a69..7e87a8a5c 100644 --- a/install/linkstack-install.sh +++ b/install/linkstack-install.sh @@ -25,10 +25,10 @@ chmod -R 755 /var/www/html/linkstack cat < /etc/apache2/sites-available/linkstack.conf ServerAdmin webmaster@localhost - DocumentRoot /var/www/html/linkstack/linkstack + DocumentRoot /var/www/html/linkstack ErrorLog /var/log/apache2/linkstack-error.log CustomLog /var/log/apache2/linkstack-access.log combined - + Options Indexes FollowSymLinks AllowOverride All Require all granted From 4c9208507482293ca5676b8f59027a0e3bf88f26 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 22 Jul 2025 09:34:58 +0200 Subject: [PATCH 0111/1733] Update linkstack.sh --- ct/linkstack.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/linkstack.sh b/ct/linkstack.sh index 2ee0eec4e..731033bf9 100644 --- a/ct/linkstack.sh +++ b/ct/linkstack.sh @@ -69,5 +69,5 @@ description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Complete setup at:${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" From 6c74217c191c5ca1c4516dd7d73727ebf8f9ec5c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 22 Jul 2025 09:37:53 +0200 Subject: [PATCH 0112/1733] formatting --- .vscode/settings.json | 2 +- misc/build.func | 2115 +++++++++++++++++++++-------------------- 2 files changed, 1061 insertions(+), 1056 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 8f17c7ff9..47009d646 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -18,7 +18,7 @@ "editor.minimap.enabled": false, "terminal.integrated.scrollback": 10000, "[shellscript]": { - "editor.defaultFormatter": "foxundermoon.shell-format", + "editor.defaultFormatter": "mads-hartmann.bash-ide-vscode", "editor.tabSize": 4, "editor.insertSpaces": true, }, diff --git a/misc/build.func b/misc/build.func index 1dc9e741c..588051550 100644 --- a/misc/build.func +++ b/misc/build.func @@ -6,211 +6,210 @@ # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE variables() { - NSAPP=$(echo "${APP,,}" | tr -d ' ') # This function sets the NSAPP variable by converting the value of the APP variable to lowercase and removing any spaces. - var_install="${NSAPP}-install" # sets the var_install variable by appending "-install" to the value of NSAPP. - INTEGER='^[0-9]+([.][0-9]+)?$' # it defines the INTEGER regular expression pattern. - PVEHOST_NAME=$(hostname) # gets the Proxmox Hostname and sets it to Uppercase - DIAGNOSTICS="yes" # sets the DIAGNOSTICS variable to "yes", used for the API call. - METHOD="default" # sets the METHOD variable to "default", used for the API call. - RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" # generates a random UUID and sets it to the RANDOM_UUID variable. - CT_TYPE=${var_unprivileged:-$CT_TYPE} + NSAPP=$(echo "${APP,,}" | tr -d ' ') # This function sets the NSAPP variable by converting the value of the APP variable to lowercase and removing any spaces. + var_install="${NSAPP}-install" # sets the var_install variable by appending "-install" to the value of NSAPP. + INTEGER='^[0-9]+([.][0-9]+)?$' # it defines the INTEGER regular expression pattern. + PVEHOST_NAME=$(hostname) # gets the Proxmox Hostname and sets it to Uppercase + DIAGNOSTICS="yes" # sets the DIAGNOSTICS variable to "yes", used for the API call. + METHOD="default" # sets the METHOD variable to "default", used for the API call. + RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" # generates a random UUID and sets it to the RANDOM_UUID variable. + CT_TYPE=${var_unprivileged:-$CT_TYPE} } source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) if command -v curl >/dev/null 2>&1; then - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) - load_functions - #echo "(build.func) Loaded core.func via curl" + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + load_functions + #echo "(build.func) Loaded core.func via curl" elif command -v wget >/dev/null 2>&1; then - source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) - load_functions - #echo "(build.func) Loaded core.func via wget" + source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + load_functions + #echo "(build.func) Loaded core.func via wget" fi # This function enables error handling in the script by setting options and defining a trap for the ERR signal. catch_errors() { - set -Eeo pipefail - # if [ -n "$BASH_VERSION" ] && command -v shopt >/dev/null 2>&1; then - # shopt -s errtrace - # fi - trap 'error_handler $LINENO "$BASH_COMMAND"' ERR + set -Eeo pipefail + # if [ -n "$BASH_VERSION" ] && command -v shopt >/dev/null 2>&1; then + # shopt -s errtrace + # fi + trap 'error_handler $LINENO "$BASH_COMMAND"' ERR } # This function is called when an error occurs. It receives the exit code, line number, and command that caused the error, and displays an error message. error_handler() { - local exit_code="$?" - local line_number="$1" - local command="$2" - printf "\e[?25h" - local error_message="[ERROR] in line $line_number: exit code $exit_code: while executing command $command" - post_update_to_api "failed" "$command" - echo -e "\n$error_message\n" - exit "$exit_code" + local exit_code="$?" + local line_number="$1" + local command="$2" + printf "\e[?25h" + local error_message="[ERROR] in line $line_number: exit code $exit_code: while executing command $command" + post_update_to_api "failed" "$command" + echo -e "\n$error_message\n" + exit "$exit_code" - if [[ -n "$CT_ID" ]]; then - read -p "Remove this Container? " prompt - if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then - pct stop "$CT_ID" &>/dev/null - pct destroy "$CT_ID" &>/dev/null - msg_ok "Removed this Container" + if [[ -n "$CT_ID" ]]; then + read -p "Remove this Container? " prompt + if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then + pct stop "$CT_ID" &>/dev/null + pct destroy "$CT_ID" &>/dev/null + msg_ok "Removed this Container" + fi fi - fi } # Check if the shell is using bash shell_check() { - if [[ "$(basename "$SHELL")" != "bash" ]]; then - clear - msg_error "Your default shell is currently not set to Bash. To use these scripts, please switch to the Bash shell." - echo -e "\nExiting..." - sleep 2 - exit - fi + if [[ "$(basename "$SHELL")" != "bash" ]]; then + clear + msg_error "Your default shell is currently not set to Bash. To use these scripts, please switch to the Bash shell." + echo -e "\nExiting..." + sleep 2 + exit + fi } # Run as root only root_check() { - if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then - clear - msg_error "Please run this script as root." - echo -e "\nExiting..." - sleep 2 - exit - fi + if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then + clear + msg_error "Please run this script as root." + echo -e "\nExiting..." + sleep 2 + exit + fi } # This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported. pve_check() { - local PVE_VER - PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" + local PVE_VER + PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" - # 8 Version Check - if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then - local MINOR="${BASH_REMATCH[1]}" - if (( MINOR < 1 || MINOR > 4 )); then - msg_error "This version of Proxmox Virtual Environment is not supported" - echo -e "Requires Proxmox Virtual Environment Version 8.1 – 8.4" - echo -e "Exiting..." - sleep 2 - exit 1 + # 8 Version Check + if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then + local MINOR="${BASH_REMATCH[1]}" + if ((MINOR < 1 || MINOR > 4)); then + msg_error "This version of Proxmox Virtual Environment is not supported" + echo -e "Requires Proxmox Virtual Environment Version 8.1 – 8.4" + echo -e "Exiting..." + sleep 2 + exit 1 + fi + return 0 fi - return 0 - fi - # 9 Beta Version Check - if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then - msg_custom "Detected Proxmox Virtual Environment $PVE_VER – Beta state, use with caution!" - return 0 - fi + # 9 Beta Version Check + if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then + msg_custom "Detected Proxmox Virtual Environment $PVE_VER – Beta state, use with caution!" + return 0 + fi - # All others (unsupported versions) - msg_error "This version of Proxmox Virtual Environment is not supported" - echo -e "Requires Proxmox Virtual Environment Version 8.1 – 8.4 or 9.x (Beta)" - echo -e "Exiting..." - sleep 2 - exit 1 + # All others (unsupported versions) + msg_error "This version of Proxmox Virtual Environment is not supported" + echo -e "Requires Proxmox Virtual Environment Version 8.1 – 8.4 or 9.x (Beta)" + echo -e "Exiting..." + sleep 2 + exit 1 } - # When a node is running tens of containers, it's possible to exceed the kernel's cryptographic key storage allocations. # These are tuneable, so verify if the currently deployment is approaching the limits, advise the user on how to tune the limits, and exit the script. # https://cleveruptime.com/docs/files/proc-key-users | https://docs.kernel.org/security/keys/core.html maxkeys_check() { - # Read kernel parameters - per_user_maxkeys=$(cat /proc/sys/kernel/keys/maxkeys 2>/dev/null || echo 0) - per_user_maxbytes=$(cat /proc/sys/kernel/keys/maxbytes 2>/dev/null || echo 0) + # Read kernel parameters + per_user_maxkeys=$(cat /proc/sys/kernel/keys/maxkeys 2>/dev/null || echo 0) + per_user_maxbytes=$(cat /proc/sys/kernel/keys/maxbytes 2>/dev/null || echo 0) - # Exit if kernel parameters are unavailable - if [[ "$per_user_maxkeys" -eq 0 || "$per_user_maxbytes" -eq 0 ]]; then - echo -e "${CROSS}${RD} Error: Unable to read kernel parameters. Ensure proper permissions.${CL}" - exit 1 - fi + # Exit if kernel parameters are unavailable + if [[ "$per_user_maxkeys" -eq 0 || "$per_user_maxbytes" -eq 0 ]]; then + echo -e "${CROSS}${RD} Error: Unable to read kernel parameters. Ensure proper permissions.${CL}" + exit 1 + fi - # Fetch key usage for user ID 100000 (typical for containers) - used_lxc_keys=$(awk '/100000:/ {print $2}' /proc/key-users 2>/dev/null || echo 0) - used_lxc_bytes=$(awk '/100000:/ {split($5, a, "/"); print a[1]}' /proc/key-users 2>/dev/null || echo 0) + # Fetch key usage for user ID 100000 (typical for containers) + used_lxc_keys=$(awk '/100000:/ {print $2}' /proc/key-users 2>/dev/null || echo 0) + used_lxc_bytes=$(awk '/100000:/ {split($5, a, "/"); print a[1]}' /proc/key-users 2>/dev/null || echo 0) - # Calculate thresholds and suggested new limits - threshold_keys=$((per_user_maxkeys - 100)) - threshold_bytes=$((per_user_maxbytes - 1000)) - new_limit_keys=$((per_user_maxkeys * 2)) - new_limit_bytes=$((per_user_maxbytes * 2)) + # Calculate thresholds and suggested new limits + threshold_keys=$((per_user_maxkeys - 100)) + threshold_bytes=$((per_user_maxbytes - 1000)) + new_limit_keys=$((per_user_maxkeys * 2)) + new_limit_bytes=$((per_user_maxbytes * 2)) - # Check if key or byte usage is near limits - failure=0 - if [[ "$used_lxc_keys" -gt "$threshold_keys" ]]; then - echo -e "${CROSS}${RD} Warning: Key usage is near the limit (${used_lxc_keys}/${per_user_maxkeys}).${CL}" - echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxkeys=${new_limit_keys}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." - failure=1 - fi - if [[ "$used_lxc_bytes" -gt "$threshold_bytes" ]]; then - echo -e "${CROSS}${RD} Warning: Key byte usage is near the limit (${used_lxc_bytes}/${per_user_maxbytes}).${CL}" - echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxbytes=${new_limit_bytes}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." - failure=1 - fi + # Check if key or byte usage is near limits + failure=0 + if [[ "$used_lxc_keys" -gt "$threshold_keys" ]]; then + echo -e "${CROSS}${RD} Warning: Key usage is near the limit (${used_lxc_keys}/${per_user_maxkeys}).${CL}" + echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxkeys=${new_limit_keys}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." + failure=1 + fi + if [[ "$used_lxc_bytes" -gt "$threshold_bytes" ]]; then + echo -e "${CROSS}${RD} Warning: Key byte usage is near the limit (${used_lxc_bytes}/${per_user_maxbytes}).${CL}" + echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxbytes=${new_limit_bytes}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." + failure=1 + fi - # Provide next steps if issues are detected - if [[ "$failure" -eq 1 ]]; then - echo -e "${INFO} To apply changes, run: ${BOLD}service procps force-reload${CL}" - exit 1 - fi + # Provide next steps if issues are detected + if [[ "$failure" -eq 1 ]]; then + echo -e "${INFO} To apply changes, run: ${BOLD}service procps force-reload${CL}" + exit 1 + fi - echo -e "${CM}${GN} All kernel key limits are within safe thresholds.${CL}" + echo -e "${CM}${GN} All kernel key limits are within safe thresholds.${CL}" } # This function checks the system architecture and exits if it's not "amd64". arch_check() { - if [ "$(dpkg --print-architecture)" != "amd64" ]; then - echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" - echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" - echo -e "Exiting..." - sleep 2 - exit - fi + if [ "$(dpkg --print-architecture)" != "amd64" ]; then + echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" + echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" + echo -e "Exiting..." + sleep 2 + exit + fi } # Function to get the current IP address based on the distribution get_current_ip() { - if [ -f /etc/os-release ]; then - # Check for Debian/Ubuntu (uses hostname -I) - if grep -qE 'ID=debian|ID=ubuntu' /etc/os-release; then - CURRENT_IP=$(hostname -I | awk '{print $1}') - # Check for Alpine (uses ip command) - elif grep -q 'ID=alpine' /etc/os-release; then - CURRENT_IP=$(ip -4 addr show eth0 | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) - else - CURRENT_IP="Unknown" + if [ -f /etc/os-release ]; then + # Check for Debian/Ubuntu (uses hostname -I) + if grep -qE 'ID=debian|ID=ubuntu' /etc/os-release; then + CURRENT_IP=$(hostname -I | awk '{print $1}') + # Check for Alpine (uses ip command) + elif grep -q 'ID=alpine' /etc/os-release; then + CURRENT_IP=$(ip -4 addr show eth0 | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) + else + CURRENT_IP="Unknown" + fi fi - fi - echo "$CURRENT_IP" + echo "$CURRENT_IP" } # Function to update the IP address in the MOTD file update_motd_ip() { - MOTD_FILE="/etc/motd" + MOTD_FILE="/etc/motd" - if [ -f "$MOTD_FILE" ]; then - # Remove existing IP Address lines to prevent duplication - sed -i '/IP Address:/d' "$MOTD_FILE" + if [ -f "$MOTD_FILE" ]; then + # Remove existing IP Address lines to prevent duplication + sed -i '/IP Address:/d' "$MOTD_FILE" - IP=$(get_current_ip) - # Add the new IP address - echo -e "${TAB}${NETWORK}${YW} IP Address: ${GN}${IP}${CL}" >>"$MOTD_FILE" - fi + IP=$(get_current_ip) + # Add the new IP address + echo -e "${TAB}${NETWORK}${YW} IP Address: ${GN}${IP}${CL}" >>"$MOTD_FILE" + fi } # This function checks if the script is running through SSH and prompts the user to confirm if they want to proceed or exit. ssh_check() { - if [ -n "${SSH_CLIENT:+x}" ]; then - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's advisable to utilize the Proxmox shell rather than SSH, as there may be potential complications with variable retrieval. Proceed using SSH?" 10 72; then - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox --title "Proceed using SSH" "You've chosen to proceed using SSH. If any issues arise, please run the script in the Proxmox shell before creating a repository issue." 10 72 - else - clear - echo "Exiting due to SSH usage. Please consider using the Proxmox shell." - exit + if [ -n "${SSH_CLIENT:+x}" ]; then + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's advisable to utilize the Proxmox shell rather than SSH, as there may be potential complications with variable retrieval. Proceed using SSH?" 10 72; then + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox --title "Proceed using SSH" "You've chosen to proceed using SSH. If any issues arise, please run the script in the Proxmox shell before creating a repository issue." 10 72 + else + clear + echo "Exiting due to SSH usage. Please consider using the Proxmox shell." + exit + fi fi - fi } # select_storage() { @@ -301,62 +300,62 @@ ssh_check() { # } base_settings() { - # Default Settings - CT_TYPE="1" - DISK_SIZE="4" - CORE_COUNT="1" - RAM_SIZE="1024" - VERBOSE="${1:-no}" - PW="" - CT_ID=$NEXTID - HN=$NSAPP - BRG="vmbr0" - NET="dhcp" - IPV6_METHOD="none" - IPV6_STATIC="" - GATE="" - APT_CACHER="" - APT_CACHER_IP="" - #DISABLEIP6="no" - MTU="" - SD="" - NS="" - MAC="" - VLAN="" - SSH="no" - SSH_AUTHORIZED_KEY="" - UDHCPC_FIX="" - TAGS="community-script;" - ENABLE_FUSE="${1:-no}" - ENABLE_TUN="${1:-no}" + # Default Settings + CT_TYPE="1" + DISK_SIZE="4" + CORE_COUNT="1" + RAM_SIZE="1024" + VERBOSE="${1:-no}" + PW="" + CT_ID=$NEXTID + HN=$NSAPP + BRG="vmbr0" + NET="dhcp" + IPV6_METHOD="none" + IPV6_STATIC="" + GATE="" + APT_CACHER="" + APT_CACHER_IP="" + #DISABLEIP6="no" + MTU="" + SD="" + NS="" + MAC="" + VLAN="" + SSH="no" + SSH_AUTHORIZED_KEY="" + UDHCPC_FIX="" + TAGS="community-script;" + ENABLE_FUSE="${1:-no}" + ENABLE_TUN="${1:-no}" - # Override default settings with variables from ct script - CT_TYPE=${var_unprivileged:-$CT_TYPE} - DISK_SIZE=${var_disk:-$DISK_SIZE} - CORE_COUNT=${var_cpu:-$CORE_COUNT} - RAM_SIZE=${var_ram:-$RAM_SIZE} - VERB=${var_verbose:-$VERBOSE} - TAGS="${TAGS}${var_tags:-}" - ENABLE_FUSE="${var_fuse:-$ENABLE_FUSE}" - ENABLE_TUN="${var_tun:-$ENABLE_TUN}" - APT_CACHER="${var_apt_cacher:-$APT_CACHER}" - APT_CACHER_IP="${var_apt_cacher_ip:-$APT_CACHER_IP}" + # Override default settings with variables from ct script + CT_TYPE=${var_unprivileged:-$CT_TYPE} + DISK_SIZE=${var_disk:-$DISK_SIZE} + CORE_COUNT=${var_cpu:-$CORE_COUNT} + RAM_SIZE=${var_ram:-$RAM_SIZE} + VERB=${var_verbose:-$VERBOSE} + TAGS="${TAGS}${var_tags:-}" + ENABLE_FUSE="${var_fuse:-$ENABLE_FUSE}" + ENABLE_TUN="${var_tun:-$ENABLE_TUN}" + APT_CACHER="${var_apt_cacher:-$APT_CACHER}" + APT_CACHER_IP="${var_apt_cacher_ip:-$APT_CACHER_IP}" - # Since these 2 are only defined outside of default_settings function, we add a temporary fallback. TODO: To align everything, we should add these as constant variables (e.g. OSTYPE and OSVERSION), but that would currently require updating the default_settings function for all existing scripts - if [ -z "$var_os" ]; then - var_os="debian" - fi - if [ -z "$var_version" ]; then - var_version="12" - fi + # Since these 2 are only defined outside of default_settings function, we add a temporary fallback. TODO: To align everything, we should add these as constant variables (e.g. OSTYPE and OSVERSION), but that would currently require updating the default_settings function for all existing scripts + if [ -z "$var_os" ]; then + var_os="debian" + fi + if [ -z "$var_version" ]; then + var_version="12" + fi } write_config() { - mkdir -p /opt/community-scripts - # This function writes the configuration to a file. - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "Write configfile" --yesno "Do you want to write the selections to a config file?" 10 60; then - FILEPATH="/opt/community-scripts/${NSAPP}.conf" - if [[ ! -f $FILEPATH ]]; then - cat <"$FILEPATH" + mkdir -p /opt/community-scripts + # This function writes the configuration to a file. + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "Write configfile" --yesno "Do you want to write the selections to a config file?" 10 60; then + FILEPATH="/opt/community-scripts/${NSAPP}.conf" + if [[ ! -f $FILEPATH ]]; then + cat <"$FILEPATH" # ${NSAPP} Configuration File # Generated on $(date) @@ -383,12 +382,12 @@ NET="${NET}" FUSE="${ENABLE_FUSE}" EOF - echo -e "${INFO}${BOLD}${GN}Writing configuration to ${FILEPATH}${CL}" - else - echo -e "${INFO}${BOLD}${RD}Configuration file already exists at ${FILEPATH}${CL}" - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "Overwrite configfile" --yesno "Do you want to overwrite the existing config file?" 10 60; then - rm -f "$FILEPATH" - cat <"$FILEPATH" + echo -e "${INFO}${BOLD}${GN}Writing configuration to ${FILEPATH}${CL}" + else + echo -e "${INFO}${BOLD}${RD}Configuration file already exists at ${FILEPATH}${CL}" + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "Overwrite configfile" --yesno "Do you want to overwrite the existing config file?" 10 60; then + rm -f "$FILEPATH" + cat <"$FILEPATH" # ${NSAPP} Configuration File # Generated on $(date) @@ -415,518 +414,518 @@ NET="${NET}" FUSE="${ENABLE_FUSE}" EOF - echo -e "${INFO}${BOLD}${GN}Writing configuration to ${FILEPATH}${CL}" - else - echo -e "${INFO}${BOLD}${RD}Configuration file not overwritten${CL}" - fi + echo -e "${INFO}${BOLD}${GN}Writing configuration to ${FILEPATH}${CL}" + else + echo -e "${INFO}${BOLD}${RD}Configuration file not overwritten${CL}" + fi + fi fi - fi } # This function displays the default values for various settings. echo_default() { - # Convert CT_TYPE to description - CT_TYPE_DESC="Unprivileged" - if [ "$CT_TYPE" -eq 0 ]; then - CT_TYPE_DESC="Privileged" - fi + # Convert CT_TYPE to description + CT_TYPE_DESC="Unprivileged" + if [ "$CT_TYPE" -eq 0 ]; then + CT_TYPE_DESC="Privileged" + fi - # Output the selected values with icons - echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}${CT_ID}${CL}" - echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os ($var_version)${CL}" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" - if [ "$VERB" == "yes" ]; then - echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}Enabled${CL}" - fi - echo -e "${CREATING}${BOLD}${BL}Creating a ${APP} LXC using the above default settings${CL}" - echo -e " " + # Output the selected values with icons + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}${CT_ID}${CL}" + echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os ($var_version)${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + if [ "$VERB" == "yes" ]; then + echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}Enabled${CL}" + fi + echo -e "${CREATING}${BOLD}${BL}Creating a ${APP} LXC using the above default settings${CL}" + echo -e " " } # This function is called when the user decides to exit the script. It clears the screen and displays an exit message. exit_script() { - clear - echo -e "\n${CROSS}${RD}User exited script${CL}\n" - exit + clear + echo -e "\n${CROSS}${RD}User exited script${CL}\n" + exit } # This function allows the user to configure advanced settings for the script. advanced_settings() { - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox --title "Here is an instructional tip:" "To make a selection, use the Spacebar." 8 58 - # Setting Default Tag for Advanced Settings - TAGS="community-script;${var_tags:-}" - CT_DEFAULT_TYPE="${CT_TYPE}" - CT_TYPE="" - while [ -z "$CT_TYPE" ]; do - if [ "$CT_DEFAULT_TYPE" == "1" ]; then - if CT_TYPE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ - "1" "Unprivileged" ON \ - "0" "Privileged" OFF \ - 3>&1 1>&2 2>&3); then - if [ -n "$CT_TYPE" ]; then - CT_TYPE_DESC="Unprivileged" - if [ "$CT_TYPE" -eq 0 ]; then - CT_TYPE_DESC="Privileged" - fi - echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os | ${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox --title "Here is an instructional tip:" "To make a selection, use the Spacebar." 8 58 + # Setting Default Tag for Advanced Settings + TAGS="community-script;${var_tags:-}" + CT_DEFAULT_TYPE="${CT_TYPE}" + CT_TYPE="" + while [ -z "$CT_TYPE" ]; do + if [ "$CT_DEFAULT_TYPE" == "1" ]; then + if CT_TYPE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ + "1" "Unprivileged" ON \ + "0" "Privileged" OFF \ + 3>&1 1>&2 2>&3); then + if [ -n "$CT_TYPE" ]; then + CT_TYPE_DESC="Unprivileged" + if [ "$CT_TYPE" -eq 0 ]; then + CT_TYPE_DESC="Privileged" + fi + echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os | ${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" + fi + else + exit_script + fi fi - else + if [ "$CT_DEFAULT_TYPE" == "0" ]; then + if CT_TYPE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ + "1" "Unprivileged" OFF \ + "0" "Privileged" ON \ + 3>&1 1>&2 2>&3); then + if [ -n "$CT_TYPE" ]; then + CT_TYPE_DESC="Unprivileged" + if [ "$CT_TYPE" -eq 0 ]; then + CT_TYPE_DESC="Privileged" + fi + echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os${CL}" + echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" + fi + else + exit_script + fi + fi + done + + while true; do + if PW1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --passwordbox "\nSet Root Password (needed for root ssh access)" 9 58 --title "PASSWORD (leave blank for automatic login)" 3>&1 1>&2 2>&3); then + # Empty = Autologin + if [[ -z "$PW1" ]]; then + PW="" + PW1="Automatic Login" + echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}$PW1${CL}" + break + fi + + # Invalid: contains spaces + if [[ "$PW1" == *" "* ]]; then + whiptail --msgbox "Password cannot contain spaces." 8 58 + continue + fi + + # Invalid: too short + if ((${#PW1} < 5)); then + whiptail --msgbox "Password must be at least 5 characters." 8 58 + continue + fi + + # Confirm password + if PW2=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then + if [[ "$PW1" == "$PW2" ]]; then + PW="-password $PW1" + echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}" + break + else + whiptail --msgbox "Passwords do not match. Please try again." 8 58 + fi + else + exit_script + fi + else + exit_script + fi + done + + if CT_ID=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Container ID" 8 58 "$NEXTID" --title "CONTAINER ID" 3>&1 1>&2 2>&3); then + if [ -z "$CT_ID" ]; then + CT_ID="$NEXTID" + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" + else + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" + fi + else exit_script - fi fi - if [ "$CT_DEFAULT_TYPE" == "0" ]; then - if CT_TYPE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ - "1" "Unprivileged" OFF \ - "0" "Privileged" ON \ - 3>&1 1>&2 2>&3); then - if [ -n "$CT_TYPE" ]; then - CT_TYPE_DESC="Unprivileged" - if [ "$CT_TYPE" -eq 0 ]; then - CT_TYPE_DESC="Privileged" - fi - echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os${CL}" - echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" + + while true; do + if CT_NAME=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 "$NSAPP" --title "HOSTNAME" 3>&1 1>&2 2>&3); then + if [ -z "$CT_NAME" ]; then + HN="$NSAPP" + else + HN=$(echo "${CT_NAME,,}" | tr -d ' ') + fi + # Hostname validate (RFC 1123) + if [[ "$HN" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ ]]; then + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" + break + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --msgbox "❌ Invalid hostname: '$HN'\n\nOnly lowercase letters, digits and hyphens (-) are allowed.\nUnderscores (_) or other characters are not permitted!" 10 70 + fi + else + exit_script fi - else + done + + while true; do + DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GB" 8 58 "$var_disk" --title "DISK SIZE" 3>&1 1>&2 2>&3) || exit_script + + if [ -z "$DISK_SIZE" ]; then + DISK_SIZE="$var_disk" + fi + + if [[ "$DISK_SIZE" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" + break + else + whiptail --msgbox "Disk size must be a positive integer!" 8 58 + fi + done + + while true; do + CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --inputbox "Allocate CPU Cores" 8 58 "$var_cpu" --title "CORE COUNT" 3>&1 1>&2 2>&3) || exit_script + + if [ -z "$CORE_COUNT" ]; then + CORE_COUNT="$var_cpu" + fi + + if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + break + else + whiptail --msgbox "CPU core count must be a positive integer!" 8 58 + fi + done + + while true; do + RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --inputbox "Allocate RAM in MiB" 8 58 "$var_ram" --title "RAM" 3>&1 1>&2 2>&3) || exit_script + + if [ -z "$RAM_SIZE" ]; then + RAM_SIZE="$var_ram" + fi + + if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + break + else + whiptail --msgbox "RAM size must be a positive integer!" 8 58 + fi + done + + IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f) + BRIDGES="" + OLD_IFS=$IFS + IFS=$'\n' + for iface_filepath in ${IFACE_FILEPATH_LIST}; do + + iface_indexes_tmpfile=$(mktemp -q -u '.iface-XXXX') + (grep -Pn '^\s*iface' "${iface_filepath}" | cut -d':' -f1 && wc -l "${iface_filepath}" | cut -d' ' -f1) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' >"${iface_indexes_tmpfile}" || true + + if [ -f "${iface_indexes_tmpfile}" ]; then + + while read -r pair; do + start=$(echo "${pair}" | cut -d':' -f1) + end=$(echo "${pair}" | cut -d':' -f2) + + if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then + iface_name=$(sed "${start}q;d" "${iface_filepath}" | awk '{print $2}') + BRIDGES="${iface_name}"$'\n'"${BRIDGES}" + fi + + done <"${iface_indexes_tmpfile}" + rm -f "${iface_indexes_tmpfile}" + fi + + done + IFS=$OLD_IFS + BRIDGES=$(echo "$BRIDGES" | grep -v '^\s*$' | sort | uniq) + if [[ -z "$BRIDGES" ]]; then + BRG="vmbr0" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + else + BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge:" 15 40 6 $(echo "$BRIDGES" | awk '{print $0, "Bridge"}') 3>&1 1>&2 2>&3) + if [[ -z "$BRG" ]]; then + exit_script + else + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + fi + fi + + # IPv4 methods: dhcp, static, none + while true; do + IPV4_METHOD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "IPv4 Address Management" \ + --menu "Select IPv4 Address Assignment Method:" 12 60 2 \ + "dhcp" "Automatic (DHCP, recommended)" \ + "static" "Static (manual entry)" \ + 3>&1 1>&2 2>&3) + + exit_status=$? + if [ $exit_status -ne 0 ]; then + exit_script + fi + + case "$IPV4_METHOD" in + dhcp) + NET="dhcp" + GATE="" + echo -e "${NETWORK}${BOLD}${DGN}IPv4: DHCP${CL}" + break + ;; + static) + # Static: call and validate CIDR address + while true; do + NET=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Enter Static IPv4 CIDR Address (e.g. 192.168.100.50/24)" 8 58 "" \ + --title "IPv4 ADDRESS" 3>&1 1>&2 2>&3) + if [ -z "$NET" ]; then + whiptail --msgbox "IPv4 address must not be empty." 8 58 + continue + elif [[ "$NET" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then + echo -e "${NETWORK}${BOLD}${DGN}IPv4 Address: ${BGN}$NET${CL}" + break + else + whiptail --msgbox "$NET is not a valid IPv4 CIDR address. Please enter a correct value!" 8 58 + fi + done + + # call and validate Gateway + while true; do + GATE1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Enter Gateway IP address for static IPv4" 8 58 "" \ + --title "Gateway IP" 3>&1 1>&2 2>&3) + if [ -z "$GATE1" ]; then + whiptail --msgbox "Gateway IP address cannot be empty." 8 58 + elif [[ ! "$GATE1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then + whiptail --msgbox "Invalid Gateway IP address format." 8 58 + else + GATE=",gw=$GATE1" + echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE1${CL}" + break + fi + done + break + ;; + esac + done + + # IPv6 Address Management selection + while true; do + IPV6_METHOD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --menu \ + "Select IPv6 Address Management Type:" 15 58 4 \ + "auto" "SLAAC/AUTO (recommended, default)" \ + "dhcp" "DHCPv6" \ + "static" "Static (manual entry)" \ + "none" "Disabled" \ + --default-item "auto" 3>&1 1>&2 2>&3) + [ $? -ne 0 ] && exit_script + + case "$IPV6_METHOD" in + auto) + echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}SLAAC/AUTO${CL}" + IPV6_ADDR="" + IPV6_GATE="" + break + ;; + dhcp) + echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}DHCPv6${CL}" + IPV6_ADDR="dhcp" + IPV6_GATE="" + break + ;; + static) + # Ask for static IPv6 address (CIDR notation, e.g., 2001:db8::1234/64) + while true; do + IPV6_ADDR=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox \ + "Set a static IPv6 CIDR address (e.g., 2001:db8::1234/64)" 8 58 "" \ + --title "IPv6 STATIC ADDRESS" 3>&1 1>&2 2>&3) || exit_script + if [[ "$IPV6_ADDR" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+(/[0-9]{1,3})$ ]]; then + echo -e "${NETWORK}${BOLD}${DGN}IPv6 Address: ${BGN}$IPV6_ADDR${CL}" + break + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox \ + "$IPV6_ADDR is an invalid IPv6 CIDR address. Please enter a valid IPv6 CIDR address (e.g., 2001:db8::1234/64)" 8 58 + fi + done + # Optional: ask for IPv6 gateway for static config + while true; do + IPV6_GATE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox \ + "Enter IPv6 gateway address (optional, leave blank for none)" 8 58 "" --title "IPv6 GATEWAY" 3>&1 1>&2 2>&3) + if [ -z "$IPV6_GATE" ]; then + IPV6_GATE="" + break + elif [[ "$IPV6_GATE" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+$ ]]; then + break + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox \ + "Invalid IPv6 gateway format." 8 58 + fi + done + break + ;; + none) + echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}Disabled${CL}" + IPV6_ADDR="none" + IPV6_GATE="" + break + ;; + *) + exit_script + ;; + esac + done + + if [ "$var_os" == "alpine" ]; then + APT_CACHER="" + APT_CACHER_IP="" + else + if APT_CACHER_IP=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set APT-Cacher IP (leave blank for none)" 8 58 --title "APT-Cacher IP" 3>&1 1>&2 2>&3); then + APT_CACHER="${APT_CACHER_IP:+yes}" + echo -e "${NETWORK}${BOLD}${DGN}APT-Cacher IP Address: ${BGN}${APT_CACHER_IP:-Default}${CL}" + else + exit_script + fi + fi + + # if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "IPv6" --yesno "Disable IPv6?" 10 58); then + # DISABLEIP6="yes" + # else + # DISABLEIP6="no" + # fi + # echo -e "${DISABLEIPV6}${BOLD}${DGN}Disable IPv6: ${BGN}$DISABLEIP6${CL}" + + if MTU1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default [The MTU of your selected vmbr, default is 1500])" 8 58 --title "MTU SIZE" 3>&1 1>&2 2>&3); then + if [ -z "$MTU1" ]; then + MTU1="Default" + MTU="" + else + MTU=",mtu=$MTU1" + fi + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" + else exit_script - fi fi - done - while true; do - if PW1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --passwordbox "\nSet Root Password (needed for root ssh access)" 9 58 --title "PASSWORD (leave blank for automatic login)" 3>&1 1>&2 2>&3); then - # Empty = Autologin - if [[ -z "$PW1" ]]; then - PW="" - PW1="Automatic Login" - echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}$PW1${CL}" - break - fi - - # Invalid: contains spaces - if [[ "$PW1" == *" "* ]]; then - whiptail --msgbox "Password cannot contain spaces." 8 58 - continue - fi - - # Invalid: too short - if ((${#PW1} < 5)); then - whiptail --msgbox "Password must be at least 5 characters." 8 58 - continue - fi - - # Confirm password - if PW2=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then - if [[ "$PW1" == "$PW2" ]]; then - PW="-password $PW1" - echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}" - break + if SD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a DNS Search Domain (leave blank for HOST)" 8 58 --title "DNS Search Domain" 3>&1 1>&2 2>&3); then + if [ -z "$SD" ]; then + SX=Host + SD="" else - whiptail --msgbox "Passwords do not match. Please try again." 8 58 + SX=$SD + SD="-searchdomain=$SD" fi - else + echo -e "${SEARCH}${BOLD}${DGN}DNS Search Domain: ${BGN}$SX${CL}" + else exit_script - fi - else - exit_script - fi - done - - if CT_ID=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Container ID" 8 58 "$NEXTID" --title "CONTAINER ID" 3>&1 1>&2 2>&3); then - if [ -z "$CT_ID" ]; then - CT_ID="$NEXTID" - echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" - else - echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" - fi - else - exit_script - fi - - while true; do - if CT_NAME=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 "$NSAPP" --title "HOSTNAME" 3>&1 1>&2 2>&3); then - if [ -z "$CT_NAME" ]; then - HN="$NSAPP" - else - HN=$(echo "${CT_NAME,,}" | tr -d ' ') - fi - # Hostname validate (RFC 1123) - if [[ "$HN" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ ]]; then - echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" - break - else - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --msgbox "❌ Invalid hostname: '$HN'\n\nOnly lowercase letters, digits and hyphens (-) are allowed.\nUnderscores (_) or other characters are not permitted!" 10 70 - fi - else - exit_script - fi - done - - while true; do - DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GB" 8 58 "$var_disk" --title "DISK SIZE" 3>&1 1>&2 2>&3) || exit_script - - if [ -z "$DISK_SIZE" ]; then - DISK_SIZE="$var_disk" fi - if [[ "$DISK_SIZE" =~ ^[1-9][0-9]*$ ]]; then - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" - break - else - whiptail --msgbox "Disk size must be a positive integer!" 8 58 - fi - done - - while true; do - CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ - --inputbox "Allocate CPU Cores" 8 58 "$var_cpu" --title "CORE COUNT" 3>&1 1>&2 2>&3) || exit_script - - if [ -z "$CORE_COUNT" ]; then - CORE_COUNT="$var_cpu" - fi - - if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" - break - else - whiptail --msgbox "CPU core count must be a positive integer!" 8 58 - fi - done - - while true; do - RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ - --inputbox "Allocate RAM in MiB" 8 58 "$var_ram" --title "RAM" 3>&1 1>&2 2>&3) || exit_script - - if [ -z "$RAM_SIZE" ]; then - RAM_SIZE="$var_ram" - fi - - if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" - break - else - whiptail --msgbox "RAM size must be a positive integer!" 8 58 - fi - done - - IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f) - BRIDGES="" - OLD_IFS=$IFS - IFS=$'\n' - for iface_filepath in ${IFACE_FILEPATH_LIST}; do - - iface_indexes_tmpfile=$(mktemp -q -u '.iface-XXXX') - (grep -Pn '^\s*iface' "${iface_filepath}" | cut -d':' -f1 && wc -l "${iface_filepath}" | cut -d' ' -f1) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' >"${iface_indexes_tmpfile}" || true - - if [ -f "${iface_indexes_tmpfile}" ]; then - - while read -r pair; do - start=$(echo "${pair}" | cut -d':' -f1) - end=$(echo "${pair}" | cut -d':' -f2) - - if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then - iface_name=$(sed "${start}q;d" "${iface_filepath}" | awk '{print $2}') - BRIDGES="${iface_name}"$'\n'"${BRIDGES}" - fi - - done <"${iface_indexes_tmpfile}" - rm -f "${iface_indexes_tmpfile}" - fi - - done - IFS=$OLD_IFS - BRIDGES=$(echo "$BRIDGES" | grep -v '^\s*$' | sort | uniq) - if [[ -z "$BRIDGES" ]]; then - BRG="vmbr0" - echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" - else - BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge:" 15 40 6 $(echo "$BRIDGES" | awk '{print $0, "Bridge"}') 3>&1 1>&2 2>&3) - if [[ -z "$BRG" ]]; then - exit_script - else - echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" - fi - fi - - # IPv4 methods: dhcp, static, none - while true; do - IPV4_METHOD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "IPv4 Address Management" \ - --menu "Select IPv4 Address Assignment Method:" 12 60 2 \ - "dhcp" "Automatic (DHCP, recommended)" \ - "static" "Static (manual entry)" \ - 3>&1 1>&2 2>&3) - - exit_status=$? - if [ $exit_status -ne 0 ]; then - exit_script - fi - - case "$IPV4_METHOD" in - dhcp) - NET="dhcp" - GATE="" - echo -e "${NETWORK}${BOLD}${DGN}IPv4: DHCP${CL}" - break - ;; - static) - # Static: call and validate CIDR address - while true; do - NET=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --inputbox "Enter Static IPv4 CIDR Address (e.g. 192.168.100.50/24)" 8 58 "" \ - --title "IPv4 ADDRESS" 3>&1 1>&2 2>&3) - if [ -z "$NET" ]; then - whiptail --msgbox "IPv4 address must not be empty." 8 58 - continue - elif [[ "$NET" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then - echo -e "${NETWORK}${BOLD}${DGN}IPv4 Address: ${BGN}$NET${CL}" - break + if NX=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a DNS Server IP (leave blank for HOST)" 8 58 --title "DNS SERVER IP" 3>&1 1>&2 2>&3); then + if [ -z "$NX" ]; then + NX=Host + NS="" else - whiptail --msgbox "$NET is not a valid IPv4 CIDR address. Please enter a correct value!" 8 58 + NS="-nameserver=$NX" fi - done + echo -e "${NETWORK}${BOLD}${DGN}DNS Server IP Address: ${BGN}$NX${CL}" + else + exit_script + fi - # call and validate Gateway - while true; do - GATE1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --inputbox "Enter Gateway IP address for static IPv4" 8 58 "" \ - --title "Gateway IP" 3>&1 1>&2 2>&3) - if [ -z "$GATE1" ]; then - whiptail --msgbox "Gateway IP address cannot be empty." 8 58 - elif [[ ! "$GATE1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then - whiptail --msgbox "Invalid Gateway IP address format." 8 58 + if [ "$var_os" == "alpine" ] && [ "$NET" == "dhcp" ] && [ "$NX" != "Host" ]; then + UDHCPC_FIX="yes" + else + UDHCPC_FIX="no" + fi + export UDHCPC_FIX + + if MAC1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a MAC Address(leave blank for generated MAC)" 8 58 --title "MAC ADDRESS" 3>&1 1>&2 2>&3); then + if [ -z "$MAC1" ]; then + MAC1="Default" + MAC="" else - GATE=",gw=$GATE1" - echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE1${CL}" - break + MAC=",hwaddr=$MAC1" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" fi - done - break - ;; - esac - done + else + exit_script + fi - # IPv6 Address Management selection - while true; do - IPV6_METHOD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --menu \ - "Select IPv6 Address Management Type:" 15 58 4 \ - "auto" "SLAAC/AUTO (recommended, default)" \ - "dhcp" "DHCPv6" \ - "static" "Static (manual entry)" \ - "none" "Disabled" \ - --default-item "auto" 3>&1 1>&2 2>&3) - [ $? -ne 0 ] && exit_script - - case "$IPV6_METHOD" in - auto) - echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}SLAAC/AUTO${CL}" - IPV6_ADDR="" - IPV6_GATE="" - break - ;; - dhcp) - echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}DHCPv6${CL}" - IPV6_ADDR="dhcp" - IPV6_GATE="" - break - ;; - static) - # Ask for static IPv6 address (CIDR notation, e.g., 2001:db8::1234/64) - while true; do - IPV6_ADDR=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox \ - "Set a static IPv6 CIDR address (e.g., 2001:db8::1234/64)" 8 58 "" \ - --title "IPv6 STATIC ADDRESS" 3>&1 1>&2 2>&3) || exit_script - if [[ "$IPV6_ADDR" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+(/[0-9]{1,3})$ ]]; then - echo -e "${NETWORK}${BOLD}${DGN}IPv6 Address: ${BGN}$IPV6_ADDR${CL}" - break + if VLAN1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for no VLAN)" 8 58 --title "VLAN" 3>&1 1>&2 2>&3); then + if [ -z "$VLAN1" ]; then + VLAN1="Default" + VLAN="" else - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox \ - "$IPV6_ADDR is an invalid IPv6 CIDR address. Please enter a valid IPv6 CIDR address (e.g., 2001:db8::1234/64)" 8 58 + VLAN=",tag=$VLAN1" fi - done - # Optional: ask for IPv6 gateway for static config - while true; do - IPV6_GATE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox \ - "Enter IPv6 gateway address (optional, leave blank for none)" 8 58 "" --title "IPv6 GATEWAY" 3>&1 1>&2 2>&3) - if [ -z "$IPV6_GATE" ]; then - IPV6_GATE="" - break - elif [[ "$IPV6_GATE" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+$ ]]; then - break + echo -e "${VLANTAG}${BOLD}${DGN}Vlan: ${BGN}$VLAN1${CL}" + else + exit_script + fi + + if ADV_TAGS=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Custom Tags?[If you remove all, there will be no tags!]" 8 58 "${TAGS}" --title "Advanced Tags" 3>&1 1>&2 2>&3); then + if [ -n "${ADV_TAGS}" ]; then + ADV_TAGS=$(echo "$ADV_TAGS" | tr -d '[:space:]') + TAGS="${ADV_TAGS}" else - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox \ - "Invalid IPv6 gateway format." 8 58 + TAGS=";" fi - done - break - ;; - none) - echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}Disabled${CL}" - IPV6_ADDR="none" - IPV6_GATE="" - break - ;; - *) - exit_script - ;; - esac - done - - if [ "$var_os" == "alpine" ]; then - APT_CACHER="" - APT_CACHER_IP="" - else - if APT_CACHER_IP=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set APT-Cacher IP (leave blank for none)" 8 58 --title "APT-Cacher IP" 3>&1 1>&2 2>&3); then - APT_CACHER="${APT_CACHER_IP:+yes}" - echo -e "${NETWORK}${BOLD}${DGN}APT-Cacher IP Address: ${BGN}${APT_CACHER_IP:-Default}${CL}" + echo -e "${NETWORK}${BOLD}${DGN}Tags: ${BGN}$TAGS${CL}" else - exit_script + exit_script fi - fi - # if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "IPv6" --yesno "Disable IPv6?" 10 58); then - # DISABLEIP6="yes" - # else - # DISABLEIP6="no" - # fi - # echo -e "${DISABLEIPV6}${BOLD}${DGN}Disable IPv6: ${BGN}$DISABLEIP6${CL}" + SSH_AUTHORIZED_KEY="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "SSH Authorized key for root (leave empty for none)" 8 58 --title "SSH Key" 3>&1 1>&2 2>&3)" - if MTU1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default [The MTU of your selected vmbr, default is 1500])" 8 58 --title "MTU SIZE" 3>&1 1>&2 2>&3); then - if [ -z "$MTU1" ]; then - MTU1="Default" - MTU="" + if [[ -z "${SSH_AUTHORIZED_KEY}" ]]; then + SSH_AUTHORIZED_KEY="" + fi + + if [[ "$PW" == -password* || -n "$SSH_AUTHORIZED_KEY" ]]; then + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable Root SSH Access?" 10 58); then + SSH="yes" + else + SSH="no" + fi + echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}" else - MTU=",mtu=$MTU1" + SSH="no" + echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}" fi - echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" - else - exit_script - fi - if SD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a DNS Search Domain (leave blank for HOST)" 8 58 --title "DNS Search Domain" 3>&1 1>&2 2>&3); then - if [ -z "$SD" ]; then - SX=Host - SD="" + if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "FUSE Support" --yesno "Enable FUSE support?\nRequired for tools like rclone, mergerfs, AppImage, etc." 10 58); then + ENABLE_FUSE="yes" else - SX=$SD - SD="-searchdomain=$SD" + ENABLE_FUSE="no" fi - echo -e "${SEARCH}${BOLD}${DGN}DNS Search Domain: ${BGN}$SX${CL}" - else - exit_script - fi + echo -e "${FUSE}${BOLD}${DGN}Enable FUSE Support: ${BGN}$ENABLE_FUSE${CL}" - if NX=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a DNS Server IP (leave blank for HOST)" 8 58 --title "DNS SERVER IP" 3>&1 1>&2 2>&3); then - if [ -z "$NX" ]; then - NX=Host - NS="" + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then + VERBOSE="yes" else - NS="-nameserver=$NX" + VERBOSE="no" fi - echo -e "${NETWORK}${BOLD}${DGN}DNS Server IP Address: ${BGN}$NX${CL}" - else - exit_script - fi + echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}" - if [ "$var_os" == "alpine" ] && [ "$NET" == "dhcp" ] && [ "$NX" != "Host" ]; then - UDHCPC_FIX="yes" - else - UDHCPC_FIX="no" - fi - export UDHCPC_FIX - - if MAC1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a MAC Address(leave blank for generated MAC)" 8 58 --title "MAC ADDRESS" 3>&1 1>&2 2>&3); then - if [ -z "$MAC1" ]; then - MAC1="Default" - MAC="" + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create ${APP} LXC?" 10 58); then + echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}" + write_config else - MAC=",hwaddr=$MAC1" - echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" + clear + header_info + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings on node $PVEHOST_NAME${CL}" + advanced_settings fi - else - exit_script - fi - - if VLAN1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for no VLAN)" 8 58 --title "VLAN" 3>&1 1>&2 2>&3); then - if [ -z "$VLAN1" ]; then - VLAN1="Default" - VLAN="" - else - VLAN=",tag=$VLAN1" - fi - echo -e "${VLANTAG}${BOLD}${DGN}Vlan: ${BGN}$VLAN1${CL}" - else - exit_script - fi - - if ADV_TAGS=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Custom Tags?[If you remove all, there will be no tags!]" 8 58 "${TAGS}" --title "Advanced Tags" 3>&1 1>&2 2>&3); then - if [ -n "${ADV_TAGS}" ]; then - ADV_TAGS=$(echo "$ADV_TAGS" | tr -d '[:space:]') - TAGS="${ADV_TAGS}" - else - TAGS=";" - fi - echo -e "${NETWORK}${BOLD}${DGN}Tags: ${BGN}$TAGS${CL}" - else - exit_script - fi - - SSH_AUTHORIZED_KEY="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "SSH Authorized key for root (leave empty for none)" 8 58 --title "SSH Key" 3>&1 1>&2 2>&3)" - - if [[ -z "${SSH_AUTHORIZED_KEY}" ]]; then - SSH_AUTHORIZED_KEY="" - fi - - if [[ "$PW" == -password* || -n "$SSH_AUTHORIZED_KEY" ]]; then - if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable Root SSH Access?" 10 58); then - SSH="yes" - else - SSH="no" - fi - echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}" - else - SSH="no" - echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}" - fi - - if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "FUSE Support" --yesno "Enable FUSE support?\nRequired for tools like rclone, mergerfs, AppImage, etc." 10 58); then - ENABLE_FUSE="yes" - else - ENABLE_FUSE="no" - fi - echo -e "${FUSE}${BOLD}${DGN}Enable FUSE Support: ${BGN}$ENABLE_FUSE${CL}" - - if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then - VERBOSE="yes" - else - VERBOSE="no" - fi - echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}" - - if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create ${APP} LXC?" 10 58); then - echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}" - write_config - else - clear - header_info - echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings on node $PVEHOST_NAME${CL}" - advanced_settings - fi } diagnostics_check() { - if ! [ -d "/usr/local/community-scripts" ]; then - mkdir -p /usr/local/community-scripts - fi + if ! [ -d "/usr/local/community-scripts" ]; then + mkdir -p /usr/local/community-scripts + fi - if ! [ -f "/usr/local/community-scripts/diagnostics" ]; then - if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS" --yesno "Send Diagnostics of LXC Installation?\n\n(This only transmits data without user data, just RAM, CPU, LXC name, ...)" 10 58); then - cat </usr/local/community-scripts/diagnostics + if ! [ -f "/usr/local/community-scripts/diagnostics" ]; then + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS" --yesno "Send Diagnostics of LXC Installation?\n\n(This only transmits data without user data, just RAM, CPU, LXC name, ...)" 10 58); then + cat </usr/local/community-scripts/diagnostics DIAGNOSTICS=yes #This file is used to store the diagnostics settings for the Community-Scripts API. @@ -951,9 +950,9 @@ DIAGNOSTICS=yes #"status" #If you have any concerns, please review the source code at /misc/build.func EOF - DIAGNOSTICS="yes" - else - cat </usr/local/community-scripts/diagnostics + DIAGNOSTICS="yes" + else + cat </usr/local/community-scripts/diagnostics DIAGNOSTICS=no #This file is used to store the diagnostics settings for the Community-Scripts API. @@ -978,248 +977,248 @@ DIAGNOSTICS=no #"status" #If you have any concerns, please review the source code at /misc/build.func EOF - DIAGNOSTICS="no" - fi - else - DIAGNOSTICS=$(awk -F '=' '/^DIAGNOSTICS/ {print $2}' /usr/local/community-scripts/diagnostics) + DIAGNOSTICS="no" + fi + else + DIAGNOSTICS=$(awk -F '=' '/^DIAGNOSTICS/ {print $2}' /usr/local/community-scripts/diagnostics) - fi + fi } install_script() { - pve_check - shell_check - root_check - arch_check - ssh_check - maxkeys_check - diagnostics_check + pve_check + shell_check + root_check + arch_check + ssh_check + maxkeys_check + diagnostics_check - if systemctl is-active -q ping-instances.service; then - systemctl -q stop ping-instances.service - fi - NEXTID=$(pvesh get /cluster/nextid) - timezone=$(cat /etc/timezone) - header_info - while true; do - - TMP_CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "SETTINGS" \ - --menu "Choose an option:" 20 60 7 \ - "1" "Default Settings" \ - "2" "Default Settings (with verbose)" \ - "3" "Advanced Settings" \ - "4" "Use Config File" \ - "5" "Manage Default Storage" \ - "6" "Diagnostic Settings" \ - "7" "Exit" \ - --default-item "1" 3>&1 1>&2 2>&3) || true - - if [ -z "$TMP_CHOICE" ]; then - echo -e "\n${CROSS}${RD}Menu canceled. Exiting script.${CL}\n" - exit 0 + if systemctl is-active -q ping-instances.service; then + systemctl -q stop ping-instances.service fi + NEXTID=$(pvesh get /cluster/nextid) + timezone=$(cat /etc/timezone) + header_info + while true; do - CHOICE="$TMP_CHOICE" + TMP_CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "SETTINGS" \ + --menu "Choose an option:" 20 60 7 \ + "1" "Default Settings" \ + "2" "Default Settings (with verbose)" \ + "3" "Advanced Settings" \ + "4" "Use Config File" \ + "5" "Manage Default Storage" \ + "6" "Diagnostic Settings" \ + "7" "Exit" \ + --default-item "1" 3>&1 1>&2 2>&3) || true - case $CHOICE in - 1) - header_info - echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME${CL}" - VERBOSE="no" - METHOD="default" - base_settings "$VERBOSE" - echo_default - break - ;; - 2) - header_info - echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME (${VERBOSE_CROPPED}Verbose)${CL}" - VERBOSE="yes" - METHOD="default" - base_settings "$VERBOSE" - echo_default - break - ;; - 3) - header_info - echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings on node $PVEHOST_NAME${CL}" - METHOD="advanced" - base_settings - advanced_settings - break - ;; - 4) - header_info - echo -e "${INFO}${HOLD} ${GN}Using Config File on node $PVEHOST_NAME${CL}" - METHOD="advanced" - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/config-file.func) - config_file - break - ;; - - 5) - manage_default_storage - ;; - 6) - if [[ $DIAGNOSTICS == "yes" ]]; then - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS SETTINGS" --yesno "Send Diagnostics of LXC Installation?\n\nCurrent setting: ${DIAGNOSTICS}" 10 58 \ - --yes-button "No" --no-button "Back"; then - DIAGNOSTICS="no" - sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=no/' /usr/local/community-scripts/diagnostics - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS SETTINGS" --msgbox "Diagnostics settings changed to ${DIAGNOSTICS}." 8 58 + if [ -z "$TMP_CHOICE" ]; then + echo -e "\n${CROSS}${RD}Menu canceled. Exiting script.${CL}\n" + exit 0 fi - else - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS SETTINGS" --yesno "Send Diagnostics of LXC Installation?\n\nCurrent setting: ${DIAGNOSTICS}" 10 58 \ - --yes-button "Yes" --no-button "Back"; then - DIAGNOSTICS="yes" - sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=yes/' /usr/local/community-scripts/diagnostics - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS SETTINGS" --msgbox "Diagnostics settings changed to ${DIAGNOSTICS}." 8 58 - fi - fi - ;; - 7) - echo -e "\n${CROSS}${RD}Script terminated. Have a great day!${CL}\n" - exit 0 - ;; - *) - echo -e "${CROSS}${RD}Invalid option, please try again.${CL}" - ;; - esac - done + + CHOICE="$TMP_CHOICE" + + case $CHOICE in + 1) + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME${CL}" + VERBOSE="no" + METHOD="default" + base_settings "$VERBOSE" + echo_default + break + ;; + 2) + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME (${VERBOSE_CROPPED}Verbose)${CL}" + VERBOSE="yes" + METHOD="default" + base_settings "$VERBOSE" + echo_default + break + ;; + 3) + header_info + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings on node $PVEHOST_NAME${CL}" + METHOD="advanced" + base_settings + advanced_settings + break + ;; + 4) + header_info + echo -e "${INFO}${HOLD} ${GN}Using Config File on node $PVEHOST_NAME${CL}" + METHOD="advanced" + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/config-file.func) + config_file + break + ;; + + 5) + manage_default_storage + ;; + 6) + if [[ $DIAGNOSTICS == "yes" ]]; then + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS SETTINGS" --yesno "Send Diagnostics of LXC Installation?\n\nCurrent setting: ${DIAGNOSTICS}" 10 58 \ + --yes-button "No" --no-button "Back"; then + DIAGNOSTICS="no" + sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=no/' /usr/local/community-scripts/diagnostics + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS SETTINGS" --msgbox "Diagnostics settings changed to ${DIAGNOSTICS}." 8 58 + fi + else + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS SETTINGS" --yesno "Send Diagnostics of LXC Installation?\n\nCurrent setting: ${DIAGNOSTICS}" 10 58 \ + --yes-button "Yes" --no-button "Back"; then + DIAGNOSTICS="yes" + sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=yes/' /usr/local/community-scripts/diagnostics + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS SETTINGS" --msgbox "Diagnostics settings changed to ${DIAGNOSTICS}." 8 58 + fi + fi + ;; + 7) + echo -e "\n${CROSS}${RD}Script terminated. Have a great day!${CL}\n" + exit 0 + ;; + *) + echo -e "${CROSS}${RD}Invalid option, please try again.${CL}" + ;; + esac + done } check_container_resources() { - # Check actual RAM & Cores - current_ram=$(free -m | awk 'NR==2{print $2}') - current_cpu=$(nproc) + # Check actual RAM & Cores + current_ram=$(free -m | awk 'NR==2{print $2}') + current_cpu=$(nproc) - # Check whether the current RAM is less than the required RAM or the CPU cores are less than required - if [[ "$current_ram" -lt "$var_ram" ]] || [[ "$current_cpu" -lt "$var_cpu" ]]; then - echo -e "\n${INFO}${HOLD} ${GN}Required: ${var_cpu} CPU, ${var_ram}MB RAM ${CL}| ${RD}Current: ${current_cpu} CPU, ${current_ram}MB RAM${CL}" - echo -e "${YWB}Please ensure that the ${APP} LXC is configured with at least ${var_cpu} vCPU and ${var_ram} MB RAM for the build process.${CL}\n" - echo -ne "${INFO}${HOLD} May cause data loss! ${INFO} Continue update with under-provisioned LXC? " - read -r prompt - # Check if the input is 'yes', otherwise exit with status 1 - if [[ ! ${prompt,,} =~ ^(yes)$ ]]; then - echo -e "${CROSS}${HOLD} ${YWB}Exiting based on user input.${CL}" - exit 1 + # Check whether the current RAM is less than the required RAM or the CPU cores are less than required + if [[ "$current_ram" -lt "$var_ram" ]] || [[ "$current_cpu" -lt "$var_cpu" ]]; then + echo -e "\n${INFO}${HOLD} ${GN}Required: ${var_cpu} CPU, ${var_ram}MB RAM ${CL}| ${RD}Current: ${current_cpu} CPU, ${current_ram}MB RAM${CL}" + echo -e "${YWB}Please ensure that the ${APP} LXC is configured with at least ${var_cpu} vCPU and ${var_ram} MB RAM for the build process.${CL}\n" + echo -ne "${INFO}${HOLD} May cause data loss! ${INFO} Continue update with under-provisioned LXC? " + read -r prompt + # Check if the input is 'yes', otherwise exit with status 1 + if [[ ! ${prompt,,} =~ ^(yes)$ ]]; then + echo -e "${CROSS}${HOLD} ${YWB}Exiting based on user input.${CL}" + exit 1 + fi + else + echo -e "" fi - else - echo -e "" - fi } check_container_storage() { - # Check if the /boot partition is more than 80% full - total_size=$(df /boot --output=size | tail -n 1) - local used_size=$(df /boot --output=used | tail -n 1) - usage=$((100 * used_size / total_size)) - if ((usage > 80)); then - # Prompt the user for confirmation to continue - echo -e "${INFO}${HOLD} ${YWB}Warning: Storage is dangerously low (${usage}%).${CL}" - echo -ne "Continue anyway? " - read -r prompt - # Check if the input is 'y' or 'yes', otherwise exit with status 1 - if [[ ! ${prompt,,} =~ ^(y|yes)$ ]]; then - echo -e "${CROSS}${HOLD}${YWB}Exiting based on user input.${CL}" - exit 1 + # Check if the /boot partition is more than 80% full + total_size=$(df /boot --output=size | tail -n 1) + local used_size=$(df /boot --output=used | tail -n 1) + usage=$((100 * used_size / total_size)) + if ((usage > 80)); then + # Prompt the user for confirmation to continue + echo -e "${INFO}${HOLD} ${YWB}Warning: Storage is dangerously low (${usage}%).${CL}" + echo -ne "Continue anyway? " + read -r prompt + # Check if the input is 'y' or 'yes', otherwise exit with status 1 + if [[ ! ${prompt,,} =~ ^(y|yes)$ ]]; then + echo -e "${CROSS}${HOLD}${YWB}Exiting based on user input.${CL}" + exit 1 + fi fi - fi } start() { - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) - if command -v pveversion >/dev/null 2>&1; then - install_script - elif [ ! -z ${PHS_SILENT+x} ] && [[ "${PHS_SILENT}" == "1" ]]; then - VERBOSE="no" - set_std_mode - update_script - else - CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "${APP} LXC Update/Setting" --menu \ - "Support/Update functions for ${APP} LXC. Choose an option:" \ - 12 60 3 \ - "1" "YES (Silent Mode)" \ - "2" "YES (Verbose Mode)" \ - "3" "NO (Cancel Update)" --nocancel --default-item "1" 3>&1 1>&2 2>&3) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) + if command -v pveversion >/dev/null 2>&1; then + install_script + elif [ ! -z ${PHS_SILENT+x} ] && [[ "${PHS_SILENT}" == "1" ]]; then + VERBOSE="no" + set_std_mode + update_script + else + CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "${APP} LXC Update/Setting" --menu \ + "Support/Update functions for ${APP} LXC. Choose an option:" \ + 12 60 3 \ + "1" "YES (Silent Mode)" \ + "2" "YES (Verbose Mode)" \ + "3" "NO (Cancel Update)" --nocancel --default-item "1" 3>&1 1>&2 2>&3) - case "$CHOICE" in - 1) - VERBOSE="no" - set_std_mode - ;; - 2) - VERBOSE="yes" - set_std_mode - ;; - 3) - clear - exit_script - exit - ;; - esac - update_script - fi + case "$CHOICE" in + 1) + VERBOSE="no" + set_std_mode + ;; + 2) + VERBOSE="yes" + set_std_mode + ;; + 3) + clear + exit_script + exit + ;; + esac + update_script + fi } # This function collects user settings and integrates all the collected information. build_container() { - # if [ "$VERBOSE" == "yes" ]; then set -x; fi + # if [ "$VERBOSE" == "yes" ]; then set -x; fi - NET_STRING="-net0 name=eth0,bridge=$BRG$MAC,ip=$NET$GATE$VLAN$MTU" - case "$IPV6_METHOD" in - auto) NET_STRING="$NET_STRING,ip6=auto" ;; - dhcp) NET_STRING="$NET_STRING,ip6=dhcp" ;; - static) - NET_STRING="$NET_STRING,ip6=$IPV6_ADDR" - [ -n "$IPV6_GATE" ] && NET_STRING="$NET_STRING,gw6=$IPV6_GATE" - ;; - none) ;; - esac + NET_STRING="-net0 name=eth0,bridge=$BRG$MAC,ip=$NET$GATE$VLAN$MTU" + case "$IPV6_METHOD" in + auto) NET_STRING="$NET_STRING,ip6=auto" ;; + dhcp) NET_STRING="$NET_STRING,ip6=dhcp" ;; + static) + NET_STRING="$NET_STRING,ip6=$IPV6_ADDR" + [ -n "$IPV6_GATE" ] && NET_STRING="$NET_STRING,gw6=$IPV6_GATE" + ;; + none) ;; + esac - if [ "$CT_TYPE" == "1" ]; then - FEATURES="keyctl=1,nesting=1" - else - FEATURES="nesting=1" - fi + if [ "$CT_TYPE" == "1" ]; then + FEATURES="keyctl=1,nesting=1" + else + FEATURES="nesting=1" + fi - if [ "$ENABLE_FUSE" == "yes" ]; then - FEATURES="$FEATURES,fuse=1" - fi + if [ "$ENABLE_FUSE" == "yes" ]; then + FEATURES="$FEATURES,fuse=1" + fi - #if [[ $DIAGNOSTICS == "yes" ]]; then - # post_to_api - #fi + #if [[ $DIAGNOSTICS == "yes" ]]; then + # post_to_api + #fi - TEMP_DIR=$(mktemp -d) - pushd "$TEMP_DIR" >/dev/null - if [ "$var_os" == "alpine" ]; then - export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-install.func)" - else - export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" - fi - export DIAGNOSTICS="$DIAGNOSTICS" - export RANDOM_UUID="$RANDOM_UUID" - export CACHER="$APT_CACHER" - export CACHER_IP="$APT_CACHER_IP" - export tz="$timezone" - #export DISABLEIPV6="$DISABLEIP6" - export APPLICATION="$APP" - export app="$NSAPP" - export PASSWORD="$PW" - export VERBOSE="$VERBOSE" - export SSH_ROOT="${SSH}" - export SSH_AUTHORIZED_KEY - export CTID="$CT_ID" - export CTTYPE="$CT_TYPE" - export ENABLE_FUSE="$ENABLE_FUSE" - export ENABLE_TUN="$ENABLE_TUN" - export PCT_OSTYPE="$var_os" - export PCT_OSVERSION="$var_version" - export PCT_DISK_SIZE="$DISK_SIZE" - export PCT_OPTIONS=" + TEMP_DIR=$(mktemp -d) + pushd "$TEMP_DIR" >/dev/null + if [ "$var_os" == "alpine" ]; then + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-install.func)" + else + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" + fi + export DIAGNOSTICS="$DIAGNOSTICS" + export RANDOM_UUID="$RANDOM_UUID" + export CACHER="$APT_CACHER" + export CACHER_IP="$APT_CACHER_IP" + export tz="$timezone" + #export DISABLEIPV6="$DISABLEIP6" + export APPLICATION="$APP" + export app="$NSAPP" + export PASSWORD="$PW" + export VERBOSE="$VERBOSE" + export SSH_ROOT="${SSH}" + export SSH_AUTHORIZED_KEY + export CTID="$CT_ID" + export CTTYPE="$CT_TYPE" + export ENABLE_FUSE="$ENABLE_FUSE" + export ENABLE_TUN="$ENABLE_TUN" + export PCT_OSTYPE="$var_os" + export PCT_OSVERSION="$var_version" + export PCT_DISK_SIZE="$DISK_SIZE" + export PCT_OPTIONS=" -features $FEATURES -hostname $HN -tags $TAGS @@ -1232,16 +1231,16 @@ build_container() { -unprivileged $CT_TYPE $PW " - bash -c "$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/create_lxc.sh)" - if [ $? -ne 0 ]; then - exit 200 - fi + bash -c "$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/create_lxc.sh)" + if [ $? -ne 0 ]; then + exit 200 + fi - LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" + LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" - # USB passthrough for privileged LXC (CT_TYPE=0) - if [ "$CT_TYPE" == "0" ]; then - cat <>"$LXC_CONFIG" + # USB passthrough for privileged LXC (CT_TYPE=0) + if [ "$CT_TYPE" == "0" ]; then + cat <>"$LXC_CONFIG" # USB passthrough lxc.cgroup2.devices.allow: a lxc.cap.drop: @@ -1253,185 +1252,191 @@ lxc.mount.entry: /dev/ttyUSB1 dev/ttyUSB1 none bind,optional,create= lxc.mount.entry: /dev/ttyACM0 dev/ttyACM0 none bind,optional,create=file lxc.mount.entry: /dev/ttyACM1 dev/ttyACM1 none bind,optional,create=file EOF - fi - - # VAAPI passthrough for privileged containers or known apps - VAAPI_APPS=( - "immich" - "Channels" - "Emby" - "ErsatzTV" - "Frigate" - "Jellyfin" - "Plex" - "Scrypted" - "Tdarr" - "Unmanic" - "Ollama" - "FileFlows" - "Open WebUI" - ) - - is_vaapi_app=false - for vaapi_app in "${VAAPI_APPS[@]}"; do - if [[ "$APP" == "$vaapi_app" ]]; then - is_vaapi_app=true - break - fi - done - - if ([ "$CT_TYPE" == "0" ] || [ "$is_vaapi_app" == "true" ]) && - ([[ -e /dev/dri/renderD128 ]] || [[ -e /dev/dri/card0 ]] || [[ -e /dev/fb0 ]]); then - - echo "" - msg_custom "⚙️ " "\e[96m" "Configuring VAAPI passthrough for LXC container" - if [ "$CT_TYPE" != "0" ]; then - msg_custom "⚠️ " "\e[33m" "Container is unprivileged – VAAPI passthrough may not work without additional host configuration (e.g., idmap)." - fi - msg_custom "ℹ️ " "\e[96m" "VAAPI enables GPU hardware acceleration (e.g., for video transcoding in Jellyfin or Plex)." - echo "" - read -rp "➤ Automatically mount all available VAAPI devices? [Y/n]: " VAAPI_ALL - - if [[ "$VAAPI_ALL" =~ ^[Yy]$|^$ ]]; then - if [ "$CT_TYPE" == "0" ]; then - # PRV Container → alles zulässig - [[ -e /dev/dri/renderD128 ]] && { - echo "lxc.cgroup2.devices.allow: c 226:128 rwm" >>"$LXC_CONFIG" - echo "lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file" >>"$LXC_CONFIG" - } - [[ -e /dev/dri/card0 ]] && { - echo "lxc.cgroup2.devices.allow: c 226:0 rwm" >>"$LXC_CONFIG" - echo "lxc.mount.entry: /dev/dri/card0 dev/dri/card0 none bind,optional,create=file" >>"$LXC_CONFIG" - } - [[ -e /dev/fb0 ]] && { - echo "lxc.cgroup2.devices.allow: c 29:0 rwm" >>"$LXC_CONFIG" - echo "lxc.mount.entry: /dev/fb0 dev/fb0 none bind,optional,create=file" >>"$LXC_CONFIG" - } - [[ -d /dev/dri ]] && { - echo "lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" - } - else - # UNPRV Container → nur devX für UI - [[ -e /dev/dri/card0 ]] && echo "dev0: /dev/dri/card0,gid=44" >>"$LXC_CONFIG" - [[ -e /dev/dri/card1 ]] && echo "dev0: /dev/dri/card1,gid=44" >>"$LXC_CONFIG" - [[ -e /dev/dri/renderD128 ]] && echo "dev1: /dev/dri/renderD128,gid=104" >>"$LXC_CONFIG" - fi fi - fi - if [ "$CT_TYPE" == "1" ] && [ "$is_vaapi_app" == "true" ]; then - if [[ -e /dev/dri/card0 ]]; then - echo "dev0: /dev/dri/card0,gid=44" >>"$LXC_CONFIG" - elif [[ -e /dev/dri/card1 ]]; then - echo "dev0: /dev/dri/card1,gid=44" >>"$LXC_CONFIG" - fi - if [[ -e /dev/dri/renderD128 ]]; then - echo "dev1: /dev/dri/renderD128,gid=104" >>"$LXC_CONFIG" - fi - fi + # VAAPI passthrough for privileged containers or known apps + VAAPI_APPS=( + "immich" + "Channels" + "Emby" + "ErsatzTV" + "Frigate" + "Jellyfin" + "Plex" + "Scrypted" + "Tdarr" + "Unmanic" + "Ollama" + "FileFlows" + "Open WebUI" + ) - # TUN device passthrough - if [ "$ENABLE_TUN" == "yes" ]; then - cat <>"$LXC_CONFIG" + is_vaapi_app=false + for vaapi_app in "${VAAPI_APPS[@]}"; do + if [[ "$APP" == "$vaapi_app" ]]; then + is_vaapi_app=true + break + fi + done + + if ([ "$CT_TYPE" == "0" ] || [ "$is_vaapi_app" == "true" ]) && + ([[ -e /dev/dri/renderD128 ]] || [[ -e /dev/dri/card0 ]] || [[ -e /dev/fb0 ]]); then + + echo "" + msg_custom "⚙️ " "\e[96m" "Configuring VAAPI passthrough for LXC container" + if [ "$CT_TYPE" != "0" ]; then + msg_custom "⚠️ " "\e[33m" "Container is unprivileged – VAAPI passthrough may not work without additional host configuration (e.g., idmap)." + fi + msg_custom "ℹ️ " "\e[96m" "VAAPI enables GPU hardware acceleration (e.g., for video transcoding in Jellyfin or Plex)." + echo "" + read -rp "➤ Automatically mount all available VAAPI devices? [Y/n]: " VAAPI_ALL + + if [[ "$VAAPI_ALL" =~ ^[Yy]$|^$ ]]; then + if [ "$CT_TYPE" == "0" ]; then + # PRV Container → alles zulässig + [[ -e /dev/dri/renderD128 ]] && { + echo "lxc.cgroup2.devices.allow: c 226:128 rwm" >>"$LXC_CONFIG" + echo "lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file" >>"$LXC_CONFIG" + } + [[ -e /dev/dri/card0 ]] && { + echo "lxc.cgroup2.devices.allow: c 226:0 rwm" >>"$LXC_CONFIG" + echo "lxc.mount.entry: /dev/dri/card0 dev/dri/card0 none bind,optional,create=file" >>"$LXC_CONFIG" + } + [[ -e /dev/fb0 ]] && { + echo "lxc.cgroup2.devices.allow: c 29:0 rwm" >>"$LXC_CONFIG" + echo "lxc.mount.entry: /dev/fb0 dev/fb0 none bind,optional,create=file" >>"$LXC_CONFIG" + } + [[ -d /dev/dri ]] && { + echo "lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" + } + else + # UNPRV Container → nur devX für UI + [[ -e /dev/dri/card0 ]] && echo "dev0: /dev/dri/card0,gid=44" >>"$LXC_CONFIG" + [[ -e /dev/dri/card1 ]] && echo "dev0: /dev/dri/card1,gid=44" >>"$LXC_CONFIG" + [[ -e /dev/dri/renderD128 ]] && echo "dev1: /dev/dri/renderD128,gid=104" >>"$LXC_CONFIG" + fi + fi + + fi + if [ "$CT_TYPE" == "1" ] && [ "$is_vaapi_app" == "true" ]; then + if [[ -e /dev/dri/card0 ]]; then + echo "dev0: /dev/dri/card0,gid=44" >>"$LXC_CONFIG" + elif [[ -e /dev/dri/card1 ]]; then + echo "dev0: /dev/dri/card1,gid=44" >>"$LXC_CONFIG" + fi + if [[ -e /dev/dri/renderD128 ]]; then + echo "dev1: /dev/dri/renderD128,gid=104" >>"$LXC_CONFIG" + fi + fi + + # TUN device passthrough + if [ "$ENABLE_TUN" == "yes" ]; then + cat <>"$LXC_CONFIG" lxc.cgroup2.devices.allow: c 10:200 rwm lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file EOF - fi + fi - # This starts the container and executes -install.sh - msg_info "Starting LXC Container" - pct start "$CTID" + # This starts the container and executes -install.sh + msg_info "Starting LXC Container" + pct start "$CTID" - # wait for status 'running' + # wait for status 'running' for i in {1..10}; do - if pct status "$CTID" | grep -q "status: running"; then - msg_ok "Startted LXC Container" - break - fi - sleep 1 - if [ "$i" -eq 10 ]; then - msg_error "LXC Container did not reach running state" - exit 1 - fi - done - - msg_info "Waiting for network in LXC container" - for i in {1..10}; do - if pct exec "$CTID" -- ping -c1 -W1 deb.debian.org >/dev/null 2>&1; then - msg_ok "Network in LXC is reachable" - break - fi - if [ "$i" -lt 10 ]; then - msg_warn "No network yet in LXC (try $i/10) – waiting..." - sleep 3 - else - msg_error "No network in LXC after waiting." - read -r -p "Set fallback DNS (1.1.1.1/8.8.8.8)? [y/N]: " choice - case "$choice" in - [yY]*) - pct set "$CTID" --nameserver 1.1.1.1 - pct set "$CTID" --nameserver 8.8.8.8 - if pct exec "$CTID" -- ping -c1 -W1 deb.debian.org >/dev/null 2>&1; then - msg_ok "Network reachable after DNS fallback" - else - msg_error "Still no network/DNS in LXC! Aborting customization." + if pct status "$CTID" | grep -q "status: running"; then + msg_ok "Startted LXC Container" + break + fi + sleep 1 + if [ "$i" -eq 10 ]; then + msg_error "LXC Container did not reach running state" exit 1 - fi - ;; - *) - msg_error "Aborted by user – no DNS fallback set." - exit 1 - ;; - esac - fi - done + fi + done - msg_info "Customizing LXC Container" - if [ "$var_os" == "alpine" ]; then - sleep 3 - pct exec "$CTID" -- /bin/sh -c 'cat </etc/apk/repositories + if [ "$var_os" == "alpine" ]; then + PING_TARGET="alpinelinux.org" + else + PING_TARGET="deb.debian.org" + fi + + msg_info "Waiting for network in LXC container" + for i in {1..10}; do + if pct exec "$CTID" -- ping -c1 -W1 "$PING_TARGET" >/dev/null 2>&1; then + msg_ok "Network in LXC is reachable" + break + fi + if [ "$i" -lt 10 ]; then + msg_warn "No network yet in LXC (try $i/10) – waiting..." + sleep 3 + else + msg_error "No network in LXC after waiting." + read -r -p "Set fallback DNS (1.1.1.1/8.8.8.8)? [y/N]: " choice + case "$choice" in + [yY]*) + pct set "$CTID" --nameserver 1.1.1.1 + pct set "$CTID" --nameserver 8.8.8.8 + if pct exec "$CTID" -- ping -c1 -W1 "$PING_TARGET" >/dev/null 2>&1; then + msg_ok "Network reachable after DNS fallback" + else + msg_error "Still no network/DNS in LXC! Aborting customization." + exit 1 + fi + ;; + *) + msg_error "Aborted by user – no DNS fallback set." + exit 1 + ;; + esac + fi + done + + msg_info "Customizing LXC Container" + if [ "$var_os" == "alpine" ]; then + sleep 3 + pct exec "$CTID" -- /bin/sh -c 'cat </etc/apk/repositories http://dl-cdn.alpinelinux.org/alpine/latest-stable/main http://dl-cdn.alpinelinux.org/alpine/latest-stable/community EOF' - pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses >/dev/null" - else - sleep 3 + pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses >/dev/null" + else + sleep 3 - pct exec "$CTID" -- bash -c "sed -i '/$LANG/ s/^# //' /etc/locale.gen" - pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \ + pct exec "$CTID" -- bash -c "sed -i '/$LANG/ s/^# //' /etc/locale.gen" + pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \ echo LANG=\$locale_line >/etc/default/locale && \ locale-gen >/dev/null && \ export LANG=\$locale_line" - if [[ -z "${tz:-}" ]]; then - tz=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "Etc/UTC") + if [[ -z "${tz:-}" ]]; then + tz=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "Etc/UTC") + fi + + if pct exec "$CTID" -- test -e "/usr/share/zoneinfo/$tz"; then + pct exec "$CTID" -- bash -c "tz='$tz'; echo \"\$tz\" >/etc/timezone && ln -sf \"/usr/share/zoneinfo/\$tz\" /etc/localtime" + else + msg_warn "Skipping timezone setup – zone '$tz' not found in container" + fi + + pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y sudo curl mc gnupg2 >/dev/null" || { + msg_error "apt-get base packages installation failed" + exit 1 + } fi + msg_ok "Customized LXC Container" - if pct exec "$CTID" -- test -e "/usr/share/zoneinfo/$tz"; then - pct exec "$CTID" -- bash -c "tz='$tz'; echo \"\$tz\" >/etc/timezone && ln -sf \"/usr/share/zoneinfo/\$tz\" /etc/localtime" - else - msg_warn "Skipping timezone setup – zone '$tz' not found in container" + if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/"$var_install".sh)"; then + return fi - - pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y sudo curl mc gnupg2 >/dev/null" || { - msg_error "apt-get base packages installation failed" - exit 1 - } - fi - msg_ok "Customized LXC Container" - - if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/"$var_install".sh)"; then - return - fi } # This function sets the description of the container. description() { - IP=$(pct exec "$CTID" ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) + IP=$(pct exec "$CTID" ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) - # Generate LXC Description - DESCRIPTION=$( - cat < Logo @@ -1459,41 +1464,41 @@ description() { EOF - ) + ) - # Set Description in LXC - pct set "$CTID" -description "$DESCRIPTION" + # Set Description in LXC + pct set "$CTID" -description "$DESCRIPTION" - if [[ -f /etc/systemd/system/ping-instances.service ]]; then - systemctl start ping-instances.service - fi + if [[ -f /etc/systemd/system/ping-instances.service ]]; then + systemctl start ping-instances.service + fi - post_update_to_api "done" "none" + post_update_to_api "done" "none" } api_exit_script() { - exit_code=$? - if [ $exit_code -ne 0 ]; then - case $exit_code in - 100) post_update_to_api "failed" "100: Unexpected error in create_lxc.sh" ;; - 101) post_update_to_api "failed" "101: No network connection detected in create_lxc.sh" ;; - 200) post_update_to_api "failed" "200: LXC creation failed in create_lxc.sh" ;; - 201) post_update_to_api "failed" "201: Invalid Storage class in create_lxc.sh" ;; - 202) post_update_to_api "failed" "202: User aborted menu in create_lxc.sh" ;; - 203) post_update_to_api "failed" "203: CTID not set in create_lxc.sh" ;; - 204) post_update_to_api "failed" "204: PCT_OSTYPE not set in create_lxc.sh" ;; - 205) post_update_to_api "failed" "205: CTID cannot be less than 100 in create_lxc.sh" ;; - 206) post_update_to_api "failed" "206: CTID already in use in create_lxc.sh" ;; - 207) post_update_to_api "failed" "207: Template not found in create_lxc.sh" ;; - 208) post_update_to_api "failed" "208: Error downloading template in create_lxc.sh" ;; - 209) post_update_to_api "failed" "209: Container creation failed, but template is intact in create_lxc.sh" ;; - *) post_update_to_api "failed" "Unknown error, exit code: $exit_code in create_lxc.sh" ;; - esac - fi + exit_code=$? + if [ $exit_code -ne 0 ]; then + case $exit_code in + 100) post_update_to_api "failed" "100: Unexpected error in create_lxc.sh" ;; + 101) post_update_to_api "failed" "101: No network connection detected in create_lxc.sh" ;; + 200) post_update_to_api "failed" "200: LXC creation failed in create_lxc.sh" ;; + 201) post_update_to_api "failed" "201: Invalid Storage class in create_lxc.sh" ;; + 202) post_update_to_api "failed" "202: User aborted menu in create_lxc.sh" ;; + 203) post_update_to_api "failed" "203: CTID not set in create_lxc.sh" ;; + 204) post_update_to_api "failed" "204: PCT_OSTYPE not set in create_lxc.sh" ;; + 205) post_update_to_api "failed" "205: CTID cannot be less than 100 in create_lxc.sh" ;; + 206) post_update_to_api "failed" "206: CTID already in use in create_lxc.sh" ;; + 207) post_update_to_api "failed" "207: Template not found in create_lxc.sh" ;; + 208) post_update_to_api "failed" "208: Error downloading template in create_lxc.sh" ;; + 209) post_update_to_api "failed" "209: Container creation failed, but template is intact in create_lxc.sh" ;; + *) post_update_to_api "failed" "Unknown error, exit code: $exit_code in create_lxc.sh" ;; + esac + fi } if command -v pveversion >/dev/null 2>&1; then - trap 'api_exit_script' EXIT + trap 'api_exit_script' EXIT fi trap 'post_update_to_api "failed" "$BASH_COMMAND"' ERR trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT From 7b814adc983506a658c494807830ee43d2615557 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 22 Jul 2025 09:42:39 +0200 Subject: [PATCH 0113/1733] Update linkstack.sh --- ct/linkstack.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ct/linkstack.sh b/ct/linkstack.sh index 731033bf9..fb421c8e7 100644 --- a/ct/linkstack.sh +++ b/ct/linkstack.sh @@ -24,7 +24,7 @@ function update_script() { check_container_storage check_container_resources - if [[ ! -f /.linkstack ]]; then + if [[ ! -f ~/.linkstack ]]; then msg_error "No ${APP} Installation Found!" exit fi @@ -62,7 +62,6 @@ function update_script() { exit } - start build_container description From ba97ccea75143704d6902c36ecc4e569feae1100 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 22 Jul 2025 09:44:40 +0200 Subject: [PATCH 0114/1733] spell issue --- ct/linkstack.sh | 2 +- install/linkstack-install.sh | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/ct/linkstack.sh b/ct/linkstack.sh index fb421c8e7..a1a63356a 100644 --- a/ct/linkstack.sh +++ b/ct/linkstack.sh @@ -49,7 +49,7 @@ function update_script() { msg_ok "Updated $APP to v${RELEASE}" msg_info "Starting $APP" - systemctl start linkstack + systemctl start apache2 msg_ok "Started $APP" msg_info "Cleaning Up" diff --git a/install/linkstack-install.sh b/install/linkstack-install.sh index 7e87a8a5c..95105bbf8 100644 --- a/install/linkstack-install.sh +++ b/install/linkstack-install.sh @@ -13,7 +13,6 @@ setting_up_container network_check update_os - PHP_VERSION="8.3" PHP_MODULE="sqlite3" PHP_APACHE="YES" setup_php fetch_and_deploy_gh_release "linkstack" "linkstackorg/linkstack" "prebuild" "latest" "/var/www/html/linkstack" "linkstack.zip" @@ -22,7 +21,7 @@ $STD a2enmod rewrite chown -R www-data:www-data /var/www/html/linkstack chmod -R 755 /var/www/html/linkstack -cat < /etc/apache2/sites-available/linkstack.conf +cat </etc/apache2/sites-available/linkstack.conf ServerAdmin webmaster@localhost DocumentRoot /var/www/html/linkstack From 8c4a7a80ec4271457828231d8f0f84a629a3998c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 22 Jul 2025 10:01:03 +0200 Subject: [PATCH 0115/1733] Update tools.func --- misc/tools.func | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 92a5bac55..9c067e64c 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -472,10 +472,12 @@ function setup_php() { $STD systemctl disable php${CURRENT_PHP}-fpm || true fi - $STD apt-get install -y $MODULE_LIST - msg_ok "Setup PHP $PHP_VERSION" - if [[ "$PHP_APACHE" == "YES" ]]; then + if [[ -f /etc/apache2/mods-enabled/php${PHP_VERSION}.load ]]; then + $STD a2enmod php${PHP_VERSION} + else + $STD a2enmod php${PHP_VERSION} + fi $STD systemctl restart apache2 || true fi @@ -499,6 +501,17 @@ function setup_php() { $STD msg_ok "Patched $ini" fi done + + # patch Apache configuration if needed + for mod in $(ls /etc/apache2/mods-enabled/ | grep -E '^php[0-9]\.[0-9]\.conf$' | sed 's/\.conf//'); do + if [[ "$mod" != "php${PHP_VERSION}" ]]; then + $STD a2dismod "$mod" || true + fi + done + $STD a2enmod "php${PHP_VERSION}" + if [[ "$PHP_APACHE" == "YES" ]]; then + $STD systemctl restart apache2 || true + fi } # ------------------------------------------------------------------------------ From 7cf8f67f6e9c0d4cb3ee489fe2e319d319508f86 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 22 Jul 2025 10:11:08 +0200 Subject: [PATCH 0116/1733] Update linkstack.sh --- ct/linkstack.sh | 33 ++------------------------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/ct/linkstack.sh b/ct/linkstack.sh index a1a63356a..e745545fc 100644 --- a/ct/linkstack.sh +++ b/ct/linkstack.sh @@ -28,37 +28,8 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - - RELEASE=$(curl -fsSL https://api.github.com/repos/linkstackorg/linkstack/releases/latest | jq -r '.tag_name | ltrimstr("v")') - if [[ "${RELEASE}" != "$(cat ~/.linkstack 2>/dev/null)" ]] || [[ ! -f ~/.linkstack ]]; then - msg_info "Stopping $APP" - systemctl stop apache2 - msg_ok "Stopped $APP" - - msg_info "Creating Backup" - BACKUP_FILE="/opt/linkstack_backup_$(date +%F).tar.gz" - $STD tar -czf "$BACKUP_FILE" /var/www/html/linkstack - msg_ok "Backup Created" - - PHP_VERSION="8.3" PHP_MODULE="sqlite3" PHP_APACHE="YES" setup_php - fetch_and_deploy_gh_release "linkstack" "linkstackorg/linkstack" "prebuild" "latest" "/var/www/html/linkstack" "linkstack.zip" - - msg_info "Updating $APP to v${RELEASE}" - chown -R www-data:www-data /var/www/html/linkstack - chmod -R 755 /var/www/html/linkstack - msg_ok "Updated $APP to v${RELEASE}" - - msg_info "Starting $APP" - systemctl start apache2 - msg_ok "Started $APP" - - msg_info "Cleaning Up" - rm -rf "$BACKUP_FILE" - msg_ok "Cleanup Completed" - msg_ok "Update Successful" - else - msg_ok "No update required. ${APP} is already at v${RELEASE}" - fi + PHP_VERSION="8.3" PHP_MODULE="sqlite3" PHP_APACHE="YES" setup_php + msg_error "Adguard Home should be updated via the user interface." exit } From 18a99c9aa730d3849b34177bf664366d379c9bc3 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 22 Jul 2025 10:26:18 +0200 Subject: [PATCH 0117/1733] Update tools.func --- misc/tools.func | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 9c067e64c..89d17102a 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -393,7 +393,6 @@ function setup_mysql() { # PHP_POST_MAX_SIZE - (default: 128M) # PHP_MAX_EXECUTION_TIME - (default: 300) # ------------------------------------------------------------------------------ - function setup_php() { local PHP_VERSION="${PHP_VERSION:-8.4}" local PHP_MODULE="${PHP_MODULE:-}" @@ -450,7 +449,11 @@ function setup_php() { done IFS=',' read -ra MODULES <<<"$COMBINED_MODULES" for mod in "${MODULES[@]}"; do - MODULE_LIST+=" php${PHP_VERSION}-${mod}" + if apt-cache show "php${PHP_VERSION}-${mod}" >/dev/null 2>&1; then + MODULE_LIST+=" php${PHP_VERSION}-${mod}" + else + msg_warn "PHP-Module ${mod} for PHP ${PHP_VERSION} not found – skipping" + fi done if [[ "$PHP_FPM" == "YES" ]]; then @@ -463,27 +466,31 @@ function setup_php() { if [[ "$PHP_APACHE" == "YES" ]] && [[ -n "$CURRENT_PHP" ]]; then if [[ -f /etc/apache2/mods-enabled/php${CURRENT_PHP}.load ]]; then - $STD a2dismod php${CURRENT_PHP} || true + $STD a2dismod php"${CURRENT_PHP}" || true fi fi if [[ "$PHP_FPM" == "YES" ]] && [[ -n "$CURRENT_PHP" ]]; then - $STD systemctl stop php${CURRENT_PHP}-fpm || true - $STD systemctl disable php${CURRENT_PHP}-fpm || true + $STD systemctl stop php"${CURRENT_PHP}"-fpm || true + $STD systemctl disable php"${CURRENT_PHP}"-fpm || true fi if [[ "$PHP_APACHE" == "YES" ]]; then if [[ -f /etc/apache2/mods-enabled/php${PHP_VERSION}.load ]]; then - $STD a2enmod php${PHP_VERSION} + $STD a2enmod php"${PHP_VERSION}" else - $STD a2enmod php${PHP_VERSION} + $STD a2enmod php"${PHP_VERSION}" fi $STD systemctl restart apache2 || true fi if [[ "$PHP_FPM" == "YES" ]]; then - $STD systemctl enable php${PHP_VERSION}-fpm - $STD systemctl restart php${PHP_VERSION}-fpm + if systemctl list-unit-files | grep -q "php${PHP_VERSION}-fpm.service"; then + $STD systemctl enable php${PHP_VERSION}-fpm + $STD systemctl restart php${PHP_VERSION}-fpm + else + msg_warn "FPM requested but service php${PHP_VERSION}-fpm not found" + fi fi # Patch all relevant php.ini files @@ -503,13 +510,13 @@ function setup_php() { done # patch Apache configuration if needed - for mod in $(ls /etc/apache2/mods-enabled/ | grep -E '^php[0-9]\.[0-9]\.conf$' | sed 's/\.conf//'); do - if [[ "$mod" != "php${PHP_VERSION}" ]]; then - $STD a2dismod "$mod" || true - fi - done - $STD a2enmod "php${PHP_VERSION}" if [[ "$PHP_APACHE" == "YES" ]]; then + for mod in $(ls /etc/apache2/mods-enabled/ 2>/dev/null | grep -E '^php[0-9]\.[0-9]\.conf$' | sed 's/\.conf//'); do + if [[ "$mod" != "php${PHP_VERSION}" ]]; then + $STD a2dismod "$mod" || true + fi + done + $STD a2enmod "php${PHP_VERSION}" $STD systemctl restart apache2 || true fi } From 001e3187176dc36719a09c30e91985fdbc571190 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 22 Jul 2025 10:26:31 +0200 Subject: [PATCH 0118/1733] Update linkstack-install.sh --- install/linkstack-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/linkstack-install.sh b/install/linkstack-install.sh index 95105bbf8..81fb19bb1 100644 --- a/install/linkstack-install.sh +++ b/install/linkstack-install.sh @@ -13,7 +13,7 @@ setting_up_container network_check update_os -PHP_VERSION="8.3" PHP_MODULE="sqlite3" PHP_APACHE="YES" setup_php +PHP_VERSION="8.3" PHP_MODULE="sqlite3,mysql" PHP_APACHE="YES" setup_php fetch_and_deploy_gh_release "linkstack" "linkstackorg/linkstack" "prebuild" "latest" "/var/www/html/linkstack" "linkstack.zip" msg_info "Configuring LinkStack" From d37fcf79e70ba024bd204d9d61257ec07f22c80d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 22 Jul 2025 10:30:07 +0200 Subject: [PATCH 0119/1733] Update build.func --- misc/build.func | 2114 +++++++++++++++++++++++------------------------ 1 file changed, 1057 insertions(+), 1057 deletions(-) diff --git a/misc/build.func b/misc/build.func index 588051550..802654589 100644 --- a/misc/build.func +++ b/misc/build.func @@ -6,210 +6,210 @@ # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE variables() { - NSAPP=$(echo "${APP,,}" | tr -d ' ') # This function sets the NSAPP variable by converting the value of the APP variable to lowercase and removing any spaces. - var_install="${NSAPP}-install" # sets the var_install variable by appending "-install" to the value of NSAPP. - INTEGER='^[0-9]+([.][0-9]+)?$' # it defines the INTEGER regular expression pattern. - PVEHOST_NAME=$(hostname) # gets the Proxmox Hostname and sets it to Uppercase - DIAGNOSTICS="yes" # sets the DIAGNOSTICS variable to "yes", used for the API call. - METHOD="default" # sets the METHOD variable to "default", used for the API call. - RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" # generates a random UUID and sets it to the RANDOM_UUID variable. - CT_TYPE=${var_unprivileged:-$CT_TYPE} + NSAPP=$(echo "${APP,,}" | tr -d ' ') # This function sets the NSAPP variable by converting the value of the APP variable to lowercase and removing any spaces. + var_install="${NSAPP}-install" # sets the var_install variable by appending "-install" to the value of NSAPP. + INTEGER='^[0-9]+([.][0-9]+)?$' # it defines the INTEGER regular expression pattern. + PVEHOST_NAME=$(hostname) # gets the Proxmox Hostname and sets it to Uppercase + DIAGNOSTICS="yes" # sets the DIAGNOSTICS variable to "yes", used for the API call. + METHOD="default" # sets the METHOD variable to "default", used for the API call. + RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" # generates a random UUID and sets it to the RANDOM_UUID variable. + CT_TYPE=${var_unprivileged:-$CT_TYPE} } source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) if command -v curl >/dev/null 2>&1; then - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) - load_functions - #echo "(build.func) Loaded core.func via curl" + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + load_functions + #echo "(build.func) Loaded core.func via curl" elif command -v wget >/dev/null 2>&1; then - source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) - load_functions - #echo "(build.func) Loaded core.func via wget" + source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + load_functions + #echo "(build.func) Loaded core.func via wget" fi # This function enables error handling in the script by setting options and defining a trap for the ERR signal. catch_errors() { - set -Eeo pipefail - # if [ -n "$BASH_VERSION" ] && command -v shopt >/dev/null 2>&1; then - # shopt -s errtrace - # fi - trap 'error_handler $LINENO "$BASH_COMMAND"' ERR + set -Eeo pipefail + # if [ -n "$BASH_VERSION" ] && command -v shopt >/dev/null 2>&1; then + # shopt -s errtrace + # fi + trap 'error_handler $LINENO "$BASH_COMMAND"' ERR } # This function is called when an error occurs. It receives the exit code, line number, and command that caused the error, and displays an error message. error_handler() { - local exit_code="$?" - local line_number="$1" - local command="$2" - printf "\e[?25h" - local error_message="[ERROR] in line $line_number: exit code $exit_code: while executing command $command" - post_update_to_api "failed" "$command" - echo -e "\n$error_message\n" - exit "$exit_code" + local exit_code="$?" + local line_number="$1" + local command="$2" + printf "\e[?25h" + local error_message="[ERROR] in line $line_number: exit code $exit_code: while executing command $command" + post_update_to_api "failed" "$command" + echo -e "\n$error_message\n" + exit "$exit_code" - if [[ -n "$CT_ID" ]]; then - read -p "Remove this Container? " prompt - if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then - pct stop "$CT_ID" &>/dev/null - pct destroy "$CT_ID" &>/dev/null - msg_ok "Removed this Container" - fi + if [[ -n "$CT_ID" ]]; then + read -p "Remove this Container? " prompt + if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then + pct stop "$CT_ID" &>/dev/null + pct destroy "$CT_ID" &>/dev/null + msg_ok "Removed this Container" fi + fi } # Check if the shell is using bash shell_check() { - if [[ "$(basename "$SHELL")" != "bash" ]]; then - clear - msg_error "Your default shell is currently not set to Bash. To use these scripts, please switch to the Bash shell." - echo -e "\nExiting..." - sleep 2 - exit - fi + if [[ "$(basename "$SHELL")" != "bash" ]]; then + clear + msg_error "Your default shell is currently not set to Bash. To use these scripts, please switch to the Bash shell." + echo -e "\nExiting..." + sleep 2 + exit + fi } # Run as root only root_check() { - if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then - clear - msg_error "Please run this script as root." - echo -e "\nExiting..." - sleep 2 - exit - fi + if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then + clear + msg_error "Please run this script as root." + echo -e "\nExiting..." + sleep 2 + exit + fi } # This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported. pve_check() { - local PVE_VER - PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" + local PVE_VER + PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" - # 8 Version Check - if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then - local MINOR="${BASH_REMATCH[1]}" - if ((MINOR < 1 || MINOR > 4)); then - msg_error "This version of Proxmox Virtual Environment is not supported" - echo -e "Requires Proxmox Virtual Environment Version 8.1 – 8.4" - echo -e "Exiting..." - sleep 2 - exit 1 - fi - return 0 + # 8 Version Check + if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then + local MINOR="${BASH_REMATCH[1]}" + if ((MINOR < 1 || MINOR > 4)); then + msg_error "This version of Proxmox Virtual Environment is not supported" + echo -e "Requires Proxmox Virtual Environment Version 8.1 – 8.4" + echo -e "Exiting..." + sleep 2 + exit 1 fi + return 0 + fi - # 9 Beta Version Check - if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then - msg_custom "Detected Proxmox Virtual Environment $PVE_VER – Beta state, use with caution!" - return 0 - fi + # 9 Beta Version Check + if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then + msg_custom "Detected Proxmox Virtual Environment $PVE_VER – Beta state, use with caution!" + return 0 + fi - # All others (unsupported versions) - msg_error "This version of Proxmox Virtual Environment is not supported" - echo -e "Requires Proxmox Virtual Environment Version 8.1 – 8.4 or 9.x (Beta)" - echo -e "Exiting..." - sleep 2 - exit 1 + # All others (unsupported versions) + msg_error "This version of Proxmox Virtual Environment is not supported" + echo -e "Requires Proxmox Virtual Environment Version 8.1 – 8.4 or 9.x (Beta)" + echo -e "Exiting..." + sleep 2 + exit 1 } # When a node is running tens of containers, it's possible to exceed the kernel's cryptographic key storage allocations. # These are tuneable, so verify if the currently deployment is approaching the limits, advise the user on how to tune the limits, and exit the script. # https://cleveruptime.com/docs/files/proc-key-users | https://docs.kernel.org/security/keys/core.html maxkeys_check() { - # Read kernel parameters - per_user_maxkeys=$(cat /proc/sys/kernel/keys/maxkeys 2>/dev/null || echo 0) - per_user_maxbytes=$(cat /proc/sys/kernel/keys/maxbytes 2>/dev/null || echo 0) + # Read kernel parameters + per_user_maxkeys=$(cat /proc/sys/kernel/keys/maxkeys 2>/dev/null || echo 0) + per_user_maxbytes=$(cat /proc/sys/kernel/keys/maxbytes 2>/dev/null || echo 0) - # Exit if kernel parameters are unavailable - if [[ "$per_user_maxkeys" -eq 0 || "$per_user_maxbytes" -eq 0 ]]; then - echo -e "${CROSS}${RD} Error: Unable to read kernel parameters. Ensure proper permissions.${CL}" - exit 1 - fi + # Exit if kernel parameters are unavailable + if [[ "$per_user_maxkeys" -eq 0 || "$per_user_maxbytes" -eq 0 ]]; then + echo -e "${CROSS}${RD} Error: Unable to read kernel parameters. Ensure proper permissions.${CL}" + exit 1 + fi - # Fetch key usage for user ID 100000 (typical for containers) - used_lxc_keys=$(awk '/100000:/ {print $2}' /proc/key-users 2>/dev/null || echo 0) - used_lxc_bytes=$(awk '/100000:/ {split($5, a, "/"); print a[1]}' /proc/key-users 2>/dev/null || echo 0) + # Fetch key usage for user ID 100000 (typical for containers) + used_lxc_keys=$(awk '/100000:/ {print $2}' /proc/key-users 2>/dev/null || echo 0) + used_lxc_bytes=$(awk '/100000:/ {split($5, a, "/"); print a[1]}' /proc/key-users 2>/dev/null || echo 0) - # Calculate thresholds and suggested new limits - threshold_keys=$((per_user_maxkeys - 100)) - threshold_bytes=$((per_user_maxbytes - 1000)) - new_limit_keys=$((per_user_maxkeys * 2)) - new_limit_bytes=$((per_user_maxbytes * 2)) + # Calculate thresholds and suggested new limits + threshold_keys=$((per_user_maxkeys - 100)) + threshold_bytes=$((per_user_maxbytes - 1000)) + new_limit_keys=$((per_user_maxkeys * 2)) + new_limit_bytes=$((per_user_maxbytes * 2)) - # Check if key or byte usage is near limits - failure=0 - if [[ "$used_lxc_keys" -gt "$threshold_keys" ]]; then - echo -e "${CROSS}${RD} Warning: Key usage is near the limit (${used_lxc_keys}/${per_user_maxkeys}).${CL}" - echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxkeys=${new_limit_keys}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." - failure=1 - fi - if [[ "$used_lxc_bytes" -gt "$threshold_bytes" ]]; then - echo -e "${CROSS}${RD} Warning: Key byte usage is near the limit (${used_lxc_bytes}/${per_user_maxbytes}).${CL}" - echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxbytes=${new_limit_bytes}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." - failure=1 - fi + # Check if key or byte usage is near limits + failure=0 + if [[ "$used_lxc_keys" -gt "$threshold_keys" ]]; then + echo -e "${CROSS}${RD} Warning: Key usage is near the limit (${used_lxc_keys}/${per_user_maxkeys}).${CL}" + echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxkeys=${new_limit_keys}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." + failure=1 + fi + if [[ "$used_lxc_bytes" -gt "$threshold_bytes" ]]; then + echo -e "${CROSS}${RD} Warning: Key byte usage is near the limit (${used_lxc_bytes}/${per_user_maxbytes}).${CL}" + echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxbytes=${new_limit_bytes}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." + failure=1 + fi - # Provide next steps if issues are detected - if [[ "$failure" -eq 1 ]]; then - echo -e "${INFO} To apply changes, run: ${BOLD}service procps force-reload${CL}" - exit 1 - fi + # Provide next steps if issues are detected + if [[ "$failure" -eq 1 ]]; then + echo -e "${INFO} To apply changes, run: ${BOLD}service procps force-reload${CL}" + exit 1 + fi - echo -e "${CM}${GN} All kernel key limits are within safe thresholds.${CL}" + echo -e "${CM}${GN} All kernel key limits are within safe thresholds.${CL}" } # This function checks the system architecture and exits if it's not "amd64". arch_check() { - if [ "$(dpkg --print-architecture)" != "amd64" ]; then - echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" - echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" - echo -e "Exiting..." - sleep 2 - exit - fi + if [ "$(dpkg --print-architecture)" != "amd64" ]; then + echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" + echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" + echo -e "Exiting..." + sleep 2 + exit + fi } # Function to get the current IP address based on the distribution get_current_ip() { - if [ -f /etc/os-release ]; then - # Check for Debian/Ubuntu (uses hostname -I) - if grep -qE 'ID=debian|ID=ubuntu' /etc/os-release; then - CURRENT_IP=$(hostname -I | awk '{print $1}') - # Check for Alpine (uses ip command) - elif grep -q 'ID=alpine' /etc/os-release; then - CURRENT_IP=$(ip -4 addr show eth0 | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) - else - CURRENT_IP="Unknown" - fi + if [ -f /etc/os-release ]; then + # Check for Debian/Ubuntu (uses hostname -I) + if grep -qE 'ID=debian|ID=ubuntu' /etc/os-release; then + CURRENT_IP=$(hostname -I | awk '{print $1}') + # Check for Alpine (uses ip command) + elif grep -q 'ID=alpine' /etc/os-release; then + CURRENT_IP=$(ip -4 addr show eth0 | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) + else + CURRENT_IP="Unknown" fi - echo "$CURRENT_IP" + fi + echo "$CURRENT_IP" } # Function to update the IP address in the MOTD file update_motd_ip() { - MOTD_FILE="/etc/motd" + MOTD_FILE="/etc/motd" - if [ -f "$MOTD_FILE" ]; then - # Remove existing IP Address lines to prevent duplication - sed -i '/IP Address:/d' "$MOTD_FILE" + if [ -f "$MOTD_FILE" ]; then + # Remove existing IP Address lines to prevent duplication + sed -i '/IP Address:/d' "$MOTD_FILE" - IP=$(get_current_ip) - # Add the new IP address - echo -e "${TAB}${NETWORK}${YW} IP Address: ${GN}${IP}${CL}" >>"$MOTD_FILE" - fi + IP=$(get_current_ip) + # Add the new IP address + echo -e "${TAB}${NETWORK}${YW} IP Address: ${GN}${IP}${CL}" >>"$MOTD_FILE" + fi } # This function checks if the script is running through SSH and prompts the user to confirm if they want to proceed or exit. ssh_check() { - if [ -n "${SSH_CLIENT:+x}" ]; then - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's advisable to utilize the Proxmox shell rather than SSH, as there may be potential complications with variable retrieval. Proceed using SSH?" 10 72; then - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox --title "Proceed using SSH" "You've chosen to proceed using SSH. If any issues arise, please run the script in the Proxmox shell before creating a repository issue." 10 72 - else - clear - echo "Exiting due to SSH usage. Please consider using the Proxmox shell." - exit - fi + if [ -n "${SSH_CLIENT:+x}" ]; then + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's advisable to utilize the Proxmox shell rather than SSH, as there may be potential complications with variable retrieval. Proceed using SSH?" 10 72; then + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox --title "Proceed using SSH" "You've chosen to proceed using SSH. If any issues arise, please run the script in the Proxmox shell before creating a repository issue." 10 72 + else + clear + echo "Exiting due to SSH usage. Please consider using the Proxmox shell." + exit fi + fi } # select_storage() { @@ -300,62 +300,62 @@ ssh_check() { # } base_settings() { - # Default Settings - CT_TYPE="1" - DISK_SIZE="4" - CORE_COUNT="1" - RAM_SIZE="1024" - VERBOSE="${1:-no}" - PW="" - CT_ID=$NEXTID - HN=$NSAPP - BRG="vmbr0" - NET="dhcp" - IPV6_METHOD="none" - IPV6_STATIC="" - GATE="" - APT_CACHER="" - APT_CACHER_IP="" - #DISABLEIP6="no" - MTU="" - SD="" - NS="" - MAC="" - VLAN="" - SSH="no" - SSH_AUTHORIZED_KEY="" - UDHCPC_FIX="" - TAGS="community-script;" - ENABLE_FUSE="${1:-no}" - ENABLE_TUN="${1:-no}" + # Default Settings + CT_TYPE="1" + DISK_SIZE="4" + CORE_COUNT="1" + RAM_SIZE="1024" + VERBOSE="${1:-no}" + PW="" + CT_ID=$NEXTID + HN=$NSAPP + BRG="vmbr0" + NET="dhcp" + IPV6_METHOD="none" + IPV6_STATIC="" + GATE="" + APT_CACHER="" + APT_CACHER_IP="" + #DISABLEIP6="no" + MTU="" + SD="" + NS="" + MAC="" + VLAN="" + SSH="no" + SSH_AUTHORIZED_KEY="" + UDHCPC_FIX="" + TAGS="community-script;" + ENABLE_FUSE="${1:-no}" + ENABLE_TUN="${1:-no}" - # Override default settings with variables from ct script - CT_TYPE=${var_unprivileged:-$CT_TYPE} - DISK_SIZE=${var_disk:-$DISK_SIZE} - CORE_COUNT=${var_cpu:-$CORE_COUNT} - RAM_SIZE=${var_ram:-$RAM_SIZE} - VERB=${var_verbose:-$VERBOSE} - TAGS="${TAGS}${var_tags:-}" - ENABLE_FUSE="${var_fuse:-$ENABLE_FUSE}" - ENABLE_TUN="${var_tun:-$ENABLE_TUN}" - APT_CACHER="${var_apt_cacher:-$APT_CACHER}" - APT_CACHER_IP="${var_apt_cacher_ip:-$APT_CACHER_IP}" + # Override default settings with variables from ct script + CT_TYPE=${var_unprivileged:-$CT_TYPE} + DISK_SIZE=${var_disk:-$DISK_SIZE} + CORE_COUNT=${var_cpu:-$CORE_COUNT} + RAM_SIZE=${var_ram:-$RAM_SIZE} + VERB=${var_verbose:-$VERBOSE} + TAGS="${TAGS}${var_tags:-}" + ENABLE_FUSE="${var_fuse:-$ENABLE_FUSE}" + ENABLE_TUN="${var_tun:-$ENABLE_TUN}" + APT_CACHER="${var_apt_cacher:-$APT_CACHER}" + APT_CACHER_IP="${var_apt_cacher_ip:-$APT_CACHER_IP}" - # Since these 2 are only defined outside of default_settings function, we add a temporary fallback. TODO: To align everything, we should add these as constant variables (e.g. OSTYPE and OSVERSION), but that would currently require updating the default_settings function for all existing scripts - if [ -z "$var_os" ]; then - var_os="debian" - fi - if [ -z "$var_version" ]; then - var_version="12" - fi + # Since these 2 are only defined outside of default_settings function, we add a temporary fallback. TODO: To align everything, we should add these as constant variables (e.g. OSTYPE and OSVERSION), but that would currently require updating the default_settings function for all existing scripts + if [ -z "$var_os" ]; then + var_os="debian" + fi + if [ -z "$var_version" ]; then + var_version="12" + fi } write_config() { - mkdir -p /opt/community-scripts - # This function writes the configuration to a file. - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "Write configfile" --yesno "Do you want to write the selections to a config file?" 10 60; then - FILEPATH="/opt/community-scripts/${NSAPP}.conf" - if [[ ! -f $FILEPATH ]]; then - cat <"$FILEPATH" + mkdir -p /opt/community-scripts + # This function writes the configuration to a file. + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "Write configfile" --yesno "Do you want to write the selections to a config file?" 10 60; then + FILEPATH="/opt/community-scripts/${NSAPP}.conf" + if [[ ! -f $FILEPATH ]]; then + cat <"$FILEPATH" # ${NSAPP} Configuration File # Generated on $(date) @@ -382,12 +382,12 @@ NET="${NET}" FUSE="${ENABLE_FUSE}" EOF - echo -e "${INFO}${BOLD}${GN}Writing configuration to ${FILEPATH}${CL}" - else - echo -e "${INFO}${BOLD}${RD}Configuration file already exists at ${FILEPATH}${CL}" - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "Overwrite configfile" --yesno "Do you want to overwrite the existing config file?" 10 60; then - rm -f "$FILEPATH" - cat <"$FILEPATH" + echo -e "${INFO}${BOLD}${GN}Writing configuration to ${FILEPATH}${CL}" + else + echo -e "${INFO}${BOLD}${RD}Configuration file already exists at ${FILEPATH}${CL}" + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "Overwrite configfile" --yesno "Do you want to overwrite the existing config file?" 10 60; then + rm -f "$FILEPATH" + cat <"$FILEPATH" # ${NSAPP} Configuration File # Generated on $(date) @@ -414,518 +414,518 @@ NET="${NET}" FUSE="${ENABLE_FUSE}" EOF - echo -e "${INFO}${BOLD}${GN}Writing configuration to ${FILEPATH}${CL}" - else - echo -e "${INFO}${BOLD}${RD}Configuration file not overwritten${CL}" - fi - fi + echo -e "${INFO}${BOLD}${GN}Writing configuration to ${FILEPATH}${CL}" + else + echo -e "${INFO}${BOLD}${RD}Configuration file not overwritten${CL}" + fi fi + fi } # This function displays the default values for various settings. echo_default() { - # Convert CT_TYPE to description - CT_TYPE_DESC="Unprivileged" - if [ "$CT_TYPE" -eq 0 ]; then - CT_TYPE_DESC="Privileged" - fi + # Convert CT_TYPE to description + CT_TYPE_DESC="Unprivileged" + if [ "$CT_TYPE" -eq 0 ]; then + CT_TYPE_DESC="Privileged" + fi - # Output the selected values with icons - echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}${CT_ID}${CL}" - echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os ($var_version)${CL}" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" - if [ "$VERB" == "yes" ]; then - echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}Enabled${CL}" - fi - echo -e "${CREATING}${BOLD}${BL}Creating a ${APP} LXC using the above default settings${CL}" - echo -e " " + # Output the selected values with icons + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}${CT_ID}${CL}" + echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os ($var_version)${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + if [ "$VERB" == "yes" ]; then + echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}Enabled${CL}" + fi + echo -e "${CREATING}${BOLD}${BL}Creating a ${APP} LXC using the above default settings${CL}" + echo -e " " } # This function is called when the user decides to exit the script. It clears the screen and displays an exit message. exit_script() { - clear - echo -e "\n${CROSS}${RD}User exited script${CL}\n" - exit + clear + echo -e "\n${CROSS}${RD}User exited script${CL}\n" + exit } # This function allows the user to configure advanced settings for the script. advanced_settings() { - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox --title "Here is an instructional tip:" "To make a selection, use the Spacebar." 8 58 - # Setting Default Tag for Advanced Settings - TAGS="community-script;${var_tags:-}" - CT_DEFAULT_TYPE="${CT_TYPE}" - CT_TYPE="" - while [ -z "$CT_TYPE" ]; do - if [ "$CT_DEFAULT_TYPE" == "1" ]; then - if CT_TYPE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ - "1" "Unprivileged" ON \ - "0" "Privileged" OFF \ - 3>&1 1>&2 2>&3); then - if [ -n "$CT_TYPE" ]; then - CT_TYPE_DESC="Unprivileged" - if [ "$CT_TYPE" -eq 0 ]; then - CT_TYPE_DESC="Privileged" - fi - echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os | ${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" - fi - else - exit_script - fi + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox --title "Here is an instructional tip:" "To make a selection, use the Spacebar." 8 58 + # Setting Default Tag for Advanced Settings + TAGS="community-script;${var_tags:-}" + CT_DEFAULT_TYPE="${CT_TYPE}" + CT_TYPE="" + while [ -z "$CT_TYPE" ]; do + if [ "$CT_DEFAULT_TYPE" == "1" ]; then + if CT_TYPE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ + "1" "Unprivileged" ON \ + "0" "Privileged" OFF \ + 3>&1 1>&2 2>&3); then + if [ -n "$CT_TYPE" ]; then + CT_TYPE_DESC="Unprivileged" + if [ "$CT_TYPE" -eq 0 ]; then + CT_TYPE_DESC="Privileged" + fi + echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os | ${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" fi - if [ "$CT_DEFAULT_TYPE" == "0" ]; then - if CT_TYPE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ - "1" "Unprivileged" OFF \ - "0" "Privileged" ON \ - 3>&1 1>&2 2>&3); then - if [ -n "$CT_TYPE" ]; then - CT_TYPE_DESC="Unprivileged" - if [ "$CT_TYPE" -eq 0 ]; then - CT_TYPE_DESC="Privileged" - fi - echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os${CL}" - echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" - fi - else - exit_script - fi - fi - done - - while true; do - if PW1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --passwordbox "\nSet Root Password (needed for root ssh access)" 9 58 --title "PASSWORD (leave blank for automatic login)" 3>&1 1>&2 2>&3); then - # Empty = Autologin - if [[ -z "$PW1" ]]; then - PW="" - PW1="Automatic Login" - echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}$PW1${CL}" - break - fi - - # Invalid: contains spaces - if [[ "$PW1" == *" "* ]]; then - whiptail --msgbox "Password cannot contain spaces." 8 58 - continue - fi - - # Invalid: too short - if ((${#PW1} < 5)); then - whiptail --msgbox "Password must be at least 5 characters." 8 58 - continue - fi - - # Confirm password - if PW2=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then - if [[ "$PW1" == "$PW2" ]]; then - PW="-password $PW1" - echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}" - break - else - whiptail --msgbox "Passwords do not match. Please try again." 8 58 - fi - else - exit_script - fi - else - exit_script - fi - done - - if CT_ID=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Container ID" 8 58 "$NEXTID" --title "CONTAINER ID" 3>&1 1>&2 2>&3); then - if [ -z "$CT_ID" ]; then - CT_ID="$NEXTID" - echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" - else - echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" - fi - else + else exit_script + fi fi - - while true; do - if CT_NAME=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 "$NSAPP" --title "HOSTNAME" 3>&1 1>&2 2>&3); then - if [ -z "$CT_NAME" ]; then - HN="$NSAPP" - else - HN=$(echo "${CT_NAME,,}" | tr -d ' ') - fi - # Hostname validate (RFC 1123) - if [[ "$HN" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ ]]; then - echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" - break - else - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --msgbox "❌ Invalid hostname: '$HN'\n\nOnly lowercase letters, digits and hyphens (-) are allowed.\nUnderscores (_) or other characters are not permitted!" 10 70 - fi - else - exit_script + if [ "$CT_DEFAULT_TYPE" == "0" ]; then + if CT_TYPE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ + "1" "Unprivileged" OFF \ + "0" "Privileged" ON \ + 3>&1 1>&2 2>&3); then + if [ -n "$CT_TYPE" ]; then + CT_TYPE_DESC="Unprivileged" + if [ "$CT_TYPE" -eq 0 ]; then + CT_TYPE_DESC="Privileged" + fi + echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os${CL}" + echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" fi - done - - while true; do - DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GB" 8 58 "$var_disk" --title "DISK SIZE" 3>&1 1>&2 2>&3) || exit_script - - if [ -z "$DISK_SIZE" ]; then - DISK_SIZE="$var_disk" - fi - - if [[ "$DISK_SIZE" =~ ^[1-9][0-9]*$ ]]; then - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" - break - else - whiptail --msgbox "Disk size must be a positive integer!" 8 58 - fi - done - - while true; do - CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ - --inputbox "Allocate CPU Cores" 8 58 "$var_cpu" --title "CORE COUNT" 3>&1 1>&2 2>&3) || exit_script - - if [ -z "$CORE_COUNT" ]; then - CORE_COUNT="$var_cpu" - fi - - if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" - break - else - whiptail --msgbox "CPU core count must be a positive integer!" 8 58 - fi - done - - while true; do - RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ - --inputbox "Allocate RAM in MiB" 8 58 "$var_ram" --title "RAM" 3>&1 1>&2 2>&3) || exit_script - - if [ -z "$RAM_SIZE" ]; then - RAM_SIZE="$var_ram" - fi - - if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" - break - else - whiptail --msgbox "RAM size must be a positive integer!" 8 58 - fi - done - - IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f) - BRIDGES="" - OLD_IFS=$IFS - IFS=$'\n' - for iface_filepath in ${IFACE_FILEPATH_LIST}; do - - iface_indexes_tmpfile=$(mktemp -q -u '.iface-XXXX') - (grep -Pn '^\s*iface' "${iface_filepath}" | cut -d':' -f1 && wc -l "${iface_filepath}" | cut -d' ' -f1) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' >"${iface_indexes_tmpfile}" || true - - if [ -f "${iface_indexes_tmpfile}" ]; then - - while read -r pair; do - start=$(echo "${pair}" | cut -d':' -f1) - end=$(echo "${pair}" | cut -d':' -f2) - - if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then - iface_name=$(sed "${start}q;d" "${iface_filepath}" | awk '{print $2}') - BRIDGES="${iface_name}"$'\n'"${BRIDGES}" - fi - - done <"${iface_indexes_tmpfile}" - rm -f "${iface_indexes_tmpfile}" - fi - - done - IFS=$OLD_IFS - BRIDGES=$(echo "$BRIDGES" | grep -v '^\s*$' | sort | uniq) - if [[ -z "$BRIDGES" ]]; then - BRG="vmbr0" - echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" - else - BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge:" 15 40 6 $(echo "$BRIDGES" | awk '{print $0, "Bridge"}') 3>&1 1>&2 2>&3) - if [[ -z "$BRG" ]]; then - exit_script - else - echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" - fi - fi - - # IPv4 methods: dhcp, static, none - while true; do - IPV4_METHOD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "IPv4 Address Management" \ - --menu "Select IPv4 Address Assignment Method:" 12 60 2 \ - "dhcp" "Automatic (DHCP, recommended)" \ - "static" "Static (manual entry)" \ - 3>&1 1>&2 2>&3) - - exit_status=$? - if [ $exit_status -ne 0 ]; then - exit_script - fi - - case "$IPV4_METHOD" in - dhcp) - NET="dhcp" - GATE="" - echo -e "${NETWORK}${BOLD}${DGN}IPv4: DHCP${CL}" - break - ;; - static) - # Static: call and validate CIDR address - while true; do - NET=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --inputbox "Enter Static IPv4 CIDR Address (e.g. 192.168.100.50/24)" 8 58 "" \ - --title "IPv4 ADDRESS" 3>&1 1>&2 2>&3) - if [ -z "$NET" ]; then - whiptail --msgbox "IPv4 address must not be empty." 8 58 - continue - elif [[ "$NET" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then - echo -e "${NETWORK}${BOLD}${DGN}IPv4 Address: ${BGN}$NET${CL}" - break - else - whiptail --msgbox "$NET is not a valid IPv4 CIDR address. Please enter a correct value!" 8 58 - fi - done - - # call and validate Gateway - while true; do - GATE1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --inputbox "Enter Gateway IP address for static IPv4" 8 58 "" \ - --title "Gateway IP" 3>&1 1>&2 2>&3) - if [ -z "$GATE1" ]; then - whiptail --msgbox "Gateway IP address cannot be empty." 8 58 - elif [[ ! "$GATE1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then - whiptail --msgbox "Invalid Gateway IP address format." 8 58 - else - GATE=",gw=$GATE1" - echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE1${CL}" - break - fi - done - break - ;; - esac - done - - # IPv6 Address Management selection - while true; do - IPV6_METHOD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --menu \ - "Select IPv6 Address Management Type:" 15 58 4 \ - "auto" "SLAAC/AUTO (recommended, default)" \ - "dhcp" "DHCPv6" \ - "static" "Static (manual entry)" \ - "none" "Disabled" \ - --default-item "auto" 3>&1 1>&2 2>&3) - [ $? -ne 0 ] && exit_script - - case "$IPV6_METHOD" in - auto) - echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}SLAAC/AUTO${CL}" - IPV6_ADDR="" - IPV6_GATE="" - break - ;; - dhcp) - echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}DHCPv6${CL}" - IPV6_ADDR="dhcp" - IPV6_GATE="" - break - ;; - static) - # Ask for static IPv6 address (CIDR notation, e.g., 2001:db8::1234/64) - while true; do - IPV6_ADDR=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox \ - "Set a static IPv6 CIDR address (e.g., 2001:db8::1234/64)" 8 58 "" \ - --title "IPv6 STATIC ADDRESS" 3>&1 1>&2 2>&3) || exit_script - if [[ "$IPV6_ADDR" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+(/[0-9]{1,3})$ ]]; then - echo -e "${NETWORK}${BOLD}${DGN}IPv6 Address: ${BGN}$IPV6_ADDR${CL}" - break - else - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox \ - "$IPV6_ADDR is an invalid IPv6 CIDR address. Please enter a valid IPv6 CIDR address (e.g., 2001:db8::1234/64)" 8 58 - fi - done - # Optional: ask for IPv6 gateway for static config - while true; do - IPV6_GATE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox \ - "Enter IPv6 gateway address (optional, leave blank for none)" 8 58 "" --title "IPv6 GATEWAY" 3>&1 1>&2 2>&3) - if [ -z "$IPV6_GATE" ]; then - IPV6_GATE="" - break - elif [[ "$IPV6_GATE" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+$ ]]; then - break - else - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox \ - "Invalid IPv6 gateway format." 8 58 - fi - done - break - ;; - none) - echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}Disabled${CL}" - IPV6_ADDR="none" - IPV6_GATE="" - break - ;; - *) - exit_script - ;; - esac - done - - if [ "$var_os" == "alpine" ]; then - APT_CACHER="" - APT_CACHER_IP="" - else - if APT_CACHER_IP=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set APT-Cacher IP (leave blank for none)" 8 58 --title "APT-Cacher IP" 3>&1 1>&2 2>&3); then - APT_CACHER="${APT_CACHER_IP:+yes}" - echo -e "${NETWORK}${BOLD}${DGN}APT-Cacher IP Address: ${BGN}${APT_CACHER_IP:-Default}${CL}" - else - exit_script - fi - fi - - # if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "IPv6" --yesno "Disable IPv6?" 10 58); then - # DISABLEIP6="yes" - # else - # DISABLEIP6="no" - # fi - # echo -e "${DISABLEIPV6}${BOLD}${DGN}Disable IPv6: ${BGN}$DISABLEIP6${CL}" - - if MTU1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default [The MTU of your selected vmbr, default is 1500])" 8 58 --title "MTU SIZE" 3>&1 1>&2 2>&3); then - if [ -z "$MTU1" ]; then - MTU1="Default" - MTU="" - else - MTU=",mtu=$MTU1" - fi - echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" - else + else exit_script + fi fi + done - if SD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a DNS Search Domain (leave blank for HOST)" 8 58 --title "DNS Search Domain" 3>&1 1>&2 2>&3); then - if [ -z "$SD" ]; then - SX=Host - SD="" + while true; do + if PW1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --passwordbox "\nSet Root Password (needed for root ssh access)" 9 58 --title "PASSWORD (leave blank for automatic login)" 3>&1 1>&2 2>&3); then + # Empty = Autologin + if [[ -z "$PW1" ]]; then + PW="" + PW1="Automatic Login" + echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}$PW1${CL}" + break + fi + + # Invalid: contains spaces + if [[ "$PW1" == *" "* ]]; then + whiptail --msgbox "Password cannot contain spaces." 8 58 + continue + fi + + # Invalid: too short + if ((${#PW1} < 5)); then + whiptail --msgbox "Password must be at least 5 characters." 8 58 + continue + fi + + # Confirm password + if PW2=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then + if [[ "$PW1" == "$PW2" ]]; then + PW="-password $PW1" + echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}" + break else - SX=$SD - SD="-searchdomain=$SD" + whiptail --msgbox "Passwords do not match. Please try again." 8 58 fi - echo -e "${SEARCH}${BOLD}${DGN}DNS Search Domain: ${BGN}$SX${CL}" - else + else exit_script + fi + else + exit_script + fi + done + + if CT_ID=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Container ID" 8 58 "$NEXTID" --title "CONTAINER ID" 3>&1 1>&2 2>&3); then + if [ -z "$CT_ID" ]; then + CT_ID="$NEXTID" + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" + else + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" + fi + else + exit_script + fi + + while true; do + if CT_NAME=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 "$NSAPP" --title "HOSTNAME" 3>&1 1>&2 2>&3); then + if [ -z "$CT_NAME" ]; then + HN="$NSAPP" + else + HN=$(echo "${CT_NAME,,}" | tr -d ' ') + fi + # Hostname validate (RFC 1123) + if [[ "$HN" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ ]]; then + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" + break + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --msgbox "❌ Invalid hostname: '$HN'\n\nOnly lowercase letters, digits and hyphens (-) are allowed.\nUnderscores (_) or other characters are not permitted!" 10 70 + fi + else + exit_script + fi + done + + while true; do + DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GB" 8 58 "$var_disk" --title "DISK SIZE" 3>&1 1>&2 2>&3) || exit_script + + if [ -z "$DISK_SIZE" ]; then + DISK_SIZE="$var_disk" fi - if NX=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a DNS Server IP (leave blank for HOST)" 8 58 --title "DNS SERVER IP" 3>&1 1>&2 2>&3); then - if [ -z "$NX" ]; then - NX=Host - NS="" - else - NS="-nameserver=$NX" + if [[ "$DISK_SIZE" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" + break + else + whiptail --msgbox "Disk size must be a positive integer!" 8 58 + fi + done + + while true; do + CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --inputbox "Allocate CPU Cores" 8 58 "$var_cpu" --title "CORE COUNT" 3>&1 1>&2 2>&3) || exit_script + + if [ -z "$CORE_COUNT" ]; then + CORE_COUNT="$var_cpu" + fi + + if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + break + else + whiptail --msgbox "CPU core count must be a positive integer!" 8 58 + fi + done + + while true; do + RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --inputbox "Allocate RAM in MiB" 8 58 "$var_ram" --title "RAM" 3>&1 1>&2 2>&3) || exit_script + + if [ -z "$RAM_SIZE" ]; then + RAM_SIZE="$var_ram" + fi + + if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + break + else + whiptail --msgbox "RAM size must be a positive integer!" 8 58 + fi + done + + IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f) + BRIDGES="" + OLD_IFS=$IFS + IFS=$'\n' + for iface_filepath in ${IFACE_FILEPATH_LIST}; do + + iface_indexes_tmpfile=$(mktemp -q -u '.iface-XXXX') + (grep -Pn '^\s*iface' "${iface_filepath}" | cut -d':' -f1 && wc -l "${iface_filepath}" | cut -d' ' -f1) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' >"${iface_indexes_tmpfile}" || true + + if [ -f "${iface_indexes_tmpfile}" ]; then + + while read -r pair; do + start=$(echo "${pair}" | cut -d':' -f1) + end=$(echo "${pair}" | cut -d':' -f2) + + if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then + iface_name=$(sed "${start}q;d" "${iface_filepath}" | awk '{print $2}') + BRIDGES="${iface_name}"$'\n'"${BRIDGES}" fi - echo -e "${NETWORK}${BOLD}${DGN}DNS Server IP Address: ${BGN}$NX${CL}" - else - exit_script + + done <"${iface_indexes_tmpfile}" + rm -f "${iface_indexes_tmpfile}" fi - if [ "$var_os" == "alpine" ] && [ "$NET" == "dhcp" ] && [ "$NX" != "Host" ]; then - UDHCPC_FIX="yes" + done + IFS=$OLD_IFS + BRIDGES=$(echo "$BRIDGES" | grep -v '^\s*$' | sort | uniq) + if [[ -z "$BRIDGES" ]]; then + BRG="vmbr0" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + else + BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge:" 15 40 6 $(echo "$BRIDGES" | awk '{print $0, "Bridge"}') 3>&1 1>&2 2>&3) + if [[ -z "$BRG" ]]; then + exit_script else - UDHCPC_FIX="no" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" fi - export UDHCPC_FIX + fi - if MAC1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a MAC Address(leave blank for generated MAC)" 8 58 --title "MAC ADDRESS" 3>&1 1>&2 2>&3); then - if [ -z "$MAC1" ]; then - MAC1="Default" - MAC="" + # IPv4 methods: dhcp, static, none + while true; do + IPV4_METHOD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "IPv4 Address Management" \ + --menu "Select IPv4 Address Assignment Method:" 12 60 2 \ + "dhcp" "Automatic (DHCP, recommended)" \ + "static" "Static (manual entry)" \ + 3>&1 1>&2 2>&3) + + exit_status=$? + if [ $exit_status -ne 0 ]; then + exit_script + fi + + case "$IPV4_METHOD" in + dhcp) + NET="dhcp" + GATE="" + echo -e "${NETWORK}${BOLD}${DGN}IPv4: DHCP${CL}" + break + ;; + static) + # Static: call and validate CIDR address + while true; do + NET=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Enter Static IPv4 CIDR Address (e.g. 192.168.100.50/24)" 8 58 "" \ + --title "IPv4 ADDRESS" 3>&1 1>&2 2>&3) + if [ -z "$NET" ]; then + whiptail --msgbox "IPv4 address must not be empty." 8 58 + continue + elif [[ "$NET" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then + echo -e "${NETWORK}${BOLD}${DGN}IPv4 Address: ${BGN}$NET${CL}" + break else - MAC=",hwaddr=$MAC1" - echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" + whiptail --msgbox "$NET is not a valid IPv4 CIDR address. Please enter a correct value!" 8 58 fi - else - exit_script - fi + done - if VLAN1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for no VLAN)" 8 58 --title "VLAN" 3>&1 1>&2 2>&3); then - if [ -z "$VLAN1" ]; then - VLAN1="Default" - VLAN="" + # call and validate Gateway + while true; do + GATE1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Enter Gateway IP address for static IPv4" 8 58 "" \ + --title "Gateway IP" 3>&1 1>&2 2>&3) + if [ -z "$GATE1" ]; then + whiptail --msgbox "Gateway IP address cannot be empty." 8 58 + elif [[ ! "$GATE1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then + whiptail --msgbox "Invalid Gateway IP address format." 8 58 else - VLAN=",tag=$VLAN1" + GATE=",gw=$GATE1" + echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE1${CL}" + break fi - echo -e "${VLANTAG}${BOLD}${DGN}Vlan: ${BGN}$VLAN1${CL}" - else - exit_script - fi + done + break + ;; + esac + done - if ADV_TAGS=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Custom Tags?[If you remove all, there will be no tags!]" 8 58 "${TAGS}" --title "Advanced Tags" 3>&1 1>&2 2>&3); then - if [ -n "${ADV_TAGS}" ]; then - ADV_TAGS=$(echo "$ADV_TAGS" | tr -d '[:space:]') - TAGS="${ADV_TAGS}" + # IPv6 Address Management selection + while true; do + IPV6_METHOD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --menu \ + "Select IPv6 Address Management Type:" 15 58 4 \ + "auto" "SLAAC/AUTO (recommended, default)" \ + "dhcp" "DHCPv6" \ + "static" "Static (manual entry)" \ + "none" "Disabled" \ + --default-item "auto" 3>&1 1>&2 2>&3) + [ $? -ne 0 ] && exit_script + + case "$IPV6_METHOD" in + auto) + echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}SLAAC/AUTO${CL}" + IPV6_ADDR="" + IPV6_GATE="" + break + ;; + dhcp) + echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}DHCPv6${CL}" + IPV6_ADDR="dhcp" + IPV6_GATE="" + break + ;; + static) + # Ask for static IPv6 address (CIDR notation, e.g., 2001:db8::1234/64) + while true; do + IPV6_ADDR=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox \ + "Set a static IPv6 CIDR address (e.g., 2001:db8::1234/64)" 8 58 "" \ + --title "IPv6 STATIC ADDRESS" 3>&1 1>&2 2>&3) || exit_script + if [[ "$IPV6_ADDR" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+(/[0-9]{1,3})$ ]]; then + echo -e "${NETWORK}${BOLD}${DGN}IPv6 Address: ${BGN}$IPV6_ADDR${CL}" + break else - TAGS=";" + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox \ + "$IPV6_ADDR is an invalid IPv6 CIDR address. Please enter a valid IPv6 CIDR address (e.g., 2001:db8::1234/64)" 8 58 fi - echo -e "${NETWORK}${BOLD}${DGN}Tags: ${BGN}$TAGS${CL}" - else - exit_script - fi - - SSH_AUTHORIZED_KEY="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "SSH Authorized key for root (leave empty for none)" 8 58 --title "SSH Key" 3>&1 1>&2 2>&3)" - - if [[ -z "${SSH_AUTHORIZED_KEY}" ]]; then - SSH_AUTHORIZED_KEY="" - fi - - if [[ "$PW" == -password* || -n "$SSH_AUTHORIZED_KEY" ]]; then - if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable Root SSH Access?" 10 58); then - SSH="yes" + done + # Optional: ask for IPv6 gateway for static config + while true; do + IPV6_GATE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox \ + "Enter IPv6 gateway address (optional, leave blank for none)" 8 58 "" --title "IPv6 GATEWAY" 3>&1 1>&2 2>&3) + if [ -z "$IPV6_GATE" ]; then + IPV6_GATE="" + break + elif [[ "$IPV6_GATE" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+$ ]]; then + break else - SSH="no" + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox \ + "Invalid IPv6 gateway format." 8 58 fi - echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}" - else - SSH="no" - echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}" - fi + done + break + ;; + none) + echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}Disabled${CL}" + IPV6_ADDR="none" + IPV6_GATE="" + break + ;; + *) + exit_script + ;; + esac + done - if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "FUSE Support" --yesno "Enable FUSE support?\nRequired for tools like rclone, mergerfs, AppImage, etc." 10 58); then - ENABLE_FUSE="yes" + if [ "$var_os" == "alpine" ]; then + APT_CACHER="" + APT_CACHER_IP="" + else + if APT_CACHER_IP=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set APT-Cacher IP (leave blank for none)" 8 58 --title "APT-Cacher IP" 3>&1 1>&2 2>&3); then + APT_CACHER="${APT_CACHER_IP:+yes}" + echo -e "${NETWORK}${BOLD}${DGN}APT-Cacher IP Address: ${BGN}${APT_CACHER_IP:-Default}${CL}" else - ENABLE_FUSE="no" + exit_script fi - echo -e "${FUSE}${BOLD}${DGN}Enable FUSE Support: ${BGN}$ENABLE_FUSE${CL}" + fi - if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then - VERBOSE="yes" - else - VERBOSE="no" - fi - echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}" + # if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "IPv6" --yesno "Disable IPv6?" 10 58); then + # DISABLEIP6="yes" + # else + # DISABLEIP6="no" + # fi + # echo -e "${DISABLEIPV6}${BOLD}${DGN}Disable IPv6: ${BGN}$DISABLEIP6${CL}" - if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create ${APP} LXC?" 10 58); then - echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}" - write_config + if MTU1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default [The MTU of your selected vmbr, default is 1500])" 8 58 --title "MTU SIZE" 3>&1 1>&2 2>&3); then + if [ -z "$MTU1" ]; then + MTU1="Default" + MTU="" else - clear - header_info - echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings on node $PVEHOST_NAME${CL}" - advanced_settings + MTU=",mtu=$MTU1" fi + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" + else + exit_script + fi + + if SD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a DNS Search Domain (leave blank for HOST)" 8 58 --title "DNS Search Domain" 3>&1 1>&2 2>&3); then + if [ -z "$SD" ]; then + SX=Host + SD="" + else + SX=$SD + SD="-searchdomain=$SD" + fi + echo -e "${SEARCH}${BOLD}${DGN}DNS Search Domain: ${BGN}$SX${CL}" + else + exit_script + fi + + if NX=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a DNS Server IP (leave blank for HOST)" 8 58 --title "DNS SERVER IP" 3>&1 1>&2 2>&3); then + if [ -z "$NX" ]; then + NX=Host + NS="" + else + NS="-nameserver=$NX" + fi + echo -e "${NETWORK}${BOLD}${DGN}DNS Server IP Address: ${BGN}$NX${CL}" + else + exit_script + fi + + if [ "$var_os" == "alpine" ] && [ "$NET" == "dhcp" ] && [ "$NX" != "Host" ]; then + UDHCPC_FIX="yes" + else + UDHCPC_FIX="no" + fi + export UDHCPC_FIX + + if MAC1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a MAC Address(leave blank for generated MAC)" 8 58 --title "MAC ADDRESS" 3>&1 1>&2 2>&3); then + if [ -z "$MAC1" ]; then + MAC1="Default" + MAC="" + else + MAC=",hwaddr=$MAC1" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" + fi + else + exit_script + fi + + if VLAN1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for no VLAN)" 8 58 --title "VLAN" 3>&1 1>&2 2>&3); then + if [ -z "$VLAN1" ]; then + VLAN1="Default" + VLAN="" + else + VLAN=",tag=$VLAN1" + fi + echo -e "${VLANTAG}${BOLD}${DGN}Vlan: ${BGN}$VLAN1${CL}" + else + exit_script + fi + + if ADV_TAGS=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Custom Tags?[If you remove all, there will be no tags!]" 8 58 "${TAGS}" --title "Advanced Tags" 3>&1 1>&2 2>&3); then + if [ -n "${ADV_TAGS}" ]; then + ADV_TAGS=$(echo "$ADV_TAGS" | tr -d '[:space:]') + TAGS="${ADV_TAGS}" + else + TAGS=";" + fi + echo -e "${NETWORK}${BOLD}${DGN}Tags: ${BGN}$TAGS${CL}" + else + exit_script + fi + + SSH_AUTHORIZED_KEY="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "SSH Authorized key for root (leave empty for none)" 8 58 --title "SSH Key" 3>&1 1>&2 2>&3)" + + if [[ -z "${SSH_AUTHORIZED_KEY}" ]]; then + SSH_AUTHORIZED_KEY="" + fi + + if [[ "$PW" == -password* || -n "$SSH_AUTHORIZED_KEY" ]]; then + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable Root SSH Access?" 10 58); then + SSH="yes" + else + SSH="no" + fi + echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}" + else + SSH="no" + echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}" + fi + + if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "FUSE Support" --yesno "Enable FUSE support?\nRequired for tools like rclone, mergerfs, AppImage, etc." 10 58); then + ENABLE_FUSE="yes" + else + ENABLE_FUSE="no" + fi + echo -e "${FUSE}${BOLD}${DGN}Enable FUSE Support: ${BGN}$ENABLE_FUSE${CL}" + + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then + VERBOSE="yes" + else + VERBOSE="no" + fi + echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}" + + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create ${APP} LXC?" 10 58); then + echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}" + write_config + else + clear + header_info + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings on node $PVEHOST_NAME${CL}" + advanced_settings + fi } diagnostics_check() { - if ! [ -d "/usr/local/community-scripts" ]; then - mkdir -p /usr/local/community-scripts - fi + if ! [ -d "/usr/local/community-scripts" ]; then + mkdir -p /usr/local/community-scripts + fi - if ! [ -f "/usr/local/community-scripts/diagnostics" ]; then - if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS" --yesno "Send Diagnostics of LXC Installation?\n\n(This only transmits data without user data, just RAM, CPU, LXC name, ...)" 10 58); then - cat </usr/local/community-scripts/diagnostics + if ! [ -f "/usr/local/community-scripts/diagnostics" ]; then + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS" --yesno "Send Diagnostics of LXC Installation?\n\n(This only transmits data without user data, just RAM, CPU, LXC name, ...)" 10 58); then + cat </usr/local/community-scripts/diagnostics DIAGNOSTICS=yes #This file is used to store the diagnostics settings for the Community-Scripts API. @@ -950,9 +950,9 @@ DIAGNOSTICS=yes #"status" #If you have any concerns, please review the source code at /misc/build.func EOF - DIAGNOSTICS="yes" - else - cat </usr/local/community-scripts/diagnostics + DIAGNOSTICS="yes" + else + cat </usr/local/community-scripts/diagnostics DIAGNOSTICS=no #This file is used to store the diagnostics settings for the Community-Scripts API. @@ -977,248 +977,248 @@ DIAGNOSTICS=no #"status" #If you have any concerns, please review the source code at /misc/build.func EOF - DIAGNOSTICS="no" - fi - else - DIAGNOSTICS=$(awk -F '=' '/^DIAGNOSTICS/ {print $2}' /usr/local/community-scripts/diagnostics) - + DIAGNOSTICS="no" fi + else + DIAGNOSTICS=$(awk -F '=' '/^DIAGNOSTICS/ {print $2}' /usr/local/community-scripts/diagnostics) + + fi } install_script() { - pve_check - shell_check - root_check - arch_check - ssh_check - maxkeys_check - diagnostics_check + pve_check + shell_check + root_check + arch_check + ssh_check + maxkeys_check + diagnostics_check - if systemctl is-active -q ping-instances.service; then - systemctl -q stop ping-instances.service + if systemctl is-active -q ping-instances.service; then + systemctl -q stop ping-instances.service + fi + NEXTID=$(pvesh get /cluster/nextid) + timezone=$(cat /etc/timezone) + header_info + while true; do + + TMP_CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "SETTINGS" \ + --menu "Choose an option:" 20 60 7 \ + "1" "Default Settings" \ + "2" "Default Settings (with verbose)" \ + "3" "Advanced Settings" \ + "4" "Use Config File" \ + "5" "Manage Default Storage" \ + "6" "Diagnostic Settings" \ + "7" "Exit" \ + --default-item "1" 3>&1 1>&2 2>&3) || true + + if [ -z "$TMP_CHOICE" ]; then + echo -e "\n${CROSS}${RD}Menu canceled. Exiting script.${CL}\n" + exit 0 fi - NEXTID=$(pvesh get /cluster/nextid) - timezone=$(cat /etc/timezone) - header_info - while true; do - TMP_CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "SETTINGS" \ - --menu "Choose an option:" 20 60 7 \ - "1" "Default Settings" \ - "2" "Default Settings (with verbose)" \ - "3" "Advanced Settings" \ - "4" "Use Config File" \ - "5" "Manage Default Storage" \ - "6" "Diagnostic Settings" \ - "7" "Exit" \ - --default-item "1" 3>&1 1>&2 2>&3) || true + CHOICE="$TMP_CHOICE" - if [ -z "$TMP_CHOICE" ]; then - echo -e "\n${CROSS}${RD}Menu canceled. Exiting script.${CL}\n" - exit 0 + case $CHOICE in + 1) + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME${CL}" + VERBOSE="no" + METHOD="default" + base_settings "$VERBOSE" + echo_default + break + ;; + 2) + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME (${VERBOSE_CROPPED}Verbose)${CL}" + VERBOSE="yes" + METHOD="default" + base_settings "$VERBOSE" + echo_default + break + ;; + 3) + header_info + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings on node $PVEHOST_NAME${CL}" + METHOD="advanced" + base_settings + advanced_settings + break + ;; + 4) + header_info + echo -e "${INFO}${HOLD} ${GN}Using Config File on node $PVEHOST_NAME${CL}" + METHOD="advanced" + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/config-file.func) + config_file + break + ;; + + 5) + manage_default_storage + ;; + 6) + if [[ $DIAGNOSTICS == "yes" ]]; then + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS SETTINGS" --yesno "Send Diagnostics of LXC Installation?\n\nCurrent setting: ${DIAGNOSTICS}" 10 58 \ + --yes-button "No" --no-button "Back"; then + DIAGNOSTICS="no" + sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=no/' /usr/local/community-scripts/diagnostics + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS SETTINGS" --msgbox "Diagnostics settings changed to ${DIAGNOSTICS}." 8 58 fi - - CHOICE="$TMP_CHOICE" - - case $CHOICE in - 1) - header_info - echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME${CL}" - VERBOSE="no" - METHOD="default" - base_settings "$VERBOSE" - echo_default - break - ;; - 2) - header_info - echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME (${VERBOSE_CROPPED}Verbose)${CL}" - VERBOSE="yes" - METHOD="default" - base_settings "$VERBOSE" - echo_default - break - ;; - 3) - header_info - echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings on node $PVEHOST_NAME${CL}" - METHOD="advanced" - base_settings - advanced_settings - break - ;; - 4) - header_info - echo -e "${INFO}${HOLD} ${GN}Using Config File on node $PVEHOST_NAME${CL}" - METHOD="advanced" - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/config-file.func) - config_file - break - ;; - - 5) - manage_default_storage - ;; - 6) - if [[ $DIAGNOSTICS == "yes" ]]; then - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS SETTINGS" --yesno "Send Diagnostics of LXC Installation?\n\nCurrent setting: ${DIAGNOSTICS}" 10 58 \ - --yes-button "No" --no-button "Back"; then - DIAGNOSTICS="no" - sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=no/' /usr/local/community-scripts/diagnostics - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS SETTINGS" --msgbox "Diagnostics settings changed to ${DIAGNOSTICS}." 8 58 - fi - else - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS SETTINGS" --yesno "Send Diagnostics of LXC Installation?\n\nCurrent setting: ${DIAGNOSTICS}" 10 58 \ - --yes-button "Yes" --no-button "Back"; then - DIAGNOSTICS="yes" - sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=yes/' /usr/local/community-scripts/diagnostics - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS SETTINGS" --msgbox "Diagnostics settings changed to ${DIAGNOSTICS}." 8 58 - fi - fi - ;; - 7) - echo -e "\n${CROSS}${RD}Script terminated. Have a great day!${CL}\n" - exit 0 - ;; - *) - echo -e "${CROSS}${RD}Invalid option, please try again.${CL}" - ;; - esac - done + else + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS SETTINGS" --yesno "Send Diagnostics of LXC Installation?\n\nCurrent setting: ${DIAGNOSTICS}" 10 58 \ + --yes-button "Yes" --no-button "Back"; then + DIAGNOSTICS="yes" + sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=yes/' /usr/local/community-scripts/diagnostics + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS SETTINGS" --msgbox "Diagnostics settings changed to ${DIAGNOSTICS}." 8 58 + fi + fi + ;; + 7) + echo -e "\n${CROSS}${RD}Script terminated. Have a great day!${CL}\n" + exit 0 + ;; + *) + echo -e "${CROSS}${RD}Invalid option, please try again.${CL}" + ;; + esac + done } check_container_resources() { - # Check actual RAM & Cores - current_ram=$(free -m | awk 'NR==2{print $2}') - current_cpu=$(nproc) + # Check actual RAM & Cores + current_ram=$(free -m | awk 'NR==2{print $2}') + current_cpu=$(nproc) - # Check whether the current RAM is less than the required RAM or the CPU cores are less than required - if [[ "$current_ram" -lt "$var_ram" ]] || [[ "$current_cpu" -lt "$var_cpu" ]]; then - echo -e "\n${INFO}${HOLD} ${GN}Required: ${var_cpu} CPU, ${var_ram}MB RAM ${CL}| ${RD}Current: ${current_cpu} CPU, ${current_ram}MB RAM${CL}" - echo -e "${YWB}Please ensure that the ${APP} LXC is configured with at least ${var_cpu} vCPU and ${var_ram} MB RAM for the build process.${CL}\n" - echo -ne "${INFO}${HOLD} May cause data loss! ${INFO} Continue update with under-provisioned LXC? " - read -r prompt - # Check if the input is 'yes', otherwise exit with status 1 - if [[ ! ${prompt,,} =~ ^(yes)$ ]]; then - echo -e "${CROSS}${HOLD} ${YWB}Exiting based on user input.${CL}" - exit 1 - fi - else - echo -e "" + # Check whether the current RAM is less than the required RAM or the CPU cores are less than required + if [[ "$current_ram" -lt "$var_ram" ]] || [[ "$current_cpu" -lt "$var_cpu" ]]; then + echo -e "\n${INFO}${HOLD} ${GN}Required: ${var_cpu} CPU, ${var_ram}MB RAM ${CL}| ${RD}Current: ${current_cpu} CPU, ${current_ram}MB RAM${CL}" + echo -e "${YWB}Please ensure that the ${APP} LXC is configured with at least ${var_cpu} vCPU and ${var_ram} MB RAM for the build process.${CL}\n" + echo -ne "${INFO}${HOLD} May cause data loss! ${INFO} Continue update with under-provisioned LXC? " + read -r prompt + # Check if the input is 'yes', otherwise exit with status 1 + if [[ ! ${prompt,,} =~ ^(yes)$ ]]; then + echo -e "${CROSS}${HOLD} ${YWB}Exiting based on user input.${CL}" + exit 1 fi + else + echo -e "" + fi } check_container_storage() { - # Check if the /boot partition is more than 80% full - total_size=$(df /boot --output=size | tail -n 1) - local used_size=$(df /boot --output=used | tail -n 1) - usage=$((100 * used_size / total_size)) - if ((usage > 80)); then - # Prompt the user for confirmation to continue - echo -e "${INFO}${HOLD} ${YWB}Warning: Storage is dangerously low (${usage}%).${CL}" - echo -ne "Continue anyway? " - read -r prompt - # Check if the input is 'y' or 'yes', otherwise exit with status 1 - if [[ ! ${prompt,,} =~ ^(y|yes)$ ]]; then - echo -e "${CROSS}${HOLD}${YWB}Exiting based on user input.${CL}" - exit 1 - fi + # Check if the /boot partition is more than 80% full + total_size=$(df /boot --output=size | tail -n 1) + local used_size=$(df /boot --output=used | tail -n 1) + usage=$((100 * used_size / total_size)) + if ((usage > 80)); then + # Prompt the user for confirmation to continue + echo -e "${INFO}${HOLD} ${YWB}Warning: Storage is dangerously low (${usage}%).${CL}" + echo -ne "Continue anyway? " + read -r prompt + # Check if the input is 'y' or 'yes', otherwise exit with status 1 + if [[ ! ${prompt,,} =~ ^(y|yes)$ ]]; then + echo -e "${CROSS}${HOLD}${YWB}Exiting based on user input.${CL}" + exit 1 fi + fi } start() { - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) - if command -v pveversion >/dev/null 2>&1; then - install_script - elif [ ! -z ${PHS_SILENT+x} ] && [[ "${PHS_SILENT}" == "1" ]]; then - VERBOSE="no" - set_std_mode - update_script - else - CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "${APP} LXC Update/Setting" --menu \ - "Support/Update functions for ${APP} LXC. Choose an option:" \ - 12 60 3 \ - "1" "YES (Silent Mode)" \ - "2" "YES (Verbose Mode)" \ - "3" "NO (Cancel Update)" --nocancel --default-item "1" 3>&1 1>&2 2>&3) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) + if command -v pveversion >/dev/null 2>&1; then + install_script + elif [ ! -z ${PHS_SILENT+x} ] && [[ "${PHS_SILENT}" == "1" ]]; then + VERBOSE="no" + set_std_mode + update_script + else + CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "${APP} LXC Update/Setting" --menu \ + "Support/Update functions for ${APP} LXC. Choose an option:" \ + 12 60 3 \ + "1" "YES (Silent Mode)" \ + "2" "YES (Verbose Mode)" \ + "3" "NO (Cancel Update)" --nocancel --default-item "1" 3>&1 1>&2 2>&3) - case "$CHOICE" in - 1) - VERBOSE="no" - set_std_mode - ;; - 2) - VERBOSE="yes" - set_std_mode - ;; - 3) - clear - exit_script - exit - ;; - esac - update_script - fi + case "$CHOICE" in + 1) + VERBOSE="no" + set_std_mode + ;; + 2) + VERBOSE="yes" + set_std_mode + ;; + 3) + clear + exit_script + exit + ;; + esac + update_script + fi } # This function collects user settings and integrates all the collected information. build_container() { - # if [ "$VERBOSE" == "yes" ]; then set -x; fi + # if [ "$VERBOSE" == "yes" ]; then set -x; fi - NET_STRING="-net0 name=eth0,bridge=$BRG$MAC,ip=$NET$GATE$VLAN$MTU" - case "$IPV6_METHOD" in - auto) NET_STRING="$NET_STRING,ip6=auto" ;; - dhcp) NET_STRING="$NET_STRING,ip6=dhcp" ;; - static) - NET_STRING="$NET_STRING,ip6=$IPV6_ADDR" - [ -n "$IPV6_GATE" ] && NET_STRING="$NET_STRING,gw6=$IPV6_GATE" - ;; - none) ;; - esac + NET_STRING="-net0 name=eth0,bridge=$BRG$MAC,ip=$NET$GATE$VLAN$MTU" + case "$IPV6_METHOD" in + auto) NET_STRING="$NET_STRING,ip6=auto" ;; + dhcp) NET_STRING="$NET_STRING,ip6=dhcp" ;; + static) + NET_STRING="$NET_STRING,ip6=$IPV6_ADDR" + [ -n "$IPV6_GATE" ] && NET_STRING="$NET_STRING,gw6=$IPV6_GATE" + ;; + none) ;; + esac - if [ "$CT_TYPE" == "1" ]; then - FEATURES="keyctl=1,nesting=1" - else - FEATURES="nesting=1" - fi + if [ "$CT_TYPE" == "1" ]; then + FEATURES="keyctl=1,nesting=1" + else + FEATURES="nesting=1" + fi - if [ "$ENABLE_FUSE" == "yes" ]; then - FEATURES="$FEATURES,fuse=1" - fi + if [ "$ENABLE_FUSE" == "yes" ]; then + FEATURES="$FEATURES,fuse=1" + fi - #if [[ $DIAGNOSTICS == "yes" ]]; then - # post_to_api - #fi + #if [[ $DIAGNOSTICS == "yes" ]]; then + # post_to_api + #fi - TEMP_DIR=$(mktemp -d) - pushd "$TEMP_DIR" >/dev/null - if [ "$var_os" == "alpine" ]; then - export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-install.func)" - else - export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" - fi - export DIAGNOSTICS="$DIAGNOSTICS" - export RANDOM_UUID="$RANDOM_UUID" - export CACHER="$APT_CACHER" - export CACHER_IP="$APT_CACHER_IP" - export tz="$timezone" - #export DISABLEIPV6="$DISABLEIP6" - export APPLICATION="$APP" - export app="$NSAPP" - export PASSWORD="$PW" - export VERBOSE="$VERBOSE" - export SSH_ROOT="${SSH}" - export SSH_AUTHORIZED_KEY - export CTID="$CT_ID" - export CTTYPE="$CT_TYPE" - export ENABLE_FUSE="$ENABLE_FUSE" - export ENABLE_TUN="$ENABLE_TUN" - export PCT_OSTYPE="$var_os" - export PCT_OSVERSION="$var_version" - export PCT_DISK_SIZE="$DISK_SIZE" - export PCT_OPTIONS=" + TEMP_DIR=$(mktemp -d) + pushd "$TEMP_DIR" >/dev/null + if [ "$var_os" == "alpine" ]; then + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-install.func)" + else + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" + fi + export DIAGNOSTICS="$DIAGNOSTICS" + export RANDOM_UUID="$RANDOM_UUID" + export CACHER="$APT_CACHER" + export CACHER_IP="$APT_CACHER_IP" + export tz="$timezone" + #export DISABLEIPV6="$DISABLEIP6" + export APPLICATION="$APP" + export app="$NSAPP" + export PASSWORD="$PW" + export VERBOSE="$VERBOSE" + export SSH_ROOT="${SSH}" + export SSH_AUTHORIZED_KEY + export CTID="$CT_ID" + export CTTYPE="$CT_TYPE" + export ENABLE_FUSE="$ENABLE_FUSE" + export ENABLE_TUN="$ENABLE_TUN" + export PCT_OSTYPE="$var_os" + export PCT_OSVERSION="$var_version" + export PCT_DISK_SIZE="$DISK_SIZE" + export PCT_OPTIONS=" -features $FEATURES -hostname $HN -tags $TAGS @@ -1231,16 +1231,16 @@ build_container() { -unprivileged $CT_TYPE $PW " - bash -c "$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/create_lxc.sh)" - if [ $? -ne 0 ]; then - exit 200 - fi + bash -c "$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/create_lxc.sh)" + if [ $? -ne 0 ]; then + exit 200 + fi - LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" + LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" - # USB passthrough for privileged LXC (CT_TYPE=0) - if [ "$CT_TYPE" == "0" ]; then - cat <>"$LXC_CONFIG" + # USB passthrough for privileged LXC (CT_TYPE=0) + if [ "$CT_TYPE" == "0" ]; then + cat <>"$LXC_CONFIG" # USB passthrough lxc.cgroup2.devices.allow: a lxc.cap.drop: @@ -1252,191 +1252,191 @@ lxc.mount.entry: /dev/ttyUSB1 dev/ttyUSB1 none bind,optional,create= lxc.mount.entry: /dev/ttyACM0 dev/ttyACM0 none bind,optional,create=file lxc.mount.entry: /dev/ttyACM1 dev/ttyACM1 none bind,optional,create=file EOF + fi + + # VAAPI passthrough for privileged containers or known apps + VAAPI_APPS=( + "immich" + "Channels" + "Emby" + "ErsatzTV" + "Frigate" + "Jellyfin" + "Plex" + "Scrypted" + "Tdarr" + "Unmanic" + "Ollama" + "FileFlows" + "Open WebUI" + ) + + is_vaapi_app=false + for vaapi_app in "${VAAPI_APPS[@]}"; do + if [[ "$APP" == "$vaapi_app" ]]; then + is_vaapi_app=true + break + fi + done + + if ([ "$CT_TYPE" == "0" ] || [ "$is_vaapi_app" == "true" ]) && + ([[ -e /dev/dri/renderD128 ]] || [[ -e /dev/dri/card0 ]] || [[ -e /dev/fb0 ]]); then + + echo "" + msg_custom "⚙️ " "\e[96m" "Configuring VAAPI passthrough for LXC container" + if [ "$CT_TYPE" != "0" ]; then + msg_custom "⚠️ " "\e[33m" "Container is unprivileged – VAAPI passthrough may not work without additional host configuration (e.g., idmap)." + fi + msg_custom "ℹ️ " "\e[96m" "VAAPI enables GPU hardware acceleration (e.g., for video transcoding in Jellyfin or Plex)." + echo "" + read -rp "➤ Automatically mount all available VAAPI devices? [Y/n]: " VAAPI_ALL + + if [[ "$VAAPI_ALL" =~ ^[Yy]$|^$ ]]; then + if [ "$CT_TYPE" == "0" ]; then + # PRV Container → alles zulässig + [[ -e /dev/dri/renderD128 ]] && { + echo "lxc.cgroup2.devices.allow: c 226:128 rwm" >>"$LXC_CONFIG" + echo "lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file" >>"$LXC_CONFIG" + } + [[ -e /dev/dri/card0 ]] && { + echo "lxc.cgroup2.devices.allow: c 226:0 rwm" >>"$LXC_CONFIG" + echo "lxc.mount.entry: /dev/dri/card0 dev/dri/card0 none bind,optional,create=file" >>"$LXC_CONFIG" + } + [[ -e /dev/fb0 ]] && { + echo "lxc.cgroup2.devices.allow: c 29:0 rwm" >>"$LXC_CONFIG" + echo "lxc.mount.entry: /dev/fb0 dev/fb0 none bind,optional,create=file" >>"$LXC_CONFIG" + } + [[ -d /dev/dri ]] && { + echo "lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" + } + else + # UNPRV Container → nur devX für UI + [[ -e /dev/dri/card0 ]] && echo "dev0: /dev/dri/card0,gid=44" >>"$LXC_CONFIG" + [[ -e /dev/dri/card1 ]] && echo "dev0: /dev/dri/card1,gid=44" >>"$LXC_CONFIG" + [[ -e /dev/dri/renderD128 ]] && echo "dev1: /dev/dri/renderD128,gid=104" >>"$LXC_CONFIG" + fi fi - # VAAPI passthrough for privileged containers or known apps - VAAPI_APPS=( - "immich" - "Channels" - "Emby" - "ErsatzTV" - "Frigate" - "Jellyfin" - "Plex" - "Scrypted" - "Tdarr" - "Unmanic" - "Ollama" - "FileFlows" - "Open WebUI" - ) - - is_vaapi_app=false - for vaapi_app in "${VAAPI_APPS[@]}"; do - if [[ "$APP" == "$vaapi_app" ]]; then - is_vaapi_app=true - break - fi - done - - if ([ "$CT_TYPE" == "0" ] || [ "$is_vaapi_app" == "true" ]) && - ([[ -e /dev/dri/renderD128 ]] || [[ -e /dev/dri/card0 ]] || [[ -e /dev/fb0 ]]); then - - echo "" - msg_custom "⚙️ " "\e[96m" "Configuring VAAPI passthrough for LXC container" - if [ "$CT_TYPE" != "0" ]; then - msg_custom "⚠️ " "\e[33m" "Container is unprivileged – VAAPI passthrough may not work without additional host configuration (e.g., idmap)." - fi - msg_custom "ℹ️ " "\e[96m" "VAAPI enables GPU hardware acceleration (e.g., for video transcoding in Jellyfin or Plex)." - echo "" - read -rp "➤ Automatically mount all available VAAPI devices? [Y/n]: " VAAPI_ALL - - if [[ "$VAAPI_ALL" =~ ^[Yy]$|^$ ]]; then - if [ "$CT_TYPE" == "0" ]; then - # PRV Container → alles zulässig - [[ -e /dev/dri/renderD128 ]] && { - echo "lxc.cgroup2.devices.allow: c 226:128 rwm" >>"$LXC_CONFIG" - echo "lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file" >>"$LXC_CONFIG" - } - [[ -e /dev/dri/card0 ]] && { - echo "lxc.cgroup2.devices.allow: c 226:0 rwm" >>"$LXC_CONFIG" - echo "lxc.mount.entry: /dev/dri/card0 dev/dri/card0 none bind,optional,create=file" >>"$LXC_CONFIG" - } - [[ -e /dev/fb0 ]] && { - echo "lxc.cgroup2.devices.allow: c 29:0 rwm" >>"$LXC_CONFIG" - echo "lxc.mount.entry: /dev/fb0 dev/fb0 none bind,optional,create=file" >>"$LXC_CONFIG" - } - [[ -d /dev/dri ]] && { - echo "lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" - } - else - # UNPRV Container → nur devX für UI - [[ -e /dev/dri/card0 ]] && echo "dev0: /dev/dri/card0,gid=44" >>"$LXC_CONFIG" - [[ -e /dev/dri/card1 ]] && echo "dev0: /dev/dri/card1,gid=44" >>"$LXC_CONFIG" - [[ -e /dev/dri/renderD128 ]] && echo "dev1: /dev/dri/renderD128,gid=104" >>"$LXC_CONFIG" - fi - fi - + fi + if [ "$CT_TYPE" == "1" ] && [ "$is_vaapi_app" == "true" ]; then + if [[ -e /dev/dri/card0 ]]; then + echo "dev0: /dev/dri/card0,gid=44" >>"$LXC_CONFIG" + elif [[ -e /dev/dri/card1 ]]; then + echo "dev0: /dev/dri/card1,gid=44" >>"$LXC_CONFIG" fi - if [ "$CT_TYPE" == "1" ] && [ "$is_vaapi_app" == "true" ]; then - if [[ -e /dev/dri/card0 ]]; then - echo "dev0: /dev/dri/card0,gid=44" >>"$LXC_CONFIG" - elif [[ -e /dev/dri/card1 ]]; then - echo "dev0: /dev/dri/card1,gid=44" >>"$LXC_CONFIG" - fi - if [[ -e /dev/dri/renderD128 ]]; then - echo "dev1: /dev/dri/renderD128,gid=104" >>"$LXC_CONFIG" - fi + if [[ -e /dev/dri/renderD128 ]]; then + echo "dev1: /dev/dri/renderD128,gid=104" >>"$LXC_CONFIG" fi + fi - # TUN device passthrough - if [ "$ENABLE_TUN" == "yes" ]; then - cat <>"$LXC_CONFIG" + # TUN device passthrough + if [ "$ENABLE_TUN" == "yes" ]; then + cat <>"$LXC_CONFIG" lxc.cgroup2.devices.allow: c 10:200 rwm lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file EOF + fi + + # This starts the container and executes -install.sh + msg_info "Starting LXC Container" + pct start "$CTID" + + # wait for status 'running' + for i in {1..10}; do + if pct status "$CTID" | grep -q "status: running"; then + msg_ok "Startted LXC Container" + break fi + sleep 1 + if [ "$i" -eq 10 ]; then + msg_error "LXC Container did not reach running state" + exit 1 + fi + done - # This starts the container and executes -install.sh - msg_info "Starting LXC Container" - pct start "$CTID" + if [ "$var_os" == "alpine" ]; then + PING_TARGET="1.1.1.1" + else + PING_TARGET="deb.debian.org" + fi - # wait for status 'running' - for i in {1..10}; do - if pct status "$CTID" | grep -q "status: running"; then - msg_ok "Startted LXC Container" - break - fi - sleep 1 - if [ "$i" -eq 10 ]; then - msg_error "LXC Container did not reach running state" - exit 1 - fi - done - - if [ "$var_os" == "alpine" ]; then - PING_TARGET="alpinelinux.org" + msg_info "Waiting for network in LXC container" + for i in {1..10}; do + if pct exec "$CTID" -- ping -c1 -W1 "$PING_TARGET" >/dev/null 2>&1; then + msg_ok "Network in LXC is reachable" + break + fi + if [ "$i" -lt 10 ]; then + msg_warn "No network yet in LXC (try $i/10) – waiting..." + sleep 3 else - PING_TARGET="deb.debian.org" - fi - - msg_info "Waiting for network in LXC container" - for i in {1..10}; do + msg_error "No network in LXC after waiting." + read -r -p "Set fallback DNS (1.1.1.1/8.8.8.8)? [y/N]: " choice + case "$choice" in + [yY]*) + pct set "$CTID" --nameserver 1.1.1.1 + pct set "$CTID" --nameserver 8.8.8.8 if pct exec "$CTID" -- ping -c1 -W1 "$PING_TARGET" >/dev/null 2>&1; then - msg_ok "Network in LXC is reachable" - break - fi - if [ "$i" -lt 10 ]; then - msg_warn "No network yet in LXC (try $i/10) – waiting..." - sleep 3 + msg_ok "Network reachable after DNS fallback" else - msg_error "No network in LXC after waiting." - read -r -p "Set fallback DNS (1.1.1.1/8.8.8.8)? [y/N]: " choice - case "$choice" in - [yY]*) - pct set "$CTID" --nameserver 1.1.1.1 - pct set "$CTID" --nameserver 8.8.8.8 - if pct exec "$CTID" -- ping -c1 -W1 "$PING_TARGET" >/dev/null 2>&1; then - msg_ok "Network reachable after DNS fallback" - else - msg_error "Still no network/DNS in LXC! Aborting customization." - exit 1 - fi - ;; - *) - msg_error "Aborted by user – no DNS fallback set." - exit 1 - ;; - esac + msg_error "Still no network/DNS in LXC! Aborting customization." + exit 1 fi - done + ;; + *) + msg_error "Aborted by user – no DNS fallback set." + exit 1 + ;; + esac + fi + done - msg_info "Customizing LXC Container" - if [ "$var_os" == "alpine" ]; then - sleep 3 - pct exec "$CTID" -- /bin/sh -c 'cat </etc/apk/repositories + msg_info "Customizing LXC Container" + if [ "$var_os" == "alpine" ]; then + sleep 3 + pct exec "$CTID" -- /bin/sh -c 'cat </etc/apk/repositories http://dl-cdn.alpinelinux.org/alpine/latest-stable/main http://dl-cdn.alpinelinux.org/alpine/latest-stable/community EOF' - pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses >/dev/null" - else - sleep 3 + pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses >/dev/null" + else + sleep 3 - pct exec "$CTID" -- bash -c "sed -i '/$LANG/ s/^# //' /etc/locale.gen" - pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \ + pct exec "$CTID" -- bash -c "sed -i '/$LANG/ s/^# //' /etc/locale.gen" + pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \ echo LANG=\$locale_line >/etc/default/locale && \ locale-gen >/dev/null && \ export LANG=\$locale_line" - if [[ -z "${tz:-}" ]]; then - tz=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "Etc/UTC") - fi - - if pct exec "$CTID" -- test -e "/usr/share/zoneinfo/$tz"; then - pct exec "$CTID" -- bash -c "tz='$tz'; echo \"\$tz\" >/etc/timezone && ln -sf \"/usr/share/zoneinfo/\$tz\" /etc/localtime" - else - msg_warn "Skipping timezone setup – zone '$tz' not found in container" - fi - - pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y sudo curl mc gnupg2 >/dev/null" || { - msg_error "apt-get base packages installation failed" - exit 1 - } + if [[ -z "${tz:-}" ]]; then + tz=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "Etc/UTC") fi - msg_ok "Customized LXC Container" - if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/"$var_install".sh)"; then - return + if pct exec "$CTID" -- test -e "/usr/share/zoneinfo/$tz"; then + pct exec "$CTID" -- bash -c "tz='$tz'; echo \"\$tz\" >/etc/timezone && ln -sf \"/usr/share/zoneinfo/\$tz\" /etc/localtime" + else + msg_warn "Skipping timezone setup – zone '$tz' not found in container" fi + + pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y sudo curl mc gnupg2 >/dev/null" || { + msg_error "apt-get base packages installation failed" + exit 1 + } + fi + msg_ok "Customized LXC Container" + + if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/"$var_install".sh)"; then + return + fi } # This function sets the description of the container. description() { - IP=$(pct exec "$CTID" ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) + IP=$(pct exec "$CTID" ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) - # Generate LXC Description - DESCRIPTION=$( - cat < Logo @@ -1464,41 +1464,41 @@ description() { EOF - ) + ) - # Set Description in LXC - pct set "$CTID" -description "$DESCRIPTION" + # Set Description in LXC + pct set "$CTID" -description "$DESCRIPTION" - if [[ -f /etc/systemd/system/ping-instances.service ]]; then - systemctl start ping-instances.service - fi + if [[ -f /etc/systemd/system/ping-instances.service ]]; then + systemctl start ping-instances.service + fi - post_update_to_api "done" "none" + post_update_to_api "done" "none" } api_exit_script() { - exit_code=$? - if [ $exit_code -ne 0 ]; then - case $exit_code in - 100) post_update_to_api "failed" "100: Unexpected error in create_lxc.sh" ;; - 101) post_update_to_api "failed" "101: No network connection detected in create_lxc.sh" ;; - 200) post_update_to_api "failed" "200: LXC creation failed in create_lxc.sh" ;; - 201) post_update_to_api "failed" "201: Invalid Storage class in create_lxc.sh" ;; - 202) post_update_to_api "failed" "202: User aborted menu in create_lxc.sh" ;; - 203) post_update_to_api "failed" "203: CTID not set in create_lxc.sh" ;; - 204) post_update_to_api "failed" "204: PCT_OSTYPE not set in create_lxc.sh" ;; - 205) post_update_to_api "failed" "205: CTID cannot be less than 100 in create_lxc.sh" ;; - 206) post_update_to_api "failed" "206: CTID already in use in create_lxc.sh" ;; - 207) post_update_to_api "failed" "207: Template not found in create_lxc.sh" ;; - 208) post_update_to_api "failed" "208: Error downloading template in create_lxc.sh" ;; - 209) post_update_to_api "failed" "209: Container creation failed, but template is intact in create_lxc.sh" ;; - *) post_update_to_api "failed" "Unknown error, exit code: $exit_code in create_lxc.sh" ;; - esac - fi + exit_code=$? + if [ $exit_code -ne 0 ]; then + case $exit_code in + 100) post_update_to_api "failed" "100: Unexpected error in create_lxc.sh" ;; + 101) post_update_to_api "failed" "101: No network connection detected in create_lxc.sh" ;; + 200) post_update_to_api "failed" "200: LXC creation failed in create_lxc.sh" ;; + 201) post_update_to_api "failed" "201: Invalid Storage class in create_lxc.sh" ;; + 202) post_update_to_api "failed" "202: User aborted menu in create_lxc.sh" ;; + 203) post_update_to_api "failed" "203: CTID not set in create_lxc.sh" ;; + 204) post_update_to_api "failed" "204: PCT_OSTYPE not set in create_lxc.sh" ;; + 205) post_update_to_api "failed" "205: CTID cannot be less than 100 in create_lxc.sh" ;; + 206) post_update_to_api "failed" "206: CTID already in use in create_lxc.sh" ;; + 207) post_update_to_api "failed" "207: Template not found in create_lxc.sh" ;; + 208) post_update_to_api "failed" "208: Error downloading template in create_lxc.sh" ;; + 209) post_update_to_api "failed" "209: Container creation failed, but template is intact in create_lxc.sh" ;; + *) post_update_to_api "failed" "Unknown error, exit code: $exit_code in create_lxc.sh" ;; + esac + fi } if command -v pveversion >/dev/null 2>&1; then - trap 'api_exit_script' EXIT + trap 'api_exit_script' EXIT fi trap 'post_update_to_api "failed" "$BASH_COMMAND"' ERR trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT From c683060454da77d1efdcb4229537719f8dd99927 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 22 Jul 2025 10:43:26 +0200 Subject: [PATCH 0120/1733] Update build.func --- misc/build.func | 66 +++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 35 deletions(-) diff --git a/misc/build.func b/misc/build.func index 802654589..4d8e759a3 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1354,42 +1354,38 @@ EOF fi done - if [ "$var_os" == "alpine" ]; then - PING_TARGET="1.1.1.1" - else - PING_TARGET="deb.debian.org" - fi - - msg_info "Waiting for network in LXC container" - for i in {1..10}; do - if pct exec "$CTID" -- ping -c1 -W1 "$PING_TARGET" >/dev/null 2>&1; then - msg_ok "Network in LXC is reachable" - break - fi - if [ "$i" -lt 10 ]; then - msg_warn "No network yet in LXC (try $i/10) – waiting..." - sleep 3 - else - msg_error "No network in LXC after waiting." - read -r -p "Set fallback DNS (1.1.1.1/8.8.8.8)? [y/N]: " choice - case "$choice" in - [yY]*) - pct set "$CTID" --nameserver 1.1.1.1 - pct set "$CTID" --nameserver 8.8.8.8 - if pct exec "$CTID" -- ping -c1 -W1 "$PING_TARGET" >/dev/null 2>&1; then - msg_ok "Network reachable after DNS fallback" - else - msg_error "Still no network/DNS in LXC! Aborting customization." + if [ "$var_os" != "alpine" ]; then + msg_info "Waiting for network in LXC container" + for i in {1..10}; do + if pct exec "$CTID" -- ping -c1 -W1 deb.debian.org >/dev/null 2>&1; then + msg_ok "Network in LXC is reachable" + break + fi + if [ "$i" -lt 10 ]; then + msg_warn "No network yet in LXC (try $i/10) – waiting..." + sleep 3 + else + msg_error "No network in LXC after waiting." + read -r -p "Set fallback DNS (1.1.1.1/8.8.8.8)? [y/N]: " choice + case "$choice" in + [yY]*) + pct set "$CTID" --nameserver 1.1.1.1 + pct set "$CTID" --nameserver 8.8.8.8 + if pct exec "$CTID" -- ping -c1 -W1 deb.debian.org >/dev/null 2>&1; then + msg_ok "Network reachable after DNS fallback" + else + msg_error "Still no network/DNS in LXC! Aborting customization." + exit 1 + fi + ;; + *) + msg_error "Aborted by user – no DNS fallback set." exit 1 - fi - ;; - *) - msg_error "Aborted by user – no DNS fallback set." - exit 1 - ;; - esac - fi - done + ;; + esac + fi + done + fi msg_info "Customizing LXC Container" if [ "$var_os" == "alpine" ]; then From 2db04e501d349761f29f8afa4d18ccc897590ba7 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 22 Jul 2025 10:43:56 +0200 Subject: [PATCH 0121/1733] Update linkstack-install.sh --- install/linkstack-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/linkstack-install.sh b/install/linkstack-install.sh index 81fb19bb1..95105bbf8 100644 --- a/install/linkstack-install.sh +++ b/install/linkstack-install.sh @@ -13,7 +13,7 @@ setting_up_container network_check update_os -PHP_VERSION="8.3" PHP_MODULE="sqlite3,mysql" PHP_APACHE="YES" setup_php +PHP_VERSION="8.3" PHP_MODULE="sqlite3" PHP_APACHE="YES" setup_php fetch_and_deploy_gh_release "linkstack" "linkstackorg/linkstack" "prebuild" "latest" "/var/www/html/linkstack" "linkstack.zip" msg_info "Configuring LinkStack" From 4c35a783ad58f624c2b8279cef96042e97a347e0 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 22 Jul 2025 10:48:56 +0200 Subject: [PATCH 0122/1733] test --- install/linkstack-install.sh | 2 +- misc/tools.func | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/install/linkstack-install.sh b/install/linkstack-install.sh index 95105bbf8..81fb19bb1 100644 --- a/install/linkstack-install.sh +++ b/install/linkstack-install.sh @@ -13,7 +13,7 @@ setting_up_container network_check update_os -PHP_VERSION="8.3" PHP_MODULE="sqlite3" PHP_APACHE="YES" setup_php +PHP_VERSION="8.3" PHP_MODULE="sqlite3,mysql" PHP_APACHE="YES" setup_php fetch_and_deploy_gh_release "linkstack" "linkstackorg/linkstack" "prebuild" "latest" "/var/www/html/linkstack" "linkstack.zip" msg_info "Configuring LinkStack" diff --git a/misc/tools.func b/misc/tools.func index 89d17102a..54773b300 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -460,7 +460,7 @@ function setup_php() { MODULE_LIST+=" php${PHP_VERSION}-fpm" fi if [[ "$PHP_APACHE" == "YES" ]]; then - $STD apt-get install -y apache2 + $STD apt-get install -y apache2 libapache2-mod-php${PHP_VERSION} $STD systemctl restart apache2 || true fi From 96351e0441c979718f76e51cac73f8559c229014 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 22 Jul 2025 10:52:35 +0200 Subject: [PATCH 0123/1733] Update teamspeak-server-install.sh --- install/teamspeak-server-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/teamspeak-server-install.sh b/install/teamspeak-server-install.sh index 8fb45f2ce..cee27f4a0 100644 --- a/install/teamspeak-server-install.sh +++ b/install/teamspeak-server-install.sh @@ -13,7 +13,7 @@ setting_up_container network_check update_os -RELEASE=$(curl -fsSL https://teamspeak.com/en/downloads/#server | grep -oP 'teamspeak3-server_linux_amd64-\K[0-9]+\.[0-9]+\.[0-9]+' | head -1) +RELEASE=$(curl -fsSL https://teamspeak.com/en/downloads/#server | grep -Eo 'teamspeak3-server_linux_amd64-[0-9]+\.[0-9]+\.[0-9]+' | head -n1 | sed 's/teamspeak3-server_linux_amd64-//') msg_info "Setting up Teamspeak Server" curl -fsSL "https://files.teamspeak-services.com/releases/server/${RELEASE}/teamspeak3-server_linux_amd64-${RELEASE}.tar.bz2" -o ts3server.tar.bz2 From 9c92bced1108793e5eed078a55450995178e0aad Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 22 Jul 2025 10:57:21 +0200 Subject: [PATCH 0124/1733] Update tools.func --- misc/tools.func | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 54773b300..df5934cc7 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -465,8 +465,10 @@ function setup_php() { fi if [[ "$PHP_APACHE" == "YES" ]] && [[ -n "$CURRENT_PHP" ]]; then - if [[ -f /etc/apache2/mods-enabled/php${CURRENT_PHP}.load ]]; then - $STD a2dismod php"${CURRENT_PHP}" || true + if [[ "$CURRENT_PHP" != "$PHP_VERSION" ]]; then + if [[ -f /etc/apache2/mods-enabled/php${CURRENT_PHP}.load ]]; then + $STD a2dismod php"${CURRENT_PHP}" || true + fi fi fi From 03dcdfb2fbd777dbad08846fa0a478ffb5068b44 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 22 Jul 2025 11:05:24 +0200 Subject: [PATCH 0125/1733] Update build.func --- misc/build.func | 174 ++++++++++++++++++++++++++---------------------- 1 file changed, 95 insertions(+), 79 deletions(-) diff --git a/misc/build.func b/misc/build.func index 4d8e759a3..61ba50dc3 100644 --- a/misc/build.func +++ b/misc/build.func @@ -998,95 +998,111 @@ install_script() { if systemctl is-active -q ping-instances.service; then systemctl -q stop ping-instances.service fi + NEXTID=$(pvesh get /cluster/nextid) timezone=$(cat /etc/timezone) header_info - while true; do - TMP_CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "SETTINGS" \ - --menu "Choose an option:" 20 60 7 \ - "1" "Default Settings" \ - "2" "Default Settings (with verbose)" \ - "3" "Advanced Settings" \ - "4" "Use Config File" \ - "5" "Manage Default Storage" \ - "6" "Diagnostic Settings" \ - "7" "Exit" \ - --default-item "1" 3>&1 1>&2 2>&3) || true - - if [ -z "$TMP_CHOICE" ]; then - echo -e "\n${CROSS}${RD}Menu canceled. Exiting script.${CL}\n" - exit 0 - fi - - CHOICE="$TMP_CHOICE" - - case $CHOICE in - 1) - header_info - echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME${CL}" - VERBOSE="no" - METHOD="default" - base_settings "$VERBOSE" - echo_default - break + # --- PRESET support --- + if [ -n "${PRESET:-}" ]; then + case "$PRESET" in + DEFAULT | default | 1) + CHOICE="1" ;; - 2) - header_info - echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME (${VERBOSE_CROPPED}Verbose)${CL}" - VERBOSE="yes" - METHOD="default" - base_settings "$VERBOSE" - echo_default - break + VERBOSE | verbose | 2) + CHOICE="2" ;; - 3) - header_info - echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings on node $PVEHOST_NAME${CL}" - METHOD="advanced" - base_settings - advanced_settings - break - ;; - 4) - header_info - echo -e "${INFO}${HOLD} ${GN}Using Config File on node $PVEHOST_NAME${CL}" - METHOD="advanced" - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/config-file.func) - config_file - break - ;; - - 5) - manage_default_storage - ;; - 6) - if [[ $DIAGNOSTICS == "yes" ]]; then - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS SETTINGS" --yesno "Send Diagnostics of LXC Installation?\n\nCurrent setting: ${DIAGNOSTICS}" 10 58 \ - --yes-button "No" --no-button "Back"; then - DIAGNOSTICS="no" - sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=no/' /usr/local/community-scripts/diagnostics - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS SETTINGS" --msgbox "Diagnostics settings changed to ${DIAGNOSTICS}." 8 58 - fi - else - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS SETTINGS" --yesno "Send Diagnostics of LXC Installation?\n\nCurrent setting: ${DIAGNOSTICS}" 10 58 \ - --yes-button "Yes" --no-button "Back"; then - DIAGNOSTICS="yes" - sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=yes/' /usr/local/community-scripts/diagnostics - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS SETTINGS" --msgbox "Diagnostics settings changed to ${DIAGNOSTICS}." 8 58 - fi - fi - ;; - 7) - echo -e "\n${CROSS}${RD}Script terminated. Have a great day!${CL}\n" - exit 0 + ADVANCED | advanced | 3) + CHOICE="3" ;; *) - echo -e "${CROSS}${RD}Invalid option, please try again.${CL}" + echo -e "\n${CROSS}${RD}Invalid PRESET value: ${PRESET}${CL}\n" + exit 1 ;; esac - done + else + while true; do + TMP_CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "SETTINGS" \ + --menu "Choose an option:" 20 60 7 \ + "1" "Default Settings" \ + "2" "Default Settings (with verbose)" \ + "3" "Advanced Settings" \ + "4" "Use Config File" \ + "5" "Manage Default Storage" \ + "6" "Diagnostic Settings" \ + "7" "Exit" \ + --default-item "1" 3>&1 1>&2 2>&3) || true + + if [ -z "$TMP_CHOICE" ]; then + echo -e "\n${CROSS}${RD}Menu canceled. Exiting script.${CL}\n" + exit 0 + fi + + CHOICE="$TMP_CHOICE" + break + done + fi + + case $CHOICE in + 1) + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME${CL}" + VERBOSE="no" + METHOD="default" + base_settings "$VERBOSE" + echo_default + ;; + 2) + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME (${VERBOSE_CROPPED}Verbose)${CL}" + VERBOSE="yes" + METHOD="default" + base_settings "$VERBOSE" + echo_default + ;; + 3) + header_info + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings on node $PVEHOST_NAME${CL}" + METHOD="advanced" + base_settings + advanced_settings + ;; + 4) + header_info + echo -e "${INFO}${HOLD} ${GN}Using Config File on node $PVEHOST_NAME${CL}" + METHOD="advanced" + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/config-file.func) + config_file + ;; + 5) + manage_default_storage + ;; + 6) + if [[ $DIAGNOSTICS == "yes" ]]; then + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS SETTINGS" --yesno "Send Diagnostics of LXC Installation?\n\nCurrent setting: ${DIAGNOSTICS}" 10 58 \ + --yes-button "No" --no-button "Back"; then + DIAGNOSTICS="no" + sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=no/' /usr/local/community-scripts/diagnostics + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS SETTINGS" --msgbox "Diagnostics settings changed to ${DIAGNOSTICS}." 8 58 + fi + else + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS SETTINGS" --yesno "Send Diagnostics of LXC Installation?\n\nCurrent setting: ${DIAGNOSTICS}" 10 58 \ + --yes-button "Yes" --no-button "Back"; then + DIAGNOSTICS="yes" + sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=yes/' /usr/local/community-scripts/diagnostics + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS SETTINGS" --msgbox "Diagnostics settings changed to ${DIAGNOSTICS}." 8 58 + fi + fi + ;; + 7) + echo -e "\n${CROSS}${RD}Script terminated. Have a great day!${CL}\n" + exit 0 + ;; + *) + echo -e "${CROSS}${RD}Invalid option, please try again.${CL}" + ;; + esac } check_container_resources() { From 2000987e247e20980cfbb917f904b9604272c15f Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 22 Jul 2025 11:21:35 +0200 Subject: [PATCH 0126/1733] Update tools.func --- misc/tools.func | 64 +++++++++++++++++++++---------------------------- 1 file changed, 27 insertions(+), 37 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index df5934cc7..6d932e03c 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -416,9 +416,10 @@ function setup_php() { COMBINED_MODULES="${DEFAULT_MODULES}" fi - # Deduplicate modules + # Deduplicate COMBINED_MODULES=$(echo "$COMBINED_MODULES" | tr ',' '\n' | awk '!seen[$0]++' | paste -sd, -) + # Aktuelle PHP-CLI-Version ermitteln local CURRENT_PHP="" if command -v php >/dev/null 2>&1; then CURRENT_PHP=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2) @@ -440,13 +441,8 @@ function setup_php() { $STD apt-get update fi + # Modul-Liste bauen local MODULE_LIST="php${PHP_VERSION}" - for pkg in $MODULE_LIST; do - if ! apt-cache show "$pkg" >/dev/null 2>&1; then - msg_error "Package not found: $pkg" - exit 1 - fi - done IFS=',' read -ra MODULES <<<"$COMBINED_MODULES" for mod in "${MODULES[@]}"; do if apt-cache show "php${PHP_VERSION}-${mod}" >/dev/null 2>&1; then @@ -455,51 +451,34 @@ function setup_php() { msg_warn "PHP-Module ${mod} for PHP ${PHP_VERSION} not found – skipping" fi done - if [[ "$PHP_FPM" == "YES" ]]; then MODULE_LIST+=" php${PHP_VERSION}-fpm" fi - if [[ "$PHP_APACHE" == "YES" ]]; then - $STD apt-get install -y apache2 libapache2-mod-php${PHP_VERSION} - $STD systemctl restart apache2 || true - fi - if [[ "$PHP_APACHE" == "YES" ]] && [[ -n "$CURRENT_PHP" ]]; then - if [[ "$CURRENT_PHP" != "$PHP_VERSION" ]]; then - if [[ -f /etc/apache2/mods-enabled/php${CURRENT_PHP}.load ]]; then - $STD a2dismod php"${CURRENT_PHP}" || true - fi + # Apache + PHP-Modul nur installieren, wenn noch nicht vorhanden + if [[ "$PHP_APACHE" == "YES" ]]; then + if ! dpkg -l | grep -q "libapache2-mod-php${PHP_VERSION}"; then + msg_info "Installing Apache with PHP${PHP_VERSION} support" + $STD apt-get install -y apache2 libapache2-mod-php${PHP_VERSION} + else + msg_info "Apache with PHP${PHP_VERSION} already installed – skipping install" fi fi - if [[ "$PHP_FPM" == "YES" ]] && [[ -n "$CURRENT_PHP" ]]; then + # setup / update PHP modules + $STD apt-get install -y $MODULE_LIST + msg_ok "Setup PHP $PHP_VERSION" + + # optional stop old PHP-FPM service + if [[ "$PHP_FPM" == "YES" && -n "$CURRENT_PHP" && "$CURRENT_PHP" != "$PHP_VERSION" ]]; then $STD systemctl stop php"${CURRENT_PHP}"-fpm || true $STD systemctl disable php"${CURRENT_PHP}"-fpm || true fi - if [[ "$PHP_APACHE" == "YES" ]]; then - if [[ -f /etc/apache2/mods-enabled/php${PHP_VERSION}.load ]]; then - $STD a2enmod php"${PHP_VERSION}" - else - $STD a2enmod php"${PHP_VERSION}" - fi - $STD systemctl restart apache2 || true - fi - - if [[ "$PHP_FPM" == "YES" ]]; then - if systemctl list-unit-files | grep -q "php${PHP_VERSION}-fpm.service"; then - $STD systemctl enable php${PHP_VERSION}-fpm - $STD systemctl restart php${PHP_VERSION}-fpm - else - msg_warn "FPM requested but service php${PHP_VERSION}-fpm not found" - fi - fi - # Patch all relevant php.ini files local PHP_INI_PATHS=("/etc/php/${PHP_VERSION}/cli/php.ini") [[ "$PHP_FPM" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/fpm/php.ini") [[ "$PHP_APACHE" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/apache2/php.ini") - for ini in "${PHP_INI_PATHS[@]}"; do if [[ -f "$ini" ]]; then $STD msg_info "Patching $ini" @@ -518,9 +497,20 @@ function setup_php() { $STD a2dismod "$mod" || true fi done + $STD a2enmod mpm_prefork $STD a2enmod "php${PHP_VERSION}" $STD systemctl restart apache2 || true fi + + # FPM aktivieren, falls gewünscht + if [[ "$PHP_FPM" == "YES" ]]; then + if systemctl list-unit-files | grep -q "php${PHP_VERSION}-fpm.service"; then + $STD systemctl enable php${PHP_VERSION}-fpm + $STD systemctl restart php${PHP_VERSION}-fpm + else + msg_warn "FPM requested but service php${PHP_VERSION}-fpm not found" + fi + fi } # ------------------------------------------------------------------------------ From b93f8ce5febca0eb816349f442d02564703095a2 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 22 Jul 2025 11:56:25 +0200 Subject: [PATCH 0127/1733] logging --- misc/build.func | 2 +- misc/tools.func | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/misc/build.func b/misc/build.func index 61ba50dc3..e9247b9de 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1438,7 +1438,7 @@ EOF' msg_ok "Customized LXC Container" if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/"$var_install".sh)"; then - return + exit $? fi } diff --git a/misc/tools.func b/misc/tools.func index 6d932e03c..dace8ca73 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -419,7 +419,7 @@ function setup_php() { # Deduplicate COMBINED_MODULES=$(echo "$COMBINED_MODULES" | tr ',' '\n' | awk '!seen[$0]++' | paste -sd, -) - # Aktuelle PHP-CLI-Version ermitteln + # Get current PHP-CLI version local CURRENT_PHP="" if command -v php >/dev/null 2>&1; then CURRENT_PHP=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2) @@ -441,7 +441,7 @@ function setup_php() { $STD apt-get update fi - # Modul-Liste bauen + # Build module list local MODULE_LIST="php${PHP_VERSION}" IFS=',' read -ra MODULES <<<"$COMBINED_MODULES" for mod in "${MODULES[@]}"; do @@ -455,7 +455,7 @@ function setup_php() { MODULE_LIST+=" php${PHP_VERSION}-fpm" fi - # Apache + PHP-Modul nur installieren, wenn noch nicht vorhanden + # install apache2 with PHP support if requested if [[ "$PHP_APACHE" == "YES" ]]; then if ! dpkg -l | grep -q "libapache2-mod-php${PHP_VERSION}"; then msg_info "Installing Apache with PHP${PHP_VERSION} support" @@ -502,7 +502,7 @@ function setup_php() { $STD systemctl restart apache2 || true fi - # FPM aktivieren, falls gewünscht + # enable and restart PHP-FPM if requested if [[ "$PHP_FPM" == "YES" ]]; then if systemctl list-unit-files | grep -q "php${PHP_VERSION}-fpm.service"; then $STD systemctl enable php${PHP_VERSION}-fpm From cd1b676097f20a0d7408661646ef7d9af7fb4847 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 22 Jul 2025 11:56:49 +0200 Subject: [PATCH 0128/1733] Update linkstack-install.sh --- install/linkstack-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/linkstack-install.sh b/install/linkstack-install.sh index 81fb19bb1..8801b2c3c 100644 --- a/install/linkstack-install.sh +++ b/install/linkstack-install.sh @@ -13,7 +13,7 @@ setting_up_container network_check update_os -PHP_VERSION="8.3" PHP_MODULE="sqlite3,mysql" PHP_APACHE="YES" setup_php +PHP_VERSION="8.3" PHP_MODULE="sqlite3,mysql,ronny" PHP_APACHE="YES" setup_php fetch_and_deploy_gh_release "linkstack" "linkstackorg/linkstack" "prebuild" "latest" "/var/www/html/linkstack" "linkstack.zip" msg_info "Configuring LinkStack" From 277d94584c15a605a522876452f3324d183b50dc Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 22 Jul 2025 12:16:35 +0200 Subject: [PATCH 0129/1733] Update maxun-install.sh --- install/maxun-install.sh | 94 ++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 43 deletions(-) diff --git a/install/maxun-install.sh b/install/maxun-install.sh index f68515a1d..57239c76b 100644 --- a/install/maxun-install.sh +++ b/install/maxun-install.sh @@ -15,35 +15,32 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - gpg \ - openssl \ - redis \ - libgbm1 \ - libnss3 \ - libatk1.0-0 \ - libatk-bridge2.0-0 \ - libdrm2 \ - libxkbcommon0 \ - libglib2.0-0 \ - libdbus-1-3 \ - libx11-xcb1 \ - libxcb1 \ - libxcomposite1 \ - libxcursor1 \ - libxdamage1 \ - libxext6 \ - libxi6 \ - libxtst6 \ - ca-certificates \ - libxrandr2 \ - libasound2 \ - libxss1 \ - libxinerama1 \ - nginx + openssl \ + redis \ + libgbm1 \ + libnss3 \ + libatk1.0-0 \ + libatk-bridge2.0-0 \ + libdrm2 \ + libxkbcommon0 \ + libglib2.0-0 \ + libdbus-1-3 \ + libx11-xcb1 \ + libxcb1 \ + libxcomposite1 \ + libxcursor1 \ + libxdamage1 \ + libxext6 \ + libxi6 \ + libxtst6 \ + ca-certificates \ + libxrandr2 \ + libasound2 \ + libxss1 \ + libxinerama1 \ + nginx msg_ok "Installed Dependencies" -#configure_lxc "Semantic Search requires a dedicated GPU and at least 16GB RAM. Would you like to install it?" 100 "memory" "16000" - PG_VERSION=17 setup_postgresql NODE_VERSION="22" setup_nodejs @@ -56,6 +53,7 @@ MINIO_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) JWT_SECRET=$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | cut -c1-32) ENCRYPTION_KEY=$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | cut -c1-32) LOCAL_IP=$(hostname -I | awk '{print $1}') +SESSION_SECRET=$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | cut -c1-32) msg_ok "Set up Variables" msg_info "Setup Database" @@ -65,12 +63,13 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8' $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC'" { - echo "Maxun-Credentials" - echo "Maxun Database User: $DB_USER" - echo "Maxun Database Password: $DB_PASS" - echo "Maxun Database Name: $DB_NAME" - echo "Maxun JWT Secret: $JWT_SECRET" - echo "Maxun Encryption Key: $ENCRYPTION_KEY" + echo "Maxun-Credentials" + echo "Maxun Database User: $DB_USER" + echo "Maxun Database Password: $DB_PASS" + echo "Maxun Database Name: $DB_NAME" + echo "Maxun JWT Secret: $JWT_SECRET" + echo "Maxun Encryption Key: $ENCRYPTION_KEY" + echo "Maxun Session Secret: $SESSION_SECRET" } >>~/maxun.creds msg_ok "Set up Database" @@ -99,9 +98,9 @@ LimitNOFILE=65536 WantedBy=multi-user.target EOF { - echo "__________________" - echo "MinIO Admin User: $MINIO_USER" - echo "MinIO Admin Password: $MINIO_PASS" + echo "__________________" + echo "MinIO Admin User: $MINIO_USER" + echo "MinIO Admin Password: $MINIO_PASS" } >>~/maxun.creds cat </etc/default/minio MINIO_ROOT_USER=${MINIO_USER} @@ -110,8 +109,9 @@ EOF systemctl enable -q --now minio msg_ok "Setup MinIO" -msg_info "Installing Maxun (Patience)" fetch_and_deploy_gh_release "maxun" "getmaxun/maxun" "source" + +msg_info "Installing Maxun (Patience)" cat </opt/maxun/.env NODE_ENV=development JWT_SECRET=${JWT_SECRET} @@ -137,6 +137,7 @@ VITE_BACKEND_URL=http://${LOCAL_IP}:8080 VITE_PUBLIC_URL=http://${LOCAL_IP}:5173 MAXUN_TELEMETRY=false +SESSION_SECRET=${SESSION_SECRET} EOF cat <<'EOF' >/usr/local/bin/update-env-ip.sh @@ -162,19 +163,27 @@ msg_info "Setting up nginx with CORS Proxy" cat <<'EOF' >/etc/nginx/sites-available/maxun server { listen 80; + server_name _; + # Frontend ausliefern + root /usr/share/nginx/html; + index index.html; location / { - root /usr/share/nginx/html; try_files $uri $uri/ /index.html; } - location ~ ^/(api|record|workflow|storage|auth|integration|proxy|api-docs) { - proxy_pass http://localhost:8080; - proxy_set_header Host $host; + # Backend Proxy + location ~ ^/(auth|storage|record|workflow|robot|proxy|api-docs|api|webhook)(/|$) { + proxy_pass http://127.0.0.1:8080; + proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; - proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + # CORS add_header Access-Control-Allow-Origin "$http_origin" always; add_header Access-Control-Allow-Credentials true always; add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS always; @@ -193,7 +202,6 @@ server { } } EOF - ln -sf /etc/nginx/sites-available/maxun /etc/nginx/sites-enabled/maxun rm -f /etc/nginx/sites-enabled/default msg_ok "nginx with CORS Proxy set up" From bd3fb793b76304212145e3bbc5bec65f5f64d34b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 22 Jul 2025 12:21:12 +0200 Subject: [PATCH 0130/1733] Update create_lxc.sh --- misc/create_lxc.sh | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index d74ee9fa9..fe4fa8ec8 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -110,6 +110,19 @@ function select_storage() { ;; esac + # >>> NEW: support STORAGE preset <<< + if [ -n "${STORAGE:-}" ]; then + # validate the given storage + if pvesm status -content "$CONTENT" | awk 'NR>1 {print $1}' | grep -qx "$STORAGE"; then + STORAGE_RESULT="$STORAGE" + msg_info "Using preset storage: $STORAGE_RESULT for $CONTENT_LABEL" + return 0 + else + msg_error "Preset storage '$STORAGE' is not valid for content type '$CONTENT'." + return 2 + fi + fi + local -a MENU local -A STORAGE_MAP local COL_WIDTH=0 From 58695de2e09778d1bcd2c4e7587632569ac34cab Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 22 Jul 2025 12:21:41 +0200 Subject: [PATCH 0131/1733] Update maxun-install.sh --- install/maxun-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/maxun-install.sh b/install/maxun-install.sh index 57239c76b..e0caea03a 100644 --- a/install/maxun-install.sh +++ b/install/maxun-install.sh @@ -42,7 +42,7 @@ $STD apt-get install -y \ msg_ok "Installed Dependencies" PG_VERSION=17 setup_postgresql -NODE_VERSION="22" setup_nodejs +NODE_VERSION="18" setup_nodejs msg_info "Setup Variables" DB_NAME=maxun_db From e33b0a93b18f5412d4286b05d570a0c003291306 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 22 Jul 2025 12:23:37 +0200 Subject: [PATCH 0132/1733] Update create_lxc.sh --- misc/create_lxc.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index fe4fa8ec8..b7d1f46e0 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -111,8 +111,7 @@ function select_storage() { esac # >>> NEW: support STORAGE preset <<< - if [ -n "${STORAGE:-}" ]; then - # validate the given storage + if [ "$CONTENT" = "rootdir" ] && [ -n "${STORAGE:-}" ]; then if pvesm status -content "$CONTENT" | awk 'NR>1 {print $1}' | grep -qx "$STORAGE"; then STORAGE_RESULT="$STORAGE" msg_info "Using preset storage: $STORAGE_RESULT for $CONTENT_LABEL" @@ -122,7 +121,6 @@ function select_storage() { return 2 fi fi - local -a MENU local -A STORAGE_MAP local COL_WIDTH=0 From 97c13e8b7ced5442c2f999e724593166f8059af9 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 22 Jul 2025 12:29:34 +0200 Subject: [PATCH 0133/1733] Update leantime-install.sh --- install/leantime-install.sh | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/install/leantime-install.sh b/install/leantime-install.sh index cd577289c..026bc2cba 100644 --- a/install/leantime-install.sh +++ b/install/leantime-install.sh @@ -13,29 +13,10 @@ setting_up_container network_check update_os -PHP_VERSION=8.4 -PHP_MODULE=mysql -PHP_APACHE=YES -PHP_FPM=YES - -msg_info "Installing Apache2" -$STD apt-get install -y \ - apache2 -msg_ok "Installed Apache2" - -setup_php - -msg_info "Installing Apache2 mod for PHP" -$STD apt-get install -y \ - "libapache2-mod-php${PHP_VERSION}" -msg_ok "Installed Apache2 mod" - +PHP_VERSION=8.4 PHP_MODULE="mysql" PHP_APACHE="YES" PHP_FPM="YES" setup_php setup_mariadb -msg_ok "Installed Dependencies" - msg_info "Setting up Database" -systemctl enable -q --now mariadb DB_NAME=leantime DB_USER=leantime DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) @@ -50,9 +31,10 @@ $STD mysql -u root -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH } >>~/"$APPLICATION".creds msg_ok "Set up Database" +fetch_and_deploy_gh_release "leantime" "Leantime/leantime" "prebuild" "latest" "/opt/leantime" Leantime*.tar.gz + msg_info "Setup ${APPLICATION}" APACHE_LOG_DIR=/var/log/apache2 -fetch_and_deploy_gh_release "$APPLICATION" "Leantime/leantime" "prebuild" "latest" "/opt/${APPLICATION}" Leantime-v[0-9].[0-9].[0-9].tar.gz chown -R www-data:www-data "/opt/${APPLICATION}" chmod -R 750 "/opt/${APPLICATION}" From 5c147d9841fa0bbbc80fda7b5f19411c9f52821a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 22 Jul 2025 12:57:59 +0200 Subject: [PATCH 0134/1733] Update leantime-install.sh --- install/leantime-install.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/install/leantime-install.sh b/install/leantime-install.sh index 026bc2cba..ff3580100 100644 --- a/install/leantime-install.sh +++ b/install/leantime-install.sh @@ -35,17 +35,17 @@ fetch_and_deploy_gh_release "leantime" "Leantime/leantime" "prebuild" "latest" " msg_info "Setup ${APPLICATION}" APACHE_LOG_DIR=/var/log/apache2 -chown -R www-data:www-data "/opt/${APPLICATION}" -chmod -R 750 "/opt/${APPLICATION}" +chown -R www-data:www-data "/opt/leantime" +chmod -R 750 "/opt/leantime" cat </etc/apache2/sites-enabled/000-default.conf ServerAdmin webmaster@localhost - DocumentRoot /opt/${APPLICATION}/public + DocumentRoot /opt/leantime/public DirectoryIndex index.php index.html index.cgi index.pl index.xhtml Options +ExecCGI - + Options FollowSymLinks Require all granted AllowOverride All @@ -60,12 +60,12 @@ cat </etc/apache2/sites-enabled/000-default.conf EOF -mv "/opt/${APPLICATION}/config/sample.env" "/opt/${APPLICATION}/config/.env" +mv "/opt/leantime/config/sample.env" "/opt/leantime/config/.env" sed -i -e "s|^LEAN_DB_DATABASE.*|LEAN_DB_DATABASE = '$DB_NAME'|" \ -e "s|^LEAN_DB_USER.*|LEAN_DB_USER = '$DB_USER'|" \ -e "s|^LEAN_DB_PASSWORD.*|LEAN_DB_PASSWORD = '$DB_PASS'|" \ -e "s|^LEAN_SESSION_PASSWORD.*|LEAN_SESSION_PASSWORD = '$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13)'|" \ - "/opt/${APPLICATION}/config/.env" + "/opt/leantime/config/.env" a2enmod -q proxy_fcgi setenvif rewrite a2enconf -q "php${PHP_VERSION}-fpm" From a4a8fab2fa7480a875f9455f4df48f13f603fe90 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 12:02:57 +0000 Subject: [PATCH 0135/1733] Bump form-data in /frontend in the npm_and_yarn group across 1 directory Bumps the npm_and_yarn group with 1 update in the /frontend directory: [form-data](https://github.com/form-data/form-data). Updates `form-data` from 4.0.1 to 4.0.4 - [Release notes](https://github.com/form-data/form-data/releases) - [Changelog](https://github.com/form-data/form-data/blob/master/CHANGELOG.md) - [Commits](https://github.com/form-data/form-data/compare/v4.0.1...v4.0.4) --- updated-dependencies: - dependency-name: form-data dependency-version: 4.0.4 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 120 +++++++++++++++++++++++++++---------- 1 file changed, 89 insertions(+), 31 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 9c8b7c97a..fe7ecb632 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -4082,6 +4082,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -4694,6 +4708,21 @@ "dev": true, "license": "MIT" }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -4804,14 +4833,11 @@ } }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "dev": true, "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, "engines": { "node": ">= 0.4" } @@ -4860,9 +4886,9 @@ "license": "MIT" }, "node_modules/es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "dev": true, "license": "MIT", "dependencies": { @@ -4873,15 +4899,16 @@ } }, "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dev": true, "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.4", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -5728,14 +5755,16 @@ } }, "node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -5851,17 +5880,22 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dev": true, "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -5879,6 +5913,20 @@ "node": ">=6" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-symbol-description": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", @@ -5982,13 +6030,13 @@ "license": "MIT" }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "dev": true, "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.1.3" + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6055,9 +6103,9 @@ } }, "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, "license": "MIT", "engines": { @@ -7037,6 +7085,16 @@ "@jridgewell/sourcemap-codec": "^1.5.0" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", From 042ef3736536f3bae6b1d00b8966d0e758129ebc Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 22 Jul 2025 15:59:29 +0200 Subject: [PATCH 0136/1733] . --- ct/salt.sh | 50 ----------------------- ct/teamspeak-server.sh | 61 ----------------------------- install/salt-install.sh | 44 --------------------- install/teamspeak-server-install.sh | 56 -------------------------- 4 files changed, 211 deletions(-) delete mode 100644 ct/salt.sh delete mode 100644 ct/teamspeak-server.sh delete mode 100644 install/salt-install.sh delete mode 100644 install/teamspeak-server-install.sh diff --git a/ct/salt.sh b/ct/salt.sh deleted file mode 100644 index c8a6cec70..000000000 --- a/ct/salt.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env bash -source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: bvdberg01 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/saltstack/salt - -APP="Salt" -var_tags="${var_tags:-automations}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-1024}" -var_disk="${var_disk:-3}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - - if [[ ! -d /etc/salt ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - RELEASE=$(curl -fsSL https://api.github.com/repos/saltstack/salt/releases/latest | jq -r .tag_name | sed 's/^v//') - if [[ ! -f /~.salt ]] || [[ "${RELEASE}" != "$(cat /~.salt)" ]]; then - msg_info "Updating $APP to ${RELEASE}" - sed -i "s/^\(Pin: version \).*/\1${RELEASE}/" /etc/apt/preferences.d/salt-pin-1001 - $STD apt-get update - $STD apt-get upgrade -y - echo "${RELEASE}" >/~.salt - msg_ok "Updated ${APP} to ${RELEASE}" - else - msg_ok "${APP} is already up to date (${RELEASE})" - fi -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" diff --git a/ct/teamspeak-server.sh b/ct/teamspeak-server.sh deleted file mode 100644 index 55f51b852..000000000 --- a/ct/teamspeak-server.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env bash -source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: tremor021 (Slaviša Arežina) -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://teamspeak.com/en/ - -APP="Teamspeak-Server" -var_tags="${var_tags:-voice;communication}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-512}" -var_disk="${var_disk:-2}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/teamspeak-server ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - RELEASE=$(curl -fsSL https://teamspeak.com/en/downloads/#server | grep -oP 'teamspeak3-server_linux_amd64-\K[0-9]+\.[0-9]+\.[0-9]+' | head -1) - if [[ "${RELEASE}" != "$(cat ~/.teamspeak-server 2>/dev/null)" ]] || [[ ! -f ~/.teamspeak-server ]]; then - msg_info "Stopping Service" - systemctl stop teamspeak-server - msg_ok "Stopped Service" - - msg_info "Updating ${APP}" - curl -fsSL "https://files.teamspeak-services.com/releases/server/${RELEASE}/teamspeak3-server_linux_amd64-${RELEASE}.tar.bz2" -o ts3server.tar.bz2 - tar -xf ./ts3server.tar.bz2 - cp -ru teamspeak3-server_linux_amd64/* /opt/teamspeak-server/ - msg_ok "Updated $APP" - - msg_info "Starting Service" - systemctl start teamspeak-server - msg_ok "Started Service" - - msg_ok "Updated Successfully" - else - msg_ok "Already up to date" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}${IP}:9987${CL}" diff --git a/install/salt-install.sh b/install/salt-install.sh deleted file mode 100644 index 4fe29a824..000000000 --- a/install/salt-install.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: bvdberg01 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/saltstack/salt - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt-get install -y jq -msg_ok "Installed Dependencies" - -msg_info "Setup Salt Repo" -mkdir -p /etc/apt/keyrings -curl -fsSL https://packages.broadcom.com/artifactory/api/security/keypair/SaltProjectKey/public -o /etc/apt/keyrings/salt-archive-keyring.pgp -curl -fsSL https://github.com/saltstack/salt-install-guide/releases/latest/download/salt.sources -o /etc/apt/sources.list.d/salt.sources -$STD apt-get update -msg_ok "Setup Salt Repo" - -msg_info "Installing Salt" -RELEASE=$(curl -fsSL https://api.github.com/repos/saltstack/salt/releases/latest | jq -r .tag_name | sed 's/^v//') -cat </etc/apt/preferences.d/salt-pin-1001 -Package: salt-* -Pin: version ${RELEASE} -Pin-Priority: 1001 -EOF -$STD apt-get install -y salt-master -echo "${RELEASE}" >/~.salt -msg_ok "Installed Salt" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" diff --git a/install/teamspeak-server-install.sh b/install/teamspeak-server-install.sh deleted file mode 100644 index cee27f4a0..000000000 --- a/install/teamspeak-server-install.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: tremor021 (Slaviša Arežina) -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://teamspeak.com/en/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -RELEASE=$(curl -fsSL https://teamspeak.com/en/downloads/#server | grep -Eo 'teamspeak3-server_linux_amd64-[0-9]+\.[0-9]+\.[0-9]+' | head -n1 | sed 's/teamspeak3-server_linux_amd64-//') - -msg_info "Setting up Teamspeak Server" -curl -fsSL "https://files.teamspeak-services.com/releases/server/${RELEASE}/teamspeak3-server_linux_amd64-${RELEASE}.tar.bz2" -o ts3server.tar.bz2 -tar -xf ./ts3server.tar.bz2 -mv teamspeak3-server_linux_amd64/ /opt/teamspeak-server/ -touch /opt/teamspeak-server/.ts3server_license_accepted -echo "${RELEASE}" >~/.teamspeak-server -msg_ok "Setup Teamspeak Server" - -msg_info "Creating service" -cat </etc/systemd/system/teamspeak-server.service -[Unit] -Description=TeamSpeak3 Server -Wants=network-online.target -After=network.target - -[Service] -WorkingDirectory=/opt/teamspeak-server -User=root -Type=forking -ExecStart=/opt/teamspeak-server/ts3server_startscript.sh start -ExecStop=/opt/teamspeak-server/ts3server_startscript.sh stop -ExecReload=/opt/teamspeak-server/ts3server_startscript.sh restart -Restart=always -RestartSec=15 - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now teamspeak-server -msg_ok "Created service" - -motd_ssh -customize - -msg_info "Cleaning up" -rm -f ~/ts3server.tar.bz* -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" From 5ceae638b56a94a4d0fa0173ecbb429fba2c8eb2 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 22 Jul 2025 13:59:54 +0000 Subject: [PATCH 0137/1733] Update .app files --- ct/headers/salt | 6 ------ ct/headers/teamspeak-server | 6 ------ 2 files changed, 12 deletions(-) delete mode 100644 ct/headers/salt delete mode 100644 ct/headers/teamspeak-server diff --git a/ct/headers/salt b/ct/headers/salt deleted file mode 100644 index 3b11949de..000000000 --- a/ct/headers/salt +++ /dev/null @@ -1,6 +0,0 @@ - _____ ____ - / ___/____ _/ / /_ - \__ \/ __ `/ / __/ - ___/ / /_/ / / /_ -/____/\__,_/_/\__/ - diff --git a/ct/headers/teamspeak-server b/ct/headers/teamspeak-server deleted file mode 100644 index 92c3a8e42..000000000 --- a/ct/headers/teamspeak-server +++ /dev/null @@ -1,6 +0,0 @@ - ______ __ _____ - /_ __/__ ____ _____ ___ _________ ___ ____ _/ /__ / ___/___ ______ _____ _____ - / / / _ \/ __ `/ __ `__ \/ ___/ __ \/ _ \/ __ `/ //_/_____\__ \/ _ \/ ___/ | / / _ \/ ___/ - / / / __/ /_/ / / / / / (__ ) /_/ / __/ /_/ / ,< /_____/__/ / __/ / | |/ / __/ / -/_/ \___/\__,_/_/ /_/ /_/____/ .___/\___/\__,_/_/|_| /____/\___/_/ |___/\___/_/ - /_/ From e3cfe521109661a7932c0541d9ed3c81c814f7f8 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Tue, 22 Jul 2025 19:40:33 -0400 Subject: [PATCH 0138/1733] Update tududi --- frontend/public/json/tududi.json | 6 +++++- install/tududi-install.sh | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/frontend/public/json/tududi.json b/frontend/public/json/tududi.json index fd884ef59..a82ec5810 100644 --- a/frontend/public/json/tududi.json +++ b/frontend/public/json/tududi.json @@ -4,7 +4,7 @@ "categories": [ 12 ], - "date_created": "2025-07-07", + "date_created": "2025-07-22", "type": "ct", "updateable": true, "privileged": false, @@ -35,6 +35,10 @@ { "text": "Create users like this: `cd /opt/tududi` => `npm run user:create `", "type": "info" + }, + { + "text": "Database location: `/opt/tududi-db`. Uploads: `/opt/tududi-uploads`", + "type": "info" } ] } diff --git a/install/tududi-install.sh b/install/tududi-install.sh index 17cd07a03..17c73099d 100644 --- a/install/tududi-install.sh +++ b/install/tududi-install.sh @@ -41,7 +41,7 @@ sed -e 's/^GOOGLE/# &/' \ -e "s/your_session_secret_here/$SECRET/" \ -e 's/development/production/' \ -e "\$a\DB_FILE=$DB_LOCATION/production.sqlite3" \ - -e "\$a\UPLOAD_LOCATION=$UPLOAD_DIR" \ + -e "\$a\TUDUDI_UPLOAD_PATH=$UPLOAD_DIR" \ /opt/tududi/backend/.env.example >/opt/tududi/backend/.env export DB_FILE="$DB_LOCATION/production.sqlite3" $STD npm run db:init From 9111c3253ace396785b10052ea296ef7a7843b31 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 23 Jul 2025 08:25:30 +0200 Subject: [PATCH 0139/1733] k --- .vscode/settings.json | 2 +- install/manyfold-install.sh | 37 +++++++++++++++++++------------------ 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 47009d646..8f17c7ff9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -18,7 +18,7 @@ "editor.minimap.enabled": false, "terminal.integrated.scrollback": 10000, "[shellscript]": { - "editor.defaultFormatter": "mads-hartmann.bash-ide-vscode", + "editor.defaultFormatter": "foxundermoon.shell-format", "editor.tabSize": 4, "editor.insertSpaces": true, }, diff --git a/install/manyfold-install.sh b/install/manyfold-install.sh index e4dcbf06a..14861fbb7 100644 --- a/install/manyfold-install.sh +++ b/install/manyfold-install.sh @@ -14,33 +14,34 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - curl \ - sudo \ - mc \ - gnupg2 postgresql \ - lsb-release \ - rbenv \ - libpq-dev \ - libarchive-dev \ - git \ - libmariadb-dev \ - redis-server \ - nginx \ - libffi-dev \ - libyaml-dev + lsb-release \ + rbenv \ + libpq-dev \ + libarchive-dev \ + git \ + libmariadb-dev \ + redis-server \ + nginx \ + libffi-dev \ + libyaml-dev msg_ok "Installed Dependencies" +PG_VERSION="16" setup_postgresql + msg_info "Setting up PostgreSQL" DB_NAME=manyfold DB_USER=manyfold DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) $STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER TEMPLATE template0;" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" { - echo "Manyfold Credentials" - echo "Manyfold Database User: $DB_USER" - echo "Manyfold Database Password: $DB_PASS" - echo "Manyfold Database Name: $DB_NAME" + echo "Manyfold Credentials" + echo "Manyfold Database User: $DB_USER" + echo "Manyfold Database Password: $DB_PASS" + echo "Manyfold Database Name: $DB_NAME" } >>~/manyfold.creds msg_ok "Set up PostgreSQL" From 172443653573c8159ece70ba5a61162b20463253 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 23 Jul 2025 08:27:12 +0200 Subject: [PATCH 0140/1733] Update manyfold-install.sh --- install/manyfold-install.sh | 31 +++---------------------------- 1 file changed, 3 insertions(+), 28 deletions(-) diff --git a/install/manyfold-install.sh b/install/manyfold-install.sh index 14861fbb7..5f4895e12 100644 --- a/install/manyfold-install.sh +++ b/install/manyfold-install.sh @@ -45,37 +45,14 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" } >>~/manyfold.creds msg_ok "Set up PostgreSQL" -msg_info "Downloading Manyfold" -RELEASE=$(curl -fsSL https://api.github.com/repos/manyfold3d/manyfold/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') -cd /opt -curl -fsSL "https://github.com/manyfold3d/manyfold/archive/refs/tags/v${RELEASE}.zip" -o manyfold.zip -unzip -q manyfold.zip -mv /opt/manyfold-${RELEASE}/ /opt/manyfold +fetch_and_deploy_gh_release "manyfold" "manyfold3d/manyfold" "tarball" "latest" "/opt/manyfold" + RUBY_INSTALL_VERSION=$(cat /opt/manyfold/.ruby-version) YARN_VERSION=$(grep '"packageManager":' /opt/manyfold/package.json | sed -E 's/.*"(yarn@[0-9\.]+)".*/\1/') -msg_ok "Downloaded Manyfold" - -NODE_VERSION="22" NODE_MODULE="npm@latest,${YARN_VERSION}" setup_nodejs +NODE_VERSION="22" NODE_MODULE="${YARN_VERSION}" setup_nodejs RUBY_VERSION=${RUBY_INSTALL_VERSION} RUBY_INSTALL_RAILS="true" setup_rbenv_stack -# msg_info "Add ruby-build" -# mkdir -p ~/.rbenv/plugins -# cd ~/.rbenv/plugins -# RUBY_BUILD_RELEASE=$(curl -s https://api.github.com/repos/rbenv/ruby-build/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') -# curl -fsSL "https://github.com/rbenv/ruby-build/archive/refs/tags/v${RUBY_BUILD_RELEASE}.zip" -o ruby-build.zip -# unzip -q ruby-build.zip -# mv ruby-build-* ~/.rbenv/plugins/ruby-build -# echo "${RUBY_BUILD_RELEASE}" >~/.rbenv/plugins/RUBY_BUILD_version.txt -# msg_ok "Added ruby-build" - -# msg_info "Installing ruby ${RUBY_VERSION}" -# $STD rbenv install $RUBY_VERSION -# echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >>~/.bashrc -# echo 'eval "$(rbenv init -)"' >>~/.bashrc -# source ~/.bashrc -# msg_ok "Installed ruby ${RUBY_VERSION}" - msg_info "Adding manyfold user" useradd -m -s /usr/bin/bash manyfold msg_ok "Added manyfold user" @@ -169,8 +146,6 @@ motd_ssh customize msg_info "Cleaning up" -rm -rf "/opt/manyfold.zip" -rm -rf "~/.rbenv/plugins/ruby-build.zip" $STD apt-get -y autoremove $STD apt-get -y autoclean msg_ok "Cleaned" From e550893e0734d62c058abb25afe39a337b162439 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 23 Jul 2025 08:34:08 +0200 Subject: [PATCH 0141/1733] Update manyfold-install.sh --- install/manyfold-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/manyfold-install.sh b/install/manyfold-install.sh index 5f4895e12..953ba4b15 100644 --- a/install/manyfold-install.sh +++ b/install/manyfold-install.sh @@ -50,7 +50,7 @@ fetch_and_deploy_gh_release "manyfold" "manyfold3d/manyfold" "tarball" "latest" RUBY_INSTALL_VERSION=$(cat /opt/manyfold/.ruby-version) YARN_VERSION=$(grep '"packageManager":' /opt/manyfold/package.json | sed -E 's/.*"(yarn@[0-9\.]+)".*/\1/') -NODE_VERSION="22" NODE_MODULE="${YARN_VERSION}" setup_nodejs +NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs RUBY_VERSION=${RUBY_INSTALL_VERSION} RUBY_INSTALL_RAILS="true" setup_rbenv_stack msg_info "Adding manyfold user" From e635143d84658bdb1262dd6cccb745e66259c13a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 23 Jul 2025 08:40:10 +0200 Subject: [PATCH 0142/1733] Update manyfold-install.sh --- install/manyfold-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/manyfold-install.sh b/install/manyfold-install.sh index 953ba4b15..094744a1e 100644 --- a/install/manyfold-install.sh +++ b/install/manyfold-install.sh @@ -51,7 +51,7 @@ RUBY_INSTALL_VERSION=$(cat /opt/manyfold/.ruby-version) YARN_VERSION=$(grep '"packageManager":' /opt/manyfold/package.json | sed -E 's/.*"(yarn@[0-9\.]+)".*/\1/') NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs -RUBY_VERSION=${RUBY_INSTALL_VERSION} RUBY_INSTALL_RAILS="true" setup_rbenv_stack +RUBY_VERSION=${RUBY_INSTALL_VERSION} RUBY_INSTALL_RAILS="true" setup_ruby msg_info "Adding manyfold user" useradd -m -s /usr/bin/bash manyfold From 30d6ac93f1751bbe8c6142d4a60b2eb77bc5364d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 23 Jul 2025 09:39:39 +0200 Subject: [PATCH 0143/1733] Update manyfold-install.sh --- install/manyfold-install.sh | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/install/manyfold-install.sh b/install/manyfold-install.sh index 094744a1e..7f2ee7581 100644 --- a/install/manyfold-install.sh +++ b/install/manyfold-install.sh @@ -59,7 +59,7 @@ msg_ok "Added manyfold user" msg_info "Setting .env file" cat </opt/.env -export APP_VERSION=${RELEASE} +export APP_VERSION=12345 export GUID=1002 export PUID=1001 export PUBLIC_HOSTNAME=subdomain.somehost.org @@ -86,15 +86,14 @@ $STD rbenv global $RUBY_INSTALL_VERSION $STD bundle install $STD gem install sidekiq $STD npm install --global corepack -corepack enable -$STD corepack prepare $YARN_VERSION --activate -$STD corepack use $YARN_VERSION +corepack enable yarn +# $STD corepack prepare $YARN_VERSION --activate +# $STD corepack use $YARN_VERSION chown manyfold:manyfold /opt/.env rm /opt/manyfold/config/credentials.yml.enc $STD bin/rails credentials:edit $STD bin/rails db:migrate $STD bin/rails assets:precompile -echo "${RELEASE}" >/opt/${APPLICATION}_version.txt msg_ok "Installed manyfold" msg_info "Creating Service" From 3cdab16975c2e17151e7ffcfb274728864d8ca3d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 23 Jul 2025 10:17:51 +0200 Subject: [PATCH 0144/1733] Update frigate-install.sh --- install/frigate-install.sh | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/install/frigate-install.sh b/install/frigate-install.sh index daf95b439..d6c0aecc0 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -13,9 +13,11 @@ setting_up_container network_check update_os +NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs + msg_info "Installing Dependencies (Patience)" $STD apt-get install -y \ - git gpg ca-certificates automake build-essential xz-utils libtool ccache pkg-config \ + git automake build-essential xz-utils libtool ccache pkg-config \ libgtk-3-dev libavcodec-dev libavformat-dev libswscale-dev libv4l-dev libxvidcore-dev libx264-dev \ libjpeg-dev libpng-dev libtiff-dev gfortran openexr libatlas-base-dev libssl-dev libtbb-dev \ libopenexr-dev libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev gcc gfortran \ @@ -28,14 +30,6 @@ $STD apt-get install -y \ $STD pip install --upgrade pip msg_ok "Setup Python3" -msg_info "Installing Node.js" -mkdir -p /etc/apt/keyrings -curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg -echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" >/etc/apt/sources.list.d/nodesource.list -$STD apt-get update -$STD apt-get install -y nodejs -msg_ok "Installed Node.js" - msg_info "Installing go2rtc" mkdir -p /usr/local/go2rtc/bin cd /usr/local/go2rtc/bin @@ -54,9 +48,9 @@ fi msg_ok "Set Up Hardware Acceleration" msg_info "Setup Frigate" -RELEASE=$(curl -s https://api.github.com/repos/blakeblackshear/frigate/releases/latest | jq -r '.tag_name') +RELEASE="0.16.0 Beta 4" mkdir -p /opt/frigate/models -curl -fsSL https://github.com/blakeblackshear/frigate/archive/refs/tags/${RELEASE}.tar.gz -o frigate.tar.gz +curl -fsSL https://github.com/blakeblackshear/frigate/archive/refs/tags/v0.16.0-beta4.tar.gz -o frigate.tar.gz tar -xzf frigate.tar.gz -C /opt/frigate --strip-components 1 rm -rf frigate.tar.gz cd /opt/frigate From a5d5a29b0f7957127b8a2800c23e66f5dacfcb6d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 23 Jul 2025 10:19:19 +0200 Subject: [PATCH 0145/1733] libusb --- ct/frigate.sh | 6 +----- install/frigate-install.sh | 16 ++++++++-------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/ct/frigate.sh b/ct/frigate.sh index 2b498b794..a5a5c7c77 100644 --- a/ct/frigate.sh +++ b/ct/frigate.sh @@ -5,20 +5,16 @@ source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/ # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://frigate.video/ -# App Default Values APP="Frigate" var_tags="${var_tags:-nvr}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-20}" var_os="${var_os:-debian}" -var_version="${var_version:-11}" +var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-0}" -# App Output header_info "$APP" - -# Core variables color catch_errors diff --git a/install/frigate-install.sh b/install/frigate-install.sh index d6c0aecc0..33fd3385b 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -113,29 +113,29 @@ else msg_ok "Skipped Semantic Search Setup" fi msg_info "Building and Installing libUSB without udev" -wget -qO /tmp/libusb.zip https://github.com/libusb/libusb/archive/v1.0.26.zip +wget -qO /tmp/libusb.zip https://github.com/libusb/libusb/archive/v1.0.29.zip unzip -q /tmp/libusb.zip -d /tmp/ -cd /tmp/libusb-1.0.26 +cd /tmp/libusb-1.0.29 ./bootstrap.sh ./configure --disable-udev --enable-shared make -j$(nproc --all) make install ldconfig -rm -rf /tmp/libusb.zip /tmp/libusb-1.0.26 +rm -rf /tmp/libusb.zip /tmp/libusb-1.0.29 msg_ok "Installed libUSB without udev" msg_info "Installing Coral Object Detection Model (Patience)" cd /opt/frigate export CCACHE_DIR=/root/.ccache export CCACHE_MAXSIZE=2G -curl -L -o v1.0.26.zip https://github.com/libusb/libusb/archive/v1.0.26.zip -unzip -q v1.0.26.zip -rm v1.0.26.zip -cd libusb-1.0.26 +curl -L -o v1.0.29.zip https://github.com/libusb/libusb/archive/v1.0.29.zip +unzip -q v1.0.29.zip +rm v1.0.29.zip +cd libusb-1.0.29 $STD ./bootstrap.sh $STD ./configure --disable-udev --enable-shared $STD make -j $(nproc --all) -cd /opt/frigate/libusb-1.0.26/libusb +cd /opt/frigate/libusb-1.0.29/libusb mkdir -p /usr/local/lib $STD /bin/bash ../libtool --mode=install /usr/bin/install -c libusb-1.0.la '/usr/local/lib' mkdir -p /usr/local/include/libusb-1.0 From be8392acbc2ce6ba36abc52a9592fbfac8891d40 Mon Sep 17 00:00:00 2001 From: Lucas Zampieri Date: Wed, 23 Jul 2025 22:15:35 +0100 Subject: [PATCH 0146/1733] feat: Add Cleanuparr to arr stack Add support for Cleanuparr, a tool for automating the cleanup of unwanted or blocked files in Sonarr, Radarr, and supported download clients. - Native Linux installation using official binaries - Runs on port 11011 - Categorized under *Arr Suite - Standard arr resources: 2 CPU, 1GB RAM, 4GB disk Signed-off-by: Lucas Zampieri --- ct/cleanuparr.sh | 57 +++++++++++++++++++++++++ ct/headers/cleanuparr | 6 +++ frontend/public/json/cleanuparr.json | 33 +++++++++++++++ install/cleanuparr-install.sh | 62 ++++++++++++++++++++++++++++ 4 files changed, 158 insertions(+) create mode 100755 ct/cleanuparr.sh create mode 100644 ct/headers/cleanuparr create mode 100644 frontend/public/json/cleanuparr.json create mode 100755 install/cleanuparr-install.sh diff --git a/ct/cleanuparr.sh b/ct/cleanuparr.sh new file mode 100755 index 000000000..05ef4ae07 --- /dev/null +++ b/ct/cleanuparr.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: community-scripts ORG +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/Cleanuparr/Cleanuparr + +APP="Cleanuparr" +var_tags="${var_tags:-arr}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-1024}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -f /opt/cleanuparr/Cleanuparr ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Stopping ${APP}" + systemctl stop cleanuparr + msg_ok "Stopped ${APP}" + + msg_info "Updating ${APP}" + cd /opt/cleanuparr + RELEASE=$(curl -fsSL https://api.github.com/repos/Cleanuparr/Cleanuparr/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4)}') + curl -fsSLO "https://github.com/Cleanuparr/Cleanuparr/releases/download/v${RELEASE}/Cleanuparr-${RELEASE}-linux-amd64.zip" + unzip -oq "Cleanuparr-${RELEASE}-linux-amd64.zip" + rm -f "Cleanuparr-${RELEASE}-linux-amd64.zip" + chmod +x /opt/cleanuparr/Cleanuparr + msg_ok "Updated ${APP}" + + msg_info "Starting ${APP}" + systemctl start cleanuparr + msg_ok "Started ${APP}" + msg_ok "Updated Successfully" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:11011${CL}" \ No newline at end of file diff --git a/ct/headers/cleanuparr b/ct/headers/cleanuparr new file mode 100644 index 000000000..ef403c52a --- /dev/null +++ b/ct/headers/cleanuparr @@ -0,0 +1,6 @@ + ________ + / ____/ /__ ____ _____ __ ______ ____ _________ + / / / / _ \/ __ `/ __ \/ / / / __ \/ __ `/ ___/ ___/ +/ /___/ / __/ /_/ / / / / /_/ / /_/ / /_/ / / / / +\____/_/\___/\__,_/_/ /_/\__,_/ .___/\__,_/_/ /_/ + /_/ \ No newline at end of file diff --git a/frontend/public/json/cleanuparr.json b/frontend/public/json/cleanuparr.json new file mode 100644 index 000000000..0a17cf642 --- /dev/null +++ b/frontend/public/json/cleanuparr.json @@ -0,0 +1,33 @@ +{ + "name": "Cleanuparr", + "slug": "cleanuparr", + "categories": [14], + "date_created": "2025-07-23", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 11011, + "documentation": null, + "website": "https://github.com/Cleanuparr/Cleanuparr", + "logo": "https://raw.githubusercontent.com/Cleanuparr/Cleanuparr/main/src/Cleanuparr.Web/wwwroot/logo.png", + "config_path": "", + "description": "Cleanuparr is a tool for automating the cleanup of unwanted or blocked files in Sonarr, Radarr, and supported download clients like qBittorrent, Transmission, and Deluge. It removes incomplete, blocked, or malicious downloads and can trigger replacement searches to ensure your media library stays complete and up-to-date.", + "install_methods": [ + { + "type": "default", + "script": "ct/cleanuparr.sh", + "resources": { + "cpu": 2, + "ram": 1024, + "hdd": 4, + "os": "debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} \ No newline at end of file diff --git a/install/cleanuparr-install.sh b/install/cleanuparr-install.sh new file mode 100755 index 000000000..89ae2dd93 --- /dev/null +++ b/install/cleanuparr-install.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: community-scripts ORG +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/Cleanuparr/Cleanuparr + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt-get install -y \ + curl \ + sudo \ + mc \ + unzip +msg_ok "Installed Dependencies" + +msg_info "Installing Cleanuparr" +RELEASE=$(curl -fsSL https://api.github.com/repos/Cleanuparr/Cleanuparr/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4)}') +mkdir -p /opt/cleanuparr +cd /opt/cleanuparr +curl -fsSLO "https://github.com/Cleanuparr/Cleanuparr/releases/download/v${RELEASE}/Cleanuparr-${RELEASE}-linux-amd64.zip" +unzip -q "Cleanuparr-${RELEASE}-linux-amd64.zip" +rm -f "Cleanuparr-${RELEASE}-linux-amd64.zip" +chmod +x /opt/cleanuparr/Cleanuparr +msg_ok "Installed Cleanuparr" + +msg_info "Creating Service" +cat </etc/systemd/system/cleanuparr.service +[Unit] +Description=Cleanuparr Daemon +After=syslog.target network.target + +[Service] +Type=simple +User=root +WorkingDirectory=/opt/cleanuparr +ExecStart=/opt/cleanuparr/Cleanuparr +Restart=on-failure +RestartSec=5 +Environment="PORT=11011" +Environment="CONFIG_DIR=/opt/cleanuparr/config" + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now cleanuparr +msg_ok "Created Service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" \ No newline at end of file From be8e38ab372be8e5071761d7c51c0b56fe71865e Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 24 Jul 2025 07:40:14 +0000 Subject: [PATCH 0147/1733] Update .app files --- ct/headers/keycloak | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/keycloak diff --git a/ct/headers/keycloak b/ct/headers/keycloak new file mode 100644 index 000000000..0c20d7510 --- /dev/null +++ b/ct/headers/keycloak @@ -0,0 +1,6 @@ + __ __ __ __ + / //_/__ __ _______/ /___ ____ _/ /__ + / ,< / _ \/ / / / ___/ / __ \/ __ `/ //_/ + / /| / __/ /_/ / /__/ / /_/ / /_/ / ,< +/_/ |_\___/\__, /\___/_/\____/\__,_/_/|_| + /____/ From 673421d9ac6364b58b2b7016f76ec19d9baeac05 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 24 Jul 2025 10:39:03 +0200 Subject: [PATCH 0148/1733] Update frigate-install.sh --- install/frigate-install.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/install/frigate-install.sh b/install/frigate-install.sh index 33fd3385b..c9db81d10 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -155,6 +155,16 @@ wget -qO /media/frigate/person-bicycle-car-detection.mp4 https://github.com/inte msg_ok "Installed Coral Object Detection Model" msg_info "Building Nginx with Custom Modules" +cat >/etc/apt/sources.list.d/debian.sources <<'EOF' +Types: deb deb-src +URIs: http://deb.debian.org/debian +Suites: bookworm bookworm-updates bookworm-backports +Components: main contrib non-free non-free-firmware +Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg +EOF +sed -i -e '/^deb-src /d' -e 's/^deb /#deb /' /etc/apt/sources.list +$STD apt-get update + $STD /opt/frigate/docker/main/build_nginx.sh sed -e '/s6-notifyoncheck/ s/^#*/#/' -i /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/run ln -sf /usr/local/nginx/sbin/nginx /usr/local/bin/nginx @@ -162,6 +172,7 @@ msg_ok "Built Nginx" msg_info "Installing Tempio" sed -i 's|/rootfs/usr/local|/usr/local|g' /opt/frigate/docker/main/install_tempio.sh +TARGETARCH="amd64" $STD /opt/frigate/docker/main/install_tempio.sh chmod +x /usr/local/tempio/bin/tempio ln -sf /usr/local/tempio/bin/tempio /usr/local/bin/tempio From f5e7581c22955f4f61619de7fae695b98a94da76 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 24 Jul 2025 11:05:21 +0200 Subject: [PATCH 0149/1733] Update frigate-install.sh --- install/frigate-install.sh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/install/frigate-install.sh b/install/frigate-install.sh index c9db81d10..b72447bee 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -49,6 +49,7 @@ msg_ok "Set Up Hardware Acceleration" msg_info "Setup Frigate" RELEASE="0.16.0 Beta 4" +export DEBIAN_FRONTEND=noninteractive mkdir -p /opt/frigate/models curl -fsSL https://github.com/blakeblackshear/frigate/archive/refs/tags/v0.16.0-beta4.tar.gz -o frigate.tar.gz tar -xzf frigate.tar.gz -C /opt/frigate --strip-components 1 @@ -112,6 +113,7 @@ if [[ "$semantic_choice" == "y" ]]; then else msg_ok "Skipped Semantic Search Setup" fi + msg_info "Building and Installing libUSB without udev" wget -qO /tmp/libusb.zip https://github.com/libusb/libusb/archive/v1.0.29.zip unzip -q /tmp/libusb.zip -d /tmp/ @@ -164,6 +166,13 @@ Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg EOF sed -i -e '/^deb-src /d' -e 's/^deb /#deb /' /etc/apt/sources.list $STD apt-get update +$STD apt-get -f install -y || true +$STD dpkg --configure -a || true + +for pkg in $(apt-mark showhold); do + echo "Unholding $pkg" + apt-mark unhold "$pkg" +done $STD /opt/frigate/docker/main/build_nginx.sh sed -e '/s6-notifyoncheck/ s/^#*/#/' -i /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/run From 259794f0a3e07ef40e8787f05fba055b9cd62cc9 Mon Sep 17 00:00:00 2001 From: Lucas Zampieri Date: Thu, 24 Jul 2025 10:21:24 +0100 Subject: [PATCH 0150/1733] Apply PR review suggestions - Remove explicit dependency installation - Use fetch_and_deploy_gh_release helper function - Add version checking before updates - Use direct curl command for release version checking Signed-off-by: Lucas Zampieri --- ct/cleanuparr.sh | 34 ++++++++++++++++++---------------- install/cleanuparr-install.sh | 16 +--------------- 2 files changed, 19 insertions(+), 31 deletions(-) diff --git a/ct/cleanuparr.sh b/ct/cleanuparr.sh index 05ef4ae07..6aea73e92 100755 --- a/ct/cleanuparr.sh +++ b/ct/cleanuparr.sh @@ -27,23 +27,25 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - msg_info "Stopping ${APP}" - systemctl stop cleanuparr - msg_ok "Stopped ${APP}" - msg_info "Updating ${APP}" - cd /opt/cleanuparr - RELEASE=$(curl -fsSL https://api.github.com/repos/Cleanuparr/Cleanuparr/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4)}') - curl -fsSLO "https://github.com/Cleanuparr/Cleanuparr/releases/download/v${RELEASE}/Cleanuparr-${RELEASE}-linux-amd64.zip" - unzip -oq "Cleanuparr-${RELEASE}-linux-amd64.zip" - rm -f "Cleanuparr-${RELEASE}-linux-amd64.zip" - chmod +x /opt/cleanuparr/Cleanuparr - msg_ok "Updated ${APP}" + RELEASE=$(curl -fsSL https://api.github.com/repos/Cleanuparr/Cleanuparr/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - msg_info "Starting ${APP}" - systemctl start cleanuparr - msg_ok "Started ${APP}" - msg_ok "Updated Successfully" + if [[ "${RELEASE}" != "$(cat ~/.Cleanuparr 2>/dev/null)" ]] || [[ ! -f ~/.Cleanuparr ]]; then + msg_info "Stopping ${APP}" + systemctl stop cleanuparr + msg_ok "Stopped ${APP}" + + msg_info "Updating ${APP} to v${RELEASE}" + fetch_and_deploy_gh_release "Cleanuparr" "Cleanuparr/Cleanuparr" "prebuild" "$RELEASE" "/opt/cleanuparr" "*linux-amd64.zip" + msg_ok "Updated ${APP}" + + msg_info "Starting ${APP}" + systemctl start cleanuparr + msg_ok "Started ${APP}" + msg_ok "Updated Successfully" + else + msg_ok "No update required. ${APP} is already at v${RELEASE}" + fi exit } @@ -54,4 +56,4 @@ description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:11011${CL}" \ No newline at end of file +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:11011${CL}" diff --git a/install/cleanuparr-install.sh b/install/cleanuparr-install.sh index 89ae2dd93..22dabb5c1 100755 --- a/install/cleanuparr-install.sh +++ b/install/cleanuparr-install.sh @@ -13,22 +13,8 @@ setting_up_container network_check update_os -msg_info "Installing Dependencies" -$STD apt-get install -y \ - curl \ - sudo \ - mc \ - unzip -msg_ok "Installed Dependencies" - msg_info "Installing Cleanuparr" -RELEASE=$(curl -fsSL https://api.github.com/repos/Cleanuparr/Cleanuparr/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4)}') -mkdir -p /opt/cleanuparr -cd /opt/cleanuparr -curl -fsSLO "https://github.com/Cleanuparr/Cleanuparr/releases/download/v${RELEASE}/Cleanuparr-${RELEASE}-linux-amd64.zip" -unzip -q "Cleanuparr-${RELEASE}-linux-amd64.zip" -rm -f "Cleanuparr-${RELEASE}-linux-amd64.zip" -chmod +x /opt/cleanuparr/Cleanuparr +fetch_and_deploy_gh_release "Cleanuparr" "Cleanuparr/Cleanuparr" "prebuild" "latest" "/opt/cleanuparr" "*linux-amd64.zip" msg_ok "Installed Cleanuparr" msg_info "Creating Service" From 8a84b80e513833035b492d2b66fb14b7e11f7b0c Mon Sep 17 00:00:00 2001 From: Lucas Zampieri Date: Thu, 24 Jul 2025 10:27:43 +0100 Subject: [PATCH 0151/1733] PR review v2 - Removed blank lines; - Added config path; - Removed messages already handled by the funcion; - Update link for VED; Signed-off-by: Lucas Zampieri --- ct/cleanuparr.sh | 4 +--- frontend/public/json/cleanuparr.json | 4 ++-- install/cleanuparr-install.sh | 3 +-- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/ct/cleanuparr.sh b/ct/cleanuparr.sh index 6aea73e92..26ac30452 100755 --- a/ct/cleanuparr.sh +++ b/ct/cleanuparr.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: community-scripts ORG # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -29,13 +29,11 @@ function update_script() { fi RELEASE=$(curl -fsSL https://api.github.com/repos/Cleanuparr/Cleanuparr/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - if [[ "${RELEASE}" != "$(cat ~/.Cleanuparr 2>/dev/null)" ]] || [[ ! -f ~/.Cleanuparr ]]; then msg_info "Stopping ${APP}" systemctl stop cleanuparr msg_ok "Stopped ${APP}" - msg_info "Updating ${APP} to v${RELEASE}" fetch_and_deploy_gh_release "Cleanuparr" "Cleanuparr/Cleanuparr" "prebuild" "$RELEASE" "/opt/cleanuparr" "*linux-amd64.zip" msg_ok "Updated ${APP}" diff --git a/frontend/public/json/cleanuparr.json b/frontend/public/json/cleanuparr.json index 0a17cf642..2d3590b46 100644 --- a/frontend/public/json/cleanuparr.json +++ b/frontend/public/json/cleanuparr.json @@ -10,7 +10,7 @@ "documentation": null, "website": "https://github.com/Cleanuparr/Cleanuparr", "logo": "https://raw.githubusercontent.com/Cleanuparr/Cleanuparr/main/src/Cleanuparr.Web/wwwroot/logo.png", - "config_path": "", + "config_path": "/opt/cleanuparr/config ", "description": "Cleanuparr is a tool for automating the cleanup of unwanted or blocked files in Sonarr, Radarr, and supported download clients like qBittorrent, Transmission, and Deluge. It removes incomplete, blocked, or malicious downloads and can trigger replacement searches to ensure your media library stays complete and up-to-date.", "install_methods": [ { @@ -30,4 +30,4 @@ "password": null }, "notes": [] -} \ No newline at end of file +} diff --git a/install/cleanuparr-install.sh b/install/cleanuparr-install.sh index 22dabb5c1..45e3bc1d4 100755 --- a/install/cleanuparr-install.sh +++ b/install/cleanuparr-install.sh @@ -13,7 +13,6 @@ setting_up_container network_check update_os -msg_info "Installing Cleanuparr" fetch_and_deploy_gh_release "Cleanuparr" "Cleanuparr/Cleanuparr" "prebuild" "latest" "/opt/cleanuparr" "*linux-amd64.zip" msg_ok "Installed Cleanuparr" @@ -45,4 +44,4 @@ customize msg_info "Cleaning up" $STD apt-get -y autoremove $STD apt-get -y autoclean -msg_ok "Cleaned" \ No newline at end of file +msg_ok "Cleaned" From 27fb6be7febf2f7530099d03ade4270f835719f8 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 24 Jul 2025 11:46:01 +0200 Subject: [PATCH 0152/1733] Update frigate-install.sh --- install/frigate-install.sh | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/install/frigate-install.sh b/install/frigate-install.sh index b72447bee..c4924509e 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -13,6 +13,17 @@ setting_up_container network_check update_os +msg_info "Configure Debian Sources" +cat >/etc/apt/sources.list.d/debian.sources <<'EOF' +Types: deb deb-src +URIs: http://deb.debian.org/debian +Suites: bookworm bookworm-updates bookworm-backports +Components: main contrib non-free non-free-firmware +Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg +EOF +sed -i -e '/^deb-src /d' -e 's/^deb /#deb /' /etc/apt/sources.list +msg_ok "Configured Debian Sources" + NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs msg_info "Installing Dependencies (Patience)" @@ -50,6 +61,8 @@ msg_ok "Set Up Hardware Acceleration" msg_info "Setup Frigate" RELEASE="0.16.0 Beta 4" export DEBIAN_FRONTEND=noninteractive +echo "libedgetpu1-max libedgetpu/accepted-eula select true" | debconf-set-selections + mkdir -p /opt/frigate/models curl -fsSL https://github.com/blakeblackshear/frigate/archive/refs/tags/v0.16.0-beta4.tar.gz -o frigate.tar.gz tar -xzf frigate.tar.gz -C /opt/frigate --strip-components 1 @@ -157,14 +170,7 @@ wget -qO /media/frigate/person-bicycle-car-detection.mp4 https://github.com/inte msg_ok "Installed Coral Object Detection Model" msg_info "Building Nginx with Custom Modules" -cat >/etc/apt/sources.list.d/debian.sources <<'EOF' -Types: deb deb-src -URIs: http://deb.debian.org/debian -Suites: bookworm bookworm-updates bookworm-backports -Components: main contrib non-free non-free-firmware -Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg -EOF -sed -i -e '/^deb-src /d' -e 's/^deb /#deb /' /etc/apt/sources.list + $STD apt-get update $STD apt-get -f install -y || true $STD dpkg --configure -a || true From 1ac8f6b279f232627d1d4d232819d09e5c03a842 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 24 Jul 2025 11:46:19 +0200 Subject: [PATCH 0153/1733] Update frigate-install.sh --- install/frigate-install.sh | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/install/frigate-install.sh b/install/frigate-install.sh index c4924509e..f7f95995f 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -117,15 +117,15 @@ fi echo "tmpfs /tmp/cache tmpfs defaults 0 0" >>/etc/fstab msg_ok "Installed Frigate $RELEASE" -read -p "Semantic Search requires a dedicated GPU and at least 16GB RAM. Would you like to install it? (y/n): " semantic_choice -if [[ "$semantic_choice" == "y" ]]; then - msg_info "Configuring Semantic Search & AI Models" - mkdir -p /opt/frigate/models/semantic_search - curl -fsSL -o /opt/frigate/models/semantic_search/clip_model.pt https://huggingface.co/openai/clip-vit-base-patch32/resolve/main/pytorch_model.bin - msg_ok "Semantic Search Models Installed" -else - msg_ok "Skipped Semantic Search Setup" -fi +# read -p "Semantic Search requires a dedicated GPU and at least 16GB RAM. Would you like to install it? (y/n): " semantic_choice +# if [[ "$semantic_choice" == "y" ]]; then +# msg_info "Configuring Semantic Search & AI Models" +# mkdir -p /opt/frigate/models/semantic_search +# curl -fsSL -o /opt/frigate/models/semantic_search/clip_model.pt https://huggingface.co/openai/clip-vit-base-patch32/resolve/main/pytorch_model.bin +# msg_ok "Semantic Search Models Installed" +# else +# msg_ok "Skipped Semantic Search Setup" +# fi msg_info "Building and Installing libUSB without udev" wget -qO /tmp/libusb.zip https://github.com/libusb/libusb/archive/v1.0.29.zip From 7833744981ca7003c04aa4b9e39e6cfabd8029c9 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 24 Jul 2025 12:45:33 +0200 Subject: [PATCH 0154/1733] Update frigate-install.sh --- install/frigate-install.sh | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/install/frigate-install.sh b/install/frigate-install.sh index f7f95995f..03a76de0d 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -13,7 +13,8 @@ setting_up_container network_check update_os -msg_info "Configure Debian Sources" +msg_info "Configure Debian Sources & Pinning (Stable > Backports)" +# APT sources im deb822-Format cat >/etc/apt/sources.list.d/debian.sources <<'EOF' Types: deb deb-src URIs: http://deb.debian.org/debian @@ -21,10 +22,26 @@ Suites: bookworm bookworm-updates bookworm-backports Components: main contrib non-free non-free-firmware Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg EOF +# klassische sources.list auskommentieren sed -i -e '/^deb-src /d' -e 's/^deb /#deb /' /etc/apt/sources.list -msg_ok "Configured Debian Sources" -NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs +# Pinning: Stable (bookworm) bevorzugt, Backports nur fallweise +cat >/etc/apt/preferences.d/stable-backports.pref <<'EOF' +Package: * +Pin: release a=bookworm +Pin-Priority: 700 + +Package: * +Pin: release a=bookworm-backports +Pin-Priority: 300 +EOF +msg_ok "Configured Debian Sources & APT pinning" + +msg_info "Prepare APT (repair & update)" +apt-get update +apt-get -f install -y || true +dpkg --configure -a || true +msg_ok "APT ready" msg_info "Installing Dependencies (Patience)" $STD apt-get install -y \ @@ -62,6 +79,7 @@ msg_info "Setup Frigate" RELEASE="0.16.0 Beta 4" export DEBIAN_FRONTEND=noninteractive echo "libedgetpu1-max libedgetpu/accepted-eula select true" | debconf-set-selections +echo "libedgetpu1-max libedgetpu/install-confirm-max select true" | debconf-set-selections mkdir -p /opt/frigate/models curl -fsSL https://github.com/blakeblackshear/frigate/archive/refs/tags/v0.16.0-beta4.tar.gz -o frigate.tar.gz From 3118770bfad269e45c51509c0cd82d90f0b6e33c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 24 Jul 2025 13:03:47 +0200 Subject: [PATCH 0155/1733] Update tools.func --- misc/tools.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/tools.func b/misc/tools.func index dace8ca73..a848a2a0a 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -886,7 +886,7 @@ function fetch_and_deploy_gh_release() { local arch arch=$(dpkg --print-architecture 2>/dev/null || uname -m) [[ "$arch" == "x86_64" ]] && arch="amd64" - [[ "$arch" == "aarch64" ]] && arch="arm64" + #[[ "$arch" == "aarch64" ]] && arch="arm64" local assets url_match="" assets=$(echo "$json" | jq -r '.assets[].browser_download_url') From fc8f8ef7fbe9105cb6c9c20ce479cd47439bcf01 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 24 Jul 2025 13:11:35 +0200 Subject: [PATCH 0156/1733] Update tools.func --- misc/tools.func | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index a848a2a0a..0edc8a46f 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -881,37 +881,43 @@ function fetch_and_deploy_gh_release() { cp -r "$unpack_dir"/* "$target/" shopt -u dotglob nullglob - ### Binary Mode ### + ### Binary Mode ### elif [[ "$mode" == "binary" ]]; then local arch arch=$(dpkg --print-architecture 2>/dev/null || uname -m) [[ "$arch" == "x86_64" ]] && arch="amd64" - #[[ "$arch" == "aarch64" ]] && arch="arm64" + [[ "$arch" == "aarch64" ]] && arch="arm64" local assets url_match="" assets=$(echo "$json" | jq -r '.assets[].browser_download_url') - # If explicit filename pattern is provided (param $6), match that first + # 1. Expliziten Pattern-Parameter bevorzugen if [[ -n "$asset_pattern" ]]; then for u in $assets; do - [[ "$u" =~ $asset_pattern || "$u" == *"$asset_pattern" ]] && url_match="$u" && break - done - fi - - # If no match via explicit pattern, fall back to architecture heuristic - if [[ -z "$url_match" ]]; then - for u in $assets; do - if [[ "$u" =~ ($arch|amd64|x86_64|aarch64|arm64).*\.deb$ ]]; then + if [[ "$u" =~ $asset_pattern || "$u" == *"$asset_pattern" ]]; then url_match="$u" break fi done fi - # Fallback: any .deb file + # 2. Wenn kein Pattern-Match, gezielt nach Architektur suchen if [[ -z "$url_match" ]]; then for u in $assets; do - [[ "$u" =~ \.deb$ ]] && url_match="$u" && break + if [[ "$u" == *"${arch}"* && "$u" == *.deb ]]; then + url_match="$u" + break + fi + done + fi + + # 3. Fallback: irgendein .deb + if [[ -z "$url_match" ]]; then + for u in $assets; do + if [[ "$u" == *.deb ]]; then + url_match="$u" + break + fi done fi From ae714cab3c63f24acc34738413484951f7ef6a1e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 24 Jul 2025 13:19:12 +0200 Subject: [PATCH 0157/1733] Update tools.func --- misc/tools.func | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 0edc8a46f..2ee9f30ed 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -881,7 +881,7 @@ function fetch_and_deploy_gh_release() { cp -r "$unpack_dir"/* "$target/" shopt -u dotglob nullglob - ### Binary Mode ### + ### Binary Mode ### elif [[ "$mode" == "binary" ]]; then local arch arch=$(dpkg --print-architecture 2>/dev/null || uname -m) @@ -891,17 +891,17 @@ function fetch_and_deploy_gh_release() { local assets url_match="" assets=$(echo "$json" | jq -r '.assets[].browser_download_url') - # 1. Expliziten Pattern-Parameter bevorzugen + # 1. wenn Pattern übergeben -> NUR dieses matchen if [[ -n "$asset_pattern" ]]; then for u in $assets; do - if [[ "$u" =~ $asset_pattern || "$u" == *"$asset_pattern" ]]; then + if [[ "$u" == *"$asset_pattern"* ]]; then url_match="$u" break fi done fi - # 2. Wenn kein Pattern-Match, gezielt nach Architektur suchen + # 2. sonst nach Host-Architektur suchen if [[ -z "$url_match" ]]; then for u in $assets; do if [[ "$u" == *"${arch}"* && "$u" == *.deb ]]; then @@ -911,7 +911,7 @@ function fetch_and_deploy_gh_release() { done fi - # 3. Fallback: irgendein .deb + # 3. sonst generischer Fallback if [[ -z "$url_match" ]]; then for u in $assets; do if [[ "$u" == *.deb ]]; then From d2dc6bb63c5707705b9ada7fdd75909b65e704d5 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 24 Jul 2025 13:23:33 +0200 Subject: [PATCH 0158/1733] Update tools.func --- misc/tools.func | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 2ee9f30ed..72b847a34 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -882,7 +882,9 @@ function fetch_and_deploy_gh_release() { shopt -u dotglob nullglob ### Binary Mode ### + ### Binary Mode ### elif [[ "$mode" == "binary" ]]; then + # Architektur ermitteln local arch arch=$(dpkg --print-architecture 2>/dev/null || uname -m) [[ "$arch" == "x86_64" ]] && arch="amd64" @@ -891,42 +893,48 @@ function fetch_and_deploy_gh_release() { local assets url_match="" assets=$(echo "$json" | jq -r '.assets[].browser_download_url') - # 1. wenn Pattern übergeben -> NUR dieses matchen + # --- 1. Wenn ein Pattern übergeben wurde, matcht nur das --- if [[ -n "$asset_pattern" ]]; then for u in $assets; do - if [[ "$u" == *"$asset_pattern"* ]]; then + filename_candidate="${u##*/}" + # einfaches String-Match + if [[ "$filename_candidate" == *"$asset_pattern"* ]]; then url_match="$u" break fi done fi - # 2. sonst nach Host-Architektur suchen + # --- 2. Wenn kein Pattern-Treffer, dann nach Host-Architektur suchen --- if [[ -z "$url_match" ]]; then for u in $assets; do - if [[ "$u" == *"${arch}"* && "$u" == *.deb ]]; then + filename_candidate="${u##*/}" + if [[ "$filename_candidate" == *"$arch"* && "$filename_candidate" == *.deb ]]; then url_match="$u" break fi done fi - # 3. sonst generischer Fallback + # --- 3. Wenn immer noch kein Treffer, nimm das erste .deb --- if [[ -z "$url_match" ]]; then for u in $assets; do - if [[ "$u" == *.deb ]]; then + filename_candidate="${u##*/}" + if [[ "$filename_candidate" == *.deb ]]; then url_match="$u" break fi done fi + # --- Wenn kein Asset gefunden, Fehler --- if [[ -z "$url_match" ]]; then msg_error "No suitable .deb asset found for $app" rm -rf "$tmpdir" return 1 fi + # Download und Install filename="${url_match##*/}" curl $download_timeout -fsSL -o "$tmpdir/$filename" "$url_match" || { msg_error "Download failed: $url_match" @@ -935,13 +943,13 @@ function fetch_and_deploy_gh_release() { } chmod 644 "$tmpdir/$filename" - $STD apt-get install -y "$tmpdir/$filename" || { - $STD dpkg -i "$tmpdir/$filename" || { + if ! $STD apt-get install -y "$tmpdir/$filename"; then + if ! $STD dpkg -i "$tmpdir/$filename"; then msg_error "Both apt and dpkg installation failed" rm -rf "$tmpdir" return 1 - } - } + fi + fi ### Prebuild Mode ### elif [[ "$mode" == "prebuild" ]]; then From a18807645701bf2dad7ddaf3deabdc694a3a3d16 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 24 Jul 2025 13:27:14 +0200 Subject: [PATCH 0159/1733] Update tools.func --- misc/tools.func | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 72b847a34..010f4409c 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -882,62 +882,77 @@ function fetch_and_deploy_gh_release() { shopt -u dotglob nullglob ### Binary Mode ### - ### Binary Mode ### + ### Binary Mode (with DEBUG) ### elif [[ "$mode" == "binary" ]]; then - # Architektur ermitteln local arch arch=$(dpkg --print-architecture 2>/dev/null || uname -m) [[ "$arch" == "x86_64" ]] && arch="amd64" [[ "$arch" == "aarch64" ]] && arch="arm64" + echo "[DEBUG] Detected system architecture: $arch" + echo "[DEBUG] Provided asset_pattern: ${asset_pattern:-}" + local assets url_match="" assets=$(echo "$json" | jq -r '.assets[].browser_download_url') - # --- 1. Wenn ein Pattern übergeben wurde, matcht nur das --- + echo "[DEBUG] Listing all available assets from release:" + for u in $assets; do + echo " -> $u" + done + + # 1. Pattern match if [[ -n "$asset_pattern" ]]; then + echo "[DEBUG] Trying to match provided pattern: $asset_pattern" for u in $assets; do filename_candidate="${u##*/}" - # einfaches String-Match + echo " [DEBUG] Checking asset: $filename_candidate" if [[ "$filename_candidate" == *"$asset_pattern"* ]]; then + echo " [DEBUG] ✅ Pattern matched: $filename_candidate" url_match="$u" break fi done fi - # --- 2. Wenn kein Pattern-Treffer, dann nach Host-Architektur suchen --- + # 2. Arch match (only if no pattern match) if [[ -z "$url_match" ]]; then + echo "[DEBUG] No pattern match, trying architecture match: $arch" for u in $assets; do filename_candidate="${u##*/}" + echo " [DEBUG] Checking asset: $filename_candidate" if [[ "$filename_candidate" == *"$arch"* && "$filename_candidate" == *.deb ]]; then + echo " [DEBUG] ✅ Architecture match: $filename_candidate" url_match="$u" break fi done fi - # --- 3. Wenn immer noch kein Treffer, nimm das erste .deb --- + # 3. Fallback if [[ -z "$url_match" ]]; then + echo "[DEBUG] No architecture match, falling back to first .deb" for u in $assets; do filename_candidate="${u##*/}" + echo " [DEBUG] Checking asset: $filename_candidate" if [[ "$filename_candidate" == *.deb ]]; then + echo " [DEBUG] ✅ Fallback match: $filename_candidate" url_match="$u" break fi done fi - # --- Wenn kein Asset gefunden, Fehler --- if [[ -z "$url_match" ]]; then - msg_error "No suitable .deb asset found for $app" + echo "[DEBUG] ❌ No suitable .deb asset found!" rm -rf "$tmpdir" return 1 fi - # Download und Install + echo "[DEBUG] Final selected asset: $url_match" filename="${url_match##*/}" + curl $download_timeout -fsSL -o "$tmpdir/$filename" "$url_match" || { - msg_error "Download failed: $url_match" + echo "[DEBUG] ❌ Download failed: $url_match" rm -rf "$tmpdir" return 1 } @@ -945,7 +960,7 @@ function fetch_and_deploy_gh_release() { chmod 644 "$tmpdir/$filename" if ! $STD apt-get install -y "$tmpdir/$filename"; then if ! $STD dpkg -i "$tmpdir/$filename"; then - msg_error "Both apt and dpkg installation failed" + echo "[DEBUG] ❌ Both apt and dpkg installation failed" rm -rf "$tmpdir" return 1 fi From 4707eac3642973cbe3a01a7c959cc9d2019e599b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 24 Jul 2025 13:31:39 +0200 Subject: [PATCH 0160/1733] Update tools.func --- misc/tools.func | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 010f4409c..5d03c24d3 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -882,16 +882,12 @@ function fetch_and_deploy_gh_release() { shopt -u dotglob nullglob ### Binary Mode ### - ### Binary Mode (with DEBUG) ### elif [[ "$mode" == "binary" ]]; then local arch arch=$(dpkg --print-architecture 2>/dev/null || uname -m) [[ "$arch" == "x86_64" ]] && arch="amd64" [[ "$arch" == "aarch64" ]] && arch="arm64" - echo "[DEBUG] Detected system architecture: $arch" - echo "[DEBUG] Provided asset_pattern: ${asset_pattern:-}" - local assets url_match="" assets=$(echo "$json" | jq -r '.assets[].browser_download_url') @@ -902,12 +898,9 @@ function fetch_and_deploy_gh_release() { # 1. Pattern match if [[ -n "$asset_pattern" ]]; then - echo "[DEBUG] Trying to match provided pattern: $asset_pattern" for u in $assets; do filename_candidate="${u##*/}" - echo " [DEBUG] Checking asset: $filename_candidate" if [[ "$filename_candidate" == *"$asset_pattern"* ]]; then - echo " [DEBUG] ✅ Pattern matched: $filename_candidate" url_match="$u" break fi @@ -916,12 +909,10 @@ function fetch_and_deploy_gh_release() { # 2. Arch match (only if no pattern match) if [[ -z "$url_match" ]]; then - echo "[DEBUG] No pattern match, trying architecture match: $arch" for u in $assets; do filename_candidate="${u##*/}" echo " [DEBUG] Checking asset: $filename_candidate" if [[ "$filename_candidate" == *"$arch"* && "$filename_candidate" == *.deb ]]; then - echo " [DEBUG] ✅ Architecture match: $filename_candidate" url_match="$u" break fi @@ -930,12 +921,9 @@ function fetch_and_deploy_gh_release() { # 3. Fallback if [[ -z "$url_match" ]]; then - echo "[DEBUG] No architecture match, falling back to first .deb" for u in $assets; do filename_candidate="${u##*/}" - echo " [DEBUG] Checking asset: $filename_candidate" if [[ "$filename_candidate" == *.deb ]]; then - echo " [DEBUG] ✅ Fallback match: $filename_candidate" url_match="$u" break fi From e58b594095b3c33b39e76835238c894d248590c2 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 24 Jul 2025 13:34:07 +0200 Subject: [PATCH 0161/1733] ddd --- frontend/public/json/salt.json | 35 ------------------- frontend/public/json/teamspeak-server.json | 39 ---------------------- 2 files changed, 74 deletions(-) delete mode 100644 frontend/public/json/salt.json delete mode 100644 frontend/public/json/teamspeak-server.json diff --git a/frontend/public/json/salt.json b/frontend/public/json/salt.json deleted file mode 100644 index 41750a2f8..000000000 --- a/frontend/public/json/salt.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "Salt", - "slug": "salt", - "categories": [ - 19 - ], - "date_created": "2025-07-02", - "type": "ct", - "updateable": true, - "privileged": false, - "config_path": "/opt/salt/.env", - "interface_port": 3000, - "documentation": "https://docs.saltproject.io/salt/install-guide/en/latest/", - "website": "https://saltproject.io/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/saltmaster.svg", - "description": "SaltStack Salt is a software for automating the management and configuration of IT infrastructure and applications. It is an event-driven automation tool and framework used to deploy, configure, and manage complex IT systems. Its primary functions include configuration management, where it ensures consistent configurations and manages operating system deployment and software installation. It also automates and orchestrates routine IT processes and can create self-aware, self-healing systems.", - "install_methods": [ - { - "type": "default", - "script": "ct/salt.sh", - "resources": { - "cpu": 1, - "ram": 1024, - "hdd": 2, - "os": "Debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] -} diff --git a/frontend/public/json/teamspeak-server.json b/frontend/public/json/teamspeak-server.json deleted file mode 100644 index 98dbec48e..000000000 --- a/frontend/public/json/teamspeak-server.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "Teamspeak Server", - "slug": "teamspeak-server", - "categories": [ - 24 - ], - "date_created": "2025-07-21", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 9987, - "documentation": "https://support.teamspeak.com/hc/en-us/categories/360000302017-TeamSpeak-3", - "website": "https://teamspeak.com/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/teamspeak-light.svgp", - "config_path": "", - "description": "TeamSpeak is a voice‑over‑IP (VoIP) application, primarily used by gamers and teams to chat in real‑time on dedicated servers. It delivers crystal‑clear, low‑latency voice communication.""install_methods": [ - { - "type": "default", - "script": "ct/teamspeak-server.sh", - "resources": { - "cpu": 1, - "ram": 512, - "hdd": 2, - "os": "debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "Use `journalctl -u teamspeak-server.service` inside LXC console to check for admin credentials!", - "type": "info" - } - ] -} From ffc0a113330593aac1b652bcada2687ed42bf495 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 24 Jul 2025 13:38:47 +0200 Subject: [PATCH 0162/1733] Update cleanuparr.json --- frontend/public/json/cleanuparr.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frontend/public/json/cleanuparr.json b/frontend/public/json/cleanuparr.json index 2d3590b46..7303bf362 100644 --- a/frontend/public/json/cleanuparr.json +++ b/frontend/public/json/cleanuparr.json @@ -1,7 +1,9 @@ { "name": "Cleanuparr", "slug": "cleanuparr", - "categories": [14], + "categories": [ + 14 + ], "date_created": "2025-07-23", "type": "ct", "updateable": true, @@ -9,7 +11,7 @@ "interface_port": 11011, "documentation": null, "website": "https://github.com/Cleanuparr/Cleanuparr", - "logo": "https://raw.githubusercontent.com/Cleanuparr/Cleanuparr/main/src/Cleanuparr.Web/wwwroot/logo.png", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/cleanuparr.webp", "config_path": "/opt/cleanuparr/config ", "description": "Cleanuparr is a tool for automating the cleanup of unwanted or blocked files in Sonarr, Radarr, and supported download clients like qBittorrent, Transmission, and Deluge. It removes incomplete, blocked, or malicious downloads and can trigger replacement searches to ensure your media library stays complete and up-to-date.", "install_methods": [ From e45663bf3b13fbc65f2519dc5e1758a8eef21a1f Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 24 Jul 2025 13:44:04 +0200 Subject: [PATCH 0163/1733] Update frigate-install.sh --- install/frigate-install.sh | 74 +++++++++++++------------------------- 1 file changed, 24 insertions(+), 50 deletions(-) diff --git a/install/frigate-install.sh b/install/frigate-install.sh index 03a76de0d..582b249ed 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -13,35 +13,7 @@ setting_up_container network_check update_os -msg_info "Configure Debian Sources & Pinning (Stable > Backports)" -# APT sources im deb822-Format -cat >/etc/apt/sources.list.d/debian.sources <<'EOF' -Types: deb deb-src -URIs: http://deb.debian.org/debian -Suites: bookworm bookworm-updates bookworm-backports -Components: main contrib non-free non-free-firmware -Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg -EOF -# klassische sources.list auskommentieren -sed -i -e '/^deb-src /d' -e 's/^deb /#deb /' /etc/apt/sources.list - -# Pinning: Stable (bookworm) bevorzugt, Backports nur fallweise -cat >/etc/apt/preferences.d/stable-backports.pref <<'EOF' -Package: * -Pin: release a=bookworm -Pin-Priority: 700 - -Package: * -Pin: release a=bookworm-backports -Pin-Priority: 300 -EOF -msg_ok "Configured Debian Sources & APT pinning" - -msg_info "Prepare APT (repair & update)" -apt-get update -apt-get -f install -y || true -dpkg --configure -a || true -msg_ok "APT ready" +NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs msg_info "Installing Dependencies (Patience)" $STD apt-get install -y \ @@ -77,10 +49,6 @@ msg_ok "Set Up Hardware Acceleration" msg_info "Setup Frigate" RELEASE="0.16.0 Beta 4" -export DEBIAN_FRONTEND=noninteractive -echo "libedgetpu1-max libedgetpu/accepted-eula select true" | debconf-set-selections -echo "libedgetpu1-max libedgetpu/install-confirm-max select true" | debconf-set-selections - mkdir -p /opt/frigate/models curl -fsSL https://github.com/blakeblackshear/frigate/archive/refs/tags/v0.16.0-beta4.tar.gz -o frigate.tar.gz tar -xzf frigate.tar.gz -C /opt/frigate --strip-components 1 @@ -135,15 +103,15 @@ fi echo "tmpfs /tmp/cache tmpfs defaults 0 0" >>/etc/fstab msg_ok "Installed Frigate $RELEASE" -# read -p "Semantic Search requires a dedicated GPU and at least 16GB RAM. Would you like to install it? (y/n): " semantic_choice -# if [[ "$semantic_choice" == "y" ]]; then -# msg_info "Configuring Semantic Search & AI Models" -# mkdir -p /opt/frigate/models/semantic_search -# curl -fsSL -o /opt/frigate/models/semantic_search/clip_model.pt https://huggingface.co/openai/clip-vit-base-patch32/resolve/main/pytorch_model.bin -# msg_ok "Semantic Search Models Installed" -# else -# msg_ok "Skipped Semantic Search Setup" -# fi +read -p "Semantic Search requires a dedicated GPU and at least 16GB RAM. Would you like to install it? (y/n): " semantic_choice +if [[ "$semantic_choice" == "y" ]]; then + msg_info "Configuring Semantic Search & AI Models" + mkdir -p /opt/frigate/models/semantic_search + curl -fsSL -o /opt/frigate/models/semantic_search/clip_model.pt https://huggingface.co/openai/clip-vit-base-patch32/resolve/main/pytorch_model.bin + msg_ok "Semantic Search Models Installed" +else + msg_ok "Skipped Semantic Search Setup" +fi msg_info "Building and Installing libUSB without udev" wget -qO /tmp/libusb.zip https://github.com/libusb/libusb/archive/v1.0.29.zip @@ -187,17 +155,23 @@ mkdir -p /media/frigate wget -qO /media/frigate/person-bicycle-car-detection.mp4 https://github.com/intel-iot-devkit/sample-videos/raw/master/person-bicycle-car-detection.mp4 msg_ok "Installed Coral Object Detection Model" -msg_info "Building Nginx with Custom Modules" - +msg_info "Temporarily enable deb-src for Nginx build" +cat >/etc/apt/sources.list.d/nginx-debsrc.list <<'EOF' +deb-src http://deb.debian.org/debian bookworm main +EOF $STD apt-get update -$STD apt-get -f install -y || true -$STD dpkg --configure -a || true +msg_ok "Enabled deb-src for Nginx" -for pkg in $(apt-mark showhold); do - echo "Unholding $pkg" - apt-mark unhold "$pkg" -done +msg_info "Installing Nginx build-dependencies" +$STD apt-get -yqq build-dep nginx +msg_ok "Installed Nginx build-dependencies" +msg_info "Cleanup temporary deb-src" +rm -f /etc/apt/sources.list.d/nginx-debsrc.list +$STD apt-get update +msg_ok "Removed temporary deb-src" + +msg_info "Building Nginx with Custom Modules" $STD /opt/frigate/docker/main/build_nginx.sh sed -e '/s6-notifyoncheck/ s/^#*/#/' -i /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/run ln -sf /usr/local/nginx/sbin/nginx /usr/local/bin/nginx From 118dc393e184abcd4833e2096ae1e3ed2800a011 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 24 Jul 2025 13:46:49 +0200 Subject: [PATCH 0164/1733] Update alpine-teamspeak-server-install.sh --- install/alpine-teamspeak-server-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/alpine-teamspeak-server-install.sh b/install/alpine-teamspeak-server-install.sh index af311467d..9a7752f34 100644 --- a/install/alpine-teamspeak-server-install.sh +++ b/install/alpine-teamspeak-server-install.sh @@ -20,7 +20,7 @@ $STD apk add --no-cache \ libc6-compat msg_ok "Installed dependencies" -RELEASE=$(curl -fsSL https://teamspeak.com/en/downloads/#server | sed -n 's/.*teamspeak3-server_linux_amd64-\([0-9]\+\.[0-9]\+\.[0-9]\+\).*/\1/p' | head -1) +RELEASE=$(curl -fsSL https://teamspeak.com/en/downloads/#server | sed -n 's/.*teamspeak3-server_linux_amd64-\([0-9]\+\.[0-9]\+\.[0-9]\+\).*/\1/p;q') msg_info "Installing Teamspeak Server v${RELEASE}" mkdir -p /opt/teamspeak-server From e3006ecbf3569af9f3be0f97694503021be6b67e Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 24 Jul 2025 13:57:38 +0200 Subject: [PATCH 0165/1733] Update TServer --- ct/alpine-teamspeak-server.sh | 3 ++- install/alpine-teamspeak-server-install.sh | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ct/alpine-teamspeak-server.sh b/ct/alpine-teamspeak-server.sh index 1fee6cdc3..204649551 100644 --- a/ct/alpine-teamspeak-server.sh +++ b/ct/alpine-teamspeak-server.sh @@ -27,7 +27,8 @@ function update_script() { exit 1 fi - RELEASE=$(curl -fsSL https://teamspeak.com/en/downloads/#server | sed -n 's/.*teamspeak3-server_linux_amd64-\([0-9]\+\.[0-9]\+\.[0-9]\+\).*/\1/p' | head -1) + RELEASE=$(curl -fsSL https://teamspeak.com/en/downloads/#server | sed -n '/teamspeak3-server_linux_amd64-/ { s/.*teamspeak3-server_linux_amd64-\([0-9]\+\.[0-9]\+\.[0-9]\+\).*/\1/p; q }') + if [ "${RELEASE}" != "$(cat ~/.teamspeak-server)" ] || [ ! -f ~/.teamspeak-server ]; then msg_info "Updating ${APP} LXC" $STD apk -U upgrade diff --git a/install/alpine-teamspeak-server-install.sh b/install/alpine-teamspeak-server-install.sh index 9a7752f34..b99642153 100644 --- a/install/alpine-teamspeak-server-install.sh +++ b/install/alpine-teamspeak-server-install.sh @@ -20,7 +20,7 @@ $STD apk add --no-cache \ libc6-compat msg_ok "Installed dependencies" -RELEASE=$(curl -fsSL https://teamspeak.com/en/downloads/#server | sed -n 's/.*teamspeak3-server_linux_amd64-\([0-9]\+\.[0-9]\+\.[0-9]\+\).*/\1/p;q') +RELEASE=$(curl -fsSL https://teamspeak.com/en/downloads/#server | sed -n '/teamspeak3-server_linux_amd64-/ { s/.*teamspeak3-server_linux_amd64-\([0-9]\+\.[0-9]\+\.[0-9]\+\).*/\1/p; q }') msg_info "Installing Teamspeak Server v${RELEASE}" mkdir -p /opt/teamspeak-server From 110c6d2544d54a6e88f3f4e1e14cd95cb94a2e91 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 24 Jul 2025 14:03:26 +0200 Subject: [PATCH 0166/1733] Add TSServer json --- frontend/public/json/teamspeak-server.json | 51 ++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 frontend/public/json/teamspeak-server.json diff --git a/frontend/public/json/teamspeak-server.json b/frontend/public/json/teamspeak-server.json new file mode 100644 index 000000000..7886d8a2a --- /dev/null +++ b/frontend/public/json/teamspeak-server.json @@ -0,0 +1,51 @@ +{ + "name": "Teamspeak-Server", + "slug": "teamspeak-server", + "categories": [ + 24 + ], + "date_created": "2025-07-21", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 9987, + "documentation": "https://support.teamspeak.com/hc/en-us/categories/360000302017-TeamSpeak-3", + "website": "https://teamspeak.com/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/teamspeak-light.webp", + "config_path": "", + "description": "TeamSpeak is a voice over IP (VoIP) application, primarily used by gamers and teams to chat in real time on dedicated servers. It delivers crystal‑clear, low‑latency voice communication.", + "install_methods": [ + { + "type": "default", + "script": "ct/teamspeak-server.sh", + "resources": { + "cpu": 1, + "ram": 512, + "hdd": 2, + "os": "debian", + "version": "12" + } + }, + { + "type": "alpine", + "script": "ct/alpine-teamspeak-server.sh", + "resources": { + "cpu": 1, + "ram": 256, + "hdd": 2, + "os": "alpine", + "version": "3.22" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Use `journalctl -u teamspeak-server.service` inside LXC console to check for admin credentials!", + "type": "info" + } + ] +} From 7f846f9fa3b6eae1b8dcabd86c03c652f5a9c3a1 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 24 Jul 2025 14:05:02 +0200 Subject: [PATCH 0167/1733] Update frigate-install.sh --- install/frigate-install.sh | 40 ++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/install/frigate-install.sh b/install/frigate-install.sh index 582b249ed..ee0ccab85 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -60,7 +60,9 @@ $STD pip3 wheel --wheel-dir=/wheels -r /opt/frigate/docker/main/requirements-whe pip3 install -U /wheels/*.whl cp -a /opt/frigate/docker/main/rootfs/. / export TARGETARCH="amd64" -echo 'libc6 libraries/restart-without-asking boolean true' | debconf-set-selections +export DEBIAN_FRONTEND=noninteractive +echo "libedgetpu1-max libedgetpu/accepted-eula select true" | debconf-set-selections +echo "libedgetpu1-max libedgetpu/install-confirm-max select true" | debconf-set-selections $STD /opt/frigate/docker/main/install_deps.sh $STD apt update $STD ln -svf /usr/lib/btbn-ffmpeg/bin/ffmpeg /usr/local/bin/ffmpeg @@ -103,15 +105,15 @@ fi echo "tmpfs /tmp/cache tmpfs defaults 0 0" >>/etc/fstab msg_ok "Installed Frigate $RELEASE" -read -p "Semantic Search requires a dedicated GPU and at least 16GB RAM. Would you like to install it? (y/n): " semantic_choice -if [[ "$semantic_choice" == "y" ]]; then - msg_info "Configuring Semantic Search & AI Models" - mkdir -p /opt/frigate/models/semantic_search - curl -fsSL -o /opt/frigate/models/semantic_search/clip_model.pt https://huggingface.co/openai/clip-vit-base-patch32/resolve/main/pytorch_model.bin - msg_ok "Semantic Search Models Installed" -else - msg_ok "Skipped Semantic Search Setup" -fi +# read -p "Semantic Search requires a dedicated GPU and at least 16GB RAM. Would you like to install it? (y/n): " semantic_choice +# if [[ "$semantic_choice" == "y" ]]; then +# msg_info "Configuring Semantic Search & AI Models" +# mkdir -p /opt/frigate/models/semantic_search +# curl -fsSL -o /opt/frigate/models/semantic_search/clip_model.pt https://huggingface.co/openai/clip-vit-base-patch32/resolve/main/pytorch_model.bin +# msg_ok "Semantic Search Models Installed" +# else +# msg_ok "Skipped Semantic Search Setup" +# fi msg_info "Building and Installing libUSB without udev" wget -qO /tmp/libusb.zip https://github.com/libusb/libusb/archive/v1.0.29.zip @@ -160,22 +162,18 @@ cat >/etc/apt/sources.list.d/nginx-debsrc.list <<'EOF' deb-src http://deb.debian.org/debian bookworm main EOF $STD apt-get update -msg_ok "Enabled deb-src for Nginx" +msg_ok "deb-src enabled" -msg_info "Installing Nginx build-dependencies" -$STD apt-get -yqq build-dep nginx -msg_ok "Installed Nginx build-dependencies" +msg_info "Building Nginx with Custom Modules" +$STD bash /opt/frigate/docker/main/build_nginx.sh +sed -e '/s6-notifyoncheck/ s/^#*/#/' -i /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/run +ln -sf /usr/local/nginx/sbin/nginx /usr/local/bin/nginx +msg_ok "Built Nginx" msg_info "Cleanup temporary deb-src" rm -f /etc/apt/sources.list.d/nginx-debsrc.list $STD apt-get update -msg_ok "Removed temporary deb-src" - -msg_info "Building Nginx with Custom Modules" -$STD /opt/frigate/docker/main/build_nginx.sh -sed -e '/s6-notifyoncheck/ s/^#*/#/' -i /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/run -ln -sf /usr/local/nginx/sbin/nginx /usr/local/bin/nginx -msg_ok "Built Nginx" +msg_ok "Temporary deb-src removed" msg_info "Installing Tempio" sed -i 's|/rootfs/usr/local|/usr/local|g' /opt/frigate/docker/main/install_tempio.sh From e11e39b2780bd3973467a8d56693fd5958a5803b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 24 Jul 2025 14:26:01 +0200 Subject: [PATCH 0168/1733] Update frigate-install.sh --- install/frigate-install.sh | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/install/frigate-install.sh b/install/frigate-install.sh index ee0ccab85..66e9ce684 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -115,6 +115,8 @@ msg_ok "Installed Frigate $RELEASE" # msg_ok "Skipped Semantic Search Setup" # fi +fetch_and_ + msg_info "Building and Installing libUSB without udev" wget -qO /tmp/libusb.zip https://github.com/libusb/libusb/archive/v1.0.29.zip unzip -q /tmp/libusb.zip -d /tmp/ @@ -157,12 +159,20 @@ mkdir -p /media/frigate wget -qO /media/frigate/person-bicycle-car-detection.mp4 https://github.com/intel-iot-devkit/sample-videos/raw/master/person-bicycle-car-detection.mp4 msg_ok "Installed Coral Object Detection Model" -msg_info "Temporarily enable deb-src for Nginx build" -cat >/etc/apt/sources.list.d/nginx-debsrc.list <<'EOF' -deb-src http://deb.debian.org/debian bookworm main +msg_info "Ensure /etc/apt/sources.list.d/debian.sources exists with deb-src" +mkdir -p /etc/apt/sources.list.d +cat >/etc/apt/sources.list.d/debian.sources <<'EOF' +Types: deb deb-src +URIs: http://deb.debian.org/debian +Suites: bookworm +Components: main +Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg EOF +msg_ok "Stub /etc/apt/sources.list.d/debian.sources created" + +msg_info "Updating APT cache" $STD apt-get update -msg_ok "deb-src enabled" +msg_ok "APT cache updated" msg_info "Building Nginx with Custom Modules" $STD bash /opt/frigate/docker/main/build_nginx.sh @@ -170,10 +180,10 @@ sed -e '/s6-notifyoncheck/ s/^#*/#/' -i /opt/frigate/docker/main/rootfs/etc/s6-o ln -sf /usr/local/nginx/sbin/nginx /usr/local/bin/nginx msg_ok "Built Nginx" -msg_info "Cleanup temporary deb-src" -rm -f /etc/apt/sources.list.d/nginx-debsrc.list +msg_info "Cleanup stub debian.sources" +rm -f /etc/apt/sources.list.d/debian.sources $STD apt-get update -msg_ok "Temporary deb-src removed" +msg_ok "Removed stub and updated APT cache" msg_info "Installing Tempio" sed -i 's|/rootfs/usr/local|/usr/local|g' /opt/frigate/docker/main/install_tempio.sh From 40cea78c0d8b88692cc23eb1e70d79bf8909e390 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 24 Jul 2025 14:26:55 +0200 Subject: [PATCH 0169/1733] Update frigate-install.sh --- install/frigate-install.sh | 53 +++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/install/frigate-install.sh b/install/frigate-install.sh index 66e9ce684..ce0d27246 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -63,6 +63,33 @@ export TARGETARCH="amd64" export DEBIAN_FRONTEND=noninteractive echo "libedgetpu1-max libedgetpu/accepted-eula select true" | debconf-set-selections echo "libedgetpu1-max libedgetpu/install-confirm-max select true" | debconf-set-selections + +msg_info "Ensure /etc/apt/sources.list.d/debian.sources exists with deb-src" +mkdir -p /etc/apt/sources.list.d +cat >/etc/apt/sources.list.d/debian.sources <<'EOF' +Types: deb deb-src +URIs: http://deb.debian.org/debian +Suites: bookworm +Components: main +Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg +EOF +msg_ok "Stub /etc/apt/sources.list.d/debian.sources created" + +msg_info "Updating APT cache" +$STD apt-get update +msg_ok "APT cache updated" + +msg_info "Building Nginx with Custom Modules" +$STD bash /opt/frigate/docker/main/build_nginx.sh +sed -e '/s6-notifyoncheck/ s/^#*/#/' -i /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/run +ln -sf /usr/local/nginx/sbin/nginx /usr/local/bin/nginx +msg_ok "Built Nginx" + +msg_info "Cleanup stub debian.sources" +rm -f /etc/apt/sources.list.d/debian.sources +$STD apt-get update +msg_ok "Removed stub and updated APT cache" + $STD /opt/frigate/docker/main/install_deps.sh $STD apt update $STD ln -svf /usr/lib/btbn-ffmpeg/bin/ffmpeg /usr/local/bin/ffmpeg @@ -159,32 +186,6 @@ mkdir -p /media/frigate wget -qO /media/frigate/person-bicycle-car-detection.mp4 https://github.com/intel-iot-devkit/sample-videos/raw/master/person-bicycle-car-detection.mp4 msg_ok "Installed Coral Object Detection Model" -msg_info "Ensure /etc/apt/sources.list.d/debian.sources exists with deb-src" -mkdir -p /etc/apt/sources.list.d -cat >/etc/apt/sources.list.d/debian.sources <<'EOF' -Types: deb deb-src -URIs: http://deb.debian.org/debian -Suites: bookworm -Components: main -Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg -EOF -msg_ok "Stub /etc/apt/sources.list.d/debian.sources created" - -msg_info "Updating APT cache" -$STD apt-get update -msg_ok "APT cache updated" - -msg_info "Building Nginx with Custom Modules" -$STD bash /opt/frigate/docker/main/build_nginx.sh -sed -e '/s6-notifyoncheck/ s/^#*/#/' -i /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/run -ln -sf /usr/local/nginx/sbin/nginx /usr/local/bin/nginx -msg_ok "Built Nginx" - -msg_info "Cleanup stub debian.sources" -rm -f /etc/apt/sources.list.d/debian.sources -$STD apt-get update -msg_ok "Removed stub and updated APT cache" - msg_info "Installing Tempio" sed -i 's|/rootfs/usr/local|/usr/local|g' /opt/frigate/docker/main/install_tempio.sh TARGETARCH="amd64" From af38bb8f96b0063749446e82aea40931d081df6b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 24 Jul 2025 14:43:38 +0200 Subject: [PATCH 0170/1733] Update frigate-install.sh --- install/frigate-install.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/install/frigate-install.sh b/install/frigate-install.sh index ce0d27246..0ca7c1bd9 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -142,8 +142,6 @@ msg_ok "Installed Frigate $RELEASE" # msg_ok "Skipped Semantic Search Setup" # fi -fetch_and_ - msg_info "Building and Installing libUSB without udev" wget -qO /tmp/libusb.zip https://github.com/libusb/libusb/archive/v1.0.29.zip unzip -q /tmp/libusb.zip -d /tmp/ From a25d5534ffb114e53658c39dfca2e3283ff6f68e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 24 Jul 2025 14:51:37 +0200 Subject: [PATCH 0171/1733] Update frigate-install.sh --- install/frigate-install.sh | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/install/frigate-install.sh b/install/frigate-install.sh index 0ca7c1bd9..fd17b6fd7 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -13,8 +13,6 @@ setting_up_container network_check update_os -NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs - msg_info "Installing Dependencies (Patience)" $STD apt-get install -y \ git automake build-essential xz-utils libtool ccache pkg-config \ @@ -30,13 +28,10 @@ $STD apt-get install -y \ $STD pip install --upgrade pip msg_ok "Setup Python3" -msg_info "Installing go2rtc" -mkdir -p /usr/local/go2rtc/bin -cd /usr/local/go2rtc/bin -curl -fsSL "https://github.com/AlexxIT/go2rtc/releases/latest/download/go2rtc_linux_amd64" -o go2rtc -chmod +x go2rtc -ln -sf /usr/local/go2rtc/bin/go2rtc /usr/local/bin/go2rtc -msg_ok "Installed go2rtc" +NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs +fetch_and_deploy_gh_release "go2rtc" "AlexxIT/go2rtc" "singlefile" "latest" "/usr/local/go2rtc/bin" "go2rtc_linux_amd64" +fetch_and_deploy_gh_release "frigate" "blakeblackshear/frigate" "tarball" "v0.16.0-beta4" "/opt/frigate" +fetch_and_deploy_gh_release "libusb" "libusb/libusb" "tarball" "v1.0.29" "/opt/frigate/libusb" msg_info "Setting Up Hardware Acceleration" $STD apt-get -y install {va-driver-all,ocl-icd-libopencl1,intel-opencl-icd,vainfo,intel-gpu-tools} @@ -48,11 +43,7 @@ fi msg_ok "Set Up Hardware Acceleration" msg_info "Setup Frigate" -RELEASE="0.16.0 Beta 4" -mkdir -p /opt/frigate/models -curl -fsSL https://github.com/blakeblackshear/frigate/archive/refs/tags/v0.16.0-beta4.tar.gz -o frigate.tar.gz -tar -xzf frigate.tar.gz -C /opt/frigate --strip-components 1 -rm -rf frigate.tar.gz +ln -sf /usr/local/go2rtc/bin/go2rtc /usr/local/bin/go2rtc cd /opt/frigate $STD pip install -r /opt/frigate/docker/main/requirements.txt --break-system-packages $STD pip install -r /opt/frigate/docker/main/requirements-ov.txt --break-system-packages @@ -130,7 +121,7 @@ else sed -i -e 's/^kvm:x:104:$/render:x:104:frigate/' -e 's/^render:x:105:$/kvm:x:105:/' /etc/group fi echo "tmpfs /tmp/cache tmpfs defaults 0 0" >>/etc/fstab -msg_ok "Installed Frigate $RELEASE" +msg_ok "Installed Frigate" # read -p "Semantic Search requires a dedicated GPU and at least 16GB RAM. Would you like to install it? (y/n): " semantic_choice # if [[ "$semantic_choice" == "y" ]]; then @@ -158,14 +149,11 @@ msg_info "Installing Coral Object Detection Model (Patience)" cd /opt/frigate export CCACHE_DIR=/root/.ccache export CCACHE_MAXSIZE=2G -curl -L -o v1.0.29.zip https://github.com/libusb/libusb/archive/v1.0.29.zip -unzip -q v1.0.29.zip -rm v1.0.29.zip -cd libusb-1.0.29 +cd libusb $STD ./bootstrap.sh $STD ./configure --disable-udev --enable-shared $STD make -j $(nproc --all) -cd /opt/frigate/libusb-1.0.29/libusb +cd /opt/frigate/libusb/libusb mkdir -p /usr/local/lib $STD /bin/bash ../libtool --mode=install /usr/bin/install -c libusb-1.0.la '/usr/local/lib' mkdir -p /usr/local/include/libusb-1.0 From 78425ebfb9712b252cb9df0f79083ef844e68954 Mon Sep 17 00:00:00 2001 From: dellthePROgrammer Date: Thu, 24 Jul 2025 13:14:29 -0400 Subject: [PATCH 0172/1733] Update installer to help with Cloudflare and Helper Scripts - Created 4 helper scripts to quickly add, edit, enable, and disable site configs from a display menu using whiptail. Commands are addsite, ensite, dissite, editsite - Added cloudflare ips if using cloudflare as DNS Default: Commented out - Added serverTransport in config to allow for traefik to skip the "insecure" screen to display a site Default: Commented out - Sets https as default entrypoint --- install/traefik-install.sh | 268 +++++++++++++++++++++++++++++++++++++ 1 file changed, 268 insertions(+) create mode 100644 install/traefik-install.sh diff --git a/install/traefik-install.sh b/install/traefik-install.sh new file mode 100644 index 000000000..0fc8d8f72 --- /dev/null +++ b/install/traefik-install.sh @@ -0,0 +1,268 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://traefik.io/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt-get install -y apt-transport-https +msg_ok "Installed Dependencies" + +RELEASE=$(curl -fsSL https://api.github.com/repos/traefik/traefik/releases | grep -oP '"tag_name":\s*"v\K[\d.]+?(?=")' | sort -V | tail -n 1) +msg_info "Installing Traefik v${RELEASE}" +mkdir -p /etc/traefik/{conf.d,ssl,sites-available} +curl -fsSL "https://github.com/traefik/traefik/releases/download/v${RELEASE}/traefik_v${RELEASE}_linux_amd64.tar.gz" -o "traefik_v${RELEASE}_linux_amd64.tar.gz" +tar -C /tmp -xzf traefik*.tar.gz +mv /tmp/traefik /usr/bin/ +rm -rf traefik*.tar.gz +echo "${RELEASE}" >/opt/${APPLICATION}_version.txt +msg_ok "Installed Traefik v${RELEASE}" + +msg_info "Creating Traefik configuration" +cat </etc/traefik/traefik.yaml +providers: + file: + directory: /etc/traefik/conf.d/ + watch: true + +entryPoints: + web: + address: ':80' + http: + redirections: + entryPoint: + to: websecure + scheme: https + websecure: + address: ':443' + http: + tls: + certResolver: letsencrypt + # Uncomment below if using cloudflare + /* + forwardedHeaders: + trustedIPs: + - 173.245.48.0/20 + - 103.21.244.0/22 + - 103.22.200.0/22 + - 103.31.101.64/22 + - 141.101.64.0/18 + - 108.162.192.0/18 + - 190.93.240.0/20 + - 188.114.96.0/20 + - 197.234.240.0/22 + - 198.41.128.0/17 + - 162.158.0.0/15 + - 104.16.0.0/13 + - 104.16.0.0/13 + - 172.64.0.0/13 + - 131.0.72.0/22 + */ + asDefault: true + traefik: + address: ':8080' + +certificatesResolvers: + letsencrypt: + acme: + email: "foo@bar.com" + storage: /etc/traefik/ssl/acme.json + tlsChallenge: {} + +# Uncomment below if you are using self signed or no certificate +#serversTransport: +# insecureSkipVerify: true + +api: + dashboard: true + insecure: true + +log: + filePath: /var/log/traefik/traefik.log + format: json + level: INFO + +accessLog: + filePath: /var/log/traefik/traefik-access.log + format: json + filters: + statusCodes: + - "200" + - "400-599" + retryAttempts: true + minDuration: "10ms" + bufferingSize: 0 + fields: + headers: + defaultMode: drop + names: + User-Agent: keep +EOF +msg_ok "Created Traefik configuration" + +msg_info "Creating Service" +cat </etc/systemd/system/traefik.service +[Unit] +Description=Traefik is an open-source Edge Router that makes publishing your services a fun and easy experience + +[Service] +Type=notify +ExecStart=/usr/bin/traefik --configFile=/etc/traefik/traefik.yaml +Restart=on-failure +ExecReload=/bin/kill -USR1 \$MAINPID + +[Install] +WantedBy=multi-user.target +EOF + +systemctl enable -q --now traefik +msg_ok "Created Service" + +msg_info "Creating site templates" +cat </etc/traefik/template.yaml.tpl +http: + routers: + ${hostname}: + rule: Host(`${FQDN}`) + service: ${hostname} + tls: + certResolver: letsencrypt + services: + ${hostname}: + loadbalancer: + servers: + - url: "${URL}" +EOF +msg_ok: "Template Created" +msg_info: "Creating Helper Scripts" +cat </usr/bin/addsite +#!/bin/bash + +function setup_site() { + hostname="$(whiptail --inputbox "Enter the hostname of the Site" 8 78 --title "Hostname" 3>&1 1>&2 2>&3)" + exitstatus=$? + [[ "$exitstatus" = 1 ]] && return; + FQDN="$(whiptail --inputbox "Enter the FQDN of the Site" 8 78 --title "FQDN" 3>&1 1>&2 2>&3)" + exitstatus=$? + [[ "$exitstatus" = 1 ]] && return; + URL="$(whiptail --inputbox "Enter the URL of the Site (For example http://192.168.x.x:8080)" 8 78 --title "URL" 3>&1 1>&2 2>&3)" + exitstatus=$? + [[ "$exitstatus" = 1 ]] && return; + filename="/etc/traefik/sites-available/${hostname}.yaml" + export hostname FQDN URL + envsubst '${hostname} ${FQDN} ${URL}' < /etc/traefik/template.yaml.tpl > ${filename} +} + +setup_site +EOF +cat </usr/bin/ensite +#!/bin/bash + +function ensite() { + DIR="/etc/traefik/sites-available" + files=( "$DIR"/* ) + + opts=() + for f in "${files[@]}"; do + name="${f##*/}" + opts+=( "$name" "" ) + done + + choice=$(whiptail \ + --title "Select an entry" \ + --menu "Choose a site" \ + 20 60 12 \ + "${opts[@]}" \ + 3>&1 1>&2 2>&3) + + if [ $? -eq 0 ]; then + ln -s $DIR/$choice /etc/traefik/conf.d + else + return + fi +} + +ensite +EOF +cat </usr/bin/dissite +#!/bin/bash + +function dissite() { + DIR="/etc/traefik/conf.d" + files=( "$DIR"/* ) + + opts=() + for f in "${files[@]}"; do + name="${f##*/}" + opts+=( "$name" "" ) + done + + choice=$(whiptail \ + --title "Select an entry" \ + --menu "Choose a site" \ + 20 60 12 \ + "${opts[@]}" \ + 3>&1 1>&2 2>&3) + + if [ $? -eq 0 ]; then + rm $DIR/$choice + else + return + fi +} + +dissite +EOF + +cat </usr/bin/editsite +#!/bin/bash + +function edit_site() { + DIR="/etc/traefik/sites-available" + files=( "$DIR"/* ) + + opts=() + for f in "${files[@]}"; do + name="${f##*/}" + opts+=( "$name" "" ) + done + + choice=$(whiptail \ + --title "Select an entry" \ + --menu "Choose a site" \ + 20 60 12 \ + "${opts[@]}" \ + 3>&1 1>&2 2>&3) + + if [ $? -eq 0 ]; then + nano $DIR/$choice + else + return + fi +} + +edit_site +EOF +msg_ok "Helper Scripts Created" +msg_info "Commands available are as below:" +msg_info "addsite - creating a config" +msg_info "ensite - enables a config" +msg_info "dissite - disables a config" +msg_info "editsite - edits a config" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 2e69aad63e4f170a6c39e6e03c178b44f2d0251d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 24 Jul 2025 19:58:18 +0200 Subject: [PATCH 0173/1733] change some things for keycloak --- ct/keycloak.sh | 86 +++++++++++++++++++++++++++----------------------- 1 file changed, 46 insertions(+), 40 deletions(-) diff --git a/ct/keycloak.sh b/ct/keycloak.sh index 8d897845c..66e8e9226 100644 --- a/ct/keycloak.sh +++ b/ct/keycloak.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/remz1337/ProxmoxVED/pr-keycloak/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 tteck # Author: tteck (tteckster) | Co-Author: remz1337 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -20,43 +20,49 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -f /etc/systemd/system/keycloak.service ]]; then - msg_error "No ${APP} Installation Found!" + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/keycloak ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + RELEASE=$(curl -fsSL https://api.github.com/repos/keycloak/keycloak/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + if [[ "${RELEASE}" != "$(cat ~/.keycloak 2>/dev/null)" ]] || [[ ! -f ~/.keycloak ]]; then + msg_info "Stopping ${APP}" + systemctl stop keycloak + msg_ok "Stopped ${APP}" + + msg_info "Updating packages" + apt-get update &>/dev/null + apt-get -y upgrade &>/dev/null + msg_ok "Updated packages" + + msg_info "Backup old Keycloak" + cd /opt + mv keycloak keycloak.old + tar -czf keycloak_conf_backup.tar.gz keycloak.old/conf + msg_ok "Backup done" + + fetch_and_deploy_gh_release "keycloak" "keycloak/keycloak" "prebuild" "latest" "/opt/keycloak" "keycloak-*.tar.gz" + + msg_info "Updating ${APP}" + cd /opt + mv keycloak_conf_backup.tar.gz keycloak/conf + cp -r keycloak.old/providers keycloak + cp -r keycloak.old/themes keycloak + rm -rf keycloak.old + msg_ok "Updated ${APP} LXC" + + msg_info "Restating Keycloak" + systemctl restart keycloak + msg_ok "Restated Keycloak" + msg_ok "Update Successful" + else + msg_ok "No update required. ${APP} is already at v${RELEASE}" + fi exit - fi - - msg_info "Stopping ${APP}" - systemctl stop keycloak - msg_ok "Stopped ${APP}" - - msg_info "Updating packages" - apt-get update &>/dev/null - apt-get -y upgrade &>/dev/null - msg_ok "Updated packages" - - msg_info "Backup old Keycloak" - cd /opt - mv keycloak keycloak.old - tar -czf keycloak_conf_backup.tar.gz keycloak.old/conf - msg_ok "Backup done" - - fetch_and_deploy_gh_release "keycloak" "keycloak/keycloak" "prebuild" "latest" "/opt/keycloak" "keycloak-*.tar.gz" - - msg_info "Updating ${APP}" - cd /opt - mv keycloak_conf_backup.tar.gz keycloak/conf - cp -r keycloak.old/providers keycloak - cp -r keycloak.old/themes keycloak - rm -rf keycloak.old - msg_ok "Updated ${APP} LXC" - - msg_info "Restating Keycloak" - systemctl restart keycloak - msg_ok "Restated Keycloak" - exit } start @@ -67,6 +73,6 @@ msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080/admin${CL}" -echo -e "${TAB}${GN}Temporary admin user:${BL}tmpadm${CL}" -echo -e "${TAB}${GN}Temporary admin password:${BL}admin123${CL}" -echo -e "${INFO}${YW} If you modified ${BL}cache-ispn.xml${YW}: Re-apply your changes to the new file, otherwise leave it unchanged.${CL}" +#echo -e "${TAB}${GN}Temporary admin user:${BL}tmpadm${CL}" +#echo -e "${TAB}${GN}Temporary admin password:${BL}admin123${CL}" +#echo -e "${INFO}${YW} If you modified ${BL}cache-ispn.xml${YW}: Re-apply your changes to the new file, otherwise leave it unchanged.${CL}" From 7a8d9e9c88b296b2b4ee8be988ffaa886450873f Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 24 Jul 2025 20:00:41 +0200 Subject: [PATCH 0174/1733] --- --- install/keycloak-install.sh | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/install/keycloak-install.sh b/install/keycloak-install.sh index 6d1deb162..8d82d16a0 100644 --- a/install/keycloak-install.sh +++ b/install/keycloak-install.sh @@ -16,14 +16,20 @@ update_os JAVA_VERSION=21 setup_java PG_VERSION=16 setup_postgresql -msg_info "Configuring PostgreSQL user" +msg_info "Configuring PostgreSQL" DB_NAME="keycloak" DB_USER="keycloak" DB_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" $STD sudo -u postgres psql -c "CREATE USER $DB_USER WITH PASSWORD '$DB_PASS';" $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8';" $STD sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER;" -msg_ok "Configured PostgreSQL user" +{ + echo "Keycloak Credentials" + echo "Database User: $DB_USER" + echo "Database Password: $DB_PASS" + echo "Database Name: $DB_NAME" +} >>~/keycloak.creds +msg_ok "Configured PostgreSQL" fetch_and_deploy_gh_release "keycloak" "keycloak/keycloak" "prebuild" "latest" "/opt/keycloak" "keycloak-*.tar.gz" From e0099b9a81b7adad8379dca6e5b4fff86b692097 Mon Sep 17 00:00:00 2001 From: dellthePROgrammer Date: Thu, 24 Jul 2025 14:40:32 -0400 Subject: [PATCH 0175/1733] Move User display for post install Moved command list to post install --- ct/traefik.sh | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 ct/traefik.sh diff --git a/ct/traefik.sh b/ct/traefik.sh new file mode 100644 index 000000000..fe4d80f92 --- /dev/null +++ b/ct/traefik.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://traefik.io/ + +APP="Traefik" +var_tags="${var_tags:-proxy}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-512}" +var_disk="${var_disk:-2}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -f /etc/systemd/system/traefik.service ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + RELEASE=$(curl -fsSL https://api.github.com/repos/traefik/traefik/releases | grep -oP '"tag_name":\s*"v\K[\d.]+?(?=")' | sort -V | tail -n 1) + msg_info "Updating $APP LXC" + if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then + curl -fsSL "https://github.com/traefik/traefik/releases/download/v${RELEASE}/traefik_v${RELEASE}_linux_amd64.tar.gz" -o $(basename "https://github.com/traefik/traefik/releases/download/v${RELEASE}/traefik_v${RELEASE}_linux_amd64.tar.gz") + tar -C /tmp -xzf traefik*.tar.gz + mv /tmp/traefik /usr/bin/ + rm -rf traefik*.tar.gz + systemctl restart traefik.service + echo "${RELEASE}" >/opt/${APP}_version.txt + msg_ok "Updated $APP LXC" + else + msg_ok "No update required. ${APP} is already at ${RELEASE}" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" +echo -e "Commands available are as below:" +echo -e "addsite - creating a config" +echo -e "ensite - enables a config" +echo -e "dissite - disables a config" +echo -e "editsite - edits a config" From 998b0ed39f91f1a5ba8329b6aefd7fd5b74418bc Mon Sep 17 00:00:00 2001 From: dellthePROgrammer Date: Thu, 24 Jul 2025 14:41:16 -0400 Subject: [PATCH 0176/1733] Moved post install info to updater Moved post install commands list to after installation script ends --- install/traefik-install.sh | 5 ----- 1 file changed, 5 deletions(-) diff --git a/install/traefik-install.sh b/install/traefik-install.sh index 0fc8d8f72..7507a8a85 100644 --- a/install/traefik-install.sh +++ b/install/traefik-install.sh @@ -253,11 +253,6 @@ function edit_site() { edit_site EOF msg_ok "Helper Scripts Created" -msg_info "Commands available are as below:" -msg_info "addsite - creating a config" -msg_info "ensite - enables a config" -msg_info "dissite - disables a config" -msg_info "editsite - edits a config" motd_ssh customize From 3b47d0359af40f5c0adf35d3d725182dd5b66a2b Mon Sep 17 00:00:00 2001 From: Jeroen Date: Thu, 24 Jul 2025 23:58:41 +0200 Subject: [PATCH 0177/1733] Created execute.sh to execute the same command inside multiple containers --- tools/pve/execute.sh | 84 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 tools/pve/execute.sh diff --git a/tools/pve/execute.sh b/tools/pve/execute.sh new file mode 100644 index 000000000..fc19be981 --- /dev/null +++ b/tools/pve/execute.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: jeroenzwart +# License: MIT +# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE + +function header_info() { + clear + cat <<"EOF" + ______ __ __ _ ________ + / ____/ _____ _______ __/ /____ / / | |/ / ____/ + / __/ | |/_/ _ \/ ___/ / / / __/ _ \ / / | / / + / /____> MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=${#ITEM}+OFFSET + EXCLUDE_MENU+=("$TAG" "$ITEM " "OFF") +done < <(pct list | awk 'NR>1') +excluded_containers=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Containers on $NODE" --checklist "\nSelect containers to skip from executing:\n" \ + 16 $((MSG_MAX_LENGTH + 23)) 6 "${EXCLUDE_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"') + +if [ $? -ne 0 ]; then + exit +fi + + +read -r -p "Enter here command for inside the containers: " custom_command + +header_info +echo "One moment please...\n" + +function execute_in() { + container=$1 + name=$(pct exec "$container" hostname) + echo -e "${BL}[Info]${GN} Execute inside${BL} ${name}${GN} with output: ${CL}" + pct exec "$container" -- bash -c "${custom_command}" | tee +} + +for container in $(pct list | awk '{if(NR>1) print $1}'); do + if [[ " ${excluded_containers[@]} " =~ " $container " ]]; then + echo -e "${BL}[Info]${GN} Skipping ${BL}$container${CL}" + else + os=$(pct config "$container" | awk '/^ostype/ {print $2}') + if [ "$os" != "debian" ] && [ "$os" != "ubuntu" ]; then + echo -e "${BL}[Info]${GN} Skipping ${name} ${RD}$container is not Debian or Ubuntu ${CL}" + continue + fi + + status=$(pct status "$container") + template=$(pct config "$container" | grep -q "template:" && echo "true" || echo "false") + if [ "$template" == "false" ] && [ "$status" == "status: stopped" ]; then + echo -e "${BL}[Info]${GN} Starting${BL} $container ${CL}" + pct start "$container" + echo -e "${BL}[Info]${GN} Waiting For${BL} $container${CL}${GN} To Start ${CL}" + sleep 5 + execute_in "$container" + echo -e "${BL}[Info]${GN} Shutting down${BL} $container ${CL}" + pct shutdown "$container" & + elif [ "$status" == "status: running" ]; then + execute_in "$container" + fi + fi +done + +wait + +echo -e "${GN} Finished, execute command inside selected containers. ${CL} \n" From 0ced215402433723b0035ed1f407843ff7fa4960 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 25 Jul 2025 10:18:52 +0200 Subject: [PATCH 0178/1733] Update frigate-install.sh --- install/frigate-install.sh | 406 ++++++++++++++++++++++++------------- 1 file changed, 266 insertions(+), 140 deletions(-) diff --git a/install/frigate-install.sh b/install/frigate-install.sh index fd17b6fd7..d5cf24fd3 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -42,67 +42,29 @@ if [[ "$CTTYPE" == "0" ]]; then fi msg_ok "Set Up Hardware Acceleration" -msg_info "Setup Frigate" -ln -sf /usr/local/go2rtc/bin/go2rtc /usr/local/bin/go2rtc +msg_info "Setting up Python venv" cd /opt/frigate -$STD pip install -r /opt/frigate/docker/main/requirements.txt --break-system-packages -$STD pip install -r /opt/frigate/docker/main/requirements-ov.txt --break-system-packages -$STD pip3 wheel --wheel-dir=/wheels -r /opt/frigate/docker/main/requirements-wheels.txt -pip3 install -U /wheels/*.whl -cp -a /opt/frigate/docker/main/rootfs/. / -export TARGETARCH="amd64" -export DEBIAN_FRONTEND=noninteractive -echo "libedgetpu1-max libedgetpu/accepted-eula select true" | debconf-set-selections -echo "libedgetpu1-max libedgetpu/install-confirm-max select true" | debconf-set-selections +python3 -m venv venv +source venv/bin/activate +$STD pip install --upgrade pip wheel +$STD pip install -r docker/main/requirements.txt +$STD pip install -r docker/main/requirements-ov.txt +msg_ok "Python venv ready" -msg_info "Ensure /etc/apt/sources.list.d/debian.sources exists with deb-src" -mkdir -p /etc/apt/sources.list.d -cat >/etc/apt/sources.list.d/debian.sources <<'EOF' -Types: deb deb-src -URIs: http://deb.debian.org/debian -Suites: bookworm -Components: main -Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg -EOF -msg_ok "Stub /etc/apt/sources.list.d/debian.sources created" - -msg_info "Updating APT cache" -$STD apt-get update -msg_ok "APT cache updated" - -msg_info "Building Nginx with Custom Modules" -$STD bash /opt/frigate/docker/main/build_nginx.sh -sed -e '/s6-notifyoncheck/ s/^#*/#/' -i /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/run -ln -sf /usr/local/nginx/sbin/nginx /usr/local/bin/nginx -msg_ok "Built Nginx" - -msg_info "Cleanup stub debian.sources" -rm -f /etc/apt/sources.list.d/debian.sources -$STD apt-get update -msg_ok "Removed stub and updated APT cache" - -$STD /opt/frigate/docker/main/install_deps.sh -$STD apt update -$STD ln -svf /usr/lib/btbn-ffmpeg/bin/ffmpeg /usr/local/bin/ffmpeg -$STD ln -svf /usr/lib/btbn-ffmpeg/bin/ffprobe /usr/local/bin/ffprobe -$STD pip3 install -U /wheels/*.whl -ldconfig -$STD pip3 install -r /opt/frigate/docker/main/requirements-dev.txt -$STD /opt/frigate/.devcontainer/initialize.sh -$STD make version +msg_info "Building Web UI" cd /opt/frigate/web $STD npm install $STD npm run build -cp -r /opt/frigate/web/dist/* /opt/frigate/web/ -cp -r /opt/frigate/config/. /config -sed -i '/^s6-svc -O \.$/s/^/#/' /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/frigate/run -cat </config/config.yml +msg_ok "Web UI built" + +msg_info "Writing default config" +mkdir -p /opt/frigate/config +cat </opt/frigate/config/config.yml mqtt: enabled: false cameras: test: ffmpeg: - #hwaccel_args: preset-vaapi inputs: - path: /media/frigate/person-bicycle-car-detection.mp4 input_args: -re -stream_loop -1 -fflags +genpts @@ -114,24 +76,10 @@ cameras: width: 1920 fps: 5 EOF -ln -sf /config/config.yml /opt/frigate/config/config.yml -if [[ "$CTTYPE" == "0" ]]; then - sed -i -e 's/^kvm:x:104:$/render:x:104:root,frigate/' -e 's/^render:x:105:root$/kvm:x:105:/' /etc/group -else - sed -i -e 's/^kvm:x:104:$/render:x:104:frigate/' -e 's/^render:x:105:$/kvm:x:105:/' /etc/group -fi -echo "tmpfs /tmp/cache tmpfs defaults 0 0" >>/etc/fstab -msg_ok "Installed Frigate" - -# read -p "Semantic Search requires a dedicated GPU and at least 16GB RAM. Would you like to install it? (y/n): " semantic_choice -# if [[ "$semantic_choice" == "y" ]]; then -# msg_info "Configuring Semantic Search & AI Models" -# mkdir -p /opt/frigate/models/semantic_search -# curl -fsSL -o /opt/frigate/models/semantic_search/clip_model.pt https://huggingface.co/openai/clip-vit-base-patch32/resolve/main/pytorch_model.bin -# msg_ok "Semantic Search Models Installed" -# else -# msg_ok "Skipped Semantic Search Setup" -# fi +ln -sf /opt/frigate/config/config.yml /config/config.yml +mkdir -p /media/frigate +wget -qO /media/frigate/person-bicycle-car-detection.mp4 https://github.com/intel-iot-devkit/sample-videos/raw/master/person-bicycle-car-detection.mp4 +msg_ok "Config ready" msg_info "Building and Installing libUSB without udev" wget -qO /tmp/libusb.zip https://github.com/libusb/libusb/archive/v1.0.29.zip @@ -145,122 +93,300 @@ ldconfig rm -rf /tmp/libusb.zip /tmp/libusb-1.0.29 msg_ok "Installed libUSB without udev" -msg_info "Installing Coral Object Detection Model (Patience)" +# Coral Object Detection Models +msg_info "Installing Coral Object Detection Models" cd /opt/frigate export CCACHE_DIR=/root/.ccache export CCACHE_MAXSIZE=2G -cd libusb -$STD ./bootstrap.sh -$STD ./configure --disable-udev --enable-shared -$STD make -j $(nproc --all) -cd /opt/frigate/libusb/libusb -mkdir -p /usr/local/lib -$STD /bin/bash ../libtool --mode=install /usr/bin/install -c libusb-1.0.la '/usr/local/lib' -mkdir -p /usr/local/include/libusb-1.0 -$STD /usr/bin/install -c -m 644 libusb.h '/usr/local/include/libusb-1.0' -ldconfig -cd / + +# edgetpu / cpu Modelle wget -qO edgetpu_model.tflite https://github.com/google-coral/test_data/raw/release-frogfish/ssdlite_mobiledet_coco_qat_postprocess_edgetpu.tflite wget -qO cpu_model.tflite https://github.com/google-coral/test_data/raw/release-frogfish/ssdlite_mobiledet_coco_qat_postprocess.tflite cp /opt/frigate/labelmap.txt /labelmap.txt + +# Audio-Modelle wget -qO yamnet-tflite-classification-tflite-v1.tar.gz https://www.kaggle.com/api/v1/models/google/yamnet/tfLite/classification-tflite/1/download tar xzf yamnet-tflite-classification-tflite-v1.tar.gz rm -rf yamnet-tflite-classification-tflite-v1.tar.gz mv 1.tflite cpu_audio_model.tflite cp /opt/frigate/audio-labelmap.txt /audio-labelmap.txt -mkdir -p /media/frigate -wget -qO /media/frigate/person-bicycle-car-detection.mp4 https://github.com/intel-iot-devkit/sample-videos/raw/master/person-bicycle-car-detection.mp4 -msg_ok "Installed Coral Object Detection Model" +msg_ok "Installed Coral Object Detection Models" +# ------------------------------------------------------------ +# Tempio installieren msg_info "Installing Tempio" sed -i 's|/rootfs/usr/local|/usr/local|g' /opt/frigate/docker/main/install_tempio.sh TARGETARCH="amd64" -$STD /opt/frigate/docker/main/install_tempio.sh +/opt/frigate/docker/main/install_tempio.sh chmod +x /usr/local/tempio/bin/tempio ln -sf /usr/local/tempio/bin/tempio /usr/local/bin/tempio msg_ok "Installed Tempio" -msg_info "Creating Services" -cat </etc/systemd/system/create_directories.service -[Unit] -Description=Create necessary directories for logs - -[Service] -Type=oneshot -ExecStart=/bin/bash -c '/bin/mkdir -p /dev/shm/logs/{frigate,go2rtc,nginx} && /bin/touch /dev/shm/logs/{frigate/current,go2rtc/current,nginx/current} && /bin/chmod -R 777 /dev/shm/logs' - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now create_directories -sleep 3 +# ------------------------------------------------------------ +# systemd Units +msg_info "Creating systemd service for go2rtc" cat </etc/systemd/system/go2rtc.service [Unit] -Description=go2rtc service +Description=go2rtc After=network.target -After=create_directories.service -StartLimitIntervalSec=0 [Service] -Type=simple +ExecStart=/usr/local/bin/go2rtc Restart=always -RestartSec=1 +RestartSec=2 User=root -ExecStartPre=+rm /dev/shm/logs/go2rtc/current -ExecStart=/bin/bash -c "bash /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/run 2> >(/usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S ' >&2) | /usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S '" -StandardOutput=file:/dev/shm/logs/go2rtc/current -StandardError=file:/dev/shm/logs/go2rtc/current +StandardOutput=journal +StandardError=journal [Install] WantedBy=multi-user.target EOF +systemctl daemon-reload systemctl enable -q --now go2rtc -sleep 3 +msg_ok "go2rtc service enabled" + +msg_info "Creating systemd service for Frigate" cat </etc/systemd/system/frigate.service [Unit] Description=Frigate service -After=go2rtc.service -After=create_directories.service -StartLimitIntervalSec=0 +After=go2rtc.service network.target [Service] -Type=simple +WorkingDirectory=/opt/frigate +Environment="PATH=/opt/frigate/venv/bin" +ExecStart=/opt/frigate/venv/bin/python3 -u -m frigate Restart=always -RestartSec=1 +RestartSec=5 User=root -# Environment=PLUS_API_KEY= -ExecStartPre=+rm /dev/shm/logs/frigate/current -ExecStart=/bin/bash -c "bash /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/frigate/run 2> >(/usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S ' >&2) | /usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S '" -StandardOutput=file:/dev/shm/logs/frigate/current -StandardError=file:/dev/shm/logs/frigate/current +StandardOutput=journal +StandardError=journal [Install] WantedBy=multi-user.target EOF +systemctl daemon-reload systemctl enable -q --now frigate -sleep 3 -cat </etc/systemd/system/nginx.service -[Unit] -Description=Nginx service -After=frigate.service -After=create_directories.service -StartLimitIntervalSec=0 +msg_ok "Frigate service enabled" -[Service] -Type=simple -Restart=always -RestartSec=1 -User=root -ExecStartPre=+rm /dev/shm/logs/nginx/current -ExecStart=/bin/bash -c "bash /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/run 2> >(/usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S ' >&2) | /usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S '" -StandardOutput=file:/dev/shm/logs/nginx/current -StandardError=file:/dev/shm/logs/nginx/current +# msg_info "Setup Frigate" +# ln -sf /usr/local/go2rtc/bin/go2rtc /usr/local/bin/go2rtc +# cd /opt/frigate +# $STD pip install -r /opt/frigate/docker/main/requirements.txt --break-system-packages +# $STD pip install -r /opt/frigate/docker/main/requirements-ov.txt --break-system-packages +# $STD pip3 wheel --wheel-dir=/wheels -r /opt/frigate/docker/main/requirements-wheels.txt +# pip3 install -U /wheels/*.whl +# cp -a /opt/frigate/docker/main/rootfs/. / +# export TARGETARCH="amd64" +# export DEBIAN_FRONTEND=noninteractive +# echo "libedgetpu1-max libedgetpu/accepted-eula select true" | debconf-set-selections +# echo "libedgetpu1-max libedgetpu/install-confirm-max select true" | debconf-set-selections -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now nginx -msg_ok "Configured Services" +# msg_info "Ensure /etc/apt/sources.list.d/debian.sources exists with deb-src" +# mkdir -p /etc/apt/sources.list.d +# cat >/etc/apt/sources.list.d/debian.sources <<'EOF' +# Types: deb deb-src +# URIs: http://deb.debian.org/debian +# Suites: bookworm +# Components: main +# Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg +# EOF +# msg_ok "Stub /etc/apt/sources.list.d/debian.sources created" + +# msg_info "Updating APT cache" +# $STD apt-get update +# msg_ok "APT cache updated" + +# msg_info "Building Nginx with Custom Modules" +# $STD bash /opt/frigate/docker/main/build_nginx.sh +# sed -e '/s6-notifyoncheck/ s/^#*/#/' -i /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/run +# ln -sf /usr/local/nginx/sbin/nginx /usr/local/bin/nginx +# msg_ok "Built Nginx" + +# msg_info "Cleanup stub debian.sources" +# rm -f /etc/apt/sources.list.d/debian.sources +# $STD apt-get update +# msg_ok "Removed stub and updated APT cache" + +# $STD /opt/frigate/docker/main/install_deps.sh +# $STD apt update +# $STD ln -svf /usr/lib/btbn-ffmpeg/bin/ffmpeg /usr/local/bin/ffmpeg +# $STD ln -svf /usr/lib/btbn-ffmpeg/bin/ffprobe /usr/local/bin/ffprobe +# $STD pip3 install -U /wheels/*.whl +# ldconfig +# $STD pip3 install -r /opt/frigate/docker/main/requirements-dev.txt +# $STD /opt/frigate/.devcontainer/initialize.sh +# $STD make version +# cd /opt/frigate/web +# $STD npm install +# $STD npm run build +# cp -r /opt/frigate/web/dist/* /opt/frigate/web/ +# cp -r /opt/frigate/config/. /config +# sed -i '/^s6-svc -O \.$/s/^/#/' /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/frigate/run +# cat </config/config.yml +# mqtt: +# enabled: false +# cameras: +# test: +# ffmpeg: +# #hwaccel_args: preset-vaapi +# inputs: +# - path: /media/frigate/person-bicycle-car-detection.mp4 +# input_args: -re -stream_loop -1 -fflags +genpts +# roles: +# - detect +# - rtmp +# detect: +# height: 1080 +# width: 1920 +# fps: 5 +# EOF +# ln -sf /config/config.yml /opt/frigate/config/config.yml +# if [[ "$CTTYPE" == "0" ]]; then +# sed -i -e 's/^kvm:x:104:$/render:x:104:root,frigate/' -e 's/^render:x:105:root$/kvm:x:105:/' /etc/group +# else +# sed -i -e 's/^kvm:x:104:$/render:x:104:frigate/' -e 's/^render:x:105:$/kvm:x:105:/' /etc/group +# fi +# echo "tmpfs /tmp/cache tmpfs defaults 0 0" >>/etc/fstab +# msg_ok "Installed Frigate" + +# # read -p "Semantic Search requires a dedicated GPU and at least 16GB RAM. Would you like to install it? (y/n): " semantic_choice +# # if [[ "$semantic_choice" == "y" ]]; then +# # msg_info "Configuring Semantic Search & AI Models" +# # mkdir -p /opt/frigate/models/semantic_search +# # curl -fsSL -o /opt/frigate/models/semantic_search/clip_model.pt https://huggingface.co/openai/clip-vit-base-patch32/resolve/main/pytorch_model.bin +# # msg_ok "Semantic Search Models Installed" +# # else +# # msg_ok "Skipped Semantic Search Setup" +# # fi + +# msg_info "Building and Installing libUSB without udev" +# wget -qO /tmp/libusb.zip https://github.com/libusb/libusb/archive/v1.0.29.zip +# unzip -q /tmp/libusb.zip -d /tmp/ +# cd /tmp/libusb-1.0.29 +# ./bootstrap.sh +# ./configure --disable-udev --enable-shared +# make -j$(nproc --all) +# make install +# ldconfig +# rm -rf /tmp/libusb.zip /tmp/libusb-1.0.29 +# msg_ok "Installed libUSB without udev" + +# msg_info "Installing Coral Object Detection Model (Patience)" +# cd /opt/frigate +# export CCACHE_DIR=/root/.ccache +# export CCACHE_MAXSIZE=2G +# cd libusb +# $STD ./bootstrap.sh +# $STD ./configure --disable-udev --enable-shared +# $STD make -j $(nproc --all) +# cd /opt/frigate/libusb/libusb +# mkdir -p /usr/local/lib +# $STD /bin/bash ../libtool --mode=install /usr/bin/install -c libusb-1.0.la '/usr/local/lib' +# mkdir -p /usr/local/include/libusb-1.0 +# $STD /usr/bin/install -c -m 644 libusb.h '/usr/local/include/libusb-1.0' +# ldconfig +# cd / +# wget -qO edgetpu_model.tflite https://github.com/google-coral/test_data/raw/release-frogfish/ssdlite_mobiledet_coco_qat_postprocess_edgetpu.tflite +# wget -qO cpu_model.tflite https://github.com/google-coral/test_data/raw/release-frogfish/ssdlite_mobiledet_coco_qat_postprocess.tflite +# cp /opt/frigate/labelmap.txt /labelmap.txt +# wget -qO yamnet-tflite-classification-tflite-v1.tar.gz https://www.kaggle.com/api/v1/models/google/yamnet/tfLite/classification-tflite/1/download +# tar xzf yamnet-tflite-classification-tflite-v1.tar.gz +# rm -rf yamnet-tflite-classification-tflite-v1.tar.gz +# mv 1.tflite cpu_audio_model.tflite +# cp /opt/frigate/audio-labelmap.txt /audio-labelmap.txt +# mkdir -p /media/frigate +# wget -qO /media/frigate/person-bicycle-car-detection.mp4 https://github.com/intel-iot-devkit/sample-videos/raw/master/person-bicycle-car-detection.mp4 +# msg_ok "Installed Coral Object Detection Model" + +# msg_info "Installing Tempio" +# sed -i 's|/rootfs/usr/local|/usr/local|g' /opt/frigate/docker/main/install_tempio.sh +# TARGETARCH="amd64" +# $STD /opt/frigate/docker/main/install_tempio.sh +# chmod +x /usr/local/tempio/bin/tempio +# ln -sf /usr/local/tempio/bin/tempio /usr/local/bin/tempio +# msg_ok "Installed Tempio" + +# msg_info "Creating Services" +# cat </etc/systemd/system/create_directories.service +# [Unit] +# Description=Create necessary directories for logs + +# [Service] +# Type=oneshot +# ExecStart=/bin/bash -c '/bin/mkdir -p /dev/shm/logs/{frigate,go2rtc,nginx} && /bin/touch /dev/shm/logs/{frigate/current,go2rtc/current,nginx/current} && /bin/chmod -R 777 /dev/shm/logs' + +# [Install] +# WantedBy=multi-user.target +# EOF +# systemctl enable -q --now create_directories +# sleep 3 +# cat </etc/systemd/system/go2rtc.service +# [Unit] +# Description=go2rtc service +# After=network.target +# After=create_directories.service +# StartLimitIntervalSec=0 + +# [Service] +# Type=simple +# Restart=always +# RestartSec=1 +# User=root +# ExecStartPre=+rm /dev/shm/logs/go2rtc/current +# ExecStart=/bin/bash -c "bash /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/run 2> >(/usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S ' >&2) | /usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S '" +# StandardOutput=file:/dev/shm/logs/go2rtc/current +# StandardError=file:/dev/shm/logs/go2rtc/current + +# [Install] +# WantedBy=multi-user.target +# EOF +# systemctl enable -q --now go2rtc +# sleep 3 +# cat </etc/systemd/system/frigate.service +# [Unit] +# Description=Frigate service +# After=go2rtc.service +# After=create_directories.service +# StartLimitIntervalSec=0 + +# [Service] +# Type=simple +# Restart=always +# RestartSec=1 +# User=root +# # Environment=PLUS_API_KEY= +# ExecStartPre=+rm /dev/shm/logs/frigate/current +# ExecStart=/bin/bash -c "bash /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/frigate/run 2> >(/usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S ' >&2) | /usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S '" +# StandardOutput=file:/dev/shm/logs/frigate/current +# StandardError=file:/dev/shm/logs/frigate/current + +# [Install] +# WantedBy=multi-user.target +# EOF +# systemctl enable -q --now frigate +# sleep 3 +# cat </etc/systemd/system/nginx.service +# [Unit] +# Description=Nginx service +# After=frigate.service +# After=create_directories.service +# StartLimitIntervalSec=0 + +# [Service] +# Type=simple +# Restart=always +# RestartSec=1 +# User=root +# ExecStartPre=+rm /dev/shm/logs/nginx/current +# ExecStart=/bin/bash -c "bash /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/run 2> >(/usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S ' >&2) | /usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S '" +# StandardOutput=file:/dev/shm/logs/nginx/current +# StandardError=file:/dev/shm/logs/nginx/current + +# [Install] +# WantedBy=multi-user.target +# EOF +# systemctl enable -q --now nginx +# msg_ok "Configured Services" motd_ssh customize From 4cb1b1d26b04e41c8212ed00f1cb22c4a71131b0 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 25 Jul 2025 10:31:33 +0200 Subject: [PATCH 0179/1733] Update frigate-install.sh --- install/frigate-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/frigate-install.sh b/install/frigate-install.sh index d5cf24fd3..fdff72d68 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -24,7 +24,7 @@ msg_ok "Installed Dependencies" msg_info "Setup Python3" $STD apt-get install -y \ - python3 python3-dev python3-setuptools python3-distutils python3-pip + python3 python3-dev python3-setuptools python3-distutils python3-pip python3-venv $STD pip install --upgrade pip msg_ok "Setup Python3" From cdb014c812b7db948ff72038133b4b3333a6bb8c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 25 Jul 2025 10:32:01 +0200 Subject: [PATCH 0180/1733] Update frigate-install.sh --- install/frigate-install.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/install/frigate-install.sh b/install/frigate-install.sh index fdff72d68..ad8fe02fb 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -25,7 +25,7 @@ msg_ok "Installed Dependencies" msg_info "Setup Python3" $STD apt-get install -y \ python3 python3-dev python3-setuptools python3-distutils python3-pip python3-venv -$STD pip install --upgrade pip +$STD pip install --upgrade pip --break-system-packages msg_ok "Setup Python3" NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs @@ -46,9 +46,9 @@ msg_info "Setting up Python venv" cd /opt/frigate python3 -m venv venv source venv/bin/activate -$STD pip install --upgrade pip wheel -$STD pip install -r docker/main/requirements.txt -$STD pip install -r docker/main/requirements-ov.txt +$STD pip install --upgrade pip wheel --break-system-packages +$STD pip install -r docker/main/requirements.txt --break-system-packages +$STD pip install -r docker/main/requirements-ov.txt --break-system-packages msg_ok "Python venv ready" msg_info "Building Web UI" From 72875fdb28032a90db5518ad8215d743c89d1a4a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 25 Jul 2025 10:42:29 +0200 Subject: [PATCH 0181/1733] Update frigate-install.sh --- install/frigate-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/frigate-install.sh b/install/frigate-install.sh index ad8fe02fb..fc07c5836 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -76,7 +76,7 @@ cameras: width: 1920 fps: 5 EOF -ln -sf /opt/frigate/config/config.yml /config/config.yml +#ln -sf /opt/frigate/config/config.yml /config/config.yml mkdir -p /media/frigate wget -qO /media/frigate/person-bicycle-car-detection.mp4 https://github.com/intel-iot-devkit/sample-videos/raw/master/person-bicycle-car-detection.mp4 msg_ok "Config ready" From 6334e3173a0567d6b1a75f6a2f1b2f285d3092d3 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 25 Jul 2025 10:50:53 +0200 Subject: [PATCH 0182/1733] test --- ct/alpine-teamspeak-server.sh | 28 +++++++++++++-------- misc/tools.func | 47 +++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 11 deletions(-) diff --git a/ct/alpine-teamspeak-server.sh b/ct/alpine-teamspeak-server.sh index 204649551..104b18f66 100644 --- a/ct/alpine-teamspeak-server.sh +++ b/ct/alpine-teamspeak-server.sh @@ -23,26 +23,32 @@ function update_script() { header_info if [[ ! -d /opt/teamspeak-server ]]; then - msg_error "No ${APP} Installation Found!" + msg_error "No ${APP} installation found!" exit 1 fi - RELEASE=$(curl -fsSL https://teamspeak.com/en/downloads/#server | sed -n '/teamspeak3-server_linux_amd64-/ { s/.*teamspeak3-server_linux_amd64-\([0-9]\+\.[0-9]\+\.[0-9]\+\).*/\1/p; q }') + # define custom command to scrape version + local CUSTOM_CMD="curl -fsSL https://teamspeak.com/en/downloads/#server \ + | sed -n '/teamspeak3-server_linux_amd64-/ { s/.*teamspeak3-server_linux_amd64-\([0-9]\+\.[0-9]\+\.[0-9]\+\).*/\1/p; q }'" - if [ "${RELEASE}" != "$(cat ~/.teamspeak-server)" ] || [ ! -f ~/.teamspeak-server ]; then - msg_info "Updating ${APP} LXC" + if check_for_update "${APP}" "${CUSTOM_CMD}"; then + local release="$CHECK_UPDATE_RELEASE" + + msg_info "Updating ${APP} LXC to v${release}" $STD apk -U upgrade $STD service teamspeak stop - curl -fsSL "https://files.teamspeak-services.com/releases/server/${RELEASE}/teamspeak3-server_linux_amd64-${RELEASE}.tar.bz2" -o ts3server.tar.bz2 - tar -xf ./ts3server.tar.bz2 + + curl -fsSL "https://files.teamspeak-services.com/releases/server/${release}/teamspeak3-server_linux_amd64-${release}.tar.bz2" -o ts3server.tar.bz2 + tar -xf ts3server.tar.bz2 cp -ru teamspeak3-server_linux_amd64/* /opt/teamspeak-server/ - rm -f ~/ts3server.tar.bz* + + rm -f ts3server.tar.bz2 rm -rf teamspeak3-server_linux_amd64 - echo "${RELEASE}" >~/.teamspeak-server + + echo "${release}" >~/.teamspeak-server + $STD service teamspeak start - msg_ok "Updated Successfully" - else - msg_ok "No update required. ${APP} is already at ${RELEASE}" + msg_ok "Updated ${APP} successfully to v${release}" fi exit 0 diff --git a/misc/tools.func b/misc/tools.func index 5d03c24d3..97782ea9a 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -1978,3 +1978,50 @@ EOF msg_ok "ClickHouse updated" fi } + +check_for_update() { + local app="$1" # e.g. "wizarr" + local source="$2" # e.g. "org/repo" or custom command + local current_file="$HOME/.${app,,}" + + msg_info "Check for update: $app" + + # DNS check if GitHub + if [[ "$source" != *" "* ]] && [[ "$source" != http* ]]; then + if ! getent hosts api.github.com >/dev/null 2>&1; then + msg_error "Network error: cannot resolve api.github.com" + return 1 + fi + if ! command -v jq &>/dev/null; then + apt-get update -qq &>/dev/null && apt-get install -y jq &>/dev/null || { + msg_error "Failed to install jq" + return 1 + } + fi + fi + + # get release + local release="" + if [[ "$source" == *" "* ]] || [[ "$source" == http* ]]; then + release=$(eval "$source") + else + release=$(curl -fsSL "https://api.github.com/repos/$source/releases/latest" | + jq -r '.tag_name' | sed 's/^v//') + fi + + if [[ -z "$release" ]]; then + msg_error "Unable to determine latest release for $app" + return 1 + fi + + # compare with local version + local current="" + [[ -f "$current_file" ]] && current=$(<"$current_file") + + if [[ "$release" != "$current" ]] || [[ ! -f "$current_file" ]]; then + return 0 # update needed + else + msg_ok "$app is up to date (v$release)" + return 1 # no update + fi +} From 69f4341aaafd9d1894fe46c418d1ff252dcc1ba9 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 25 Jul 2025 10:52:59 +0200 Subject: [PATCH 0183/1733] Update keycloak.sh --- ct/keycloak.sh | 77 ++++++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/ct/keycloak.sh b/ct/keycloak.sh index 66e8e9226..32e25fd26 100644 --- a/ct/keycloak.sh +++ b/ct/keycloak.sh @@ -20,49 +20,52 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/keycloak ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi + header_info + check_container_storage + check_container_resources - RELEASE=$(curl -fsSL https://api.github.com/repos/keycloak/keycloak/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - if [[ "${RELEASE}" != "$(cat ~/.keycloak 2>/dev/null)" ]] || [[ ! -f ~/.keycloak ]]; then - msg_info "Stopping ${APP}" - systemctl stop keycloak - msg_ok "Stopped ${APP}" + if [[ ! -d /opt/keycloak ]]; then + msg_error "No ${APP} installation found!" + exit 1 + fi - msg_info "Updating packages" - apt-get update &>/dev/null - apt-get -y upgrade &>/dev/null - msg_ok "Updated packages" + if check_for_update "${APP}" "keycloak/keycloak"; then + local release="$CHECK_UPDATE_RELEASE" - msg_info "Backup old Keycloak" - cd /opt - mv keycloak keycloak.old - tar -czf keycloak_conf_backup.tar.gz keycloak.old/conf - msg_ok "Backup done" + msg_info "Stopping ${APP}" + systemctl stop keycloak + msg_ok "Stopped ${APP}" - fetch_and_deploy_gh_release "keycloak" "keycloak/keycloak" "prebuild" "latest" "/opt/keycloak" "keycloak-*.tar.gz" + msg_info "Updating packages" + apt-get update &>/dev/null + apt-get -y upgrade &>/dev/null + msg_ok "Updated packages" - msg_info "Updating ${APP}" - cd /opt - mv keycloak_conf_backup.tar.gz keycloak/conf - cp -r keycloak.old/providers keycloak - cp -r keycloak.old/themes keycloak - rm -rf keycloak.old - msg_ok "Updated ${APP} LXC" + msg_info "Backup old Keycloak" + cd /opt + mv keycloak keycloak.old + tar -czf keycloak_conf_backup.tar.gz keycloak.old/conf + msg_ok "Backup done" - msg_info "Restating Keycloak" - systemctl restart keycloak - msg_ok "Restated Keycloak" - msg_ok "Update Successful" - else - msg_ok "No update required. ${APP} is already at v${RELEASE}" - fi - exit + fetch_and_deploy_gh_release "keycloak" "keycloak/keycloak" "prebuild" "$release" "/opt/keycloak" "keycloak-*.tar.gz" + + msg_info "Updating ${APP}" + cd /opt + mv keycloak_conf_backup.tar.gz keycloak/conf + cp -r keycloak.old/providers keycloak + cp -r keycloak.old/themes keycloak + rm -rf keycloak.old + msg_ok "Updated ${APP} LXC" + + echo "${release}" >~/.keycloak + + msg_info "Restarting Keycloak" + systemctl restart keycloak + msg_ok "Restarted Keycloak" + msg_ok "Update to v${release} successful" + fi + + exit 0 } start From be83b85e404c635f7a6719c884b48159ca56486e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 25 Jul 2025 10:57:03 +0200 Subject: [PATCH 0184/1733] Update tools.func --- misc/tools.func | 73 +++++++++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 97782ea9a..b55f0af8a 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -1980,48 +1980,55 @@ EOF } check_for_update() { - local app="$1" # e.g. "wizarr" - local source="$2" # e.g. "org/repo" or custom command + local app="$1" + local source="$2" local current_file="$HOME/.${app,,}" - msg_info "Check for update: $app" + msg_info "Check for update: ${app}" - # DNS check if GitHub - if [[ "$source" != *" "* ]] && [[ "$source" != http* ]]; then - if ! getent hosts api.github.com >/dev/null 2>&1; then - msg_error "Network error: cannot resolve api.github.com" - return 1 - fi - if ! command -v jq &>/dev/null; then - apt-get update -qq &>/dev/null && apt-get install -y jq &>/dev/null || { - msg_error "Failed to install jq" - return 1 - } - fi - fi - - # get release - local release="" - if [[ "$source" == *" "* ]] || [[ "$source" == http* ]]; then - release=$(eval "$source") - else - release=$(curl -fsSL "https://api.github.com/repos/$source/releases/latest" | - jq -r '.tag_name' | sed 's/^v//') - fi - - if [[ -z "$release" ]]; then - msg_error "Unable to determine latest release for $app" + # DNS check for GitHub + if ! getent hosts api.github.com >/dev/null 2>&1; then + msg_error "Network error: cannot resolve api.github.com" + return 1 + fi + + # jq check + if ! command -v jq &>/dev/null; then + apt-get update -qq &>/dev/null && apt-get install -y jq &>/dev/null || { + msg_error "Failed to install jq" + return 1 + } + fi + + # get latest release + local release + release=$(curl -fsSL "https://api.github.com/repos/${source}/releases/latest" | + jq -r '.tag_name' | sed 's/^v//') + + # DEBUG + echo "[DEBUG] Latest release fetched: '${release}'" + + if [[ -z "$release" ]]; then + msg_error "Unable to determine latest release for ${app}" return 1 fi - # compare with local version local current="" - [[ -f "$current_file" ]] && current=$(<"$current_file") + if [[ -f "$current_file" ]]; then + current=$(<"$current_file") + fi + + # DEBUG + echo "[DEBUG] Current file: '${current_file}'" + echo "[DEBUG] Current version read: '${current}'" if [[ "$release" != "$current" ]] || [[ ! -f "$current_file" ]]; then - return 0 # update needed + echo "[DEBUG] Decision: Update required (release='${release}' current='${current}')" + CHECK_UPDATE_RELEASE="$release" + return 0 else - msg_ok "$app is up to date (v$release)" - return 1 # no update + echo "[DEBUG] Decision: No update (release='${release}' current='${current}')" + msg_ok "${app} is up to date (v${release})" + return 1 fi } From 5ad7379bf01659b533f7a8ecc1aa0c23977332fa Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 25 Jul 2025 10:58:39 +0200 Subject: [PATCH 0185/1733] test --- ct/keycloak.sh | 7 ++----- misc/tools.func | 10 +++++----- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/ct/keycloak.sh b/ct/keycloak.sh index 32e25fd26..f473ac0c8 100644 --- a/ct/keycloak.sh +++ b/ct/keycloak.sh @@ -30,7 +30,6 @@ function update_script() { fi if check_for_update "${APP}" "keycloak/keycloak"; then - local release="$CHECK_UPDATE_RELEASE" msg_info "Stopping ${APP}" systemctl stop keycloak @@ -47,7 +46,7 @@ function update_script() { tar -czf keycloak_conf_backup.tar.gz keycloak.old/conf msg_ok "Backup done" - fetch_and_deploy_gh_release "keycloak" "keycloak/keycloak" "prebuild" "$release" "/opt/keycloak" "keycloak-*.tar.gz" + fetch_and_deploy_gh_release "keycloak" "keycloak/keycloak" "prebuild" "latest" "/opt/keycloak" "keycloak-*.tar.gz" msg_info "Updating ${APP}" cd /opt @@ -57,12 +56,10 @@ function update_script() { rm -rf keycloak.old msg_ok "Updated ${APP} LXC" - echo "${release}" >~/.keycloak - msg_info "Restarting Keycloak" systemctl restart keycloak msg_ok "Restarted Keycloak" - msg_ok "Update to v${release} successful" + msg_ok "Update successful" fi exit 0 diff --git a/misc/tools.func b/misc/tools.func index b55f0af8a..f88116980 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -2006,7 +2006,7 @@ check_for_update() { jq -r '.tag_name' | sed 's/^v//') # DEBUG - echo "[DEBUG] Latest release fetched: '${release}'" + #echo "[DEBUG] Latest release fetched: '${release}'" if [[ -z "$release" ]]; then msg_error "Unable to determine latest release for ${app}" @@ -2019,15 +2019,15 @@ check_for_update() { fi # DEBUG - echo "[DEBUG] Current file: '${current_file}'" - echo "[DEBUG] Current version read: '${current}'" + #echo "[DEBUG] Current file: '${current_file}'" + #echo "[DEBUG] Current version read: '${current}'" if [[ "$release" != "$current" ]] || [[ ! -f "$current_file" ]]; then - echo "[DEBUG] Decision: Update required (release='${release}' current='${current}')" + #echo "[DEBUG] Decision: Update required (release='${release}' current='${current}')" CHECK_UPDATE_RELEASE="$release" return 0 else - echo "[DEBUG] Decision: No update (release='${release}' current='${current}')" + #echo "[DEBUG] Decision: No update (release='${release}' current='${current}')" msg_ok "${app} is up to date (v${release})" return 1 fi From 04b8c2212453e2b49400f555d31951eeb2a1ca05 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 25 Jul 2025 11:05:18 +0200 Subject: [PATCH 0186/1733] Update build.func --- misc/build.func | 65 ++++++++++++++++++++----------------------------- 1 file changed, 26 insertions(+), 39 deletions(-) diff --git a/misc/build.func b/misc/build.func index e9247b9de..8f56440e6 100644 --- a/misc/build.func +++ b/misc/build.func @@ -301,45 +301,32 @@ ssh_check() { base_settings() { # Default Settings - CT_TYPE="1" - DISK_SIZE="4" - CORE_COUNT="1" - RAM_SIZE="1024" - VERBOSE="${1:-no}" - PW="" - CT_ID=$NEXTID - HN=$NSAPP - BRG="vmbr0" - NET="dhcp" - IPV6_METHOD="none" - IPV6_STATIC="" - GATE="" - APT_CACHER="" - APT_CACHER_IP="" - #DISABLEIP6="no" - MTU="" - SD="" - NS="" - MAC="" - VLAN="" - SSH="no" - SSH_AUTHORIZED_KEY="" - UDHCPC_FIX="" - TAGS="community-script;" - ENABLE_FUSE="${1:-no}" - ENABLE_TUN="${1:-no}" - - # Override default settings with variables from ct script - CT_TYPE=${var_unprivileged:-$CT_TYPE} - DISK_SIZE=${var_disk:-$DISK_SIZE} - CORE_COUNT=${var_cpu:-$CORE_COUNT} - RAM_SIZE=${var_ram:-$RAM_SIZE} - VERB=${var_verbose:-$VERBOSE} - TAGS="${TAGS}${var_tags:-}" - ENABLE_FUSE="${var_fuse:-$ENABLE_FUSE}" - ENABLE_TUN="${var_tun:-$ENABLE_TUN}" - APT_CACHER="${var_apt_cacher:-$APT_CACHER}" - APT_CACHER_IP="${var_apt_cacher_ip:-$APT_CACHER_IP}" + CT_TYPE=${var_unprivileged:-"1"} + DISK_SIZE=${var_disk:-"4"} + CORE_COUNT=${var_cpu:-"1"} + RAM_SIZE=${var_ram:-"1024"} + VERBOSE=${var_verbose:-"${1:-no}"} + PW=${var_pw:-""} + CT_ID=${var_ctid:-$NEXTID} + HN=${var_hostname:-$NSAPP} + BRG=${var_brg:-"vmbr0"} + NET=${var_net:-"dhcp"} + IPV6_METHOD=${var_ipv6_method:-"none"} + IPV6_STATIC=${var_ipv6_static:-""} + GATE=${var_gateway:-""} + APT_CACHER=${var_apt_cacher:-""} + APT_CACHER_IP=${var_apt_cacher_ip:-""} + MTU=${var_mtu:-""} + SD=${var_storage:-""} + NS=${var_ns:-""} + MAC=${var_mac:-""} + VLAN=${var_vlan:-""} + SSH=${var_ssh:-"no"} + SSH_AUTHORIZED_KEY=${var_ssh_authorized_key:-""} + UDHCPC_FIX=${var_udhcpc_fix:-""} + TAGS="community-script;${var_tags:-}" + ENABLE_FUSE=${var_fuse:-"${1:-no}"} + ENABLE_TUN=${var_tun:-"${1:-no}"} # Since these 2 are only defined outside of default_settings function, we add a temporary fallback. TODO: To align everything, we should add these as constant variables (e.g. OSTYPE and OSVERSION), but that would currently require updating the default_settings function for all existing scripts if [ -z "$var_os" ]; then From b12a6f0eaca06c531c65db65bbf593f2e055e4a6 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 25 Jul 2025 11:08:23 +0200 Subject: [PATCH 0187/1733] Update build.func --- misc/build.func | 72 ------------------------------------------------- 1 file changed, 72 deletions(-) diff --git a/misc/build.func b/misc/build.func index 8f56440e6..cbbaec5fd 100644 --- a/misc/build.func +++ b/misc/build.func @@ -336,78 +336,6 @@ base_settings() { var_version="12" fi } -write_config() { - mkdir -p /opt/community-scripts - # This function writes the configuration to a file. - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "Write configfile" --yesno "Do you want to write the selections to a config file?" 10 60; then - FILEPATH="/opt/community-scripts/${NSAPP}.conf" - if [[ ! -f $FILEPATH ]]; then - cat <"$FILEPATH" -# ${NSAPP} Configuration File -# Generated on $(date) - -CT_TYPE="${CT_TYPE}" -DISK_SIZE="${DISK_SIZE}" -CORE_COUNT="${CORE_COUNT}" -RAM_SIZE="${RAM_SIZE}" -HN="${HN}" -BRG="${BRG}" -APT_CACHER_IP="${APT_CACHER_IP:-none}" -DISABLEIP6="${DISABLEIP6}" -PW='${PW:-none}' -SSH="${SSH}" -SSH_AUTHORIZED_KEY="${SSH_AUTHORIZED_KEY}" -VERBOSE="${VERBOSE}" -TAGS="${TAGS:-none}" -VLAN="${VLAN:-none}" -MTU="${MTU:-1500}" -GATE="${GATE:-none}" -SD="${SD:-none}" -MAC="${MAC:-none}" -NS="${NS:-none}" -NET="${NET}" -FUSE="${ENABLE_FUSE}" - -EOF - echo -e "${INFO}${BOLD}${GN}Writing configuration to ${FILEPATH}${CL}" - else - echo -e "${INFO}${BOLD}${RD}Configuration file already exists at ${FILEPATH}${CL}" - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "Overwrite configfile" --yesno "Do you want to overwrite the existing config file?" 10 60; then - rm -f "$FILEPATH" - cat <"$FILEPATH" -# ${NSAPP} Configuration File -# Generated on $(date) - -CT_TYPE="${CT_TYPE}" -DISK_SIZE="${DISK_SIZE}" -CORE_COUNT="${CORE_COUNT}" -RAM_SIZE="${RAM_SIZE}" -HN="${HN}" -BRG="${BRG}" -APT_CACHER_IP="${APT_CACHER_IP:-none}" -DISABLEIP6="${DISABLEIP6}" -PW="${PW:-none}" -SSH="${SSH}" -SSH_AUTHORIZED_KEY="${SSH_AUTHORIZED_KEY}" -VERBOSE="${VERBOSE}" -TAGS="${TAGS:-none}" -VLAN="${VLAN:-none}" -MTU="${MTU:-1500}" -GATE="${GATE:-none}" -SD="${SD:-none}" -MAC="${MAC:-none}" -NS="${NS:-none}" -NET="${NET}" -FUSE="${ENABLE_FUSE}" - -EOF - echo -e "${INFO}${BOLD}${GN}Writing configuration to ${FILEPATH}${CL}" - else - echo -e "${INFO}${BOLD}${RD}Configuration file not overwritten${CL}" - fi - fi - fi -} # This function displays the default values for various settings. echo_default() { From 9841a7832e614d8904e597f94d28250a3c82b69e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 25 Jul 2025 11:13:00 +0200 Subject: [PATCH 0188/1733] Update frigate-install.sh --- install/frigate-install.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/install/frigate-install.sh b/install/frigate-install.sh index fc07c5836..860fdb4a3 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -116,7 +116,10 @@ msg_ok "Installed Coral Object Detection Models" # Tempio installieren msg_info "Installing Tempio" sed -i 's|/rootfs/usr/local|/usr/local|g' /opt/frigate/docker/main/install_tempio.sh -TARGETARCH="amd64" +export TARGETARCH="amd64" +export DEBIAN_FRONTEND=noninteractive +echo "libedgetpu1-max libedgetpu/accepted-eula select true" | debconf-set-selections +echo "libedgetpu1-max libedgetpu/install-confirm-max select true" | debconf-set-selections /opt/frigate/docker/main/install_tempio.sh chmod +x /usr/local/tempio/bin/tempio ln -sf /usr/local/tempio/bin/tempio /usr/local/bin/tempio From ae55c0b278d2ee1d66402468b055f8068529d6f3 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 25 Jul 2025 11:44:55 +0200 Subject: [PATCH 0189/1733] Update frigate-install.sh --- install/frigate-install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install/frigate-install.sh b/install/frigate-install.sh index 860fdb4a3..702229931 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -48,6 +48,7 @@ python3 -m venv venv source venv/bin/activate $STD pip install --upgrade pip wheel --break-system-packages $STD pip install -r docker/main/requirements.txt --break-system-packages +$STD pip install -r docker/main/requirements-wheels.txt --break-system-packages $STD pip install -r docker/main/requirements-ov.txt --break-system-packages msg_ok "Python venv ready" From df7e16fe3e8da1dfa09a07c06809499710e69cd3 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 25 Jul 2025 12:21:34 +0200 Subject: [PATCH 0190/1733] Update frigate-install.sh --- install/frigate-install.sh | 74 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/install/frigate-install.sh b/install/frigate-install.sh index 702229931..52884f075 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -19,7 +19,7 @@ $STD apt-get install -y \ libgtk-3-dev libavcodec-dev libavformat-dev libswscale-dev libv4l-dev libxvidcore-dev libx264-dev \ libjpeg-dev libpng-dev libtiff-dev gfortran openexr libatlas-base-dev libssl-dev libtbb-dev \ libopenexr-dev libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev gcc gfortran \ - libopenblas-dev liblapack-dev libusb-1.0-0-dev jq moreutils tclsh libhdf5-dev libopenexr-dev + libopenblas-dev liblapack-dev libusb-1.0-0-dev jq moreutils tclsh libhdf5-dev libopenexr-dev nginx msg_ok "Installed Dependencies" msg_info "Setup Python3" @@ -28,6 +28,75 @@ $STD apt-get install -y \ $STD pip install --upgrade pip --break-system-packages msg_ok "Setup Python3" +msg_info "Setup NGINX" +apt-get update +apt-get -y build-dep nginx +apt-get -y install wget build-essential ccache patch ca-certificates +update-ca-certificates -f +export PATH="/usr/lib/ccache:$PATH" + +cd /tmp +wget -nv https://nginx.org/download/nginx-1.29.0.tar.gz +tar -xf nginx-1.29.0.tar.gz +cd nginx-1.29.0 + +mkdir /tmp/nginx-vod +wget -nv https://github.com/kaltura/nginx-vod-module/archive/refs/tags/1.31.tar.gz +tar -xf 1.31.tar.gz -C /tmp/nginx-vod --strip-components=1 +sed -i 's/MAX_CLIPS (128)/MAX_CLIPS (1080)/g' /tmp/nginx-vod/vod/media_set.h +patch -d /tmp/nginx-vod -p1 <<'EOF' +--- a/vod/avc_hevc_parser.c ++++ b/vod/avc_hevc_parser.c +@@ -3,6 +3,9 @@ + bool_t + avc_hevc_parser_rbsp_trailing_bits(bit_reader_state_t* reader) + { ++ // https://github.com/blakeblackshear/frigate/issues/4572 ++ return TRUE; ++ + uint32_t one_bit; + + if (reader->stream.eof_reached) +EOF +# secure-token module +mkdir /tmp/nginx-secure-token +wget -nv https://github.com/kaltura/nginx-secure-token-module/archive/refs/tags/1.5.tar.gz +tar -xf 1.5.tar.gz -C /tmp/nginx-secure-token --strip-components=1 + +# ngx-devel-kit +mkdir /tmp/ngx-devel-kit +wget -nv https://github.com/vision5/ngx_devel_kit/archive/refs/tags/v0.3.3.tar.gz +tar -xf v0.3.3.tar.gz -C /tmp/ngx-devel-kit --strip-components=1 + +# set-misc module +mkdir /tmp/nginx-set-misc +wget -nv https://github.com/openresty/set-misc-nginx-module/archive/refs/tags/v0.33.tar.gz +tar -xf v0.33.tar.gz -C /tmp/nginx-set-misc --strip-components=1 + +# configure & build +cd /tmp/nginx-1.29.0 +./configure --prefix=/usr/local/nginx \ + --with-file-aio \ + --with-http_sub_module \ + --with-http_ssl_module \ + --with-http_auth_request_module \ + --with-http_realip_module \ + --with-threads \ + --add-module=/tmp/ngx-devel-kit \ + --add-module=/tmp/nginx-set-misc \ + --add-module=/tmp/nginx-vod \ + --add-module=/tmp/nginx-secure-token \ + --with-cc-opt="-O3 -Wno-error=implicit-fallthrough" + +make CC="ccache gcc" -j"$(nproc)" +make install +ln -sf /usr/local/nginx/sbin/nginx /usr/local/bin/nginx + +# cleanup +rm -rf /tmp/nginx-1.29.0* /tmp/nginx-vod /tmp/nginx-secure-token /tmp/ngx-devel-kit /tmp/nginx-set-misc + +msg_ok "NGINX with Custom Modules Built" + NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs fetch_and_deploy_gh_release "go2rtc" "AlexxIT/go2rtc" "singlefile" "latest" "/usr/local/go2rtc/bin" "go2rtc_linux_amd64" fetch_and_deploy_gh_release "frigate" "blakeblackshear/frigate" "tarball" "v0.16.0-beta4" "/opt/frigate" @@ -77,7 +146,8 @@ cameras: width: 1920 fps: 5 EOF -#ln -sf /opt/frigate/config/config.yml /config/config.yml +mkdir -p /config +ln -sf /opt/frigate/config/config.yml /config/config.yml mkdir -p /media/frigate wget -qO /media/frigate/person-bicycle-car-detection.mp4 https://github.com/intel-iot-devkit/sample-videos/raw/master/person-bicycle-car-detection.mp4 msg_ok "Config ready" From 2cc6c6f4bc6d807e39db54bba17a36a6ff1a68f8 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 25 Jul 2025 12:29:44 +0200 Subject: [PATCH 0191/1733] Update keycloak.sh --- ct/keycloak.sh | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/ct/keycloak.sh b/ct/keycloak.sh index f473ac0c8..109fb3f1c 100644 --- a/ct/keycloak.sh +++ b/ct/keycloak.sh @@ -23,14 +23,13 @@ function update_script() { header_info check_container_storage check_container_resources - if [[ ! -d /opt/keycloak ]]; then - msg_error "No ${APP} installation found!" - exit 1 + msg_error "No ${APP} Installation Found!" + exit fi - if check_for_update "${APP}" "keycloak/keycloak"; then - + RELEASE=$(curl -fsSL https://api.github.com/repos/keycloak/keycloak/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + if [[ "${RELEASE}" != "$(cat ~/.keycloak 2>/dev/null)" ]] || [[ ! -f ~/.keycloak ]]; then msg_info "Stopping ${APP}" systemctl stop keycloak msg_ok "Stopped ${APP}" @@ -56,13 +55,14 @@ function update_script() { rm -rf keycloak.old msg_ok "Updated ${APP} LXC" - msg_info "Restarting Keycloak" + msg_info "Restating Keycloak" systemctl restart keycloak - msg_ok "Restarted Keycloak" - msg_ok "Update successful" + msg_ok "Restated Keycloak" + msg_ok "Update Successful" + else + msg_ok "No update required. ${APP} is already at v${RELEASE}" fi - - exit 0 + exit } start From ef5845c8a0d39cb3c37f4fb1ccaf3030598039bf Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 25 Jul 2025 12:30:50 +0200 Subject: [PATCH 0192/1733] Update keycloak.sh --- ct/keycloak.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/ct/keycloak.sh b/ct/keycloak.sh index 109fb3f1c..83fb4f44a 100644 --- a/ct/keycloak.sh +++ b/ct/keycloak.sh @@ -73,6 +73,3 @@ msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080/admin${CL}" -#echo -e "${TAB}${GN}Temporary admin user:${BL}tmpadm${CL}" -#echo -e "${TAB}${GN}Temporary admin password:${BL}admin123${CL}" -#echo -e "${INFO}${YW} If you modified ${BL}cache-ispn.xml${YW}: Re-apply your changes to the new file, otherwise leave it unchanged.${CL}" From c2da8bf912010b8a75a0f3f950d36425cdb96dbd Mon Sep 17 00:00:00 2001 From: vhsdream Date: Fri, 25 Jul 2025 08:06:46 -0400 Subject: [PATCH 0193/1733] Update tududi --- ct/tududi.sh | 7 +++---- install/tududi-install.sh | 6 +++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/ct/tududi.sh b/ct/tududi.sh index a4d6a4d49..b92724e6e 100644 --- a/ct/tududi.sh +++ b/ct/tududi.sh @@ -36,16 +36,15 @@ function update_script() { msg_info "Updating ${APP}" cp /opt/"$APP"/backend/.env /opt/"$APP".env - rm -rf /opt/"$APP" fetch_and_deploy_gh_release "tududi" "chrisvel/tududi" cd /opt/"$APP" $STD npm install export NODE_ENV=production $STD npm run frontend:build - cp -r ./dist ./backend/dist - cp -r ./public/locales ./backend/dist/locales - cp ./public/favicon.* ./backend/dist + mv ./dist ./backend + mv ./public/locales ./backend/dist + mv ./public/favicon.* ./backend/dist mv /opt/"$APP".env /opt/"$APP"/.env msg_ok "Updated $APP" diff --git a/install/tududi-install.sh b/install/tududi-install.sh index 17c73099d..0e334685a 100644 --- a/install/tududi-install.sh +++ b/install/tududi-install.sh @@ -25,9 +25,9 @@ cd /opt/tududi $STD npm install export NODE_ENV=production $STD npm run frontend:build -cp -r ./dist ./backend/dist -cp -r ./public/locales ./backend/dist/locales -cp ./public/favicon.* ./backend/dist +mv ./dist ./backend +mv ./public/locales ./backend/dist +mv ./public/favicon.* ./backend/dist msg_ok "Installed Tududi" msg_info "Creating config and database" From c82b62ac2fa5bed5aed2f9aed6b1842a991c944c Mon Sep 17 00:00:00 2001 From: vhsdream Date: Fri, 25 Jul 2025 08:11:22 -0400 Subject: [PATCH 0194/1733] Remove write_config from build.func --- misc/build.func | 1 - 1 file changed, 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index cbbaec5fd..c8b1ff4b6 100644 --- a/misc/build.func +++ b/misc/build.func @@ -824,7 +824,6 @@ advanced_settings() { if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create ${APP} LXC?" 10 58); then echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}" - write_config else clear header_info From 2d814c10e3c443419d55663efeb6fc5fcad2f502 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Fri, 25 Jul 2025 08:21:03 -0400 Subject: [PATCH 0195/1733] Update tududi --- ct/tududi.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/tududi.sh b/ct/tududi.sh index b92724e6e..b1624d3d6 100644 --- a/ct/tududi.sh +++ b/ct/tududi.sh @@ -65,6 +65,6 @@ description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${ADVANCED}${BL}Create your initial user in${CL} ${BGN}/opt/${APP}${CL}${BL} in the LXC:${CL} ${RD}npm run user:create ${CL}" +echo -e "${ADVANCED}${BL}Create your initial user in${CL} ${BGN}/opt/tududi${CL}${BL} in the LXC:${CL} ${RD}npm run user:create ${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3002${CL}" From fc5b0c1656ca7712985f4a4101b65c96af831854 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Fri, 25 Jul 2025 08:26:24 -0400 Subject: [PATCH 0196/1733] tududi: don't use APP var --- ct/tududi.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ct/tududi.sh b/ct/tududi.sh index b1624d3d6..50423aa42 100644 --- a/ct/tududi.sh +++ b/ct/tududi.sh @@ -35,17 +35,17 @@ function update_script() { msg_ok "Stopped Service" msg_info "Updating ${APP}" - cp /opt/"$APP"/backend/.env /opt/"$APP".env + cp /opt/tududi/backend/.env /opt/tududi.env fetch_and_deploy_gh_release "tududi" "chrisvel/tududi" - cd /opt/"$APP" + cd /opt/tududi $STD npm install export NODE_ENV=production $STD npm run frontend:build mv ./dist ./backend mv ./public/locales ./backend/dist mv ./public/favicon.* ./backend/dist - mv /opt/"$APP".env /opt/"$APP"/.env + mv /opt/tududi.env /opt/tududi/.env msg_ok "Updated $APP" msg_info "Starting Service" From fbaedb0f2af825c0795f6cf6aa7f8c0187bef74a Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 25 Jul 2025 12:26:39 +0000 Subject: [PATCH 0197/1733] Update .app files --- ct/headers/cleanuparr | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ct/headers/cleanuparr b/ct/headers/cleanuparr index ef403c52a..28427b25d 100644 --- a/ct/headers/cleanuparr +++ b/ct/headers/cleanuparr @@ -1,6 +1,6 @@ - ________ - / ____/ /__ ____ _____ __ ______ ____ _________ + ________ + / ____/ /__ ____ _____ __ ______ ____ ___________ / / / / _ \/ __ `/ __ \/ / / / __ \/ __ `/ ___/ ___/ / /___/ / __/ /_/ / / / / /_/ / /_/ / /_/ / / / / \____/_/\___/\__,_/_/ /_/\__,_/ .___/\__,_/_/ /_/ - /_/ \ No newline at end of file + /_/ From ed46b1ac9242549e5c3603a550d73e543b3cab0e Mon Sep 17 00:00:00 2001 From: vhsdream Date: Fri, 25 Jul 2025 08:52:17 -0400 Subject: [PATCH 0198/1733] Update tududi --- ct/tududi.sh | 1 + install/tududi-install.sh | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ct/tududi.sh b/ct/tududi.sh index 50423aa42..f8d4aaec2 100644 --- a/ct/tududi.sh +++ b/ct/tududi.sh @@ -36,6 +36,7 @@ function update_script() { msg_info "Updating ${APP}" cp /opt/tududi/backend/.env /opt/tududi.env + rm -rf /opt/tududi/backend/dist fetch_and_deploy_gh_release "tududi" "chrisvel/tududi" cd /opt/tududi diff --git a/install/tududi-install.sh b/install/tududi-install.sh index 0e334685a..006fa560c 100644 --- a/install/tududi-install.sh +++ b/install/tududi-install.sh @@ -14,7 +14,7 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt-get install -y sqlite3 +$STD apt-get install -y sqlite3 yq msg_ok "Installed Dependencies" NODE_VERSION="20" setup_nodejs From 4e33b5b437d2cd4136545994c126a87fc2ed3e02 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Fri, 25 Jul 2025 09:08:44 -0400 Subject: [PATCH 0199/1733] tududi: update messages --- install/tududi-install.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/install/tududi-install.sh b/install/tududi-install.sh index 006fa560c..b3d87d795 100644 --- a/install/tududi-install.sh +++ b/install/tududi-install.sh @@ -19,8 +19,8 @@ msg_ok "Installed Dependencies" NODE_VERSION="20" setup_nodejs -msg_info "Installing Tududi" fetch_and_deploy_gh_release "tududi" "chrisvel/tududi" +msg_info "Configuring Tududi" cd /opt/tududi $STD npm install export NODE_ENV=production @@ -28,9 +28,9 @@ $STD npm run frontend:build mv ./dist ./backend mv ./public/locales ./backend/dist mv ./public/favicon.* ./backend/dist -msg_ok "Installed Tududi" +msg_ok "Configured Tududi" -msg_info "Creating config and database" +msg_info "Creating env and database" DB_LOCATION="/opt/tududi-db" UPLOAD_DIR="/opt/tududi-uploads" mkdir -p {"$DB_LOCATION","$UPLOAD_DIR"} @@ -45,7 +45,7 @@ sed -e 's/^GOOGLE/# &/' \ /opt/tududi/backend/.env.example >/opt/tududi/backend/.env export DB_FILE="$DB_LOCATION/production.sqlite3" $STD npm run db:init -msg_ok "Created config and database" +msg_ok "Created env and database" msg_info "Creating service" cat </etc/systemd/system/tududi.service From c0749cb0f1d454848e291e8e811f88f81c2aa19f Mon Sep 17 00:00:00 2001 From: vhsdream Date: Fri, 25 Jul 2025 09:28:21 -0400 Subject: [PATCH 0200/1733] Remove maybefinance --- ct/maybefinance.sh | 85 ----------------- frontend/public/json/maybefinance.json | 35 ------- install/maybefinance-install.sh | 126 ------------------------- 3 files changed, 246 deletions(-) delete mode 100644 ct/maybefinance.sh delete mode 100644 frontend/public/json/maybefinance.json delete mode 100644 install/maybefinance-install.sh diff --git a/ct/maybefinance.sh b/ct/maybefinance.sh deleted file mode 100644 index 38a52ad3d..000000000 --- a/ct/maybefinance.sh +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env bash -source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: vhsdream -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://maybefinance.com - -APP="Maybe Finance" -var_tags="${var_tags:-finance;budget}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-4}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - - if [[ ! -d /opt/maybe ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - RELEASE=$(curl -s https://api.github.com/repos/maybe-finance/maybe/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') - if [[ "${RELEASE}" != "$(cat /opt/maybe_version.txt)" ]] || [[ ! -f /opt/maybe_version.txt ]]; then - msg_info "Stopping $APP" - systemctl stop maybe-web maybe-worker - msg_ok "Stopped $APP" - - msg_info "Creating Backup" - BACKUP_FILE="/opt/maybe_backup_$(date +%F).tar.gz" - $STD tar -czf "$BACKUP_FILE" /opt/maybe/{.env,storage/} &>/dev/null - msg_ok "Backup Created" - - msg_info "Updating $APP to v${RELEASE}" - rm -rf /opt/maybe - curl -fsSL "https://github.com/maybe-finance/maybe/archive/refs/tags/v${RELEASE}.zip" -o /tmp/v"$RELEASE".zip - unzip -q /tmp/v"$RELEASE".zip - mv maybe-"$RELEASE" /opt/maybe - RUBY_VERSION="$(cat /opt/maybe/.ruby-version)" RUBY_INSTALL_RAILS=false setup_rbenv_stack - cd /opt/maybe - rm ./config/credentials.yml.enc - source ~/.profile - $STD tar -xf "$BACKUP_FILE" --directory=/ - $STD ./bin/bundle install - $STD ./bin/bundle exec bootsnap precompile --gemfile -j 0 - $STD ./bin/bundle exec bootsnap precompile -j 0 app/ lib/ - export SECRET_KEY_BASE_DUMMY=1 - $STD dotenv -f ./.env ./bin/rails assets:precompile - $STD dotenv -f ./.env ./bin/rails db:prepare - msg_ok "Updated $APP to v${RELEASE}" - - msg_info "Starting $APP" - systemctl start maybe-worker maybe-web - msg_ok "Started $APP" - - msg_info "Cleaning Up" - rm /tmp/v"$RELEASE".zip - rm -f "$BACKUP_FILE" - msg_ok "Cleanup Completed" - - echo "${RELEASE}" >/opt/maybe_version.txt - msg_ok "Update Successful" - else - msg_ok "No update required. ${APP} is already at v${RELEASE}" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" diff --git a/frontend/public/json/maybefinance.json b/frontend/public/json/maybefinance.json deleted file mode 100644 index 9b35c3721..000000000 --- a/frontend/public/json/maybefinance.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "Maybe Finance", - "slug": "maybefinance", - "categories": [ - 23 - ], - "date_created": "2025-06-08", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 3000, - "documentation": "https://github.com/maybe-finance/maybe/", - "website": "https://maybefinance.com/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/maybe.webp", - "config_path": "/opt/maybe/.env", - "description": "Maybe is an all-in-one personal finance platform. Track, optimize, grow, and manage your money through every stage of life.", - "install_methods": [ - { - "type": "default", - "script": "ct/maybefinance.sh", - "resources": { - "cpu": 2, - "ram": 2048, - "hdd": 4, - "os": "debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] -} diff --git a/install/maybefinance-install.sh b/install/maybefinance-install.sh deleted file mode 100644 index e5d4a7887..000000000 --- a/install/maybefinance-install.sh +++ /dev/null @@ -1,126 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: vhsdream -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://maybefinance.com - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt-get install -y --no-install-recommends \ - libpq-dev \ - libvips42 \ - git \ - zlib1g-dev \ - build-essential \ - libssl-dev \ - libreadline-dev \ - libyaml-dev \ - libsqlite3-dev \ - sqlite3 \ - libxml2-dev \ - libxslt1-dev \ - libcurl4-openssl-dev \ - software-properties-common \ - libffi-dev \ - redis -msg_ok "Installed Dependencies" - -PG_VERSION=16 setup_postgresql - -msg_info "Setting up Postgresql" -DB_NAME="maybe" -DB_USER="maybe" -DB_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13)" -$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" -$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER TEMPLATE template0;" -{ - echo "${APPLICATION} database credentials" - echo "Database Name: ${DB_NAME}" - echo "Database User: ${DB_USER}" - echo "Database Password: ${DB_PASS}" -} >~/maybe.creds -msg_ok "Setup Postgresql" - -msg_info "Installing ${APPLICATION}" -RELEASE=$(curl -s https://api.github.com/repos/maybe-finance/maybe/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') -curl -fsSL "https://github.com/maybe-finance/maybe/archive/refs/tags/v${RELEASE}.zip" -o /tmp/v"$RELEASE".zip -unzip -q /tmp/v"$RELEASE".zip -mv maybe-"$RELEASE" /opt/maybe -RUBY_VERSION="$(cat /opt/maybe/.ruby-version)" RUBY_INSTALL_RAILS=false setup_rbenv_stack -cd /opt/maybe -cp ./.env.example ./.env -sed -i -e '/SELF_/a RAILS_ENV=production' \ - -e "s/secret-value/\"$(openssl rand -hex 64)\"/" \ - -e "/^SECRET_KEY/a RAILS_MASTER_KEY=\"$(openssl rand -hex 16)\"" \ - -e "s/_USER=postgres/_USER=${DB_USER}/" \ - -e "s/_PASSWORD=postgres/_PASSWORD=${DB_PASS}/" \ - -e "/_USER=/a POSTGRES_DB=${DB_NAME}" \ - -e 's/^# DISABLE/DISABLE/' \ - ./.env -sed -i -e '/_DB=/a\ -\ -REDIS_URL=redis://localhost:6379/1' \ - -e '/_SSL/a\ -RAILS_FORCE_SSL=false\ -RAILS_ASSUME_SSL=false' \ - ./.env -rm -f ./config/credentials.yml.enc -$STD ./bin/bundle install -$STD ./bin/bundle exec bootsnap precompile --gemfile -j 0 -$STD ./bin/bundle exec bootsnap precompile -j 0 app/ lib/ -export SECRET_KEY_BASE_DUMMY=1 -$STD dotenv -f ./.env ./bin/rails assets:precompile -$STD dotenv -f ./.env ./bin/rails db:prepare -echo "${RELEASE}" >/opt/maybe_version.txt -msg_ok "Installed ${APPLICATION}" - -msg_info "Creating services" - -cat </etc/systemd/system/maybe-web.service -[Unit] -Description=${APPLICATION} Web Service -After=network.target redis.service postgresql.service - -[Service] -Type=simple -WorkingDirectory=/opt/maybe -ExecStart=/root/.rbenv/shims/dotenv -f /opt/maybe/.env /opt/maybe/bin/rails s -Restart=on-abnormal - -[Install] -WantedBy=multi-user.target -EOF - -cat </etc/systemd/system/maybe-worker.service -[Unit] -Description=${APPLICATION} Worker Service -After=redis.service - -[Service] -Type=simple -WorkingDirectory=/opt/maybe -ExecStart=/root/.rbenv/shims/dotenv -f /opt/maybe/.env /opt/maybe/bin/bundle exec sidekiq -Restart=on-abnormal - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now maybe-web maybe-worker -msg_ok "Created services" - -motd_ssh -customize - -msg_info "Cleaning up" -rm -f /tmp/v"$RELEASE".zip -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" From 6aa4e15c93c3d9e9b45100a5a3e03495446985a4 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 25 Jul 2025 13:28:57 +0000 Subject: [PATCH 0201/1733] Update .app files --- ct/headers/maybefinance | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 ct/headers/maybefinance diff --git a/ct/headers/maybefinance b/ct/headers/maybefinance deleted file mode 100644 index 834c0fb4d..000000000 --- a/ct/headers/maybefinance +++ /dev/null @@ -1,6 +0,0 @@ - __ ___ __ _______ - / |/ /___ ___ __/ /_ ___ / ____(_)___ ____ _____ ________ - / /|_/ / __ `/ / / / __ \/ _ \ / /_ / / __ \/ __ `/ __ \/ ___/ _ \ - / / / / /_/ / /_/ / /_/ / __/ / __/ / / / / / /_/ / / / / /__/ __/ -/_/ /_/\__,_/\__, /_.___/\___/ /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/ - /____/ From 09f32293f14efe9aba0a9ad443a5692c29b1b297 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sun, 27 Jul 2025 11:06:05 -0400 Subject: [PATCH 0202/1733] Add MediaManager files --- ct/mediamanager.sh | 60 +++++++++++++ frontend/public/json/mediamanager.json | 41 +++++++++ install/mediamanager-install.sh | 113 +++++++++++++++++++++++++ 3 files changed, 214 insertions(+) create mode 100644 ct/mediamanager.sh create mode 100644 frontend/public/json/mediamanager.json create mode 100644 install/mediamanager-install.sh diff --git a/ct/mediamanager.sh b/ct/mediamanager.sh new file mode 100644 index 000000000..4ad52968b --- /dev/null +++ b/ct/mediamanager.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: vhsdream +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/maxdorninger/MediaManager + +APP="MediaManager" +var_tags="${var_tags:-arr}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/mediamanager ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + RELEASE=$(curl -fsSL https://api.github.com/repos/maxdorninger/MediaManager/releases/latest | yq '.tag_name' | sed 's/^v//') + if [[ "${RELEASE}" != "$(cat ~/.mediamanager 2>/dev/null)" ]] || [[ ! -f ~/.tududi ]]; then + msg_info "Stopping Service" + systemctl stop mediamanager + msg_ok "Stopped Service" + + msg_info "Updating ${APP}" + fetch_and_deploy_gh_release "MediaManager" "maxdorninger/MediaManager" "latest" "tarball" "/opt/mediamanager" + + msg_ok "Updated $APP" + + msg_info "Starting Service" + systemctl start mediamanager + msg_ok "Started Service" + + msg_ok "Updated Successfully" + else + msg_ok "Already up to date" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}" diff --git a/frontend/public/json/mediamanager.json b/frontend/public/json/mediamanager.json new file mode 100644 index 000000000..5df6c4b78 --- /dev/null +++ b/frontend/public/json/mediamanager.json @@ -0,0 +1,41 @@ +{ + "name": "MediaManager", + "slug": "mediamanager", + "categories": [ + 14, + 13 + ], + "date_created": "2025-07-22", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 8000, + "documentation": "https://maxdorninger.github.io/MediaManager/introduction.html", + "config_path": "/opt/mm_data/config.toml", + "website": "https://github.com/maxdorninger/MediaManager", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/mediamanager.webp", + "description": "A modern selfhosted media management system for your media library", + "install_methods": [ + { + "type": "default", + "script": "ct/mediamanager.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 4, + "os": "Debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": "", + "password": "admin" + }, + "notes": [ + { + "text": "During the installation, provide the email address of the first admin user", + "type": "info" + } + ] +} diff --git a/install/mediamanager-install.sh b/install/mediamanager-install.sh new file mode 100644 index 000000000..fa2bbdbf7 --- /dev/null +++ b/install/mediamanager-install.sh @@ -0,0 +1,113 @@ +#!/usr/bin/env bash + +# Copyright (c) 2025 Community Scripts ORG +# Author: vhsdream +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/maxdorninger/MediaManager + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing dependencies" +$STD apt-get install -y yq +msg_ok "Installed dependencies" + +NODE_VERSION="24" setup_nodejs +PYTHON_VERSION="3.13" setup_uv +PG_VERSION="17" setup_postgresql + +msg_info "Setting up PostgreSQL" +DB_NAME="MediaManager" +DB_USER="MediaManager" +DB_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" +$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" +$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER TEMPLATE template0;" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" +{ + echo "MediaManager Credentials" + echo "MediaManager Database User: $DB_USER" + echo "MediaManager Database Password: $DB_PASS" + echo "MediaManager Database Name: $DB_NAME" +} >>~/mediamanager.creds +msg_ok "Set up PostgreSQL" + +fetch_and_deploy_gh_release "MediaManager" "maxdorninger/MediaManager" "tarball" "latest" "/opt/mediamanager" +msg_info "Configuring MediaManager" +export CONFIG_DIR="/opt/mm_data/" +export FRONTEND_FILES_DIR="/opt/mm_data/web/build" +export BASE_PATH="" +export PUBLIC_VERSION="" +export PUBLIC_API_URL="${BASE_PATH}/api/v1" +export BASE_PATH="${BASE_PATH}/web" +cd /opt/mediamanager/web +$STD npm ci +$STD npm run build +mkdir -p "$CONFIG_DIR"/web +cp -r build "$FRONTEND_FILES_DIR" + +export BASE_PATH="" +export VIRTUAL_ENV="/opt/mm_data/venv" +cd /opt/mediamanager +$STD /usr/local/bin/uv venv "$VIRTUAL_ENV" +$STD /usr/local/bin/uv sync --locked --active +msg_ok "Configured MediaManager" + +read -r -p "Enter the email address of your first admin user: " admin_email +if [[ "$admin_email" ]]; then + EMAIL="$admin_email" +fi + +msg_info "Creating config and start script" +LOCAL_IP="$(hostname -I | awk '{print $1}')" +SECRET="$(openssl rand -hex 32)" +sed -e "s/localhost:8/$LOCAL_IP:8/g" \ + -e "s|/data/|$CONFIG_DIR|g" \ + -e 's/"db"/"localhost"/' \ + -e "s/password = \"MediaManager\"/password = \"$DB_PASS\"/" \ + -e "/^token_secret/s/=.*/= \"$SECRET\"/" \ + -e "s/admin@example.com/$EMAIL/" \ + /opt/mediamanager/config.example.toml >/opt/mm_data/config.toml + +mkdir -p "$CONFIG_DIR"/{images,tv,movies,torrents} + +cat </opt/mm_data/start.sh +#!/usr/bin/env bash + +export CONFIG_DIR="$CONFIG_DIR" +export FRONTEND_FILES_DIR="$FRONTEND_FILES_DIR" + +cd /opt/mediamanager +/usr/local/bin/uv run alembic upgrade head +/usr/local/bin/uv run fastapi run ./media_manager/main.py --port 8000 +EOF +msg_ok "Created config and start script" + +msg_info "Creating service" +cat </etc/systemd/system/mediamanager.service +[Unit] +Description=MediaManager Backend Service +After=network.target + +[Service] +Type=simple +WorkingDirectory=/opt/mm_data +ExecStart=/usr/bin/bash start.sh + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now mediamanager +msg_ok "Created service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 99c62b9b9f09d8e3fc533e3f1c017175faf14bf7 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sun, 27 Jul 2025 11:09:39 -0400 Subject: [PATCH 0203/1733] Try to fix teamspeak JSON --- frontend/public/json/teamspeak-server.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/teamspeak-server.json b/frontend/public/json/teamspeak-server.json index 7886d8a2a..2fe96bcee 100644 --- a/frontend/public/json/teamspeak-server.json +++ b/frontend/public/json/teamspeak-server.json @@ -1,6 +1,6 @@ { "name": "Teamspeak-Server", - "slug": "teamspeak-server", + "slug": "alpine-teamspeak-server", "categories": [ 24 ], From a334f2d99124a3bf90f7ca781dfe7445a84e517c Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sun, 27 Jul 2025 11:13:19 -0400 Subject: [PATCH 0204/1733] Remove non-alpine TS server install method from JSON --- frontend/public/json/teamspeak-server.json | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/frontend/public/json/teamspeak-server.json b/frontend/public/json/teamspeak-server.json index 2fe96bcee..1e23459b3 100644 --- a/frontend/public/json/teamspeak-server.json +++ b/frontend/public/json/teamspeak-server.json @@ -1,6 +1,6 @@ { "name": "Teamspeak-Server", - "slug": "alpine-teamspeak-server", + "slug": "teamspeak-server", "categories": [ 24 ], @@ -15,17 +15,6 @@ "config_path": "", "description": "TeamSpeak is a voice over IP (VoIP) application, primarily used by gamers and teams to chat in real time on dedicated servers. It delivers crystal‑clear, low‑latency voice communication.", "install_methods": [ - { - "type": "default", - "script": "ct/teamspeak-server.sh", - "resources": { - "cpu": 1, - "ram": 512, - "hdd": 2, - "os": "debian", - "version": "12" - } - }, { "type": "alpine", "script": "ct/alpine-teamspeak-server.sh", From 6c49a1cc7b1209ee3ef21e9fd4c6d5bd6b89006c Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sun, 27 Jul 2025 12:23:17 -0400 Subject: [PATCH 0205/1733] MediaManager: fix issues with install script --- install/mediamanager-install.sh | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/install/mediamanager-install.sh b/install/mediamanager-install.sh index fa2bbdbf7..7e0503856 100644 --- a/install/mediamanager-install.sh +++ b/install/mediamanager-install.sh @@ -18,12 +18,12 @@ $STD apt-get install -y yq msg_ok "Installed dependencies" NODE_VERSION="24" setup_nodejs -PYTHON_VERSION="3.13" setup_uv +setup_uv PG_VERSION="17" setup_postgresql msg_info "Setting up PostgreSQL" -DB_NAME="MediaManager" -DB_USER="MediaManager" +DB_NAME="mm_db" +DB_USER="mm_user" DB_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" $STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER TEMPLATE template0;" @@ -49,6 +49,8 @@ $STD npm ci $STD npm run build mkdir -p "$CONFIG_DIR"/web cp -r build "$FRONTEND_FILES_DIR" +cp -r media_manager "$CONFIG_DIR" +cp -r alembic* "$CONFIG_DIR" export BASE_PATH="" export VIRTUAL_ENV="/opt/mm_data/venv" @@ -68,9 +70,12 @@ SECRET="$(openssl rand -hex 32)" sed -e "s/localhost:8/$LOCAL_IP:8/g" \ -e "s|/data/|$CONFIG_DIR|g" \ -e 's/"db"/"localhost"/' \ + -e "s/user = \"MediaManager\"/user = \"$DB_USER\"/" \ -e "s/password = \"MediaManager\"/password = \"$DB_PASS\"/" \ + -e "s/dbname = \"MediaManager\"/dbname = \"$DB_NAME\"/" \ -e "/^token_secret/s/=.*/= \"$SECRET\"/" \ -e "s/admin@example.com/$EMAIL/" \ + -e '/^admin_emails/s/, .*/]/' \ /opt/mediamanager/config.example.toml >/opt/mm_data/config.toml mkdir -p "$CONFIG_DIR"/{images,tv,movies,torrents} @@ -85,6 +90,7 @@ cd /opt/mediamanager /usr/local/bin/uv run alembic upgrade head /usr/local/bin/uv run fastapi run ./media_manager/main.py --port 8000 EOF +chmod +x /opt/mm_data/start.sh msg_ok "Created config and start script" msg_info "Creating service" From 2b6757fca9e44032e018e60831d2bb9e9bfee04b Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sun, 27 Jul 2025 12:36:10 -0400 Subject: [PATCH 0206/1733] move copy commands to proper sequence --- install/mediamanager-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/mediamanager-install.sh b/install/mediamanager-install.sh index 7e0503856..2526b33c1 100644 --- a/install/mediamanager-install.sh +++ b/install/mediamanager-install.sh @@ -49,12 +49,12 @@ $STD npm ci $STD npm run build mkdir -p "$CONFIG_DIR"/web cp -r build "$FRONTEND_FILES_DIR" -cp -r media_manager "$CONFIG_DIR" -cp -r alembic* "$CONFIG_DIR" export BASE_PATH="" export VIRTUAL_ENV="/opt/mm_data/venv" cd /opt/mediamanager +cp -r media_manager "$CONFIG_DIR" +cp -r alembic* "$CONFIG_DIR" $STD /usr/local/bin/uv venv "$VIRTUAL_ENV" $STD /usr/local/bin/uv sync --locked --active msg_ok "Configured MediaManager" From a706d1d59d4814900bdf8fc01c5f2bf5aae00157 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sun, 27 Jul 2025 12:42:41 -0400 Subject: [PATCH 0207/1733] MediaManager: add check for config.example.toml --- install/mediamanager-install.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/install/mediamanager-install.sh b/install/mediamanager-install.sh index 2526b33c1..b827cfc9b 100644 --- a/install/mediamanager-install.sh +++ b/install/mediamanager-install.sh @@ -67,6 +67,10 @@ fi msg_info "Creating config and start script" LOCAL_IP="$(hostname -I | awk '{print $1}')" SECRET="$(openssl rand -hex 32)" +config_file="config.example.toml" +if [[ ! -f /opt/mediamanager/"$config_file" ]]; then + config_file="config.toml" +fi sed -e "s/localhost:8/$LOCAL_IP:8/g" \ -e "s|/data/|$CONFIG_DIR|g" \ -e 's/"db"/"localhost"/' \ @@ -76,7 +80,7 @@ sed -e "s/localhost:8/$LOCAL_IP:8/g" \ -e "/^token_secret/s/=.*/= \"$SECRET\"/" \ -e "s/admin@example.com/$EMAIL/" \ -e '/^admin_emails/s/, .*/]/' \ - /opt/mediamanager/config.example.toml >/opt/mm_data/config.toml + /opt/mediamanager/"$config_file" >/opt/mm_data/config.toml mkdir -p "$CONFIG_DIR"/{images,tv,movies,torrents} From 75d00ca6e3d136e518beda82d9cf3f39e2ba254b Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sun, 27 Jul 2025 12:51:02 -0400 Subject: [PATCH 0208/1733] MediaManager: fix start.sh --- install/mediamanager-install.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/install/mediamanager-install.sh b/install/mediamanager-install.sh index b827cfc9b..7428569a4 100644 --- a/install/mediamanager-install.sh +++ b/install/mediamanager-install.sh @@ -89,8 +89,10 @@ cat </opt/mm_data/start.sh export CONFIG_DIR="$CONFIG_DIR" export FRONTEND_FILES_DIR="$FRONTEND_FILES_DIR" +export BASE_PATH="" -cd /opt/mediamanager +cd /opt/mm_data +source ./venv/bin/activate /usr/local/bin/uv run alembic upgrade head /usr/local/bin/uv run fastapi run ./media_manager/main.py --port 8000 EOF From 757064341375abaed0aa26c444d84d63e9e808f5 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sun, 27 Jul 2025 13:26:42 -0400 Subject: [PATCH 0209/1733] MediaManager: tweaks to paths etc --- ct/mediamanager.sh | 22 ++++++++++++++++++++-- install/mediamanager-install.sh | 31 ++++++++++++++----------------- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/ct/mediamanager.sh b/ct/mediamanager.sh index 4ad52968b..be6e41f96 100644 --- a/ct/mediamanager.sh +++ b/ct/mediamanager.sh @@ -29,14 +29,32 @@ function update_script() { fi RELEASE=$(curl -fsSL https://api.github.com/repos/maxdorninger/MediaManager/releases/latest | yq '.tag_name' | sed 's/^v//') - if [[ "${RELEASE}" != "$(cat ~/.mediamanager 2>/dev/null)" ]] || [[ ! -f ~/.tududi ]]; then + if [[ "${RELEASE}" != "$(cat ~/.mediamanager 2>/dev/null)" ]] || [[ ! -f ~/.mediamanager ]]; then msg_info "Stopping Service" systemctl stop mediamanager msg_ok "Stopped Service" msg_info "Updating ${APP}" - fetch_and_deploy_gh_release "MediaManager" "maxdorninger/MediaManager" "latest" "tarball" "/opt/mediamanager" + fetch_and_deploy_gh_release "MediaManager" "maxdorninger/MediaManager" "tarball" "latest" "/opt/mediamanager" + MM_DIR="/opt/mm" + export CONFIG_DIR="${MM_DIR}/config" + export FRONTEND_FILES_DIR="${MM_DIR}/web/build" + export BASE_PATH="" + export PUBLIC_VERSION="" + export PUBLIC_API_URL="${BASE_PATH}/api/v1" + export BASE_PATH="${BASE_PATH}/web" + cd /opt/mediamanager/web + $STD npm ci + $STD npm run build + rm -rf "$FRONTEND_FILES_DIR"/build + cp -r build "$FRONTEND_FILES_DIR" + export BASE_PATH="" + export VIRTUAL_ENV="/opt/${MM_DIR}/venv" + cd /opt/mediamanager + rm -rf "$MM_DIR"/{media_manager,alembic*} + cp -r {media_manager,alembic*} "$MM_DIR" + $STD /usr/local/bin/uv sync --locked --active msg_ok "Updated $APP" msg_info "Starting Service" diff --git a/install/mediamanager-install.sh b/install/mediamanager-install.sh index 7428569a4..80cb37435 100644 --- a/install/mediamanager-install.sh +++ b/install/mediamanager-install.sh @@ -38,8 +38,10 @@ msg_ok "Set up PostgreSQL" fetch_and_deploy_gh_release "MediaManager" "maxdorninger/MediaManager" "tarball" "latest" "/opt/mediamanager" msg_info "Configuring MediaManager" -export CONFIG_DIR="/opt/mm_data/" -export FRONTEND_FILES_DIR="/opt/mm_data/web/build" +MM_DIR="/opt/mm" +MEDIA_DIR="${MM_DIR}/media" +export CONFIG_DIR="${MM_DIR}/config" +export FRONTEND_FILES_DIR="${MM_DIR}/web/build" export BASE_PATH="" export PUBLIC_VERSION="" export PUBLIC_API_URL="${BASE_PATH}/api/v1" @@ -47,14 +49,13 @@ export BASE_PATH="${BASE_PATH}/web" cd /opt/mediamanager/web $STD npm ci $STD npm run build -mkdir -p "$CONFIG_DIR"/web +mkdir -p {"$MM_DIR"/web,"$MEDIA_DIR","$CONFIG_DIR"} cp -r build "$FRONTEND_FILES_DIR" export BASE_PATH="" -export VIRTUAL_ENV="/opt/mm_data/venv" +export VIRTUAL_ENV="${MM_DIR}/venv" cd /opt/mediamanager -cp -r media_manager "$CONFIG_DIR" -cp -r alembic* "$CONFIG_DIR" +cp -r {media_manager,alembic*} "$MM_DIR" $STD /usr/local/bin/uv venv "$VIRTUAL_ENV" $STD /usr/local/bin/uv sync --locked --active msg_ok "Configured MediaManager" @@ -67,12 +68,8 @@ fi msg_info "Creating config and start script" LOCAL_IP="$(hostname -I | awk '{print $1}')" SECRET="$(openssl rand -hex 32)" -config_file="config.example.toml" -if [[ ! -f /opt/mediamanager/"$config_file" ]]; then - config_file="config.toml" -fi sed -e "s/localhost:8/$LOCAL_IP:8/g" \ - -e "s|/data/|$CONFIG_DIR|g" \ + -e "s|/data/|$MEDIA_DIR|g" \ -e 's/"db"/"localhost"/' \ -e "s/user = \"MediaManager\"/user = \"$DB_USER\"/" \ -e "s/password = \"MediaManager\"/password = \"$DB_PASS\"/" \ @@ -80,23 +77,23 @@ sed -e "s/localhost:8/$LOCAL_IP:8/g" \ -e "/^token_secret/s/=.*/= \"$SECRET\"/" \ -e "s/admin@example.com/$EMAIL/" \ -e '/^admin_emails/s/, .*/]/' \ - /opt/mediamanager/"$config_file" >/opt/mm_data/config.toml + /opt/mediamanager/config.example.toml >/opt/"$CONFIG_DIR"/config.toml -mkdir -p "$CONFIG_DIR"/{images,tv,movies,torrents} +mkdir -p "$MEDIA_DIR"/{images,tv,movies,torrents} -cat </opt/mm_data/start.sh +cat </opt/"$MM_DIR"/start.sh #!/usr/bin/env bash export CONFIG_DIR="$CONFIG_DIR" export FRONTEND_FILES_DIR="$FRONTEND_FILES_DIR" export BASE_PATH="" -cd /opt/mm_data +cd /opt/"$MM_DIR" source ./venv/bin/activate /usr/local/bin/uv run alembic upgrade head /usr/local/bin/uv run fastapi run ./media_manager/main.py --port 8000 EOF -chmod +x /opt/mm_data/start.sh +chmod +x /opt/"$MM_DIR"/start.sh msg_ok "Created config and start script" msg_info "Creating service" @@ -107,7 +104,7 @@ After=network.target [Service] Type=simple -WorkingDirectory=/opt/mm_data +WorkingDirectory=/opt/"$MM_DIR" ExecStart=/usr/bin/bash start.sh [Install] From 3458318e93afaf5085602c94c02f95fd2f081ca2 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sun, 27 Jul 2025 17:26:56 +0000 Subject: [PATCH 0210/1733] Update .app files --- ct/headers/mediamanager | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/mediamanager diff --git a/ct/headers/mediamanager b/ct/headers/mediamanager new file mode 100644 index 000000000..b05f4db74 --- /dev/null +++ b/ct/headers/mediamanager @@ -0,0 +1,6 @@ + __ ___ ___ __ ___ + / |/ /__ ____/ (_)___ _/ |/ /___ _____ ____ _____ ____ _____ + / /|_/ / _ \/ __ / / __ `/ /|_/ / __ `/ __ \/ __ `/ __ `/ _ \/ ___/ + / / / / __/ /_/ / / /_/ / / / / /_/ / / / / /_/ / /_/ / __/ / +/_/ /_/\___/\__,_/_/\__,_/_/ /_/\__,_/_/ /_/\__,_/\__, /\___/_/ + /____/ From 3e3dedb35cdae0caa762db1c88cd654b1d2b48fa Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 09:01:08 +0200 Subject: [PATCH 0211/1733] add vaapi check --- misc/build.func | 93 ++++++++++++++++++++++++++----------------------- 1 file changed, 50 insertions(+), 43 deletions(-) diff --git a/misc/build.func b/misc/build.func index c8b1ff4b6..ad84ec157 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1209,54 +1209,61 @@ EOF fi done - if ([ "$CT_TYPE" == "0" ] || [ "$is_vaapi_app" == "true" ]) && - ([[ -e /dev/dri/renderD128 ]] || [[ -e /dev/dri/card0 ]] || [[ -e /dev/fb0 ]]); then + if [[ "$CT_TYPE" == "0" || "$is_vaapi_app" == "true" ]]; then + VAAPI_DEVICES=() + for bypath in /dev/dri/by-path/*; do + [[ "$bypath" =~ -card$ || "$bypath" =~ -render$ ]] || continue + target=$(readlink -f "$bypath") + [[ -e "$target" ]] && VAAPI_DEVICES+=("$target") + done + [[ -e /dev/fb0 ]] && VAAPI_DEVICES+=("/dev/fb0") - echo "" - msg_custom "⚙️ " "\e[96m" "Configuring VAAPI passthrough for LXC container" - if [ "$CT_TYPE" != "0" ]; then - msg_custom "⚠️ " "\e[33m" "Container is unprivileged – VAAPI passthrough may not work without additional host configuration (e.g., idmap)." - fi - msg_custom "ℹ️ " "\e[96m" "VAAPI enables GPU hardware acceleration (e.g., for video transcoding in Jellyfin or Plex)." - echo "" - read -rp "➤ Automatically mount all available VAAPI devices? [Y/n]: " VAAPI_ALL + if [[ "${#VAAPI_DEVICES[@]}" -eq 0 ]]; then + : + elif [[ "${#VAAPI_DEVICES[@]}" -eq 1 && "$CT_TYPE" == "0" ]]; then + # Privileged + 1 device → silent passthrough + device="${VAAPI_DEVICES[0]}" + major_minor=$(stat -c '%t:%T' "$device" | awk -F: '{ printf "%d:%d", "0x"$1, "0x"$2 }') + echo "lxc.cgroup2.devices.allow: c $major_minor rwm" >>"$LXC_CONFIG" + echo "lxc.mount.entry: $device $device none bind,optional,create=file" >>"$LXC_CONFIG" + else + # Display info message + whiptail --title "VAAPI passthrough" --msgbox "\ +VAAPI passthrough has been enabled for this container. - if [[ "$VAAPI_ALL" =~ ^[Yy]$|^$ ]]; then - if [ "$CT_TYPE" == "0" ]; then - # PRV Container → alles zulässig - [[ -e /dev/dri/renderD128 ]] && { - echo "lxc.cgroup2.devices.allow: c 226:128 rwm" >>"$LXC_CONFIG" - echo "lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file" >>"$LXC_CONFIG" - } - [[ -e /dev/dri/card0 ]] && { - echo "lxc.cgroup2.devices.allow: c 226:0 rwm" >>"$LXC_CONFIG" - echo "lxc.mount.entry: /dev/dri/card0 dev/dri/card0 none bind,optional,create=file" >>"$LXC_CONFIG" - } - [[ -e /dev/fb0 ]] && { - echo "lxc.cgroup2.devices.allow: c 29:0 rwm" >>"$LXC_CONFIG" - echo "lxc.mount.entry: /dev/fb0 dev/fb0 none bind,optional,create=file" >>"$LXC_CONFIG" - } - [[ -d /dev/dri ]] && { - echo "lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" - } +This allows GPU hardware acceleration (e.g., video transcoding for Jellyfin, Plex, Frigate, etc.). + +You will now be prompted to select which VAAPI devices should be passed through." 12 72 + + CHOICES=() + for dev in "${VAAPI_DEVICES[@]}"; do + desc="$(basename "$dev")" + CHOICES+=("$dev" "$desc" "OFF") + done + + SELECTED_DEVICES=$(whiptail --title "VAAPI Device Selection" \ + --checklist "Select VAAPI device(s) to passthrough:" 20 70 10 \ + "${CHOICES[@]}" 3>&1 1>&2 2>&3) + + if [[ -n "$SELECTED_DEVICES" ]]; then + IDX=0 + for dev in $SELECTED_DEVICES; do + dev=$(sed 's/"//g' <<<"$dev") + if [[ "$CT_TYPE" == "0" ]]; then + major_minor=$(stat -c '%t:%T' "$dev" | awk -F: '{ printf "%d:%d", "0x"$1, "0x"$2 }') + echo "lxc.cgroup2.devices.allow: c $major_minor rwm" >>"$LXC_CONFIG" + echo "lxc.mount.entry: $dev $dev none bind,optional,create=file" >>"$LXC_CONFIG" + else + # Unprivileged: use devX: with proper GID + GID=$([[ "$dev" =~ render ]] && echo "104" || echo "44") + echo "dev${IDX}: $dev,gid=${GID}" >>"$LXC_CONFIG" + IDX=$((IDX + 1)) + fi + done else - # UNPRV Container → nur devX für UI - [[ -e /dev/dri/card0 ]] && echo "dev0: /dev/dri/card0,gid=44" >>"$LXC_CONFIG" - [[ -e /dev/dri/card1 ]] && echo "dev0: /dev/dri/card1,gid=44" >>"$LXC_CONFIG" - [[ -e /dev/dri/renderD128 ]] && echo "dev1: /dev/dri/renderD128,gid=104" >>"$LXC_CONFIG" + msg_warn "No VAAPI devices selected – passthrough skipped." fi fi - - fi - if [ "$CT_TYPE" == "1" ] && [ "$is_vaapi_app" == "true" ]; then - if [[ -e /dev/dri/card0 ]]; then - echo "dev0: /dev/dri/card0,gid=44" >>"$LXC_CONFIG" - elif [[ -e /dev/dri/card1 ]]; then - echo "dev0: /dev/dri/card1,gid=44" >>"$LXC_CONFIG" - fi - if [[ -e /dev/dri/renderD128 ]]; then - echo "dev1: /dev/dri/renderD128,gid=104" >>"$LXC_CONFIG" - fi fi # TUN device passthrough From ba178bef5ee6af467668a4cdd52a3eceed73177a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 09:01:53 +0200 Subject: [PATCH 0212/1733] validate render and video group --- misc/build.func | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/misc/build.func b/misc/build.func index ad84ec157..0b56a2470 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1218,16 +1218,24 @@ EOF done [[ -e /dev/fb0 ]] && VAAPI_DEVICES+=("/dev/fb0") + # Dynamically resolve group IDs + GID_VIDEO=$(getent group video | cut -d: -f3) + GID_RENDER=$(getent group render | cut -d: -f3) + [[ -z "$GID_VIDEO" ]] && GID_VIDEO=44 && msg_warn "'video' group not found, falling back to GID 44" + [[ -z "$GID_RENDER" ]] && GID_RENDER=104 && msg_warn "'render' group not found, falling back to GID 104" + if [[ "${#VAAPI_DEVICES[@]}" -eq 0 ]]; then : elif [[ "${#VAAPI_DEVICES[@]}" -eq 1 && "$CT_TYPE" == "0" ]]; then # Privileged + 1 device → silent passthrough device="${VAAPI_DEVICES[0]}" - major_minor=$(stat -c '%t:%T' "$device" | awk -F: '{ printf "%d:%d", "0x"$1, "0x"$2 }') - echo "lxc.cgroup2.devices.allow: c $major_minor rwm" >>"$LXC_CONFIG" - echo "lxc.mount.entry: $device $device none bind,optional,create=file" >>"$LXC_CONFIG" + if ! major_minor=$(stat -c '%t:%T' "$device" 2>/dev/null | awk -F: '{ printf "%d:%d", "0x"$1, "0x"$2 }'); then + msg_warn "Could not stat $device – skipping passthrough." + else + echo "lxc.cgroup2.devices.allow: c $major_minor rwm" >>"$LXC_CONFIG" + echo "lxc.mount.entry: $device $device none bind,optional,create=file" >>"$LXC_CONFIG" + fi else - # Display info message whiptail --title "VAAPI passthrough" --msgbox "\ VAAPI passthrough has been enabled for this container. @@ -1250,12 +1258,14 @@ You will now be prompted to select which VAAPI devices should be passed through. for dev in $SELECTED_DEVICES; do dev=$(sed 's/"//g' <<<"$dev") if [[ "$CT_TYPE" == "0" ]]; then - major_minor=$(stat -c '%t:%T' "$dev" | awk -F: '{ printf "%d:%d", "0x"$1, "0x"$2 }') + if ! major_minor=$(stat -c '%t:%T' "$dev" 2>/dev/null | awk -F: '{ printf "%d:%d", "0x"$1, "0x"$2 }'); then + msg_warn "Could not stat $dev – skipping." + continue + fi echo "lxc.cgroup2.devices.allow: c $major_minor rwm" >>"$LXC_CONFIG" echo "lxc.mount.entry: $dev $dev none bind,optional,create=file" >>"$LXC_CONFIG" else - # Unprivileged: use devX: with proper GID - GID=$([[ "$dev" =~ render ]] && echo "104" || echo "44") + GID=$([[ "$dev" =~ render ]] && echo "$GID_RENDER" || echo "$GID_VIDEO") echo "dev${IDX}: $dev,gid=${GID}" >>"$LXC_CONFIG" IDX=$((IDX + 1)) fi From 143bdcfd9baf4c5ca70a211028f0fe8ec6c424f5 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 09:08:02 +0200 Subject: [PATCH 0213/1733] displayname of vaapi device --- misc/build.func | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/misc/build.func b/misc/build.func index 0b56a2470..6db161052 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1211,10 +1211,13 @@ EOF if [[ "$CT_TYPE" == "0" || "$is_vaapi_app" == "true" ]]; then VAAPI_DEVICES=() - for bypath in /dev/dri/by-path/*; do - [[ "$bypath" =~ -card$ || "$bypath" =~ -render$ ]] || continue - target=$(readlink -f "$bypath") - [[ -e "$target" ]] && VAAPI_DEVICES+=("$target") + for bypath in /dev/dri/by-path/*-render; do + dev_target=$(readlink -f "$bypath") + pci_addr=$(basename "$bypath" | cut -d- -f1 --complement | sed 's/-render//') + pci_info=$(lspci -nn | grep "$pci_addr") + name="${pci_info#*: }" + label="$(basename "$dev_target") - ${name:-Unknown GPU}" + VAAPI_DEVICES+=("$dev_target" "$label" "OFF") done [[ -e /dev/fb0 ]] && VAAPI_DEVICES+=("/dev/fb0") @@ -1243,15 +1246,9 @@ This allows GPU hardware acceleration (e.g., video transcoding for Jellyfin, Ple You will now be prompted to select which VAAPI devices should be passed through." 12 72 - CHOICES=() - for dev in "${VAAPI_DEVICES[@]}"; do - desc="$(basename "$dev")" - CHOICES+=("$dev" "$desc" "OFF") - done - SELECTED_DEVICES=$(whiptail --title "VAAPI Device Selection" \ - --checklist "Select VAAPI device(s) to passthrough:" 20 70 10 \ - "${CHOICES[@]}" 3>&1 1>&2 2>&3) + --checklist "Select VAAPI device(s) / GPU(s) to passthrough:" 20 70 10 \ + "${VAAPI_DEVICES[@]}" 3>&1 1>&2 2>&3) if [[ -n "$SELECTED_DEVICES" ]]; then IDX=0 From 6c0599e30bc80473cdca95e4619f1ca17db05645 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 09:14:10 +0200 Subject: [PATCH 0214/1733] create_lxc: better handling of storage names & exit script --- misc/core.func | 69 ++++------------------------------------------ misc/create_lxc.sh | 16 +++++++---- 2 files changed, 16 insertions(+), 69 deletions(-) diff --git a/misc/core.func b/misc/core.func index a8774fc29..f7e48d412 100644 --- a/misc/core.func +++ b/misc/core.func @@ -415,70 +415,11 @@ msg_custom() { echo -e "${BFR:-} ${symbol} ${color}${msg}${CL:-\e[0m}" } -# msg_ok() { -# local msg="$1" -# [[ -z "$msg" ]] && return -# stop_spinner -# printf "\r\e[2K%s %b\n" "$CM" "${GN}${msg}${CL}" >&2 -# if declare -p MSG_INFO_SHOWN &>/dev/null && [[ "$(declare -p MSG_INFO_SHOWN 2>/dev/null)" =~ "declare -A" ]]; then -# unset MSG_INFO_SHOWN["$msg"] -# fi -# } - -# msg_error() { -# local msg="$1" -# [[ -z "$msg" ]] && return -# stop_spinner -# printf "\r\e[2K%s %b\n" "$CROSS" "${RD}${msg}${CL}" >&2 -# } - -# msg_warn() { -# local msg="$1" -# [[ -z "$msg" ]] && return -# stop_spinner -# printf "\r\e[2K%s %b\n" "$INFO" "${YWB}${msg}${CL}" >&2 -# if declare -p MSG_INFO_SHOWN &>/dev/null && [[ "$(declare -p MSG_INFO_SHOWN 2>/dev/null)" =~ "declare -A" ]]; then -# unset MSG_INFO_SHOWN["$msg"] -# fi -# } - -# msg_custom() { -# local symbol="${1:-"[*]"}" -# local color="${2:-"\e[36m"}" # Default: Cyan -# local msg="${3:-}" - -# [[ -z "$msg" ]] && return -# stop_spinner 2>/dev/null || true -# printf "\r\e[2K%s %b\n" "$symbol" "${color}${msg}${CL:-\e[0m}" >&2 -# } - -# msg_progress() { -# local current="$1" -# local total="$2" -# local label="$3" -# local width=40 -# local filled percent bar empty -# local fill_char="#" -# local empty_char="-" - -# if ! [[ "$current" =~ ^[0-9]+$ ]] || ! [[ "$total" =~ ^[0-9]+$ ]] || [[ "$total" -eq 0 ]]; then -# printf "\r\e[2K%s %b\n" "$CROSS" "${RD}Invalid progress input${CL}" >&2 -# return -# fi - -# percent=$(((current * 100) / total)) -# filled=$(((current * width) / total)) -# empty=$((width - filled)) - -# bar=$(printf "%${filled}s" | tr ' ' "$fill_char") -# bar+=$(printf "%${empty}s" | tr ' ' "$empty_char") - -# printf "\r\e[2K%s [%s] %3d%% %s" "${TAB}" "$bar" "$percent" "$label" >&2 - -# if [[ "$current" -eq "$total" ]]; then -# printf "\n" >&2 -# fi -# } +exit_script() { + clear + echo -e "\n${CROSS}${RD}User exited script${CL}\n" + exit +} run_container_safe() { local ct="$1" diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index b7d1f46e0..93d06bd0c 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -75,7 +75,7 @@ if ! check_storage_support "vztmpl"; then fi msg_ok "Validated Storage (rootdir / vztmpl)." -# This function is used to select the storage class and determine the corresponding storage content type and label. +# This function selects a storage pool for a given content type (e.g., rootdir, vztmpl). function select_storage() { local CLASS=$1 CONTENT CONTENT_LABEL @@ -110,7 +110,7 @@ function select_storage() { ;; esac - # >>> NEW: support STORAGE preset <<< + # Check for preset STORAGE variable if [ "$CONTENT" = "rootdir" ] && [ -n "${STORAGE:-}" ]; then if pvesm status -content "$CONTENT" | awk 'NR>1 {print $1}' | grep -qx "$STORAGE"; then STORAGE_RESULT="$STORAGE" @@ -121,8 +121,9 @@ function select_storage() { return 2 fi fi - local -a MENU + local -A STORAGE_MAP + local -a MENU local COL_WIDTH=0 while read -r TAG TYPE _ TOTAL USED FREE _; do @@ -148,12 +149,17 @@ function select_storage() { local WIDTH=$((COL_WIDTH + 42)) while true; do - local DISPLAY_SELECTED=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + local DISPLAY_SELECTED + DISPLAY_SELECTED=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "Storage Pools" \ --radiolist "Which storage pool for ${CONTENT_LABEL,,}?\n(Spacebar to select)" \ 16 "$WIDTH" 6 "${MENU[@]}" 3>&1 1>&2 2>&3) - [[ $? -ne 0 ]] && return 3 + # Cancel or ESC + [[ $? -ne 0 ]] && exit_script + + # Strip trailing whitespace or newline (important for storages like "storage (dir)") + DISPLAY_SELECTED=$(sed 's/[[:space:]]*$//' <<<"$DISPLAY_SELECTED") if [[ -z "$DISPLAY_SELECTED" || -z "${STORAGE_MAP[$DISPLAY_SELECTED]+_}" ]]; then whiptail --msgbox "No valid storage selected. Please try again." 8 58 From 54aa5f7b2d69c1f9e508fa5d04cc8d80288421f2 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 09:17:03 +0200 Subject: [PATCH 0215/1733] better error_handling --- misc/core.func | 6 ------ misc/create_lxc.sh | 8 ++++++++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/misc/core.func b/misc/core.func index f7e48d412..e06d98f80 100644 --- a/misc/core.func +++ b/misc/core.func @@ -415,12 +415,6 @@ msg_custom() { echo -e "${BFR:-} ${symbol} ${color}${msg}${CL:-\e[0m}" } -exit_script() { - clear - echo -e "\n${CROSS}${RD}User exited script${CL}\n" - exit -} - run_container_safe() { local ct="$1" shift diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index 93d06bd0c..7cc7b7021 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -50,6 +50,14 @@ function on_terminate() { exit 143 } +exit_script() { + clear + printf "\e[?25h" + echo -e "\n${CROSS}${RD}User exited script${CL}\n" + kill 0 + exit 1 +} + function check_storage_support() { local CONTENT="$1" local -a VALID_STORAGES=() From 313a5be0689247680962a8d8f52da8d15e80fc83 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 09:19:41 +0200 Subject: [PATCH 0216/1733] better error handling --- misc/create_lxc.sh | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index 7cc7b7021..6e9668729 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -31,10 +31,15 @@ function on_exit() { exit "$exit_code" } -function error_handler() { +error_handler() { local exit_code="$?" local line_number="$1" local command="$2" + + if [[ "${USER_EXITED:-false}" == "true" ]]; then + exit "$exit_code" + fi + printf "\e[?25h" echo -e "\n${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}\n" exit "$exit_code" @@ -45,12 +50,14 @@ function on_interrupt() { exit 130 } -function on_terminate() { +on_terminate() { + [[ "${USER_EXITED:-false}" == "true" ]] && exit 0 echo -e "\n${RD}Terminated by signal (SIGTERM)${CL}" exit 143 } exit_script() { + USER_EXITED=true clear printf "\e[?25h" echo -e "\n${CROSS}${RD}User exited script${CL}\n" From 858f0d7befce26fae1a16dc6e10c22ca5518ea5a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 09:22:03 +0200 Subject: [PATCH 0217/1733] Update create_lxc.sh --- misc/create_lxc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index 6e9668729..03d801315 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -61,7 +61,7 @@ exit_script() { clear printf "\e[?25h" echo -e "\n${CROSS}${RD}User exited script${CL}\n" - kill 0 + kill -TERM "$TOP_PID" 2>/dev/null || exit 1 exit 1 } From 2e95e2c7b9fdaabb87ba2fc3a01c8978ce486bf5 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 09:22:20 +0200 Subject: [PATCH 0218/1733] Update create_lxc.sh --- misc/create_lxc.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index 03d801315..a52fdb0f3 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -20,6 +20,8 @@ fi # This sets error handling options and defines the error_handler function to handle errors set -Eeuo pipefail +TOP_PID=$$ +USER_EXITED=false trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap on_exit EXIT trap on_interrupt INT From ba0a4197d97f26bb3a158506fdbc349cc5a2ce26 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 09:23:16 +0200 Subject: [PATCH 0219/1733] shebang --- misc/core.func | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/misc/core.func b/misc/core.func index e06d98f80..04d8528f8 100644 --- a/misc/core.func +++ b/misc/core.func @@ -1,30 +1,7 @@ +#!/usr/bin/env bash # Copyright (c) 2021-2025 community-scripts ORG # License: MIT | https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/LICENSE -# if ! declare -f wait_for >/dev/null; then -# echo "[DEBUG] Undefined function 'wait_for' used from: ${BASH_SOURCE[*]}" >&2 -# wait_for() { -# echo "[DEBUG] Fallback: wait_for called with: $*" >&2 -# true -# } -# fi - -# trap 'on_error $? $LINENO' ERR -# trap 'on_exit' EXIT -# trap 'on_interrupt' INT -# trap 'on_terminate' TERM - -# if ! declare -f wait_for >/dev/null; then -# wait_for() { -# true -# } -# fi - -# declare -A MSG_INFO_SHOWN=() -# SPINNER_PID="" -# SPINNER_ACTIVE=0 -# SPINNER_MSG="" - # ------------------------------------------------------------------------------ # Loads core utility groups once (colors, formatting, icons, defaults). # ------------------------------------------------------------------------------ From 7e843f08ac2d60270f1c25d4233d1ccf5b3150d4 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 09:23:56 +0200 Subject: [PATCH 0220/1733] Update build.func --- misc/build.func | 1 + 1 file changed, 1 insertion(+) diff --git a/misc/build.func b/misc/build.func index 6db161052..cb27dbaa5 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1199,6 +1199,7 @@ EOF "Ollama" "FileFlows" "Open WebUI" + "Debian" ) is_vaapi_app=false From 8772b26a82afa04f1a062fa610461b4cc7de560c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 09:27:05 +0200 Subject: [PATCH 0221/1733] Update create_lxc.sh --- misc/create_lxc.sh | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index a52fdb0f3..7cc7b7021 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -20,8 +20,6 @@ fi # This sets error handling options and defines the error_handler function to handle errors set -Eeuo pipefail -TOP_PID=$$ -USER_EXITED=false trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap on_exit EXIT trap on_interrupt INT @@ -33,15 +31,10 @@ function on_exit() { exit "$exit_code" } -error_handler() { +function error_handler() { local exit_code="$?" local line_number="$1" local command="$2" - - if [[ "${USER_EXITED:-false}" == "true" ]]; then - exit "$exit_code" - fi - printf "\e[?25h" echo -e "\n${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}\n" exit "$exit_code" @@ -52,18 +45,16 @@ function on_interrupt() { exit 130 } -on_terminate() { - [[ "${USER_EXITED:-false}" == "true" ]] && exit 0 +function on_terminate() { echo -e "\n${RD}Terminated by signal (SIGTERM)${CL}" exit 143 } exit_script() { - USER_EXITED=true clear printf "\e[?25h" echo -e "\n${CROSS}${RD}User exited script${CL}\n" - kill -TERM "$TOP_PID" 2>/dev/null || exit 1 + kill 0 exit 1 } From 23ee056cf063147d29a16845ea34547614e0ca36 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 09:27:16 +0200 Subject: [PATCH 0222/1733] Update create_lxc.sh --- misc/create_lxc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index 7cc7b7021..2eb67519f 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -50,7 +50,7 @@ function on_terminate() { exit 143 } -exit_script() { +function exit_script() { clear printf "\e[?25h" echo -e "\n${CROSS}${RD}User exited script${CL}\n" From f1c89cb165c9035157bbb0faca75af3c5068dcaf Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 09:32:10 +0200 Subject: [PATCH 0223/1733] better output for validated storage --- misc/create_lxc.sh | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index 2eb67519f..24fa41a4e 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -81,7 +81,7 @@ if ! check_storage_support "vztmpl"; then msg_error "No valid storage found for 'vztmpl' (Template)." exit 1 fi -msg_ok "Validated Storage (rootdir / vztmpl)." +msg_ok "Validated Storage | Container: $STORAGE_CT ($STORAGE_CT_INFO), Template: $STORAGE_TMPL ($STORAGE_TMPL_INFO)" # This function selects a storage pool for a given content type (e.g., rootdir, vztmpl). function select_storage() { @@ -152,6 +152,7 @@ function select_storage() { if [ $((${#MENU[@]} / 3)) -eq 1 ]; then STORAGE_RESULT="${STORAGE_MAP[${MENU[0]}]}" + STORAGE_INFO="${MENU[1]}" return 0 fi @@ -174,7 +175,13 @@ function select_storage() { continue fi - STORAGE_RESULT="${STORAGE_MAP[$DISPLAY_SELECTED]}" + STORAGE_RESULT="${STORAGE_MAP[$DISPLAY_SELECTED]}"STORAGE_RESULT="${STORAGE_MAP[$DISPLAY_SELECTED]}" + for ((i = 0; i < ${#MENU[@]}; i += 3)); do + if [[ "${MENU[$i]}" == "$DISPLAY_SELECTED" ]]; then + STORAGE_INFO="${MENU[$i + 1]}" + break + fi + done return 0 done } From 7f67d23cab2ff612d26ea8c0fa21ee0af301b44b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 09:34:09 +0200 Subject: [PATCH 0224/1733] fix path issue --- misc/build.func | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/misc/build.func b/misc/build.func index cb27dbaa5..ceebbba68 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1213,11 +1213,15 @@ EOF if [[ "$CT_TYPE" == "0" || "$is_vaapi_app" == "true" ]]; then VAAPI_DEVICES=() for bypath in /dev/dri/by-path/*-render; do - dev_target=$(readlink -f "$bypath") + dev_target=$(readlink -f "$bypath") || continue pci_addr=$(basename "$bypath" | cut -d- -f1 --complement | sed 's/-render//') - pci_info=$(lspci -nn | grep "$pci_addr") - name="${pci_info#*: }" - label="$(basename "$dev_target") - ${name:-Unknown GPU}" + pci_info=$(lspci -nn | grep "$pci_addr" || true) + if [[ -z "$pci_info" ]]; then + name="Unknown GPU ($pci_addr)" + else + name="${pci_info#*: }" + fi + label="$(basename "$dev_target") - $name" VAAPI_DEVICES+=("$dev_target" "$label" "OFF") done [[ -e /dev/fb0 ]] && VAAPI_DEVICES+=("/dev/fb0") From 6648c2e4ea84bd8286606341da5364e8f6056aa6 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 09:36:22 +0200 Subject: [PATCH 0225/1733] hardening --- misc/create_lxc.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index 24fa41a4e..594afc520 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -81,7 +81,7 @@ if ! check_storage_support "vztmpl"; then msg_error "No valid storage found for 'vztmpl' (Template)." exit 1 fi -msg_ok "Validated Storage | Container: $STORAGE_CT ($STORAGE_CT_INFO), Template: $STORAGE_TMPL ($STORAGE_TMPL_INFO)" +msg_ok "Validated Storage | Container: $CONTAINER_STORAGE ($CONTAINER_STORAGE_INFO), Template: $TEMPLATE_STORAGE ($TEMPLATE_STORAGE_INFO)" # This function selects a storage pool for a given content type (e.g., rootdir, vztmpl). function select_storage() { @@ -249,6 +249,7 @@ fi while true; do if select_storage template; then TEMPLATE_STORAGE="$STORAGE_RESULT" + TEMPLATE_STORAGE_INFO="$STORAGE_INFO" break fi done @@ -256,6 +257,7 @@ done while true; do if select_storage container; then CONTAINER_STORAGE="$STORAGE_RESULT" + CONTAINER_STORAGE_INFO="$STORAGE_INFO" break fi done From 2b378e08efadf4c60599081aabe2b8ef1ed54933 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 09:39:55 +0200 Subject: [PATCH 0226/1733] Update create_lxc.sh --- misc/create_lxc.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index 594afc520..e0e636df8 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -24,6 +24,10 @@ trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap on_exit EXIT trap on_interrupt INT trap on_terminate TERM +TEMPLATE_STORAGE="" +TEMPLATE_STORAGE_INFO="" +CONTAINER_STORAGE="" +CONTAINER_STORAGE_INFO="" function on_exit() { local exit_code="$?" From 1f65d5b2bb82554dded3c44488c5756491dbd707 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 09:47:12 +0200 Subject: [PATCH 0227/1733] Update create_lxc.sh --- misc/create_lxc.sh | 62 ++++++++-------------------------------------- 1 file changed, 11 insertions(+), 51 deletions(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index e0e636df8..e63880a84 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -24,10 +24,6 @@ trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap on_exit EXIT trap on_interrupt INT trap on_terminate TERM -TEMPLATE_STORAGE="" -TEMPLATE_STORAGE_INFO="" -CONTAINER_STORAGE="" -CONTAINER_STORAGE_INFO="" function on_exit() { local exit_code="$?" @@ -75,18 +71,6 @@ function check_storage_support() { [[ ${#VALID_STORAGES[@]} -gt 0 ]] } -# This checks for the presence of valid Container Storage and Template Storage locations -msg_info "Validating Storage" -if ! check_storage_support "rootdir"; then - msg_error "No valid storage found for 'rootdir' (Container)." - exit 1 -fi -if ! check_storage_support "vztmpl"; then - msg_error "No valid storage found for 'vztmpl' (Template)." - exit 1 -fi -msg_ok "Validated Storage | Container: $CONTAINER_STORAGE ($CONTAINER_STORAGE_INFO), Template: $TEMPLATE_STORAGE ($TEMPLATE_STORAGE_INFO)" - # This function selects a storage pool for a given content type (e.g., rootdir, vztmpl). function select_storage() { local CLASS=$1 CONTENT CONTENT_LABEL @@ -214,41 +198,16 @@ if qm status "$CTID" &>/dev/null || pct status "$CTID" &>/dev/null; then exit 206 fi -# DEFAULT_FILE="/usr/local/community-scripts/default_storage" -# if [[ -f "$DEFAULT_FILE" ]]; then -# source "$DEFAULT_FILE" -# if [[ -n "$TEMPLATE_STORAGE" && -n "$CONTAINER_STORAGE" ]]; then -# msg_info "Using default storage configuration from: $DEFAULT_FILE" -# msg_ok "Template Storage: ${BL}$TEMPLATE_STORAGE${CL} ${GN}|${CL} Container Storage: ${BL}$CONTAINER_STORAGE${CL}" -# else -# msg_warn "Default storage file exists but is incomplete – falling back to manual selection" -# TEMPLATE_STORAGE=$(select_storage template) -# msg_ok "Using ${BL}$TEMPLATE_STORAGE${CL} ${GN}for Template Storage." -# CONTAINER_STORAGE=$(select_storage container) -# msg_ok "Using ${BL}$CONTAINER_STORAGE${CL} ${GN}for Container Storage." -# fi -# else -# # TEMPLATE STORAGE SELECTION -# # Template Storage -# while true; do -# TEMPLATE_STORAGE=$(select_storage template) -# if [[ -n "$TEMPLATE_STORAGE" ]]; then -# msg_ok "Using ${BL}$TEMPLATE_STORAGE${CL} ${GN}for Template Storage." -# break -# fi -# msg_warn "No valid template storage selected. Please try again." -# done - -# while true; do -# CONTAINER_STORAGE=$(select_storage container) -# if [[ -n "$CONTAINER_STORAGE" ]]; then -# msg_ok "Using ${BL}$CONTAINER_STORAGE${CL} ${GN}for Container Storage." -# break -# fi -# msg_warn "No valid container storage selected. Please try again." -# done - -# fi +# This checks for the presence of valid Container Storage and Template Storage locations +msg_info "Validating Storage" +if ! check_storage_support "rootdir"; then + msg_error "No valid storage found for 'rootdir' (Container)." + exit 1 +fi +if ! check_storage_support "vztmpl"; then + msg_error "No valid storage found for 'vztmpl' (Template)." + exit 1 +fi while true; do if select_storage template; then @@ -265,6 +224,7 @@ while true; do break fi done +msg_ok "Validated Storage | Container: ${BL}$CONTAINER_STORAGE${CL} ($CONTAINER_STORAGE_INFO), Template: ${BL}$TEMPLATE_STORAGE${CL} ($TEMPLATE_STORAGE_INFO)" # Check free space on selected container storage STORAGE_FREE=$(pvesm status | awk -v s="$CONTAINER_STORAGE" '$1 == s { print $6 }') From 30a0298ab4886562043f471e82b922de14505fd0 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 10:02:15 +0200 Subject: [PATCH 0228/1733] Update create_lxc.sh --- misc/create_lxc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index e63880a84..e63dfd058 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -163,7 +163,7 @@ function select_storage() { continue fi - STORAGE_RESULT="${STORAGE_MAP[$DISPLAY_SELECTED]}"STORAGE_RESULT="${STORAGE_MAP[$DISPLAY_SELECTED]}" + STORAGE_RESULT="${STORAGE_MAP[$DISPLAY_SELECTED]}" for ((i = 0; i < ${#MENU[@]}; i += 3)); do if [[ "${MENU[$i]}" == "$DISPLAY_SELECTED" ]]; then STORAGE_INFO="${MENU[$i + 1]}" From 6ee841908010c9f22be9170b9668fe7bcced1776 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 10:04:27 +0200 Subject: [PATCH 0229/1733] Update create_lxc.sh --- misc/create_lxc.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index e63dfd058..a18774d8a 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -208,6 +208,7 @@ if ! check_storage_support "vztmpl"; then msg_error "No valid storage found for 'vztmpl' (Template)." exit 1 fi +msg_ok "Valid Storage Found" while true; do if select_storage template; then From 2b4422d2a85708a594c8d01358b8b122d97c8968 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 10:05:51 +0200 Subject: [PATCH 0230/1733] add container storage info --- misc/create_lxc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index a18774d8a..1d21f3279 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -225,7 +225,7 @@ while true; do break fi done -msg_ok "Validated Storage | Container: ${BL}$CONTAINER_STORAGE${CL} ($CONTAINER_STORAGE_INFO), Template: ${BL}$TEMPLATE_STORAGE${CL} ($TEMPLATE_STORAGE_INFO)" +msg_ok "Validated Storage | Container: ${BL}$CONTAINER_STORAGE${CL} ($CONTAINER_STORAGE_INFO)" # Check free space on selected container storage STORAGE_FREE=$(pvesm status | awk -v s="$CONTAINER_STORAGE" '$1 == s { print $6 }') From f3d8cab8e1de3b430e7001b0b3cbec40a28b1eb7 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 10:07:01 +0200 Subject: [PATCH 0231/1733] spell --- misc/build.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index ceebbba68..27191f0e9 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1293,7 +1293,7 @@ EOF # wait for status 'running' for i in {1..10}; do if pct status "$CTID" | grep -q "status: running"; then - msg_ok "Startted LXC Container" + msg_ok "Started LXC Container" break fi sleep 1 From 8eb2bbd4f7d2542ed47dad4a67b0f607f47e0e53 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 10:09:47 +0200 Subject: [PATCH 0232/1733] Update build.func --- misc/build.func | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/misc/build.func b/misc/build.func index 27191f0e9..adf2c4b39 100644 --- a/misc/build.func +++ b/misc/build.func @@ -85,30 +85,32 @@ pve_check() { local PVE_VER PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" - # 8 Version Check + # Check for Proxmox VE 8.x if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" if ((MINOR < 1 || MINOR > 4)); then - msg_error "This version of Proxmox Virtual Environment is not supported" - echo -e "Requires Proxmox Virtual Environment Version 8.1 – 8.4" - echo -e "Exiting..." - sleep 2 + msg_error "This version of Proxmox VE is not supported." + echo -e "Required: Proxmox VE version 8.1 – 8.4" exit 1 fi return 0 fi - # 9 Beta Version Check + # Check for Proxmox VE 9.x (Beta) — require confirmation if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then - msg_custom "Detected Proxmox Virtual Environment $PVE_VER – Beta state, use with caution!" - return 0 + if whiptail --title "Proxmox 9.x Detected (Beta)" \ + --yesno "You are using Proxmox VE $PVE_VER, which is currently in Beta state.\n\nThis version is experimentally supported.\n\nDo you want to proceed anyway?" 12 70; then + msg_ok "Confirmed: Continuing with Proxmox VE $PVE_VER" + return 0 + else + msg_error "Aborted by user: Proxmox VE 9.x was not confirmed." + exit 1 + fi fi - # All others (unsupported versions) - msg_error "This version of Proxmox Virtual Environment is not supported" - echo -e "Requires Proxmox Virtual Environment Version 8.1 – 8.4 or 9.x (Beta)" - echo -e "Exiting..." - sleep 2 + # All other unsupported versions + msg_error "This version of Proxmox VE is not supported." + echo -e "Supported versions: Proxmox VE 8.1 – 8.4 or 9.x (Beta, with confirmation)" exit 1 } From 5a55c0d1880d17e017bf8f68381985f375bf4d43 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 10:28:42 +0200 Subject: [PATCH 0233/1733] Update create_lxc.sh --- misc/create_lxc.sh | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index 1d21f3279..1e213fc56 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -311,7 +311,7 @@ PCT_OPTIONS=(${PCT_OPTIONS[@]:-${DEFAULT_PCT_OPTIONS[@]}}) # Secure creation of the LXC container with lock and template check lockfile="/tmp/template.${TEMPLATE}.lock" -exec 9>"$lockfile" >/dev/null 2>&1 || { +exec 9>"$lockfile" || { msg_error "Failed to create lock file '$lockfile'." exit 200 } @@ -377,27 +377,4 @@ if [[ "${PCT_UNPRIVILEGED:-1}" == "1" && " ${PCT_OPTIONS[*]} " == *"fuse=1"* ]]; msg_warn "Unprivileged container with FUSE may fail unless extra device mappings are configured." fi -# Extra: Debug-Ausgabe (wenn DEBUG=yes gesetzt) -DEBUG_LOG="/tmp/lxc_debug_${CTID}.log" -{ - echo "--- DEBUG DUMP for CTID $CTID ---" - echo "Hostname: ${CT_HOSTNAME:-unknown}" - echo "Template: ${TEMPLATE}" - echo "Template Storage: ${TEMPLATE_STORAGE}" - echo "Container Storage: ${CONTAINER_STORAGE}" - echo "Template Path: ${TEMPLATE_PATH}" - echo "Disk Size: ${PCT_DISK_SIZE:-8} GB" - echo "RAM Size: ${PCT_RAM_SIZE:-2048} MB" - echo "CPU Cores: ${PCT_CPU_CORES:-2}" - echo "Unprivileged: ${PCT_UNPRIVILEGED:-1}" - echo "PCT_OPTIONS:" - printf ' %s\n' "${PCT_OPTIONS[@]}" - echo "--- Container Config Dump ---" - [[ -f "/etc/pve/lxc/$CTID.conf" ]] && cat "/etc/pve/lxc/$CTID.conf" - echo "--- LVM Volumes ---" - lvs | grep "vm-${CTID}-disk-0" || echo "No LVM volume found." - echo "--- pct list ---" - pct list -} >"$DEBUG_LOG" - msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created." From 939b8d400d744a8e8b3930c31f4f4ff2191d3f37 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 11:08:38 +0200 Subject: [PATCH 0234/1733] Update build.func --- misc/build.func | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index adf2c4b39..2294e7981 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1214,7 +1214,8 @@ EOF if [[ "$CT_TYPE" == "0" || "$is_vaapi_app" == "true" ]]; then VAAPI_DEVICES=() - for bypath in /dev/dri/by-path/*-render; do + for bypath in /dev/dri/by-path/*-render /dev/dri/renderD*; do + [[ -e "$bypath" ]] || continue dev_target=$(readlink -f "$bypath") || continue pci_addr=$(basename "$bypath" | cut -d- -f1 --complement | sed 's/-render//') pci_info=$(lspci -nn | grep "$pci_addr" || true) From ba581801c2536c4ec0285e103654d3e1403b464f Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 11:30:01 +0200 Subject: [PATCH 0235/1733] Update build.func --- misc/build.func | 3 +++ 1 file changed, 3 insertions(+) diff --git a/misc/build.func b/misc/build.func index 2294e7981..a040a4527 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1263,6 +1263,9 @@ You will now be prompted to select which VAAPI devices should be passed through. for dev in $SELECTED_DEVICES; do dev=$(sed 's/"//g' <<<"$dev") if [[ "$CT_TYPE" == "0" ]]; then + if [[ -d /dev/dri ]]; then + echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" + fi if ! major_minor=$(stat -c '%t:%T' "$dev" 2>/dev/null | awk -F: '{ printf "%d:%d", "0x"$1, "0x"$2 }'); then msg_warn "Could not stat $dev – skipping." continue From 1b61a77c761198b5ac1cb026bcbbf290d02c9455 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 11:31:50 +0200 Subject: [PATCH 0236/1733] Update build.func --- misc/build.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index a040a4527..aa7d865fd 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1163,7 +1163,7 @@ build_container() { -unprivileged $CT_TYPE $PW " - bash -c "$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/create_lxc.sh)" + bash -c "$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/create_lxc.sh)" || exit if [ $? -ne 0 ]; then exit 200 fi From d4d2d2a0e157d69c2f7401f179dd68f7f9ab4d3e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 11:35:36 +0200 Subject: [PATCH 0237/1733] Update build.func --- misc/build.func | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/misc/build.func b/misc/build.func index aa7d865fd..635a98e18 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1247,12 +1247,23 @@ EOF echo "lxc.mount.entry: $device $device none bind,optional,create=file" >>"$LXC_CONFIG" fi else - whiptail --title "VAAPI passthrough" --msgbox "\ -VAAPI passthrough has been enabled for this container. + if [[ "$CT_TYPE" == "0" ]]; then + whiptail --title "VAAPI passthrough" --msgbox "\ +⚙️ VAAPI passthrough enabled -This allows GPU hardware acceleration (e.g., video transcoding for Jellyfin, Plex, Frigate, etc.). +GPU hardware acceleration will be available inside the container +(e.g., for Jellyfin, Plex, Frigate, etc.). -You will now be prompted to select which VAAPI devices should be passed through." 12 72 +You can now select which VAAPI devices to passthrough." 13 72 + else + whiptail --title "VAAPI passthrough (limited)" --msgbox "\ +⚠️ Limited VAAPI support in unprivileged container + +Some drivers (e.g., iHD) may not work due to LXC restrictions. +If VAAPI fails, consider using a privileged container. + +You can now select which VAAPI devices to passthrough." 13 72 + fi SELECTED_DEVICES=$(whiptail --title "VAAPI Device Selection" \ --checklist "Select VAAPI device(s) / GPU(s) to passthrough:" 20 70 10 \ @@ -1260,11 +1271,13 @@ You will now be prompted to select which VAAPI devices should be passed through. if [[ -n "$SELECTED_DEVICES" ]]; then IDX=0 + DID_MOUNT_DRI=0 for dev in $SELECTED_DEVICES; do dev=$(sed 's/"//g' <<<"$dev") if [[ "$CT_TYPE" == "0" ]]; then - if [[ -d /dev/dri ]]; then + if [[ "$DID_MOUNT_DRI" -eq 0 && -d /dev/dri ]]; then echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" + DID_MOUNT_DRI=1 fi if ! major_minor=$(stat -c '%t:%T' "$dev" 2>/dev/null | awk -F: '{ printf "%d:%d", "0x"$1, "0x"$2 }'); then msg_warn "Could not stat $dev – skipping." From 437890c3610cb52aef972238dcf867d1cdfb5904 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 11:39:22 +0200 Subject: [PATCH 0238/1733] Update build.func --- misc/build.func | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/misc/build.func b/misc/build.func index 635a98e18..f056549fa 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1254,7 +1254,10 @@ EOF GPU hardware acceleration will be available inside the container (e.g., for Jellyfin, Plex, Frigate, etc.). -You can now select which VAAPI devices to passthrough." 13 72 +⚠️ Note: You may need to install drivers manually inside the container, +such as 'intel-media-driver', 'libva2', or 'vainfo'. + +You can now select which VAAPI devices to passthrough." 15 74 else whiptail --title "VAAPI passthrough (limited)" --msgbox "\ ⚠️ Limited VAAPI support in unprivileged container @@ -1262,7 +1265,10 @@ You can now select which VAAPI devices to passthrough." 13 72 Some drivers (e.g., iHD) may not work due to LXC restrictions. If VAAPI fails, consider using a privileged container. -You can now select which VAAPI devices to passthrough." 13 72 +⚠️ Note: You may need to install drivers manually inside the container, +such as 'intel-media-driver', 'libva2', or 'vainfo'. + +You can now select which VAAPI devices to passthrough." 15 74 fi SELECTED_DEVICES=$(whiptail --title "VAAPI Device Selection" \ From 40b3ec344a41757dbcd01eaac5e9dc10ba6a39a4 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 11:44:50 +0200 Subject: [PATCH 0239/1733] Update build.func --- misc/build.func | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/misc/build.func b/misc/build.func index f056549fa..41c802c82 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1213,17 +1213,20 @@ EOF done if [[ "$CT_TYPE" == "0" || "$is_vaapi_app" == "true" ]]; then + declare -A seen_devices VAAPI_DEVICES=() + for bypath in /dev/dri/by-path/*-render /dev/dri/renderD*; do [[ -e "$bypath" ]] || continue dev_target=$(readlink -f "$bypath") || continue - pci_addr=$(basename "$bypath" | cut -d- -f1 --complement | sed 's/-render//') + [[ -n "${seen_devices[$dev_target]}" ]] && continue + seen_devices["$dev_target"]=1 + + pci_addr=$(basename "$bypath" | cut -d- -f1 --complement | sed 's/-render//' || true) pci_info=$(lspci -nn | grep "$pci_addr" || true) - if [[ -z "$pci_info" ]]; then - name="Unknown GPU ($pci_addr)" - else - name="${pci_info#*: }" - fi + name="${pci_info#*: }" + [[ -z "$name" ]] && name="Unknown GPU ($pci_addr)" + label="$(basename "$dev_target") - $name" VAAPI_DEVICES+=("$dev_target" "$label" "OFF") done From d28237502d5fdc81a1f38d909d06faa521579212 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 11:58:38 +0200 Subject: [PATCH 0240/1733] Update build.func --- misc/build.func | 54 +++++++++++++++++-------------------------------- 1 file changed, 19 insertions(+), 35 deletions(-) diff --git a/misc/build.func b/misc/build.func index 41c802c82..850cd27e2 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1188,20 +1188,9 @@ EOF # VAAPI passthrough for privileged containers or known apps VAAPI_APPS=( - "immich" - "Channels" - "Emby" - "ErsatzTV" - "Frigate" - "Jellyfin" - "Plex" - "Scrypted" - "Tdarr" - "Unmanic" - "Ollama" - "FileFlows" - "Open WebUI" - "Debian" + "immich" "Channels" "Emby" "ErsatzTV" "Frigate" "Jellyfin" + "Plex" "Scrypted" "Tdarr" "Unmanic" "Ollama" "FileFlows" + "Open WebUI" "Debian" ) is_vaapi_app=false @@ -1216,9 +1205,9 @@ EOF declare -A seen_devices VAAPI_DEVICES=() - for bypath in /dev/dri/by-path/*-render /dev/dri/renderD*; do + for bypath in /dev/dri/by-path/*-render /dev/dri/renderD* /dev/dri/card*; do [[ -e "$bypath" ]] || continue - dev_target=$(readlink -f "$bypath") || continue + dev_target=$(readlink -f "$bypath" 2>/dev/null || echo "$bypath") [[ -n "${seen_devices[$dev_target]}" ]] && continue seen_devices["$dev_target"]=1 @@ -1230,18 +1219,17 @@ EOF label="$(basename "$dev_target") - $name" VAAPI_DEVICES+=("$dev_target" "$label" "OFF") done - [[ -e /dev/fb0 ]] && VAAPI_DEVICES+=("/dev/fb0") + [[ -e /dev/fb0 ]] && VAAPI_DEVICES+=("/dev/fb0" "fb0 - Framebuffer" "OFF") - # Dynamically resolve group IDs GID_VIDEO=$(getent group video | cut -d: -f3) GID_RENDER=$(getent group render | cut -d: -f3) [[ -z "$GID_VIDEO" ]] && GID_VIDEO=44 && msg_warn "'video' group not found, falling back to GID 44" [[ -z "$GID_RENDER" ]] && GID_RENDER=104 && msg_warn "'render' group not found, falling back to GID 104" if [[ "${#VAAPI_DEVICES[@]}" -eq 0 ]]; then - : - elif [[ "${#VAAPI_DEVICES[@]}" -eq 1 && "$CT_TYPE" == "0" ]]; then - # Privileged + 1 device → silent passthrough + msg_warn "No VAAPI-compatible devices found." + elif [[ "${#VAAPI_DEVICES[@]}" -eq 3 && "$CT_TYPE" == "0" ]]; then + # Only one device and privileged → silent passthrough device="${VAAPI_DEVICES[0]}" if ! major_minor=$(stat -c '%t:%T' "$device" 2>/dev/null | awk -F: '{ printf "%d:%d", "0x"$1, "0x"$2 }'); then msg_warn "Could not stat $device – skipping passthrough." @@ -1252,30 +1240,26 @@ EOF else if [[ "$CT_TYPE" == "0" ]]; then whiptail --title "VAAPI passthrough" --msgbox "\ -⚙️ VAAPI passthrough enabled +✅ VAAPI passthrough has been enabled GPU hardware acceleration will be available inside the container -(e.g., for Jellyfin, Plex, Frigate, etc.). +(e.g., for Jellyfin, Plex, Frigate, etc.) -⚠️ Note: You may need to install drivers manually inside the container, -such as 'intel-media-driver', 'libva2', or 'vainfo'. - -You can now select which VAAPI devices to passthrough." 15 74 +ℹ️ Note: You may need to install drivers manually inside the container, +such as 'intel-media-driver', 'libva2', or 'vainfo'." 15 74 else whiptail --title "VAAPI passthrough (limited)" --msgbox "\ -⚠️ Limited VAAPI support in unprivileged container +⚠️ VAAPI passthrough in unprivileged containers may be limited -Some drivers (e.g., iHD) may not work due to LXC restrictions. -If VAAPI fails, consider using a privileged container. +Some drivers (e.g., iHD) require privileged access to DRM subsystems. +If VAAPI fails, consider switching to a privileged container. -⚠️ Note: You may need to install drivers manually inside the container, -such as 'intel-media-driver', 'libva2', or 'vainfo'. - -You can now select which VAAPI devices to passthrough." 15 74 +ℹ️ Note: You may need to install drivers manually inside the container, +such as 'intel-media-driver', 'libva2', or 'vainfo'." 15 74 fi SELECTED_DEVICES=$(whiptail --title "VAAPI Device Selection" \ - --checklist "Select VAAPI device(s) / GPU(s) to passthrough:" 20 70 10 \ + --checklist "Select VAAPI device(s) / GPU(s) to passthrough:" 20 72 10 \ "${VAAPI_DEVICES[@]}" 3>&1 1>&2 2>&3) if [[ -n "$SELECTED_DEVICES" ]]; then From e8764ef60dc5ad2c5984a7e6252bdc33956262e3 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 12:58:24 +0200 Subject: [PATCH 0241/1733] fix cp issue --- misc/build.func | 2 +- misc/tools.func | 34 ++++++++++++++++++++++++++-------- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/misc/build.func b/misc/build.func index 850cd27e2..fa17adbbb 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1212,7 +1212,7 @@ EOF seen_devices["$dev_target"]=1 pci_addr=$(basename "$bypath" | cut -d- -f1 --complement | sed 's/-render//' || true) - pci_info=$(lspci -nn | grep "$pci_addr" || true) + pci_info=$(lspci -nn | grep "${pci_addr#0000:}" || true) name="${pci_info#*: }" [[ -z "$name" ]] && name="Unknown GPU ($pci_addr)" diff --git a/misc/tools.func b/misc/tools.func index f88116980..04688d407 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -1007,23 +1007,41 @@ function fetch_and_deploy_gh_release() { local top_dirs top_dirs=$(find "$unpack_tmp" -mindepth 1 -maxdepth 1 -type d | wc -l) - - if [[ "$top_dirs" -eq 1 ]]; then + local top_entries inner_dir + top_entries=$(find "$unpack_tmp" -mindepth 1 -maxdepth 1) + if [[ "$(echo "$top_entries" | wc -l)" -eq 1 && -d "$top_entries" ]]; then # Strip leading folder - local inner_dir - inner_dir=$(find "$unpack_tmp" -mindepth 1 -maxdepth 1 -type d) + inner_dir="$top_entries" shopt -s dotglob nullglob - cp -r "$inner_dir"/* "$target/" + if compgen -G "$inner_dir/*" >/dev/null; then + cp -r "$inner_dir"/* "$target/" || { + msg_error "Failed to copy contents from $inner_dir to $target" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + } + else + msg_error "Inner directory is empty: $inner_dir" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + fi shopt -u dotglob nullglob else # Copy all contents shopt -s dotglob nullglob - cp -r "$unpack_tmp"/* "$target/" + if compgen -G "$unpack_tmp/*" >/dev/null; then + cp -r "$unpack_tmp"/* "$target/" || { + msg_error "Failed to copy contents to $target" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + } + else + msg_error "Unpacked archive is empty" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + fi shopt -u dotglob nullglob fi - rm -rf "$unpack_tmp" - ### Singlefile Mode ### elif [[ "$mode" == "singlefile" ]]; then local pattern="${6%\"}" From b4fe5299ac65b4c74dbe170c72f846305af12193 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 13:12:20 +0200 Subject: [PATCH 0242/1733] Update build.func --- misc/build.func | 70 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 22 deletions(-) diff --git a/misc/build.func b/misc/build.func index fa17adbbb..827c8e0fa 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1205,20 +1205,36 @@ EOF declare -A seen_devices VAAPI_DEVICES=() - for bypath in /dev/dri/by-path/*-render /dev/dri/renderD* /dev/dri/card*; do + VAAPI_DEVICES=() + seen_ids=() + + for bypath in /dev/dri/by-path/*-render /dev/dri/renderD*; do [[ -e "$bypath" ]] || continue - dev_target=$(readlink -f "$bypath" 2>/dev/null || echo "$bypath") - [[ -n "${seen_devices[$dev_target]}" ]] && continue - seen_devices["$dev_target"]=1 + dev_render=$(readlink -f "$bypath") || continue + id=$(basename "$dev_render") + [[ " ${seen_ids[*]} " == *" $id "* ]] && continue + seen_ids+=("$id") + + card="/dev/dri/card${id#renderD}" + combo_devices=("$dev_render") + [[ -e "$card" ]] && combo_devices+=("$card") pci_addr=$(basename "$bypath" | cut -d- -f1 --complement | sed 's/-render//' || true) pci_info=$(lspci -nn | grep "${pci_addr#0000:}" || true) name="${pci_info#*: }" [[ -z "$name" ]] && name="Unknown GPU ($pci_addr)" - label="$(basename "$dev_target") - $name" - VAAPI_DEVICES+=("$dev_target" "$label" "OFF") + label="$(basename "$dev_render")" + [[ -e "$card" ]] && label+=" + $(basename "$card")" + label+=" – $name" + + # Encode both devices with ":" separated, e.g.: /dev/dri/renderD128:/dev/dri/card0 + VAAPI_DEVICES+=("$( + IFS=: + echo "${combo_devices[*]}" + )" "$label" "OFF") done + [[ -e /dev/fb0 ]] && VAAPI_DEVICES+=("/dev/fb0" "fb0 - Framebuffer" "OFF") GID_VIDEO=$(getent group video | cut -d: -f3) @@ -1259,30 +1275,40 @@ such as 'intel-media-driver', 'libva2', or 'vainfo'." 15 74 fi SELECTED_DEVICES=$(whiptail --title "VAAPI Device Selection" \ - --checklist "Select VAAPI device(s) / GPU(s) to passthrough:" 20 72 10 \ + --checklist "Select VAAPI device(s) / GPU(s) to passthrough:" 20 100 6 \ "${VAAPI_DEVICES[@]}" 3>&1 1>&2 2>&3) + WHIPTAIL_EXIT=$? + if [[ "$WHIPTAIL_EXIT" -ne 0 ]]; then + exit_script + msg_error "VAAPI passthrough selection cancelled by user." + fi + if [[ -n "$SELECTED_DEVICES" ]]; then IDX=0 DID_MOUNT_DRI=0 for dev in $SELECTED_DEVICES; do dev=$(sed 's/"//g' <<<"$dev") - if [[ "$CT_TYPE" == "0" ]]; then - if [[ "$DID_MOUNT_DRI" -eq 0 && -d /dev/dri ]]; then - echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" - DID_MOUNT_DRI=1 + IFS=":" read -ra devices <<<"$dev" + + for d in "${devices[@]}"; do + if [[ "$CT_TYPE" == "0" ]]; then + if [[ "$DID_MOUNT_DRI" -eq 0 && -d /dev/dri ]]; then + echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" + DID_MOUNT_DRI=1 + fi + if ! major_minor=$(stat -c '%t:%T' "$d" 2>/dev/null | awk -F: '{ printf "%d:%d", "0x"$1, "0x"$2 }'); then + msg_warn "Could not stat $d – skipping." + continue + fi + echo "lxc.cgroup2.devices.allow: c $major_minor rwm" >>"$LXC_CONFIG" + echo "lxc.mount.entry: $d $d none bind,optional,create=file" >>"$LXC_CONFIG" + else + GID=$([[ "$d" =~ render ]] && echo "$GID_RENDER" || echo "$GID_VIDEO") + echo "dev${IDX}: $d,gid=${GID}" >>"$LXC_CONFIG" + IDX=$((IDX + 1)) fi - if ! major_minor=$(stat -c '%t:%T' "$dev" 2>/dev/null | awk -F: '{ printf "%d:%d", "0x"$1, "0x"$2 }'); then - msg_warn "Could not stat $dev – skipping." - continue - fi - echo "lxc.cgroup2.devices.allow: c $major_minor rwm" >>"$LXC_CONFIG" - echo "lxc.mount.entry: $dev $dev none bind,optional,create=file" >>"$LXC_CONFIG" - else - GID=$([[ "$dev" =~ render ]] && echo "$GID_RENDER" || echo "$GID_VIDEO") - echo "dev${IDX}: $dev,gid=${GID}" >>"$LXC_CONFIG" - IDX=$((IDX + 1)) - fi + done done else msg_warn "No VAAPI devices selected – passthrough skipped." From 2012bdd34a082b7bde7949cac8b25cd29acc2958 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 13:16:38 +0200 Subject: [PATCH 0243/1733] Update build.func --- misc/build.func | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/misc/build.func b/misc/build.func index 827c8e0fa..8b0cc3fc6 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1245,14 +1245,22 @@ EOF if [[ "${#VAAPI_DEVICES[@]}" -eq 0 ]]; then msg_warn "No VAAPI-compatible devices found." elif [[ "${#VAAPI_DEVICES[@]}" -eq 3 && "$CT_TYPE" == "0" ]]; then - # Only one device and privileged → silent passthrough - device="${VAAPI_DEVICES[0]}" - if ! major_minor=$(stat -c '%t:%T' "$device" 2>/dev/null | awk -F: '{ printf "%d:%d", "0x"$1, "0x"$2 }'); then - msg_warn "Could not stat $device – skipping passthrough." - else + # Silent passthrough mit ggf. mehreren Subdevices + IDX=0 + DID_MOUNT_DRI=0 + IFS=":" read -ra devices <<<"$(sed 's/"//g' <<<"${VAAPI_DEVICES[0]}")" + for d in "${devices[@]}"; do + if [[ "$DID_MOUNT_DRI" -eq 0 && -d /dev/dri ]]; then + echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" + DID_MOUNT_DRI=1 + fi + if ! major_minor=$(stat -c '%t:%T' "$d" 2>/dev/null | awk -F: '{ printf "%d:%d", "0x"$1, "0x"$2 }'); then + msg_warn "Could not stat $d – skipping." + continue + fi echo "lxc.cgroup2.devices.allow: c $major_minor rwm" >>"$LXC_CONFIG" - echo "lxc.mount.entry: $device $device none bind,optional,create=file" >>"$LXC_CONFIG" - fi + echo "lxc.mount.entry: $d $d none bind,optional,create=file" >>"$LXC_CONFIG" + done else if [[ "$CT_TYPE" == "0" ]]; then whiptail --title "VAAPI passthrough" --msgbox "\ From d183ff5c9ea535ddcc46ad0705aa12cfe9efba8b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 13:31:39 +0200 Subject: [PATCH 0244/1733] Update build.func --- misc/build.func | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/misc/build.func b/misc/build.func index 8b0cc3fc6..cef990b6c 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1296,26 +1296,29 @@ such as 'intel-media-driver', 'libva2', or 'vainfo'." 15 74 IDX=0 DID_MOUNT_DRI=0 for dev in $SELECTED_DEVICES; do - dev=$(sed 's/"//g' <<<"$dev") + dev="${dev%\"}" # remove trailing " + dev="${dev#\"}" # remove leading " IFS=":" read -ra devices <<<"$dev" - for d in "${devices[@]}"; do - if [[ "$CT_TYPE" == "0" ]]; then - if [[ "$DID_MOUNT_DRI" -eq 0 && -d /dev/dri ]]; then - echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" - DID_MOUNT_DRI=1 + + for d in "${devices[@]}"; do + if [[ "$CT_TYPE" == "0" ]]; then + if [[ "$DID_MOUNT_DRI" -eq 0 && -d /dev/dri ]]; then + echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" + DID_MOUNT_DRI=1 + fi + if ! major_minor=$(stat -c '%t:%T' "$d" 2>/dev/null | awk -F: '{ printf "%d:%d", "0x"$1, "0x"$2 }'); then + msg_warn "Could not stat $d – skipping." + continue + fi + echo "lxc.cgroup2.devices.allow: c $major_minor rwm" >>"$LXC_CONFIG" + echo "lxc.mount.entry: $d $d none bind,optional,create=file" >>"$LXC_CONFIG" + else + GID=$([[ "$d" =~ render ]] && echo "$GID_RENDER" || echo "$GID_VIDEO") + echo "dev${IDX}: $d,gid=${GID}" >>"$LXC_CONFIG" + IDX=$((IDX + 1)) fi - if ! major_minor=$(stat -c '%t:%T' "$d" 2>/dev/null | awk -F: '{ printf "%d:%d", "0x"$1, "0x"$2 }'); then - msg_warn "Could not stat $d – skipping." - continue - fi - echo "lxc.cgroup2.devices.allow: c $major_minor rwm" >>"$LXC_CONFIG" - echo "lxc.mount.entry: $d $d none bind,optional,create=file" >>"$LXC_CONFIG" - else - GID=$([[ "$d" =~ render ]] && echo "$GID_RENDER" || echo "$GID_VIDEO") - echo "dev${IDX}: $d,gid=${GID}" >>"$LXC_CONFIG" - IDX=$((IDX + 1)) - fi + done done done else From 2003628036c6462790bf8a717c306c6f411ef72e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 13:33:01 +0200 Subject: [PATCH 0245/1733] Update ubuntu.sh --- ct/ubuntu.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ct/ubuntu.sh b/ct/ubuntu.sh index 112ab19de..8cdf458c3 100644 --- a/ct/ubuntu.sh +++ b/ct/ubuntu.sh @@ -14,6 +14,7 @@ var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-ubuntu}" var_version="${var_version:-24.04}" +var_unprivileged="${var_unprivileged:-0}" header_info "$APP" variables From 0df796a730d7ab3cfe907fffb16613d2fa17b9d8 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 13:41:31 +0200 Subject: [PATCH 0246/1733] Update build.func --- misc/build.func | 53 ++++++++++++++++++++++--------------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/misc/build.func b/misc/build.func index cef990b6c..c83f9dd9f 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1202,10 +1202,8 @@ EOF done if [[ "$CT_TYPE" == "0" || "$is_vaapi_app" == "true" ]]; then - declare -A seen_devices - VAAPI_DEVICES=() - VAAPI_DEVICES=() + SINGLE_VAAPI_DEVICE="" seen_ids=() for bypath in /dev/dri/by-path/*-render /dev/dri/renderD*; do @@ -1228,7 +1226,6 @@ EOF [[ -e "$card" ]] && label+=" + $(basename "$card")" label+=" – $name" - # Encode both devices with ":" separated, e.g.: /dev/dri/renderD128:/dev/dri/card0 VAAPI_DEVICES+=("$( IFS=: echo "${combo_devices[*]}" @@ -1245,10 +1242,12 @@ EOF if [[ "${#VAAPI_DEVICES[@]}" -eq 0 ]]; then msg_warn "No VAAPI-compatible devices found." elif [[ "${#VAAPI_DEVICES[@]}" -eq 3 && "$CT_TYPE" == "0" ]]; then - # Silent passthrough mit ggf. mehreren Subdevices - IDX=0 + # Auto passthrough for single device + msg_info "Only one VAAPI-compatible device found – enabling passthrough." + + IFS=":" read -ra devices <<<"${VAAPI_DEVICES[0]//\"/}" DID_MOUNT_DRI=0 - IFS=":" read -ra devices <<<"$(sed 's/"//g' <<<"${VAAPI_DEVICES[0]}")" + for d in "${devices[@]}"; do if [[ "$DID_MOUNT_DRI" -eq 0 && -d /dev/dri ]]; then echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" @@ -1286,8 +1285,7 @@ such as 'intel-media-driver', 'libva2', or 'vainfo'." 15 74 --checklist "Select VAAPI device(s) / GPU(s) to passthrough:" 20 100 6 \ "${VAAPI_DEVICES[@]}" 3>&1 1>&2 2>&3) - WHIPTAIL_EXIT=$? - if [[ "$WHIPTAIL_EXIT" -ne 0 ]]; then + if [[ $? -ne 0 ]]; then exit_script msg_error "VAAPI passthrough selection cancelled by user." fi @@ -1296,29 +1294,26 @@ such as 'intel-media-driver', 'libva2', or 'vainfo'." 15 74 IDX=0 DID_MOUNT_DRI=0 for dev in $SELECTED_DEVICES; do - dev="${dev%\"}" # remove trailing " - dev="${dev#\"}" # remove leading " + dev="${dev%\"}" + dev="${dev#\"}" # strip quotes IFS=":" read -ra devices <<<"$dev" for d in "${devices[@]}"; do - - for d in "${devices[@]}"; do - if [[ "$CT_TYPE" == "0" ]]; then - if [[ "$DID_MOUNT_DRI" -eq 0 && -d /dev/dri ]]; then - echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" - DID_MOUNT_DRI=1 - fi - if ! major_minor=$(stat -c '%t:%T' "$d" 2>/dev/null | awk -F: '{ printf "%d:%d", "0x"$1, "0x"$2 }'); then - msg_warn "Could not stat $d – skipping." - continue - fi - echo "lxc.cgroup2.devices.allow: c $major_minor rwm" >>"$LXC_CONFIG" - echo "lxc.mount.entry: $d $d none bind,optional,create=file" >>"$LXC_CONFIG" - else - GID=$([[ "$d" =~ render ]] && echo "$GID_RENDER" || echo "$GID_VIDEO") - echo "dev${IDX}: $d,gid=${GID}" >>"$LXC_CONFIG" - IDX=$((IDX + 1)) + if [[ "$CT_TYPE" == "0" ]]; then + if [[ "$DID_MOUNT_DRI" -eq 0 && -d /dev/dri ]]; then + echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" + DID_MOUNT_DRI=1 fi - done + if ! major_minor=$(stat -c '%t:%T' "$d" 2>/dev/null | awk -F: '{ printf "%d:%d", "0x"$1, "0x"$2 }'); then + msg_warn "Could not stat $d – skipping." + continue + fi + echo "lxc.cgroup2.devices.allow: c $major_minor rwm" >>"$LXC_CONFIG" + echo "lxc.mount.entry: $d $d none bind,optional,create=file" >>"$LXC_CONFIG" + else + GID=$([[ "$d" =~ render ]] && echo "$GID_RENDER" || echo "$GID_VIDEO") + echo "dev${IDX}: $d,gid=${GID}" >>"$LXC_CONFIG" + IDX=$((IDX + 1)) + fi done done else From b2b1f8d042ef0be71153b15658eb138962a510cd Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 13:48:00 +0200 Subject: [PATCH 0247/1733] Update ubuntu-install.sh --- install/ubuntu-install.sh | 5 ----- 1 file changed, 5 deletions(-) diff --git a/install/ubuntu-install.sh b/install/ubuntu-install.sh index fe4658b69..ed5dc8170 100644 --- a/install/ubuntu-install.sh +++ b/install/ubuntu-install.sh @@ -17,9 +17,6 @@ msg_info "Installing Dependencies" $STD apt-get install -y jq msg_ok "Installed Dependencies" -msg_info "Check GH Releases" -fetch_and_deploy_gh_release 0xERR0R/blocky - # echo "Getting aceberg/WatchYourLAN..." # fetch_and_deploy_gh_release aceberg/WatchYourLAN # echo "Got Version: $RELEASE" @@ -116,8 +113,6 @@ fetch_and_deploy_gh_release 0xERR0R/blocky # RELEASE=$(get_gh_release dani-garcia/bw_web_builds) # echo "Got Version: $RELEASE" -msg_ok "Done" - motd_ssh customize From dc260de6409d5b823ff239ef1743a97974819e92 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 13:52:11 +0200 Subject: [PATCH 0248/1733] Update build.func --- misc/build.func | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/misc/build.func b/misc/build.func index c83f9dd9f..d696b8bd3 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1241,24 +1241,30 @@ EOF if [[ "${#VAAPI_DEVICES[@]}" -eq 0 ]]; then msg_warn "No VAAPI-compatible devices found." - elif [[ "${#VAAPI_DEVICES[@]}" -eq 3 && "$CT_TYPE" == "0" ]]; then - # Auto passthrough for single device + elif [[ "${#VAAPI_DEVICES[@]}" -eq 3 ]]; then msg_info "Only one VAAPI-compatible device found – enabling passthrough." IFS=":" read -ra devices <<<"${VAAPI_DEVICES[0]//\"/}" + IDX=0 DID_MOUNT_DRI=0 for d in "${devices[@]}"; do - if [[ "$DID_MOUNT_DRI" -eq 0 && -d /dev/dri ]]; then - echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" - DID_MOUNT_DRI=1 + if [[ "$CT_TYPE" == "0" ]]; then + if [[ "$DID_MOUNT_DRI" -eq 0 && -d /dev/dri ]]; then + echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" + DID_MOUNT_DRI=1 + fi + if ! major_minor=$(stat -c '%t:%T' "$d" 2>/dev/null | awk -F: '{ printf "%d:%d", "0x"$1, "0x"$2 }'); then + msg_warn "Could not stat $d – skipping." + continue + fi + echo "lxc.cgroup2.devices.allow: c $major_minor rwm" >>"$LXC_CONFIG" + echo "lxc.mount.entry: $d $d none bind,optional,create=file" >>"$LXC_CONFIG" + else + GID=$([[ "$d" =~ render ]] && echo "$GID_RENDER" || echo "$GID_VIDEO") + echo "dev${IDX}: $d,gid=${GID}" >>"$LXC_CONFIG" + IDX=$((IDX + 1)) fi - if ! major_minor=$(stat -c '%t:%T' "$d" 2>/dev/null | awk -F: '{ printf "%d:%d", "0x"$1, "0x"$2 }'); then - msg_warn "Could not stat $d – skipping." - continue - fi - echo "lxc.cgroup2.devices.allow: c $major_minor rwm" >>"$LXC_CONFIG" - echo "lxc.mount.entry: $d $d none bind,optional,create=file" >>"$LXC_CONFIG" done else if [[ "$CT_TYPE" == "0" ]]; then @@ -1311,6 +1317,7 @@ such as 'intel-media-driver', 'libva2', or 'vainfo'." 15 74 echo "lxc.mount.entry: $d $d none bind,optional,create=file" >>"$LXC_CONFIG" else GID=$([[ "$d" =~ render ]] && echo "$GID_RENDER" || echo "$GID_VIDEO") + echo "dev${IDX}: $d,gid=${GID}" >>"$LXC_CONFIG" IDX=$((IDX + 1)) fi From cc4597218cdec1a3428fbf04c9d7f9e0029d6e46 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 14:08:36 +0200 Subject: [PATCH 0249/1733] Update build.func --- misc/build.func | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/misc/build.func b/misc/build.func index d696b8bd3..3be1bdafe 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1213,9 +1213,12 @@ EOF [[ " ${seen_ids[*]} " == *" $id "* ]] && continue seen_ids+=("$id") - card="/dev/dri/card${id#renderD}" + card_index="${id#renderD}" + card="/dev/dri/card${card_index}" combo_devices=("$dev_render") - [[ -e "$card" ]] && combo_devices+=("$card") + if [[ "${#combo_devices[@]}" -eq 1 && -e /dev/dri/card0 ]]; then + combo_devices+=("/dev/dri/card0") + fi pci_addr=$(basename "$bypath" | cut -d- -f1 --complement | sed 's/-render//' || true) pci_info=$(lspci -nn | grep "${pci_addr#0000:}" || true) @@ -1266,6 +1269,7 @@ EOF IDX=$((IDX + 1)) fi done + else if [[ "$CT_TYPE" == "0" ]]; then whiptail --title "VAAPI passthrough" --msgbox "\ From 923c04d7bf75cde732c1f326728d449fa894326a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 14:11:42 +0200 Subject: [PATCH 0250/1733] Update build.func --- misc/build.func | 3 +++ 1 file changed, 3 insertions(+) diff --git a/misc/build.func b/misc/build.func index 3be1bdafe..16d36a9fd 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1219,6 +1219,7 @@ EOF if [[ "${#combo_devices[@]}" -eq 1 && -e /dev/dri/card0 ]]; then combo_devices+=("/dev/dri/card0") fi + echo "combo_devices=${combo_devices[*]}" pci_addr=$(basename "$bypath" | cut -d- -f1 --complement | sed 's/-render//' || true) pci_info=$(lspci -nn | grep "${pci_addr#0000:}" || true) @@ -1228,6 +1229,7 @@ EOF label="$(basename "$dev_render")" [[ -e "$card" ]] && label+=" + $(basename "$card")" label+=" – $name" + echo "[VAAPI DEBUG] Adding VAAPI combo: ${combo_devices[*]} ($label)" VAAPI_DEVICES+=("$( IFS=: @@ -1307,6 +1309,7 @@ such as 'intel-media-driver', 'libva2', or 'vainfo'." 15 74 dev="${dev%\"}" dev="${dev#\"}" # strip quotes IFS=":" read -ra devices <<<"$dev" + echo "[VAAPI DEBUG] Autopassthrough for devices: ${devices[*]}" for d in "${devices[@]}"; do if [[ "$CT_TYPE" == "0" ]]; then if [[ "$DID_MOUNT_DRI" -eq 0 && -d /dev/dri ]]; then From b17ae237d7cfced5796daf285beeedc32b00b7e1 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 14:19:17 +0200 Subject: [PATCH 0251/1733] msg_debug --- misc/build.func | 6 +++--- misc/core.func | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/misc/build.func b/misc/build.func index 16d36a9fd..0a0d27976 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1229,7 +1229,7 @@ EOF label="$(basename "$dev_render")" [[ -e "$card" ]] && label+=" + $(basename "$card")" label+=" – $name" - echo "[VAAPI DEBUG] Adding VAAPI combo: ${combo_devices[*]} ($label)" + msg_debug "[VAAPI DEBUG] Adding VAAPI combo: ${combo_devices[*]} ($label)" VAAPI_DEVICES+=("$( IFS=: @@ -1247,7 +1247,7 @@ EOF if [[ "${#VAAPI_DEVICES[@]}" -eq 0 ]]; then msg_warn "No VAAPI-compatible devices found." elif [[ "${#VAAPI_DEVICES[@]}" -eq 3 ]]; then - msg_info "Only one VAAPI-compatible device found – enabling passthrough." + #msg_info "Only one VAAPI-compatible device found – enabling passthrough." IFS=":" read -ra devices <<<"${VAAPI_DEVICES[0]//\"/}" IDX=0 @@ -1309,7 +1309,7 @@ such as 'intel-media-driver', 'libva2', or 'vainfo'." 15 74 dev="${dev%\"}" dev="${dev#\"}" # strip quotes IFS=":" read -ra devices <<<"$dev" - echo "[VAAPI DEBUG] Autopassthrough for devices: ${devices[*]}" + msg_debug "[VAAPI DEBUG] Autopassthrough for devices: ${devices[*]}" for d in "${devices[@]}"; do if [[ "$CT_TYPE" == "0" ]]; then if [[ "$DID_MOUNT_DRI" -eq 0 && -d /dev/dri ]]; then diff --git a/misc/core.func b/misc/core.func index 04d8528f8..42ee17b6d 100644 --- a/misc/core.func +++ b/misc/core.func @@ -392,6 +392,12 @@ msg_custom() { echo -e "${BFR:-} ${symbol} ${color}${msg}${CL:-\e[0m}" } +function msg_debug() { + if [[ "${var_full_verbose:-0}" == "1" ]]; then + echo -e "${DGY}[$(date '+%F %T')] [DEBUG]${CL} $*" + fi +} + run_container_safe() { local ct="$1" shift From 28b16eb7defb0070387a7d7d7352cae853d1c142 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 14:26:52 +0200 Subject: [PATCH 0252/1733] Update create_lxc.sh --- misc/create_lxc.sh | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index 1e213fc56..794d12a24 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -184,6 +184,10 @@ function select_storage() { exit 204 } +msg_debug "CTID=$CTID" +msg_debug "PCT_OSTYPE=$PCT_OSTYPE" +msg_debug "PCT_OSVERSION=${PCT_OSVERSION:-default}" + # Test if ID is valid [ "$CTID" -ge "100" ] || { msg_error "ID cannot be less than 100." @@ -202,10 +206,12 @@ fi msg_info "Validating Storage" if ! check_storage_support "rootdir"; then msg_error "No valid storage found for 'rootdir' (Container)." + msg_debug "check_storage_support('rootdir') → success" exit 1 fi if ! check_storage_support "vztmpl"; then msg_error "No valid storage found for 'vztmpl' (Template)." + msg_debug "check_storage_support('vztmpl') → success" exit 1 fi msg_ok "Valid Storage Found" @@ -214,6 +220,8 @@ while true; do if select_storage template; then TEMPLATE_STORAGE="$STORAGE_RESULT" TEMPLATE_STORAGE_INFO="$STORAGE_INFO" + msg_debug "TEMPLATE_STORAGE=$TEMPLATE_STORAGE" + msg_debug "TEMPLATE_STORAGE_INFO=$TEMPLATE_STORAGE_INFO" break fi done @@ -222,6 +230,8 @@ while true; do if select_storage container; then CONTAINER_STORAGE="$STORAGE_RESULT" CONTAINER_STORAGE_INFO="$STORAGE_INFO" + msg_debug "CONTAINER_STORAGE=$CONTAINER_STORAGE" + msg_debug "CONTAINER_STORAGE_INFO=$CONTAINER_STORAGE_INFO" break fi done @@ -249,7 +259,11 @@ fi TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" msg_info "Updating LXC Template List" -if ! timeout 15 pveam update >/dev/null 2>&1; then +msg_debug "TEMPLATE_SEARCH=$TEMPLATE_SEARCH" +msg_debug "TEMPLATES=(${TEMPLATES[*]})" +msg_debug "Selected TEMPLATE=$TEMPLATE" +msg_debug "TEMPLATE_PATH=$TEMPLATE_PATH" +if ! pveam update >/dev/null 2>&1; then TEMPLATE_FALLBACK=$(pveam list "$TEMPLATE_STORAGE" | awk "/$TEMPLATE_SEARCH/ {print \$2}" | sort -t - -k 2 -V | tail -n1) if [[ -z "$TEMPLATE_FALLBACK" ]]; then msg_error "Failed to update LXC template list and no local template matching '$TEMPLATE_SEARCH' found." @@ -311,6 +325,7 @@ PCT_OPTIONS=(${PCT_OPTIONS[@]:-${DEFAULT_PCT_OPTIONS[@]}}) # Secure creation of the LXC container with lock and template check lockfile="/tmp/template.${TEMPLATE}.lock" +msg_debug "Creating lockfile: $lockfile" exec 9>"$lockfile" || { msg_error "Failed to create lock file '$lockfile'." exit 200 @@ -320,6 +335,7 @@ flock -w 60 9 || { exit 211 } +msg_debug "pct create command: pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" &>/dev/null; then msg_error "Container creation failed. Checking if template is corrupted or incomplete." From 76d540810c3c5156381f6a122297850459e408bf Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 14:33:15 +0200 Subject: [PATCH 0253/1733] Update core.func --- misc/core.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/core.func b/misc/core.func index 42ee17b6d..86e8a07de 100644 --- a/misc/core.func +++ b/misc/core.func @@ -394,7 +394,7 @@ msg_custom() { function msg_debug() { if [[ "${var_full_verbose:-0}" == "1" ]]; then - echo -e "${DGY}[$(date '+%F %T')] [DEBUG]${CL} $*" + echo -e "${YWB}[$(date '+%F %T')] [DEBUG]${CL} $*" fi } From 8d50641c37ae5dcb83eb3a2ee410f46a15d81fff Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 14:37:37 +0200 Subject: [PATCH 0254/1733] Update create_lxc.sh --- misc/create_lxc.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index 794d12a24..03de6aaa3 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -259,10 +259,6 @@ fi TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" msg_info "Updating LXC Template List" -msg_debug "TEMPLATE_SEARCH=$TEMPLATE_SEARCH" -msg_debug "TEMPLATES=(${TEMPLATES[*]})" -msg_debug "Selected TEMPLATE=$TEMPLATE" -msg_debug "TEMPLATE_PATH=$TEMPLATE_PATH" if ! pveam update >/dev/null 2>&1; then TEMPLATE_FALLBACK=$(pveam list "$TEMPLATE_STORAGE" | awk "/$TEMPLATE_SEARCH/ {print \$2}" | sort -t - -k 2 -V | tail -n1) if [[ -z "$TEMPLATE_FALLBACK" ]]; then @@ -285,6 +281,10 @@ fi TEMPLATE="${TEMPLATES[-1]}" TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || echo "/var/lib/vz/template/cache/$TEMPLATE")" +msg_debug "TEMPLATE_SEARCH=$TEMPLATE_SEARCH" +msg_debug "TEMPLATES=(${TEMPLATES[*]})" +msg_debug "Selected TEMPLATE=$TEMPLATE" +msg_debug "TEMPLATE_PATH=$TEMPLATE_PATH" TEMPLATE_VALID=1 if ! pveam list "$TEMPLATE_STORAGE" | grep -q "$TEMPLATE"; then From f5b1cecef29a05f669087ad1823bbd6056d5779a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 14:40:36 +0200 Subject: [PATCH 0255/1733] Update core.func --- misc/core.func | 1 + 1 file changed, 1 insertion(+) diff --git a/misc/core.func b/misc/core.func index 86e8a07de..5df50efd9 100644 --- a/misc/core.func +++ b/misc/core.func @@ -394,6 +394,7 @@ msg_custom() { function msg_debug() { if [[ "${var_full_verbose:-0}" == "1" ]]; then + [[ "${var_verbose:-0}" != "1" ]] && var_verbose=1 echo -e "${YWB}[$(date '+%F %T')] [DEBUG]${CL} $*" fi } From c24b0b67cef75fc2287f1754419f5876a7cfa8ba Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 28 Jul 2025 14:44:37 +0200 Subject: [PATCH 0256/1733] Update build.func --- misc/build.func | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/misc/build.func b/misc/build.func index 0a0d27976..36cb530d5 100644 --- a/misc/build.func +++ b/misc/build.func @@ -98,14 +98,14 @@ pve_check() { # Check for Proxmox VE 9.x (Beta) — require confirmation if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then - if whiptail --title "Proxmox 9.x Detected (Beta)" \ - --yesno "You are using Proxmox VE $PVE_VER, which is currently in Beta state.\n\nThis version is experimentally supported.\n\nDo you want to proceed anyway?" 12 70; then - msg_ok "Confirmed: Continuing with Proxmox VE $PVE_VER" - return 0 - else - msg_error "Aborted by user: Proxmox VE 9.x was not confirmed." - exit 1 - fi + #if whiptail --title "Proxmox 9.x Detected (Beta)" \ + #--yesno "You are using Proxmox VE $PVE_VER, which is currently in Beta state.\n\nThis version is experimentally supported.\n\nDo you want to proceed anyway?" 12 70; then + #msg_ok "Confirmed: Continuing with Proxmox VE $PVE_VER" + return 0 + #else + #msg_error "Aborted by user: Proxmox VE 9.x was not confirmed." + #exit 1 + #fi fi # All other unsupported versions From cdf7263c37a1c6456a727b278bf72cee3f0e36bc Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Mon, 28 Jul 2025 21:20:14 +0200 Subject: [PATCH 0257/1733] Update ots-install.sh --- install/ots-install.sh | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/install/ots-install.sh b/install/ots-install.sh index da8e1d550..6e8dbb76e 100644 --- a/install/ots-install.sh +++ b/install/ots-install.sh @@ -28,6 +28,44 @@ STORAGE_TYPE=redis EOF msg_ok "Installed OTS" +msg_info "Generating Universal SSL Certificate" +mkdir -p /etc/ssl/ots +$STD openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \ + -keyout /etc/ssl/ots/key.pem \ + -out /etc/ssl/ots/cert.pem \ + -subj "/CN=ots" +msg_ok "Certificate Generated" + +msg_info "Setting up nginx" +cat </etc/nginx/sites-available/ots.conf +server { + listen 443 ssl; + listen [::]:443 ssl; + server_name ots; + + ssl_certificate /etc/ssl/ots/cert.pem; + ssl_certificate_key /etc/ssl/ots/key.pem; + + location / { + add_header X-Robots-Tag noindex; + + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + proxy_pass http://127.0.0.1:3000/; + } +} +EOF + +ln -s /etc/nginx/sites-available/ots.conf /etc/nginx/sites-enabled/ +rm -f /etc/nginx/sites-enabled/default +$STD systemctl reload nginx +msg_ok "Configured nginx" + msg_info "Creating Services" cat </etc/systemd/system/ots.service [Unit] From ddf62811518419cbc7ae2386512bb814154a37bb Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Mon, 28 Jul 2025 21:21:00 +0200 Subject: [PATCH 0258/1733] Update ots-install.sh --- install/ots-install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/install/ots-install.sh b/install/ots-install.sh index 6e8dbb76e..ef944f8c4 100644 --- a/install/ots-install.sh +++ b/install/ots-install.sh @@ -15,7 +15,8 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - redis-server + redis-server \ + nginx msg_ok "Installed Dependencies" msg_info "Installing OTS" From ac0fecdd9780fa4378b87b89f6a7dfbe4d8a773e Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Mon, 28 Jul 2025 21:21:15 +0200 Subject: [PATCH 0259/1733] Update ots-install.sh --- install/ots-install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/install/ots-install.sh b/install/ots-install.sh index ef944f8c4..973cae899 100644 --- a/install/ots-install.sh +++ b/install/ots-install.sh @@ -16,7 +16,8 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ redis-server \ - nginx + nginx \ + openssl msg_ok "Installed Dependencies" msg_info "Installing OTS" From bc3abac12f2c059a9eec01c01e78c067f0c2d05a Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Mon, 28 Jul 2025 21:26:35 +0200 Subject: [PATCH 0260/1733] Update ots-install.sh --- install/ots-install.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/install/ots-install.sh b/install/ots-install.sh index 973cae899..e2cf4c486 100644 --- a/install/ots-install.sh +++ b/install/ots-install.sh @@ -51,12 +51,12 @@ server { location / { add_header X-Robots-Tag noindex; - proxy_set_header Upgrade $http_upgrade; + proxy_set_header Upgrade \$http_upgrade; proxy_set_header Connection "Upgrade"; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto \$scheme; proxy_pass http://127.0.0.1:3000/; } From a3c18122d5e9e954aff34d25d32dadfcc1e03918 Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Mon, 28 Jul 2025 21:35:10 +0200 Subject: [PATCH 0261/1733] Update ots.sh --- ct/ots.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ct/ots.sh b/ct/ots.sh index e3e03df99..774c44847 100644 --- a/ct/ots.sh +++ b/ct/ots.sh @@ -29,10 +29,11 @@ function update_script() { fi RELEASE=$(curl -fsSL https://api.github.com/repos/Luzifer/ots/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then + if [[ "${RELEASE}" != "$(cat ~/.ots 2>/dev/null)" ]] || [[ ! -f ~/.ots ]]; then msg_info "Stopping ${APP} Service" systemctl stop ots + systemctl stop nginx msg_ok "Stopped ${APP} Service" msg_info "Updating ${APP} to v${RELEASE}" @@ -41,6 +42,7 @@ function update_script() { msg_info "Stopping ${APP} Service" systemctl start ots + systemctl start nginx msg_ok "Stopped ${APP} Service" else From 21eebe474b88ab5c77c95233fd2ec0ed0f0cb559 Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Mon, 28 Jul 2025 21:37:34 +0200 Subject: [PATCH 0262/1733] Update ots-install.sh --- install/ots-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/ots-install.sh b/install/ots-install.sh index e2cf4c486..1a829132a 100644 --- a/install/ots-install.sh +++ b/install/ots-install.sh @@ -23,7 +23,7 @@ msg_ok "Installed Dependencies" msg_info "Installing OTS" fetch_and_deploy_gh_release "ots" "Luzifer/ots" "prebuild" "latest" "/opt/ots" "ots_linux_amd64.tgz" cat </opt/ots/env -LISTEN=0.0.0.0:3000 +LISTEN=127.0.0.1:3000 REDIS_URL=redis://127.0.0.1:6379 SECRET_EXPIRY=604800 STORAGE_TYPE=redis From 8d26e9b8028a0278623582ed477229eb84e1a000 Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Mon, 28 Jul 2025 21:39:36 +0200 Subject: [PATCH 0263/1733] Update ots-install.sh --- install/ots-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/ots-install.sh b/install/ots-install.sh index 1a829132a..be172063b 100644 --- a/install/ots-install.sh +++ b/install/ots-install.sh @@ -57,7 +57,7 @@ server { proxy_set_header X-Real-IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto \$scheme; - + client_max_body_size 64M; proxy_pass http://127.0.0.1:3000/; } } From 394befcfef5ce2515f727b499bce451ec26f1353 Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Mon, 28 Jul 2025 21:46:53 +0200 Subject: [PATCH 0264/1733] Update ots.sh --- ct/ots.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/ots.sh b/ct/ots.sh index 774c44847..26a224e87 100644 --- a/ct/ots.sh +++ b/ct/ots.sh @@ -58,4 +58,4 @@ description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" +echo -e "${TAB}${GATEWAY}${BGN}https://${IP}${CL}" From 8b96e92db9d182cb836be3adb4194d49a233b100 Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Mon, 28 Jul 2025 21:47:20 +0200 Subject: [PATCH 0265/1733] Create ots --- frontend/public/json/ots | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 frontend/public/json/ots diff --git a/frontend/public/json/ots b/frontend/public/json/ots new file mode 100644 index 000000000..a9f042f9f --- /dev/null +++ b/frontend/public/json/ots @@ -0,0 +1,40 @@ +{ + "name": "OTS", + "slug": "ots", + "categories": [ + 6 + ], + "date_created": "2025-07-28", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 443, + "documentation": "https://github.com/Luzifer/ots/wiki", + "config_path": "/opt/ots/env", + "website": "https://github.com/Luzifer/ots", + "logo": null, + "description": "One-Time-Secret sharing platform with a symmetric 256bit AES encryption in the browser.", + "install_methods": [ + { + "type": "default", + "script": "ct/ots.sh", + "resources": { + "cpu": 1, + "ram": 512, + "hdd": 3, + "os": "Debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "When it is in used external please use it behind reverse proxy or create your own certificates", + "type": "info" + } + ] +} From 7e7566ccc02a33da5cf7115ba6e207a4cf98232f Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 29 Jul 2025 10:32:57 +0200 Subject: [PATCH 0266/1733] cleanup --- ct/cleanuparr.sh | 57 ---------------- ct/keycloak.sh | 75 ---------------------- ct/linkstack.sh | 43 ------------- frontend/public/json/cleanuparr.json | 35 ---------- frontend/public/json/linkstack.json | 44 ------------- install/alpine-teamspeak-server-install.sh | 69 -------------------- install/cleanuparr-install.sh | 47 -------------- install/keycloak-install.sh | 74 --------------------- install/linkstack-install.sh | 48 -------------- 9 files changed, 492 deletions(-) delete mode 100755 ct/cleanuparr.sh delete mode 100644 ct/keycloak.sh delete mode 100644 ct/linkstack.sh delete mode 100644 frontend/public/json/cleanuparr.json delete mode 100644 frontend/public/json/linkstack.json delete mode 100644 install/alpine-teamspeak-server-install.sh delete mode 100755 install/cleanuparr-install.sh delete mode 100644 install/keycloak-install.sh delete mode 100644 install/linkstack-install.sh diff --git a/ct/cleanuparr.sh b/ct/cleanuparr.sh deleted file mode 100755 index 26ac30452..000000000 --- a/ct/cleanuparr.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: community-scripts ORG -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/Cleanuparr/Cleanuparr - -APP="Cleanuparr" -var_tags="${var_tags:-arr}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-1024}" -var_disk="${var_disk:-4}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -f /opt/cleanuparr/Cleanuparr ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - RELEASE=$(curl -fsSL https://api.github.com/repos/Cleanuparr/Cleanuparr/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - if [[ "${RELEASE}" != "$(cat ~/.Cleanuparr 2>/dev/null)" ]] || [[ ! -f ~/.Cleanuparr ]]; then - msg_info "Stopping ${APP}" - systemctl stop cleanuparr - msg_ok "Stopped ${APP}" - - fetch_and_deploy_gh_release "Cleanuparr" "Cleanuparr/Cleanuparr" "prebuild" "$RELEASE" "/opt/cleanuparr" "*linux-amd64.zip" - msg_ok "Updated ${APP}" - - msg_info "Starting ${APP}" - systemctl start cleanuparr - msg_ok "Started ${APP}" - msg_ok "Updated Successfully" - else - msg_ok "No update required. ${APP} is already at v${RELEASE}" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:11011${CL}" diff --git a/ct/keycloak.sh b/ct/keycloak.sh deleted file mode 100644 index 83fb4f44a..000000000 --- a/ct/keycloak.sh +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) | Co-Author: remz1337 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://www.keycloak.org/ - -APP="Keycloak" -var_tags="${var_tags:-access-management}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-4}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/keycloak ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - RELEASE=$(curl -fsSL https://api.github.com/repos/keycloak/keycloak/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - if [[ "${RELEASE}" != "$(cat ~/.keycloak 2>/dev/null)" ]] || [[ ! -f ~/.keycloak ]]; then - msg_info "Stopping ${APP}" - systemctl stop keycloak - msg_ok "Stopped ${APP}" - - msg_info "Updating packages" - apt-get update &>/dev/null - apt-get -y upgrade &>/dev/null - msg_ok "Updated packages" - - msg_info "Backup old Keycloak" - cd /opt - mv keycloak keycloak.old - tar -czf keycloak_conf_backup.tar.gz keycloak.old/conf - msg_ok "Backup done" - - fetch_and_deploy_gh_release "keycloak" "keycloak/keycloak" "prebuild" "latest" "/opt/keycloak" "keycloak-*.tar.gz" - - msg_info "Updating ${APP}" - cd /opt - mv keycloak_conf_backup.tar.gz keycloak/conf - cp -r keycloak.old/providers keycloak - cp -r keycloak.old/themes keycloak - rm -rf keycloak.old - msg_ok "Updated ${APP} LXC" - - msg_info "Restating Keycloak" - systemctl restart keycloak - msg_ok "Restated Keycloak" - msg_ok "Update Successful" - else - msg_ok "No update required. ${APP} is already at v${RELEASE}" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080/admin${CL}" diff --git a/ct/linkstack.sh b/ct/linkstack.sh deleted file mode 100644 index e745545fc..000000000 --- a/ct/linkstack.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Omar Minaya | MickLesk (CanbiZ) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://linkstack.org/ - -APP="LinkStack" -var_tags="${var_tags:-os}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-1024}" -var_disk="${var_disk:-5}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - - if [[ ! -f ~/.linkstack ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - PHP_VERSION="8.3" PHP_MODULE="sqlite3" PHP_APACHE="YES" setup_php - msg_error "Adguard Home should be updated via the user interface." - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" diff --git a/frontend/public/json/cleanuparr.json b/frontend/public/json/cleanuparr.json deleted file mode 100644 index 7303bf362..000000000 --- a/frontend/public/json/cleanuparr.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "Cleanuparr", - "slug": "cleanuparr", - "categories": [ - 14 - ], - "date_created": "2025-07-23", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 11011, - "documentation": null, - "website": "https://github.com/Cleanuparr/Cleanuparr", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/cleanuparr.webp", - "config_path": "/opt/cleanuparr/config ", - "description": "Cleanuparr is a tool for automating the cleanup of unwanted or blocked files in Sonarr, Radarr, and supported download clients like qBittorrent, Transmission, and Deluge. It removes incomplete, blocked, or malicious downloads and can trigger replacement searches to ensure your media library stays complete and up-to-date.", - "install_methods": [ - { - "type": "default", - "script": "ct/cleanuparr.sh", - "resources": { - "cpu": 2, - "ram": 1024, - "hdd": 4, - "os": "debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] -} diff --git a/frontend/public/json/linkstack.json b/frontend/public/json/linkstack.json deleted file mode 100644 index 33c6d980c..000000000 --- a/frontend/public/json/linkstack.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "name": "LinkStack", - "slug": "linkstack", - "categories": [ - 9 - ], - "date_created": "2025-07-22", - "type": "ct", - "updateable": true, - "privileged": false, - "config_path": "/var/www/html/linkstack/.env", - "interface_port": 80, - "documentation": "https://docs.linkstack.org/", - "website": "https://linkstack.org/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/linkstack.webp", - "description": "LinkStack is an open-source, self-hosted alternative to Linktree, allowing users to create a customizable profile page to share multiple links, hosted on their own server.", - "install_methods": [ - { - "type": "default", - "script": "ct/linkstack.sh", - "resources": { - "cpu": 1, - "ram": 1024, - "hdd": 5, - "os": "Debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "LinkStack can be updated via the user interface or with the command `update`.", - "type": "info" - }, - { - "text": "Complete setup via the web interface at http:///. Check installation logs: `cat ~/linkstack-install.log`", - "type": "info" - } - ] -} diff --git a/install/alpine-teamspeak-server-install.sh b/install/alpine-teamspeak-server-install.sh deleted file mode 100644 index b99642153..000000000 --- a/install/alpine-teamspeak-server-install.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: tremor021 (Slaviša Arežina) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://teamspeak.com/en/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing dependencies" -$STD apk add --no-cache \ - ca-certificates \ - libstdc++ \ - libc6-compat -msg_ok "Installed dependencies" - -RELEASE=$(curl -fsSL https://teamspeak.com/en/downloads/#server | sed -n '/teamspeak3-server_linux_amd64-/ { s/.*teamspeak3-server_linux_amd64-\([0-9]\+\.[0-9]\+\.[0-9]\+\).*/\1/p; q }') - -msg_info "Installing Teamspeak Server v${RELEASE}" -mkdir -p /opt/teamspeak-server -cd /opt/teamspeak-server -curl -fsSL "https://files.teamspeak-services.com/releases/server/${RELEASE}/teamspeak3-server_linux_amd64-${RELEASE}.tar.bz2" -o ts3server.tar.bz2 -tar xf ts3server.tar.bz2 --strip-components=1 -mkdir -p logs data lib -mv *.so lib -touch data/ts3server.sqlitedb data/query_ip_blacklist.txt data/query_ip_whitelist.txt .ts3server_license_accepted -echo "${RELEASE}" >~/.teamspeak-server -msg_ok "Installed TeamSpeak Server v${RELEASE}" - -msg_info "Enabling TeamSpeak Server Service" -cat </etc/init.d/teamspeak -#!/sbin/openrc-run - -name="TeamSpeak Server" -description="TeamSpeak 3 Server" -command="/opt/teamspeak-server/ts3server_startscript.sh" -command_args="start" -output_log="/var/log/teamspeak.out.log" -error_log="/var/log/teamspeak.err.log" -command_background=true -pidfile="/run/teamspeak-server.pid" -directory="/opt/teamspeak-server" - -depend() { - need net - use dns -} -EOF -chmod +x /etc/init.d/teamspeak -$STD rc-update add teamspeak default -msg_ok "Enabled TeamSpeak Server Service" - -msg_info "Starting TeamSpeak Server" -$STD service teamspeak start -msg_ok "Started TeamSpeak Server" - -motd_ssh -customize - -msg_info "Cleaning up" -rm -r ts3server.tar.bz* LICENSE* CHANGELOG doc serverquerydocs tsdns redist -$STD apk cache clean -msg_ok "Cleaned" diff --git a/install/cleanuparr-install.sh b/install/cleanuparr-install.sh deleted file mode 100755 index 45e3bc1d4..000000000 --- a/install/cleanuparr-install.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: community-scripts ORG -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/Cleanuparr/Cleanuparr - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -fetch_and_deploy_gh_release "Cleanuparr" "Cleanuparr/Cleanuparr" "prebuild" "latest" "/opt/cleanuparr" "*linux-amd64.zip" -msg_ok "Installed Cleanuparr" - -msg_info "Creating Service" -cat </etc/systemd/system/cleanuparr.service -[Unit] -Description=Cleanuparr Daemon -After=syslog.target network.target - -[Service] -Type=simple -User=root -WorkingDirectory=/opt/cleanuparr -ExecStart=/opt/cleanuparr/Cleanuparr -Restart=on-failure -RestartSec=5 -Environment="PORT=11011" -Environment="CONFIG_DIR=/opt/cleanuparr/config" - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now cleanuparr -msg_ok "Created Service" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" diff --git a/install/keycloak-install.sh b/install/keycloak-install.sh deleted file mode 100644 index 8d82d16a0..000000000 --- a/install/keycloak-install.sh +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: tteck (tteckster) | Co-Authors: Slaviša Arežina (tremor021), remz1337 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/keycloak/keycloak - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -JAVA_VERSION=21 setup_java -PG_VERSION=16 setup_postgresql - -msg_info "Configuring PostgreSQL" -DB_NAME="keycloak" -DB_USER="keycloak" -DB_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" -$STD sudo -u postgres psql -c "CREATE USER $DB_USER WITH PASSWORD '$DB_PASS';" -$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8';" -$STD sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER;" -{ - echo "Keycloak Credentials" - echo "Database User: $DB_USER" - echo "Database Password: $DB_PASS" - echo "Database Name: $DB_NAME" -} >>~/keycloak.creds -msg_ok "Configured PostgreSQL" - -fetch_and_deploy_gh_release "keycloak" "keycloak/keycloak" "prebuild" "latest" "/opt/keycloak" "keycloak-*.tar.gz" - -msg_info "Creating Service" -cat </etc/systemd/system/keycloak.service -[Unit] -Description=Keycloak Service -Requires=network.target -After=syslog.target network-online.target - -[Service] -Type=idle -User=root -WorkingDirectory=/opt/keycloak -ExecStart=/opt/keycloak/bin/kc.sh start -ExecStop=/opt/keycloak/bin/kc.sh stop -Restart=always -RestartSec=3 -Environment="JAVA_HOME=/usr/lib/jvm/temurin-21-jdk-amd64" -Environment="KC_DB=postgres" -Environment="KC_DB_USERNAME=$DB_USER" -Environment="KC_DB_PASSWORD=$DB_PASS" -Environment="KC_HTTP_ENABLED=true" -Environment="KC_BOOTSTRAP_ADMIN_USERNAME=tmpadm" -Environment="KC_BOOTSTRAP_ADMIN_PASSWORD=admin123" -# Comment following line and uncomment the next 2 if working behind a reverse proxy -Environment="KC_HOSTNAME_STRICT=false" -#Environment="KC_HOSTNAME=keycloak.example.com" -#Environment="KC_PROXY_HEADERS=xforwarded" -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now keycloak -msg_ok "Created Service" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" diff --git a/install/linkstack-install.sh b/install/linkstack-install.sh deleted file mode 100644 index 8801b2c3c..000000000 --- a/install/linkstack-install.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Omar Minaya | MickLesk (CanbiZ) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://linkstack.org/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -PHP_VERSION="8.3" PHP_MODULE="sqlite3,mysql,ronny" PHP_APACHE="YES" setup_php -fetch_and_deploy_gh_release "linkstack" "linkstackorg/linkstack" "prebuild" "latest" "/var/www/html/linkstack" "linkstack.zip" - -msg_info "Configuring LinkStack" -$STD a2enmod rewrite -chown -R www-data:www-data /var/www/html/linkstack -chmod -R 755 /var/www/html/linkstack - -cat </etc/apache2/sites-available/linkstack.conf - - ServerAdmin webmaster@localhost - DocumentRoot /var/www/html/linkstack - ErrorLog /var/log/apache2/linkstack-error.log - CustomLog /var/log/apache2/linkstack-access.log combined - - Options Indexes FollowSymLinks - AllowOverride All - Require all granted - - -EOF -$STD a2dissite 000-default.conf -$STD a2ensite linkstack.conf -$STD systemctl restart apache2 -msg_ok "Configured LinkStack" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" From 3872c2062830996f69f5c7b61f526ed8de8e9e2c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 29 Jul 2025 10:34:11 +0200 Subject: [PATCH 0267/1733] Update scraparr-install.sh --- install/scraparr-install.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/install/scraparr-install.sh b/install/scraparr-install.sh index 4eafe0a60..510f2248e 100644 --- a/install/scraparr-install.sh +++ b/install/scraparr-install.sh @@ -13,10 +13,11 @@ setting_up_container network_check update_os -msg_info "Installing Scraparr" PYTHON_VERSION="3.12" setup_uv fetch_and_deploy_gh_release "scrappar" "thecfu/scraparr" "tarball" "latest" "/opt/scraparr" -cd /opt/scraparr || exit + +msg_info "Installing Scraparr" +cd /opt/scraparr $STD uv venv /opt/scraparr/.venv $STD /opt/scraparr/.venv/bin/python -m ensurepip --upgrade $STD /opt/scraparr/.venv/bin/python -m pip install --upgrade pip @@ -43,7 +44,6 @@ Restart=always [Install] WantedBy=multi-user.target EOF -systemctl daemon-reload systemctl enable -q --now scraparr msg_ok "Configured Service" From 41bfaf3a1c6fce0f7b14dcf731364ae87e6ca55f Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 29 Jul 2025 10:34:29 +0200 Subject: [PATCH 0268/1733] Update scraparr.json --- frontend/public/json/scraparr.json | 76 +++++++++++++++--------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/frontend/public/json/scraparr.json b/frontend/public/json/scraparr.json index 731618e39..9badfbcce 100644 --- a/frontend/public/json/scraparr.json +++ b/frontend/public/json/scraparr.json @@ -1,40 +1,40 @@ { - "name": "Scraparr", - "slug": "scraparr", - "categories": [ - 14 - ], - "date_created": "2024-05-02", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 7100, - "documentation": "https://github.com/thecfu/scraparr/blob/main/README.md", - "website": "https://github.com/thecfu/scraparr", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/scraparr-dark.svg", - "config_path": "/scraparr/config/config.yaml", - "description": "Scraparr is a Prometheus exporter for the *arr suite (Sonarr, Radarr, Lidarr, etc.). It provides metrics that can be scraped by Prometheus to monitor and visualize the health and performance of your *arr applications.", - "install_methods": [ - { - "type": "default", - "script": "ct/scraparr.sh", - "resources": { - "cpu": 2, - "ram": 1024, - "hdd": 4, - "os": "debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "Edit config file then restart the scraparr service: `systemctl restart scraparr`", - "type": "info" - } - ] + "name": "Scraparr", + "slug": "scraparr", + "categories": [ + 14 + ], + "date_created": "2025-07-29", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 7100, + "documentation": "https://github.com/thecfu/scraparr/blob/main/README.md", + "website": "https://github.com/thecfu/scraparr", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/scraparr-dark.svg", + "config_path": "/scraparr/config/config.yaml", + "description": "Scraparr is a Prometheus exporter for the *arr suite (Sonarr, Radarr, Lidarr, etc.). It provides metrics that can be scraped by Prometheus to monitor and visualize the health and performance of your *arr applications.", + "install_methods": [ + { + "type": "default", + "script": "ct/scraparr.sh", + "resources": { + "cpu": 2, + "ram": 1024, + "hdd": 4, + "os": "debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Edit config file then restart the scraparr service: `systemctl restart scraparr`", + "type": "info" + } + ] } From a05e6caf7b46781d60a08141c367d2dee07b1ba1 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 29 Jul 2025 10:37:19 +0200 Subject: [PATCH 0269/1733] Update jeedom.sh --- ct/jeedom.sh | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/ct/jeedom.sh b/ct/jeedom.sh index b64d2685c..36f98a14d 100644 --- a/ct/jeedom.sh +++ b/ct/jeedom.sh @@ -11,7 +11,7 @@ var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-16}" var_os="${var_os:-debian}" -var_version="${var_version:-11}" +var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" @@ -20,20 +20,20 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources + header_info + check_container_storage + check_container_resources - if [[ ! -f /var/www/html/core/config/version ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - msg_info "Updating OS" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "OS updated, you can now update Jeedom from the Web UI." + if [[ ! -f /var/www/html/core/config/version ]]; then + msg_error "No ${APP} Installation Found!" exit + fi + + msg_info "Updating OS" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "OS updated, you can now update Jeedom from the Web UI." + exit } start From d49b1f90b45ad5ce99b45d2e089c0bf221d0e4d7 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 29 Jul 2025 08:37:37 +0000 Subject: [PATCH 0270/1733] Update .app files --- ct/headers/cleanuparr | 6 ------ ct/headers/keycloak | 6 ------ ct/headers/linkstack | 6 ------ 3 files changed, 18 deletions(-) delete mode 100644 ct/headers/cleanuparr delete mode 100644 ct/headers/keycloak delete mode 100644 ct/headers/linkstack diff --git a/ct/headers/cleanuparr b/ct/headers/cleanuparr deleted file mode 100644 index 28427b25d..000000000 --- a/ct/headers/cleanuparr +++ /dev/null @@ -1,6 +0,0 @@ - ________ - / ____/ /__ ____ _____ __ ______ ____ ___________ - / / / / _ \/ __ `/ __ \/ / / / __ \/ __ `/ ___/ ___/ -/ /___/ / __/ /_/ / / / / /_/ / /_/ / /_/ / / / / -\____/_/\___/\__,_/_/ /_/\__,_/ .___/\__,_/_/ /_/ - /_/ diff --git a/ct/headers/keycloak b/ct/headers/keycloak deleted file mode 100644 index 0c20d7510..000000000 --- a/ct/headers/keycloak +++ /dev/null @@ -1,6 +0,0 @@ - __ __ __ __ - / //_/__ __ _______/ /___ ____ _/ /__ - / ,< / _ \/ / / / ___/ / __ \/ __ `/ //_/ - / /| / __/ /_/ / /__/ / /_/ / /_/ / ,< -/_/ |_\___/\__, /\___/_/\____/\__,_/_/|_| - /____/ diff --git a/ct/headers/linkstack b/ct/headers/linkstack deleted file mode 100644 index c3413d299..000000000 --- a/ct/headers/linkstack +++ /dev/null @@ -1,6 +0,0 @@ - __ _ __ _____ __ __ - / / (_)___ / /__/ ___// /_____ ______/ /__ - / / / / __ \/ //_/\__ \/ __/ __ `/ ___/ //_/ - / /___/ / / / / ,< ___/ / /_/ /_/ / /__/ ,< -/_____/_/_/ /_/_/|_|/____/\__/\__,_/\___/_/|_| - From 3696b65c7da2708ff56102f90683b3d302f0a17d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 29 Jul 2025 10:49:50 +0200 Subject: [PATCH 0271/1733] fixes --- frontend/public/json/jeedom.json | 89 +++++++++++++++----------------- install/jeedom-install.sh | 22 ++++---- 2 files changed, 54 insertions(+), 57 deletions(-) diff --git a/frontend/public/json/jeedom.json b/frontend/public/json/jeedom.json index 35fe87b09..60641af82 100644 --- a/frontend/public/json/jeedom.json +++ b/frontend/public/json/jeedom.json @@ -1,51 +1,44 @@ { - "name": "Jeedom", - "slug": "jeedom", - "categories": [ - 16 - ], - "date_created": "2025-03-06", - "type": "ct", - "updateable": false, - "privileged": false, - "interface_port": 80, - "documentation": "https://doc.jeedom.com", - "config_path": "", - "website": "https://jeedom.com/", - "logo": "https://jeedom.com/_next/image?url=%2Fassets%2Fimg%2Flogo.png&w=256&q=75", - "description": "From individual homes to IoT infrastructures\n\nJeedom: the tailor-made open source solution", - "install_methods": [ - { - "type": "default", - "script": "ct/jeedom.sh", - "resources": { - "cpu": 2, - "ram": 2048, - "hdd": 16, - "os": "Debian", - "version": "11" - } - }, - { - "type": "default", - "script": "ct/jeedom.sh", - "resources": { - "cpu": 2, - "ram": 2048, - "hdd": 16, - "os": "Debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": "admin", - "password": "admin" + "name": "Jeedom", + "slug": "jeedom", + "categories": [ + 16 + ], + "date_created": "2025-03-06", + "type": "ct", + "updateable": false, + "privileged": false, + "interface_port": 80, + "documentation": "https://doc.jeedom.com", + "config_path": "", + "website": "https://jeedom.com/", + "logo": "https://jeedom.com/_next/image?url=%2Fassets%2Fimg%2Flogo.png&w=256&q=75", + "description": "Jeedom is a home automation system that is free, open, and cloudless. It allows users to manage and automate various aspects of their homes by creating objects, installing plugins for added functionalities, and connecting to a Market account for services. It also supports direct access URLs and user management.", + "install_methods": [ + { + "type": "default", + "script": "ct/jeedom.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 16, + "os": "Debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": "admin", + "password": "admin" + }, + "notes": [ + { + "text": "WARNING: Installation sources scripts outside of Community Scripts repo. Please check the source before installing.", + "type": "warning" }, - "notes": [ - { - "text": "Only OS packages are updateable. To update Jeedom, please use the web interface.", - "type": "info" - } - ] + { + "text": "Only OS packages are updateable. To update Jeedom, please use the web interface.", + "type": "info" + } + ] } diff --git a/install/jeedom-install.sh b/install/jeedom-install.sh index c9a736abf..d868e84b1 100644 --- a/install/jeedom-install.sh +++ b/install/jeedom-install.sh @@ -15,24 +15,27 @@ update_os msg_info "Installing dependencies" $STD apt-get install -y \ - lsb-release \ - git + lsb-release \ + git msg_ok "Dependencies installed" DEFAULT_BRANCH="master" +REPO_URL="https://github.com/jeedom/core.git" + echo while true; do - read -r -p "Enter branch to use (master, beta, alpha...) (Default: ${DEFAULT_BRANCH}): " BRANCH - BRANCH=${BRANCH:-$DEFAULT_BRANCH} + read -rp "${TAB3}Enter branch to use (master, beta, alpha...) (Default: ${DEFAULT_BRANCH}): " BRANCH + BRANCH="${BRANCH:-$DEFAULT_BRANCH}" - if git ls-remote --heads https://github.com/jeedom/core.git "$BRANCH" | grep -q "$BRANCH"; then - break - else - echo "Branch '$BRANCH' does not exist. Please enter a valid branch." - fi + if git ls-remote --heads "$REPO_URL" "refs/heads/$BRANCH" | grep -q .; then + break + else + msg_error "Branch '$BRANCH' does not exist on remote. Please try again." + fi done msg_info "Downloading Jeedom installation script" +cd /tmp wget -q https://raw.githubusercontent.com/jeedom/core/"${BRANCH}"/install/install.sh chmod +x install.sh msg_ok "Installation script downloaded" @@ -85,6 +88,7 @@ motd_ssh customize msg_info "Cleaning up" +rm -rf /tmp/install.sh $STD apt-get -y autoremove $STD apt-get -y autoclean msg_ok "Cleaned" From e94f2bf68e0a92158b5e62638bacd4e19138d5b4 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 29 Jul 2025 11:09:10 +0200 Subject: [PATCH 0272/1733] Create update-apps.json --- frontend/public/json/update-apps.json | 40 +++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 frontend/public/json/update-apps.json diff --git a/frontend/public/json/update-apps.json b/frontend/public/json/update-apps.json new file mode 100644 index 000000000..553863671 --- /dev/null +++ b/frontend/public/json/update-apps.json @@ -0,0 +1,40 @@ +{ + "name": "Proxmox VE LXC-Updater", + "slug": "update-apps", + "categories": [ + 1 + ], + "date_created": "2025-07-29", + "type": "pve", + "updateable": false, + "privileged": false, + "interface_port": null, + "documentation": null, + "website": null, + "logo": "https://raw.githubusercontent.com/selfhst/icons/refs/heads/main/svg/proxmox.svg", + "config_path": "", + "description": "This script automatically updates LXC containers using a Systemd service. The service also updates the tags if a LXC IP address is changed.", + "install_methods": [ + { + "type": "default", + "script": "tools/pve/update-apps.sh", + "resources": { + "cpu": null, + "ram": null, + "hdd": null, + "os": null, + "version": null + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Execute only within the Proxmox shell", + "type": "info" + } + ] +} From 3c8184221a948be36a12c4c3e7811b95e09ad0ce Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 29 Jul 2025 11:13:11 +0200 Subject: [PATCH 0273/1733] Update update-apps.sh --- tools/pve/update-apps.sh | 45 ++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/tools/pve/update-apps.sh b/tools/pve/update-apps.sh index ef646ddf4..c3714fdd5 100644 --- a/tools/pve/update-apps.sh +++ b/tools/pve/update-apps.sh @@ -18,16 +18,16 @@ function header_info { EOF } -function detect_service(){ +function detect_service() { pushd $(mktemp -d) >/dev/null pct pull "$1" /usr/bin/update update 2>/dev/null service=$(cat update | sed 's|.*/ct/||g' | sed 's|\.sh).*||g') popd >/dev/null } -function backup_container(){ +function backup_container() { msg_info "Creating backup for container $1" - vzdump $1 --compress zstd --storage $STORAGE_CHOICE -notes-template "community-scripts backup updater" > /dev/null 2>&1 + vzdump $1 --compress zstd --storage $STORAGE_CHOICE -notes-template "community-scripts backup updater" >/dev/null 2>&1 status=$? if [ $status -eq 0 ]; then @@ -38,8 +38,8 @@ function backup_container(){ fi } -function get_backup_storages(){ -STORAGES=$(awk ' +function get_backup_storages() { + STORAGES=$(awk ' /^[a-z]+:/ { if (name != "") { if (has_backup || (!has_content && type == "dir")) print name @@ -71,8 +71,8 @@ NODE=$(hostname) containers=$(pct list | tail -n +2 | awk '{print $0 " " $4}') if [ -z "$containers" ]; then - whiptail --title "LXC Container Update" --msgbox "No LXC containers available!" 10 60 - exit 1 + whiptail --title "LXC Container Update" --msgbox "No LXC containers available!" 10 60 + exit 1 fi menu_items=() @@ -87,26 +87,26 @@ while read -r container; do if pct config "$container_id" | grep -qE "^tags:.*(${TAGS}).*"; then menu_items+=("$container_id" "$formatted_line" "OFF") fi -done <<< "$containers" +done <<<"$containers" CHOICE=$(whiptail --title "LXC Container Update" \ - --checklist "Select LXC containers to update:" 25 60 13 \ - "${menu_items[@]}" 3>&2 2>&1 1>&3 | tr -d '"') + --checklist "Select LXC containers to update:" 25 60 13 \ + "${menu_items[@]}" 3>&2 2>&1 1>&3 | tr -d '"') if [ -z "$CHOICE" ]; then - whiptail --title "LXC Container Update" \ - --msgbox "No containers selected!" 10 60 - exit 1 + whiptail --title "LXC Container Update" \ + --msgbox "No containers selected!" 10 60 + exit 1 fi header_info BACKUP_CHOICE="no" -if(whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC Container Update" --yesno "Do you want to backup your containers before update?" 10 58); then +if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC Container Update" --yesno "Do you want to backup your containers before update?" 10 58); then BACKUP_CHOICE="yes" fi UNATTENDED_UPDATE="no" -if(whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC Container Update" --yesno "Run updates unattended?" 10 58); then +if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC Container Update" --yesno "Run updates unattended?" 10 58); then UNATTENDED_UPDATE="yes" fi @@ -121,19 +121,19 @@ if [ "$BACKUP_CHOICE" == "yes" ]; then MENU_ITEMS=() for STORAGE in $STORAGES; do - MENU_ITEMS+=("$STORAGE" "") + MENU_ITEMS+=("$STORAGE" "") done STORAGE_CHOICE=$(whiptail --title "Select storage device" --menu "Select a storage device (Only storage devices with 'backup' support are listed):" 15 50 5 "${MENU_ITEMS[@]}" 3>&1 1>&2 2>&3) if [ -z "$STORAGE_CHOICE" ]; then - msg_error "No storage selected!" - exit 1 + msg_error "No storage selected!" + exit 1 fi fi UPDATE_CMD="update;" -if [ "$UNATTENDED_UPDATE" == "yes" ];then +if [ "$UNATTENDED_UPDATE" == "yes" ]; then UPDATE_CMD="export PHS_SILENT=1;update;" fi @@ -141,7 +141,7 @@ containers_needing_reboot=() for container in $CHOICE; do echo -e "${BL}[INFO]${CL} Updating container $container" - if [ "$BACKUP_CHOICE" == "yes" ];then + if [ "$BACKUP_CHOICE" == "yes" ]; then backup_container $container fi @@ -213,7 +213,6 @@ for container in $CHOICE; do if [ "$UPDATE_BUILD_RESOURCES" -eq "1" ]; then pct set "$container" --cores "$build_cpu" --memory "$build_ram" fi -} #4) Update service, using the update command case "$os" in @@ -243,11 +242,11 @@ for container in $CHOICE; do if [ $exit_code -eq 0 ]; then msg_ok "Updated container $container" - elif [ "BACKUP_CHOICE" == "yes" ];then + elif [ "$BACKUP_CHOICE" == "yes" ]; then msg_info "Restoring LXC from backup" pct stop $container LXC_STORAGE=$(pct config $container | awk -F '[:,]' '/rootfs/ {print $2}') - pct restore $container /var/lib/vz/dump/vzdump-lxc-${container}-*.tar.zst --storage $LXC_STORAGE --force > /dev/null 2>&1 + pct restore $container /var/lib/vz/dump/vzdump-lxc-${container}-*.tar.zst --storage $LXC_STORAGE --force >/dev/null 2>&1 pct start $container restorestatus=$? if [ $restorestatus -eq 0 ]; then From 88ba7983991078196ef3500b61737331b4afa67e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 29 Jul 2025 11:14:12 +0200 Subject: [PATCH 0274/1733] Update update-apps.sh --- tools/pve/update-apps.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/pve/update-apps.sh b/tools/pve/update-apps.sh index c3714fdd5..787fb7aac 100644 --- a/tools/pve/update-apps.sh +++ b/tools/pve/update-apps.sh @@ -64,7 +64,8 @@ END { } header_info -echo "Loading..." +echo "Loading all possible LXC containers from Proxmox VE" +echo "This may take a few seconds..." whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC Container Update" --yesno "This will update LXC container. Proceed?" 10 58 || exit NODE=$(hostname) From 35a1225c3ed3eff148e9c99bc953e21f45234f04 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 29 Jul 2025 11:25:33 +0200 Subject: [PATCH 0275/1733] Update update-apps.sh --- tools/pve/update-apps.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/pve/update-apps.sh b/tools/pve/update-apps.sh index 787fb7aac..f07e5c03d 100644 --- a/tools/pve/update-apps.sh +++ b/tools/pve/update-apps.sh @@ -64,8 +64,7 @@ END { } header_info -echo "Loading all possible LXC containers from Proxmox VE" -echo "This may take a few seconds..." +msg_info "Loading all possible LXC containers from Proxmox VE. This may take a few seconds..." whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC Container Update" --yesno "This will update LXC container. Proceed?" 10 58 || exit NODE=$(hostname) @@ -89,6 +88,7 @@ while read -r container; do menu_items+=("$container_id" "$formatted_line" "OFF") fi done <<<"$containers" +msg_ok "Loaded ${#menu_items[@]} containers" CHOICE=$(whiptail --title "LXC Container Update" \ --checklist "Select LXC containers to update:" 25 60 13 \ From 2c883bd9284dba9342a8cdd0068de438b696bc7a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 29 Jul 2025 13:08:05 +0200 Subject: [PATCH 0276/1733] kanba --- ct/kanba.sh | 44 ++++++++++++++++++++++ install/kanba-install.sh | 80 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 ct/kanba.sh create mode 100644 install/kanba-install.sh diff --git a/ct/kanba.sh b/ct/kanba.sh new file mode 100644 index 000000000..da8a564b7 --- /dev/null +++ b/ct/kanba.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://github.com/Kanba-co/kanba + +APP="Kanba" +var_tags="${var_tags:-kanban}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-5}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /var ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating $APP LXC" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Updated $APP LXC" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" diff --git a/install/kanba-install.sh b/install/kanba-install.sh new file mode 100644 index 000000000..c23544e41 --- /dev/null +++ b/install/kanba-install.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://github.com/Kanba-co/kanba + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +NODE_VERSION="22" NODE_MODULE="npx" setup_nodejs +fetch_and_deploy_gh_release "kanba" "Kanba-co/kanba" "tarball" "latest" "/opt/kanba" +POSTGRES_VERSION="16" setup_postgresql + +msg_info "Set up PostgreSQL Database" +DB_NAME=kanba_db +DB_USER=kanba_usr +DB_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" +DB_URL="postgresql://${DB_USER}:${DB_PASS}@localhost:5432/${DB_NAME}" +$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" +$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" +{ + echo "Kanba-Credentials" + echo "Kanba Database Name: $DB_NAME" + echo "Kanba Database User: $DB_USER" + echo "Kanba Database Password: $DB_PASS" +} >>~/kanba.creds +msg_ok "Set up PostgreSQL Database" + +msg_info "Preparing .env.local" +cd /opt/kanba +cp .env.example .env.local +sed -i "s|^DATABASE_PROVIDER=.*|DATABASE_PROVIDER=postgresql|" .env.local +sed -i "s|^DATABASE_URL=.*|DATABASE_URL=${DB_URL}|" .env.local +sed -i "s|^NEXT_PUBLIC_SITE_URL=.*|NEXT_PUBLIC_SITE_URL=http://localhost:3000|" .env.local +sed -i "s|^NEXTAUTH_URL=.*|NEXTAUTH_URL=http://localhost:3000|" .env.local +sed -i "s|^NEXTAUTH_SECRET=.*|NEXTAUTH_SECRET=$(openssl rand -hex 32)|" .env.local +msg_ok "Prepared .env.local" + +msg_info "Installing Kanba" +$STD npm install +$STD npx prisma generate +$STD npx prisma migrate deploy +$STD npm run build +msg_ok "Installed Kanba" + +msg_info "Creating systemd Service" +cat </etc/systemd/system/kanba.service +[Unit] +Description=Kanba - Lightweight Trello Alternative +After=network.target postgresql.service + +[Service] +Type=simple +WorkingDirectory=/opt/kanba +EnvironmentFile=/opt/kanba/.env.local +ExecStart=/usr/bin/npx next start -p 3000 +Restart=always + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now kanba +msg_ok "Created systemd Service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From c07e11c59712d244560148e30d5aa0a91b0fb036 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 29 Jul 2025 11:08:22 +0000 Subject: [PATCH 0277/1733] Update .app files --- ct/headers/kanba | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/kanba diff --git a/ct/headers/kanba b/ct/headers/kanba new file mode 100644 index 000000000..40c351f8b --- /dev/null +++ b/ct/headers/kanba @@ -0,0 +1,6 @@ + __ __ __ + / //_/___ _____ / /_ ____ _ + / ,< / __ `/ __ \/ __ \/ __ `/ + / /| / /_/ / / / / /_/ / /_/ / +/_/ |_\__,_/_/ /_/_.___/\__,_/ + From 5156ad9126719c0d90d6ae481446be8242ba2615 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 29 Jul 2025 13:12:25 +0200 Subject: [PATCH 0278/1733] Update kanba-install.sh --- install/kanba-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/kanba-install.sh b/install/kanba-install.sh index c23544e41..896a01e58 100644 --- a/install/kanba-install.sh +++ b/install/kanba-install.sh @@ -13,7 +13,7 @@ setting_up_container network_check update_os -NODE_VERSION="22" NODE_MODULE="npx" setup_nodejs +NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "kanba" "Kanba-co/kanba" "tarball" "latest" "/opt/kanba" POSTGRES_VERSION="16" setup_postgresql From 7181f449dd567ffd04c4ef308f6cd1f0d2cb3320 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 29 Jul 2025 13:51:33 +0200 Subject: [PATCH 0279/1733] fixes --- ct/kanba.sh | 2 +- install/kanba-install.sh | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/ct/kanba.sh b/ct/kanba.sh index da8a564b7..7beb967ad 100644 --- a/ct/kanba.sh +++ b/ct/kanba.sh @@ -7,7 +7,7 @@ source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/ APP="Kanba" var_tags="${var_tags:-kanban}" -var_cpu="${var_cpu:-1}" +var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-5}" var_os="${var_os:-debian}" diff --git a/install/kanba-install.sh b/install/kanba-install.sh index 896a01e58..a11e59b51 100644 --- a/install/kanba-install.sh +++ b/install/kanba-install.sh @@ -15,6 +15,7 @@ update_os NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "kanba" "Kanba-co/kanba" "tarball" "latest" "/opt/kanba" +fetch_and_deploy_gh_release "supabase" "supabase/cli" "binary" "latest" "/opt/supabase" "supabase-linux-x64" POSTGRES_VERSION="16" setup_postgresql msg_info "Set up PostgreSQL Database" @@ -40,12 +41,16 @@ cd /opt/kanba cp .env.example .env.local sed -i "s|^DATABASE_PROVIDER=.*|DATABASE_PROVIDER=postgresql|" .env.local sed -i "s|^DATABASE_URL=.*|DATABASE_URL=${DB_URL}|" .env.local +sed -i "s|^DIRECT_URL=.*|DIRECT_URL=${DB_URL}|" .env.local sed -i "s|^NEXT_PUBLIC_SITE_URL=.*|NEXT_PUBLIC_SITE_URL=http://localhost:3000|" .env.local sed -i "s|^NEXTAUTH_URL=.*|NEXTAUTH_URL=http://localhost:3000|" .env.local sed -i "s|^NEXTAUTH_SECRET=.*|NEXTAUTH_SECRET=$(openssl rand -hex 32)|" .env.local +sed -i "s|^NEXT_PUBLIC_SUPABASE_URL=.*|NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321|" .env.local +sed -i "s|^NEXT_PUBLIC_SUPABASE_ANON_KEY=.*|NEXT_PUBLIC_SUPABASE_ANON_KEY=dummy-key|" .env.local msg_ok "Prepared .env.local" msg_info "Installing Kanba" +export $(grep -v '^#' .env.local | xargs) $STD npm install $STD npx prisma generate $STD npx prisma migrate deploy From c5907dc20dca73f9c2ab517e7118991ed1590d53 Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Thu, 31 Jul 2025 20:25:27 +0200 Subject: [PATCH 0280/1733] Update ots-install.sh --- install/ots-install.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/install/ots-install.sh b/install/ots-install.sh index be172063b..599932019 100644 --- a/install/ots-install.sh +++ b/install/ots-install.sh @@ -40,6 +40,12 @@ msg_ok "Certificate Generated" msg_info "Setting up nginx" cat </etc/nginx/sites-available/ots.conf +server { + listen 80; + listen [::]:80; + server_name ots; + return 301 https://$host$request_uri; +} server { listen 443 ssl; listen [::]:443 ssl; From c99bb7ca6c57a29963b4a547deb66a978519a04d Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Thu, 31 Jul 2025 20:40:05 +0200 Subject: [PATCH 0281/1733] Update ots-install.sh --- install/ots-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/ots-install.sh b/install/ots-install.sh index 599932019..6bbfe81f5 100644 --- a/install/ots-install.sh +++ b/install/ots-install.sh @@ -44,7 +44,7 @@ server { listen 80; listen [::]:80; server_name ots; - return 301 https://$host$request_uri; + return 301 https://\$host\$request_uri; } server { listen 443 ssl; From d3ebd8b64d433ffe2eeaaf1696ee813470c85b61 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 1 Aug 2025 08:51:12 +0200 Subject: [PATCH 0282/1733] tandoor refactor --- ct/tandoor.sh | 61 ++++++++++++++++ install/tandoor-install.sh | 142 +++++++++++++++++++++++++++++++++++++ 2 files changed, 203 insertions(+) create mode 100644 ct/tandoor.sh create mode 100644 install/tandoor-install.sh diff --git a/ct/tandoor.sh b/ct/tandoor.sh new file mode 100644 index 000000000..acac0d6a5 --- /dev/null +++ b/ct/tandoor.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 tteck +# Author: MickLesk (Canbiz) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://tandoor.dev/ + +APP="Tandoor" +var_tags="${var_tags:-recipes}" +var_cpu="${var_cpu:-4}" +var_ram="${var_ram:-4096}" +var_disk="${var_disk:-10}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/tandoor ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + #if ! [[ $(dpkg -s python3-xmlsec 2>/dev/null) ]]; then + #$STD apt-get update + #$STD apt-get install -y python3-xmlsec + #fi + #if cd /opt/tandoor && git pull | grep -q 'Already up to date'; then + msg_ok "There is currently no update available." + #else + #msg_info "Updating ${APP} (Patience)" + #export $(cat /opt/tandoor/.env | grep "^[^#]" | xargs) + #cd /opt/tandoor/ + #$STD pip3 install -r requirements.txt + #$STD /usr/bin/python3 /opt/tandoor/manage.py migrate + #$STD /usr/bin/python3 /opt/tandoor/manage.py collectstatic --no-input + #$STD /usr/bin/python3 /opt/tandoor/manage.py collectstatic_js_reverse + #cd /opt/tandoor/vue + #$STD yarn install + #$STD yarn build + #cd /opt/tandoor + #$STD python3 version.py + #systemctl restart gunicorn_tandoor + #msg_ok "Updated ${APP}" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8002${CL}" diff --git a/install/tandoor-install.sh b/install/tandoor-install.sh new file mode 100644 index 000000000..f1cd7f1a4 --- /dev/null +++ b/install/tandoor-install.sh @@ -0,0 +1,142 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 tteck +# Author: tteck +# Co-Author: MickLesk (Canbiz) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://tandoor.dev/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies (Patience)" +$STD apt-get install -y --no-install-recommends \ + build-essential \ + libpq-dev \ + libmagic-dev \ + libzbar0 \ + nginx \ + libsasl2-dev \ + libldap2-dev \ + libssl-dev \ + git \ + make \ + pkg-config \ + libxmlsec1-dev \ + libxml2-dev \ + libxmlsec1-openssl +msg_ok "Installed Dependencies" + +msg_info "Setup Python3" +$STD apt-get install -y \ + python3 \ + python3-dev \ + python3-setuptools \ + python3-pip \ + python3-xmlsec +rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED +msg_ok "Setup Python3" + +NODE_VERSION="20" NODE_MODULE="yarn@latest" setup_nodejs +fetch_and_deploy_gh_release "tandoor" "TandoorRecipes/recipes" "tarball" "latest" "/opt/tandoor" +PG_VERSION="16" setup_postgresql + +msg_info "Set up PostgreSQL Database" +DB_NAME=db_recipes +DB_USER=tandoor +SECRET_KEY=$(openssl rand -base64 45 | sed 's/\//\\\//g') +DB_PASS="$(openssl rand -base64 18 | cut -c1-13)" +$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" +$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER TEMPLATE template0;" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC'" +{ + echo "Tandoor-Credentials" + echo "Tandoor Database Name: $DB_NAME" + echo "Tandoor Database User: $DB_USER" + echo "Tandoor Database Password: $DB_PASS" +} >>~/tandoor.creds +msg_ok "Set up PostgreSQL Database" + +msg_info "Setup Tandoor" +mkdir -p /opt/tandoor/{config,api,mediafiles,staticfiles} +cd /opt/tandoor +uv venv .venv --python=python3 +.venv/bin/uv pip install -r requirements.txt +cd /opt/tandoor/vue3 +$STD yarn install +$STD yarn build +cat </opt/tandoor/.env +SECRET_KEY=$SECRET_KEY +TZ=Europe/Berlin + +DB_ENGINE=django.db.backends.postgresql +POSTGRES_HOST=localhost +POSTGRES_DB=$DB_NAME +POSTGRES_PORT=5432 +POSTGRES_USER=$DB_USER +POSTGRES_PASSWORD=$DB_PASS +EOF +.venv/bin/python version.py +export $(cat /opt/tandoor/.env | grep "^[^#]" | xargs) +.venv/bin/python manage.py migrate +.venv/bin/python manage.py collectstatic --no-input +.venv/bin/python manage.py collectstatic_js_reverse +msg_ok "Installed Tandoor" + +msg_info "Creating Services" +cat </etc/systemd/system/gunicorn_tandoor.service +[Unit] +Description=gunicorn daemon for tandoor +After=network.target + +[Service] +Type=simple +Restart=always +RestartSec=3 +WorkingDirectory=/opt/tandoor +EnvironmentFile=/opt/tandoor/.env +ExecStart=/usr/local/bin/gunicorn --error-logfile /tmp/gunicorn_err.log --log-level debug --capture-output --bind unix:/opt/tandoor/tandoor.sock recipes.wsgi:application + +[Install] +WantedBy=multi-user.target +EOF + +cat <<'EOF' >/etc/nginx/conf.d/tandoor.conf +server { + listen 8002; + #access_log /var/log/nginx/access.log; + #error_log /var/log/nginx/error.log; + client_max_body_size 128M; + # serve media files + location /static/ { + alias /opt/tandoor/staticfiles/; + } + + location /media/ { + alias /opt/tandoor/mediafiles/; + } + + location / { + proxy_set_header Host $http_host; + proxy_pass http://unix:/opt/tandoor/tandoor.sock; + } +} +EOF +systemctl reload nginx +systemctl enable -q --now gunicorn_tandoor +msg_ok "Created Services" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From bfb5583324ae3054f8ccc229154908e766961b01 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 1 Aug 2025 06:51:41 +0000 Subject: [PATCH 0283/1733] Update .app files --- ct/headers/tandoor | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/tandoor diff --git a/ct/headers/tandoor b/ct/headers/tandoor new file mode 100644 index 000000000..7f2db76a1 --- /dev/null +++ b/ct/headers/tandoor @@ -0,0 +1,6 @@ + ______ __ + /_ __/___ _____ ____/ /___ ____ _____ + / / / __ `/ __ \/ __ / __ \/ __ \/ ___/ + / / / /_/ / / / / /_/ / /_/ / /_/ / / +/_/ \__,_/_/ /_/\__,_/\____/\____/_/ + From 7ae8b7ec24c3d935b240baa4fc11476c7fb4e33f Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 1 Aug 2025 08:51:55 +0200 Subject: [PATCH 0284/1733] Update tandoor-install.sh --- install/tandoor-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/tandoor-install.sh b/install/tandoor-install.sh index f1cd7f1a4..c9aa75b89 100644 --- a/install/tandoor-install.sh +++ b/install/tandoor-install.sh @@ -102,7 +102,7 @@ Restart=always RestartSec=3 WorkingDirectory=/opt/tandoor EnvironmentFile=/opt/tandoor/.env -ExecStart=/usr/local/bin/gunicorn --error-logfile /tmp/gunicorn_err.log --log-level debug --capture-output --bind unix:/opt/tandoor/tandoor.sock recipes.wsgi:application +ExecStart=/opt/tandoor/.venv/bin/gunicorn --error-logfile /tmp/gunicorn_err.log --log-level debug --capture-output --bind unix:/opt/tandoor/tandoor.sock recipes.wsgi:application [Install] WantedBy=multi-user.target From 342bc833e19759592909d43551686672785195cd Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 1 Aug 2025 09:01:05 +0200 Subject: [PATCH 0285/1733] Update tandoor-install.sh --- install/tandoor-install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install/tandoor-install.sh b/install/tandoor-install.sh index c9aa75b89..2a21d4adc 100644 --- a/install/tandoor-install.sh +++ b/install/tandoor-install.sh @@ -45,6 +45,7 @@ msg_ok "Setup Python3" NODE_VERSION="20" NODE_MODULE="yarn@latest" setup_nodejs fetch_and_deploy_gh_release "tandoor" "TandoorRecipes/recipes" "tarball" "latest" "/opt/tandoor" PG_VERSION="16" setup_postgresql +PYTHON_VERSION="3.13" setup_uv msg_info "Set up PostgreSQL Database" DB_NAME=db_recipes From 42b04adabfcd6dc321dc35707253640e1a9dc160 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 1 Aug 2025 09:13:22 +0200 Subject: [PATCH 0286/1733] Update tandoor-install.sh --- install/tandoor-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/tandoor-install.sh b/install/tandoor-install.sh index 2a21d4adc..813754f0f 100644 --- a/install/tandoor-install.sh +++ b/install/tandoor-install.sh @@ -69,7 +69,7 @@ msg_info "Setup Tandoor" mkdir -p /opt/tandoor/{config,api,mediafiles,staticfiles} cd /opt/tandoor uv venv .venv --python=python3 -.venv/bin/uv pip install -r requirements.txt +uv pip install -r requirements.txt --venv .venv cd /opt/tandoor/vue3 $STD yarn install $STD yarn build From 3f01c35e012ff9ef3db32bfe9cc50ccc71f16d43 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 1 Aug 2025 09:21:44 +0200 Subject: [PATCH 0287/1733] Update tandoor-install.sh --- install/tandoor-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/tandoor-install.sh b/install/tandoor-install.sh index 813754f0f..130c72620 100644 --- a/install/tandoor-install.sh +++ b/install/tandoor-install.sh @@ -69,7 +69,7 @@ msg_info "Setup Tandoor" mkdir -p /opt/tandoor/{config,api,mediafiles,staticfiles} cd /opt/tandoor uv venv .venv --python=python3 -uv pip install -r requirements.txt --venv .venv +uv pip install -r requirements.txt --python .venv/bin/python cd /opt/tandoor/vue3 $STD yarn install $STD yarn build From 3393b9b174af4b3a9fc7e55442cce8a34f20ca0c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 1 Aug 2025 09:40:13 +0200 Subject: [PATCH 0288/1733] Update tandoor-install.sh --- install/tandoor-install.sh | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/install/tandoor-install.sh b/install/tandoor-install.sh index 130c72620..b81f70fa8 100644 --- a/install/tandoor-install.sh +++ b/install/tandoor-install.sh @@ -84,11 +84,16 @@ POSTGRES_PORT=5432 POSTGRES_USER=$DB_USER POSTGRES_PASSWORD=$DB_PASS EOF -.venv/bin/python version.py +TANDOOR_VERSION="$(curl -s https://api.github.com/repos/TandoorRecipes/recipes/releases/latest | jq -r .tag_name)" +cat </opt/tandoor/cookbook/version_info.py +TANDOOR_VERSION = "$TANDOOR_VERSION" +TANDOOR_REF = "bare-metal" +VERSION_INFO = [] +EOF export $(cat /opt/tandoor/.env | grep "^[^#]" | xargs) -.venv/bin/python manage.py migrate -.venv/bin/python manage.py collectstatic --no-input -.venv/bin/python manage.py collectstatic_js_reverse +/opt/tandoor/.venv/bin/python manage.py migrate +/opt/tandoor/.venv/bin/python manage.py collectstatic --no-input +/opt/tandoor/.venv/bin/python manage.py collectstatic_js_reverse msg_ok "Installed Tandoor" msg_info "Creating Services" From d02231866de0012f313f4e9f77c43e440718138c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 1 Aug 2025 09:41:19 +0200 Subject: [PATCH 0289/1733] Update tandoor-install.sh --- install/tandoor-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/install/tandoor-install.sh b/install/tandoor-install.sh index b81f70fa8..82759a388 100644 --- a/install/tandoor-install.sh +++ b/install/tandoor-install.sh @@ -93,7 +93,6 @@ EOF export $(cat /opt/tandoor/.env | grep "^[^#]" | xargs) /opt/tandoor/.venv/bin/python manage.py migrate /opt/tandoor/.venv/bin/python manage.py collectstatic --no-input -/opt/tandoor/.venv/bin/python manage.py collectstatic_js_reverse msg_ok "Installed Tandoor" msg_info "Creating Services" From 4430abced0c2fc4dc6d9cf047727608f50c4e24c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 1 Aug 2025 09:51:19 +0200 Subject: [PATCH 0290/1733] Update tandoor-install.sh --- install/tandoor-install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install/tandoor-install.sh b/install/tandoor-install.sh index 82759a388..f0bedab78 100644 --- a/install/tandoor-install.sh +++ b/install/tandoor-install.sh @@ -90,6 +90,7 @@ TANDOOR_VERSION = "$TANDOOR_VERSION" TANDOOR_REF = "bare-metal" VERSION_INFO = [] EOF +cd /opt/tandoor export $(cat /opt/tandoor/.env | grep "^[^#]" | xargs) /opt/tandoor/.venv/bin/python manage.py migrate /opt/tandoor/.venv/bin/python manage.py collectstatic --no-input From f17bcdbe864bbf87bb72207e7a3bd2b894468b98 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 1 Aug 2025 10:28:45 +0200 Subject: [PATCH 0291/1733] Update tandoor-install.sh --- install/tandoor-install.sh | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/install/tandoor-install.sh b/install/tandoor-install.sh index f0bedab78..e65e69e4f 100644 --- a/install/tandoor-install.sh +++ b/install/tandoor-install.sh @@ -83,21 +83,25 @@ POSTGRES_DB=$DB_NAME POSTGRES_PORT=5432 POSTGRES_USER=$DB_USER POSTGRES_PASSWORD=$DB_PASS + +STATIC_URL=/staticfiles/ +MEDIA_URL=/mediafiles/ EOF + TANDOOR_VERSION="$(curl -s https://api.github.com/repos/TandoorRecipes/recipes/releases/latest | jq -r .tag_name)" cat </opt/tandoor/cookbook/version_info.py TANDOOR_VERSION = "$TANDOOR_VERSION" TANDOOR_REF = "bare-metal" VERSION_INFO = [] EOF + cd /opt/tandoor -export $(cat /opt/tandoor/.env | grep "^[^#]" | xargs) /opt/tandoor/.venv/bin/python manage.py migrate /opt/tandoor/.venv/bin/python manage.py collectstatic --no-input msg_ok "Installed Tandoor" msg_info "Creating Services" -cat </etc/systemd/system/gunicorn_tandoor.service +cat <<'EOF' >/etc/systemd/system/tandoor.service [Unit] Description=gunicorn daemon for tandoor After=network.target @@ -116,7 +120,7 @@ EOF cat <<'EOF' >/etc/nginx/conf.d/tandoor.conf server { - listen 8002; + listen 80; #access_log /var/log/nginx/access.log; #error_log /var/log/nginx/error.log; client_max_body_size 128M; From 73e287d617b7a6c2ded6c9767c5c51cb9ca46e15 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 1 Aug 2025 10:41:22 +0200 Subject: [PATCH 0292/1733] Update tandoor-install.sh --- install/tandoor-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/tandoor-install.sh b/install/tandoor-install.sh index e65e69e4f..691139819 100644 --- a/install/tandoor-install.sh +++ b/install/tandoor-install.sh @@ -140,7 +140,7 @@ server { } EOF systemctl reload nginx -systemctl enable -q --now gunicorn_tandoor +systemctl enable -q --now tandoor msg_ok "Created Services" motd_ssh From 6d0eb6d2434312d6d32988632d5340692e4c7191 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 1 Aug 2025 11:02:29 +0200 Subject: [PATCH 0293/1733] Update tandoor-install.sh --- install/tandoor-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/tandoor-install.sh b/install/tandoor-install.sh index 691139819..6adb0e029 100644 --- a/install/tandoor-install.sh +++ b/install/tandoor-install.sh @@ -101,7 +101,7 @@ cd /opt/tandoor msg_ok "Installed Tandoor" msg_info "Creating Services" -cat <<'EOF' >/etc/systemd/system/tandoor.service +cat </etc/systemd/system/tandoor.service [Unit] Description=gunicorn daemon for tandoor After=network.target @@ -118,7 +118,7 @@ ExecStart=/opt/tandoor/.venv/bin/gunicorn --error-logfile /tmp/gunicorn_err.log WantedBy=multi-user.target EOF -cat <<'EOF' >/etc/nginx/conf.d/tandoor.conf +cat </etc/nginx/conf.d/tandoor.conf server { listen 80; #access_log /var/log/nginx/access.log; From 98b8902f84ba2c9da957e41a584463d324815ce9 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 1 Aug 2025 11:20:22 +0200 Subject: [PATCH 0294/1733] test --- vm/docker-vm.sh | 18 +-- vm/unifi-os-vm.sh | 383 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 392 insertions(+), 9 deletions(-) create mode 100644 vm/unifi-os-vm.sh diff --git a/vm/docker-vm.sh b/vm/docker-vm.sh index 3eab0aac9..67455134c 100644 --- a/vm/docker-vm.sh +++ b/vm/docker-vm.sh @@ -475,15 +475,15 @@ if ! command -v virt-customize &>/dev/null; then msg_ok "Installed libguestfs-tools successfully" fi -msg_info "Adding Docker and Docker Compose Plugin to Debian 12 Qcow2 Disk Image" -virt-customize -q -a "${FILE}" --install qemu-guest-agent,apt-transport-https,ca-certificates,curl,gnupg,software-properties-common,lsb-release >/dev/null && - virt-customize -q -a "${FILE}" --run-command "mkdir -p /etc/apt/keyrings && curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg" >/dev/null && - virt-customize -q -a "${FILE}" --run-command "echo 'deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian bookworm stable' > /etc/apt/sources.list.d/docker.list" >/dev/null && - virt-customize -q -a "${FILE}" --run-command "apt-get update -qq && apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin" >/dev/null && - virt-customize -q -a "${FILE}" --run-command "systemctl enable docker" >/dev/null && - virt-customize -q -a "${FILE}" --hostname "${HN}" >/dev/null && - virt-customize -q -a "${FILE}" --run-command "echo -n > /etc/machine-id" >/dev/null -msg_ok "Added Docker and Docker Compose Plugin to Debian 12 Qcow2 Disk Image successfully" +msg_info "Adding UniFi OS Server Installer to Debian 12 Qcow2 Disk Image" +UOS_VERSION="4.2.23" +UOS_URL="https://fw-download.ubnt.com/data/unifi-os-server/8b93-linux-x64-4.2.23-158fa00b-6b2c-4cd8-94ea-e92bc4a81369.23-x64" +UOS_INSTALLER="unifi-os-server-${UOS_VERSION}.bin" +virt-customize -q -a "${FILE}" \ + --install qemu-guest-agent,ca-certificates,curl,lsb-release,podman \ + --run-command "curl -fsSL '${UOS_URL}' -o /root/${UOS_INSTALLER} && chmod +x /root/${UOS_INSTALLER}" >/dev/null + +msg_ok "Added UniFi OS Server Installer to Debian 12 Qcow2 Disk Image successfully" msg_info "Expanding root partition to use full disk space" qemu-img create -f qcow2 expanded.qcow2 ${DISK_SIZE} >/dev/null 2>&1 diff --git a/vm/unifi-os-vm.sh b/vm/unifi-os-vm.sh new file mode 100644 index 000000000..330a65e94 --- /dev/null +++ b/vm/unifi-os-vm.sh @@ -0,0 +1,383 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE + +source /dev/stdin <<<$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/vm-core.func) +load_functions + +function header_info { + clear + cat <<"EOF" + ____ __ _ ______ + / __ \___ / /_ (_)___ _____ < /__ \ + / / / / _ \/ __ \/ / __ `/ __ \ / /__/ / + / /_/ / __/ /_/ / / /_/ / / / / / // __/ +/_____/\___/_.___/_/\__,_/_/ /_/ /_//____/ + +EOF +} +header_info +echo -e "\n Loading..." +GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') +RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" +METHOD="" +NSAPP="UniFi OS Server" +var_os="linux" +var_version="x64" +UOS_VERSION="4.2.23" +UOS_URL="https://fw-download.ubnt.com/data/unifi-os-server/8b93-linux-x64-4.2.23-158fa00b-6b2c-4cd8-94ea-e92bc4a81369.23-x64" +UOS_INSTALLER="unifi-os-server-${UOS_VERSION}.bin" + +THIN="discard=on,ssd=1," +set -e +trap 'error_handler $LINENO "$BASH_COMMAND"' ERR +trap cleanup EXIT +trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT +trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM +function error_handler() { + local exit_code="$?" + local line_number="$1" + local command="$2" + local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" + post_update_to_api "failed" "${command}" + echo -e "\n$error_message\n" + cleanup_vmid +} + +get_valid_nextid +cleanup_vmid +cleanup +post_update_to_api "done" "none" +[[ -n "${TEMP_DIR:-}" && -d "$TEMP_DIR" ]] && rm -rf "$TEMP_DIR" +check_root +pve_check +arch_check +ssh_check + +TEMP_DIR=$(mktemp -d) +pushd $TEMP_DIR >/dev/null +if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Debian 12 VM" --yesno "This will create a New Debian 12 VM. Proceed?" 10 58; then + : +else + header_info && exit_script +fi + +function default_settings() { + VMID=$(get_valid_nextid) + FORMAT=",efitype=4m" + MACHINE="" + DISK_SIZE="32G" + DISK_CACHE="" + HN="debian" + CPU_TYPE="" + CORE_COUNT="2" + RAM_SIZE="2048" + BRG="vmbr0" + MAC="$GEN_MAC" + VLAN="" + MTU="" + START_VM="yes" + METHOD="default" + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" + echo -e "${CREATING}${BOLD}${DGN}Creating a Debian 12 VM using the above default settings${CL}" +} + +function advanced_settings() { + METHOD="advanced" + [ -z "${VMID:-}" ] && VMID=$(get_valid_nextid) + while true; do + if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z "$VMID" ]; then + VMID=$(get_valid_nextid) + fi + if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then + echo -e "${CROSS}${RD} ID $VMID is already in use${CL}" + sleep 2 + continue + fi + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" + break + else + exit_script + fi + done + + if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \ + "i440fx" "Machine i440fx" ON \ + "q35" "Machine q35" OFF \ + 3>&1 1>&2 2>&3); then + if [ $MACH = q35 ]; then + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" + FORMAT="" + MACHINE=" -machine q35" + else + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" + FORMAT=",efitype=4m" + MACHINE="" + fi + else + exit_script + fi + + if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') + if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then + DISK_SIZE="${DISK_SIZE}G" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" + elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" + else + echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}" + exit_script + fi + else + exit_script + fi + + if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ + "0" "None (Default)" ON \ + "1" "Write Through" OFF \ + 3>&1 1>&2 2>&3); then + if [ $DISK_CACHE = "1" ]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" + DISK_CACHE="cache=writethrough," + else + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" + DISK_CACHE="" + fi + else + exit_script + fi + + if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 debian --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $VM_NAME ]; then + HN="debian" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" + else + HN=$(echo ${VM_NAME,,} | tr -d ' ') + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" + fi + else + exit_script + fi + + if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ + "0" "KVM64 (Default)" ON \ + "1" "Host" OFF \ + 3>&1 1>&2 2>&3); then + if [ $CPU_TYPE1 = "1" ]; then + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" + CPU_TYPE=" -cpu host" + else + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" + CPU_TYPE="" + fi + else + exit_script + fi + + if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $CORE_COUNT ]; then + CORE_COUNT="2" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + else + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + fi + else + exit_script + fi + + if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $RAM_SIZE ]; then + RAM_SIZE="2048" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" + else + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" + fi + else + exit_script + fi + + if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $BRG ]; then + BRG="vmbr0" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + else + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + fi + else + exit_script + fi + + if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $MAC1 ]; then + MAC="$GEN_MAC" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" + else + MAC="$MAC1" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" + fi + else + exit_script + fi + + if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $VLAN1 ]; then + VLAN1="Default" + VLAN="" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" + else + VLAN=",tag=$VLAN1" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" + fi + else + exit_script + fi + + if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $MTU1 ]; then + MTU1="Default" + MTU="" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" + else + MTU=",mtu=$MTU1" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" + fi + else + exit_script + fi + + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" + START_VM="yes" + else + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}" + START_VM="no" + fi + + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Debian 12 VM?" --no-button Do-Over 10 58); then + echo -e "${CREATING}${BOLD}${DGN}Creating a Debian 12 VM using the above advanced settings${CL}" + else + header_info + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" + advanced_settings + fi +} + +function start_script() { + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}" + default_settings + else + header_info + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" + advanced_settings + fi +} + +start_script +post_to_api_vm + +msg_info "Validating Storage" +while read -r line; do + TAG=$(echo $line | awk '{print $1}') + TYPE=$(echo $line | awk '{printf "%-10s", $2}') + FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') + ITEM=" Type: $TYPE Free: $FREE " + OFFSET=2 + if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then + MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) + fi + STORAGE_MENU+=("$TAG" "$ITEM" "OFF") +done < <(pvesm status -content images | awk 'NR>1') +VALID=$(pvesm status -content images | awk 'NR>1') +if [ -z "$VALID" ]; then + msg_error "Unable to detect a valid storage location." + exit +elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then + STORAGE=${STORAGE_MENU[0]} +else + while [ -z "${STORAGE:+x}" ]; do + STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ + "Which storage pool you would like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \ + 16 $(($MSG_MAX_LENGTH + 23)) 6 \ + "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) || exit + done +fi +msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." +msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." +msg_info "Retrieving the URL for the Debian 12 Qcow2 Disk Image" +URL=https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-nocloud-amd64.qcow2 +sleep 2 +msg_ok "${CL}${BL}${URL}${CL}" +curl -f#SL "$URL" -O +echo -en "\e[1A\e[0K" +FILE=$(basename $URL) +msg_ok "Downloaded ${CL}${BL}${FILE}${CL}" + +STORAGE_TYPE=$(pvesm status -storage $STORAGE | awk 'NR>1 {print $2}') +case $STORAGE_TYPE in +nfs | dir) + DISK_EXT=".qcow2" + DISK_REF="$VMID/" + DISK_IMPORT="-format qcow2" + THIN="" + ;; +btrfs) + DISK_EXT=".raw" + DISK_REF="$VMID/" + DISK_IMPORT="-format raw" + FORMAT=",efitype=4m" + THIN="" + ;; +esac +for i in {0,1}; do + disk="DISK$i" + eval DISK${i}=vm-${VMID}-disk-${i}${DISK_EXT:-} + eval DISK${i}_REF=${STORAGE}:${DISK_REF:-}${!disk} +done + +msg_info "Creating a Debian 12 VM" +qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \ + -name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci +pvesm alloc $STORAGE $VMID $DISK0 4M 1>&/dev/null +qm importdisk $VMID ${FILE} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null +qm set $VMID \ + -efidisk0 ${DISK0_REF}${FORMAT} \ + -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ + -boot order=scsi0 \ + -serial0 socket >/dev/null + +if [ -n "$DISK_SIZE" ]; then + msg_info "Resizing disk to $DISK_SIZE GB" + qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null +else + msg_info "Using default disk size of $DEFAULT_DISK_SIZE GB" + qm resize $VMID scsi0 ${DEFAULT_DISK_SIZE} >/dev/null +fi + +msg_ok "Created a Debian 12 VM ${CL}${BL}(${HN})" +if [ "$START_VM" == "yes" ]; then + msg_info "Starting Debian 12 VM" + qm start $VMID + msg_ok "Started Debian 12 VM" +fi + +msg_ok "Completed Successfully!\n" +msg_custom "More Info at https://github.com/community-scripts/ProxmoxVED/discussions/836" From dce6c3f828659ee1258996c3f12d1209a04770c6 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 1 Aug 2025 11:21:42 +0200 Subject: [PATCH 0295/1733] Update tandoor-install.sh --- install/tandoor-install.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/install/tandoor-install.sh b/install/tandoor-install.sh index 6adb0e029..a9682b275 100644 --- a/install/tandoor-install.sh +++ b/install/tandoor-install.sh @@ -118,11 +118,11 @@ ExecStart=/opt/tandoor/.venv/bin/gunicorn --error-logfile /tmp/gunicorn_err.log WantedBy=multi-user.target EOF -cat </etc/nginx/conf.d/tandoor.conf +cat <<'EOF' >/etc/nginx/conf.d/tandoor.conf server { - listen 80; - #access_log /var/log/nginx/access.log; - #error_log /var/log/nginx/error.log; + listen 8002; + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log; client_max_body_size 128M; # serve media files location /static/ { From 4619bf8e3aa1814547fe5b315e4a34b82344b3aa Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 1 Aug 2025 11:24:10 +0200 Subject: [PATCH 0296/1733] Update unifi-os-vm.sh --- vm/unifi-os-vm.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/vm/unifi-os-vm.sh b/vm/unifi-os-vm.sh index 330a65e94..b43c6d533 100644 --- a/vm/unifi-os-vm.sh +++ b/vm/unifi-os-vm.sh @@ -53,7 +53,6 @@ cleanup post_update_to_api "done" "none" [[ -n "${TEMP_DIR:-}" && -d "$TEMP_DIR" ]] && rm -rf "$TEMP_DIR" check_root -pve_check arch_check ssh_check From 607e29fab365d48e120f2f07a2eadae2d0bba7a9 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 1 Aug 2025 11:27:18 +0200 Subject: [PATCH 0297/1733] Update unifi-os-vm.sh --- vm/unifi-os-vm.sh | 220 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 189 insertions(+), 31 deletions(-) diff --git a/vm/unifi-os-vm.sh b/vm/unifi-os-vm.sh index b43c6d533..61a79fbe7 100644 --- a/vm/unifi-os-vm.sh +++ b/vm/unifi-os-vm.sh @@ -2,11 +2,9 @@ # Copyright (c) 2021-2025 community-scripts ORG # Author: MickLesk (CanbiZ) -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -source /dev/stdin <<<$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/vm-core.func) -load_functions +source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) function header_info { clear @@ -31,6 +29,39 @@ UOS_VERSION="4.2.23" UOS_URL="https://fw-download.ubnt.com/data/unifi-os-server/8b93-linux-x64-4.2.23-158fa00b-6b2c-4cd8-94ea-e92bc4a81369.23-x64" UOS_INSTALLER="unifi-os-server-${UOS_VERSION}.bin" +YW=$(echo "\033[33m") +BL=$(echo "\033[36m") +RD=$(echo "\033[01;31m") +BGN=$(echo "\033[4;92m") +GN=$(echo "\033[1;92m") +DGN=$(echo "\033[32m") +CL=$(echo "\033[m") + +CL=$(echo "\033[m") +BOLD=$(echo "\033[1m") +BFR="\\r\\033[K" +HOLD=" " +TAB=" " + +CM="${TAB}✔️${TAB}${CL}" +CROSS="${TAB}✖️${TAB}${CL}" +INFO="${TAB}💡${TAB}${CL}" +OS="${TAB}🖥️${TAB}${CL}" +CONTAINERTYPE="${TAB}📦${TAB}${CL}" +DISKSIZE="${TAB}💾${TAB}${CL}" +CPUCORE="${TAB}🧠${TAB}${CL}" +RAMSIZE="${TAB}🛠️${TAB}${CL}" +CONTAINERID="${TAB}🆔${TAB}${CL}" +HOSTNAME="${TAB}🏠${TAB}${CL}" +BRIDGE="${TAB}🌉${TAB}${CL}" +GATEWAY="${TAB}🌐${TAB}${CL}" +DEFAULT="${TAB}⚙️${TAB}${CL}" +MACADDRESS="${TAB}🔗${TAB}${CL}" +VLANTAG="${TAB}🏷️${TAB}${CL}" +CREATING="${TAB}🚀${TAB}${CL}" +ADVANCED="${TAB}🧩${TAB}${CL}" +CLOUD="${TAB}☁️${TAB}${CL}" + THIN="discard=on,ssd=1," set -e trap 'error_handler $LINENO "$BASH_COMMAND"' ERR @@ -47,28 +78,136 @@ function error_handler() { cleanup_vmid } -get_valid_nextid -cleanup_vmid -cleanup -post_update_to_api "done" "none" -[[ -n "${TEMP_DIR:-}" && -d "$TEMP_DIR" ]] && rm -rf "$TEMP_DIR" -check_root -arch_check -ssh_check +function get_valid_nextid() { + local try_id + try_id=$(pvesh get /cluster/nextid) + while true; do + if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then + try_id=$((try_id + 1)) + continue + fi + if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then + try_id=$((try_id + 1)) + continue + fi + break + done + echo "$try_id" +} + +function cleanup_vmid() { + if qm status $VMID &>/dev/null; then + qm stop $VMID &>/dev/null + qm destroy $VMID &>/dev/null + fi +} + +function cleanup() { + popd >/dev/null + post_update_to_api "done" "none" + rm -rf $TEMP_DIR +} TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Debian 12 VM" --yesno "This will create a New Debian 12 VM. Proceed?" 10 58; then : else - header_info && exit_script + header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit fi +function msg_info() { + local msg="$1" + echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" +} + +function msg_ok() { + local msg="$1" + echo -e "${BFR}${CM}${GN}${msg}${CL}" +} + +function msg_error() { + local msg="$1" + echo -e "${BFR}${CROSS}${RD}${msg}${CL}" +} + +function check_root() { + if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then + clear + msg_error "Please run this script as root." + echo -e "\nExiting..." + sleep 2 + exit + fi +} + +pve_check() { + local PVE_VER + PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" + + # Check for Proxmox VE 8.x + if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then + local MINOR="${BASH_REMATCH[1]}" + if ((MINOR < 1 || MINOR > 4)); then + msg_error "This version of Proxmox VE is not supported." + echo -e "Required: Proxmox VE version 8.1 – 8.4" + exit 1 + fi + return 0 + fi + + # Check for Proxmox VE 9.x (Beta) — require confirmation + if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then + if whiptail --title "Proxmox 9.x Detected (Beta)" \ + --yesno "You are using Proxmox VE $PVE_VER, which is currently in Beta state.\n\nThis version is experimentally supported.\n\nDo you want to proceed anyway?" 12 70; then + msg_ok "Confirmed: Continuing with Proxmox VE $PVE_VER" + return 0 + else + msg_error "Aborted by user: Proxmox VE 9.x was not confirmed." + exit 1 + fi + fi + + # All other unsupported versions + msg_error "This version of Proxmox VE is not supported." + echo -e "Supported versions: Proxmox VE 8.1 – 8.4 or 9.x (Beta, with confirmation)" + exit 1 +} + +function arch_check() { + if [ "$(dpkg --print-architecture)" != "amd64" ]; then + echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" + echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" + echo -e "Exiting..." + sleep 2 + exit + fi +} + +function ssh_check() { + if command -v pveversion >/dev/null 2>&1; then + if [ -n "${SSH_CLIENT:+x}" ]; then + if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then + echo "you've been warned" + else + clear + exit + fi + fi + fi +} + +function exit-script() { + clear + echo -e "\n${CROSS}${RD}User exited script${CL}\n" + exit +} + function default_settings() { VMID=$(get_valid_nextid) FORMAT=",efitype=4m" MACHINE="" - DISK_SIZE="32G" + DISK_SIZE="8G" DISK_CACHE="" HN="debian" CPU_TYPE="" @@ -79,6 +218,7 @@ function default_settings() { VLAN="" MTU="" START_VM="yes" + CLOUD_INIT="no" METHOD="default" echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" @@ -92,6 +232,7 @@ function default_settings() { echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" + echo -e "${CLOUD}${BOLD}${DGN}Configure Cloud-init: ${BGN}no${CL}" echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" echo -e "${CREATING}${BOLD}${DGN}Creating a Debian 12 VM using the above default settings${CL}" } @@ -112,7 +253,7 @@ function advanced_settings() { echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" break else - exit_script + exit-script fi done @@ -130,7 +271,7 @@ function advanced_settings() { MACHINE="" fi else - exit_script + exit-script fi if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then @@ -142,10 +283,10 @@ function advanced_settings() { echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" else echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}" - exit_script + exit-script fi else - exit_script + exit-script fi if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ @@ -160,7 +301,7 @@ function advanced_settings() { DISK_CACHE="" fi else - exit_script + exit-script fi if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 debian --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then @@ -172,7 +313,7 @@ function advanced_settings() { echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" fi else - exit_script + exit-script fi if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ @@ -187,7 +328,7 @@ function advanced_settings() { CPU_TYPE="" fi else - exit_script + exit-script fi if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then @@ -198,7 +339,7 @@ function advanced_settings() { echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" fi else - exit_script + exit-script fi if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then @@ -209,7 +350,7 @@ function advanced_settings() { echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" fi else - exit_script + exit-script fi if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then @@ -220,7 +361,7 @@ function advanced_settings() { echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" fi else - exit_script + exit-script fi if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then @@ -232,7 +373,7 @@ function advanced_settings() { echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" fi else - exit_script + exit-script fi if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then @@ -245,7 +386,7 @@ function advanced_settings() { echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" fi else - exit_script + exit-script fi if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then @@ -258,7 +399,15 @@ function advanced_settings() { echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" fi else - exit_script + exit-script + fi + + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "CLOUD-INIT" --yesno "Configure the VM with Cloud-init?" --defaultno 10 58); then + echo -e "${CLOUD}${BOLD}${DGN}Configure Cloud-init: ${BGN}yes${CL}" + CLOUD_INIT="yes" + else + echo -e "${CLOUD}${BOLD}${DGN}Configure Cloud-init: ${BGN}no${CL}" + CLOUD_INIT="no" fi if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then @@ -290,7 +439,12 @@ function start_script() { fi } +check_root +arch_check +pve_check +ssh_check start_script + post_to_api_vm msg_info "Validating Storage" @@ -314,18 +468,22 @@ elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then else while [ -z "${STORAGE:+x}" ]; do STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ - "Which storage pool you would like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \ + "Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ - "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) || exit + "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) done fi msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." msg_info "Retrieving the URL for the Debian 12 Qcow2 Disk Image" -URL=https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-nocloud-amd64.qcow2 +if [ "$CLOUD_INIT" == "yes" ]; then + URL=https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2 +else + URL=https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-nocloud-amd64.qcow2 +fi sleep 2 msg_ok "${CL}${BL}${URL}${CL}" -curl -f#SL "$URL" -O +curl -f#SL -o "$(basename "$URL")" "$URL" echo -en "\e[1A\e[0K" FILE=$(basename $URL) msg_ok "Downloaded ${CL}${BL}${FILE}${CL}" From 0cdea98a717cebab10ad9bfae5d11942a1b5d39e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 1 Aug 2025 11:34:55 +0200 Subject: [PATCH 0298/1733] Update unifi-os-vm.sh --- vm/unifi-os-vm.sh | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/vm/unifi-os-vm.sh b/vm/unifi-os-vm.sh index 61a79fbe7..9308f7b72 100644 --- a/vm/unifi-os-vm.sh +++ b/vm/unifi-os-vm.sh @@ -488,6 +488,37 @@ echo -en "\e[1A\e[0K" FILE=$(basename $URL) msg_ok "Downloaded ${CL}${BL}${FILE}${CL}" +### UNI-FI OS BLOCK BEGIN + +UOS_VERSION="4.2.23" +UOS_URL="https://fw-download.ubnt.com/data/unifi-os-server/8b93-linux-x64-4.2.23-158fa00b-6b2c-4cd8-94ea-e92bc4a81369.23-x64" +UOS_INSTALLER="unifi-os-server-${UOS_VERSION}.bin" + +if ! command -v virt-customize &>/dev/null; then + msg_info "Installing required package: libguestfs-tools on Proxmox host" + apt-get -qq update >/dev/null + apt-get -qq install libguestfs-tools lsb-release -y >/dev/null + msg_ok "libguestfs-tools installed successfully" +fi + +msg_info "Patching DNS resolver for apt inside the image" +virt-customize -q -a "${FILE}" --run-command 'echo "nameserver 1.1.1.1" > /etc/resolv.conf' >/dev/null + +msg_info "Injecting UniFi OS Server dependencies and installer into Debian 12 image" +virt-customize -q -a "${FILE}" \ + --install qemu-guest-agent,ca-certificates,curl,lsb-release,podman \ + --run-command "curl -fsSL '${UOS_URL}' -o /root/${UOS_INSTALLER} && chmod +x /root/${UOS_INSTALLER}" >/dev/null + +msg_info "Cleaning up temporary DNS resolver in image" +virt-customize -q -a "${FILE}" --run-command 'rm -f /etc/resolv.conf' >/dev/null + +msg_ok "UniFi OS Server installer and dependencies successfully added to Debian 12 image" +msg_custom "After first VM boot, SSH to the VM as root and run:" +msg_custom "/root/${UOS_INSTALLER} --install" +msg_custom "Official UniFi OS Server Guide: https://help.ui.com/hc/en-us/articles/26951761949147-UniFi-OS-Server" + +### UNI-FI OS BLOCK END + STORAGE_TYPE=$(pvesm status -storage $STORAGE | awk 'NR>1 {print $2}') case $STORAGE_TYPE in nfs | dir) From 99cf21c76b1286bb6eebf13c0306b82d6dc59458 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 1 Aug 2025 11:38:17 +0200 Subject: [PATCH 0299/1733] Update tandoor-install.sh --- install/tandoor-install.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/install/tandoor-install.sh b/install/tandoor-install.sh index a9682b275..6f2638e4b 100644 --- a/install/tandoor-install.sh +++ b/install/tandoor-install.sh @@ -135,6 +135,9 @@ server { location / { proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; proxy_pass http://unix:/opt/tandoor/tandoor.sock; } } From d9d07a87537525b194b519210dc2612d69991f33 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 1 Aug 2025 11:44:11 +0200 Subject: [PATCH 0300/1733] Update unifi-os-vm.sh --- vm/unifi-os-vm.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vm/unifi-os-vm.sh b/vm/unifi-os-vm.sh index 9308f7b72..e3994bbfc 100644 --- a/vm/unifi-os-vm.sh +++ b/vm/unifi-os-vm.sh @@ -513,9 +513,9 @@ msg_info "Cleaning up temporary DNS resolver in image" virt-customize -q -a "${FILE}" --run-command 'rm -f /etc/resolv.conf' >/dev/null msg_ok "UniFi OS Server installer and dependencies successfully added to Debian 12 image" -msg_custom "After first VM boot, SSH to the VM as root and run:" -msg_custom "/root/${UOS_INSTALLER} --install" -msg_custom "Official UniFi OS Server Guide: https://help.ui.com/hc/en-us/articles/26951761949147-UniFi-OS-Server" +msg_ok "After first VM boot, SSH to the VM as root and run:" +msg_ok "/root/${UOS_INSTALLER} --install" +msg_ok "Official UniFi OS Server Guide: https://help.ui.com/hc/en-us/articles/26951761949147-UniFi-OS-Server" ### UNI-FI OS BLOCK END @@ -568,4 +568,4 @@ if [ "$START_VM" == "yes" ]; then fi msg_ok "Completed Successfully!\n" -msg_custom "More Info at https://github.com/community-scripts/ProxmoxVED/discussions/836" +msg_ok "More Info at https://github.com/community-scripts/ProxmoxVED/discussions/836" From d82b41779641c7f410e558fcea81d7bc956691a2 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 1 Aug 2025 11:47:30 +0200 Subject: [PATCH 0301/1733] fix --- install/tandoor-install.sh | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/install/tandoor-install.sh b/install/tandoor-install.sh index 6f2638e4b..823a60d17 100644 --- a/install/tandoor-install.sh +++ b/install/tandoor-install.sh @@ -17,6 +17,7 @@ update_os msg_info "Installing Dependencies (Patience)" $STD apt-get install -y --no-install-recommends \ build-essential \ + python3 \ libpq-dev \ libmagic-dev \ libzbar0 \ @@ -24,24 +25,12 @@ $STD apt-get install -y --no-install-recommends \ libsasl2-dev \ libldap2-dev \ libssl-dev \ - git \ - make \ pkg-config \ libxmlsec1-dev \ libxml2-dev \ libxmlsec1-openssl msg_ok "Installed Dependencies" -msg_info "Setup Python3" -$STD apt-get install -y \ - python3 \ - python3-dev \ - python3-setuptools \ - python3-pip \ - python3-xmlsec -rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED -msg_ok "Setup Python3" - NODE_VERSION="20" NODE_MODULE="yarn@latest" setup_nodejs fetch_and_deploy_gh_release "tandoor" "TandoorRecipes/recipes" "tarball" "latest" "/opt/tandoor" PG_VERSION="16" setup_postgresql @@ -68,8 +57,8 @@ msg_ok "Set up PostgreSQL Database" msg_info "Setup Tandoor" mkdir -p /opt/tandoor/{config,api,mediafiles,staticfiles} cd /opt/tandoor -uv venv .venv --python=python3 -uv pip install -r requirements.txt --python .venv/bin/python +$STD uv venv .venv --python=python3 +$STD uv pip install -r requirements.txt --python .venv/bin/python cd /opt/tandoor/vue3 $STD yarn install $STD yarn build @@ -96,8 +85,8 @@ VERSION_INFO = [] EOF cd /opt/tandoor -/opt/tandoor/.venv/bin/python manage.py migrate -/opt/tandoor/.venv/bin/python manage.py collectstatic --no-input +$STD /opt/tandoor/.venv/bin/python manage.py migrate +$STD /opt/tandoor/.venv/bin/python manage.py collectstatic --no-input msg_ok "Installed Tandoor" msg_info "Creating Services" From 1c950e7d41f434b326d6cce6f8af101726677a96 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 1 Aug 2025 12:12:53 +0200 Subject: [PATCH 0302/1733] fixes --- ct/tandoor.sh | 58 ++++++++++++++++++++++++++++----------------- vm/unifi-os-vm.sh | 60 ++++++++++++++++++++--------------------------- 2 files changed, 62 insertions(+), 56 deletions(-) diff --git a/ct/tandoor.sh b/ct/tandoor.sh index acac0d6a5..1effb13b8 100644 --- a/ct/tandoor.sh +++ b/ct/tandoor.sh @@ -27,27 +27,43 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - #if ! [[ $(dpkg -s python3-xmlsec 2>/dev/null) ]]; then - #$STD apt-get update - #$STD apt-get install -y python3-xmlsec - #fi - #if cd /opt/tandoor && git pull | grep -q 'Already up to date'; then - msg_ok "There is currently no update available." - #else - #msg_info "Updating ${APP} (Patience)" - #export $(cat /opt/tandoor/.env | grep "^[^#]" | xargs) - #cd /opt/tandoor/ - #$STD pip3 install -r requirements.txt - #$STD /usr/bin/python3 /opt/tandoor/manage.py migrate - #$STD /usr/bin/python3 /opt/tandoor/manage.py collectstatic --no-input - #$STD /usr/bin/python3 /opt/tandoor/manage.py collectstatic_js_reverse - #cd /opt/tandoor/vue - #$STD yarn install - #$STD yarn build - #cd /opt/tandoor - #$STD python3 version.py - #systemctl restart gunicorn_tandoor - #msg_ok "Updated ${APP}" + + RELEASE=$(curl -fsSL https://api.github.com/repos/wizarrrr/wizarr/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + if [[ "${RELEASE}" != "$(cat ~/.tandoor 2>/dev/null)" ]] || [[ ! -f ~/.tandoor ]]; then + msg_info "Stopping $APP" + systemctl stop tandoor + msg_ok "Stopped $APP" + + msg_info "Creating Backup" + BACKUP_FILE="/opt/tandoor_backup_$(date +%F).tar.gz" + $STD tar -czf "$BACKUP_FILE" /opt/tandoor/{.env,start.sh} /opt/tandoor/database/ &>/dev/null + msg_ok "Backup Created" + + setup_uv + fetch_and_deploy_gh_release "tandoor" "TandoorRecipes/recipes" "tarball" "latest" "/opt/tandoor" + + msg_info "Updating $APP to v${RELEASE}" + cd /opt/wizarr + /usr/local/bin/uv -q sync --locked + $STD /usr/local/bin/uv -q run pybabel compile -d app/translations + $STD npm --prefix app/static install + $STD npm --prefix app/static run build:css + mkdir -p ./.cache + $STD tar -xf "$BACKUP_FILE" --directory=/ + $STD /usr/local/bin/uv -q run flask db upgrade + msg_ok "Updated $APP to v${RELEASE}" + + msg_info "Starting $APP" + systemctl start wizarr + msg_ok "Started $APP" + + msg_info "Cleaning Up" + rm -rf "$BACKUP_FILE" + msg_ok "Cleanup Completed" + msg_ok "Update Successful" + else + msg_ok "No update required. ${APP} is already at v${RELEASE}" + fi exit } diff --git a/vm/unifi-os-vm.sh b/vm/unifi-os-vm.sh index e3994bbfc..9503d180b 100644 --- a/vm/unifi-os-vm.sh +++ b/vm/unifi-os-vm.sh @@ -207,7 +207,7 @@ function default_settings() { VMID=$(get_valid_nextid) FORMAT=",efitype=4m" MACHINE="" - DISK_SIZE="8G" + DISK_SIZE="32G" DISK_CACHE="" HN="debian" CPU_TYPE="" @@ -446,7 +446,6 @@ ssh_check start_script post_to_api_vm - msg_info "Validating Storage" while read -r line; do TAG=$(echo $line | awk '{print $1}') @@ -475,12 +474,8 @@ else fi msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." -msg_info "Retrieving the URL for the Debian 12 Qcow2 Disk Image" -if [ "$CLOUD_INIT" == "yes" ]; then - URL=https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2 -else - URL=https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-nocloud-amd64.qcow2 -fi +msg_info "Retrieving the URL for the Debian 12 Cloud-Init Image" +URL=https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2 sleep 2 msg_ok "${CL}${BL}${URL}${CL}" curl -f#SL -o "$(basename "$URL")" "$URL" @@ -488,36 +483,23 @@ echo -en "\e[1A\e[0K" FILE=$(basename $URL) msg_ok "Downloaded ${CL}${BL}${FILE}${CL}" -### UNI-FI OS BLOCK BEGIN - +# Create Cloud-Init User-Data Snippet for UniFi OS Server UOS_VERSION="4.2.23" UOS_URL="https://fw-download.ubnt.com/data/unifi-os-server/8b93-linux-x64-4.2.23-158fa00b-6b2c-4cd8-94ea-e92bc4a81369.23-x64" UOS_INSTALLER="unifi-os-server-${UOS_VERSION}.bin" -if ! command -v virt-customize &>/dev/null; then - msg_info "Installing required package: libguestfs-tools on Proxmox host" - apt-get -qq update >/dev/null - apt-get -qq install libguestfs-tools lsb-release -y >/dev/null - msg_ok "libguestfs-tools installed successfully" -fi +USERDATA_SNIPPET="/var/lib/vz/snippets/unifios-server-${VMID}-user-data.yaml" +cat >"$USERDATA_SNIPPET" < /etc/resolv.conf' >/dev/null - -msg_info "Injecting UniFi OS Server dependencies and installer into Debian 12 image" -virt-customize -q -a "${FILE}" \ - --install qemu-guest-agent,ca-certificates,curl,lsb-release,podman \ - --run-command "curl -fsSL '${UOS_URL}' -o /root/${UOS_INSTALLER} && chmod +x /root/${UOS_INSTALLER}" >/dev/null - -msg_info "Cleaning up temporary DNS resolver in image" -virt-customize -q -a "${FILE}" --run-command 'rm -f /etc/resolv.conf' >/dev/null - -msg_ok "UniFi OS Server installer and dependencies successfully added to Debian 12 image" -msg_ok "After first VM boot, SSH to the VM as root and run:" -msg_ok "/root/${UOS_INSTALLER} --install" -msg_ok "Official UniFi OS Server Guide: https://help.ui.com/hc/en-us/articles/26951761949147-UniFi-OS-Server" - -### UNI-FI OS BLOCK END +msg_ok "Cloud-Init user-data snippet for UniFi OS Server created at ${USERDATA_SNIPPET}" STORAGE_TYPE=$(pvesm status -storage $STORAGE | awk 'NR>1 {print $2}') case $STORAGE_TYPE in @@ -541,7 +523,7 @@ for i in {0,1}; do eval DISK${i}_REF=${STORAGE}:${DISK_REF:-}${!disk} done -msg_info "Creating a Debian 12 VM" +msg_info "Creating a Debian 12 Cloud VM" qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \ -name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci pvesm alloc $STORAGE $VMID $DISK0 4M 1>&/dev/null @@ -560,7 +542,13 @@ else qm resize $VMID scsi0 ${DEFAULT_DISK_SIZE} >/dev/null fi -msg_ok "Created a Debian 12 VM ${CL}${BL}(${HN})" +# Attach Cloud-Init Drive & user-data to VM +msg_info "Attaching Cloud-Init Drive and custom user-data to VM" +qm set $VMID --ide2 $STORAGE:cloudinit +qm set $VMID --cicustom "user=local:snippets/unifios-server-${VMID}-user-data.yaml" + +msg_ok "Created a Debian 12 Cloud-Init VM with UniFi OS Server auto-install" + if [ "$START_VM" == "yes" ]; then msg_info "Starting Debian 12 VM" qm start $VMID @@ -568,4 +556,6 @@ if [ "$START_VM" == "yes" ]; then fi msg_ok "Completed Successfully!\n" +msg_ok "After VM startup, UniFi OS Server will be installed automatically!" +msg_ok "Webinterface will be reachable after a few minutes: https://:443" msg_ok "More Info at https://github.com/community-scripts/ProxmoxVED/discussions/836" From 509dd63d9a935a2c84d2b92f329deb25cdbaac7d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 1 Aug 2025 12:16:35 +0200 Subject: [PATCH 0303/1733] Update tandoor.sh --- ct/tandoor.sh | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/ct/tandoor.sh b/ct/tandoor.sh index 1effb13b8..40cfb61ac 100644 --- a/ct/tandoor.sh +++ b/ct/tandoor.sh @@ -28,7 +28,12 @@ function update_script() { exit fi - RELEASE=$(curl -fsSL https://api.github.com/repos/wizarrrr/wizarr/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + if [[ ! -f ~/.tandoor ]]; then + msg_error "v1 Installation found, please create an new LXC!" + exit + fi + + RELEASE=$(curl -fsSL https://api.github.com/repos/TandoorRecipes/recipes/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') if [[ "${RELEASE}" != "$(cat ~/.tandoor 2>/dev/null)" ]] || [[ ! -f ~/.tandoor ]]; then msg_info "Stopping $APP" systemctl stop tandoor @@ -37,24 +42,36 @@ function update_script() { msg_info "Creating Backup" BACKUP_FILE="/opt/tandoor_backup_$(date +%F).tar.gz" $STD tar -czf "$BACKUP_FILE" /opt/tandoor/{.env,start.sh} /opt/tandoor/database/ &>/dev/null + mv /opt/tandoor/.env /opt/.env msg_ok "Backup Created" - setup_uv + NODE_VERSION="20" NODE_MODULE="yarn@latest" setup_nodejs fetch_and_deploy_gh_release "tandoor" "TandoorRecipes/recipes" "tarball" "latest" "/opt/tandoor" + PYTHON_VERSION="3.13" setup_uv - msg_info "Updating $APP to v${RELEASE}" - cd /opt/wizarr - /usr/local/bin/uv -q sync --locked - $STD /usr/local/bin/uv -q run pybabel compile -d app/translations - $STD npm --prefix app/static install - $STD npm --prefix app/static run build:css - mkdir -p ./.cache - $STD tar -xf "$BACKUP_FILE" --directory=/ - $STD /usr/local/bin/uv -q run flask db upgrade - msg_ok "Updated $APP to v${RELEASE}" + msg_info "Updating $APP to ${RELEASE}" + mkdir -p /opt/tandoor/{config,api,mediafiles,staticfiles} + mv /opt/.env /opt/tandoor/.env + cd /opt/tandoor + $STD uv venv .venv --python=python3 + $STD uv pip install -r requirements.txt --python .venv/bin/python + cd /opt/tandoor/vue3 + $STD yarn install + $STD yarn build + TANDOOR_VERSION="$(curl -s https://api.github.com/repos/TandoorRecipes/recipes/releases/latest | jq -r .tag_name)" + cat </opt/tandoor/cookbook/version_info.py +TANDOOR_VERSION = "$TANDOOR_VERSION" +TANDOOR_REF = "bare-metal" +VERSION_INFO = [] +EOF + cd /opt/tandoor + $STD /opt/tandoor/.venv/bin/python manage.py migrate + $STD /opt/tandoor/.venv/bin/python manage.py collectstatic --no-input + msg_ok "Updated $APP to ${RELEASE}" msg_info "Starting $APP" - systemctl start wizarr + systemctl start tandoor + systemctl reload nginx msg_ok "Started $APP" msg_info "Cleaning Up" From ea547d358ede928fd87f6cb1cda7b123fad1e8ea Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 1 Aug 2025 14:48:06 +0200 Subject: [PATCH 0304/1733] Update unifi-os-vm.sh --- vm/unifi-os-vm.sh | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/vm/unifi-os-vm.sh b/vm/unifi-os-vm.sh index 9503d180b..f06f7e90c 100644 --- a/vm/unifi-os-vm.sh +++ b/vm/unifi-os-vm.sh @@ -475,7 +475,7 @@ fi msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." msg_info "Retrieving the URL for the Debian 12 Cloud-Init Image" -URL=https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-amd64.qcow2 +URL=https://cloud-images.ubuntu.com/oracular/current/oracular-server-cloudimg-amd64.img sleep 2 msg_ok "${CL}${BL}${URL}${CL}" curl -f#SL -o "$(basename "$URL")" "$URL" @@ -492,11 +492,19 @@ USERDATA_SNIPPET="/var/lib/vz/snippets/unifios-server-${VMID}-user-data.yaml" cat >"$USERDATA_SNIPPET" < /etc/systemd/system/getty@tty1.service.d/override.conf < Date: Fri, 1 Aug 2025 14:52:40 +0200 Subject: [PATCH 0305/1733] Update unifi-os-vm.sh --- vm/unifi-os-vm.sh | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/vm/unifi-os-vm.sh b/vm/unifi-os-vm.sh index f06f7e90c..5770838c9 100644 --- a/vm/unifi-os-vm.sh +++ b/vm/unifi-os-vm.sh @@ -446,6 +446,7 @@ ssh_check start_script post_to_api_vm + msg_info "Validating Storage" while read -r line; do TAG=$(echo $line | awk '{print $1}') @@ -474,8 +475,8 @@ else fi msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." -msg_info "Retrieving the URL for the Debian 12 Cloud-Init Image" -URL=https://cloud-images.ubuntu.com/oracular/current/oracular-server-cloudimg-amd64.img +msg_info "Retrieving the URL for the Ubuntu 24.04 Disk Image" +URL=https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img sleep 2 msg_ok "${CL}${BL}${URL}${CL}" curl -f#SL -o "$(basename "$URL")" "$URL" @@ -511,7 +512,7 @@ msg_ok "Cloud-Init user-data snippet for UniFi OS Server created at ${USERDATA_S STORAGE_TYPE=$(pvesm status -storage $STORAGE | awk 'NR>1 {print $2}') case $STORAGE_TYPE in -nfs | dir) +nfs | dir | cifs) DISK_EXT=".qcow2" DISK_REF="$VMID/" DISK_IMPORT="-format qcow2" @@ -531,7 +532,7 @@ for i in {0,1}; do eval DISK${i}_REF=${STORAGE}:${DISK_REF:-}${!disk} done -msg_info "Creating a Debian 12 Cloud VM" +msg_info "Creating a Ubuntu 24.04 VM" qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \ -name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci pvesm alloc $STORAGE $VMID $DISK0 4M 1>&/dev/null @@ -539,9 +540,40 @@ qm importdisk $VMID ${FILE} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null qm set $VMID \ -efidisk0 ${DISK0_REF}${FORMAT} \ -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ + -ide2 ${STORAGE}:cloudinit \ -boot order=scsi0 \ -serial0 socket >/dev/null +DESCRIPTION=$( + cat < + + Logo + +

ubuntu VM

+ +

+ + spend Coffee + +

+ + + + GitHub + + + + Discussions + + + + Issues + + +EOF +) +qm set "$VMID" -description "$DESCRIPTION" >/dev/null if [ -n "$DISK_SIZE" ]; then msg_info "Resizing disk to $DISK_SIZE GB" qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null From ea489d315171d20af74415acbeb05e2da6bb6256 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 1 Aug 2025 15:01:34 +0200 Subject: [PATCH 0306/1733] Update unifi-os-vm.sh --- vm/unifi-os-vm.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/vm/unifi-os-vm.sh b/vm/unifi-os-vm.sh index 5770838c9..88e649db7 100644 --- a/vm/unifi-os-vm.sh +++ b/vm/unifi-os-vm.sh @@ -584,9 +584,7 @@ fi # Attach Cloud-Init Drive & user-data to VM msg_info "Attaching Cloud-Init Drive and custom user-data to VM" -qm set $VMID --ide2 $STORAGE:cloudinit qm set $VMID --cicustom "user=local:snippets/unifios-server-${VMID}-user-data.yaml" - msg_ok "Created a Debian 12 Cloud-Init VM with UniFi OS Server auto-install" if [ "$START_VM" == "yes" ]; then From 6abf402a5783ead2a0f032f386087e6b0f162e72 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 1 Aug 2025 15:07:39 +0200 Subject: [PATCH 0307/1733] t --- ct/proxmox-backup-server.sh | 44 ++++++++++++++++++++++++ install/proxmox-backup-server-install.sh | 31 +++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 ct/proxmox-backup-server.sh create mode 100644 install/proxmox-backup-server-install.sh diff --git a/ct/proxmox-backup-server.sh b/ct/proxmox-backup-server.sh new file mode 100644 index 000000000..dc23371c4 --- /dev/null +++ b/ct/proxmox-backup-server.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://www.proxmox.com/en/proxmox-backup-server + +APP="Proxmox-Backup-Server" +var_tags="${var_tags:-backup}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-10}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -e /usr/sbin/proxmox-backup-manager ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating $APP LXC" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Updated $APP LXC" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:8007${CL}" \ No newline at end of file diff --git a/install/proxmox-backup-server-install.sh b/install/proxmox-backup-server-install.sh new file mode 100644 index 000000000..65439f63d --- /dev/null +++ b/install/proxmox-backup-server-install.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://www.proxmox.com/en/proxmox-backup-server + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Proxmox Backup Server" +curl -fsSL "https://enterprise.proxmox.com/debian/proxmox-release-bookworm.gpg" -o "/etc/apt/trusted.gpg.d/proxmox-release-bookworm.gpg" +cat <>/etc/apt/sources.list +deb http://download.proxmox.com/debian/pbs bookworm pbs-no-subscription +EOF +$STD apt-get update +$STD apt-get install -y proxmox-backup-server +msg_ok "Installed Proxmox Backup Server" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 39bb36db146c3a55fb6471d06442a10d2bddb05c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 1 Aug 2025 15:11:06 +0200 Subject: [PATCH 0308/1733] fix --- ct/proxmox-backup-server.sh | 26 ++++++++++++------------ install/proxmox-backup-server-install.sh | 19 +++++++++++++---- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/ct/proxmox-backup-server.sh b/ct/proxmox-backup-server.sh index dc23371c4..5463b1eba 100644 --- a/ct/proxmox-backup-server.sh +++ b/ct/proxmox-backup-server.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -20,18 +20,18 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -e /usr/sbin/proxmox-backup-manager ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - msg_info "Updating $APP LXC" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "Updated $APP LXC" + header_info + check_container_storage + check_container_resources + if [[ ! -e /usr/sbin/proxmox-backup-manager ]]; then + msg_error "No ${APP} Installation Found!" exit + fi + msg_info "Updating $APP LXC" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Updated $APP LXC" + exit } start @@ -41,4 +41,4 @@ description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:8007${CL}" \ No newline at end of file +echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:8007${CL}" diff --git a/install/proxmox-backup-server-install.sh b/install/proxmox-backup-server-install.sh index 65439f63d..502ea9e95 100644 --- a/install/proxmox-backup-server-install.sh +++ b/install/proxmox-backup-server-install.sh @@ -14,10 +14,21 @@ network_check update_os msg_info "Installing Proxmox Backup Server" -curl -fsSL "https://enterprise.proxmox.com/debian/proxmox-release-bookworm.gpg" -o "/etc/apt/trusted.gpg.d/proxmox-release-bookworm.gpg" -cat <>/etc/apt/sources.list -deb http://download.proxmox.com/debian/pbs bookworm pbs-no-subscription -EOF +read -rp "${TAB3}Do you want to use the Enterprise repository (requires valid subscription key)? [y/N]: " USE_ENTERPRISE_REPO + +if [[ "$USE_ENTERPRISE_REPO" =~ ^([yY].*)$ ]]; then + echo "deb https://enterprise.proxmox.com/debian/pbs bookworm pbs-enterprise" >/etc/apt/sources.list.d/pbs-enterprise.list + sed -i '/pbs-no-subscription/s/^/#/' /etc/apt/sources.list + msg_custom "Enterprise repository enabled. Make sure your subscription key is installed." +else + if ! grep -q "pbs-no-subscription" /etc/apt/sources.list; then + echo "deb http://download.proxmox.com/debian/pbs bookworm pbs-no-subscription" >>/etc/apt/sources.list + else + sed -i '/pbs-no-subscription/s/^#//' /etc/apt/sources.list + fi + rm -f /etc/apt/sources.list.d/pbs-enterprise.list +fi + $STD apt-get update $STD apt-get install -y proxmox-backup-server msg_ok "Installed Proxmox Backup Server" From 8dfa04d928ad8bc852f3993156af5e83849f531a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 1 Aug 2025 15:12:29 +0200 Subject: [PATCH 0309/1733] Update unifi-os-vm.sh --- vm/unifi-os-vm.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/vm/unifi-os-vm.sh b/vm/unifi-os-vm.sh index 88e649db7..2a6be023b 100644 --- a/vm/unifi-os-vm.sh +++ b/vm/unifi-os-vm.sh @@ -217,8 +217,7 @@ function default_settings() { MAC="$GEN_MAC" VLAN="" MTU="" - START_VM="yes" - CLOUD_INIT="no" + START_VM="no" METHOD="default" echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" From 8e368efe290faf85d421b3435d834309eac2129c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 1 Aug 2025 15:12:41 +0200 Subject: [PATCH 0310/1733] Update unifi-os-vm.sh --- vm/unifi-os-vm.sh | 8 -------- 1 file changed, 8 deletions(-) diff --git a/vm/unifi-os-vm.sh b/vm/unifi-os-vm.sh index 2a6be023b..ee09733a9 100644 --- a/vm/unifi-os-vm.sh +++ b/vm/unifi-os-vm.sh @@ -401,14 +401,6 @@ function advanced_settings() { exit-script fi - if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "CLOUD-INIT" --yesno "Configure the VM with Cloud-init?" --defaultno 10 58); then - echo -e "${CLOUD}${BOLD}${DGN}Configure Cloud-init: ${BGN}yes${CL}" - CLOUD_INIT="yes" - else - echo -e "${CLOUD}${BOLD}${DGN}Configure Cloud-init: ${BGN}no${CL}" - CLOUD_INIT="no" - fi - if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" START_VM="yes" From 821650ea3a4757544f627efefab23294a924740c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 1 Aug 2025 15:14:55 +0200 Subject: [PATCH 0311/1733] Update unifi-os-vm.sh --- vm/unifi-os-vm.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/vm/unifi-os-vm.sh b/vm/unifi-os-vm.sh index ee09733a9..bfda54079 100644 --- a/vm/unifi-os-vm.sh +++ b/vm/unifi-os-vm.sh @@ -110,7 +110,7 @@ function cleanup() { TEMP_DIR=$(mktemp -d) pushd $TEMP_DIR >/dev/null -if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Debian 12 VM" --yesno "This will create a New Debian 12 VM. Proceed?" 10 58; then +if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Unifi OS VM" --yesno "This will create a New Unifi OS VM. Proceed?" 10 58; then : else header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit @@ -233,7 +233,7 @@ function default_settings() { echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" echo -e "${CLOUD}${BOLD}${DGN}Configure Cloud-init: ${BGN}no${CL}" echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" - echo -e "${CREATING}${BOLD}${DGN}Creating a Debian 12 VM using the above default settings${CL}" + echo -e "${CREATING}${BOLD}${DGN}Creating a Unifi OS VM using the above default settings${CL}" } function advanced_settings() { @@ -409,8 +409,8 @@ function advanced_settings() { START_VM="no" fi - if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Debian 12 VM?" --no-button Do-Over 10 58); then - echo -e "${CREATING}${BOLD}${DGN}Creating a Debian 12 VM using the above advanced settings${CL}" + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Unifi OS VM?" --no-button Do-Over 10 58); then + echo -e "${CREATING}${BOLD}${DGN}Creating a Unifi OS VM using the above advanced settings${CL}" else header_info echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" @@ -579,9 +579,9 @@ qm set $VMID --cicustom "user=local:snippets/unifios-server-${VMID}-user-data.ya msg_ok "Created a Debian 12 Cloud-Init VM with UniFi OS Server auto-install" if [ "$START_VM" == "yes" ]; then - msg_info "Starting Debian 12 VM" + msg_info "Starting Unifi OS VM" qm start $VMID - msg_ok "Started Debian 12 VM" + msg_ok "Started Unifi OS VM" fi msg_ok "Completed Successfully!\n" From 3a0508677bb7bb2c1446fd6666309a150ae0e8f2 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 1 Aug 2025 15:24:42 +0200 Subject: [PATCH 0312/1733] Update unifi-os-vm.sh --- vm/unifi-os-vm.sh | 116 ++++++++++++++++++++-------------------------- 1 file changed, 50 insertions(+), 66 deletions(-) diff --git a/vm/unifi-os-vm.sh b/vm/unifi-os-vm.sh index bfda54079..41239a6b1 100644 --- a/vm/unifi-os-vm.sh +++ b/vm/unifi-os-vm.sh @@ -6,14 +6,14 @@ source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) -function header_info { +function header_info() { clear cat <<"EOF" - ____ __ _ ______ - / __ \___ / /_ (_)___ _____ < /__ \ - / / / / _ \/ __ \/ / __ `/ __ \ / /__/ / - / /_/ / __/ /_/ / / /_/ / / / / / // __/ -/_____/\___/_.___/_/\__,_/_/ /_/ /_//____/ + ____ __ _ ____ ___ + / __ \____ _____/ /_____ _____ | | / / |/ / + / / / / __ \/ ___/ //_/ _ \/ ___/ | | / / /|_/ / + / /_/ / /_/ / /__/ ,< / __/ / | |/ / / / / +/_____/\____/\___/_/|_|\___/_/ |___/_/ /_/ EOF } @@ -31,6 +31,7 @@ UOS_INSTALLER="unifi-os-server-${UOS_VERSION}.bin" YW=$(echo "\033[33m") BL=$(echo "\033[36m") +HA=$(echo "\033[1;34m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") @@ -60,8 +61,6 @@ MACADDRESS="${TAB}🔗${TAB}${CL}" VLANTAG="${TAB}🏷️${TAB}${CL}" CREATING="${TAB}🚀${TAB}${CL}" ADVANCED="${TAB}🧩${TAB}${CL}" -CLOUD="${TAB}☁️${TAB}${CL}" - THIN="discard=on,ssd=1," set -e trap 'error_handler $LINENO "$BASH_COMMAND"' ERR @@ -72,8 +71,8 @@ function error_handler() { local exit_code="$?" local line_number="$1" local command="$2" - local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" post_update_to_api "failed" "${command}" + local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" echo -e "\n$error_message\n" cleanup_vmid } @@ -207,17 +206,17 @@ function default_settings() { VMID=$(get_valid_nextid) FORMAT=",efitype=4m" MACHINE="" - DISK_SIZE="32G" DISK_CACHE="" - HN="debian" + DISK_SIZE="30G" + HN="unifi-server-os" CPU_TYPE="" CORE_COUNT="2" - RAM_SIZE="2048" + RAM_SIZE="4096" BRG="vmbr0" MAC="$GEN_MAC" VLAN="" MTU="" - START_VM="no" + START_VM="yes" METHOD="default" echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" @@ -231,7 +230,6 @@ function default_settings() { echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" - echo -e "${CLOUD}${BOLD}${DGN}Configure Cloud-init: ${BGN}no${CL}" echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" echo -e "${CREATING}${BOLD}${DGN}Creating a Unifi OS VM using the above default settings${CL}" } @@ -303,9 +301,9 @@ function advanced_settings() { exit-script fi - if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 debian --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 docker --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $VM_NAME ]; then - HN="debian" + HN="docker" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" else HN=$(echo ${VM_NAME,,} | tr -d ' ') @@ -429,13 +427,11 @@ function start_script() { advanced_settings fi } - check_root arch_check pve_check ssh_check start_script - post_to_api_vm msg_info "Validating Storage" @@ -466,8 +462,8 @@ else fi msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." -msg_info "Retrieving the URL for the Ubuntu 24.04 Disk Image" -URL=https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img +msg_info "Retrieving the URL for the Debian 12 Qcow2 Disk Image" +URL="https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-nocloud-$(dpkg --print-architecture).qcow2" sleep 2 msg_ok "${CL}${BL}${URL}${CL}" curl -f#SL -o "$(basename "$URL")" "$URL" @@ -475,33 +471,7 @@ echo -en "\e[1A\e[0K" FILE=$(basename $URL) msg_ok "Downloaded ${CL}${BL}${FILE}${CL}" -# Create Cloud-Init User-Data Snippet for UniFi OS Server -UOS_VERSION="4.2.23" -UOS_URL="https://fw-download.ubnt.com/data/unifi-os-server/8b93-linux-x64-4.2.23-158fa00b-6b2c-4cd8-94ea-e92bc4a81369.23-x64" -UOS_INSTALLER="unifi-os-server-${UOS_VERSION}.bin" - -USERDATA_SNIPPET="/var/lib/vz/snippets/unifios-server-${VMID}-user-data.yaml" -cat >"$USERDATA_SNIPPET" < /etc/systemd/system/getty@tty1.service.d/override.conf <1 {print $2}') +STORAGE_TYPE=$(pvesm status -storage "$STORAGE" | awk 'NR>1 {print $2}') case $STORAGE_TYPE in nfs | dir | cifs) DISK_EXT=".qcow2" @@ -523,7 +493,33 @@ for i in {0,1}; do eval DISK${i}_REF=${STORAGE}:${DISK_REF:-}${!disk} done -msg_info "Creating a Ubuntu 24.04 VM" +if ! command -v virt-customize &>/dev/null; then + msg_info "Installing Pre-Requisite libguestfs-tools onto Host" + apt-get -qq update >/dev/null + apt-get -qq install libguestfs-tools lsb-release -y >/dev/null + msg_ok "Installed libguestfs-tools successfully" +fi + +msg_info "Adding UniFi OS Server Installer & root Autologin zu Debian 12 Qcow2 Disk Image" +UOS_VERSION="4.2.23" +UOS_URL="https://fw-download.ubnt.com/data/unifi-os-server/8b93-linux-x64-4.2.23-158fa00b-6b2c-4cd8-94ea-e92bc4a81369.23-x64" +UOS_INSTALLER="unifi-os-server-${UOS_VERSION}.bin" +virt-customize -q -a "${FILE}" \ + --install qemu-guest-agent,ca-certificates,curl,lsb-release,podman \ + --run-command "curl -fsSL '${UOS_URL}' -o /root/${UOS_INSTALLER} && chmod +x /root/${UOS_INSTALLER}" \ + --run-command 'mkdir -p /etc/systemd/system/getty@tty1.service.d' \ + --run-command "bash -c 'echo -e \"[Service]\nExecStart=\nExecStart=-/sbin/agetty --autologin root --noclear %I \$TERM\" > /etc/systemd/system/getty@tty1.service.d/override.conf'" \ + --run-command 'systemctl daemon-reload' \ + --run-command 'systemctl restart getty@tty1' >/dev/null +msg_ok "Installer & root Autologin integrated in image" + +msg_info "Expanding root partition to use full disk space" +qemu-img create -f qcow2 expanded.qcow2 ${DISK_SIZE} >/dev/null 2>&1 +virt-resize --expand /dev/sda1 ${FILE} expanded.qcow2 >/dev/null 2>&1 +mv expanded.qcow2 ${FILE} >/dev/null 2>&1 +msg_ok "Expanded image to full size" + +msg_info "Creating a Unifi OS VM" qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \ -name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci pvesm alloc $STORAGE $VMID $DISK0 4M 1>&/dev/null @@ -531,9 +527,11 @@ qm importdisk $VMID ${FILE} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null qm set $VMID \ -efidisk0 ${DISK0_REF}${FORMAT} \ -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ - -ide2 ${STORAGE}:cloudinit \ -boot order=scsi0 \ -serial0 socket >/dev/null +qm resize $VMID scsi0 8G >/dev/null +qm set $VMID --agent enabled=1 >/dev/null + DESCRIPTION=$( cat < @@ -541,7 +539,7 @@ DESCRIPTION=$( Logo -

ubuntu VM

+

Unifi OS VM

@@ -565,26 +563,12 @@ DESCRIPTION=$( EOF ) qm set "$VMID" -description "$DESCRIPTION" >/dev/null -if [ -n "$DISK_SIZE" ]; then - msg_info "Resizing disk to $DISK_SIZE GB" - qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null -else - msg_info "Using default disk size of $DEFAULT_DISK_SIZE GB" - qm resize $VMID scsi0 ${DEFAULT_DISK_SIZE} >/dev/null -fi - -# Attach Cloud-Init Drive & user-data to VM -msg_info "Attaching Cloud-Init Drive and custom user-data to VM" -qm set $VMID --cicustom "user=local:snippets/unifios-server-${VMID}-user-data.yaml" -msg_ok "Created a Debian 12 Cloud-Init VM with UniFi OS Server auto-install" +msg_ok "Created a Unifi OS VM ${CL}${BL}(${HN})" if [ "$START_VM" == "yes" ]; then msg_info "Starting Unifi OS VM" qm start $VMID msg_ok "Started Unifi OS VM" fi - +post_update_to_api "done" "none" msg_ok "Completed Successfully!\n" -msg_ok "After VM startup, UniFi OS Server will be installed automatically!" -msg_ok "Webinterface will be reachable after a few minutes: https://:443" -msg_ok "More Info at https://github.com/community-scripts/ProxmoxVED/discussions/836" From 01e5e0fa0a22e0dda52c746d645f482b45495c87 Mon Sep 17 00:00:00 2001 From: Joshua Shabanov Date: Fri, 1 Aug 2025 20:14:38 +0300 Subject: [PATCH 0313/1733] ct swizzin.sh --- ct/swizzin.sh | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 ct/swizzin.sh diff --git a/ct/swizzin.sh b/ct/swizzin.sh new file mode 100644 index 000000000..2311e7c6f --- /dev/null +++ b/ct/swizzin.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# Swizzin Seedbox – Proxmox Helper‑Script (CT) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: EEJoshua +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://swizzin.ltd/ + +APP="Swizzin" +var_tags="${var_tags:-seedbox}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-4096}" +var_disk="${var_disk:-20}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if ! command -v sudo box >/dev/null 2>&1; then + msg_error "No ${APP} installation found!\n" + exit + fi + msg_info "Running 'sudo box update' inside the container\n" + sudo box update + msg_ok "Update finished\n" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${CREATING}${GN}${APP} Access it by running 'sudo box' inside the new container${CL}" +echo -e "${INFO}${YW}If installed panel, access through the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" + From 2d5052bca598889ebba9421c7383bee6b1bc721e Mon Sep 17 00:00:00 2001 From: Joshua Shabanov Date: Fri, 1 Aug 2025 20:15:07 +0300 Subject: [PATCH 0314/1733] install swizzin-install.sh --- install/swizzin-install.sh | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 install/swizzin-install.sh diff --git a/install/swizzin-install.sh b/install/swizzin-install.sh new file mode 100644 index 000000000..6cdca80d6 --- /dev/null +++ b/install/swizzin-install.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: EEJoshua +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://swizzin.ltd/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt-get install -y curl wget gnupg lsb-release +msg_ok "Dependencies installed" +echo -e "Launching upstream Swizzin installer..." +bash <(curl -sL s5n.sh) +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" + +msg_ok "Swizzin base installation complete. Re‑login and run 'sudo box' to add apps." From 64444efb157291f3b035af02aa8434d603c2718d Mon Sep 17 00:00:00 2001 From: Joshua Shabanov Date: Fri, 1 Aug 2025 20:16:31 +0300 Subject: [PATCH 0315/1733] json swizzin.json --- frontend/public/json/swizzin.json | 48 +++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 frontend/public/json/swizzin.json diff --git a/frontend/public/json/swizzin.json b/frontend/public/json/swizzin.json new file mode 100644 index 000000000..2f08f31f6 --- /dev/null +++ b/frontend/public/json/swizzin.json @@ -0,0 +1,48 @@ +{ + "name": "Swizzin", + "slug": "swizzin", + "categories": [ + 15 + ], + "date_created": "2025-08-01", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 80, + "documentation": "https://swizzin.ltd/getting-started", + "config_path": "/etc/swizzin/", + "website": "https://swizzin.ltd/", + "logo": "https://swizzin.ltd/img/logo-sm.png", + "description": "Swizzin is a light-weight, modular, and user-friendly seedbox solution for Debian-based servers. It allows for the easy installation and management of a wide variety of applications commonly used for torrenting and media management, such as rTorrent, Sonarr, Radarr, and Plex, all accessible through a command-line utility or a web-based dashboard.", + "install_methods": [ + { + "type": "default", + "script": "ct/swizzin.sh", + "resources": { + "cpu": 2, + "ram": 4096, + "hdd": 20, + "os": "Debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "It is very recommended to install at least the 'panel' for web access, and 'nginx' for easy access to other apps.", + "type": "warning" + }, + { + "text": "Installation might take a long time if choosing to install many apps. Be patient.", + "type": "warning" + }, + { + "text": "Swizzin is a management suite, not a single application. Use the 'box' command inside the container to install/manage individual apps like rTorrent, Sonarr, etc. A full list can be found in documentation.", + "type": "info" + } + ] +} \ No newline at end of file From 3e4541df00ea7cd871a2fa0267616695f484c600 Mon Sep 17 00:00:00 2001 From: Javier Pastor Date: Sun, 27 Jul 2025 20:11:55 +0200 Subject: [PATCH 0316/1733] Remove commercial modules & add verbose install - Added option to remove commercial modules - Added option to update system and modules --- ct/freepbx.sh | 67 ++++++++++++++++++ frontend/public/json/freepbx.json | 40 +++++++++++ install/freepbx-install.sh | 111 ++++++++++++++++++++++++++++++ misc/build.func | 20 +++--- 4 files changed, 228 insertions(+), 10 deletions(-) create mode 100644 ct/freepbx.sh create mode 100644 frontend/public/json/freepbx.json create mode 100644 install/freepbx-install.sh diff --git a/ct/freepbx.sh b/ct/freepbx.sh new file mode 100644 index 000000000..d7526bcda --- /dev/null +++ b/ct/freepbx.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +source <(curl -s https://raw.githubusercontent.com/vsc55/community-scripts-ProxmoxVED/refs/heads/freepbx/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Arian Nasr (arian-nasr) +# Updated by: Javier Pastor (vsc55) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://www.freepbx.org/ + +APP="FreePBX" +var_tags="pbx;voip;telephony" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-10}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -f /lib/systemd/system/freepbx.service ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + msg_info "Updating $APP LXC" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Updated $APP LXC" + + msg_info "Updating $APP Modules" + $STD fwconsole ma updateall + $STD fwconsole reload + msg_ok "Updated $APP Modules" + + exit +} + +start + +if whiptail --title "Commercial Modules" --yesno "Remove Commercial modules?" --defaultno 10 50; then + export ONLY_OPENSOURCE="yes" + + if whiptail --title "Firewall Module" --yesno "Do you want to KEEP the Firewall module (and sysadmin)?" 10 50; then + export REMOVE_FIREWALL="no" + else + export REMOVE_FIREWALL="yes" + fi +else + export ONLY_OPENSOURCE="no" + export REMOVE_FIREWALL="no" +fi + +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" diff --git a/frontend/public/json/freepbx.json b/frontend/public/json/freepbx.json new file mode 100644 index 000000000..11040c040 --- /dev/null +++ b/frontend/public/json/freepbx.json @@ -0,0 +1,40 @@ +{ + "name": "FreePBX", + "slug": "freepbx", + "categories": [ + 0 + ], + "date_created": "2025-05-22", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 80, + "documentation": "https://sangomakb.atlassian.net/wiki/spaces/FP/overview?homepageId=8454359", + "website": "https://www.freepbx.org/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/freepbx.webp", + "config_path": "", + "description": "FreePBX is a web-based open-source graphical user interface that manages Asterisk, a voice over IP and telephony server.", + "install_methods": [ + { + "type": "default", + "script": "ct/freepbx.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 10, + "os": "debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "This script uses the official FreePBX install script. Check it here: https://github.com/FreePBX/sng_freepbx_debian_install", + "type": "info" + } + ] +} diff --git a/install/freepbx-install.sh b/install/freepbx-install.sh new file mode 100644 index 000000000..c5da11380 --- /dev/null +++ b/install/freepbx-install.sh @@ -0,0 +1,111 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Arian Nasr (arian-nasr) +# Updated by: Javier Pastor (vsc55) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://www.freepbx.org/ + +INSTALL_URL="https://github.com/FreePBX/sng_freepbx_debian_install/raw/master/sng_freepbx_debian_install.sh" +INSTALL_PATH="/opt/sng_freepbx_debian_install.sh" + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +ONLY_OPENSOURCE="${ONLY_OPENSOURCE:-no}" +REMOVE_FIREWALL="${REMOVE_FIREWALL:-no}" +msg_ok "Remove Commercial modules is set to: $ONLY_OPENSOURCE" +msg_ok "Remove Firewall module is set to: $REMOVE_FIREWALL" + +msg_info "Downloading FreePBX installation script..." +if curl -fsSL "$INSTALL_URL" -o "$INSTALL_PATH"; then + msg_ok "Download completed successfully" +else + curl_exit_code=$? + msg_error "Error downloading FreePBX installation script (curl exit code: $curl_exit_code)" + msg_error "Aborting!" + exit 1 +fi + +if [[ "$VERBOSE" == "yes" ]]; then + msg_info "Installing FreePBX (Verbose)\n" +else + msg_info "Installing FreePBX, be patient, this takes time..." +fi +$STD bash "$INSTALL_PATH" + +if [[ $ONLY_OPENSOURCE == "yes" ]]; then + msg_info "Removing Commercial modules..." + + end_count=0 + max=5 + count=0 + while fwconsole ma list | awk '/Commercial/ {found=1} END {exit !found}'; do + count=$((count + 1)) + while read -r module; do + msg_info "Removing module: $module" + + if [[ "$REMOVE_FIREWALL" == "no" ]] && [[ "$module" == "sysadmin" ]]; then + msg_warn "Skipping sysadmin module removal, it is required for Firewall!" + continue + fi + + code=0 + $STD fwconsole ma -f remove $module || code=$? + if [[ $code -ne 0 ]]; then + msg_error "Module $module could not be removed - error code $code" + else + msg_ok "Module $module removed successfully" + fi + done < <(fwconsole ma list | awk '/Commercial/ {print $2}') + + [[ $count -ge $max ]] && break + + com_list=$(fwconsole ma list) + end_count=$(awk '/Commercial/ {count++} END {print count + 0}' <<< "$com_list") + awk '/Commercial/ {found=1} END {exit !found}' <<< "$com_list" || break + if [[ "$REMOVE_FIREWALL" == "no" ]] && \ + [[ $end_count -eq 1 ]] && \ + [[ $(awk '/Commercial/ {print $2}' <<< "$com_list") == "sysadmin" ]]; then + break + fi + + msg_warn "Not all commercial modules could be removed, retrying (attempt $count of $max)..." + done + + if [[ $REMOVE_FIREWALL == "yes" ]] && [[ $end_count -gt 0 ]]; then + msg_info "Removing Firewall module..." + if $STD fwconsole ma -f remove firewall; then + msg_ok "Firewall module removed successfully" + else + msg_error "Firewall module could not be removed, please check manually!" + fi + fi + + if [[ $end_count -eq 0 ]]; then + msg_ok "All commercial modules removed successfully" + elif [[ $end_count -eq 1 ]] && [[ $REMOVE_FIREWALL == "no" ]] && [[ $(fwconsole ma list | awk '/Commercial/ {print $2}') == "sysadmin" ]]; then + msg_ok "Only sysadmin module left, which is required for Firewall, skipping removal" + else + msg_warn "Some commercial modules could not be removed, please check the web interface for removal manually!" + fi + + msg_info "Reloading FreePBX..." + $STD fwconsole reload + msg_ok "FreePBX reloaded completely" +fi +msg_ok "Installed FreePBX finished" + +motd_ssh +customize + +msg_info "Cleaning up" +rm -f "$INSTALL_PATH" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" diff --git a/misc/build.func b/misc/build.func index c8b1ff4b6..0364736f2 100644 --- a/misc/build.func +++ b/misc/build.func @@ -16,14 +16,14 @@ variables() { CT_TYPE=${var_unprivileged:-$CT_TYPE} } -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) +source <(curl -fsSL https://raw.githubusercontent.com/vsc55/community-scripts-ProxmoxVED/refs/heads/freepbx/misc/api.func) if command -v curl >/dev/null 2>&1; then - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + source <(curl -fsSL https://raw.githubusercontent.com/vsc55/community-scripts-ProxmoxVED/refs/heads/freepbx/misc/core.func) load_functions #echo "(build.func) Loaded core.func via curl" elif command -v wget >/dev/null 2>&1; then - source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + source <(wget -qO- https://raw.githubusercontent.com/vsc55/community-scripts-ProxmoxVED/refs/heads/freepbx/misc/core.func) load_functions #echo "(build.func) Loaded core.func via wget" fi @@ -986,7 +986,7 @@ install_script() { header_info echo -e "${INFO}${HOLD} ${GN}Using Config File on node $PVEHOST_NAME${CL}" METHOD="advanced" - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/config-file.func) + source <(curl -fsSL https://raw.githubusercontent.com/vsc55/community-scripts-ProxmoxVED/refs/heads/freepbx/misc/config-file.func) config_file ;; 5) @@ -1059,7 +1059,7 @@ check_container_storage() { } start() { - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) + source <(curl -fsSL https://raw.githubusercontent.com/vsc55/community-scripts-ProxmoxVED/refs/heads/freepbx/misc/tools.func) if command -v pveversion >/dev/null 2>&1; then install_script elif [ ! -z ${PHS_SILENT+x} ] && [[ "${PHS_SILENT}" == "1" ]]; then @@ -1125,9 +1125,9 @@ build_container() { TEMP_DIR=$(mktemp -d) pushd "$TEMP_DIR" >/dev/null if [ "$var_os" == "alpine" ]; then - export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-install.func)" + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://raw.githubusercontent.com/vsc55/community-scripts-ProxmoxVED/refs/heads/freepbx/misc/alpine-install.func)" else - export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://raw.githubusercontent.com/vsc55/community-scripts-ProxmoxVED/refs/heads/freepbx/misc/install.func)" fi export DIAGNOSTICS="$DIAGNOSTICS" export RANDOM_UUID="$RANDOM_UUID" @@ -1161,7 +1161,7 @@ build_container() { -unprivileged $CT_TYPE $PW " - bash -c "$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/create_lxc.sh)" + bash -c "$(curl -fsSL https://raw.githubusercontent.com/vsc55/community-scripts-ProxmoxVED/refs/heads/freepbx/misc/create_lxc.sh)" if [ $? -ne 0 ]; then exit 200 fi @@ -1351,7 +1351,7 @@ EOF' fi msg_ok "Customized LXC Container" - if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/"$var_install".sh)"; then + if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/vsc55/community-scripts-ProxmoxVED/refs/heads/freepbx/install/"$var_install".sh)"; then exit $? fi } @@ -1365,7 +1365,7 @@ description() { cat < - Logo + Logo

${APP} LXC

From 70f35283fa7053a826f02a9a4b104cbf2973aa63 Mon Sep 17 00:00:00 2001 From: Javier Pastor Date: Sun, 3 Aug 2025 13:02:19 +0200 Subject: [PATCH 0317/1733] revert build.func --- misc/build.func | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/misc/build.func b/misc/build.func index 0364736f2..c8b1ff4b6 100644 --- a/misc/build.func +++ b/misc/build.func @@ -16,14 +16,14 @@ variables() { CT_TYPE=${var_unprivileged:-$CT_TYPE} } -source <(curl -fsSL https://raw.githubusercontent.com/vsc55/community-scripts-ProxmoxVED/refs/heads/freepbx/misc/api.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) if command -v curl >/dev/null 2>&1; then - source <(curl -fsSL https://raw.githubusercontent.com/vsc55/community-scripts-ProxmoxVED/refs/heads/freepbx/misc/core.func) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) load_functions #echo "(build.func) Loaded core.func via curl" elif command -v wget >/dev/null 2>&1; then - source <(wget -qO- https://raw.githubusercontent.com/vsc55/community-scripts-ProxmoxVED/refs/heads/freepbx/misc/core.func) + source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) load_functions #echo "(build.func) Loaded core.func via wget" fi @@ -986,7 +986,7 @@ install_script() { header_info echo -e "${INFO}${HOLD} ${GN}Using Config File on node $PVEHOST_NAME${CL}" METHOD="advanced" - source <(curl -fsSL https://raw.githubusercontent.com/vsc55/community-scripts-ProxmoxVED/refs/heads/freepbx/misc/config-file.func) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/config-file.func) config_file ;; 5) @@ -1059,7 +1059,7 @@ check_container_storage() { } start() { - source <(curl -fsSL https://raw.githubusercontent.com/vsc55/community-scripts-ProxmoxVED/refs/heads/freepbx/misc/tools.func) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) if command -v pveversion >/dev/null 2>&1; then install_script elif [ ! -z ${PHS_SILENT+x} ] && [[ "${PHS_SILENT}" == "1" ]]; then @@ -1125,9 +1125,9 @@ build_container() { TEMP_DIR=$(mktemp -d) pushd "$TEMP_DIR" >/dev/null if [ "$var_os" == "alpine" ]; then - export FUNCTIONS_FILE_PATH="$(curl -fsSL https://raw.githubusercontent.com/vsc55/community-scripts-ProxmoxVED/refs/heads/freepbx/misc/alpine-install.func)" + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-install.func)" else - export FUNCTIONS_FILE_PATH="$(curl -fsSL https://raw.githubusercontent.com/vsc55/community-scripts-ProxmoxVED/refs/heads/freepbx/misc/install.func)" + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" fi export DIAGNOSTICS="$DIAGNOSTICS" export RANDOM_UUID="$RANDOM_UUID" @@ -1161,7 +1161,7 @@ build_container() { -unprivileged $CT_TYPE $PW " - bash -c "$(curl -fsSL https://raw.githubusercontent.com/vsc55/community-scripts-ProxmoxVED/refs/heads/freepbx/misc/create_lxc.sh)" + bash -c "$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/create_lxc.sh)" if [ $? -ne 0 ]; then exit 200 fi @@ -1351,7 +1351,7 @@ EOF' fi msg_ok "Customized LXC Container" - if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/vsc55/community-scripts-ProxmoxVED/refs/heads/freepbx/install/"$var_install".sh)"; then + if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/"$var_install".sh)"; then exit $? fi } @@ -1365,7 +1365,7 @@ description() { cat < - Logo + Logo

${APP} LXC

From c62a38a9d4c647b6e5aff34df36bf15966c40ae8 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Sun, 3 Aug 2025 19:54:15 +0200 Subject: [PATCH 0318/1733] test --- ct/ots.sh | 54 +++++++++++++++++------------------ frontend/public/json/ots | 40 -------------------------- frontend/public/json/ots.json | 40 ++++++++++++++++++++++++++ install/ots-install.sh | 21 +++++++------- 4 files changed, 77 insertions(+), 78 deletions(-) delete mode 100644 frontend/public/json/ots create mode 100644 frontend/public/json/ots.json diff --git a/ct/ots.sh b/ct/ots.sh index 26a224e87..9c711ae71 100644 --- a/ct/ots.sh +++ b/ct/ots.sh @@ -20,35 +20,33 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/ots ]]; then - msg_error "No ${APP} Installation Found!" + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/ots ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + RELEASE=$(curl -fsSL https://api.github.com/repos/Luzifer/ots/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + if [[ "${RELEASE}" != "$(cat ~/.ots 2>/dev/null)" ]] || [[ ! -f ~/.ots ]]; then + + msg_info "Stopping ${APP} Service" + systemctl stop ots + systemctl stop nginx + msg_ok "Stopped ${APP} Service" + + fetch_and_deploy_gh_release "ots" "Luzifer/ots" "prebuild" "latest" "/opt/ots" "ots_linux_amd64.tgz" + + msg_info "Stopping ${APP} Service" + systemctl start ots + systemctl start nginx + msg_ok "Stopped ${APP} Service" + + else + msg_ok "No update required. ${APP} is already at ${RELEASE}" + fi exit - fi - - RELEASE=$(curl -fsSL https://api.github.com/repos/Luzifer/ots/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - if [[ "${RELEASE}" != "$(cat ~/.ots 2>/dev/null)" ]] || [[ ! -f ~/.ots ]]; then - - msg_info "Stopping ${APP} Service" - systemctl stop ots - systemctl stop nginx - msg_ok "Stopped ${APP} Service" - - msg_info "Updating ${APP} to v${RELEASE}" - fetch_and_deploy_gh_release "ots" "Luzifer/ots" "prebuild" "latest" "/opt/ots" "ots_linux_amd64.tgz" - msg_ok "Updated ${APP} to v${RELEASE}" - - msg_info "Stopping ${APP} Service" - systemctl start ots - systemctl start nginx - msg_ok "Stopped ${APP} Service" - - else - msg_ok "No update required. ${APP} is already at ${RELEASE}" - fi - exit } start diff --git a/frontend/public/json/ots b/frontend/public/json/ots deleted file mode 100644 index a9f042f9f..000000000 --- a/frontend/public/json/ots +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "OTS", - "slug": "ots", - "categories": [ - 6 - ], - "date_created": "2025-07-28", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 443, - "documentation": "https://github.com/Luzifer/ots/wiki", - "config_path": "/opt/ots/env", - "website": "https://github.com/Luzifer/ots", - "logo": null, - "description": "One-Time-Secret sharing platform with a symmetric 256bit AES encryption in the browser.", - "install_methods": [ - { - "type": "default", - "script": "ct/ots.sh", - "resources": { - "cpu": 1, - "ram": 512, - "hdd": 3, - "os": "Debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "When it is in used external please use it behind reverse proxy or create your own certificates", - "type": "info" - } - ] -} diff --git a/frontend/public/json/ots.json b/frontend/public/json/ots.json new file mode 100644 index 000000000..2bec84e8c --- /dev/null +++ b/frontend/public/json/ots.json @@ -0,0 +1,40 @@ +{ + "name": "OTS", + "slug": "ots", + "categories": [ + 6 + ], + "date_created": "2025-07-28", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 443, + "documentation": "https://github.com/Luzifer/ots/wiki", + "config_path": "/opt/ots/.env", + "website": "https://github.com/Luzifer/ots", + "logo": null, + "description": "One-Time-Secret sharing platform with a symmetric 256bit AES encryption in the browser.", + "install_methods": [ + { + "type": "default", + "script": "ct/ots.sh", + "resources": { + "cpu": 1, + "ram": 512, + "hdd": 3, + "os": "Debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "When it is in used external please use it behind reverse proxy or create your own certificates", + "type": "info" + } + ] +} diff --git a/install/ots-install.sh b/install/ots-install.sh index 6bbfe81f5..526d64bd1 100644 --- a/install/ots-install.sh +++ b/install/ots-install.sh @@ -15,27 +15,28 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - redis-server \ - nginx \ - openssl + redis-server \ + nginx \ + openssl msg_ok "Installed Dependencies" -msg_info "Installing OTS" fetch_and_deploy_gh_release "ots" "Luzifer/ots" "prebuild" "latest" "/opt/ots" "ots_linux_amd64.tgz" -cat </opt/ots/env + +msg_info "Setup OTS" +cat </opt/ots/.env LISTEN=127.0.0.1:3000 REDIS_URL=redis://127.0.0.1:6379 SECRET_EXPIRY=604800 STORAGE_TYPE=redis EOF -msg_ok "Installed OTS" +msg_ok "Setup OTS" msg_info "Generating Universal SSL Certificate" mkdir -p /etc/ssl/ots $STD openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \ - -keyout /etc/ssl/ots/key.pem \ - -out /etc/ssl/ots/cert.pem \ - -subj "/CN=ots" + -keyout /etc/ssl/ots/key.pem \ + -out /etc/ssl/ots/cert.pem \ + -subj "/CN=ots" msg_ok "Certificate Generated" msg_info "Setting up nginx" @@ -82,7 +83,7 @@ After=network-online.target Requires=network-online.target [Service] -EnvironmentFile=/opt/ots/env +EnvironmentFile=/opt/ots/.env ExecStart=/opt/ots/ots Restart=Always RestartSecs=5 From 65577e6e5f9bbeba0865012e0fcede1fb06aa3ce Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sun, 3 Aug 2025 17:55:56 +0000 Subject: [PATCH 0319/1733] Update .app files --- ct/headers/proxmox-backup-server | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/proxmox-backup-server diff --git a/ct/headers/proxmox-backup-server b/ct/headers/proxmox-backup-server new file mode 100644 index 000000000..8536d112d --- /dev/null +++ b/ct/headers/proxmox-backup-server @@ -0,0 +1,6 @@ + ____ ____ __ _____ + / __ \_________ _ ______ ___ ____ _ __ / __ )____ ______/ /____ ______ / ___/___ ______ _____ _____ + / /_/ / ___/ __ \| |/_/ __ `__ \/ __ \| |/_/_____/ __ / __ `/ ___/ //_/ / / / __ \______\__ \/ _ \/ ___/ | / / _ \/ ___/ + / ____/ / / /_/ /> Date: Sun, 3 Aug 2025 20:04:00 +0200 Subject: [PATCH 0320/1733] fixes --- install/proxmox-backup-server-install.sh | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/install/proxmox-backup-server-install.sh b/install/proxmox-backup-server-install.sh index 502ea9e95..ee20e1dd6 100644 --- a/install/proxmox-backup-server-install.sh +++ b/install/proxmox-backup-server-install.sh @@ -13,20 +13,20 @@ setting_up_container network_check update_os -msg_info "Installing Proxmox Backup Server" read -rp "${TAB3}Do you want to use the Enterprise repository (requires valid subscription key)? [y/N]: " USE_ENTERPRISE_REPO +msg_info "Installing Proxmox Backup Server" if [[ "$USE_ENTERPRISE_REPO" =~ ^([yY].*)$ ]]; then - echo "deb https://enterprise.proxmox.com/debian/pbs bookworm pbs-enterprise" >/etc/apt/sources.list.d/pbs-enterprise.list - sed -i '/pbs-no-subscription/s/^/#/' /etc/apt/sources.list - msg_custom "Enterprise repository enabled. Make sure your subscription key is installed." + echo "deb https://enterprise.proxmox.com/debian/pbs bookworm pbs-enterprise" >/etc/apt/sources.list.d/pbs-enterprise.list + sed -i '/pbs-no-subscription/s/^/#/' /etc/apt/sources.list + msg_custom "Enterprise repository enabled. Make sure your subscription key is installed." else - if ! grep -q "pbs-no-subscription" /etc/apt/sources.list; then - echo "deb http://download.proxmox.com/debian/pbs bookworm pbs-no-subscription" >>/etc/apt/sources.list - else - sed -i '/pbs-no-subscription/s/^#//' /etc/apt/sources.list - fi - rm -f /etc/apt/sources.list.d/pbs-enterprise.list + if ! grep -q "pbs-no-subscription" /etc/apt/sources.list; then + echo "deb http://download.proxmox.com/debian/pbs bookworm pbs-no-subscription" >>/etc/apt/sources.list + else + sed -i '/pbs-no-subscription/s/^#//' /etc/apt/sources.list + fi + rm -f /etc/apt/sources.list.d/pbs-enterprise.list fi $STD apt-get update From 0b0656795d7c58221993f2919a121d3a14e3b7cf Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Sun, 3 Aug 2025 20:10:59 +0200 Subject: [PATCH 0321/1733] fixes --- install/proxmox-backup-server-install.sh | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/install/proxmox-backup-server-install.sh b/install/proxmox-backup-server-install.sh index ee20e1dd6..cac095b99 100644 --- a/install/proxmox-backup-server-install.sh +++ b/install/proxmox-backup-server-install.sh @@ -16,17 +16,14 @@ update_os read -rp "${TAB3}Do you want to use the Enterprise repository (requires valid subscription key)? [y/N]: " USE_ENTERPRISE_REPO msg_info "Installing Proxmox Backup Server" +curl -fsSL https://enterprise.proxmox.com/debian/proxmox-release-bookworm.gpg | + gpg --dearmor -o /etc/apt/trusted.gpg.d/proxmox-release-bookworm.gpg if [[ "$USE_ENTERPRISE_REPO" =~ ^([yY].*)$ ]]; then echo "deb https://enterprise.proxmox.com/debian/pbs bookworm pbs-enterprise" >/etc/apt/sources.list.d/pbs-enterprise.list - sed -i '/pbs-no-subscription/s/^/#/' /etc/apt/sources.list - msg_custom "Enterprise repository enabled. Make sure your subscription key is installed." + msg_ok "Enterprise repository enabled. Make sure your subscription key is installed." else - if ! grep -q "pbs-no-subscription" /etc/apt/sources.list; then - echo "deb http://download.proxmox.com/debian/pbs bookworm pbs-no-subscription" >>/etc/apt/sources.list - else - sed -i '/pbs-no-subscription/s/^#//' /etc/apt/sources.list - fi - rm -f /etc/apt/sources.list.d/pbs-enterprise.list + echo "deb http://download.proxmox.com/debian/pbs bookworm pbs-no-subscription" >>/etc/apt/sources.list + msg_ok "No-subscription repository enabled." fi $STD apt-get update From 8b4f82b44b2f9c764bbe2efb7651cdb93d12a789 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sun, 3 Aug 2025 16:43:33 -0400 Subject: [PATCH 0322/1733] update opencloud-install --- install/opencloud-install.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/install/opencloud-install.sh b/install/opencloud-install.sh index 22ee79387..16d7f3845 100644 --- a/install/opencloud-install.sh +++ b/install/opencloud-install.sh @@ -125,9 +125,8 @@ WEB_ASSET_APPS_PATH=${CONFIG_DIR}/assets/apps # SEARCH_EXTRACTOR_TIKA_TIKA_URL= # External storage test - currently not working -# STORAGE_USERS_POSIX_ROOT= -# STORAGE_USERS_DECOMPOSED_ROOT= -# STORAGE_SYSTEM_OC_ROOT= # this definitely breaks shit, wouldn't ever change it +# STORAGE_USERS_POSIX_ROOT= +# STORAGE_USERS_ID_CACHE_STORE=nats-js-kv EOF cat </etc/systemd/system/opencloud.service From 212c5ac2358a0ef9c03cfe829f5a8f24be43f24c Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sun, 3 Aug 2025 20:15:54 -0400 Subject: [PATCH 0323/1733] Add Palmr --- ct/palmr.sh | 75 +++++++++++++++++++++++++++ frontend/public/json/palmr.json | 39 +++++++++++++++ install/palmr-install.sh | 89 +++++++++++++++++++++++++++++++++ 3 files changed, 203 insertions(+) create mode 100644 ct/palmr.sh create mode 100644 frontend/public/json/palmr.json create mode 100644 install/palmr-install.sh diff --git a/ct/palmr.sh b/ct/palmr.sh new file mode 100644 index 000000000..a9a7297e0 --- /dev/null +++ b/ct/palmr.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: vhsdream +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/kyantech/Palmr + +APP="Palmr" +var_tags="${var_tags:-files}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-4096}" +var_disk="${var_disk:-6}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/palmr_data ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + RELEASE=$(curl -fsSL https://api.github.com/repos/kyantech/palmr/releases/latest | yq '.tag_name' | sed 's/^v//') + if [[ "${RELEASE}" != "$(cat ~/.palmr 2>/dev/null)" ]] || [[ ! -f ~/.palmr ]]; then + msg_info "Stopping Services" + systemctl stop palmr-frontend palmr-backend + msg_ok "Stopped Services" + + msg_info "Updating ${APP}" + cp /opt/palmr/apps/server/.env /opt/palmr.env + fetch_and_deploy_gh_release "Palmr" "kyantech/Palmr" "tarball" "latest" "/opt/palmr" + cd /opt/palmr/apps/server + PALMR_DIR="/opt/palmr_data" + # export PALMR_DB="${PALMR_DIR}/palmr.db" + sed -i "s|/app/server|$PALMR_DIR|" ./src/config/directories.config.ts + $STD pnpm install + mv /opt/palmr.env ./.env + $STD pnpm dlx prisma generate + $STD pnpm dlx prisma migrate deploy + $STD pnpm build + + cd /opt/palmr/apps/web + export NODE_ENV=production + export NEXT_TELEMETRY_DISABLED=1 + $STD pnpm install + $STD pnpm build + msg_ok "Updated $APP" + + msg_info "Starting Services" + systemctl start palmr-backend palmr-frontend + msg_ok "Started Services" + + msg_ok "Updated Successfully" + else + msg_ok "Already up to date" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" diff --git a/frontend/public/json/palmr.json b/frontend/public/json/palmr.json new file mode 100644 index 000000000..7dddd14fa --- /dev/null +++ b/frontend/public/json/palmr.json @@ -0,0 +1,39 @@ +{ + "name": "Palmr", + "slug": "palmr", + "categories": [ + 11 + ], + "date_created": "2025-08-03", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 3000, + "documentation": "https://palmr.kyantech.com.br/docs/3.1-beta", + "config_path": "/opt/palmr/backend.env, /opt/palmr/frontend.env", + "website": "https://palmr.kyantech.com.br/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/palmr.webp", + "description": "Palmr is a fast and secure platform for sharing files, built with performance and privacy in mind.", + "install_methods": [ + { + "type": "default", + "script": "ct/palmr.sh", + "resources": { + "cpu": 2, + "ram": 4096, + "hdd": 6, + "os": "Debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": , + "password": , + "notes": [ + { + "text": "Info here", + "type": "info" + } + ] +} diff --git a/install/palmr-install.sh b/install/palmr-install.sh new file mode 100644 index 000000000..33ebde486 --- /dev/null +++ b/install/palmr-install.sh @@ -0,0 +1,89 @@ +#!/usr/bin/env bash + +# Copyright (c) 2025 Community Scripts ORG +# Author: vhsdream +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/kyantech/Palmr + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing dependencies" +$STD apt-get install -y yq +msg_ok "Installed dependencies" + +NODE_VERSION="20" setup_nodejs + +fetch_and_deploy_gh_release "Palmr" "kyantech/Palmr" "tarball" "latest" "/opt/palmr" +msg_info "Configuring palmr backend" +PALMR_DIR="/opt/palmr_data" +mkdir -p "$PALMR_DIR" +PALMR_DB="${PALMR_DIR}/palmr.db" +PALMR_KEY="$(openssl rand -hex 32)" +cd /opt/palmr/apps/server +sed -e 's/_ENCRYPTION=true/_ENCRYPTION=false/' \ + -e '/^# ENC/s/# //' \ + -e "s/ENCRYPTION_KEY=.*$/ENCRYPTION_KEY=$PALMR_KEY/" \ + -e "s|file:.*$|file:$PALMR_DB\"|" \ + .env.example >./.env +sed -i "s|/app/server|$PALMR_DIR|" ./src/config/directories.config.ts +$STD pnpm install +$STD pnpm dlx prisma generate +$STD pnpm dlx prisma migrate deploy +$STD pnpm dlx prisma db push # This was missing from the docs +$STD pnpm db:seed +$STD pnpm build +msg_ok "Configured palmr backend" + +msg_info "Configuring palmr frontend" +cd /opt/palmr/apps/web +mv ./.env.example ./.env +export NODE_ENV=production +export NEXT_TELEMETRY_DISABLED=1 +$STD pnpm install +$STD pnpm build +msg_ok "Configured palmr frontend" + +msg_info "Creating service files" +cat </etc/systemd/system/palmr-backend.service +[Unit] +Description=palmr Backend Service +After=network.target + +[Service] +Type=simple +WorkingDirectory=/opt/palmr/apps/server +ExecStart=/usr/bin/pnpm start + +[Install] +WantedBy=multi-user.target +EOF + +cat </etc/systemd/system/palmr-frontend.service +[Unit] +Description=palmr Frontend Service +After=network.target palmr-backend.service + +[Service] +Type=simple +WorkingDirectory=/opt/palmr/apps/web +ExecStart=/usr/bin/pnpm start + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now palmr-backend palmr-frontend +msg_ok "Created services" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 3e40b1e829a0220c6fe0695590056b06eaf9969f Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sun, 3 Aug 2025 20:17:37 -0400 Subject: [PATCH 0324/1733] fix JSON --- frontend/public/json/palmr.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/public/json/palmr.json b/frontend/public/json/palmr.json index 7dddd14fa..07435e321 100644 --- a/frontend/public/json/palmr.json +++ b/frontend/public/json/palmr.json @@ -28,8 +28,8 @@ } ], "default_credentials": { - "username": , - "password": , + "username": null, + "password": null, "notes": [ { "text": "Info here", From cf628a46358a944a930c992d14c8bc2bf607ab54 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sun, 3 Aug 2025 20:19:54 -0400 Subject: [PATCH 0325/1733] remove comma --- frontend/public/json/palmr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/palmr.json b/frontend/public/json/palmr.json index 07435e321..74d351126 100644 --- a/frontend/public/json/palmr.json +++ b/frontend/public/json/palmr.json @@ -29,7 +29,7 @@ ], "default_credentials": { "username": null, - "password": null, + "password": null "notes": [ { "text": "Info here", From 82ce5d0647f1c89303cb31bff397f17597733199 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sun, 3 Aug 2025 20:21:29 -0400 Subject: [PATCH 0326/1733] add missing closing brace --- frontend/public/json/palmr.json | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/public/json/palmr.json b/frontend/public/json/palmr.json index 74d351126..2f5771180 100644 --- a/frontend/public/json/palmr.json +++ b/frontend/public/json/palmr.json @@ -30,6 +30,7 @@ "default_credentials": { "username": null, "password": null + }, "notes": [ { "text": "Info here", From b91580ce9d4c823728b4ed68f22a0da7aa2f85a7 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sun, 3 Aug 2025 20:45:02 -0400 Subject: [PATCH 0327/1733] Palmr: ensure pnpm installed --- ct/palmr.sh | 4 ++++ install/palmr-install.sh | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/ct/palmr.sh b/ct/palmr.sh index a9a7297e0..5722ecdb7 100644 --- a/ct/palmr.sh +++ b/ct/palmr.sh @@ -36,7 +36,10 @@ function update_script() { msg_info "Updating ${APP}" cp /opt/palmr/apps/server/.env /opt/palmr.env + rm -rf /opt/palmr fetch_and_deploy_gh_release "Palmr" "kyantech/Palmr" "tarball" "latest" "/opt/palmr" + PNPM="$(jq -r '.packageManager' /opt/palmr/package.json)" + NODE_VERSION="20" NODE_MODULE="$PNPM" setup_nodejs cd /opt/palmr/apps/server PALMR_DIR="/opt/palmr_data" # export PALMR_DB="${PALMR_DIR}/palmr.db" @@ -50,6 +53,7 @@ function update_script() { cd /opt/palmr/apps/web export NODE_ENV=production export NEXT_TELEMETRY_DISABLED=1 + mv ./.env.example ./.env $STD pnpm install $STD pnpm build msg_ok "Updated $APP" diff --git a/install/palmr-install.sh b/install/palmr-install.sh index 33ebde486..7d7856a5a 100644 --- a/install/palmr-install.sh +++ b/install/palmr-install.sh @@ -17,9 +17,10 @@ msg_info "Installing dependencies" $STD apt-get install -y yq msg_ok "Installed dependencies" -NODE_VERSION="20" setup_nodejs - fetch_and_deploy_gh_release "Palmr" "kyantech/Palmr" "tarball" "latest" "/opt/palmr" +PNPM="$(jq -r '.packageManager' /opt/palmr/package.json)" +NODE_VERSION="20" NODE_MODULE="$PNPM" setup_nodejs + msg_info "Configuring palmr backend" PALMR_DIR="/opt/palmr_data" mkdir -p "$PALMR_DIR" From ffc5c67a6a06a411f589d113aca10f95ab9db964 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sun, 3 Aug 2025 21:49:41 -0400 Subject: [PATCH 0328/1733] Palmr: bypass pnpm for running backend --- ct/palmr.sh | 1 - install/palmr-install.sh | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/ct/palmr.sh b/ct/palmr.sh index 5722ecdb7..fd4c41a10 100644 --- a/ct/palmr.sh +++ b/ct/palmr.sh @@ -43,7 +43,6 @@ function update_script() { cd /opt/palmr/apps/server PALMR_DIR="/opt/palmr_data" # export PALMR_DB="${PALMR_DIR}/palmr.db" - sed -i "s|/app/server|$PALMR_DIR|" ./src/config/directories.config.ts $STD pnpm install mv /opt/palmr.env ./.env $STD pnpm dlx prisma generate diff --git a/install/palmr-install.sh b/install/palmr-install.sh index 7d7856a5a..d5ed53a55 100644 --- a/install/palmr-install.sh +++ b/install/palmr-install.sh @@ -32,7 +32,6 @@ sed -e 's/_ENCRYPTION=true/_ENCRYPTION=false/' \ -e "s/ENCRYPTION_KEY=.*$/ENCRYPTION_KEY=$PALMR_KEY/" \ -e "s|file:.*$|file:$PALMR_DB\"|" \ .env.example >./.env -sed -i "s|/app/server|$PALMR_DIR|" ./src/config/directories.config.ts $STD pnpm install $STD pnpm dlx prisma generate $STD pnpm dlx prisma migrate deploy @@ -58,8 +57,8 @@ After=network.target [Service] Type=simple -WorkingDirectory=/opt/palmr/apps/server -ExecStart=/usr/bin/pnpm start +WorkingDirectory=/opt/palmr_data +ExecStart=/usr/bin/node /opt/palmr/apps/server/dist/server.js [Install] WantedBy=multi-user.target From bf7544d2f0906a8b349324ca007a297acc4165fc Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 4 Aug 2025 01:50:07 +0000 Subject: [PATCH 0329/1733] Update .app files --- ct/headers/palmr | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/palmr diff --git a/ct/headers/palmr b/ct/headers/palmr new file mode 100644 index 000000000..c485f3c60 --- /dev/null +++ b/ct/headers/palmr @@ -0,0 +1,6 @@ + ____ __ + / __ \____ _/ /___ ___ _____ + / /_/ / __ `/ / __ `__ \/ ___/ + / ____/ /_/ / / / / / / / / +/_/ \__,_/_/_/ /_/ /_/_/ + From ad77df1529d757b2b171d2fb3b29a6dbc05e7cac Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sun, 3 Aug 2025 22:40:53 -0400 Subject: [PATCH 0330/1733] Palmr: add env var for reverse proxy option --- install/palmr-install.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/install/palmr-install.sh b/install/palmr-install.sh index d5ed53a55..3a12f0584 100644 --- a/install/palmr-install.sh +++ b/install/palmr-install.sh @@ -31,6 +31,8 @@ sed -e 's/_ENCRYPTION=true/_ENCRYPTION=false/' \ -e '/^# ENC/s/# //' \ -e "s/ENCRYPTION_KEY=.*$/ENCRYPTION_KEY=$PALMR_KEY/" \ -e "s|file:.*$|file:$PALMR_DB\"|" \ + -e '/db"$/a\# Uncomment below when using reverse proxy\ + # SECURE_SITE=true' \ .env.example >./.env $STD pnpm install $STD pnpm dlx prisma generate From 12b0f0337be5dc90329951ec535c975f1518fb17 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 09:26:37 +0200 Subject: [PATCH 0331/1733] cleanup --- ct/tududi.sh | 7 ++++--- install/tududi-install.sh | 6 ++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/ct/tududi.sh b/ct/tududi.sh index f8d4aaec2..f84079894 100644 --- a/ct/tududi.sh +++ b/ct/tududi.sh @@ -34,11 +34,14 @@ function update_script() { systemctl stop tududi msg_ok "Stopped Service" - msg_info "Updating ${APP}" + msg_info "Remove and backup Files" cp /opt/tududi/backend/.env /opt/tududi.env rm -rf /opt/tududi/backend/dist + msg_ok "Backup and removed Files" + fetch_and_deploy_gh_release "tududi" "chrisvel/tududi" + msg_info "Updating ${APP}" cd /opt/tududi $STD npm install export NODE_ENV=production @@ -52,7 +55,6 @@ function update_script() { msg_info "Starting Service" systemctl start tududi msg_ok "Started Service" - msg_ok "Updated Successfully" else msg_ok "Already up to date" @@ -66,6 +68,5 @@ description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${ADVANCED}${BL}Create your initial user in${CL} ${BGN}/opt/tududi${CL}${BL} in the LXC:${CL} ${RD}npm run user:create ${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3002${CL}" diff --git a/install/tududi-install.sh b/install/tududi-install.sh index b3d87d795..d89d8c3d2 100644 --- a/install/tududi-install.sh +++ b/install/tududi-install.sh @@ -14,12 +14,14 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt-get install -y sqlite3 yq +$STD apt-get install -y \ + sqlite3 \ + yq msg_ok "Installed Dependencies" NODE_VERSION="20" setup_nodejs - fetch_and_deploy_gh_release "tududi" "chrisvel/tududi" + msg_info "Configuring Tududi" cd /opt/tududi $STD npm install From bca8a10f7e447e666448497badd3ef2fb9758d2b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 09:38:06 +0200 Subject: [PATCH 0332/1733] changes --- install/swizzin-install.sh | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/install/swizzin-install.sh b/install/swizzin-install.sh index 6cdca80d6..2e6b57425 100644 --- a/install/swizzin-install.sh +++ b/install/swizzin-install.sh @@ -13,14 +13,20 @@ setting_up_container network_check update_os -msg_info "Installing Dependencies" -$STD apt-get install -y curl wget gnupg lsb-release -msg_ok "Dependencies installed" -echo -e "Launching upstream Swizzin installer..." +msg_custom "!" "$YELLOW" "WARNING: This script will run an external installer from a third-party source (swizzin.io)." +msg_custom "!" "$YELLOW" "You are about to execute code that is NOT maintained by our repository." +echo +read -r -p "Do you want to continue? [y/N]: " CONFIRM +if [[ ! "$CONFIRM" =~ ^([yY][eE][sS]|[yY])$ ]]; then + msg_error "Aborted by user. No changes have been made." + exit 1 +fi bash <(curl -sL s5n.sh) + +motd_ssh +customize + msg_info "Cleaning up" $STD apt-get -y autoremove $STD apt-get -y autoclean msg_ok "Cleaned" - -msg_ok "Swizzin base installation complete. Re‑login and run 'sudo box' to add apps." From 26376db6d5981ea8f0e1c262105a3ee10dd824cd Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 4 Aug 2025 07:38:49 +0000 Subject: [PATCH 0333/1733] Update .app files --- ct/headers/swizzin | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/swizzin diff --git a/ct/headers/swizzin b/ct/headers/swizzin new file mode 100644 index 000000000..473b027ed --- /dev/null +++ b/ct/headers/swizzin @@ -0,0 +1,6 @@ + _____ _ _ + / ___/ __(_)_______ (_)___ + \__ \ | /| / / /_ /_ / / / __ \ + ___/ / |/ |/ / / / /_/ /_/ / / / / +/____/|__/|__/_/ /___/___/_/_/ /_/ + From 49e0887b22275d56fd885aa085c81c951ba78abf Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 09:41:58 +0200 Subject: [PATCH 0334/1733] fixes --- ct/swizzin.sh | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/ct/swizzin.sh b/ct/swizzin.sh index 2311e7c6f..5aaafed62 100644 --- a/ct/swizzin.sh +++ b/ct/swizzin.sh @@ -1,6 +1,5 @@ #!/usr/bin/env bash -# Swizzin Seedbox – Proxmox Helper‑Script (CT) -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: EEJoshua # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -15,23 +14,23 @@ var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" -header_info "$APP" +header_info "$APP" variables color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if ! command -v sudo box >/dev/null 2>&1; then - msg_error "No ${APP} installation found!\n" - exit - fi - msg_info "Running 'sudo box update' inside the container\n" - sudo box update - msg_ok "Update finished\n" + header_info + check_container_storage + check_container_resources + if ! command -v sudo box >/dev/null 2>&1; then + msg_error "No ${APP} installation found!\n" exit + fi + msg_info "Running 'sudo box update' inside the container\n" + sudo box update + msg_ok "Update finished\n" + exit } start @@ -43,4 +42,3 @@ echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${CREATING}${GN}${APP} Access it by running 'sudo box' inside the new container${CL}" echo -e "${INFO}${YW}If installed panel, access through the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" - From 073dbe898b0ff77d78781b792abcff420aadf5d1 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 09:58:00 +0200 Subject: [PATCH 0335/1733] yw --- install/swizzin-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/swizzin-install.sh b/install/swizzin-install.sh index 2e6b57425..808e096c6 100644 --- a/install/swizzin-install.sh +++ b/install/swizzin-install.sh @@ -13,8 +13,8 @@ setting_up_container network_check update_os -msg_custom "!" "$YELLOW" "WARNING: This script will run an external installer from a third-party source (swizzin.io)." -msg_custom "!" "$YELLOW" "You are about to execute code that is NOT maintained by our repository." +msg_custom "!" "$YW" "WARNING: This script will run an external installer from a third-party source (swizzin.io)." +msg_custom "!" "$YW" "You are about to execute code that is NOT maintained by our repository." echo read -r -p "Do you want to continue? [y/N]: " CONFIRM if [[ ! "$CONFIRM" =~ ^([yY][eE][sS]|[yY])$ ]]; then From eaa4e4b32d01558709f6c4cb1d7af6238a38f43d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 10:16:22 +0200 Subject: [PATCH 0336/1733] Tab3 --- install/swizzin-install.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/install/swizzin-install.sh b/install/swizzin-install.sh index 808e096c6..ec6f2ced2 100644 --- a/install/swizzin-install.sh +++ b/install/swizzin-install.sh @@ -13,10 +13,10 @@ setting_up_container network_check update_os -msg_custom "!" "$YW" "WARNING: This script will run an external installer from a third-party source (swizzin.io)." -msg_custom "!" "$YW" "You are about to execute code that is NOT maintained by our repository." +msg_warn "WARNING: This script will run an external installer from a third-party source (swizzin.io)." +msg_warn "You are about to execute code that is NOT maintained by our repository." echo -read -r -p "Do you want to continue? [y/N]: " CONFIRM +read -r -p "${TAB3}Do you want to continue? [y/N]: " CONFIRM if [[ ! "$CONFIRM" =~ ^([yY][eE][sS]|[yY])$ ]]; then msg_error "Aborted by user. No changes have been made." exit 1 From e2e595049c28b3b51bab8cd10e61968adfa2dff5 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 10:22:20 +0200 Subject: [PATCH 0337/1733] Hint --- ct/swizzin.sh | 3 +-- install/swizzin-install.sh | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ct/swizzin.sh b/ct/swizzin.sh index 5aaafed62..de10cb1bb 100644 --- a/ct/swizzin.sh +++ b/ct/swizzin.sh @@ -28,7 +28,7 @@ function update_script() { exit fi msg_info "Running 'sudo box update' inside the container\n" - sudo box update + $STD sudo box update msg_ok "Update finished\n" exit } @@ -39,6 +39,5 @@ description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${CREATING}${GN}${APP} Access it by running 'sudo box' inside the new container${CL}" echo -e "${INFO}${YW}If installed panel, access through the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" diff --git a/install/swizzin-install.sh b/install/swizzin-install.sh index ec6f2ced2..a9e143b1f 100644 --- a/install/swizzin-install.sh +++ b/install/swizzin-install.sh @@ -14,7 +14,9 @@ network_check update_os msg_warn "WARNING: This script will run an external installer from a third-party source (swizzin.io)." -msg_warn "You are about to execute code that is NOT maintained by our repository." +msg_warn "The following code is NOT maintained or audited by our repository." +msg_warn "If you have any doubts or concerns, please review the installer code before proceeding:" +msg_warn " → https://s5n.sh" echo read -r -p "${TAB3}Do you want to continue? [y/N]: " CONFIRM if [[ ! "$CONFIRM" =~ ^([yY][eE][sS]|[yY])$ ]]; then From cb5176a32a6c1d34698ad70c65c4cbb23108fffd Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 10:28:04 +0200 Subject: [PATCH 0338/1733] msg_custom --- install/swizzin-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/swizzin-install.sh b/install/swizzin-install.sh index a9e143b1f..6d6380832 100644 --- a/install/swizzin-install.sh +++ b/install/swizzin-install.sh @@ -16,7 +16,7 @@ update_os msg_warn "WARNING: This script will run an external installer from a third-party source (swizzin.io)." msg_warn "The following code is NOT maintained or audited by our repository." msg_warn "If you have any doubts or concerns, please review the installer code before proceeding:" -msg_warn " → https://s5n.sh" +msg_custom "\e[1;34m→\e[0m" "\e[1;34m" "https://s5n.sh" echo read -r -p "${TAB3}Do you want to continue? [y/N]: " CONFIRM if [[ ! "$CONFIRM" =~ ^([yY][eE][sS]|[yY])$ ]]; then From b743b30b61789e40c4716055eb3669f20b20d131 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 10:45:33 +0200 Subject: [PATCH 0339/1733] destroy_lxc --- install/swizzin-install.sh | 1 + misc/core.func | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/install/swizzin-install.sh b/install/swizzin-install.sh index 6d6380832..d59e1dde4 100644 --- a/install/swizzin-install.sh +++ b/install/swizzin-install.sh @@ -21,6 +21,7 @@ echo read -r -p "${TAB3}Do you want to continue? [y/N]: " CONFIRM if [[ ! "$CONFIRM" =~ ^([yY][eE][sS]|[yY])$ ]]; then msg_error "Aborted by user. No changes have been made." + destroy_lxc exit 1 fi bash <(curl -sL s5n.sh) diff --git a/misc/core.func b/misc/core.func index 5df50efd9..1c56d1de5 100644 --- a/misc/core.func +++ b/misc/core.func @@ -449,3 +449,18 @@ check_or_create_swap() { } trap 'stop_spinner' EXIT INT TERM + +destroy_lxc() { + if [[ -n "$CT_ID" ]]; then + read -p "Remove this Container? " prompt + if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then + pct stop "$CT_ID" &>/dev/null + pct destroy "$CT_ID" &>/dev/null + msg_ok "Removed this Container" + else + msg_info "Container was not removed." + fi + else + msg_error "No CT_ID found. Nothing to remove." + fi +} From d38f0d343e970bbab375bb8b390de80ada0cc882 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 10:47:36 +0200 Subject: [PATCH 0340/1733] formatting --- install/swizzin-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/swizzin-install.sh b/install/swizzin-install.sh index d59e1dde4..888a836b0 100644 --- a/install/swizzin-install.sh +++ b/install/swizzin-install.sh @@ -16,7 +16,7 @@ update_os msg_warn "WARNING: This script will run an external installer from a third-party source (swizzin.io)." msg_warn "The following code is NOT maintained or audited by our repository." msg_warn "If you have any doubts or concerns, please review the installer code before proceeding:" -msg_custom "\e[1;34m→\e[0m" "\e[1;34m" "https://s5n.sh" +msg_custom "${TAB3}${GATEWAY}${BGN}→${CL}" "\e[1;34m" "https://s5n.sh" echo read -r -p "${TAB3}Do you want to continue? [y/N]: " CONFIRM if [[ ! "$CONFIRM" =~ ^([yY][eE][sS]|[yY])$ ]]; then From 1cb2d7e1bb8e9025dfdbdfa7a40d8dff397ae62a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 10:57:18 +0200 Subject: [PATCH 0341/1733] fixes --- install/swizzin-install.sh | 3 +-- misc/build.func | 8 ++++++-- misc/tools.func | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/install/swizzin-install.sh b/install/swizzin-install.sh index 888a836b0..34707e634 100644 --- a/install/swizzin-install.sh +++ b/install/swizzin-install.sh @@ -21,8 +21,7 @@ echo read -r -p "${TAB3}Do you want to continue? [y/N]: " CONFIRM if [[ ! "$CONFIRM" =~ ^([yY][eE][sS]|[yY])$ ]]; then msg_error "Aborted by user. No changes have been made." - destroy_lxc - exit 1 + exit 10 fi bash <(curl -sL s5n.sh) diff --git a/misc/build.func b/misc/build.func index 36cb530d5..97ef9a8bb 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1428,8 +1428,12 @@ EOF' fi msg_ok "Customized LXC Container" - if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/"$var_install".sh)"; then - exit $? + if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/$var_install.sh)"; then + EXITCODE=$? + if [[ $EXITCODE -eq 10 ]]; then + destroy_lxc + fi + exit $EXITCODE fi } diff --git a/misc/tools.func b/misc/tools.func index 04688d407..b6451ad19 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -544,7 +544,7 @@ function setup_composer() { fi chmod +x "$COMPOSER_BIN" - composer diagnose >/dev/null 2>&1 + $STD composer diagnose msg_ok "Setup Composer" } From 5094a4d330b504dc772c8bcba6992a1b3bed3dfe Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 10:57:57 +0200 Subject: [PATCH 0342/1733] Update swizzin-install.sh --- install/swizzin-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/swizzin-install.sh b/install/swizzin-install.sh index 34707e634..88d870c13 100644 --- a/install/swizzin-install.sh +++ b/install/swizzin-install.sh @@ -16,7 +16,7 @@ update_os msg_warn "WARNING: This script will run an external installer from a third-party source (swizzin.io)." msg_warn "The following code is NOT maintained or audited by our repository." msg_warn "If you have any doubts or concerns, please review the installer code before proceeding:" -msg_custom "${TAB3}${GATEWAY}${BGN}→${CL}" "\e[1;34m" "https://s5n.sh" +msg_custom "${TAB3}${GATEWAY}${BGN}${CL}" "\e[1;34m" "→https://s5n.sh" echo read -r -p "${TAB3}Do you want to continue? [y/N]: " CONFIRM if [[ ! "$CONFIRM" =~ ^([yY][eE][sS]|[yY])$ ]]; then From 72d31f1015569aa6650f441928a9667213117505 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 11:01:51 +0200 Subject: [PATCH 0343/1733] testing --- install/swizzin-install.sh | 2 +- misc/build.func | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/install/swizzin-install.sh b/install/swizzin-install.sh index 88d870c13..a825b00d1 100644 --- a/install/swizzin-install.sh +++ b/install/swizzin-install.sh @@ -16,7 +16,7 @@ update_os msg_warn "WARNING: This script will run an external installer from a third-party source (swizzin.io)." msg_warn "The following code is NOT maintained or audited by our repository." msg_warn "If you have any doubts or concerns, please review the installer code before proceeding:" -msg_custom "${TAB3}${GATEWAY}${BGN}${CL}" "\e[1;34m" "→https://s5n.sh" +msg_custom "${TAB3}${GATEWAY}${BGN}${CL}" "\e[1;34m" "→ https://s5n.sh" echo read -r -p "${TAB3}Do you want to continue? [y/N]: " CONFIRM if [[ ! "$CONFIRM" =~ ^([yY][eE][sS]|[yY])$ ]]; then diff --git a/misc/build.func b/misc/build.func index 97ef9a8bb..f0a8b71b4 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1437,6 +1437,17 @@ EOF' fi } +destroy_lxc() { + if [[ -n "$CTID" ]]; then + echo "Würde Container $CTID entfernen!" + # pct stop "$CTID" + # pct destroy "$CTID" + # msg_ok "Removed this Container" + else + echo "Keine CTID – nichts zu entfernen." + fi +} + # This function sets the description of the container. description() { IP=$(pct exec "$CTID" ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) From e885ad8bf629c2831149c7359f7e395e7ca6c5e0 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 11:09:44 +0200 Subject: [PATCH 0344/1733] Update swizzin-install.sh --- install/swizzin-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/swizzin-install.sh b/install/swizzin-install.sh index a825b00d1..aff5c85dc 100644 --- a/install/swizzin-install.sh +++ b/install/swizzin-install.sh @@ -13,7 +13,7 @@ setting_up_container network_check update_os -msg_warn "WARNING: This script will run an external installer from a third-party source (swizzin.io)." +msg_warn "WARNING: This script will run an external installer from a third-party source (https://swizzin.ltd/)." msg_warn "The following code is NOT maintained or audited by our repository." msg_warn "If you have any doubts or concerns, please review the installer code before proceeding:" msg_custom "${TAB3}${GATEWAY}${BGN}${CL}" "\e[1;34m" "→ https://s5n.sh" From d9987442ae36e49a947b4612309ad73722232a2a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 11:13:19 +0200 Subject: [PATCH 0345/1733] test --- misc/build.func | 16 ++++++++++------ misc/core.func | 15 --------------- 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/misc/build.func b/misc/build.func index f0a8b71b4..f96a09848 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1438,13 +1438,17 @@ EOF' } destroy_lxc() { - if [[ -n "$CTID" ]]; then - echo "Würde Container $CTID entfernen!" - # pct stop "$CTID" - # pct destroy "$CTID" - # msg_ok "Removed this Container" + if [[ -n "$CT_ID" ]]; then + read -p "Remove this Container? " prompt + if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then + pct stop "$CT_ID" &>/dev/null + pct destroy "$CT_ID" &>/dev/null + msg_ok "Removed this Container" + else + msg_info "Container was not removed." + fi else - echo "Keine CTID – nichts zu entfernen." + msg_error "No CT_ID found. Nothing to remove." fi } diff --git a/misc/core.func b/misc/core.func index 1c56d1de5..5df50efd9 100644 --- a/misc/core.func +++ b/misc/core.func @@ -449,18 +449,3 @@ check_or_create_swap() { } trap 'stop_spinner' EXIT INT TERM - -destroy_lxc() { - if [[ -n "$CT_ID" ]]; then - read -p "Remove this Container? " prompt - if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then - pct stop "$CT_ID" &>/dev/null - pct destroy "$CT_ID" &>/dev/null - msg_ok "Removed this Container" - else - msg_info "Container was not removed." - fi - else - msg_error "No CT_ID found. Nothing to remove." - fi -} From 675f68f70dc0e5845887a3f3cb8f2fde130584d9 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 11:15:53 +0200 Subject: [PATCH 0346/1733] Update build.func --- misc/build.func | 1 + 1 file changed, 1 insertion(+) diff --git a/misc/build.func b/misc/build.func index f96a09848..62c2f73bd 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1430,6 +1430,7 @@ EOF' if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/$var_install.sh)"; then EXITCODE=$? + echo "DEBUG: lxc-attach exit code was $EXITCODE" if [[ $EXITCODE -eq 10 ]]; then destroy_lxc fi From 1c736626dfb49f1b72a37849c2139adbb150fd53 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 11:32:36 +0200 Subject: [PATCH 0347/1733] Update swizzin-install.sh --- install/swizzin-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/swizzin-install.sh b/install/swizzin-install.sh index aff5c85dc..7ef19093f 100644 --- a/install/swizzin-install.sh +++ b/install/swizzin-install.sh @@ -21,7 +21,7 @@ echo read -r -p "${TAB3}Do you want to continue? [y/N]: " CONFIRM if [[ ! "$CONFIRM" =~ ^([yY][eE][sS]|[yY])$ ]]; then msg_error "Aborted by user. No changes have been made." - exit 10 + return 10 fi bash <(curl -sL s5n.sh) From e10cbff31c5466488201bf00d64d7da763f5b5fe Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 11:38:42 +0200 Subject: [PATCH 0348/1733] test --- install/swizzin-install.sh | 2 +- misc/build.func | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/install/swizzin-install.sh b/install/swizzin-install.sh index 7ef19093f..aff5c85dc 100644 --- a/install/swizzin-install.sh +++ b/install/swizzin-install.sh @@ -21,7 +21,7 @@ echo read -r -p "${TAB3}Do you want to continue? [y/N]: " CONFIRM if [[ ! "$CONFIRM" =~ ^([yY][eE][sS]|[yY])$ ]]; then msg_error "Aborted by user. No changes have been made." - return 10 + exit 10 fi bash <(curl -sL s5n.sh) diff --git a/misc/build.func b/misc/build.func index 62c2f73bd..751bb9158 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1428,7 +1428,7 @@ EOF' fi msg_ok "Customized LXC Container" - if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/$var_install.sh)"; then + if ! lxc-attach -n "$CTID" -- bash -s < <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/$var_install.sh); then EXITCODE=$? echo "DEBUG: lxc-attach exit code was $EXITCODE" if [[ $EXITCODE -eq 10 ]]; then From 441a69c85dcd9a00f702410d33e2058f41c4cf4e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 12:32:56 +0200 Subject: [PATCH 0349/1733] Update build.func --- misc/build.func | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/misc/build.func b/misc/build.func index 751bb9158..6c7781f61 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1428,14 +1428,17 @@ EOF' fi msg_ok "Customized LXC Container" - if ! lxc-attach -n "$CTID" -- bash -s < <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/$var_install.sh); then - EXITCODE=$? - echo "DEBUG: lxc-attach exit code was $EXITCODE" - if [[ $EXITCODE -eq 10 ]]; then - destroy_lxc - fi - exit $EXITCODE + lxc-attach -n "$CTID" -- bash -c ' + bash -s < /dev/stdin + echo __EXITCODE__$? +' < <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/$var_install.sh) | tee /tmp/app_install.log + + EXITCODE=$(awk -F__EXITCODE__ '/__EXITCODE__/{print $2}' /tmp/app_install.log | tail -n1) + echo "DEBUG: Parsed container exit code: $EXITCODE" + if [[ "$EXITCODE" == "10" ]]; then + destroy_lxc fi + exit "$EXITCODE" } destroy_lxc() { From da63ff920166d8b2cb21df2e3565d34007ee9c90 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 12:36:54 +0200 Subject: [PATCH 0350/1733] Update build.func --- misc/build.func | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/misc/build.func b/misc/build.func index 6c7781f61..35a2d87f5 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1428,10 +1428,12 @@ EOF' fi msg_ok "Customized LXC Container" + pct push "$CTID" <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/$var_install.sh) /tmp/app-install.sh + pct exec "$CTID" -- bash -c 'chmod +x /tmp/app-install.sh' lxc-attach -n "$CTID" -- bash -c ' - bash -s < /dev/stdin + /tmp/app-install.sh echo __EXITCODE__$? -' < <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/$var_install.sh) | tee /tmp/app_install.log +' | tee /tmp/app_install.log EXITCODE=$(awk -F__EXITCODE__ '/__EXITCODE__/{print $2}' /tmp/app_install.log | tail -n1) echo "DEBUG: Parsed container exit code: $EXITCODE" From 2f151a6389fd2c3fdc27cf69130b9956d1f22017 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 13:57:41 +0200 Subject: [PATCH 0351/1733] Update build.func --- misc/build.func | 50 +++++++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/misc/build.func b/misc/build.func index 35a2d87f5..a0d7d8310 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1364,32 +1364,42 @@ EOF if [ "$var_os" != "alpine" ]; then msg_info "Waiting for network in LXC container" for i in {1..10}; do + # 1. Primary check: ICMP ping (fastest, but may be blocked by ISP/firewall) if pct exec "$CTID" -- ping -c1 -W1 deb.debian.org >/dev/null 2>&1; then - msg_ok "Network in LXC is reachable" + msg_ok "Network in LXC is reachable (ping)" break fi + # Wait and retry if not reachable yet if [ "$i" -lt 10 ]; then - msg_warn "No network yet in LXC (try $i/10) – waiting..." + msg_warn "No network in LXC yet (try $i/10) – waiting..." sleep 3 else - msg_error "No network in LXC after waiting." - read -r -p "Set fallback DNS (1.1.1.1/8.8.8.8)? [y/N]: " choice - case "$choice" in - [yY]*) - pct set "$CTID" --nameserver 1.1.1.1 - pct set "$CTID" --nameserver 8.8.8.8 - if pct exec "$CTID" -- ping -c1 -W1 deb.debian.org >/dev/null 2>&1; then - msg_ok "Network reachable after DNS fallback" - else - msg_error "Still no network/DNS in LXC! Aborting customization." - exit 1 - fi - ;; - *) - msg_error "Aborted by user – no DNS fallback set." - exit 1 - ;; - esac + # After 10 unsuccessful ping attempts, try HTTP connectivity via wget as fallback + msg_warn "Ping failed 10 times. Trying HTTP connectivity check (wget) as fallback..." + if pct exec "$CTID" -- wget -q --spider http://deb.debian.org; then + msg_ok "Network in LXC is reachable (wget fallback)" + else + msg_error "No network in LXC after all checks." + read -r -p "Set fallback DNS (1.1.1.1/8.8.8.8)? [y/N]: " choice + case "$choice" in + [yY]*) + pct set "$CTID" --nameserver 1.1.1.1 + pct set "$CTID" --nameserver 8.8.8.8 + # Final attempt with wget after DNS change + if pct exec "$CTID" -- wget -q --spider http://deb.debian.org; then + msg_ok "Network reachable after DNS fallback" + else + msg_error "Still no network/DNS in LXC! Aborting customization." + exit_script + fi + ;; + *) + msg_error "Aborted by user – no DNS fallback set." + exit_script + ;; + esac + fi + break fi done fi From 77defbbceb6b82fc87a0f0b33088667d05d1723b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 14:01:28 +0200 Subject: [PATCH 0352/1733] cleanup --- ct/ots.sh | 59 ------------------ ct/tududi.sh | 72 --------------------- frontend/public/json/ots.json | 40 ------------ frontend/public/json/tududi.json | 44 ------------- install/ots-install.sh | 103 ------------------------------- install/tududi-install.sh | 76 ----------------------- 6 files changed, 394 deletions(-) delete mode 100644 ct/ots.sh delete mode 100644 ct/tududi.sh delete mode 100644 frontend/public/json/ots.json delete mode 100644 frontend/public/json/tududi.json delete mode 100644 install/ots-install.sh delete mode 100644 install/tududi-install.sh diff --git a/ct/ots.sh b/ct/ots.sh deleted file mode 100644 index 9c711ae71..000000000 --- a/ct/ots.sh +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env bash -source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: bvdberg01 -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://github.com/Luzifer/ots - -APP="OTS" -var_tags="${var_tags:-secrets-sharer}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-512}" -var_disk="${var_disk:-3}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/ots ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - RELEASE=$(curl -fsSL https://api.github.com/repos/Luzifer/ots/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - if [[ "${RELEASE}" != "$(cat ~/.ots 2>/dev/null)" ]] || [[ ! -f ~/.ots ]]; then - - msg_info "Stopping ${APP} Service" - systemctl stop ots - systemctl stop nginx - msg_ok "Stopped ${APP} Service" - - fetch_and_deploy_gh_release "ots" "Luzifer/ots" "prebuild" "latest" "/opt/ots" "ots_linux_amd64.tgz" - - msg_info "Stopping ${APP} Service" - systemctl start ots - systemctl start nginx - msg_ok "Stopped ${APP} Service" - - else - msg_ok "No update required. ${APP} is already at ${RELEASE}" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}https://${IP}${CL}" diff --git a/ct/tududi.sh b/ct/tududi.sh deleted file mode 100644 index f84079894..000000000 --- a/ct/tududi.sh +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env bash -source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: vhsdream -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://tududi.com - -APP="Tududi" -var_tags="${var_tags:-todo-app}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-4}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/tududi ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - RELEASE=$(curl -fsSL https://api.github.com/repos/chrisvel/tududi/releases/latest | yq '.tag_name' | sed 's/^v//') - if [[ "${RELEASE}" != "$(cat ~/.tududi 2>/dev/null)" ]] || [[ ! -f ~/.tududi ]]; then - msg_info "Stopping Service" - systemctl stop tududi - msg_ok "Stopped Service" - - msg_info "Remove and backup Files" - cp /opt/tududi/backend/.env /opt/tududi.env - rm -rf /opt/tududi/backend/dist - msg_ok "Backup and removed Files" - - fetch_and_deploy_gh_release "tududi" "chrisvel/tududi" - - msg_info "Updating ${APP}" - cd /opt/tududi - $STD npm install - export NODE_ENV=production - $STD npm run frontend:build - mv ./dist ./backend - mv ./public/locales ./backend/dist - mv ./public/favicon.* ./backend/dist - mv /opt/tududi.env /opt/tududi/.env - msg_ok "Updated $APP" - - msg_info "Starting Service" - systemctl start tududi - msg_ok "Started Service" - msg_ok "Updated Successfully" - else - msg_ok "Already up to date" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3002${CL}" diff --git a/frontend/public/json/ots.json b/frontend/public/json/ots.json deleted file mode 100644 index 2bec84e8c..000000000 --- a/frontend/public/json/ots.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "OTS", - "slug": "ots", - "categories": [ - 6 - ], - "date_created": "2025-07-28", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 443, - "documentation": "https://github.com/Luzifer/ots/wiki", - "config_path": "/opt/ots/.env", - "website": "https://github.com/Luzifer/ots", - "logo": null, - "description": "One-Time-Secret sharing platform with a symmetric 256bit AES encryption in the browser.", - "install_methods": [ - { - "type": "default", - "script": "ct/ots.sh", - "resources": { - "cpu": 1, - "ram": 512, - "hdd": 3, - "os": "Debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "When it is in used external please use it behind reverse proxy or create your own certificates", - "type": "info" - } - ] -} diff --git a/frontend/public/json/tududi.json b/frontend/public/json/tududi.json deleted file mode 100644 index a82ec5810..000000000 --- a/frontend/public/json/tududi.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "name": "Tududi", - "slug": "tududi", - "categories": [ - 12 - ], - "date_created": "2025-07-22", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 3002, - "documentation": null, - "config_path": "/opt/tududi/backend/.env", - "website": "https://tududi.com/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/tududi.webp", - "description": "Self-hosted task management with functional programming architecture, hierarchical organization, and multi-language support.", - "install_methods": [ - { - "type": "default", - "script": "ct/tududi.sh", - "resources": { - "cpu": 2, - "ram": 2048, - "hdd": 4, - "os": "Debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "Create users like this: `cd /opt/tududi` => `npm run user:create `", - "type": "info" - }, - { - "text": "Database location: `/opt/tududi-db`. Uploads: `/opt/tududi-uploads`", - "type": "info" - } - ] -} diff --git a/install/ots-install.sh b/install/ots-install.sh deleted file mode 100644 index 526d64bd1..000000000 --- a/install/ots-install.sh +++ /dev/null @@ -1,103 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: bvberg01 -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://github.com/Luzifer/ots - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt-get install -y \ - redis-server \ - nginx \ - openssl -msg_ok "Installed Dependencies" - -fetch_and_deploy_gh_release "ots" "Luzifer/ots" "prebuild" "latest" "/opt/ots" "ots_linux_amd64.tgz" - -msg_info "Setup OTS" -cat </opt/ots/.env -LISTEN=127.0.0.1:3000 -REDIS_URL=redis://127.0.0.1:6379 -SECRET_EXPIRY=604800 -STORAGE_TYPE=redis -EOF -msg_ok "Setup OTS" - -msg_info "Generating Universal SSL Certificate" -mkdir -p /etc/ssl/ots -$STD openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \ - -keyout /etc/ssl/ots/key.pem \ - -out /etc/ssl/ots/cert.pem \ - -subj "/CN=ots" -msg_ok "Certificate Generated" - -msg_info "Setting up nginx" -cat </etc/nginx/sites-available/ots.conf -server { - listen 80; - listen [::]:80; - server_name ots; - return 301 https://\$host\$request_uri; -} -server { - listen 443 ssl; - listen [::]:443 ssl; - server_name ots; - - ssl_certificate /etc/ssl/ots/cert.pem; - ssl_certificate_key /etc/ssl/ots/key.pem; - - location / { - add_header X-Robots-Tag noindex; - - proxy_set_header Upgrade \$http_upgrade; - proxy_set_header Connection "Upgrade"; - proxy_set_header Host \$host; - proxy_set_header X-Real-IP \$remote_addr; - proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto \$scheme; - client_max_body_size 64M; - proxy_pass http://127.0.0.1:3000/; - } -} -EOF - -ln -s /etc/nginx/sites-available/ots.conf /etc/nginx/sites-enabled/ -rm -f /etc/nginx/sites-enabled/default -$STD systemctl reload nginx -msg_ok "Configured nginx" - -msg_info "Creating Services" -cat </etc/systemd/system/ots.service -[Unit] -Description=One-Time-Secret Service -After=network-online.target -Requires=network-online.target - -[Service] -EnvironmentFile=/opt/ots/.env -ExecStart=/opt/ots/ots -Restart=Always -RestartSecs=5 - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now ots -msg_ok "Created Services" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" diff --git a/install/tududi-install.sh b/install/tududi-install.sh deleted file mode 100644 index d89d8c3d2..000000000 --- a/install/tududi-install.sh +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2025 Community Scripts ORG -# Author: vhsdream -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://tududi.com/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt-get install -y \ - sqlite3 \ - yq -msg_ok "Installed Dependencies" - -NODE_VERSION="20" setup_nodejs -fetch_and_deploy_gh_release "tududi" "chrisvel/tududi" - -msg_info "Configuring Tududi" -cd /opt/tududi -$STD npm install -export NODE_ENV=production -$STD npm run frontend:build -mv ./dist ./backend -mv ./public/locales ./backend/dist -mv ./public/favicon.* ./backend/dist -msg_ok "Configured Tududi" - -msg_info "Creating env and database" -DB_LOCATION="/opt/tududi-db" -UPLOAD_DIR="/opt/tududi-uploads" -mkdir -p {"$DB_LOCATION","$UPLOAD_DIR"} -SECRET="$(openssl rand -hex 64)" -sed -e 's/^GOOGLE/# &/' \ - -e '/TUDUDI_SESSION/s/^# //' \ - -e '/NODE_ENV/s/^# //' \ - -e "s/your_session_secret_here/$SECRET/" \ - -e 's/development/production/' \ - -e "\$a\DB_FILE=$DB_LOCATION/production.sqlite3" \ - -e "\$a\TUDUDI_UPLOAD_PATH=$UPLOAD_DIR" \ - /opt/tududi/backend/.env.example >/opt/tududi/backend/.env -export DB_FILE="$DB_LOCATION/production.sqlite3" -$STD npm run db:init -msg_ok "Created env and database" - -msg_info "Creating service" -cat </etc/systemd/system/tududi.service -[Unit] -Description=Tududi Service -After=network.target - -[Service] -Type=simple -WorkingDirectory=/opt/tududi -EnvironmentFile=/opt/tududi/backend/.env -ExecStart=/usr/bin/npm run start - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now tududi -msg_ok "Created service" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" From 32461e453a420211b883c5c908d11aa8efc41e27 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 4 Aug 2025 12:01:51 +0000 Subject: [PATCH 0353/1733] Update .app files --- ct/headers/ots | 6 ------ ct/headers/tududi | 6 ------ 2 files changed, 12 deletions(-) delete mode 100644 ct/headers/ots delete mode 100644 ct/headers/tududi diff --git a/ct/headers/ots b/ct/headers/ots deleted file mode 100644 index 64a5fc431..000000000 --- a/ct/headers/ots +++ /dev/null @@ -1,6 +0,0 @@ - ____ ___________ - / __ \/_ __/ ___/ - / / / / / / \__ \ -/ /_/ / / / ___/ / -\____/ /_/ /____/ - diff --git a/ct/headers/tududi b/ct/headers/tududi deleted file mode 100644 index 17ec52284..000000000 --- a/ct/headers/tududi +++ /dev/null @@ -1,6 +0,0 @@ - ______ __ ___ - /_ __/_ ______/ /_ ______/ (_) - / / / / / / __ / / / / __ / / - / / / /_/ / /_/ / /_/ / /_/ / / -/_/ \__,_/\__,_/\__,_/\__,_/_/ - From 35adea34948571d3f4e6a84b1b3a3737bdd06cfc Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 14:12:52 +0200 Subject: [PATCH 0354/1733] refactor --- tools/pve/clean-lxcs.sh | 125 ++++++++++++++++++++-------------------- tools/pve/clean.sh | 82 ++++++++++++++++++++++++++ 2 files changed, 143 insertions(+), 64 deletions(-) create mode 100644 tools/pve/clean.sh diff --git a/tools/pve/clean-lxcs.sh b/tools/pve/clean-lxcs.sh index feb0921ce..0951d8b24 100644 --- a/tools/pve/clean-lxcs.sh +++ b/tools/pve/clean-lxcs.sh @@ -8,8 +8,8 @@ set -eEuo pipefail function header_info() { - clear - cat <<"EOF" + clear + cat <<"EOF" ________ __ _ ________ / ____/ /__ ____ _____ / / | |/ / ____/ / / / / _ \/ __ `/ __ \ / / | / / @@ -28,81 +28,78 @@ CL="\033[m" header_info echo "Loading..." whiptail --backtitle "Proxmox VE Helper Scripts" --title "Proxmox VE LXC Updater" \ - --yesno "This Will Clean logs, cache and update apt/apk lists on selected LXC Containers. Proceed?" 10 68 || exit + --yesno "This Will Clean logs, cache and update apt/apk lists on selected LXC Containers. Proceed?" 10 68 || exit NODE=$(hostname) EXCLUDE_MENU=() MSG_MAX_LENGTH=0 while read -r TAG ITEM; do - OFFSET=2 - ((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=${#ITEM}+OFFSET - EXCLUDE_MENU+=("$TAG" "$ITEM " "OFF") + OFFSET=2 + ((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=${#ITEM}+OFFSET + EXCLUDE_MENU+=("$TAG" "$ITEM " "OFF") done < <(pct list | awk 'NR>1') excluded_containers=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Containers on $NODE" \ - --checklist "\nSelect containers to skip from cleaning:\n" 16 $((MSG_MAX_LENGTH + 23)) 6 "${EXCLUDE_MENU[@]}" \ - 3>&1 1>&2 2>&3 | tr -d '"') || exit + --checklist "\nSelect containers to skip from cleaning:\n" 16 $((MSG_MAX_LENGTH + 23)) 6 "${EXCLUDE_MENU[@]}" \ + 3>&1 1>&2 2>&3 | tr -d '"') || exit function clean_container() { - local container=$1 - local os=$2 - header_info - name=$(pct exec "$container" hostname) - echo -e "${BL}[Info]${GN} Cleaning ${name} (${os}) ${CL} \n" - if [[ "$os" == "alpine" ]]; then - pct exec "$container" -- sh -c \ - "apk update && apk cache clean && rm -rf /var/cache/apk/*" - else - pct exec "$container" -- bash -c \ - "apt-get -y --purge autoremove && apt-get -y autoclean && \ - bash <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/clean.sh) && \ - rm -rf /var/lib/apt/lists/* && apt-get update" - fi + container=$1 + header_info + name=$(pct exec "$container" hostname) + os=$(pct config "$container" | awk '/^ostype/ {print $2}') + echo -e "${BL}[Info]${GN} Cleaning ${name} (${os}) ${CL} \n" + if [ "$os" = "alpine" ]; then + pct exec "$container" -- ash -c "apk update && apk cache clean && rm -rf /var/cache/apk/*" + else + pct exec "$container" -- bash -c "apt-get -y --purge autoremove && apt-get -y autoclean && bash <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/clean.sh) && rm -rf /var/lib/apt/lists/* && apt-get update" + fi } for container in $(pct list | awk 'NR>1 {print $1}'); do - if [[ " ${excluded_containers[*]} " =~ " $container " ]]; then - header_info - echo -e "${BL}[Info]${GN} Skipping ${BL}$container${CL}" - sleep 1 - continue - fi - - # locked? - if pct status "$container" | grep -q 'locked'; then - header_info - echo -e "${BL}[Info]${RD} Skipping locked container ${BL}$container${CL}" - sleep 1 - continue - fi - - os=$(pct config "$container" | awk '/^ostype/ {print $2}') - [[ "$os" != "debian" && "$os" != "ubuntu" && "$os" != "alpine" ]] && { - header_info - echo -e "${BL}[Info]${RD} Skipping unsupported OS in $container: $os ${CL}" - sleep 1 - continue - } - - status=$(pct status "$container" | awk '{print $2}') - template=$(pct config "$container" | grep -q "template:" && echo "true" || echo "false") - - if [[ "$template" == "false" && "$status" == "stopped" ]]; then - if whiptail --backtitle "Proxmox VE Helper Scripts" \ - --title "Container $container is stopped" \ - --yesno "Container $container is stopped.\n\nStart and clean?" 10 58; then - echo -e "${BL}[Info]${GN} Starting${BL} $container ${CL} \n" - pct start "$container" - echo -e "${BL}[Info]${GN} Waiting for${BL} $container${CL}${GN} to start ${CL} \n" - sleep 5 - clean_container "$container" "$os" - echo -e "${BL}[Info]${GN} Shutting down${BL} $container ${CL} \n" - pct shutdown "$container" & - else - echo -e "${BL}[Info]${GN} Skipping stopped container ${BL}$container${CL}" - fi - elif [[ "$status" == "running" ]]; then - clean_container "$container" "$os" + if [[ " ${excluded_containers[*]} " =~ " $container " ]]; then + header_info + echo -e "${BL}[Info]${GN} Skipping ${BL}$container${CL}" + sleep 1 + continue + fi + + # locked? + if pct status "$container" | grep -q 'locked'; then + header_info + echo -e "${BL}[Info]${RD} Skipping locked container ${BL}$container${CL}" + sleep 1 + continue + fi + + os=$(pct config "$container" | awk '/^ostype/ {print $2}') + if [ "$os" != "debian" ] && [ "$os" != "ubuntu" ] && [ "$os" != "alpine" ]; then + header_info + name=$(pct exec "$container" hostname) + echo -e "${BL}[Info]${GN} Skipping ${name} ${RD}$container is not supported OS (${os})${CL} \n" + sleep 1 + continue + fi + + status=$(pct status "$container" | awk '{print $2}') + template=$(pct config "$container" | grep -q "template:" && echo "true" || echo "false") + + if [[ "$template" == "false" && "$status" == "stopped" ]]; then + if whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "Container $container is stopped" \ + --yesno "Container $container is stopped.\n\nStart and clean?" 10 58; then + echo -e "${BL}[Info]${GN} Starting${BL} $container ${CL} \n" + pct start "$container" + echo -e "${BL}[Info]${GN} Waiting for${BL} $container${CL}${GN} to start ${CL} \n" + sleep 5 + clean_container "$container" "$os" + echo -e "${BL}[Info]${GN} Shutting down${BL} $container ${CL} \n" + pct shutdown "$container" & + else + echo -e "${BL}[Info]${GN} Skipping stopped container ${BL}$container${CL}" fi + elif [[ "$status" == "running" ]]; then + clean_container "$container" "$os" + fi done wait diff --git a/tools/pve/clean.sh b/tools/pve/clean.sh new file mode 100644 index 000000000..216f30fbd --- /dev/null +++ b/tools/pve/clean.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# License: MIT +# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE + +function header_info() { + clear + cat <<"EOF" + ________ __ _ ________ + / ____/ /__ ____ _____ / / | |/ / ____/ + / / / / _ \/ __ `/ __ \ / / | / / +/ /___/ / __/ /_/ / / / / / /___/ / /___ +\____/_/\___/\__,_/_/ /_/ /_____/_/|_\____/ + +EOF +} +set -eEuo pipefail +BL=$(echo "\033[36m") +RD=$(echo "\033[01;31m") +CM='\xE2\x9C\x94\033' +GN=$(echo "\033[1;92m") +CL=$(echo "\033[m") +header_info +echo "Loading..." +whiptail --backtitle "Proxmox VE Helper Scripts" --title "Proxmox VE LXC Updater" --yesno "This Will Clean logs, cache and update apt lists on selected LXC Containers. Proceed?" 10 58 +NODE=$(hostname) +EXCLUDE_MENU=() +MSG_MAX_LENGTH=0 +while read -r TAG ITEM; do + OFFSET=2 + ((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=${#ITEM}+OFFSET + EXCLUDE_MENU+=("$TAG" "$ITEM " "OFF") +done < <(pct list | awk 'NR>1') +excluded_containers=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Containers on $NODE" --checklist "\nSelect containers to skip from cleaning:\n" \ + 16 $((MSG_MAX_LENGTH + 23)) 6 "${EXCLUDE_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"') + +if [ $? -ne 0 ]; then + exit +fi + +function clean_container() { + container=$1 + header_info + name=$(pct exec "$container" hostname) + echo -e "${BL}[Info]${GN} Cleaning ${name} ${CL} \n" + pct exec "$container" -- bash -c "apt-get -y --purge autoremove && apt-get -y autoclean && bash <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/clean.sh) && rm -rf /var/lib/apt/lists/* && apt-get update" +} +for container in $(pct list | awk '{if(NR>1) print $1}'); do + if [[ " ${excluded_containers[@]} " =~ " $container " ]]; then + header_info + echo -e "${BL}[Info]${GN} Skipping ${BL}$container${CL}" + sleep 1 + else + os=$(pct config "$container" | awk '/^ostype/ {print $2}') + if [ "$os" != "debian" ] && [ "$os" != "ubuntu" ]; then + header_info + echo -e "${BL}[Info]${GN} Skipping ${name} ${RD}$container is not Debian or Ubuntu ${CL} \n" + sleep 1 + continue + fi + + status=$(pct status "$container") + template=$(pct config "$container" | grep -q "template:" && echo "true" || echo "false") + if [ "$template" == "false" ] && [ "$status" == "status: stopped" ]; then + echo -e "${BL}[Info]${GN} Starting${BL} $container ${CL} \n" + pct start "$container" + echo -e "${BL}[Info]${GN} Waiting For${BL} $container${CL}${GN} To Start ${CL} \n" + sleep 5 + clean_container "$container" + echo -e "${BL}[Info]${GN} Shutting down${BL} $container ${CL} \n" + pct shutdown "$container" & + elif [ "$status" == "status: running" ]; then + clean_container "$container" + fi + fi +done + +wait +header_info +echo -e "${GN} Finished, Selected Containers Cleaned. ${CL} \n" From b3baf883a0f8050803ecef836dc4b2a1963af08b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 14:14:38 +0200 Subject: [PATCH 0355/1733] Update clean.sh --- tools/pve/clean.sh | 81 +++++++++++++--------------------------------- 1 file changed, 23 insertions(+), 58 deletions(-) diff --git a/tools/pve/clean.sh b/tools/pve/clean.sh index 216f30fbd..1c92d79ec 100644 --- a/tools/pve/clean.sh +++ b/tools/pve/clean.sh @@ -16,67 +16,32 @@ function header_info() { EOF } -set -eEuo pipefail BL=$(echo "\033[36m") -RD=$(echo "\033[01;31m") -CM='\xE2\x9C\x94\033' GN=$(echo "\033[1;92m") CL=$(echo "\033[m") +name=$(hostname) header_info -echo "Loading..." -whiptail --backtitle "Proxmox VE Helper Scripts" --title "Proxmox VE LXC Updater" --yesno "This Will Clean logs, cache and update apt lists on selected LXC Containers. Proceed?" 10 58 -NODE=$(hostname) -EXCLUDE_MENU=() -MSG_MAX_LENGTH=0 -while read -r TAG ITEM; do - OFFSET=2 - ((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=${#ITEM}+OFFSET - EXCLUDE_MENU+=("$TAG" "$ITEM " "OFF") -done < <(pct list | awk 'NR>1') -excluded_containers=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Containers on $NODE" --checklist "\nSelect containers to skip from cleaning:\n" \ - 16 $((MSG_MAX_LENGTH + 23)) 6 "${EXCLUDE_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"') - -if [ $? -ne 0 ]; then - exit +echo -e "${BL}[Info]${GN} Cleaning $name${CL} \n" +cache=$(find /var/cache/ -type f) +if [[ -z "$cache" ]]; then + echo -e "It appears there are no cached files on your system. \n" + sleep 2 +else + find /var/cache -type f -delete + echo "Successfully Removed Cache" + sleep 2 fi - -function clean_container() { - container=$1 - header_info - name=$(pct exec "$container" hostname) - echo -e "${BL}[Info]${GN} Cleaning ${name} ${CL} \n" - pct exec "$container" -- bash -c "apt-get -y --purge autoremove && apt-get -y autoclean && bash <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/clean.sh) && rm -rf /var/lib/apt/lists/* && apt-get update" -} -for container in $(pct list | awk '{if(NR>1) print $1}'); do - if [[ " ${excluded_containers[@]} " =~ " $container " ]]; then - header_info - echo -e "${BL}[Info]${GN} Skipping ${BL}$container${CL}" - sleep 1 - else - os=$(pct config "$container" | awk '/^ostype/ {print $2}') - if [ "$os" != "debian" ] && [ "$os" != "ubuntu" ]; then - header_info - echo -e "${BL}[Info]${GN} Skipping ${name} ${RD}$container is not Debian or Ubuntu ${CL} \n" - sleep 1 - continue - fi - - status=$(pct status "$container") - template=$(pct config "$container" | grep -q "template:" && echo "true" || echo "false") - if [ "$template" == "false" ] && [ "$status" == "status: stopped" ]; then - echo -e "${BL}[Info]${GN} Starting${BL} $container ${CL} \n" - pct start "$container" - echo -e "${BL}[Info]${GN} Waiting For${BL} $container${CL}${GN} To Start ${CL} \n" - sleep 5 - clean_container "$container" - echo -e "${BL}[Info]${GN} Shutting down${BL} $container ${CL} \n" - pct shutdown "$container" & - elif [ "$status" == "status: running" ]; then - clean_container "$container" - fi - fi -done - -wait header_info -echo -e "${GN} Finished, Selected Containers Cleaned. ${CL} \n" +echo -e "${BL}[Info]${GN} Cleaning $name${CL} \n" +logs=$(find /var/log/ -type f) +if [[ -z "$logs" ]]; then + echo -e "It appears there are no logs on your system. \n" + sleep 2 +else + find /var/log -type f -delete + echo "Successfully Removed Logs" + sleep 2 +fi +header_info +echo -e "${BL}[Info]${GN} Cleaning $name${CL} \n" +echo -e "${GN}Populating apt lists${CL} \n" From dde33d6d78d0545e035a03bab1de164368e04e12 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 14:15:03 +0200 Subject: [PATCH 0356/1733] Update clean.sh --- tools/pve/clean.sh | 71 +++++++++++++++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 23 deletions(-) diff --git a/tools/pve/clean.sh b/tools/pve/clean.sh index 1c92d79ec..87d3c9af8 100644 --- a/tools/pve/clean.sh +++ b/tools/pve/clean.sh @@ -5,6 +5,8 @@ # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +set -euo pipefail + function header_info() { clear cat <<"EOF" @@ -16,32 +18,55 @@ function header_info() { EOF } -BL=$(echo "\033[36m") -GN=$(echo "\033[1;92m") -CL=$(echo "\033[m") + +BL="\033[36m" +GN="\033[1;92m" +CL="\033[m" name=$(hostname) + header_info echo -e "${BL}[Info]${GN} Cleaning $name${CL} \n" -cache=$(find /var/cache/ -type f) -if [[ -z "$cache" ]]; then - echo -e "It appears there are no cached files on your system. \n" - sleep 2 + +# OS-Detection +if [ -f /etc/alpine-release ]; then + OS="alpine" +elif [ -f /etc/debian_version ] || grep -qi ubuntu /etc/issue 2>/dev/null; then + OS="debian" else - find /var/cache -type f -delete - echo "Successfully Removed Cache" - sleep 2 + OS="unknown" fi -header_info -echo -e "${BL}[Info]${GN} Cleaning $name${CL} \n" -logs=$(find /var/log/ -type f) -if [[ -z "$logs" ]]; then - echo -e "It appears there are no logs on your system. \n" - sleep 2 -else - find /var/log -type f -delete - echo "Successfully Removed Logs" - sleep 2 + +# Universal Cleaning +function clean_universal() { + # Caches + find /var/cache/ -type f -delete 2>/dev/null || true + # Logs + find /var/log/ -type f -delete 2>/dev/null || true + # Tmp + find /tmp/ -mindepth 1 -delete 2>/dev/null || true + find /var/tmp/ -mindepth 1 -delete 2>/dev/null || true + # User Trash (Desktop-Umgebungen) + for u in /home/* /root; do + find "$u/.local/share/Trash/" -type f -delete 2>/dev/null || true + done +} + +clean_universal + +if [ "$OS" = "alpine" ]; then + echo -e "${BL}[Info]${GN} Alpine detected: Cleaning apk cache...${CL}" + rm -rf /var/cache/apk/* 2>/dev/null || true + apk cache clean 2>/dev/null || true + rm -rf /etc/apk/cache/* 2>/dev/null || true + +elif [ "$OS" = "debian" ]; then + echo -e "${BL}[Info]${GN} Debian/Ubuntu detected: Cleaning apt and journal...${CL}" + apt-get -y autoremove --purge >/dev/null 2>&1 || true + apt-get -y autoclean >/dev/null 2>&1 || true + apt-get -y clean >/dev/null 2>&1 || true + journalctl --vacuum-time=2d --rotate >/dev/null 2>&1 || true + rm -rf /var/lib/apt/lists/* 2>/dev/null || true + apt-get update >/dev/null 2>&1 || true fi -header_info -echo -e "${BL}[Info]${GN} Cleaning $name${CL} \n" -echo -e "${GN}Populating apt lists${CL} \n" + +echo -e "${GN}Cleanup completed for $name ($OS)${CL}\n" From bda070ae0749d759435ed3a87e5369faa1c7e772 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 14:15:15 +0200 Subject: [PATCH 0357/1733] Update clean-lxcs.sh --- tools/pve/clean-lxcs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/pve/clean-lxcs.sh b/tools/pve/clean-lxcs.sh index 0951d8b24..4edb7b6cd 100644 --- a/tools/pve/clean-lxcs.sh +++ b/tools/pve/clean-lxcs.sh @@ -51,7 +51,7 @@ function clean_container() { if [ "$os" = "alpine" ]; then pct exec "$container" -- ash -c "apk update && apk cache clean && rm -rf /var/cache/apk/*" else - pct exec "$container" -- bash -c "apt-get -y --purge autoremove && apt-get -y autoclean && bash <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/tools/pve/clean.sh) && rm -rf /var/lib/apt/lists/* && apt-get update" + pct exec "$container" -- bash -c "apt-get -y --purge autoremove && apt-get -y autoclean && bash <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/tools/pve/clean.sh) && rm -rf /var/lib/apt/lists/* && apt-get update" fi } From 0271e704a8f638fca482dfe97bc2e1f4c5ba91ef Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 15:27:00 +0200 Subject: [PATCH 0358/1733] hortus test --- ct/hortusfox.sh | 65 +++++++++++++++++++++++++++ install/hortusfox-install.sh | 86 ++++++++++++++++++++++++++++++++++++ tools/pve/clean-lxcs.sh | 3 +- 3 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 ct/hortusfox.sh create mode 100644 install/hortusfox-install.sh diff --git a/ct/hortusfox.sh b/ct/hortusfox.sh new file mode 100644 index 000000000..63ea9db3e --- /dev/null +++ b/ct/hortusfox.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: bvdberg01 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/manyfold3d/manyfold + +APP="HortusFox" +var_tags="${var_tags:-network}" +var_cpu="${var_cpu:-4}" +var_ram="${var_ram:-4096}" +var_disk="${var_disk:-15}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/hortusfox ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + RELEASE=$(curl -s https://api.github.com/repos/danielbrendel/hortusfox-web/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then + msg_info "Stopping Service" + systemctl stop apache2 + msg_ok "Stopped Service" + + msg_info "Updating ${APP} to v${RELEASE}" + cd /opt + mv /opt/hortusfox/ /opt/hortusfox-backup + + echo "${RELEASE}" >/opt/${APP}_version.txt + msg_ok "Updated $APP to v${RELEASE}" + + msg_info "Starting Service" + systemctl start service + msg_ok "Started Service" + + msg_info "Cleaning up" + rm -r "/opt/${RELEASE}.zip" + rm -r /opt/hortusfox-backup + msg_ok "Cleaned" + msg_ok "Updated Successfully" + else + msg_ok "No update required. ${APP} is already at v${RELEASE}" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" diff --git a/install/hortusfox-install.sh b/install/hortusfox-install.sh new file mode 100644 index 000000000..49c78c5b4 --- /dev/null +++ b/install/hortusfox-install.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/danielbrendel/hortusfox-web + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt-get install -y apache2 +msg_ok "Installed Dependencies" + +PHP_MODULE="exif,pcntl,mysql" PHP_APACHE="YES" PHP_FPM="NO" PHP_VERSION="8.3" setup_php +setup_mariadb +setup_composer + +msg_info "Setting up database" +DB_NAME=hortusfox +DB_USER=hortusfox +DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) +$STD mariadb -u root -e "CREATE DATABASE $DB_NAME;" +$STD mariadb -u root -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';" +$STD mariadb -u root -e "GRANT ALL PRIVILEGES ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" +{ + echo "HortusFox Database Credentials" + echo "Database: $DB_NAME" + echo "Username: $DB_USER" + echo "Password: $DB_PASS" +} >>~/hortusfox.creds +msg_ok "Set up database" + +fetch_and_deploy_gh_release "hortusfox-web" "danielbrendel/hortusfox-web" + +msg_info "Configuring .env" +cp /opt/hortusfox-web/.env.example /opt/hortusfox-web/.env +sed -i "s|^DB_HOST=.*|DB_HOST=localhost|" /opt/hortusfox-web/.env +sed -i "s|^DB_USER=.*|DB_USER=$DB_USER|" /opt/hortusfox-web/.env +sed -i "s|^DB_PASSWORD=.*|DB_PASSWORD=$DB_PASS|" /opt/hortusfox-web/.env +sed -i "s|^DB_DATABASE=.*|DB_DATABASE=$DB_NAME|" /opt/hortusfox-web/.env +sed -i "s|^DB_ENABLE=.*|DB_ENABLE=true|" /opt/hortusfox-web/.env +sed -i "s|^APP_TIMEZONE=.*|APP_TIMEZONE=Europe/Berlin|" /opt/hortusfox-web/.env +msg_ok ".env configured" + +msg_info "Installing PHP dependencies (composer)" +cd /opt/hortusfox-web +$STD composer install --no-dev --optimize-autoloader +msg_ok "PHP dependencies installed" + +msg_info "Running DB migration" +php asatru migrate:fresh +msg_ok "Migration finished" + +msg_info "Configuring Apache vHost" +cat </etc/apache2/sites-available/hortusfox.conf + + ServerAdmin webmaster@localhost + DocumentRoot /opt/hortusfox-web/public + + Options Indexes FollowSymLinks + AllowOverride All + Require all granted + + ErrorLog \${APACHE_LOG_DIR}/hortusfox_error.log + CustomLog \${APACHE_LOG_DIR}/hortusfox_access.log combined + +EOF +$STD a2dissite 000-default +$STD a2ensite hortusfox +$STD a2enmod rewrite +systemctl restart apache2 +msg_ok "Apache configured" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" diff --git a/tools/pve/clean-lxcs.sh b/tools/pve/clean-lxcs.sh index 4edb7b6cd..dc96eaa86 100644 --- a/tools/pve/clean-lxcs.sh +++ b/tools/pve/clean-lxcs.sh @@ -49,10 +49,11 @@ function clean_container() { os=$(pct config "$container" | awk '/^ostype/ {print $2}') echo -e "${BL}[Info]${GN} Cleaning ${name} (${os}) ${CL} \n" if [ "$os" = "alpine" ]; then - pct exec "$container" -- ash -c "apk update && apk cache clean && rm -rf /var/cache/apk/*" + pct exec "$container" -- ash -c "wget -qO- https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/tools/pve/clean.sh | ash" else pct exec "$container" -- bash -c "apt-get -y --purge autoremove && apt-get -y autoclean && bash <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/tools/pve/clean.sh) && rm -rf /var/lib/apt/lists/* && apt-get update" fi + } for container in $(pct list | awk 'NR>1 {print $1}'); do From c79714b2751692c3b3eec22a6032b5888c036f48 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 4 Aug 2025 13:27:24 +0000 Subject: [PATCH 0359/1733] Update .app files --- ct/headers/hortusfox | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/hortusfox diff --git a/ct/headers/hortusfox b/ct/headers/hortusfox new file mode 100644 index 000000000..617179884 --- /dev/null +++ b/ct/headers/hortusfox @@ -0,0 +1,6 @@ + __ __ __ ______ + / / / /___ _____/ /___ _______/ ____/___ _ __ + / /_/ / __ \/ ___/ __/ / / / ___/ /_ / __ \| |/_/ + / __ / /_/ / / / /_/ /_/ (__ ) __/ / /_/ /> < +/_/ /_/\____/_/ \__/\__,_/____/_/ \____/_/|_| + From 1352f870c55a51fbb4a8049b9615a4cdef8e9811 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 15:40:11 +0200 Subject: [PATCH 0360/1733] Update hortusfox-install.sh --- install/hortusfox-install.sh | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/install/hortusfox-install.sh b/install/hortusfox-install.sh index 49c78c5b4..3b39d8f85 100644 --- a/install/hortusfox-install.sh +++ b/install/hortusfox-install.sh @@ -17,7 +17,7 @@ msg_info "Installing Dependencies" $STD apt-get install -y apache2 msg_ok "Installed Dependencies" -PHP_MODULE="exif,pcntl,mysql" PHP_APACHE="YES" PHP_FPM="NO" PHP_VERSION="8.3" setup_php +PHP_MODULE="exif,mysql" PHP_APACHE="YES" PHP_FPM="NO" PHP_VERSION="8.3" setup_php setup_mariadb setup_composer @@ -48,10 +48,24 @@ sed -i "s|^DB_ENABLE=.*|DB_ENABLE=true|" /opt/hortusfox-web/.env sed -i "s|^APP_TIMEZONE=.*|APP_TIMEZONE=Europe/Berlin|" /opt/hortusfox-web/.env msg_ok ".env configured" -msg_info "Installing PHP dependencies (composer)" +msg_info "Setting up HortusFox" cd /opt/hortusfox-web $STD composer install --no-dev --optimize-autoloader -msg_ok "PHP dependencies installed" +mariadb -u root -D $DB_NAME -e "INSERT IGNORE INTO AppModel (workspace, language, created_at) VALUES ('Default Workspace', 'en', NOW());" +php asatru plants:attributes +php asatru calendar:classes +ADMIN_EMAIL="admin@example.com" +ADMIN_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13)" +ADMIN_HASH=$(php -r "echo password_hash('$ADMIN_PASS', PASSWORD_BCRYPT);") +mariadb -u root -D $DB_NAME -e "INSERT IGNORE INTO UserModel (name, email, password, admin) VALUES ('Admin', '$ADMIN_EMAIL', '$ADMIN_HASH', 1);" +{ + echo "" + echo "HortusFox-Admin-Creds:" + echo "E-Mail: $ADMIN_EMAIL" + echo "Passwort: $ADMIN_PASS" +} >>~/hortusfox.creds +mariadb -u root -D $DB_NAME -e "INSERT IGNORE INTO LocationsModel (name, active, created_at) VALUES ('Home', 1, NOW());" +msg_ok "Set up HortusFox" msg_info "Running DB migration" php asatru migrate:fresh From 206e6da46e3152aef1b61e2a495c1baee316797e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 15:52:16 +0200 Subject: [PATCH 0361/1733] Update hortusfox-install.sh --- install/hortusfox-install.sh | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/install/hortusfox-install.sh b/install/hortusfox-install.sh index 3b39d8f85..7b6a4cfac 100644 --- a/install/hortusfox-install.sh +++ b/install/hortusfox-install.sh @@ -48,9 +48,16 @@ sed -i "s|^DB_ENABLE=.*|DB_ENABLE=true|" /opt/hortusfox-web/.env sed -i "s|^APP_TIMEZONE=.*|APP_TIMEZONE=Europe/Berlin|" /opt/hortusfox-web/.env msg_ok ".env configured" -msg_info "Setting up HortusFox" +msg_info "Installing Composer dependencies" cd /opt/hortusfox-web $STD composer install --no-dev --optimize-autoloader +msg_ok "Composer dependencies installed" + +msg_info "Running DB migration" +php asatru migrate:fresh +msg_ok "Migration finished" + +msg_info "Setting up HortusFox" mariadb -u root -D $DB_NAME -e "INSERT IGNORE INTO AppModel (workspace, language, created_at) VALUES ('Default Workspace', 'en', NOW());" php asatru plants:attributes php asatru calendar:classes @@ -67,10 +74,6 @@ mariadb -u root -D $DB_NAME -e "INSERT IGNORE INTO UserModel (name, email, passw mariadb -u root -D $DB_NAME -e "INSERT IGNORE INTO LocationsModel (name, active, created_at) VALUES ('Home', 1, NOW());" msg_ok "Set up HortusFox" -msg_info "Running DB migration" -php asatru migrate:fresh -msg_ok "Migration finished" - msg_info "Configuring Apache vHost" cat </etc/apache2/sites-available/hortusfox.conf From f3c883103b628978a612e0183abf4af525c00d9f Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 15:52:43 +0200 Subject: [PATCH 0362/1733] verbose --- install/hortusfox-install.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/install/hortusfox-install.sh b/install/hortusfox-install.sh index 7b6a4cfac..ede8a7204 100644 --- a/install/hortusfox-install.sh +++ b/install/hortusfox-install.sh @@ -58,20 +58,20 @@ php asatru migrate:fresh msg_ok "Migration finished" msg_info "Setting up HortusFox" -mariadb -u root -D $DB_NAME -e "INSERT IGNORE INTO AppModel (workspace, language, created_at) VALUES ('Default Workspace', 'en', NOW());" +$STD mariadb -u root -D $DB_NAME -e "INSERT IGNORE INTO AppModel (workspace, language, created_at) VALUES ('Default Workspace', 'en', NOW());" php asatru plants:attributes php asatru calendar:classes ADMIN_EMAIL="admin@example.com" ADMIN_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13)" ADMIN_HASH=$(php -r "echo password_hash('$ADMIN_PASS', PASSWORD_BCRYPT);") -mariadb -u root -D $DB_NAME -e "INSERT IGNORE INTO UserModel (name, email, password, admin) VALUES ('Admin', '$ADMIN_EMAIL', '$ADMIN_HASH', 1);" +$STD mariadb -u root -D $DB_NAME -e "INSERT IGNORE INTO UserModel (name, email, password, admin) VALUES ('Admin', '$ADMIN_EMAIL', '$ADMIN_HASH', 1);" { echo "" echo "HortusFox-Admin-Creds:" echo "E-Mail: $ADMIN_EMAIL" echo "Passwort: $ADMIN_PASS" } >>~/hortusfox.creds -mariadb -u root -D $DB_NAME -e "INSERT IGNORE INTO LocationsModel (name, active, created_at) VALUES ('Home', 1, NOW());" +$STD mariadb -u root -D $DB_NAME -e "INSERT IGNORE INTO LocationsModel (name, active, created_at) VALUES ('Home', 1, NOW());" msg_ok "Set up HortusFox" msg_info "Configuring Apache vHost" From 63611a8fe1b5ae9619925dbff9d5b4163a903447 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 15:54:20 +0200 Subject: [PATCH 0363/1733] curl --- ct/alpine.sh | 24 +-- ct/debian.sh | 24 +-- ct/deferred/alpine-homarr.sh | 106 ++++++------- ct/deferred/ampache.sh | 2 +- ct/deferred/ghostfolio.sh | 2 +- ct/deferred/hoodik.sh | 76 ++++----- ct/deferred/koel.sh | 78 ++++----- ct/deferred/netbootxyz.sh | 137 ++++++++-------- ct/deferred/nginxproxymanager.sh | 218 +++++++++++++------------- ct/deferred/ocis.sh | 25 ++- ct/deferred/openwebui.sh | 78 ++++----- ct/deferred/pixelfed.sh | 30 ++-- ct/deferred/roundcubemail.sh | 66 ++++---- ct/deferred/squirrelserversmanager.sh | 2 +- ct/docspell.sh | 2 +- ct/frigate.sh | 18 +-- ct/hanko.sh | 24 +-- ct/healthchecks.sh | 2 +- ct/hortusfox.sh | 8 +- ct/kanba.sh | 24 +-- ct/librenms.sh | 28 ++-- ct/manyfold.sh | 4 +- ct/maxun.sh | 82 +++++----- ct/mediamanager.sh | 92 +++++------ ct/notesnook.sh | 54 +++---- ct/opencloud.sh | 58 +++---- ct/palmr.sh | 92 +++++------ ct/postiz.sh | 62 ++++---- ct/rybbit.sh | 24 +-- ct/tandoor.sh | 102 ++++++------ ct/ubuntu.sh | 24 +-- ct/viseron.sh | 2 +- ct/wallabag.sh | 60 +++---- install/hortusfox-install.sh | 2 +- 34 files changed, 817 insertions(+), 815 deletions(-) diff --git a/ct/alpine.sh b/ct/alpine.sh index 11972a3c5..ea946a60e 100644 --- a/ct/alpine.sh +++ b/ct/alpine.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) # Copyright (c) 2021-2025 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE @@ -20,18 +20,18 @@ color catch_errors function update_script() { - header_info - #check_container_storage - #check_container_resources - UPD=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "SUPPORT" --radiolist --cancel-button Exit-Script "Spacebar = Select" 11 58 1 \ - "1" "Check for Alpine Updates" ON \ - 3>&1 1>&2 2>&3) + header_info + #check_container_storage + #check_container_resources + UPD=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "SUPPORT" --radiolist --cancel-button Exit-Script "Spacebar = Select" 11 58 1 \ + "1" "Check for Alpine Updates" ON \ + 3>&1 1>&2 2>&3) - header_info - if [ "$UPD" == "1" ]; then - apk update && apk upgrade - exit - fi + header_info + if [ "$UPD" == "1" ]; then + apk update && apk upgrade + exit + fi } start diff --git a/ct/debian.sh b/ct/debian.sh index c525e5e22..4bcf91d6c 100644 --- a/ct/debian.sh +++ b/ct/debian.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) # Copyright (c) 2021-2025 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE @@ -22,18 +22,18 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /var ]]; then - msg_error "No ${APP} Installation Found!" + header_info + check_container_storage + check_container_resources + if [[ ! -d /var ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating $APP LXC" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Updated $APP LXC" exit - fi - msg_info "Updating $APP LXC" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "Updated $APP LXC" - exit } start diff --git a/ct/deferred/alpine-homarr.sh b/ct/deferred/alpine-homarr.sh index fc87c9075..c78398d65 100644 --- a/ct/deferred/alpine-homarr.sh +++ b/ct/deferred/alpine-homarr.sh @@ -21,22 +21,22 @@ color catch_errors function update_script() { - header_info - RELEASE=$(curl -fsSL https://api.github.com/repos/homarr-labs/homarr/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') - if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then + header_info + RELEASE=$(curl -fsSL https://api.github.com/repos/homarr-labs/homarr/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') + if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then - msg_info "Stopping Services (Patience)" - systemctl stop homarr - msg_ok "Services Stopped" + msg_info "Stopping Services (Patience)" + systemctl stop homarr + msg_ok "Services Stopped" - msg_info "Backup Data" - mkdir -p /opt/homarr-data-backup - cp /opt/homarr/.env /opt/homarr-data-backup/.env - msg_ok "Backup Data" + msg_info "Backup Data" + mkdir -p /opt/homarr-data-backup + cp /opt/homarr/.env /opt/homarr-data-backup/.env + msg_ok "Backup Data" - msg_info "Updating and rebuilding ${APP} to v${RELEASE} (Patience)" - rm /opt/run_homarr.sh - cat <<'EOF' >/opt/run_homarr.sh + msg_info "Updating and rebuilding ${APP} to v${RELEASE} (Patience)" + rm /opt/run_homarr.sh + cat <<'EOF' >/opt/run_homarr.sh #!/bin/bash set -a source /opt/homarr/.env @@ -58,50 +58,50 @@ node apps/websocket/wssServer.cjs & node apps/nextjs/server.js & PID=$! wait $PID EOF - chmod +x /opt/run_homarr.sh - NODE_VERSION=$(curl -s https://raw.githubusercontent.com/homarr-labs/homarr/dev/package.json | jq -r '.engines.node | split(">=")[1] | split(".")[0]') - NODE_MODULE="pnpm@$(curl -s https://raw.githubusercontent.com/homarr-labs/homarr/dev/package.json | jq -r '.packageManager | split("@")[1]')" - install_node_and_modules - rm -rf /opt/homarr - fetch_and_deploy_gh_release "homarr-labs/homarr" - mv /opt/homarr-data-backup/.env /opt/homarr/.env - cd /opt/homarr - echo "test2" - export NODE_ENV="" - $STD pnpm install --recursive --frozen-lockfile --shamefully-hoist - $STD pnpm build - cp /opt/homarr/apps/nextjs/next.config.ts . - cp /opt/homarr/apps/nextjs/package.json . - cp -r /opt/homarr/packages/db/migrations /opt/homarr_db/migrations - cp -r /opt/homarr/apps/nextjs/.next/standalone/* /opt/homarr - mkdir -p /appdata/redis - cp /opt/homarr/packages/redis/redis.conf /opt/homarr/redis.conf - rm /etc/nginx/nginx.conf - mkdir -p /etc/nginx/templates - cp /opt/homarr/nginx.conf /etc/nginx/templates/nginx.conf + chmod +x /opt/run_homarr.sh + NODE_VERSION=$(curl -fsSL https://raw.githubusercontent.com/homarr-labs/homarr/dev/package.json | jq -r '.engines.node | split(">=")[1] | split(".")[0]') + NODE_MODULE="pnpm@$(curl -fsSL https://raw.githubusercontent.com/homarr-labs/homarr/dev/package.json | jq -r '.packageManager | split("@")[1]')" + install_node_and_modules + rm -rf /opt/homarr + fetch_and_deploy_gh_release "homarr-labs/homarr" + mv /opt/homarr-data-backup/.env /opt/homarr/.env + cd /opt/homarr + echo "test2" + export NODE_ENV="" + $STD pnpm install --recursive --frozen-lockfile --shamefully-hoist + $STD pnpm build + cp /opt/homarr/apps/nextjs/next.config.ts . + cp /opt/homarr/apps/nextjs/package.json . + cp -r /opt/homarr/packages/db/migrations /opt/homarr_db/migrations + cp -r /opt/homarr/apps/nextjs/.next/standalone/* /opt/homarr + mkdir -p /appdata/redis + cp /opt/homarr/packages/redis/redis.conf /opt/homarr/redis.conf + rm /etc/nginx/nginx.conf + mkdir -p /etc/nginx/templates + cp /opt/homarr/nginx.conf /etc/nginx/templates/nginx.conf - mkdir -p /opt/homarr/apps/cli - cp /opt/homarr/packages/cli/cli.cjs /opt/homarr/apps/cli/cli.cjs - echo $'#!/bin/bash\ncd /opt/homarr/apps/cli && node ./cli.cjs "$@"' >/usr/bin/homarr - chmod +x /usr/bin/homarr + mkdir -p /opt/homarr/apps/cli + cp /opt/homarr/packages/cli/cli.cjs /opt/homarr/apps/cli/cli.cjs + echo $'#!/bin/bash\ncd /opt/homarr/apps/cli && node ./cli.cjs "$@"' >/usr/bin/homarr + chmod +x /usr/bin/homarr - mkdir /opt/homarr/build - cp ./node_modules/better-sqlite3/build/Release/better_sqlite3.node ./build/better_sqlite3.node - echo "${RELEASE}" >/opt/${APP}_version.txt - msg_ok "Updated ${APP}" + mkdir /opt/homarr/build + cp ./node_modules/better-sqlite3/build/Release/better_sqlite3.node ./build/better_sqlite3.node + echo "${RELEASE}" >/opt/${APP}_version.txt + msg_ok "Updated ${APP}" - msg_info "Starting Services" - systemctl start homarr - msg_ok "Started Services" - msg_ok "Updated Successfully" - read -p "It's recommended to reboot the LXC after an update, would you like to reboot the LXC now ? (y/n): " choice - if [[ "$choice" =~ ^[Yy]$ ]]; then - reboot + msg_info "Starting Services" + systemctl start homarr + msg_ok "Started Services" + msg_ok "Updated Successfully" + read -p "It's recommended to reboot the LXC after an update, would you like to reboot the LXC now ? (y/n): " choice + if [[ "$choice" =~ ^[Yy]$ ]]; then + reboot + fi + else + msg_ok "No update required. ${APP} is already at v${RELEASE}" fi - else - msg_ok "No update required. ${APP} is already at v${RELEASE}" - fi - exit + exit } start diff --git a/ct/deferred/ampache.sh b/ct/deferred/ampache.sh index 89bca190b..ada18f098 100644 --- a/ct/deferred/ampache.sh +++ b/ct/deferred/ampache.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/ct/deferred/ghostfolio.sh b/ct/deferred/ghostfolio.sh index 184841a14..56002d405 100644 --- a/ct/deferred/ghostfolio.sh +++ b/ct/deferred/ghostfolio.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/ct/deferred/hoodik.sh b/ct/deferred/hoodik.sh index 0f84b5138..0e7597b14 100644 --- a/ct/deferred/hoodik.sh +++ b/ct/deferred/hoodik.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -21,46 +21,46 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/hoodik ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - RELEASE=$(curl -s https://api.github.com/repos/hudikhq/hoodik/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then - msg_info "Stopping Services" - systemctl stop hoodik - msg_ok "Services Stopped" - - msg_info "Updating ${APP} to ${RELEASE}" - cd /opt - if [ -d hoodik_bak ]; then - rm -rf hoodik_bak + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/hoodik ]]; then + msg_error "No ${APP} Installation Found!" + exit fi - mv hoodik hoodik_bak - curl -fsSL "https://github.com/hudikhq/hoodik/archive/refs/tags/${RELEASE}.zip" - unzip -q ${RELEASE}.zip - mv hoodik-${RELEASE} /opt/hoodik - cd /opt/hoodik - cargo update -q - cargo build -q --release - msg_ok "Updated Hoodik" + RELEASE=$(curl -fsSL https://api.github.com/repos/hudikhq/hoodik/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then + msg_info "Stopping Services" + systemctl stop hoodik + msg_ok "Services Stopped" - msg_info "Starting Services" - systemctl start hoodik - msg_ok "Started Services" + msg_info "Updating ${APP} to ${RELEASE}" + cd /opt + if [ -d hoodik_bak ]; then + rm -rf hoodik_bak + fi + mv hoodik hoodik_bak + curl -fsSL "https://github.com/hudikhq/hoodik/archive/refs/tags/${RELEASE}.zip" + unzip -q ${RELEASE}.zip + mv hoodik-${RELEASE} /opt/hoodik + cd /opt/hoodik + cargo update -q + cargo build -q --release + msg_ok "Updated Hoodik" - msg_info "Cleaning Up" - rm -R /opt/${RELEASE}.zip - rm -R /opt/hoodik_bak - msg_ok "Cleaned" - msg_ok "Updated Successfully" - else - msg_ok "No update required. ${APP} is already at ${RELEASE}" - fi - exit + msg_info "Starting Services" + systemctl start hoodik + msg_ok "Started Services" + + msg_info "Cleaning Up" + rm -R /opt/${RELEASE}.zip + rm -R /opt/hoodik_bak + msg_ok "Cleaned" + msg_ok "Updated Successfully" + else + msg_ok "No update required. ${APP} is already at ${RELEASE}" + fi + exit } start diff --git a/ct/deferred/koel.sh b/ct/deferred/koel.sh index 839ac5365..a4b6ea7e8 100644 --- a/ct/deferred/koel.sh +++ b/ct/deferred/koel.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -20,45 +20,45 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/koel ]]; then - msg_error "No ${APP} Installation Found!" + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/koel ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + RELEASE=$(curl -fsSL https://api.github.com/repos/koel/koel/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then + msg_info "Stopping ${APP} Service" + systemctl stop nginx + msg_ok "Stopped ${APP} Service" + + msg_info "Updating ${APP} to v${RELEASE}" + cd /opt + curl -fsSL https://github.com/koel/koel/releases/download/${RELEASE}/koel-${RELEASE}.zip + unzip -q koel-${RELEASE}.zip + cd /opt/koel + composer update --no-interaction >/dev/null 2>&1 + composer install --no-interaction >/dev/null 2>&1 + php artisan migrate --force >/dev/null 2>&1 + php artisan cache:clear >/dev/null 2>&1 + php artisan config:clear >/dev/null 2>&1 + php artisan view:clear >/dev/null 2>&1 + php artisan koel:init --no-interaction >/dev/null 2>&1 + msg_ok "Updated ${APP} to v${RELEASE}" + + msg_info "Starting ${APP} Service" + systemctl start nginx + msg_ok "Started ${APP} Service" + + msg_info "Cleaning up" + rm -rf /opt/koel-${RELEASE}.zip + msg_ok "Cleaned" + msg_ok "Updated Successfully!\n" + else + msg_ok "No update required. ${APP} is already at v${RELEASE}" + fi exit - fi - RELEASE=$(curl -s https://api.github.com/repos/koel/koel/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then - msg_info "Stopping ${APP} Service" - systemctl stop nginx - msg_ok "Stopped ${APP} Service" - - msg_info "Updating ${APP} to v${RELEASE}" - cd /opt - curl -fsSL https://github.com/koel/koel/releases/download/${RELEASE}/koel-${RELEASE}.zip - unzip -q koel-${RELEASE}.zip - cd /opt/koel - composer update --no-interaction >/dev/null 2>&1 - composer install --no-interaction >/dev/null 2>&1 - php artisan migrate --force >/dev/null 2>&1 - php artisan cache:clear >/dev/null 2>&1 - php artisan config:clear >/dev/null 2>&1 - php artisan view:clear >/dev/null 2>&1 - php artisan koel:init --no-interaction >/dev/null 2>&1 - msg_ok "Updated ${APP} to v${RELEASE}" - - msg_info "Starting ${APP} Service" - systemctl start nginx - msg_ok "Started ${APP} Service" - - msg_info "Cleaning up" - rm -rf /opt/koel-${RELEASE}.zip - msg_ok "Cleaned" - msg_ok "Updated Successfully!\n" - else - msg_ok "No update required. ${APP} is already at v${RELEASE}" - fi - exit } start diff --git a/ct/deferred/netbootxyz.sh b/ct/deferred/netbootxyz.sh index 4a3c009db..201073af9 100644 --- a/ct/deferred/netbootxyz.sh +++ b/ct/deferred/netbootxyz.sh @@ -1,13 +1,13 @@ #!/usr/bin/env bash -source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) # Copyright (c) 2021-2023 tteck # Author: tteck (tteckster) # License: MIT # https://github.com/tteck/Proxmox/raw/main/LICENSE function header_info { -clear -cat <<"EOF" + clear + cat <<"EOF" __ __ __ ____ ___ / /_/ /_ ____ ____ / /_ _ ____ ______ / __ \/ _ \/ __/ __ \/ __ \/ __ \/ __/ | |/_/ / / /_ / @@ -29,58 +29,61 @@ color catch_errors function default_settings() { - CT_TYPE="1" - PW="" - CT_ID=$NEXTID - HN=$NSAPP - DISK_SIZE="$var_disk" - CORE_COUNT="$var_cpu" - RAM_SIZE="$var_ram" - BRG="vmbr0" - NET="dhcp" - GATE="" - DISABLEIP6="no" - MTU="" - SD="" - NS="" - MAC="" - VLAN="" - SSH="no" - VERB="no" - echo_default + CT_TYPE="1" + PW="" + CT_ID=$NEXTID + HN=$NSAPP + DISK_SIZE="$var_disk" + CORE_COUNT="$var_cpu" + RAM_SIZE="$var_ram" + BRG="vmbr0" + NET="dhcp" + GATE="" + DISABLEIP6="no" + MTU="" + SD="" + NS="" + MAC="" + VLAN="" + SSH="no" + VERB="no" + echo_default } function update_script() { -header_info -if [[ ! -d /opt/netboot.xyz ]]; then msg_error "No ${APP} Installation Found!"; exit; fi -msg_info "Stopping ${APP}" -systemctl disable netbootxyz.service &>/dev/null -systemctl stop netbootxyz -sleep 1 -msg_ok "Stopped ${APP}" + header_info + if [[ ! -d /opt/netboot.xyz ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Stopping ${APP}" + systemctl disable netbootxyz.service &>/dev/null + systemctl stop netbootxyz + sleep 1 + msg_ok "Stopped ${APP}" -msg_info "Backing up Data" -cp -R /opt/netboot.xyz/config config-backup -cp -R /opt/netboot.xyz/assets assets-backup -sleep 1 -msg_ok "Backed up Data" + msg_info "Backing up Data" + cp -R /opt/netboot.xyz/config config-backup + cp -R /opt/netboot.xyz/assets assets-backup + sleep 1 + msg_ok "Backed up Data" -RELEASE=$(curl -sX GET "https://api.github.com/repos/netbootxyz/netboot.xyz/releases/latest" | awk '/tag_name/{print $4;exit}' FS='[""]') -msg_info "Updating netboot.xyz to ${RELEASE}" -curl --silent -o ${RELEASE}.tar.gz -L "https://github.com/netbootxyz/netboot.xyz/archive/${RELEASE}.tar.gz" &>/dev/null -tar xvzf ${RELEASE}.tar.gz &>/dev/null -VER=$(curl -s https://api.github.com/repos/netbootxyz/netboot.xyz/releases/latest | - grep "tag_name" | - awk '{print substr($2, 2, length($2)-3) }') + RELEASE=$(curl -fsSLX GET "https://api.github.com/repos/netbootxyz/netboot.xyz/releases/latest" | awk '/tag_name/{print $4;exit}' FS='[""]') + msg_info "Updating netboot.xyz to ${RELEASE}" + curl --silent -o ${RELEASE}.tar.gz -L "https://github.com/netbootxyz/netboot.xyz/archive/${RELEASE}.tar.gz" &>/dev/null + tar xvzf ${RELEASE}.tar.gz &>/dev/null + VER=$(curl -fsSL https://api.github.com/repos/netbootxyz/netboot.xyz/releases/latest | + grep "tag_name" | + awk '{print substr($2, 2, length($2)-3) }') -if [ ! -d "/opt/netboot.xyz" ]; then - mv netboot.xyz-${VER} /opt/netboot.xyz -else - cp -R netboot.xyz-${VER}/* /opt/netboot.xyz -fi + if [ ! -d "/opt/netboot.xyz" ]; then + mv netboot.xyz-${VER} /opt/netboot.xyz + else + cp -R netboot.xyz-${VER}/* /opt/netboot.xyz + fi -service_path="/etc/systemd/system/netbootxyz.service" -echo "[Unit] + service_path="/etc/systemd/system/netbootxyz.service" + echo "[Unit] Description=netboot.xyz After=network.target [Service] @@ -93,28 +96,28 @@ ExecStart="ansible-playbook" -i inventory site.yml TimeoutStopSec=30 [Install] WantedBy=multi-user.target" >$service_path -msg_ok "Updated netboot.xyz to ${RELEASE}" + msg_ok "Updated netboot.xyz to ${RELEASE}" -msg_info "Restoring Data" -cp -R config-backup/* /opt/netboot.xyz/config -cp -R assets-backup/* /opt/netboot.xyz/assets -sleep 1 -msg_ok "Restored Data" + msg_info "Restoring Data" + cp -R config-backup/* /opt/netboot.xyz/config + cp -R assets-backup/* /opt/netboot.xyz/assets + sleep 1 + msg_ok "Restored Data" -msg_info "Cleanup" -rm -rf ${RELEASE}.tar.gz -rm -rf netboot.xyz-${VER} -rm -rf config-backup -rm -rf assets-backup -sleep 1 -msg_ok "Cleaned" + msg_info "Cleanup" + rm -rf ${RELEASE}.tar.gz + rm -rf netboot.xyz-${VER} + rm -rf config-backup + rm -rf assets-backup + sleep 1 + msg_ok "Cleaned" -msg_info "Starting ${APP}" -systemctl enable --now netbootxyz.service &>/dev/null -sleep 2 -msg_ok "Started ${APP}" -msg_ok "Updated Successfully" -exit + msg_info "Starting ${APP}" + systemctl enable --now netbootxyz.service &>/dev/null + sleep 2 + msg_ok "Started ${APP}" + msg_ok "Updated Successfully" + exit } start diff --git a/ct/deferred/nginxproxymanager.sh b/ct/deferred/nginxproxymanager.sh index 6b75c62c2..b1ff024d7 100644 --- a/ct/deferred/nginxproxymanager.sh +++ b/ct/deferred/nginxproxymanager.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) # Copyright (c) 2021-2025 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -20,103 +20,103 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -f /lib/systemd/system/npm.service ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - if ! command -v pnpm &>/dev/null; then - msg_info "Installing pnpm" - #export NODE_OPTIONS=--openssl-legacy-provider - $STD npm install -g pnpm@8.15 - msg_ok "Installed pnpm" - fi - RELEASE=$(curl -s https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/releases/latest | - grep "tag_name" | - awk '{print substr($2, 3, length($2)-4) }') - msg_info "Stopping Services" - systemctl stop openresty - systemctl stop npm - msg_ok "Stopped Services" + header_info + check_container_storage + check_container_resources + if [[ ! -f /lib/systemd/system/npm.service ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + if ! command -v pnpm &>/dev/null; then + msg_info "Installing pnpm" + #export NODE_OPTIONS=--openssl-legacy-provider + $STD npm install -g pnpm@8.15 + msg_ok "Installed pnpm" + fi + RELEASE=$(curl -fsSL https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/releases/latest | + grep "tag_name" | + awk '{print substr($2, 3, length($2)-4) }') + msg_info "Stopping Services" + systemctl stop openresty + systemctl stop npm + msg_ok "Stopped Services" - msg_info "Cleaning Old Files" - rm -rf /app \ - /var/www/html \ - /etc/nginx \ - /var/log/nginx \ - /var/lib/nginx \ - $STD /var/cache/nginx - msg_ok "Cleaned Old Files" + msg_info "Cleaning Old Files" + rm -rf /app \ + /var/www/html \ + /etc/nginx \ + /var/log/nginx \ + /var/lib/nginx \ + $STD /var/cache/nginx + msg_ok "Cleaned Old Files" - msg_info "Downloading NPM v${RELEASE}" - curl -fsSL https://codeload.github.com/NginxProxyManager/nginx-proxy-manager/tar.gz/v${RELEASE} -o - | tar -xz - cd nginx-proxy-manager-${RELEASE} - msg_ok "Downloaded NPM v${RELEASE}" + msg_info "Downloading NPM v${RELEASE}" + curl -fsSL https://codeload.github.com/NginxProxyManager/nginx-proxy-manager/tar.gz/v${RELEASE} -o - | tar -xz + cd nginx-proxy-manager-${RELEASE} + msg_ok "Downloaded NPM v${RELEASE}" - msg_info "Setting up Enviroment" - ln -sf /usr/bin/python3 /usr/bin/python - ln -sf /usr/bin/certbot /opt/certbot/bin/certbot - ln -sf /usr/local/openresty/nginx/sbin/nginx /usr/sbin/nginx - ln -sf /usr/local/openresty/nginx/ /etc/nginx - sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" backend/package.json - sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" frontend/package.json - sed -i 's|"fork-me": ".*"|"fork-me": "Proxmox VE Helper-Scripts"|' frontend/js/i18n/messages.json - sed -i "s|https://github.com.*source=nginx-proxy-manager|https://helper-scripts.com|g" frontend/js/app/ui/footer/main.ejs - sed -i 's+^daemon+#daemon+g' docker/rootfs/etc/nginx/nginx.conf - NGINX_CONFS=$(find "$(pwd)" -type f -name "*.conf") - for NGINX_CONF in $NGINX_CONFS; do - sed -i 's+include conf.d+include /etc/nginx/conf.d+g' "$NGINX_CONF" - done - mkdir -p /var/www/html /etc/nginx/logs - cp -r docker/rootfs/var/www/html/* /var/www/html/ - cp -r docker/rootfs/etc/nginx/* /etc/nginx/ - cp docker/rootfs/etc/letsencrypt.ini /etc/letsencrypt.ini - cp docker/rootfs/etc/logrotate.d/nginx-proxy-manager /etc/logrotate.d/nginx-proxy-manager - ln -sf /etc/nginx/nginx.conf /etc/nginx/conf/nginx.conf - rm -f /etc/nginx/conf.d/dev.conf - mkdir -p /tmp/nginx/body \ - /run/nginx \ - /data/nginx \ - /data/custom_ssl \ - /data/logs \ - /data/access \ - /data/nginx/default_host \ - /data/nginx/default_www \ - /data/nginx/proxy_host \ - /data/nginx/redirection_host \ - /data/nginx/stream \ - /data/nginx/dead_host \ - /data/nginx/temp \ - /var/lib/nginx/cache/public \ - /var/lib/nginx/cache/private \ - /var/cache/nginx/proxy_temp - chmod -R 777 /var/cache/nginx - chown root /tmp/nginx - echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" {print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf);" >/etc/nginx/conf.d/include/resolvers.conf - if [ ! -f /data/nginx/dummycert.pem ] || [ ! -f /data/nginx/dummykey.pem ]; then - $STD openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 -subj "/O=Nginx Proxy Manager/OU=Dummy Certificate/CN=localhost" -keyout /data/nginx/dummykey.pem -out /data/nginx/dummycert.pem - fi - mkdir -p /app/global /app/frontend/images - cp -r backend/* /app - cp -r global/* /app/global - $STD python3 -m pip install --no-cache-dir certbot-dns-cloudflare - msg_ok "Setup Enviroment" + msg_info "Setting up Enviroment" + ln -sf /usr/bin/python3 /usr/bin/python + ln -sf /usr/bin/certbot /opt/certbot/bin/certbot + ln -sf /usr/local/openresty/nginx/sbin/nginx /usr/sbin/nginx + ln -sf /usr/local/openresty/nginx/ /etc/nginx + sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" backend/package.json + sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" frontend/package.json + sed -i 's|"fork-me": ".*"|"fork-me": "Proxmox VE Helper-Scripts"|' frontend/js/i18n/messages.json + sed -i "s|https://github.com.*source=nginx-proxy-manager|https://helper-scripts.com|g" frontend/js/app/ui/footer/main.ejs + sed -i 's+^daemon+#daemon+g' docker/rootfs/etc/nginx/nginx.conf + NGINX_CONFS=$(find "$(pwd)" -type f -name "*.conf") + for NGINX_CONF in $NGINX_CONFS; do + sed -i 's+include conf.d+include /etc/nginx/conf.d+g' "$NGINX_CONF" + done + mkdir -p /var/www/html /etc/nginx/logs + cp -r docker/rootfs/var/www/html/* /var/www/html/ + cp -r docker/rootfs/etc/nginx/* /etc/nginx/ + cp docker/rootfs/etc/letsencrypt.ini /etc/letsencrypt.ini + cp docker/rootfs/etc/logrotate.d/nginx-proxy-manager /etc/logrotate.d/nginx-proxy-manager + ln -sf /etc/nginx/nginx.conf /etc/nginx/conf/nginx.conf + rm -f /etc/nginx/conf.d/dev.conf + mkdir -p /tmp/nginx/body \ + /run/nginx \ + /data/nginx \ + /data/custom_ssl \ + /data/logs \ + /data/access \ + /data/nginx/default_host \ + /data/nginx/default_www \ + /data/nginx/proxy_host \ + /data/nginx/redirection_host \ + /data/nginx/stream \ + /data/nginx/dead_host \ + /data/nginx/temp \ + /var/lib/nginx/cache/public \ + /var/lib/nginx/cache/private \ + /var/cache/nginx/proxy_temp + chmod -R 777 /var/cache/nginx + chown root /tmp/nginx + echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" {print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf);" >/etc/nginx/conf.d/include/resolvers.conf + if [ ! -f /data/nginx/dummycert.pem ] || [ ! -f /data/nginx/dummykey.pem ]; then + $STD openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 -subj "/O=Nginx Proxy Manager/OU=Dummy Certificate/CN=localhost" -keyout /data/nginx/dummykey.pem -out /data/nginx/dummycert.pem + fi + mkdir -p /app/global /app/frontend/images + cp -r backend/* /app + cp -r global/* /app/global + $STD python3 -m pip install --no-cache-dir certbot-dns-cloudflare + msg_ok "Setup Enviroment" - msg_info "Building Frontend" - cd ./frontend - $STD pnpm install - $STD pnpm upgrade - $STD pnpm run build - cp -r dist/* /app/frontend - cp -r app-images/* /app/frontend/images - msg_ok "Built Frontend" + msg_info "Building Frontend" + cd ./frontend + $STD pnpm install + $STD pnpm upgrade + $STD pnpm run build + cp -r dist/* /app/frontend + cp -r app-images/* /app/frontend/images + msg_ok "Built Frontend" - msg_info "Initializing Backend" - $STD rm -rf /app/config/default.json - if [ ! -f /app/config/production.json ]; then - cat <<'EOF' >/app/config/production.json + msg_info "Initializing Backend" + $STD rm -rf /app/config/default.json + if [ ! -f /app/config/production.json ]; then + cat <<'EOF' >/app/config/production.json { "database": { "engine": "knex-native", @@ -129,25 +129,25 @@ function update_script() { } } EOF - fi - cd /app - $STD pnpm install - msg_ok "Initialized Backend" + fi + cd /app + $STD pnpm install + msg_ok "Initialized Backend" - msg_info "Starting Services" - sed -i 's/user npm/user root/g; s/^pid/#pid/g' /usr/local/openresty/nginx/conf/nginx.conf - sed -i 's/su npm npm/su root root/g' /etc/logrotate.d/nginx-proxy-manager - sed -i 's/include-system-site-packages = false/include-system-site-packages = true/g' /opt/certbot/pyvenv.cfg - systemctl enable -q --now openresty - systemctl enable -q --now npm - msg_ok "Started Services" + msg_info "Starting Services" + sed -i 's/user npm/user root/g; s/^pid/#pid/g' /usr/local/openresty/nginx/conf/nginx.conf + sed -i 's/su npm npm/su root root/g' /etc/logrotate.d/nginx-proxy-manager + sed -i 's/include-system-site-packages = false/include-system-site-packages = true/g' /opt/certbot/pyvenv.cfg + systemctl enable -q --now openresty + systemctl enable -q --now npm + msg_ok "Started Services" - msg_info "Cleaning up" - rm -rf ~/nginx-proxy-manager-* - msg_ok "Cleaned" + msg_info "Cleaning up" + rm -rf ~/nginx-proxy-manager-* + msg_ok "Cleaned" - msg_ok "Updated Successfully" - exit + msg_ok "Updated Successfully" + exit } start diff --git a/ct/deferred/ocis.sh b/ct/deferred/ocis.sh index b28a1a36d..167b4593d 100644 --- a/ct/deferred/ocis.sh +++ b/ct/deferred/ocis.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) # Copyright (c) 2021-2025 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE @@ -20,18 +20,18 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /var ]]; then - msg_error "No ${APP} Installation Found!" + header_info + check_container_storage + check_container_resources + if [[ ! -d /var ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating $APP LXC" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Updated $APP LXC" exit - fi - msg_info "Updating $APP LXC" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "Updated $APP LXC" - exit } start @@ -42,4 +42,3 @@ msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9200${CL}" - diff --git a/ct/deferred/openwebui.sh b/ct/deferred/openwebui.sh index 96647a974..85341bd9b 100644 --- a/ct/deferred/openwebui.sh +++ b/ct/deferred/openwebui.sh @@ -20,47 +20,47 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/open-webui ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if [ -x "/usr/bin/ollama" ]; then - msg_info "Updating Ollama" - OLLAMA_VERSION=$(ollama -v | awk '{print $NF}') - RELEASE=$(curl -s https://api.github.com/repos/ollama/ollama/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4)}') - if [ "$OLLAMA_VERSION" != "$RELEASE" ]; then - curl -fsSLO https://ollama.com/download/ollama-linux-amd64.tgz - tar -C /usr -xzf ollama-linux-amd64.tgz - rm -rf ollama-linux-amd64.tgz - msg_ok "Ollama updated to version $RELEASE" - else - msg_ok "Ollama is already up to date." + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/open-webui ]]; then + msg_error "No ${APP} Installation Found!" + exit fi - fi - msg_info "Updating ${APP} (Patience)" - systemctl stop open-webui.service - mkdir -p /opt/openwebui-backup - cp -rf /opt/openwebui/backend/data /opt/openwebui-backup - cp /opt/openwebui/.env /opt - rm -rf /opt/openwebui - fetch_and_deploy_gh_release "open-webui/open-webui" - cd /opt/openwebui - $STD npm install - export NODE_OPTIONS="--max-old-space-size=3584" - sed -i "s/git rev-parse HEAD/openssl rand -hex 20/g" /opt/openwebui/svelte.config.js - $STD npm run build - cd ./backend - $STD pip install -r requirements.txt -U - cp -rf /opt/openwebui-backup/* /opt/openwebui/backend - mv /opt/.env /opt/openwebui/ - systemctl start open-webui.service - msg_ok "Updated Successfully" - exit + if [ -x "/usr/bin/ollama" ]; then + msg_info "Updating Ollama" + OLLAMA_VERSION=$(ollama -v | awk '{print $NF}') + RELEASE=$(curl -fsSL https://api.github.com/repos/ollama/ollama/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4)}') + if [ "$OLLAMA_VERSION" != "$RELEASE" ]; then + curl -fsSLO https://ollama.com/download/ollama-linux-amd64.tgz + tar -C /usr -xzf ollama-linux-amd64.tgz + rm -rf ollama-linux-amd64.tgz + msg_ok "Ollama updated to version $RELEASE" + else + msg_ok "Ollama is already up to date." + fi + fi + + msg_info "Updating ${APP} (Patience)" + systemctl stop open-webui.service + mkdir -p /opt/openwebui-backup + cp -rf /opt/openwebui/backend/data /opt/openwebui-backup + cp /opt/openwebui/.env /opt + rm -rf /opt/openwebui + fetch_and_deploy_gh_release "open-webui/open-webui" + cd /opt/openwebui + $STD npm install + export NODE_OPTIONS="--max-old-space-size=3584" + sed -i "s/git rev-parse HEAD/openssl rand -hex 20/g" /opt/openwebui/svelte.config.js + $STD npm run build + cd ./backend + $STD pip install -r requirements.txt -U + cp -rf /opt/openwebui-backup/* /opt/openwebui/backend + mv /opt/.env /opt/openwebui/ + systemctl start open-webui.service + msg_ok "Updated Successfully" + exit } start diff --git a/ct/deferred/pixelfed.sh b/ct/deferred/pixelfed.sh index c053e0795..8ddab519c 100644 --- a/ct/deferred/pixelfed.sh +++ b/ct/deferred/pixelfed.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -19,21 +19,21 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/pixelfed ]]; then - msg_error "No ${APP} Installation Found!" + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/pixelfed ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + RELEASE=$(curl -fsSL https://api.github.com/repos/xxxx/xxxx/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then + msg_info "Updating ${APP} to ${RELEASE}" + cd /opt + else + msg_ok "No update required. ${APP} is already at ${RELEASE}" + fi exit - fi - RELEASE=$(curl -s https://api.github.com/repos/xxxx/xxxx/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then - msg_info "Updating ${APP} to ${RELEASE}" - cd /opt - else - msg_ok "No update required. ${APP} is already at ${RELEASE}" - fi - exit } start diff --git a/ct/deferred/roundcubemail.sh b/ct/deferred/roundcubemail.sh index 4423aea54..75d0e3bcd 100644 --- a/ct/deferred/roundcubemail.sh +++ b/ct/deferred/roundcubemail.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -20,39 +20,39 @@ color catch_errors function update_script() { - header_info - if [[ ! -d /opt/roundcubemail ]]; then - msg_error "No ${APP} Installation Found!" + header_info + if [[ ! -d /opt/roundcubemail ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + if (($(df /boot | awk 'NR==2{gsub("%","",$5); print $5}') > 80)); then + read -r -p "Warning: Storage is dangerously low, continue anyway? " prompt + [[ ${prompt,,} =~ ^(y|yes)$ ]] || exit + fi + RELEASE=$(curl -fsSL https://api.github.com/repos/roundcube/roundcubemail/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then + msg_info "Updating ${APP} to ${RELEASE}" + cd /opt + curl -fsSL "https://github.com/roundcube/roundcubemail/releases/download/${RELEASE}/roundcubemail-${RELEASE}-complete.tar.gz" + tar -xf roundcubemail-${RELEASE}-complete.tar.gz + mv roundcubemail-${RELEASE} /opt/roundcubemail + cd /opt/roundcubemail + COMPOSER_ALLOW_SUPERUSER=1 composer install --no-dev + chown -R www-data:www-data temp/ logs/ + msg_ok "Updated ${APP}" + + msg_info "Reload Apache2" + systemctl reload apache2 + msg_ok "Apache2 Reloaded" + + msg_info "Cleaning Up" + rm -rf /opt/roundcubemail-${RELEASE}-complete.tar.gz + msg_ok "Cleaned" + msg_ok "Updated Successfully" + else + msg_ok "No update required. ${APP} is already at ${RELEASE}" + fi exit - fi - if (($(df /boot | awk 'NR==2{gsub("%","",$5); print $5}') > 80)); then - read -r -p "Warning: Storage is dangerously low, continue anyway? " prompt - [[ ${prompt,,} =~ ^(y|yes)$ ]] || exit - fi - RELEASE=$(curl -s https://api.github.com/repos/roundcube/roundcubemail/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then - msg_info "Updating ${APP} to ${RELEASE}" - cd /opt - curl -fsSL "https://github.com/roundcube/roundcubemail/releases/download/${RELEASE}/roundcubemail-${RELEASE}-complete.tar.gz" - tar -xf roundcubemail-${RELEASE}-complete.tar.gz - mv roundcubemail-${RELEASE} /opt/roundcubemail - cd /opt/roundcubemail - COMPOSER_ALLOW_SUPERUSER=1 composer install --no-dev - chown -R www-data:www-data temp/ logs/ - msg_ok "Updated ${APP}" - - msg_info "Reload Apache2" - systemctl reload apache2 - msg_ok "Apache2 Reloaded" - - msg_info "Cleaning Up" - rm -rf /opt/roundcubemail-${RELEASE}-complete.tar.gz - msg_ok "Cleaned" - msg_ok "Updated Successfully" - else - msg_ok "No update required. ${APP} is already at ${RELEASE}" - fi - exit } start diff --git a/ct/deferred/squirrelserversmanager.sh b/ct/deferred/squirrelserversmanager.sh index 9927d23c7..4403ab5f0 100644 --- a/ct/deferred/squirrelserversmanager.sh +++ b/ct/deferred/squirrelserversmanager.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/ct/docspell.sh b/ct/docspell.sh index 3ff1cd3bd..93eb6806f 100644 --- a/ct/docspell.sh +++ b/ct/docspell.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/ct/frigate.sh b/ct/frigate.sh index a5a5c7c77..479c6b98d 100644 --- a/ct/frigate.sh +++ b/ct/frigate.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Authors: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -20,15 +20,15 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -f /etc/systemd/system/frigate.service ]]; then - msg_error "No ${APP} Installation Found!" + header_info + check_container_storage + check_container_resources + if [[ ! -f /etc/systemd/system/frigate.service ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_error "To update Frigate, create a new container and transfer your configuration." exit - fi - msg_error "To update Frigate, create a new container and transfer your configuration." - exit } start diff --git a/ct/hanko.sh b/ct/hanko.sh index 2a8dda15b..de3079c26 100644 --- a/ct/hanko.sh +++ b/ct/hanko.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) # Copyright (c) 2021-2025 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE @@ -20,18 +20,18 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /var ]]; then - msg_error "No ${APP} Installation Found!" + header_info + check_container_storage + check_container_resources + if [[ ! -d /var ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating $APP LXC" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Updated $APP LXC" exit - fi - msg_info "Updating $APP LXC" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "Updated $APP LXC" - exit } start diff --git a/ct/healthchecks.sh b/ct/healthchecks.sh index 9c6f404c3..8b55f7fdb 100644 --- a/ct/healthchecks.sh +++ b/ct/healthchecks.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/ct/hortusfox.sh b/ct/hortusfox.sh index 63ea9db3e..0936a87cc 100644 --- a/ct/hortusfox.sh +++ b/ct/hortusfox.sh @@ -1,9 +1,9 @@ #!/usr/bin/env bash -source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG -# Author: bvdberg01 +# Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/manyfold3d/manyfold +# Source: https://github.com/danielbrendel/hortusfox-web APP="HortusFox" var_tags="${var_tags:-network}" @@ -27,7 +27,7 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - RELEASE=$(curl -s https://api.github.com/repos/danielbrendel/hortusfox-web/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + RELEASE=$(curl -fsSL https://api.github.com/repos/danielbrendel/hortusfox-web/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then msg_info "Stopping Service" systemctl stop apache2 diff --git a/ct/kanba.sh b/ct/kanba.sh index 7beb967ad..270f5e00f 100644 --- a/ct/kanba.sh +++ b/ct/kanba.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE @@ -20,18 +20,18 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /var ]]; then - msg_error "No ${APP} Installation Found!" + header_info + check_container_storage + check_container_resources + if [[ ! -d /var ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating $APP LXC" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Updated $APP LXC" exit - fi - msg_info "Updating $APP LXC" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "Updated $APP LXC" - exit } start diff --git a/ct/librenms.sh b/ct/librenms.sh index 84eb74b40..aaa71a1af 100644 --- a/ct/librenms.sh +++ b/ct/librenms.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: michelroegl-brunner # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE @@ -20,20 +20,20 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [ ! -d /opt/librenms ]; then - msg_error "No ${APP} Installation Found!" - exit - fi - msg_info "Updating ${APP} Installation" - su librenms - cd /opt/librenms - ./daily.sh - msg_ok "Updated ${APP} Installation" + header_info + check_container_storage + check_container_resources + if [ ! -d /opt/librenms ]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating ${APP} Installation" + su librenms + cd /opt/librenms + ./daily.sh + msg_ok "Updated ${APP} Installation" - exit + exit } start diff --git a/ct/manyfold.sh b/ct/manyfold.sh index b54ee9b15..2d782d551 100644 --- a/ct/manyfold.sh +++ b/ct/manyfold.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -27,7 +27,7 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - RELEASE=$(curl -s https://api.github.com/repos/benjaminjonard/manyfold/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + RELEASE=$(curl -fsSL https://api.github.com/repos/benjaminjonard/manyfold/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then msg_info "Stopping Service" systemctl stop apache2 diff --git a/ct/maxun.sh b/ct/maxun.sh index ee0b3819e..a5561dc56 100644 --- a/ct/maxun.sh +++ b/ct/maxun.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -20,47 +20,47 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/maxun ]]; then - msg_error "No ${APP} Installation Found!" + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/maxun ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + RELEASE=$(curl -fsSL https://api.github.com/repos/getmaxun/maxun/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') + if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then + msg_info "Stopping Services" + systemctl stop maxun minio redis + msg_ok "Services Stopped" + + msg_info "Updating ${APP} to v${RELEASE}" + mv /opt/maxun /opt/maxun_bak + cd /opt + curl -fsSL "https://github.com/getmaxun/maxun/archive/refs/tags/v${RELEASE}.zip" + unzip -q v${RELEASE}.zip + mv maxun-${RELEASE} /opt/maxun + mv /opt/maxun_bak/.env /opt/maxun/ + cd /opt/maxun + npm install --legacy-peer-deps + cd /opt/maxun/maxun-core + npm install --legacy-peer-deps + cd /opt/maxun + npx playwright install --with-deps chromium + npx playwright install-deps + "${RELEASE}" >/opt/${APP}_version.txt + + msg_info "Starting Services" + systemctl start minio redis maxun + msg_ok "Started Services" + + msg_info "Cleaning Up" + rm -rf /opt/v${RELEASE}.zip + msg_ok "Cleaned" + msg_ok "Updated Successfully" + else + msg_ok "No update required. ${APP} is already at v${RELEASE}" + fi exit - fi - RELEASE=$(curl -s https://api.github.com/repos/getmaxun/maxun/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') - if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then - msg_info "Stopping Services" - systemctl stop maxun minio redis - msg_ok "Services Stopped" - - msg_info "Updating ${APP} to v${RELEASE}" - mv /opt/maxun /opt/maxun_bak - cd /opt - curl -fsSL "https://github.com/getmaxun/maxun/archive/refs/tags/v${RELEASE}.zip" - unzip -q v${RELEASE}.zip - mv maxun-${RELEASE} /opt/maxun - mv /opt/maxun_bak/.env /opt/maxun/ - cd /opt/maxun - npm install --legacy-peer-deps - cd /opt/maxun/maxun-core - npm install --legacy-peer-deps - cd /opt/maxun - npx playwright install --with-deps chromium - npx playwright install-deps - "${RELEASE}" >/opt/${APP}_version.txt - - msg_info "Starting Services" - systemctl start minio redis maxun - msg_ok "Started Services" - - msg_info "Cleaning Up" - rm -rf /opt/v${RELEASE}.zip - msg_ok "Cleaned" - msg_ok "Updated Successfully" - else - msg_ok "No update required. ${APP} is already at v${RELEASE}" - fi - exit } start diff --git a/ct/mediamanager.sh b/ct/mediamanager.sh index be6e41f96..5290a9b9d 100644 --- a/ct/mediamanager.sh +++ b/ct/mediamanager.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -20,52 +20,52 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/mediamanager ]]; then - msg_error "No ${APP} Installation Found!" + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/mediamanager ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + RELEASE=$(curl -fsSL https://api.github.com/repos/maxdorninger/MediaManager/releases/latest | yq '.tag_name' | sed 's/^v//') + if [[ "${RELEASE}" != "$(cat ~/.mediamanager 2>/dev/null)" ]] || [[ ! -f ~/.mediamanager ]]; then + msg_info "Stopping Service" + systemctl stop mediamanager + msg_ok "Stopped Service" + + msg_info "Updating ${APP}" + fetch_and_deploy_gh_release "MediaManager" "maxdorninger/MediaManager" "tarball" "latest" "/opt/mediamanager" + MM_DIR="/opt/mm" + export CONFIG_DIR="${MM_DIR}/config" + export FRONTEND_FILES_DIR="${MM_DIR}/web/build" + export BASE_PATH="" + export PUBLIC_VERSION="" + export PUBLIC_API_URL="${BASE_PATH}/api/v1" + export BASE_PATH="${BASE_PATH}/web" + cd /opt/mediamanager/web + $STD npm ci + $STD npm run build + rm -rf "$FRONTEND_FILES_DIR"/build + cp -r build "$FRONTEND_FILES_DIR" + + export BASE_PATH="" + export VIRTUAL_ENV="/opt/${MM_DIR}/venv" + cd /opt/mediamanager + rm -rf "$MM_DIR"/{media_manager,alembic*} + cp -r {media_manager,alembic*} "$MM_DIR" + $STD /usr/local/bin/uv sync --locked --active + msg_ok "Updated $APP" + + msg_info "Starting Service" + systemctl start mediamanager + msg_ok "Started Service" + + msg_ok "Updated Successfully" + else + msg_ok "Already up to date" + fi exit - fi - - RELEASE=$(curl -fsSL https://api.github.com/repos/maxdorninger/MediaManager/releases/latest | yq '.tag_name' | sed 's/^v//') - if [[ "${RELEASE}" != "$(cat ~/.mediamanager 2>/dev/null)" ]] || [[ ! -f ~/.mediamanager ]]; then - msg_info "Stopping Service" - systemctl stop mediamanager - msg_ok "Stopped Service" - - msg_info "Updating ${APP}" - fetch_and_deploy_gh_release "MediaManager" "maxdorninger/MediaManager" "tarball" "latest" "/opt/mediamanager" - MM_DIR="/opt/mm" - export CONFIG_DIR="${MM_DIR}/config" - export FRONTEND_FILES_DIR="${MM_DIR}/web/build" - export BASE_PATH="" - export PUBLIC_VERSION="" - export PUBLIC_API_URL="${BASE_PATH}/api/v1" - export BASE_PATH="${BASE_PATH}/web" - cd /opt/mediamanager/web - $STD npm ci - $STD npm run build - rm -rf "$FRONTEND_FILES_DIR"/build - cp -r build "$FRONTEND_FILES_DIR" - - export BASE_PATH="" - export VIRTUAL_ENV="/opt/${MM_DIR}/venv" - cd /opt/mediamanager - rm -rf "$MM_DIR"/{media_manager,alembic*} - cp -r {media_manager,alembic*} "$MM_DIR" - $STD /usr/local/bin/uv sync --locked --active - msg_ok "Updated $APP" - - msg_info "Starting Service" - systemctl start mediamanager - msg_ok "Started Service" - - msg_ok "Updated Successfully" - else - msg_ok "Already up to date" - fi - exit } start diff --git a/ct/notesnook.sh b/ct/notesnook.sh index 942db0c32..3d1fbb3cf 100644 --- a/ct/notesnook.sh +++ b/ct/notesnook.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -20,33 +20,33 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/notesnook ]]; then - msg_error "No ${APP} Installation Found!" + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/notesnook ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + msg_info "Stopping Service" + systemctl stop notesnook + msg_ok "Stopped Service" + + msg_info "Updating ${APP} (Patience)" + rm -rf /opt/notesnook + fetch_and_deploy_gh_release "notesnook" "streetwriters/notesnook" "tarball" + cd /opt/notesnook + export NODE_OPTIONS="--max-old-space-size=2560" + $STD npm install + $STD npm run build:web + msg_ok "Updated $APP" + + msg_info "Starting Service" + systemctl start notesnook + msg_ok "Started Service" + + msg_ok "Updated Successfully" exit - fi - - msg_info "Stopping Service" - systemctl stop notesnook - msg_ok "Stopped Service" - - msg_info "Updating ${APP} (Patience)" - rm -rf /opt/notesnook - fetch_and_deploy_gh_release "notesnook" "streetwriters/notesnook" "tarball" - cd /opt/notesnook - export NODE_OPTIONS="--max-old-space-size=2560" - $STD npm install - $STD npm run build:web - msg_ok "Updated $APP" - - msg_info "Starting Service" - systemctl start notesnook - msg_ok "Started Service" - - msg_ok "Updated Successfully" - exit } start diff --git a/ct/opencloud.sh b/ct/opencloud.sh index dd11f29a9..2c428f93c 100644 --- a/ct/opencloud.sh +++ b/ct/opencloud.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE @@ -20,36 +20,36 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources + header_info + check_container_storage + check_container_resources - if [[ ! -d /etc/opencloud ]]; then - msg_error "No ${APP} Installation Found!" + if [[ ! -d /etc/opencloud ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + RELEASE=$(curl -fsSL https://api.github.com/repos/opencloud-eu/opencloud/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') + if [[ "${RELEASE}" != "$(cat /etc/opencloud/version)" ]] || [[ ! -f /etc/opencloud/version ]]; then + msg_info "Stopping $APP" + systemctl stop opencloud opencloud-wopi + msg_ok "Stopped $APP" + + msg_info "Updating $APP to v${RELEASE}" + curl -fsSL "https://github.com/opencloud-eu/opencloud/releases/download/v${RELEASE}/opencloud-${RELEASE}-linux-amd64" -o /usr/bin/opencloud + chmod +x /usr/bin/opencloud + msg_ok "Updated $APP to v${RELEASE}" + + msg_info "Starting $APP" + systemctl start opencloud opencloud-wopi + msg_ok "Started $APP" + + echo "${RELEASE}" >/etc/opencloud/version + msg_ok "Update Successful" + else + msg_ok "No update required. ${APP} is already at v${RELEASE}" + fi exit - fi - - RELEASE=$(curl -s https://api.github.com/repos/opencloud-eu/opencloud/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') - if [[ "${RELEASE}" != "$(cat /etc/opencloud/version)" ]] || [[ ! -f /etc/opencloud/version ]]; then - msg_info "Stopping $APP" - systemctl stop opencloud opencloud-wopi - msg_ok "Stopped $APP" - - msg_info "Updating $APP to v${RELEASE}" - curl -fsSL "https://github.com/opencloud-eu/opencloud/releases/download/v${RELEASE}/opencloud-${RELEASE}-linux-amd64" -o /usr/bin/opencloud - chmod +x /usr/bin/opencloud - msg_ok "Updated $APP to v${RELEASE}" - - msg_info "Starting $APP" - systemctl start opencloud opencloud-wopi - msg_ok "Started $APP" - - echo "${RELEASE}" >/etc/opencloud/version - msg_ok "Update Successful" - else - msg_ok "No update required. ${APP} is already at v${RELEASE}" - fi - exit } start diff --git a/ct/palmr.sh b/ct/palmr.sh index fd4c41a10..3c026ab45 100644 --- a/ct/palmr.sh +++ b/ct/palmr.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: vhsdream # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -20,52 +20,52 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/palmr_data ]]; then - msg_error "No ${APP} Installation Found!" + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/palmr_data ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + RELEASE=$(curl -fsSL https://api.github.com/repos/kyantech/palmr/releases/latest | yq '.tag_name' | sed 's/^v//') + if [[ "${RELEASE}" != "$(cat ~/.palmr 2>/dev/null)" ]] || [[ ! -f ~/.palmr ]]; then + msg_info "Stopping Services" + systemctl stop palmr-frontend palmr-backend + msg_ok "Stopped Services" + + msg_info "Updating ${APP}" + cp /opt/palmr/apps/server/.env /opt/palmr.env + rm -rf /opt/palmr + fetch_and_deploy_gh_release "Palmr" "kyantech/Palmr" "tarball" "latest" "/opt/palmr" + PNPM="$(jq -r '.packageManager' /opt/palmr/package.json)" + NODE_VERSION="20" NODE_MODULE="$PNPM" setup_nodejs + cd /opt/palmr/apps/server + PALMR_DIR="/opt/palmr_data" + # export PALMR_DB="${PALMR_DIR}/palmr.db" + $STD pnpm install + mv /opt/palmr.env ./.env + $STD pnpm dlx prisma generate + $STD pnpm dlx prisma migrate deploy + $STD pnpm build + + cd /opt/palmr/apps/web + export NODE_ENV=production + export NEXT_TELEMETRY_DISABLED=1 + mv ./.env.example ./.env + $STD pnpm install + $STD pnpm build + msg_ok "Updated $APP" + + msg_info "Starting Services" + systemctl start palmr-backend palmr-frontend + msg_ok "Started Services" + + msg_ok "Updated Successfully" + else + msg_ok "Already up to date" + fi exit - fi - - RELEASE=$(curl -fsSL https://api.github.com/repos/kyantech/palmr/releases/latest | yq '.tag_name' | sed 's/^v//') - if [[ "${RELEASE}" != "$(cat ~/.palmr 2>/dev/null)" ]] || [[ ! -f ~/.palmr ]]; then - msg_info "Stopping Services" - systemctl stop palmr-frontend palmr-backend - msg_ok "Stopped Services" - - msg_info "Updating ${APP}" - cp /opt/palmr/apps/server/.env /opt/palmr.env - rm -rf /opt/palmr - fetch_and_deploy_gh_release "Palmr" "kyantech/Palmr" "tarball" "latest" "/opt/palmr" - PNPM="$(jq -r '.packageManager' /opt/palmr/package.json)" - NODE_VERSION="20" NODE_MODULE="$PNPM" setup_nodejs - cd /opt/palmr/apps/server - PALMR_DIR="/opt/palmr_data" - # export PALMR_DB="${PALMR_DIR}/palmr.db" - $STD pnpm install - mv /opt/palmr.env ./.env - $STD pnpm dlx prisma generate - $STD pnpm dlx prisma migrate deploy - $STD pnpm build - - cd /opt/palmr/apps/web - export NODE_ENV=production - export NEXT_TELEMETRY_DISABLED=1 - mv ./.env.example ./.env - $STD pnpm install - $STD pnpm build - msg_ok "Updated $APP" - - msg_info "Starting Services" - systemctl start palmr-backend palmr-frontend - msg_ok "Started Services" - - msg_ok "Updated Successfully" - else - msg_ok "Already up to date" - fi - exit } start diff --git a/ct/postiz.sh b/ct/postiz.sh index c647b4fb5..e53553677 100644 --- a/ct/postiz.sh +++ b/ct/postiz.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -20,38 +20,38 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources + header_info + check_container_storage + check_container_resources - if [[ ! -f /etc/systemd/system/postiz.service ]]; then - msg_error "No ${APP} Installation Found!" + if [[ ! -f /etc/systemd/system/postiz.service ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + RELEASE=$(curl -fsSL https://api.github.com/repos/Casvt/Kapowarr/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + if [[ "${RELEASE}" != "$(cat $HOME/.kapowarr)" ]] || [[ ! -f $HOME/.kapowarr ]]; then + msg_info "Stopping $APP" + systemctl stop kapowarr + msg_ok "Stopped $APP" + + msg_info "Creating Backup" + mv /opt/kapowarr/db /opt/ + msg_ok "Backup Created" + + msg_info "Updating $APP to ${RELEASE}" + fetch_and_deploy_gh_release "kapowarr" "Casvt/Kapowarr" + mv /opt/db /opt/kapowarr + msg_ok "Updated $APP to ${RELEASE}" + + msg_info "Starting $APP" + systemctl start kapowarr + msg_ok "Started $APP" + + msg_ok "Update Successful" + else + msg_ok "No update required. ${APP} is already at ${RELEASE}" + fi exit - fi - RELEASE=$(curl -s https://api.github.com/repos/Casvt/Kapowarr/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - if [[ "${RELEASE}" != "$(cat $HOME/.kapowarr)" ]] || [[ ! -f $HOME/.kapowarr ]]; then - msg_info "Stopping $APP" - systemctl stop kapowarr - msg_ok "Stopped $APP" - - msg_info "Creating Backup" - mv /opt/kapowarr/db /opt/ - msg_ok "Backup Created" - - msg_info "Updating $APP to ${RELEASE}" - fetch_and_deploy_gh_release "kapowarr" "Casvt/Kapowarr" - mv /opt/db /opt/kapowarr - msg_ok "Updated $APP to ${RELEASE}" - - msg_info "Starting $APP" - systemctl start kapowarr - msg_ok "Started $APP" - - msg_ok "Update Successful" - else - msg_ok "No update required. ${APP} is already at ${RELEASE}" - fi - exit } start diff --git a/ct/rybbit.sh b/ct/rybbit.sh index e9164be40..e523bde2b 100644 --- a/ct/rybbit.sh +++ b/ct/rybbit.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE @@ -20,18 +20,18 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /var ]]; then - msg_error "No ${APP} Installation Found!" + header_info + check_container_storage + check_container_resources + if [[ ! -d /var ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating $APP LXC" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Updated $APP LXC" exit - fi - msg_info "Updating $APP LXC" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "Updated $APP LXC" - exit } start diff --git a/ct/tandoor.sh b/ct/tandoor.sh index 40cfb61ac..248df63d5 100644 --- a/ct/tandoor.sh +++ b/ct/tandoor.sh @@ -20,68 +20,68 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/tandoor ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/tandoor ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi - if [[ ! -f ~/.tandoor ]]; then - msg_error "v1 Installation found, please create an new LXC!" - exit - fi + if [[ ! -f ~/.tandoor ]]; then + msg_error "v1 Installation found, please create an new LXC!" + exit + fi - RELEASE=$(curl -fsSL https://api.github.com/repos/TandoorRecipes/recipes/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - if [[ "${RELEASE}" != "$(cat ~/.tandoor 2>/dev/null)" ]] || [[ ! -f ~/.tandoor ]]; then - msg_info "Stopping $APP" - systemctl stop tandoor - msg_ok "Stopped $APP" + RELEASE=$(curl -fsSL https://api.github.com/repos/TandoorRecipes/recipes/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + if [[ "${RELEASE}" != "$(cat ~/.tandoor 2>/dev/null)" ]] || [[ ! -f ~/.tandoor ]]; then + msg_info "Stopping $APP" + systemctl stop tandoor + msg_ok "Stopped $APP" - msg_info "Creating Backup" - BACKUP_FILE="/opt/tandoor_backup_$(date +%F).tar.gz" - $STD tar -czf "$BACKUP_FILE" /opt/tandoor/{.env,start.sh} /opt/tandoor/database/ &>/dev/null - mv /opt/tandoor/.env /opt/.env - msg_ok "Backup Created" + msg_info "Creating Backup" + BACKUP_FILE="/opt/tandoor_backup_$(date +%F).tar.gz" + $STD tar -czf "$BACKUP_FILE" /opt/tandoor/{.env,start.sh} /opt/tandoor/database/ &>/dev/null + mv /opt/tandoor/.env /opt/.env + msg_ok "Backup Created" - NODE_VERSION="20" NODE_MODULE="yarn@latest" setup_nodejs - fetch_and_deploy_gh_release "tandoor" "TandoorRecipes/recipes" "tarball" "latest" "/opt/tandoor" - PYTHON_VERSION="3.13" setup_uv + NODE_VERSION="20" NODE_MODULE="yarn@latest" setup_nodejs + fetch_and_deploy_gh_release "tandoor" "TandoorRecipes/recipes" "tarball" "latest" "/opt/tandoor" + PYTHON_VERSION="3.13" setup_uv - msg_info "Updating $APP to ${RELEASE}" - mkdir -p /opt/tandoor/{config,api,mediafiles,staticfiles} - mv /opt/.env /opt/tandoor/.env - cd /opt/tandoor - $STD uv venv .venv --python=python3 - $STD uv pip install -r requirements.txt --python .venv/bin/python - cd /opt/tandoor/vue3 - $STD yarn install - $STD yarn build - TANDOOR_VERSION="$(curl -s https://api.github.com/repos/TandoorRecipes/recipes/releases/latest | jq -r .tag_name)" - cat </opt/tandoor/cookbook/version_info.py + msg_info "Updating $APP to ${RELEASE}" + mkdir -p /opt/tandoor/{config,api,mediafiles,staticfiles} + mv /opt/.env /opt/tandoor/.env + cd /opt/tandoor + $STD uv venv .venv --python=python3 + $STD uv pip install -r requirements.txt --python .venv/bin/python + cd /opt/tandoor/vue3 + $STD yarn install + $STD yarn build + TANDOOR_VERSION="$(curl -fsSL https://api.github.com/repos/TandoorRecipes/recipes/releases/latest | jq -r .tag_name)" + cat </opt/tandoor/cookbook/version_info.py TANDOOR_VERSION = "$TANDOOR_VERSION" TANDOOR_REF = "bare-metal" VERSION_INFO = [] EOF - cd /opt/tandoor - $STD /opt/tandoor/.venv/bin/python manage.py migrate - $STD /opt/tandoor/.venv/bin/python manage.py collectstatic --no-input - msg_ok "Updated $APP to ${RELEASE}" + cd /opt/tandoor + $STD /opt/tandoor/.venv/bin/python manage.py migrate + $STD /opt/tandoor/.venv/bin/python manage.py collectstatic --no-input + msg_ok "Updated $APP to ${RELEASE}" - msg_info "Starting $APP" - systemctl start tandoor - systemctl reload nginx - msg_ok "Started $APP" + msg_info "Starting $APP" + systemctl start tandoor + systemctl reload nginx + msg_ok "Started $APP" - msg_info "Cleaning Up" - rm -rf "$BACKUP_FILE" - msg_ok "Cleanup Completed" - msg_ok "Update Successful" - else - msg_ok "No update required. ${APP} is already at v${RELEASE}" - fi - exit + msg_info "Cleaning Up" + rm -rf "$BACKUP_FILE" + msg_ok "Cleanup Completed" + msg_ok "Update Successful" + else + msg_ok "No update required. ${APP} is already at v${RELEASE}" + fi + exit } start diff --git a/ct/ubuntu.sh b/ct/ubuntu.sh index 8cdf458c3..f208d7d09 100644 --- a/ct/ubuntu.sh +++ b/ct/ubuntu.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) # source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/github.func) # Copyright (c) 2021-2025 tteck # Author: tteck (tteckster) @@ -22,18 +22,18 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /var ]]; then - msg_error "No ${APP} Installation Found!" + header_info + check_container_storage + check_container_resources + if [[ ! -d /var ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating ${APP} LXC" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Updated ${APP} LXC" exit - fi - msg_info "Updating ${APP} LXC" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "Updated ${APP} LXC" - exit } start diff --git a/ct/viseron.sh b/ct/viseron.sh index 32cb237e7..206272f34 100644 --- a/ct/viseron.sh +++ b/ct/viseron.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) APP="Viseron" var_tags="${var_tags:-nvr}" diff --git a/ct/wallabag.sh b/ct/wallabag.sh index aded3f3ea..9325189a7 100644 --- a/ct/wallabag.sh +++ b/ct/wallabag.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -25,36 +25,36 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/wallabag ]]; then - msg_error "No ${APP} Installation Found!" + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/wallabag ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + RELEASE=$(curl -fsSL https://api.github.com/repos/wallabag/wallabag/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') + if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then + msg_info "Stopping $APP" + + msg_ok "Stopped $APP" + + msg_info "Updating $APP to v${RELEASE}" + + msg_ok "Updated $APP to v${RELEASE}" + + msg_info "Starting $APP" + + msg_ok "Started $APP" + + msg_info "Cleaning Up" + rm -rf /opt/v${RELEASE}.zip + rm -rf /opt/paperless-ai_bak + msg_ok "Cleanup Completed" + msg_ok "Update Successful" + else + msg_ok "No update required. ${APP} is already at v${RELEASE}" + fi exit - fi - RELEASE=$(curl -s https://api.github.com/repos/wallabag/wallabag/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') - if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then - msg_info "Stopping $APP" - - msg_ok "Stopped $APP" - - msg_info "Updating $APP to v${RELEASE}" - - msg_ok "Updated $APP to v${RELEASE}" - - msg_info "Starting $APP" - - msg_ok "Started $APP" - - msg_info "Cleaning Up" - rm -rf /opt/v${RELEASE}.zip - rm -rf /opt/paperless-ai_bak - msg_ok "Cleanup Completed" - msg_ok "Update Successful" - else - msg_ok "No update required. ${APP} is already at v${RELEASE}" - fi - exit } start build_container diff --git a/install/hortusfox-install.sh b/install/hortusfox-install.sh index ede8a7204..8e2843a23 100644 --- a/install/hortusfox-install.sh +++ b/install/hortusfox-install.sh @@ -36,7 +36,7 @@ $STD mariadb -u root -e "GRANT ALL PRIVILEGES ON $DB_NAME.* TO '$DB_USER'@'local } >>~/hortusfox.creds msg_ok "Set up database" -fetch_and_deploy_gh_release "hortusfox-web" "danielbrendel/hortusfox-web" +fetch_and_deploy_gh_release "hortusfox" "danielbrendel/hortusfox-web" msg_info "Configuring .env" cp /opt/hortusfox-web/.env.example /opt/hortusfox-web/.env From 81c9cfc2537f282a26360e8edb1c94a8a69a7027 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 15:58:07 +0200 Subject: [PATCH 0364/1733] Update hortusfox.sh --- ct/hortusfox.sh | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/ct/hortusfox.sh b/ct/hortusfox.sh index 0936a87cc..c86c75e2c 100644 --- a/ct/hortusfox.sh +++ b/ct/hortusfox.sh @@ -6,7 +6,7 @@ source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxV # Source: https://github.com/danielbrendel/hortusfox-web APP="HortusFox" -var_tags="${var_tags:-network}" +var_tags="${var_tags:-plants}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-15}" @@ -27,25 +27,33 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - RELEASE=$(curl -fsSL https://api.github.com/repos/danielbrendel/hortusfox-web/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then + RELEASE=$(curl -fsSL https://api.github.com/repos/danielbrendel/hortusfox-web/releases/latest | jq -r .tag_name | sed 's/^v//') + if [[ ! -f ~/.hortusfox ]] || [[ "${RELEASE}" != "$(cat ~/.hortusfox)" ]]; then msg_info "Stopping Service" systemctl stop apache2 msg_ok "Stopped Service" - msg_info "Updating ${APP} to v${RELEASE}" + msg_info "Backing up current HortusFox installation" cd /opt mv /opt/hortusfox/ /opt/hortusfox-backup + msg_ok "Backed up current HortusFox installation" - echo "${RELEASE}" >/opt/${APP}_version.txt - msg_ok "Updated $APP to v${RELEASE}" + fetch_and_deploy_gh_release "hortusfox" "danielbrendel/hortusfox-web" + + msg_info "Updating HortusFox" + cd /opt/hortusfox + mv /opt/hortusfox-backup/.env /opt/hortusfox/.env + $STD composer install --no-dev --optimize-autoloader + php asatru migrate --no-interaction + php asatru plants:attributes + php asatru calendar:classes + msg_ok "Updated HortusFox" msg_info "Starting Service" - systemctl start service + systemctl start apache2 msg_ok "Started Service" msg_info "Cleaning up" - rm -r "/opt/${RELEASE}.zip" rm -r /opt/hortusfox-backup msg_ok "Cleaned" msg_ok "Updated Successfully" From a33ed0edb6267abac758ec205fadccd91f71a804 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 16:00:37 +0200 Subject: [PATCH 0365/1733] Create hortusfox.json --- frontend/public/json/hortusfox.json | 39 +++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 frontend/public/json/hortusfox.json diff --git a/frontend/public/json/hortusfox.json b/frontend/public/json/hortusfox.json new file mode 100644 index 000000000..7a154dbcb --- /dev/null +++ b/frontend/public/json/hortusfox.json @@ -0,0 +1,39 @@ +{ + "name": "HortusFox", + "slug": "hortusfox", + "categories": [ + 21, + 26 + ], + "type": "ct", + "updateable": true, + "privileged": false, + "date_created": "2025-08-04", + "config_path": "/opt/hortusfox/.env", + "interface_port": 80, + "documentation": "https://github.com/danielbrendel/hortusfox-web", + "website": "https://www.hortusfox.com/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/hortusfox.webp", + "description": "HortusFox is a collaborative plant management system for plant enthusiasts. Manage, document and track your entire plant collection – self-hosted and privacy-friendly.", + "install_methods": [ + { + "type": "default", + "script": "ct/hortusfox.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 6, + "os": "Debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": "admin@example.com", + "password": null + }, + "notes": [ + "Initial password is generated and saved in ~/hortusfox.creds after install.", + "After installation, access the web UI on http:/// and log in as admin@example.com with the generated password." + ] +} From 6a7b47acc476ba63f992ba3593461fe3bb980bda Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 16:01:43 +0200 Subject: [PATCH 0366/1733] path correction --- install/hortusfox-install.sh | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/install/hortusfox-install.sh b/install/hortusfox-install.sh index 8e2843a23..57312198e 100644 --- a/install/hortusfox-install.sh +++ b/install/hortusfox-install.sh @@ -39,17 +39,17 @@ msg_ok "Set up database" fetch_and_deploy_gh_release "hortusfox" "danielbrendel/hortusfox-web" msg_info "Configuring .env" -cp /opt/hortusfox-web/.env.example /opt/hortusfox-web/.env -sed -i "s|^DB_HOST=.*|DB_HOST=localhost|" /opt/hortusfox-web/.env -sed -i "s|^DB_USER=.*|DB_USER=$DB_USER|" /opt/hortusfox-web/.env -sed -i "s|^DB_PASSWORD=.*|DB_PASSWORD=$DB_PASS|" /opt/hortusfox-web/.env -sed -i "s|^DB_DATABASE=.*|DB_DATABASE=$DB_NAME|" /opt/hortusfox-web/.env -sed -i "s|^DB_ENABLE=.*|DB_ENABLE=true|" /opt/hortusfox-web/.env -sed -i "s|^APP_TIMEZONE=.*|APP_TIMEZONE=Europe/Berlin|" /opt/hortusfox-web/.env +cp /opt/hortusfox/.env.example /opt/hortusfox/.env +sed -i "s|^DB_HOST=.*|DB_HOST=localhost|" /opt/hortusfox/.env +sed -i "s|^DB_USER=.*|DB_USER=$DB_USER|" /opt/hortusfox/.env +sed -i "s|^DB_PASSWORD=.*|DB_PASSWORD=$DB_PASS|" /opt/hortusfox/.env +sed -i "s|^DB_DATABASE=.*|DB_DATABASE=$DB_NAME|" /opt/hortusfox/.env +sed -i "s|^DB_ENABLE=.*|DB_ENABLE=true|" /opt/hortusfox/.env +sed -i "s|^APP_TIMEZONE=.*|APP_TIMEZONE=Europe/Berlin|" /opt/hortusfox/.env msg_ok ".env configured" msg_info "Installing Composer dependencies" -cd /opt/hortusfox-web +cd /opt/hortusfox $STD composer install --no-dev --optimize-autoloader msg_ok "Composer dependencies installed" @@ -78,8 +78,8 @@ msg_info "Configuring Apache vHost" cat </etc/apache2/sites-available/hortusfox.conf ServerAdmin webmaster@localhost - DocumentRoot /opt/hortusfox-web/public - + DocumentRoot /opt/hortusfox/public + Options Indexes FollowSymLinks AllowOverride All Require all granted From 8dbb30ba87c2701764c6e800a8d763ae46d2da38 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Mon, 4 Aug 2025 10:11:45 -0400 Subject: [PATCH 0367/1733] Palmr: increase CPU resources --- ct/palmr.sh | 2 +- install/palmr-install.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/palmr.sh b/ct/palmr.sh index 3c026ab45..276ea22c6 100644 --- a/ct/palmr.sh +++ b/ct/palmr.sh @@ -7,7 +7,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV APP="Palmr" var_tags="${var_tags:-files}" -var_cpu="${var_cpu:-2}" +var_cpu="${var_cpu:-4}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-6}" var_os="${var_os:-debian}" diff --git a/install/palmr-install.sh b/install/palmr-install.sh index 3a12f0584..9d1d87c0e 100644 --- a/install/palmr-install.sh +++ b/install/palmr-install.sh @@ -37,7 +37,7 @@ sed -e 's/_ENCRYPTION=true/_ENCRYPTION=false/' \ $STD pnpm install $STD pnpm dlx prisma generate $STD pnpm dlx prisma migrate deploy -$STD pnpm dlx prisma db push # This was missing from the docs +$STD pnpm dlx prisma db push $STD pnpm db:seed $STD pnpm build msg_ok "Configured palmr backend" From c427419bf9502020386ebb36a1b18c8366997f48 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Mon, 4 Aug 2025 10:12:24 -0400 Subject: [PATCH 0368/1733] Palmr: formatting --- ct/palmr.sh | 90 ++++++++++++++++++++++++++--------------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/ct/palmr.sh b/ct/palmr.sh index 276ea22c6..5872f971b 100644 --- a/ct/palmr.sh +++ b/ct/palmr.sh @@ -20,52 +20,52 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/palmr_data ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - RELEASE=$(curl -fsSL https://api.github.com/repos/kyantech/palmr/releases/latest | yq '.tag_name' | sed 's/^v//') - if [[ "${RELEASE}" != "$(cat ~/.palmr 2>/dev/null)" ]] || [[ ! -f ~/.palmr ]]; then - msg_info "Stopping Services" - systemctl stop palmr-frontend palmr-backend - msg_ok "Stopped Services" - - msg_info "Updating ${APP}" - cp /opt/palmr/apps/server/.env /opt/palmr.env - rm -rf /opt/palmr - fetch_and_deploy_gh_release "Palmr" "kyantech/Palmr" "tarball" "latest" "/opt/palmr" - PNPM="$(jq -r '.packageManager' /opt/palmr/package.json)" - NODE_VERSION="20" NODE_MODULE="$PNPM" setup_nodejs - cd /opt/palmr/apps/server - PALMR_DIR="/opt/palmr_data" - # export PALMR_DB="${PALMR_DIR}/palmr.db" - $STD pnpm install - mv /opt/palmr.env ./.env - $STD pnpm dlx prisma generate - $STD pnpm dlx prisma migrate deploy - $STD pnpm build - - cd /opt/palmr/apps/web - export NODE_ENV=production - export NEXT_TELEMETRY_DISABLED=1 - mv ./.env.example ./.env - $STD pnpm install - $STD pnpm build - msg_ok "Updated $APP" - - msg_info "Starting Services" - systemctl start palmr-backend palmr-frontend - msg_ok "Started Services" - - msg_ok "Updated Successfully" - else - msg_ok "Already up to date" - fi + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/palmr_data ]]; then + msg_error "No ${APP} Installation Found!" exit + fi + + RELEASE=$(curl -fsSL https://api.github.com/repos/kyantech/palmr/releases/latest | yq '.tag_name' | sed 's/^v//') + if [[ "${RELEASE}" != "$(cat ~/.palmr 2>/dev/null)" ]] || [[ ! -f ~/.palmr ]]; then + msg_info "Stopping Services" + systemctl stop palmr-frontend palmr-backend + msg_ok "Stopped Services" + + msg_info "Updating ${APP}" + cp /opt/palmr/apps/server/.env /opt/palmr.env + rm -rf /opt/palmr + fetch_and_deploy_gh_release "Palmr" "kyantech/Palmr" "tarball" "latest" "/opt/palmr" + PNPM="$(jq -r '.packageManager' /opt/palmr/package.json)" + NODE_VERSION="20" NODE_MODULE="$PNPM" setup_nodejs + cd /opt/palmr/apps/server + PALMR_DIR="/opt/palmr_data" + # export PALMR_DB="${PALMR_DIR}/palmr.db" + $STD pnpm install + mv /opt/palmr.env ./.env + $STD pnpm dlx prisma generate + $STD pnpm dlx prisma migrate deploy + $STD pnpm build + + cd /opt/palmr/apps/web + export NODE_ENV=production + export NEXT_TELEMETRY_DISABLED=1 + mv ./.env.example ./.env + $STD pnpm install + $STD pnpm build + msg_ok "Updated $APP" + + msg_info "Starting Services" + systemctl start palmr-backend palmr-frontend + msg_ok "Started Services" + + msg_ok "Updated Successfully" + else + msg_ok "Already up to date" + fi + exit } start From 43b8b40c9090ed75efd20afe47224276d5008092 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 16:12:35 +0200 Subject: [PATCH 0369/1733] Update hortusfox.json --- frontend/public/json/hortusfox.json | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/frontend/public/json/hortusfox.json b/frontend/public/json/hortusfox.json index 7a154dbcb..f878e5f45 100644 --- a/frontend/public/json/hortusfox.json +++ b/frontend/public/json/hortusfox.json @@ -33,7 +33,13 @@ "password": null }, "notes": [ - "Initial password is generated and saved in ~/hortusfox.creds after install.", - "After installation, access the web UI on http:/// and log in as admin@example.com with the generated password." + { + "text": "Initial password is generated and saved in ~/hortusfox.creds after install.", + "type": "info" + }, + { + "text": "After installation, access the web UI on http:/// and log in as admin@example.com with the generated password.", + "type": "info" + } ] } From 5711b260ccc8f22ddc3a571e3cc59b830d23fd19 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 17:38:04 +0200 Subject: [PATCH 0370/1733] test --- misc/create_lxc.sh | 3 +- vm/debian-13-vm.sh | 576 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 578 insertions(+), 1 deletion(-) create mode 100644 vm/debian-13-vm.sh diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index 03de6aaa3..fd8334f4b 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -6,7 +6,8 @@ # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # This sets verbose mode if the global variable is set to "yes" -# if [ "$VERBOSE" == "yes" ]; then set -x; fi + +if [ "$VERBOSE" == "yes" ]; then set -x; fi if command -v curl >/dev/null 2>&1; then source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) diff --git a/vm/debian-13-vm.sh b/vm/debian-13-vm.sh new file mode 100644 index 000000000..eeb0d1068 --- /dev/null +++ b/vm/debian-13-vm.sh @@ -0,0 +1,576 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE + +source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) + +function header_info { + clear + cat <<"EOF" + ____ __ _ ________ + / __ \___ / /_ (_)___ _____ < /__ / + / / / / _ \/ __ \/ / __ `/ __ \ / / /_ < + / /_/ / __/ /_/ / / /_/ / / / / / /___/ / +/_____/\___/_.___/_/\__,_/_/ /_/ /_//____/ + (Trixie) +EOF +} +header_info +echo -e "\n Loading..." +GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') +RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" +METHOD="" +NSAPP="debian13vm" +var_os="debian" +var_version="13" + +YW=$(echo "\033[33m") +BL=$(echo "\033[36m") +RD=$(echo "\033[01;31m") +BGN=$(echo "\033[4;92m") +GN=$(echo "\033[1;92m") +DGN=$(echo "\033[32m") +CL=$(echo "\033[m") + +CL=$(echo "\033[m") +BOLD=$(echo "\033[1m") +BFR="\\r\\033[K" +HOLD=" " +TAB=" " + +CM="${TAB}✔️${TAB}${CL}" +CROSS="${TAB}✖️${TAB}${CL}" +INFO="${TAB}💡${TAB}${CL}" +OS="${TAB}🖥️${TAB}${CL}" +CONTAINERTYPE="${TAB}📦${TAB}${CL}" +DISKSIZE="${TAB}💾${TAB}${CL}" +CPUCORE="${TAB}🧠${TAB}${CL}" +RAMSIZE="${TAB}🛠️${TAB}${CL}" +CONTAINERID="${TAB}🆔${TAB}${CL}" +HOSTNAME="${TAB}🏠${TAB}${CL}" +BRIDGE="${TAB}🌉${TAB}${CL}" +GATEWAY="${TAB}🌐${TAB}${CL}" +DEFAULT="${TAB}⚙️${TAB}${CL}" +MACADDRESS="${TAB}🔗${TAB}${CL}" +VLANTAG="${TAB}🏷️${TAB}${CL}" +CREATING="${TAB}🚀${TAB}${CL}" +ADVANCED="${TAB}🧩${TAB}${CL}" +CLOUD="${TAB}☁️${TAB}${CL}" + +THIN="discard=on,ssd=1," +set -e +trap 'error_handler $LINENO "$BASH_COMMAND"' ERR +trap cleanup EXIT +trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT +trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM +function error_handler() { + local exit_code="$?" + local line_number="$1" + local command="$2" + local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" + post_update_to_api "failed" "${command}" + echo -e "\n$error_message\n" + cleanup_vmid +} + +function get_valid_nextid() { + local try_id + try_id=$(pvesh get /cluster/nextid) + while true; do + if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then + try_id=$((try_id + 1)) + continue + fi + if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then + try_id=$((try_id + 1)) + continue + fi + break + done + echo "$try_id" +} + +function cleanup_vmid() { + if qm status $VMID &>/dev/null; then + qm stop $VMID &>/dev/null + qm destroy $VMID &>/dev/null + fi +} + +function cleanup() { + popd >/dev/null + post_update_to_api "done" "none" + rm -rf $TEMP_DIR +} + +TEMP_DIR=$(mktemp -d) +pushd $TEMP_DIR >/dev/null +if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Debian 13 VM" --yesno "This will create a New Debian 13 VM. Proceed?" 10 58; then + : +else + header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit +fi + +function msg_info() { + local msg="$1" + echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" +} + +function msg_ok() { + local msg="$1" + echo -e "${BFR}${CM}${GN}${msg}${CL}" +} + +function msg_error() { + local msg="$1" + echo -e "${BFR}${CROSS}${RD}${msg}${CL}" +} + +function check_root() { + if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then + clear + msg_error "Please run this script as root." + echo -e "\nExiting..." + sleep 2 + exit + fi +} + +pve_check() { + local PVE_VER + PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" + + # Check for Proxmox VE 8.x + if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then + local MINOR="${BASH_REMATCH[1]}" + if ((MINOR < 1 || MINOR > 4)); then + msg_error "This version of Proxmox VE is not supported." + echo -e "Required: Proxmox VE version 8.1 – 8.4" + exit 1 + fi + return 0 + fi + + # Check for Proxmox VE 9.x (Beta) — require confirmation + if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then + if whiptail --title "Proxmox 9.x Detected (Beta)" \ + --yesno "You are using Proxmox VE $PVE_VER, which is currently in Beta state.\n\nThis version is experimentally supported.\n\nDo you want to proceed anyway?" 12 70; then + msg_ok "Confirmed: Continuing with Proxmox VE $PVE_VER" + return 0 + else + msg_error "Aborted by user: Proxmox VE 9.x was not confirmed." + exit 1 + fi + fi + + # All other unsupported versions + msg_error "This version of Proxmox VE is not supported." + echo -e "Supported versions: Proxmox VE 8.1 – 8.4 or 9.x (Beta, with confirmation)" + exit 1 +} + +function arch_check() { + if [ "$(dpkg --print-architecture)" != "amd64" ]; then + echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" + echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" + echo -e "Exiting..." + sleep 2 + exit + fi +} + +function ssh_check() { + if command -v pveversion >/dev/null 2>&1; then + if [ -n "${SSH_CLIENT:+x}" ]; then + if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then + echo "you've been warned" + else + clear + exit + fi + fi + fi +} + +function exit-script() { + clear + echo -e "\n${CROSS}${RD}User exited script${CL}\n" + exit +} + +function default_settings() { + VMID=$(get_valid_nextid) + FORMAT=",efitype=4m" + MACHINE="" + DISK_SIZE="8G" + DISK_CACHE="" + HN="debian" + CPU_TYPE="" + CORE_COUNT="2" + RAM_SIZE="2048" + BRG="vmbr0" + MAC="$GEN_MAC" + VLAN="" + MTU="" + START_VM="yes" + CLOUD_INIT="no" + METHOD="default" + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" + echo -e "${CLOUD}${BOLD}${DGN}Configure Cloud-init: ${BGN}no${CL}" + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" + echo -e "${CREATING}${BOLD}${DGN}Creating a Debian 13 VM using the above default settings${CL}" +} + +function advanced_settings() { + METHOD="advanced" + [ -z "${VMID:-}" ] && VMID=$(get_valid_nextid) + while true; do + if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z "$VMID" ]; then + VMID=$(get_valid_nextid) + fi + if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then + echo -e "${CROSS}${RD} ID $VMID is already in use${CL}" + sleep 2 + continue + fi + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" + break + else + exit-script + fi + done + + if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \ + "i440fx" "Machine i440fx" ON \ + "q35" "Machine q35" OFF \ + 3>&1 1>&2 2>&3); then + if [ $MACH = q35 ]; then + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" + FORMAT="" + MACHINE=" -machine q35" + else + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" + FORMAT=",efitype=4m" + MACHINE="" + fi + else + exit-script + fi + + if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') + if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then + DISK_SIZE="${DISK_SIZE}G" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" + elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" + else + echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}" + exit-script + fi + else + exit-script + fi + + if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ + "0" "None (Default)" ON \ + "1" "Write Through" OFF \ + 3>&1 1>&2 2>&3); then + if [ $DISK_CACHE = "1" ]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" + DISK_CACHE="cache=writethrough," + else + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" + DISK_CACHE="" + fi + else + exit-script + fi + + if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 debian --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $VM_NAME ]; then + HN="debian" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" + else + HN=$(echo ${VM_NAME,,} | tr -d ' ') + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" + fi + else + exit-script + fi + + if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ + "0" "KVM64 (Default)" ON \ + "1" "Host" OFF \ + 3>&1 1>&2 2>&3); then + if [ $CPU_TYPE1 = "1" ]; then + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" + CPU_TYPE=" -cpu host" + else + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" + CPU_TYPE="" + fi + else + exit-script + fi + + if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $CORE_COUNT ]; then + CORE_COUNT="2" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + else + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + fi + else + exit-script + fi + + if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $RAM_SIZE ]; then + RAM_SIZE="2048" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" + else + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" + fi + else + exit-script + fi + + if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $BRG ]; then + BRG="vmbr0" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + else + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + fi + else + exit-script + fi + + if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $MAC1 ]; then + MAC="$GEN_MAC" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" + else + MAC="$MAC1" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" + fi + else + exit-script + fi + + if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $VLAN1 ]; then + VLAN1="Default" + VLAN="" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" + else + VLAN=",tag=$VLAN1" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" + fi + else + exit-script + fi + + if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $MTU1 ]; then + MTU1="Default" + MTU="" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" + else + MTU=",mtu=$MTU1" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" + fi + else + exit-script + fi + + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "CLOUD-INIT" --yesno "Configure the VM with Cloud-init?" --defaultno 10 58); then + echo -e "${CLOUD}${BOLD}${DGN}Configure Cloud-init: ${BGN}yes${CL}" + CLOUD_INIT="yes" + else + echo -e "${CLOUD}${BOLD}${DGN}Configure Cloud-init: ${BGN}no${CL}" + CLOUD_INIT="no" + fi + + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" + START_VM="yes" + else + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}" + START_VM="no" + fi + + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Debian 13 VM?" --no-button Do-Over 10 58); then + echo -e "${CREATING}${BOLD}${DGN}Creating a Debian 13 VM using the above advanced settings${CL}" + else + header_info + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" + advanced_settings + fi +} + +function start_script() { + if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}" + default_settings + else + header_info + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" + advanced_settings + fi +} + +check_root +arch_check +pve_check +ssh_check +start_script + +post_to_api_vm + +msg_info "Validating Storage" +while read -r line; do + TAG=$(echo $line | awk '{print $1}') + TYPE=$(echo $line | awk '{printf "%-10s", $2}') + FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') + ITEM=" Type: $TYPE Free: $FREE " + OFFSET=2 + if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then + MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) + fi + STORAGE_MENU+=("$TAG" "$ITEM" "OFF") +done < <(pvesm status -content images | awk 'NR>1') +VALID=$(pvesm status -content images | awk 'NR>1') +if [ -z "$VALID" ]; then + msg_error "Unable to detect a valid storage location." + exit +elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then + STORAGE=${STORAGE_MENU[0]} +else + while [ -z "${STORAGE:+x}" ]; do + STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ + "Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \ + 16 $(($MSG_MAX_LENGTH + 23)) 6 \ + "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) + done +fi +msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." +msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." +msg_info "Retrieving the URL for the Debian 13 Qcow2 Disk Image" +if [ "$CLOUD_INIT" == "yes" ]; then + URL=https://cloud.debian.org/images/cloud/trixie/daily/latest/debian-13-genericcloud-amd64-daily.qcow2 +else + URL=https://cloud.debian.org/images/cloud/trixie/daily/latest/debian-13-nocloud-amd64-daily.qcow2 +fi +sleep 2 +msg_ok "${CL}${BL}${URL}${CL}" +curl -f#SL -o "$(basename "$URL")" "$URL" +echo -en "\e[1A\e[0K" +FILE=$(basename $URL) +msg_ok "Downloaded ${CL}${BL}${FILE}${CL}" + +STORAGE_TYPE=$(pvesm status -storage $STORAGE | awk 'NR>1 {print $2}') +case $STORAGE_TYPE in +nfs | dir) + DISK_EXT=".qcow2" + DISK_REF="$VMID/" + DISK_IMPORT="-format qcow2" + THIN="" + ;; +btrfs) + DISK_EXT=".raw" + DISK_REF="$VMID/" + DISK_IMPORT="-format raw" + FORMAT=",efitype=4m" + THIN="" + ;; +esac +for i in {0,1}; do + disk="DISK$i" + eval DISK${i}=vm-${VMID}-disk-${i}${DISK_EXT:-} + eval DISK${i}_REF=${STORAGE}:${DISK_REF:-}${!disk} +done + +msg_info "Creating a Debian 13 VM" +qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \ + -name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci +pvesm alloc $STORAGE $VMID $DISK0 4M 1>&/dev/null +qm importdisk $VMID ${FILE} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null +if [ "$CLOUD_INIT" == "yes" ]; then + qm set $VMID \ + -efidisk0 ${DISK0_REF}${FORMAT} \ + -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ + -scsi1 ${STORAGE}:cloudinit \ + -boot order=scsi0 \ + -serial0 socket >/dev/null +else + qm set $VMID \ + -efidisk0 ${DISK0_REF}${FORMAT} \ + -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ + -boot order=scsi0 \ + -serial0 socket >/dev/null +fi +DESCRIPTION=$( + cat < + + Logo + + +

Debian VM

+ +

+ + spend Coffee + +

+ + + + GitHub + + + + Discussions + + + + Issues + + +EOF +) +qm set "$VMID" -description "$DESCRIPTION" >/dev/null +if [ -n "$DISK_SIZE" ]; then + msg_info "Resizing disk to $DISK_SIZE GB" + qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null +else + msg_info "Using default disk size of $DEFAULT_DISK_SIZE GB" + qm resize $VMID scsi0 ${DEFAULT_DISK_SIZE} >/dev/null +fi + +msg_ok "Created a Debian 13 VM ${CL}${BL}(${HN})" +if [ "$START_VM" == "yes" ]; then + msg_info "Starting Debian 13 VM" + qm start $VMID + msg_ok "Started Debian 13 VM" +fi + +msg_ok "Completed Successfully!\n" +echo "More Info at https://github.com/community-scripts/ProxmoxVE/discussions/836" From 22fcede55a12b287ac957ce5108bea770ca7ed8d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 4 Aug 2025 17:43:44 +0200 Subject: [PATCH 0371/1733] Update create_lxc.sh --- misc/create_lxc.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index fd8334f4b..03de6aaa3 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -6,8 +6,7 @@ # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # This sets verbose mode if the global variable is set to "yes" - -if [ "$VERBOSE" == "yes" ]; then set -x; fi +# if [ "$VERBOSE" == "yes" ]; then set -x; fi if command -v curl >/dev/null 2>&1; then source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) From 1f9db7d927c4590fae2983341fcc0bba8eacaed2 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Mon, 4 Aug 2025 12:55:46 -0400 Subject: [PATCH 0372/1733] Palmr: run services under low-priv user - installing older version to test DB operations during upgrade --- ct/palmr.sh | 3 ++- install/palmr-install.sh | 18 ++++++++++-------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/ct/palmr.sh b/ct/palmr.sh index 5872f971b..ba94de6f1 100644 --- a/ct/palmr.sh +++ b/ct/palmr.sh @@ -34,10 +34,10 @@ function update_script() { systemctl stop palmr-frontend palmr-backend msg_ok "Stopped Services" - msg_info "Updating ${APP}" cp /opt/palmr/apps/server/.env /opt/palmr.env rm -rf /opt/palmr fetch_and_deploy_gh_release "Palmr" "kyantech/Palmr" "tarball" "latest" "/opt/palmr" + msg_info "Updating ${APP}" PNPM="$(jq -r '.packageManager' /opt/palmr/package.json)" NODE_VERSION="20" NODE_MODULE="$PNPM" setup_nodejs cd /opt/palmr/apps/server @@ -55,6 +55,7 @@ function update_script() { mv ./.env.example ./.env $STD pnpm install $STD pnpm build + chown -R palmr:palmr "$PALMR_DIR" /opt/palmr msg_ok "Updated $APP" msg_info "Starting Services" diff --git a/install/palmr-install.sh b/install/palmr-install.sh index 9d1d87c0e..73c1ea568 100644 --- a/install/palmr-install.sh +++ b/install/palmr-install.sh @@ -13,11 +13,7 @@ setting_up_container network_check update_os -msg_info "Installing dependencies" -$STD apt-get install -y yq -msg_ok "Installed dependencies" - -fetch_and_deploy_gh_release "Palmr" "kyantech/Palmr" "tarball" "latest" "/opt/palmr" +fetch_and_deploy_gh_release "Palmr" "kyantech/Palmr" "tarball" "v3.14-beta" "/opt/palmr" PNPM="$(jq -r '.packageManager' /opt/palmr/package.json)" NODE_VERSION="20" NODE_MODULE="$PNPM" setup_nodejs @@ -32,7 +28,7 @@ sed -e 's/_ENCRYPTION=true/_ENCRYPTION=false/' \ -e "s/ENCRYPTION_KEY=.*$/ENCRYPTION_KEY=$PALMR_KEY/" \ -e "s|file:.*$|file:$PALMR_DB\"|" \ -e '/db"$/a\# Uncomment below when using reverse proxy\ - # SECURE_SITE=true' \ +# SECURE_SITE=true' \ .env.example >./.env $STD pnpm install $STD pnpm dlx prisma generate @@ -51,7 +47,9 @@ $STD pnpm install $STD pnpm build msg_ok "Configured palmr frontend" -msg_info "Creating service files" +msg_info "Creating user & services" +useradd -d "$PALMR_DIR" -M -s /usr/sbin/nologin -U palmr +chown -R palmr:palmr "$PALMR_DIR" /opt/palmr cat </etc/systemd/system/palmr-backend.service [Unit] Description=palmr Backend Service @@ -59,6 +57,8 @@ After=network.target [Service] Type=simple +User=palmr +Group=palmr WorkingDirectory=/opt/palmr_data ExecStart=/usr/bin/node /opt/palmr/apps/server/dist/server.js @@ -73,6 +73,8 @@ After=network.target palmr-backend.service [Service] Type=simple +User=palmr +Group=palmr WorkingDirectory=/opt/palmr/apps/web ExecStart=/usr/bin/pnpm start @@ -80,7 +82,7 @@ ExecStart=/usr/bin/pnpm start WantedBy=multi-user.target EOF systemctl enable -q --now palmr-backend palmr-frontend -msg_ok "Created services" +msg_ok "Created user & services" motd_ssh customize From 970596be400bb81728a2a429dd52e6c097401895 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Mon, 4 Aug 2025 12:59:51 -0400 Subject: [PATCH 0373/1733] fix repo tag --- install/palmr-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/palmr-install.sh b/install/palmr-install.sh index 73c1ea568..37ab3e3d7 100644 --- a/install/palmr-install.sh +++ b/install/palmr-install.sh @@ -13,7 +13,7 @@ setting_up_container network_check update_os -fetch_and_deploy_gh_release "Palmr" "kyantech/Palmr" "tarball" "v3.14-beta" "/opt/palmr" +fetch_and_deploy_gh_release "Palmr" "kyantech/Palmr" "tarball" "v3.1.4-beta" "/opt/palmr" PNPM="$(jq -r '.packageManager' /opt/palmr/package.json)" NODE_VERSION="20" NODE_MODULE="$PNPM" setup_nodejs From 64b5e2f2b79b90289b2f152877a70fafa4b33b45 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Mon, 4 Aug 2025 18:11:30 -0400 Subject: [PATCH 0374/1733] Update palmr --- ct/palmr.sh | 2 +- frontend/public/json/palmr.json | 10 +++++++--- install/palmr-install.sh | 6 +++++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/ct/palmr.sh b/ct/palmr.sh index ba94de6f1..06014b5bb 100644 --- a/ct/palmr.sh +++ b/ct/palmr.sh @@ -42,11 +42,11 @@ function update_script() { NODE_VERSION="20" NODE_MODULE="$PNPM" setup_nodejs cd /opt/palmr/apps/server PALMR_DIR="/opt/palmr_data" - # export PALMR_DB="${PALMR_DIR}/palmr.db" $STD pnpm install mv /opt/palmr.env ./.env $STD pnpm dlx prisma generate $STD pnpm dlx prisma migrate deploy + $STD pnpm dlx prisma db push $STD pnpm build cd /opt/palmr/apps/web diff --git a/frontend/public/json/palmr.json b/frontend/public/json/palmr.json index 2f5771180..0f5fffd2c 100644 --- a/frontend/public/json/palmr.json +++ b/frontend/public/json/palmr.json @@ -10,7 +10,7 @@ "privileged": false, "interface_port": 3000, "documentation": "https://palmr.kyantech.com.br/docs/3.1-beta", - "config_path": "/opt/palmr/backend.env, /opt/palmr/frontend.env", + "config_path": "/opt/palmr/apps/server/.env, /opt/palmr/apps/web/.env", "website": "https://palmr.kyantech.com.br/", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/palmr.webp", "description": "Palmr is a fast and secure platform for sharing files, built with performance and privacy in mind.", @@ -19,7 +19,7 @@ "type": "default", "script": "ct/palmr.sh", "resources": { - "cpu": 2, + "cpu": 4, "ram": 4096, "hdd": 6, "os": "Debian", @@ -33,7 +33,11 @@ }, "notes": [ { - "text": "Info here", + "text": "To use a bind mount for storage, create symlinks to your mount for both `uploads` and `temp-uploads` in `/opt/palmr_data`", + "type": "info" + }, + { + "text": "To use Palmr with a reverse proxy, uncomment `SECURE_SITE` in `/opt/palmr/apps/server/.env`", "type": "info" } ] diff --git a/install/palmr-install.sh b/install/palmr-install.sh index 37ab3e3d7..30759aa23 100644 --- a/install/palmr-install.sh +++ b/install/palmr-install.sh @@ -13,7 +13,11 @@ setting_up_container network_check update_os -fetch_and_deploy_gh_release "Palmr" "kyantech/Palmr" "tarball" "v3.1.4-beta" "/opt/palmr" +msg_info "Installing dependencies" +$STD apt-get install yq -y +msg_ok "Installed dependencies" + +fetch_and_deploy_gh_release "Palmr" "kyantech/Palmr" "tarball" "latest" "/opt/palmr" PNPM="$(jq -r '.packageManager' /opt/palmr/package.json)" NODE_VERSION="20" NODE_MODULE="$PNPM" setup_nodejs From 4122d61e014a0773e979b295b906be35c396ec90 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 5 Aug 2025 07:24:16 +0200 Subject: [PATCH 0375/1733] Update build.func --- misc/build.func | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/misc/build.func b/misc/build.func index a0d7d8310..9cb90894d 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1438,19 +1438,7 @@ EOF' fi msg_ok "Customized LXC Container" - pct push "$CTID" <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/$var_install.sh) /tmp/app-install.sh - pct exec "$CTID" -- bash -c 'chmod +x /tmp/app-install.sh' - lxc-attach -n "$CTID" -- bash -c ' - /tmp/app-install.sh - echo __EXITCODE__$? -' | tee /tmp/app_install.log - - EXITCODE=$(awk -F__EXITCODE__ '/__EXITCODE__/{print $2}' /tmp/app_install.log | tail -n1) - echo "DEBUG: Parsed container exit code: $EXITCODE" - if [[ "$EXITCODE" == "10" ]]; then - destroy_lxc - fi - exit "$EXITCODE" + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/${var_install}.sh)" } destroy_lxc() { From 92e39b50f809d4e40df6b9a1deafd85e0807b29b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 5 Aug 2025 07:29:08 +0200 Subject: [PATCH 0376/1733] Update build.func --- misc/build.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index 9cb90894d..a5b2e0f31 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1438,7 +1438,7 @@ EOF' fi msg_ok "Customized LXC Container" - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/install/${var_install}.sh)" + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)" } destroy_lxc() { From 3c694aad16261e371f4b17ca729c1a3c1922d34e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 5 Aug 2025 07:32:06 +0200 Subject: [PATCH 0377/1733] Update build.func --- misc/build.func | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/misc/build.func b/misc/build.func index a5b2e0f31..53f45d5b8 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1411,7 +1411,7 @@ EOF http://dl-cdn.alpinelinux.org/alpine/latest-stable/main http://dl-cdn.alpinelinux.org/alpine/latest-stable/community EOF' - pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses >/dev/null" + pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses jq >/dev/null" else sleep 3 @@ -1431,7 +1431,7 @@ EOF' msg_warn "Skipping timezone setup – zone '$tz' not found in container" fi - pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y sudo curl mc gnupg2 >/dev/null" || { + pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y sudo curl mc gnupg2 jq >/dev/null" || { msg_error "apt-get base packages installation failed" exit 1 } From ceb414d967c15d76da90e97e0cbb052d8bcb2f1e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 5 Aug 2025 07:56:47 +0200 Subject: [PATCH 0378/1733] Update viseron-install.sh --- install/viseron-install.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/install/viseron-install.sh b/install/viseron-install.sh index 13aea1b3c..de2a51765 100644 --- a/install/viseron-install.sh +++ b/install/viseron-install.sh @@ -24,6 +24,14 @@ $STD apt-get install -y \ gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-libav msg_ok "Installed Dependencies" +msg_info "Setting up Hardware Acceleration" +if [[ "$CTTYPE" == "0" ]]; then + chgrp video /dev/dri + chmod 755 /dev/dri + chmod 660 /dev/dri/* +fi +msg_ok "Hardware Acceleration Configured" + msg_info "Setting up Python Environment with uv" cd /opt uv venv viseron @@ -119,14 +127,6 @@ EOF systemctl enable -q --now viseron msg_ok "Created Systemd Service" -msg_info "Setting up Hardware Acceleration" -if [[ "$CTTYPE" == "0" ]]; then - chgrp video /dev/dri - chmod 755 /dev/dri - chmod 660 /dev/dri/* -fi -msg_ok "Hardware Acceleration Configured" - motd_ssh customize From cab4d85468fcd9c25dc025bc4fe886cdef69a9f9 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 5 Aug 2025 08:19:20 +0200 Subject: [PATCH 0379/1733] test --- ct/viseron.sh | 18 +++++++++--------- install/viseron-install.sh | 26 +++++++++++++------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/ct/viseron.sh b/ct/viseron.sh index 206272f34..09134918a 100644 --- a/ct/viseron.sh +++ b/ct/viseron.sh @@ -8,7 +8,7 @@ var_ram="${var_ram:-2048}" var_disk="${var_disk:-10}" var_os="${var_os:-debian}" var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" +var_unprivileged="${var_unprivileged:-0}" header_info "$APP" @@ -17,15 +17,15 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -f /etc/systemd/system/viseron.service ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - msg_error "To update Viseron, create a new container and transfer your configuration." + header_info + check_container_storage + check_container_resources + if [[ ! -f /etc/systemd/system/viseron.service ]]; then + msg_error "No ${APP} Installation Found!" exit + fi + msg_error "To update Viseron, create a new container and transfer your configuration." + exit } start diff --git a/install/viseron-install.sh b/install/viseron-install.sh index de2a51765..56abb8c22 100644 --- a/install/viseron-install.sh +++ b/install/viseron-install.sh @@ -21,7 +21,10 @@ $STD apt-get install -y \ python3-opencv jq \ libgl1-mesa-glx libglib2.0-0 \ libgstreamer1.0-0 libgstreamer-plugins-base1.0-0 \ - gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-libav + gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-libav \ + build-essential python3-dev python3-gi pkg-config libcairo2-dev gir1.2-glib-2.0 \ + cmake gfortran libopenblas-dev liblapack-dev libgirepository1.0-dev + msg_ok "Installed Dependencies" msg_info "Setting up Hardware Acceleration" @@ -32,18 +35,15 @@ if [[ "$CTTYPE" == "0" ]]; then fi msg_ok "Hardware Acceleration Configured" -msg_info "Setting up Python Environment with uv" -cd /opt -uv venv viseron -source viseron/bin/activate -uv pip install --upgrade pip setuptools wheel -msg_ok "Python Environment Setup (uv)" +fetch_and_deploy_gh_release "viseron" "roflcoopter/viseron" "tarball" "latest" "/opt/viseron" -msg_info "Installing Viseron" -RELEASE=$(curl -s https://api.github.com/repos/roflcoopter/viseron/releases/latest | jq -r '.tag_name') -uv pip install https://github.com/roflcoopter/viseron/archive/refs/tags/${RELEASE}.tar.gz -ln -s /opt/viseron/bin/viseron /usr/local/bin/viseron -msg_ok "Installed Viseron $RELEASE" +msg_info "Setting up Viseron (Patience)" +cd /opt/viseron +uv venv .venv +$STD uv pip install --upgrade pip setuptools wheel +$STD uv pip install -r requirements.txt --python /opt/viseron/.venv/bin/python +ln -s /opt/viseron/.venv/bin/viseron /usr/local/bin/viseron +msg_ok "Setup Viseron" msg_info "Creating Configuration Directory" mkdir -p /config @@ -117,7 +117,7 @@ Type=simple User=root WorkingDirectory=/opt/viseron Environment=PATH=/opt/viseron/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin -ExecStart=/opt/viseron/bin/viseron --config /config/viseron.yaml +ExecStart=/opt/viseron/.venv/bin/viseron --config /config/viseron.yaml Restart=always RestartSec=10 From 7842c4d0e94e7f957b0f303d24b0b92b243a2e25 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 5 Aug 2025 08:33:15 +0200 Subject: [PATCH 0380/1733] remove --- install/viseron-install.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/install/viseron-install.sh b/install/viseron-install.sh index 56abb8c22..7253f90cf 100644 --- a/install/viseron-install.sh +++ b/install/viseron-install.sh @@ -27,13 +27,13 @@ $STD apt-get install -y \ msg_ok "Installed Dependencies" -msg_info "Setting up Hardware Acceleration" -if [[ "$CTTYPE" == "0" ]]; then - chgrp video /dev/dri - chmod 755 /dev/dri - chmod 660 /dev/dri/* -fi -msg_ok "Hardware Acceleration Configured" +# msg_info "Setting up Hardware Acceleration" +# if [[ "$CTTYPE" == "0" ]]; then +# chgrp video /dev/dri +# chmod 755 /dev/dri +# chmod 660 /dev/dri/* +# fi +# msg_ok "Hardware Acceleration Configured" fetch_and_deploy_gh_release "viseron" "roflcoopter/viseron" "tarball" "latest" "/opt/viseron" From 67a83de93728b25904aa0dbb39f81968f7f5cb16 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 5 Aug 2025 09:03:10 +0200 Subject: [PATCH 0381/1733] increase disk --- ct/viseron.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/viseron.sh b/ct/viseron.sh index 09134918a..ef0f4a98b 100644 --- a/ct/viseron.sh +++ b/ct/viseron.sh @@ -5,7 +5,7 @@ APP="Viseron" var_tags="${var_tags:-nvr}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" -var_disk="${var_disk:-10}" +var_disk="${var_disk:-25}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-0}" From 01a797f7fffe6953cf329eb892c00c75d7ea90bd Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 5 Aug 2025 15:17:59 +0200 Subject: [PATCH 0382/1733] copyparty --- tools/addon/copyparty.sh | 249 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 249 insertions(+) create mode 100644 tools/addon/copyparty.sh diff --git a/tools/addon/copyparty.sh b/tools/addon/copyparty.sh new file mode 100644 index 000000000..d16a230d2 --- /dev/null +++ b/tools/addon/copyparty.sh @@ -0,0 +1,249 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: MickLesk +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE + +function header_info() { + clear + cat <<"EOF" + ______ ____ __ + / ____/___ ____ __ __/ __ \____ ______/ /___ __ + / / / __ \/ __ \/ / / / /_/ / __ `/ ___/ __/ / / / +/ /___/ /_/ / /_/ / /_/ / ____/ /_/ / / / /_/ /_/ / +\____/\____/ .___/\__, /_/ \__,_/_/ \__/\__, / + /_/ /____/ /____/ +EOF +} + +YW=$(echo "\033[33m") +GN=$(echo "\033[1;92m") +RD=$(echo "\033[01;31m") +BL=$(echo "\033[36m") +CL=$(echo "\033[m") +CM="${GN}✔️${CL}" +CROSS="${RD}✖️${CL}" +INFO="${BL}ℹ️${CL}" + +APP="CopyParty" +BIN_PATH="/usr/local/bin/copyparty-sfx.py" +CONF_PATH="/etc/copyparty.conf" +LOG_PATH="/var/log/copyparty" +DATA_PATH="/var/lib/copyparty" +SERVICE_PATH_DEB="/etc/systemd/system/copyparty.service" +SERVICE_PATH_ALP="/etc/init.d/copyparty" +SVC_USER="copyparty" +SVC_GROUP="copyparty" +SRC_URL="https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py" +DEFAULT_PORT=3923 + +# OS Detection +if [[ -f "/etc/alpine-release" ]]; then + OS="Alpine" + PKG_MANAGER="apk add --no-cache" + SERVICE_PATH="$SERVICE_PATH_ALP" +elif [[ -f "/etc/debian_version" ]]; then + OS="Debian" + PKG_MANAGER="apt-get install -y" + SERVICE_PATH="$SERVICE_PATH_DEB" +else + echo -e "${CROSS} Unsupported OS detected. Exiting." + exit 1 +fi + +header_info + +function msg_info() { echo -e "${INFO} ${YW}$1...${CL}"; } +function msg_ok() { echo -e "${CM} ${GN}$1${CL}"; } +function msg_error() { echo -e "${CROSS} ${RD}$1${CL}"; } + +# User/Group/Dirs +function setup_user_and_dirs() { + msg_info "Creating $SVC_USER user and directories" + if ! id "$SVC_USER" &>/dev/null; then + if [[ "$OS" == "Debian" ]]; then + useradd -r -s /sbin/nologin -d "$DATA_PATH" "$SVC_USER" + else + adduser -D -H -h "$DATA_PATH" -s /sbin/nologin "$SVC_USER" + fi + fi + mkdir -p "$DATA_PATH" "$LOG_PATH" + chown -R "$SVC_USER:$SVC_GROUP" "$DATA_PATH" "$LOG_PATH" + chmod 755 "$DATA_PATH" "$LOG_PATH" + msg_ok "User/Group/Dirs ready" +} + +function uninstall_copyparty() { + msg_info "Uninstalling $APP" + if [[ "$OS" == "Debian" ]]; then + systemctl disable --now copyparty &>/dev/null + rm -f "$SERVICE_PATH_DEB" + else + rc-service copyparty stop &>/dev/null + rc-update del copyparty &>/dev/null + rm -f "$SERVICE_PATH_ALP" + fi + rm -f "$BIN_PATH" "$CONF_PATH" + msg_ok "$APP has been uninstalled." + exit 0 +} + +function update_copyparty() { + msg_info "Updating $APP" + curl -fsSL "$SRC_URL" -o "$BIN_PATH" + chmod +x "$BIN_PATH" + msg_ok "Updated $APP" + exit 0 +} + +# --- Existing Install/Update/Uninstall Check --- +if [[ -f "$BIN_PATH" ]]; then + echo -e "${YW}⚠️ $APP is already installed.${CL}" + echo -n "Uninstall $APP? (y/N): " + read -r uninstall_prompt + if [[ "${uninstall_prompt,,}" =~ ^(y|yes)$ ]]; then + uninstall_copyparty + fi + + echo -n "Update $APP? (y/N): " + read -r update_prompt + if [[ "${update_prompt,,}" =~ ^(y|yes)$ ]]; then + update_copyparty + else + echo -e "${YW}⚠️ Update skipped. Exiting.${CL}" + exit 0 + fi +fi + +# --- Deps --- +msg_info "Installing dependencies" +if [[ "$OS" == "Debian" ]]; then + $PKG_MANAGER python3 curl &>/dev/null +else + $PKG_MANAGER python3 curl &>/dev/null +fi +msg_ok "Dependencies installed" + +# --- User/Dirs --- +setup_user_and_dirs + +# --- Download Binary --- +msg_info "Downloading $APP" +curl -fsSL "$SRC_URL" -o "$BIN_PATH" +chmod +x "$BIN_PATH" +chown "$SVC_USER:$SVC_GROUP" "$BIN_PATH" +msg_ok "Downloaded to $BIN_PATH" + +# --- Config: Interaktiv, Auth, Rootdir, Port --- +echo -n "Enter port for $APP (default: $DEFAULT_PORT): " +read -r PORT +PORT=${PORT:-$DEFAULT_PORT} + +echo -n "Set data directory (default: $DATA_PATH): " +read -r USER_DATA_PATH +USER_DATA_PATH=${USER_DATA_PATH:-$DATA_PATH} +mkdir -p "$USER_DATA_PATH" +chown "$SVC_USER:$SVC_GROUP" "$USER_DATA_PATH" + +echo -n "Enable authentication? (Y/n): " +read -r auth_enable +if [[ "${auth_enable,,}" =~ ^(n|no)$ ]]; then + AUTH_LINE="" + msg_ok "Configured without authentication" +else + echo -n "Set admin username [default: admin]: " + read -r ADMIN_USER + ADMIN_USER=${ADMIN_USER:-admin} + echo -n "Set admin password [default: helper-scripts.com]: " + read -rs ADMIN_PASS + ADMIN_PASS=${ADMIN_PASS:-helper-scripts.com} + echo + AUTH_LINE="auth vhost=/:$ADMIN_USER:$ADMIN_PASS:admin,," + msg_ok "Configured with admin user: $ADMIN_USER" +fi + +# --- Generate /etc/copyparty.conf --- +msg_info "Writing config to $CONF_PATH" +cat <"$CONF_PATH" +# CopyParty main config +# More options: https://github.com/9001/copyparty/blob/hovudstraum/contrib/systemd/copyparty.conf +# +# storage +root = $USER_DATA_PATH + +# HTTP port +port = $PORT + +# logs +logs = $LOG_PATH + +# authentication +$AUTH_LINE + +# allow upload +ul = * + +# Example: allow delete +#dl = * + +# basic web-ui +w = * + +EOF +chmod 640 "$CONF_PATH" +chown "$SVC_USER:$SVC_GROUP" "$CONF_PATH" +msg_ok "Config written" + +# --- Systemd/OpenRC Service --- +msg_info "Creating service" +if [[ "$OS" == "Debian" ]]; then + cat <"$SERVICE_PATH_DEB" +[Unit] +Description=CopyParty file server + +[Service] +Type=simple +User=$SVC_USER +Group=$SVC_GROUP +WorkingDirectory=$USER_DATA_PATH +Environment=PYTHONUNBUFFERED=x +LogsDirectory=copyparty +ExecStart=/usr/bin/python3 $BIN_PATH -c $CONF_PATH +Restart=always + +[Install] +WantedBy=multi-user.target +EOF + systemctl daemon-reload + systemctl enable --now copyparty &>/dev/null +else + cat <"$SERVICE_PATH_ALP" +#!/sbin/openrc-run + +command="/usr/bin/python3" +command_args="$BIN_PATH -c $CONF_PATH" +command_background=true +directory="$USER_DATA_PATH" +pidfile="$USER_DATA_PATH/copyparty.pid" + +depend() { + need net +} +EOF + chmod +x "$SERVICE_PATH_ALP" + rc-update add copyparty default &>/dev/null + rc-service copyparty start &>/dev/null +fi +msg_ok "Service created and started" + +# IP detection (as root, maybe interface up/loopback fallback) +IFACE=$(ip -4 route | awk '/default/ {print $5; exit}') +IP=$(ip -4 addr show "$IFACE" | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) +[[ -z "$IP" ]] && IP=$(hostname -I | awk '{print $1}') +[[ -z "$IP" ]] && IP="127.0.0.1" + +echo -e "${CM} ${GN}$APP is running at: ${BL}http://$IP:$PORT${CL}" +echo -e "${INFO} Storage directory: ${YW}$USER_DATA_PATH${CL}" +if [[ -n "$AUTH_LINE" ]]; then + echo -e "${INFO} Login: ${GN}${ADMIN_USER}${CL} / ${GN}${ADMIN_PASS}${CL}" +fi From fdaa956eabdcd847e2c26311a8757c27cee12078 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 5 Aug 2025 15:28:37 +0200 Subject: [PATCH 0383/1733] Update copyparty.sh --- tools/addon/copyparty.sh | 39 ++++++++++++++++----------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/tools/addon/copyparty.sh b/tools/addon/copyparty.sh index d16a230d2..79bd509fe 100644 --- a/tools/addon/copyparty.sh +++ b/tools/addon/copyparty.sh @@ -164,32 +164,25 @@ fi # --- Generate /etc/copyparty.conf --- msg_info "Writing config to $CONF_PATH" -cat <"$CONF_PATH" -# CopyParty main config -# More options: https://github.com/9001/copyparty/blob/hovudstraum/contrib/systemd/copyparty.conf -# -# storage -root = $USER_DATA_PATH +cat </etc/copyparty.conf +[global] + p: $PORT + ansi + e2dsa + e2ts + theme: 2 + grid -# HTTP port -port = $PORT - -# logs -logs = $LOG_PATH - -# authentication -$AUTH_LINE - -# allow upload -ul = * - -# Example: allow delete -#dl = * - -# basic web-ui -w = * +[accounts] + $ADMIN_USER: $ADMIN_PASS +[/] + $USER_DATA_PATH + accs: + rw: * + rwmda: $ADMIN_USER EOF + chmod 640 "$CONF_PATH" chown "$SVC_USER:$SVC_GROUP" "$CONF_PATH" msg_ok "Config written" From aea44d2df59d053a936966a5a38124077562dfdd Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 6 Aug 2025 10:22:49 +0200 Subject: [PATCH 0384/1733] Update build.func --- misc/build.func | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/misc/build.func b/misc/build.func index 53f45d5b8..c40a57bd1 100644 --- a/misc/build.func +++ b/misc/build.func @@ -81,36 +81,36 @@ root_check() { } # This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported. +# Supported: Proxmox VE 8.0.x – 8.9.x and 9.0 (NOT 9.1+) pve_check() { local PVE_VER PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" - # Check for Proxmox VE 8.x + # Check for Proxmox VE 8.x: allow 8.0–8.9 if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" - if ((MINOR < 1 || MINOR > 4)); then + if ((MINOR < 0 || MINOR > 9)); then msg_error "This version of Proxmox VE is not supported." - echo -e "Required: Proxmox VE version 8.1 – 8.4" + msg_error "Supported: Proxmox VE version 8.0 – 8.9" exit 1 fi return 0 fi - # Check for Proxmox VE 9.x (Beta) — require confirmation + # Check for Proxmox VE 9.x: allow ONLY 9.0 if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then - #if whiptail --title "Proxmox 9.x Detected (Beta)" \ - #--yesno "You are using Proxmox VE $PVE_VER, which is currently in Beta state.\n\nThis version is experimentally supported.\n\nDo you want to proceed anyway?" 12 70; then - #msg_ok "Confirmed: Continuing with Proxmox VE $PVE_VER" + local MINOR="${BASH_REMATCH[1]}" + if ((MINOR != 0)); then + msg_error "This version of Proxmox VE is not yet supported." + msg_error "Supported: Proxmox VE version 9.0" + exit 1 + fi return 0 - #else - #msg_error "Aborted by user: Proxmox VE 9.x was not confirmed." - #exit 1 - #fi fi # All other unsupported versions msg_error "This version of Proxmox VE is not supported." - echo -e "Supported versions: Proxmox VE 8.1 – 8.4 or 9.x (Beta, with confirmation)" + msg_error "Supported versions: Proxmox VE 8.0 – 8.x or 9.0" exit 1 } From 0a2a94469b0c8a8497a4d4925bda0b0f1222b40d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 6 Aug 2025 10:40:50 +0200 Subject: [PATCH 0385/1733] Update post-pve-install.sh --- tools/pve/post-pve-install.sh | 75 ++++++++++------------------------- 1 file changed, 21 insertions(+), 54 deletions(-) diff --git a/tools/pve/post-pve-install.sh b/tools/pve/post-pve-install.sh index b6c704f0e..d62e22bf2 100644 --- a/tools/pve/post-pve-install.sh +++ b/tools/pve/post-pve-install.sh @@ -44,12 +44,6 @@ msg_error() { echo -e "${BFR} ${CROSS} ${RD}${msg}${CL}" } -msg_custom() { - local msg="$1" - echo -e "${BFR} ${YW}⚠ ${msg}${CL}" -} - - start_routines() { header_info @@ -139,24 +133,23 @@ EOF ;; esac - if [[ ! -f /etc/apt/apt.conf.d/no-nag-script ]]; then - CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "SUBSCRIPTION NAG" --menu "This will disable the nag message reminding you to purchase a subscription every time you log in to the web interface.\n \nDisable subscription nag?" 14 58 2 \ - "yes" " " \ - "no" " " 3>&2 2>&1 1>&3) - case $CHOICE in - yes) - whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox --title "Support Subscriptions" "Supporting the software's development team is essential. Check their official website's Support Subscriptions for pricing. Without their dedicated work, we wouldn't have this exceptional software." 10 58 - msg_info "Disabling subscription nag" - echo "DPkg::Post-Invoke { \"dpkg -V proxmox-widget-toolkit 2>/dev/null && [ -f /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js ] && echo 'Removing subscription nag from UI...' && sed -i '/data\.status/{s/\!/=/;s/active/NoMoreNagging/}' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js\"; };" >/etc/apt/apt.conf.d/no-nag-script - apt --reinstall install proxmox-widget-toolkit &>/dev/null - msg_ok "Disabled subscription nag (Delete browser cache)" - ;; - no) - whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox --title "Support Subscriptions" "Supporting the software's development team is essential. Check their official website's Support Subscriptions for pricing. Without their dedicated work, we wouldn't have this exceptional software." 10 58 - msg_error "Selected no to Disabling subscription nag" - ;; - esac - fi + CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "SUBSCRIPTION NAG" --menu "This will disable the nag message reminding you to purchase a subscription every time you log in to the web interface.\n \nDisable subscription nag?" 14 58 2 \ + "yes" " " \ + "no" " " 3>&2 2>&1 1>&3) + case $CHOICE in + yes) + whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox --title "Support Subscriptions" "Supporting the software's development team is essential. Check their official website's Support Subscriptions for pricing. Without their dedicated work, we wouldn't have this exceptional software." 10 58 + msg_info "Disabling subscription nag" + echo "DPkg::Post-Invoke { \"if [ -s /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js ] && ! grep -q -F 'NoMoreNagging' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js; then echo 'Removing subscription nag from UI...'; sed -i '/data\.status/{s/\!//;s/active/NoMoreNagging/}' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js; fi\" };" >/etc/apt/apt.conf.d/no-nag-script + msg_ok "Disabled subscription nag (Delete browser cache)" + ;; + no) + whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox --title "Support Subscriptions" "Supporting the software's development team is essential. Check their official website's Support Subscriptions for pricing. Without their dedicated work, we wouldn't have this exceptional software." 10 58 + msg_error "Selected no to Disabling subscription nag" + rm /etc/apt/apt.conf.d/no-nag-script 2>/dev/null + ;; + esac + apt --reinstall install proxmox-widget-toolkit &>/dev/null if ! systemctl is-active --quiet pve-ha-lrm; then CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "HIGH AVAILABILITY" --menu "Enable high availability?" 10 58 2 \ @@ -252,38 +245,12 @@ while true; do esac done -# This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported. -pve_check() { - local PVE_VER - PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" - - # 8 Version Check - if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then - local MINOR="${BASH_REMATCH[1]}" - if (( MINOR < 1 || MINOR > 4 )); then - msg_error "This version of Proxmox Virtual Environment is not supported" - echo -e "Requires Proxmox Virtual Environment Version 8.1 – 8.4" - echo -e "Exiting..." - sleep 2 - exit 1 - fi - return 0 - fi - - # 9 Beta Version Check - if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then - msg_custom "Detected Proxmox Virtual Environment $PVE_VER – Beta state, use with caution!" - return 0 - fi - - # All others (unsupported versions) +if ! pveversion | grep -Eq "pve-manager/8\.[0-4](\.[0-9]+)*"; then msg_error "This version of Proxmox Virtual Environment is not supported" - echo -e "Requires Proxmox Virtual Environment Version 8.1 – 8.4 or 9.x (Beta)" + echo -e "Requires Proxmox Virtual Environment Version 8.0 or later." echo -e "Exiting..." sleep 2 - exit 1 -} + exit +fi - -pve_check start_routines From 44dec209a60cc365ba217fbfec3914a9b06996ed Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 6 Aug 2025 10:53:55 +0200 Subject: [PATCH 0386/1733] Update post-pve-install.sh --- tools/pve/post-pve-install.sh | 57 ++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/tools/pve/post-pve-install.sh b/tools/pve/post-pve-install.sh index d62e22bf2..5bc632750 100644 --- a/tools/pve/post-pve-install.sh +++ b/tools/pve/post-pve-install.sh @@ -44,7 +44,62 @@ msg_error() { echo -e "${BFR} ${CROSS} ${RD}${msg}${CL}" } -start_routines() { +get_pve_version() { + local pve_ver + pve_ver="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" + # Output: 8.4.6 or 9.0.0 + echo "$pve_ver" +} + +get_pve_major_minor() { + # Outputs MAJOR and MINOR as "8 4" or "9 0" + local ver="$1" + local major minor + IFS='.' read -r major minor _ <<<"$ver" + echo "$major $minor" +} + +# --- Core Logic Trampoline --- +main() { + header_info + echo -e "\nThis script will Perform Post Install Routines.\n" + while true; do + read -p "Start the Proxmox VE Post Install Script (y/n)? " yn + case $yn in + [Yy]*) break ;; + [Nn]*) + clear + exit + ;; + *) echo "Please answer yes or no." ;; + esac + done + + local PVE_VERSION PVE_MAJOR PVE_MINOR + PVE_VERSION="$(get_pve_version)" + read -r PVE_MAJOR PVE_MINOR <<<"$(get_pve_major_minor "$PVE_VERSION")" + + # Supported: 8.0–8.9.x or 9.0 (further 9.x possibly later) + if [[ "$PVE_MAJOR" == "8" ]]; then + if ((PVE_MINOR < 0 || PVE_MINOR > 9)); then + msg_error "Unsupported Proxmox 8 version" + exit 1 + fi + start_routines_8 + elif [[ "$PVE_MAJOR" == "9" ]]; then + if ((PVE_MINOR != 0)); then + msg_error "Only Proxmox 9.0 is currently supported" + exit 1 + fi + start_routines_9 + else + msg_error "Unsupported Proxmox VE major version: $PVE_MAJOR" + echo -e "Supported: 8.0–8.9.x and 9.0" + exit 1 + fi +} + +start_routines_8() { header_info CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "SOURCES" --menu "The package manager will use the correct sources to update and install packages on your Proxmox VE server.\n \nCorrect Proxmox VE sources?" 14 58 2 \ From 8b81ea3f3184c2a4909132b6dc4cc84a3f73a5fb Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 6 Aug 2025 10:54:49 +0200 Subject: [PATCH 0387/1733] Update post-pve-install.sh --- tools/pve/post-pve-install.sh | 185 ++++++++++++++++++++++++---------- 1 file changed, 129 insertions(+), 56 deletions(-) diff --git a/tools/pve/post-pve-install.sh b/tools/pve/post-pve-install.sh index 5bc632750..89b2ab7b6 100644 --- a/tools/pve/post-pve-install.sh +++ b/tools/pve/post-pve-install.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) +# Author: tteckster # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -47,19 +47,16 @@ msg_error() { get_pve_version() { local pve_ver pve_ver="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" - # Output: 8.4.6 or 9.0.0 echo "$pve_ver" } get_pve_major_minor() { - # Outputs MAJOR and MINOR as "8 4" or "9 0" local ver="$1" local major minor IFS='.' read -r major minor _ <<<"$ver" echo "$major $minor" } -# --- Core Logic Trampoline --- main() { header_info echo -e "\nThis script will Perform Post Install Routines.\n" @@ -79,7 +76,6 @@ main() { PVE_VERSION="$(get_pve_version)" read -r PVE_MAJOR PVE_MINOR <<<"$(get_pve_major_minor "$PVE_VERSION")" - # Supported: 8.0–8.9.x or 9.0 (further 9.x possibly later) if [[ "$PVE_MAJOR" == "8" ]]; then if ((PVE_MINOR < 0 || PVE_MINOR > 9)); then msg_error "Unsupported Proxmox 8 version" @@ -102,6 +98,7 @@ main() { start_routines_8() { header_info + # === Bookworm/8.x: .list-Dateien === CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "SOURCES" --menu "The package manager will use the correct sources to update and install packages on your Proxmox VE server.\n \nCorrect Proxmox VE sources?" 14 58 2 \ "yes" " " \ "no" " " 3>&2 2>&1 1>&3) @@ -116,9 +113,7 @@ EOF echo 'APT::Get::Update::SourceListWarnings::NonFreeFirmware "false";' >/etc/apt/apt.conf.d/no-bookworm-firmware.conf msg_ok "Corrected Proxmox VE Sources" ;; - no) - msg_error "Selected no to Correcting Proxmox VE Sources" - ;; + no) msg_error "Selected no to Correcting Proxmox VE Sources" ;; esac CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PVE-ENTERPRISE" --menu "The 'pve-enterprise' repository is only available to users who have purchased a Proxmox VE subscription.\n \nDisable 'pve-enterprise' repository?" 14 58 2 \ @@ -132,9 +127,7 @@ EOF EOF msg_ok "Disabled 'pve-enterprise' repository" ;; - no) - msg_error "Selected no to Disabling 'pve-enterprise' repository" - ;; + no) msg_error "Selected no to Disabling 'pve-enterprise' repository" ;; esac CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PVE-NO-SUBSCRIPTION" --menu "The 'pve-no-subscription' repository provides access to all of the open-source components of Proxmox VE.\n \nEnable 'pve-no-subscription' repository?" 14 58 2 \ @@ -148,9 +141,7 @@ deb http://download.proxmox.com/debian/pve bookworm pve-no-subscription EOF msg_ok "Enabled 'pve-no-subscription' repository" ;; - no) - msg_error "Selected no to Enabling 'pve-no-subscription' repository" - ;; + no) msg_error "Selected no to Enabling 'pve-no-subscription' repository" ;; esac CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CEPH PACKAGE REPOSITORIES" --menu "The 'Ceph Package Repositories' provides access to both the 'no-subscription' and 'enterprise' repositories (initially disabled).\n \nCorrect 'ceph package sources?" 14 58 2 \ @@ -167,9 +158,7 @@ EOF EOF msg_ok "Corrected 'ceph package repositories'" ;; - no) - msg_error "Selected no to Correcting 'ceph package repositories'" - ;; + no) msg_error "Selected no to Correcting 'ceph package repositories'" ;; esac CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PVETEST" --menu "The 'pvetest' repository can give advanced users access to new features and updates before they are officially released.\n \nAdd (Disabled) 'pvetest' repository?" 14 58 2 \ @@ -183,11 +172,125 @@ EOF EOF msg_ok "Added 'pvetest' repository" ;; - no) - msg_error "Selected no to Adding 'pvetest' repository" - ;; + no) msg_error "Selected no to Adding 'pvetest' repository" ;; esac + post_routines_common +} + +start_routines_9() { + header_info + + # === Trixie/9.x: deb822 .sources === + CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "SOURCES" --menu "The package manager will use the correct sources to update and install packages on your Proxmox VE 9 server.\n\nMigrate to deb822 sources format?" 14 58 2 \ + "yes" " " \ + "no" " " 3>&2 2>&1 1>&3) + case $CHOICE in + yes) + msg_info "Correcting Proxmox VE Sources (deb822)" + rm -f /etc/apt/sources.list.d/*.list + sed -i '/proxmox/d;/bookworm/d' /etc/apt/sources.list || true + # Modern Debian base sources (deb822) + cat >/etc/apt/sources.list.d/debian.sources <&2 2>&1 1>&3) + case $CHOICE in + yes) + msg_info "Adding 'pve-enterprise' repository (deb822)" + cat >/etc/apt/sources.list.d/pve-enterprise.sources <&2 2>&1 1>&3) + case $CHOICE in + yes) + msg_info "Adding 'pve-no-subscription' repository (deb822)" + cat >/etc/apt/sources.list.d/proxmox.sources <&2 2>&1 1>&3) + case $CHOICE in + yes) + msg_info "Adding 'ceph package repositories' (deb822)" + cat >/etc/apt/sources.list.d/ceph.sources <&2 2>&1 1>&3) + case $CHOICE in + yes) + msg_info "Adding 'pvetest' repository (deb822, disabled)" + cat >/etc/apt/sources.list.d/pvetest.sources <&2 2>&1 1>&3) @@ -195,7 +298,7 @@ EOF yes) whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox --title "Support Subscriptions" "Supporting the software's development team is essential. Check their official website's Support Subscriptions for pricing. Without their dedicated work, we wouldn't have this exceptional software." 10 58 msg_info "Disabling subscription nag" - echo "DPkg::Post-Invoke { \"if [ -s /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js ] && ! grep -q -F 'NoMoreNagging' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js; then echo 'Removing subscription nag from UI...'; sed -i '/data\.status/{s/\!//;s/active/NoMoreNagging/}' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js; fi\" };" >/etc/apt/apt.conf.d/no-nag-script + echo "DPkg::Post-Invoke { \"if [ -s /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js ] && ! grep -q -F 'NoMoreNagging' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js; then echo 'Removing subscription nag from UI...'; sed -i '/data\.status/{s/\\!//;s/active/NoMoreNagging/}' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js; fi\" };" >/etc/apt/apt.conf.d/no-nag-script msg_ok "Disabled subscription nag (Delete browser cache)" ;; no) @@ -218,9 +321,7 @@ EOF systemctl enable -q --now corosync msg_ok "Enabled high availability" ;; - no) - msg_error "Selected no to Enabling high availability" - ;; + no) msg_error "Selected no to Enabling high availability" ;; esac fi @@ -243,14 +344,10 @@ EOF systemctl disable -q --now corosync msg_ok "Disabled Corosync" ;; - no) - msg_error "Selected no to Disabling Corosync" - ;; + no) msg_error "Selected no to Disabling Corosync" ;; esac ;; - no) - msg_error "Selected no to Disabling high availability" - ;; + no) msg_error "Selected no to Disabling high availability" ;; esac fi @@ -264,9 +361,7 @@ EOF apt-get -y dist-upgrade &>/dev/null msg_ok "Updated Proxmox VE" ;; - no) - msg_error "Selected no to Updating Proxmox VE" - ;; + no) msg_error "Selected no to Updating Proxmox VE" ;; esac CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "REBOOT" --menu "\nReboot Proxmox VE now? (recommended)" 11 58 2 \ @@ -286,26 +381,4 @@ EOF esac } -header_info -echo -e "\nThis script will Perform Post Install Routines.\n" -while true; do - read -p "Start the Proxmox VE Post Install Script (y/n)?" yn - case $yn in - [Yy]*) break ;; - [Nn]*) - clear - exit - ;; - *) echo "Please answer yes or no." ;; - esac -done - -if ! pveversion | grep -Eq "pve-manager/8\.[0-4](\.[0-9]+)*"; then - msg_error "This version of Proxmox Virtual Environment is not supported" - echo -e "Requires Proxmox Virtual Environment Version 8.0 or later." - echo -e "Exiting..." - sleep 2 - exit -fi - -start_routines +main From 65fd5260ba6425b6778a0e5fb3f456cf6a284847 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 6 Aug 2025 10:55:23 +0200 Subject: [PATCH 0388/1733] Update post-pve-install.sh --- tools/pve/post-pve-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/pve/post-pve-install.sh b/tools/pve/post-pve-install.sh index 89b2ab7b6..248bd5256 100644 --- a/tools/pve/post-pve-install.sh +++ b/tools/pve/post-pve-install.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # Copyright (c) 2021-2025 tteck -# Author: tteckster +# Author: tteckster | MickLesk (CanbiZ) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -98,7 +98,7 @@ main() { start_routines_8() { header_info - # === Bookworm/8.x: .list-Dateien === + # === Bookworm/8.x: .list-Files === CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "SOURCES" --menu "The package manager will use the correct sources to update and install packages on your Proxmox VE server.\n \nCorrect Proxmox VE sources?" 14 58 2 \ "yes" " " \ "no" " " 3>&2 2>&1 1>&3) From e7019d6b05150400271c77524c293c608846571d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 6 Aug 2025 10:58:01 +0200 Subject: [PATCH 0389/1733] Update post-pve-install.sh --- tools/pve/post-pve-install.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tools/pve/post-pve-install.sh b/tools/pve/post-pve-install.sh index 248bd5256..ac7ea35c0 100644 --- a/tools/pve/post-pve-install.sh +++ b/tools/pve/post-pve-install.sh @@ -364,6 +364,16 @@ post_routines_common() { no) msg_error "Selected no to Updating Proxmox VE" ;; esac + # Final message for all hosts in cluster and browser cache + whiptail --backtitle "Proxmox VE Helper Scripts" --title "Post-Install Reminder" --msgbox \ + "IMPORTANT: +If you have multiple Proxmox VE hosts in a cluster, please make sure to run this script on every node individually. + +After completing these steps, it is strongly recommended to REBOOT your node. + +After the upgrade or post-install routines, always clear your browser cache or perform a hard reload (Ctrl+Shift+R) before using the Proxmox VE Web UI to avoid UI display issues. +" 14 70 + CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "REBOOT" --menu "\nReboot Proxmox VE now? (recommended)" 11 58 2 \ "yes" " " \ "no" " " 3>&2 2>&1 1>&3) From 5a290974a2e844c06f6324ef67b026012464ec54 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 6 Aug 2025 11:02:42 +0200 Subject: [PATCH 0390/1733] debug --- tools/pve/post-pve-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/pve/post-pve-install.sh b/tools/pve/post-pve-install.sh index ac7ea35c0..ddbcdb88d 100644 --- a/tools/pve/post-pve-install.sh +++ b/tools/pve/post-pve-install.sh @@ -307,8 +307,8 @@ post_routines_common() { rm /etc/apt/apt.conf.d/no-nag-script 2>/dev/null ;; esac - apt --reinstall install proxmox-widget-toolkit &>/dev/null - + apt --reinstall install proxmox-widget-toolkit &>/dev/null || msg_error "Widget toolkit reinstall failed" + echo "AFTER WIDGET" if ! systemctl is-active --quiet pve-ha-lrm; then CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "HIGH AVAILABILITY" --menu "Enable high availability?" 10 58 2 \ "yes" " " \ From e01b1669c85e224235eaade007d22932703a9665 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 6 Aug 2025 11:08:39 +0200 Subject: [PATCH 0391/1733] Update post-pve-install.sh --- tools/pve/post-pve-install.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/pve/post-pve-install.sh b/tools/pve/post-pve-install.sh index ddbcdb88d..b51a7a276 100644 --- a/tools/pve/post-pve-install.sh +++ b/tools/pve/post-pve-install.sh @@ -307,6 +307,7 @@ post_routines_common() { rm /etc/apt/apt.conf.d/no-nag-script 2>/dev/null ;; esac + echo "BEFORE WIDGET" apt --reinstall install proxmox-widget-toolkit &>/dev/null || msg_error "Widget toolkit reinstall failed" echo "AFTER WIDGET" if ! systemctl is-active --quiet pve-ha-lrm; then @@ -357,8 +358,8 @@ post_routines_common() { case $CHOICE in yes) msg_info "Updating Proxmox VE (Patience)" - apt-get update &>/dev/null - apt-get -y dist-upgrade &>/dev/null + apt update &>/dev/null || msg_error "apt update failed" + apt -y dist-upgrade &>/dev/null || msg_error "apt dist-upgrade failed" msg_ok "Updated Proxmox VE" ;; no) msg_error "Selected no to Updating Proxmox VE" ;; From 80919c76b330446be7f2b9684d9c546e0161b0b3 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 6 Aug 2025 11:17:27 +0200 Subject: [PATCH 0392/1733] Update post-pve-install.sh --- tools/pve/post-pve-install.sh | 88 ++++++++++++++++++++++++++++------- 1 file changed, 72 insertions(+), 16 deletions(-) diff --git a/tools/pve/post-pve-install.sh b/tools/pve/post-pve-install.sh index b51a7a276..88f89083d 100644 --- a/tools/pve/post-pve-install.sh +++ b/tools/pve/post-pve-install.sh @@ -181,17 +181,71 @@ EOF start_routines_9() { header_info - # === Trixie/9.x: deb822 .sources === - CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "SOURCES" --menu "The package manager will use the correct sources to update and install packages on your Proxmox VE 9 server.\n\nMigrate to deb822 sources format?" 14 58 2 \ - "yes" " " \ - "no" " " 3>&2 2>&1 1>&3) - case $CHOICE in - yes) - msg_info "Correcting Proxmox VE Sources (deb822)" - rm -f /etc/apt/sources.list.d/*.list - sed -i '/proxmox/d;/bookworm/d' /etc/apt/sources.list || true - # Modern Debian base sources (deb822) - cat >/etc/apt/sources.list.d/debian.sources </dev/null) + if [[ -n "$list_files" ]]; then + LEGACY_COUNT=$((LEGACY_COUNT + $(echo "$list_files" | wc -l))) + fi + + if ((LEGACY_COUNT > 0)); then + # Show summary to user + local MSG="Legacy APT sources found:\n" + [[ -f "$listfile" ]] && MSG+=" - /etc/apt/sources.list\n" + [[ -n "$list_files" ]] && MSG+="$(echo "$list_files" | sed 's|^| - |')\n" + MSG+="\nDo you want to disable (comment out/rename) all legacy sources and use ONLY deb822 .sources format?\n\nRecommended for Proxmox VE 9." + + whiptail --backtitle "Proxmox VE Helper Scripts" --title "Disable legacy sources?" \ + --yesno "$MSG" 18 80 + if [[ $? -eq 0 ]]; then + # Backup and disable sources.list + if [[ -f "$listfile" ]] && grep -qE '^\s*deb ' "$listfile"; then + cp "$listfile" "$listfile.bak" + sed -i '/^\s*deb /s/^/# Disabled by Proxmox Helper Script /' "$listfile" + msg_ok "Disabled entries in sources.list (backup: sources.list.bak)" + fi + # Rename all .list files to .list.bak + if [[ -n "$list_files" ]]; then + while IFS= read -r f; do + mv "$f" "$f.bak" + done <<<"$list_files" + msg_ok "Renamed legacy .list files to .bak" + fi + else + msg_error "Kept legacy sources as-is (may cause APT warnings)" + fi + fi + } + + check_and_disable_legacy_sources + # === Trixie/9.x: deb822 .sources === + CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "SOURCES" --menu \ + "The package manager will use the correct sources to update and install packages on your Proxmox VE 9 server.\n\nMigrate to deb822 sources format?" 14 58 2 \ + "yes" " " \ + "no" " " 3>&2 2>&1 1>&3) + case $CHOICE in + yes) + msg_info "Correcting Proxmox VE Sources (deb822)" + # remove all existing .list files + rm -f /etc/apt/sources.list.d/*.list + # remove bookworm and proxmox entries from sources.list + sed -i '/proxmox/d;/bookworm/d' /etc/apt/sources.list || true + # Create new deb822 sources + cat >/etc/apt/sources.list.d/debian.sources <&2 2>&1 1>&3) case $CHOICE in From 201208bfe5e7c19597eb10e9bb98ce42b40ac1ed Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 6 Aug 2025 11:17:56 +0200 Subject: [PATCH 0393/1733] Update post-pve-install.sh --- tools/pve/post-pve-install.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tools/pve/post-pve-install.sh b/tools/pve/post-pve-install.sh index 88f89083d..986e7210c 100644 --- a/tools/pve/post-pve-install.sh +++ b/tools/pve/post-pve-install.sh @@ -270,10 +270,13 @@ EOF esac fi - CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PVE-ENTERPRISE" --menu \ - "The 'pve-enterprise' repository is only available to users who have purchased a Proxmox VE subscription.\n \nAdd 'pve-enterprise' repository (deb822)?" 14 58 2 \ + CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "PVE-ENTERPRISE" \ + --menu "The 'pve-enterprise' repository is only available to users who have purchased a Proxmox VE subscription.\n\nAdd 'pve-enterprise' repository (deb822)?" 14 58 2 \ + "no" " " \ "yes" " " \ - "no" " " 3>&2 2>&1 1>&3) + --default-item "no" \ + 3>&2 2>&1 1>&3) case $CHOICE in yes) msg_info "Adding 'pve-enterprise' repository (deb822)" From 7d38e46ba27c687ce2003b34400b249246114d7b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 6 Aug 2025 11:31:46 +0200 Subject: [PATCH 0394/1733] Update post-pve-install.sh --- tools/pve/post-pve-install.sh | 127 +++++++++++++++++++++------------- 1 file changed, 78 insertions(+), 49 deletions(-) diff --git a/tools/pve/post-pve-install.sh b/tools/pve/post-pve-install.sh index 986e7210c..bbd83822d 100644 --- a/tools/pve/post-pve-install.sh +++ b/tools/pve/post-pve-install.sh @@ -57,6 +57,11 @@ get_pve_major_minor() { echo "$major $minor" } +component_exists_in_sources() { + local component="$1" + grep -h -E "^[^#]*Components:[^#]*\b${component}\b" /etc/apt/sources.list.d/*.sources 2>/dev/null | grep -q . +} + main() { header_info echo -e "\nThis script will Perform Post Install Routines.\n" @@ -270,81 +275,104 @@ EOF esac fi - CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ - --title "PVE-ENTERPRISE" \ - --menu "The 'pve-enterprise' repository is only available to users who have purchased a Proxmox VE subscription.\n\nAdd 'pve-enterprise' repository (deb822)?" 14 58 2 \ - "no" " " \ - "yes" " " \ - --default-item "no" \ - 3>&2 2>&1 1>&3) - case $CHOICE in - yes) - msg_info "Adding 'pve-enterprise' repository (deb822)" - cat >/etc/apt/sources.list.d/pve-enterprise.sources <&2 2>&1 1>&3) + case $CHOICE in + yes) + msg_info "Adding 'pve-enterprise' repository (deb822)" + cat >/etc/apt/sources.list.d/pve-enterprise.sources <&2 2>&1 1>&3) - case $CHOICE in - yes) - msg_info "Adding 'pve-no-subscription' repository (deb822)" - cat >/etc/apt/sources.list.d/proxmox.sources <&2 2>&1 1>&3) + case $CHOICE in + yes) + msg_info "Adding 'pve-no-subscription' repository (deb822)" + cat >/etc/apt/sources.list.d/proxmox.sources <&2 2>&1 1>&3) - case $CHOICE in - yes) - msg_info "Adding 'ceph package repositories' (deb822)" - cat >/etc/apt/sources.list.d/ceph.sources <&2 2>&1 1>&3) + case $CHOICE in + yes) + msg_info "Adding 'ceph package repositories' (deb822)" + cat >/etc/apt/sources.list.d/ceph.sources <&2 2>&1 1>&3) - case $CHOICE in - yes) - msg_info "Adding 'pvetest' repository (deb822, disabled)" - cat >/etc/apt/sources.list.d/pvetest.sources <&2 2>&1 1>&3) + case $CHOICE in + yes) + msg_info "Adding 'pvetest' repository (deb822, disabled)" + cat >/etc/apt/sources.list.d/pvetest.sources < Date: Wed, 6 Aug 2025 11:32:27 +0200 Subject: [PATCH 0395/1733] Delete ct/gitea-mirror.sh --- ct/gitea-mirror.sh | 105 --------------------------------------------- 1 file changed, 105 deletions(-) delete mode 100644 ct/gitea-mirror.sh diff --git a/ct/gitea-mirror.sh b/ct/gitea-mirror.sh deleted file mode 100644 index ce0e21ee7..000000000 --- a/ct/gitea-mirror.sh +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: CrazyWolf13 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/RayLabsHQ/gitea-mirror - -APP="gitea-mirror" -var_tags="${var_tags:-mirror;gitea}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-6}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" - -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - - if [[ ! -d /opt/gitea-mirror ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - APP_VERSION=$(grep -o '"version": *"[^"]*"' /opt/gitea-mirror/package.json | cut -d'"' -f4) - if [[ $APP_VERSION =~ ^2\. ]]; then - if ! whiptail --backtitle "Gitea Mirror Update" --title "⚠️ VERSION 2.x DETECTED" --yesno \ - "WARNING: Version $APP_VERSION detected!\n\nUpdating from version 2.x will CLEAR ALL CONFIGURATION.\n\nThis includes:\n• API tokens\n• User settings\n• Repository configurations\n• All custom settings\n\nDo you want to continue with the update process?" 15 70 --defaultno - then - exit 0 - fi - - if ! whiptail --backtitle "Gitea Mirror Update" --title "⚠️ FINAL CONFIRMATION" --yesno \ - "FINAL WARNING: This update WILL clear all configuration!\n\nBEFORE PROCEEDING, please:\n\n• Copy API tokens to a safe location\n• Backup any custom configurations\n• Note down repository settings\n\nThis action CANNOT be undone!" 18 70 --defaultno - then - whiptail --backtitle "Gitea Mirror Update" --title "Update Cancelled" --msgbox "Update process cancelled. Please backup your configuration before proceeding." 8 60 - exit 0 - fi - whiptail --backtitle "Gitea Mirror Update" --title "Proceeding with Update" --msgbox \ - "Proceeding with version $APP_VERSION update.\n\nAll configuration will be cleared as warned." 8 50 - rm -rf /opt/gitea-mirror - fi - - RELEASE=$(curl -fsSL https://api.github.com/repos/RayLabsHQ/gitea-mirror/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') - if [[ "${RELEASE}" != "$(cat ~/.${APP} 2>/dev/null || cat /opt/${APP}_version.txt 2>/dev/null)" ]]; then - - msg_info "Stopping Services" - systemctl stop gitea-mirror - msg_ok "Services Stopped" - - if [[ -d /opt/gitea-mirror/data ]]; then - msg_info "Backing up Data" - mkdir -p /opt/gitea-mirror-backup/data - cp /opt/gitea-mirror/data/* /opt/gitea-mirror-backup/data/ - msg_ok "Backed up Data" - fi - - msg_info "Installing Bun" - export BUN_INSTALL=/opt/bun - curl -fsSL https://bun.sh/install | $STD bash - ln -sf /opt/bun/bin/bun /usr/local/bin/bun - ln -sf /opt/bun/bin/bun /usr/local/bin/bunx - msg_ok "Installed Bun" - - rm -rf /opt/gitea-mirror - fetch_and_deploy_gh_release "gitea-mirror" "RayLabsHQ/gitea-mirror" "tarball" "v3.0.2" - - msg_info "Updating and rebuilding ${APP} to v${RELEASE}" - cd /opt/gitea-mirror - $STD bun run setup - $STD bun run build - APP_VERSION=$(grep -o '"version": *"[^"]*"' package.json | cut -d'"' -f4) - sudo sed -i.bak "s|^Environment=npm_package_version=.*|Environment=npm_package_version=${APP_VERSION}|" /etc/systemd/system/gitea-mirror.service - msg_ok "Updated and rebuilt ${APP} to v${RELEASE}" - - msg_info "Restoring Data" - cp /opt/gitea-mirror-backup/data/* /opt/gitea-mirror/data || true - msg_ok "Restored Data" - - msg_info "Starting Service" - systemctl daemon-reload - systemctl start gitea-mirror - msg_ok "Service Started" - else - msg_ok "No update required. ${APP} is already at v${RELEASE}" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:4321${CL}" From 9184cdaa22647aec8cedd3c35e789ea61acb0ca1 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 6 Aug 2025 11:33:24 +0200 Subject: [PATCH 0396/1733] Delete install/gitea-mirror-install.sh --- install/gitea-mirror-install.sh | 70 --------------------------------- 1 file changed, 70 deletions(-) delete mode 100644 install/gitea-mirror-install.sh diff --git a/install/gitea-mirror-install.sh b/install/gitea-mirror-install.sh deleted file mode 100644 index a1c68c170..000000000 --- a/install/gitea-mirror-install.sh +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: CrazyWolf13 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/RayLabsHQ/gitea-mirror - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing dependencies" -$STD apt-get install -y \ - build-essential \ - openssl \ - sqlite3 \ - unzip -msg_ok "Installed Dependencies" - -msg_info "Installing Bun" -export BUN_INSTALL=/opt/bun -curl -fsSL https://bun.sh/install | $STD bash -ln -sf /opt/bun/bin/bun /usr/local/bin/bun -ln -sf /opt/bun/bin/bun /usr/local/bin/bunx -msg_ok "Installed Bun" - -fetch_and_deploy_gh_release "gitea-mirror" "RayLabsHQ/gitea-mirror" "tarball" "v3.0.2" - -msg_info "Installing gitea-mirror" -cd /opt/gitea-mirror -$STD bun run setup -$STD bun run build -msg_ok "Installed gitea-mirror" - -msg_info "Creating Services" -JWT_SECRET=$(openssl rand -hex 32) -APP_VERSION=$(grep -o '"version": *"[^"]*"' package.json | cut -d'"' -f4) -cat </etc/systemd/system/gitea-mirror.service -[Unit] -Description=Gitea Mirror -After=network.target -[Service] -Type=simple -WorkingDirectory=/opt/gitea-mirror -ExecStart=/usr/local/bin/bun dist/server/entry.mjs -Restart=on-failure -RestartSec=10 -Environment=NODE_ENV=production -Environment=HOST=0.0.0.0 -Environment=PORT=4321 -Environment=DATABASE_URL=file:/opt/gitea-mirror/data/gitea-mirror.db -Environment=JWT_SECRET=${JWT_SECRET} -Environment=npm_package_version=${APP_VERSION} -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now gitea-mirror -msg_ok "Created Service" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" From 63f7f6f66b57e980b26ac29ef65703a8950ae15b Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 6 Aug 2025 11:33:42 +0200 Subject: [PATCH 0397/1733] Delete ct/headers/gitea-mirror --- ct/headers/gitea-mirror | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 ct/headers/gitea-mirror diff --git a/ct/headers/gitea-mirror b/ct/headers/gitea-mirror deleted file mode 100644 index 57003b058..000000000 --- a/ct/headers/gitea-mirror +++ /dev/null @@ -1,6 +0,0 @@ - _ __ _ - ____ _(_) /____ ____ _ ____ ___ (_)_____________ _____ - / __ `/ / __/ _ \/ __ `/_____/ __ `__ \/ / ___/ ___/ __ \/ ___/ - / /_/ / / /_/ __/ /_/ /_____/ / / / / / / / / / / /_/ / / - \__, /_/\__/\___/\__,_/ /_/ /_/ /_/_/_/ /_/ \____/_/ -/____/ From adbe68e5791f9ca987ef314eb293800ef21ee8c7 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 6 Aug 2025 11:33:52 +0200 Subject: [PATCH 0398/1733] Delete frontend/public/json/gitea-mirror.json --- frontend/public/json/gitea-mirror.json | 35 -------------------------- 1 file changed, 35 deletions(-) delete mode 100644 frontend/public/json/gitea-mirror.json diff --git a/frontend/public/json/gitea-mirror.json b/frontend/public/json/gitea-mirror.json deleted file mode 100644 index 5be5cc905..000000000 --- a/frontend/public/json/gitea-mirror.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "Gitea-Mirror", - "slug": "gitea-mirror", - "categories": [ - 7 - ], - "date_created": "2025-06-05", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 4321, - "documentation": "https://github.com/arunavo4/gitea-mirror/", - "config_path": "/etc/systemd/system/gitea-mirror.service", - "website": "https://github.com/arunavo4/gitea-mirror/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/gitea-mirror.webp", - "description": "Gitea Mirror auto-syncs GitHub repos to your self-hosted Gitea, with a sleek Web UI and easy Docker deployment. ", - "install_methods": [ - { - "type": "default", - "script": "ct/gitea-mirror.sh", - "resources": { - "cpu": 2, - "ram": 2048, - "hdd": 6, - "os": "Debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] -} From 819a93c47d649bd4c7706eefa7d11c1cf24c6626 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 6 Aug 2025 11:41:35 +0200 Subject: [PATCH 0399/1733] Create tracktor.json --- frontend/public/json/tracktor.json | 35 ++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 frontend/public/json/tracktor.json diff --git a/frontend/public/json/tracktor.json b/frontend/public/json/tracktor.json new file mode 100644 index 000000000..fa6319c6a --- /dev/null +++ b/frontend/public/json/tracktor.json @@ -0,0 +1,35 @@ +{ + "name": "Tracktor", + "slug": "tracktor", + "categories": [ + 9 + ], + "date_created": "2025-08-06", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 5173, + "documentation": "https://tracktor.bytedge.in/introduction.html", + "config_path": "/etc/system/systemd/tracktor.service", + "website": "https://tracktor.bytedge.in/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/tracktor.svg", + "description": "Tracktor is an open-source web application for comprehensive vehicle management.\nEasily track ⛽ fuel consumption, 🛠️ maintenance, 🛡️ insurance, and 📄 regulatory documents for all your vehicles in one place. ", + "install_methods": [ + { + "type": "default", + "script": "ct/tracktor.sh", + "resources": { + "cpu": 1, + "ram": 1024, + "hdd": 6, + "os": "Debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} From 2dad761b60b5c31aeb427e0c43c5b7754dbb342f Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 6 Aug 2025 11:41:54 +0200 Subject: [PATCH 0400/1733] Create tracktor.sh --- ct/tracktor.sh | 1 + 1 file changed, 1 insertion(+) create mode 100644 ct/tracktor.sh diff --git a/ct/tracktor.sh b/ct/tracktor.sh new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/ct/tracktor.sh @@ -0,0 +1 @@ + From a21fe2ee9357103e443d5c240fa6a3717f629156 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 6 Aug 2025 09:42:07 +0000 Subject: [PATCH 0401/1733] Update .app files --- tools/headers/copyparty | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 tools/headers/copyparty diff --git a/tools/headers/copyparty b/tools/headers/copyparty new file mode 100644 index 000000000..7e072248d --- /dev/null +++ b/tools/headers/copyparty @@ -0,0 +1,6 @@ + ______ ____ __ + / ____/___ ____ __ __/ __ \____ ______/ /___ __ + / / / __ \/ __ \/ / / / /_/ / __ `/ ___/ __/ / / / +/ /___/ /_/ / /_/ / /_/ / ____/ /_/ / / / /_/ /_/ / +\____/\____/ .___/\__, /_/ \__,_/_/ \__/\__, / + /_/ /____/ /____/ From 41fde7d50e6499b53a1a2bdb4f544a34fb9624e5 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 6 Aug 2025 11:42:14 +0200 Subject: [PATCH 0402/1733] Create tracktor-install.sh --- install/tracktor-install.sh | 1 + 1 file changed, 1 insertion(+) create mode 100644 install/tracktor-install.sh diff --git a/install/tracktor-install.sh b/install/tracktor-install.sh new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/install/tracktor-install.sh @@ -0,0 +1 @@ + From ee5c68baa9d9caa4a43be8dce19c49027358d0b0 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 6 Aug 2025 10:06:12 +0000 Subject: [PATCH 0403/1733] Update .app files --- ct/headers/traefik | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/traefik diff --git a/ct/headers/traefik b/ct/headers/traefik new file mode 100644 index 000000000..1e9d42389 --- /dev/null +++ b/ct/headers/traefik @@ -0,0 +1,6 @@ + ______ _____ __ + /_ __/________ ____ / __(_) /__ + / / / ___/ __ `/ _ \/ /_/ / //_/ + / / / / / /_/ / __/ __/ / ,< +/_/ /_/ \__,_/\___/_/ /_/_/|_| + From 908ead9a00fb2e1257ce642bce07749f7c558951 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 6 Aug 2025 12:12:47 +0200 Subject: [PATCH 0404/1733] Update post-pve-install.sh --- tools/pve/post-pve-install.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/tools/pve/post-pve-install.sh b/tools/pve/post-pve-install.sh index bbd83822d..e51a72c44 100644 --- a/tools/pve/post-pve-install.sh +++ b/tools/pve/post-pve-install.sh @@ -394,9 +394,7 @@ post_routines_common() { rm /etc/apt/apt.conf.d/no-nag-script 2>/dev/null ;; esac - echo "BEFORE WIDGET" apt --reinstall install proxmox-widget-toolkit &>/dev/null || msg_error "Widget toolkit reinstall failed" - echo "AFTER WIDGET" if ! systemctl is-active --quiet pve-ha-lrm; then CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "HIGH AVAILABILITY" --menu "Enable high availability?" 10 58 2 \ "yes" " " \ From 59493d936b066dab789d71b3ec64cfe7e4818ad3 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 6 Aug 2025 12:34:08 +0200 Subject: [PATCH 0405/1733] Update post-pve-install.sh --- tools/pve/post-pve-install.sh | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/tools/pve/post-pve-install.sh b/tools/pve/post-pve-install.sh index e51a72c44..3e4e09834 100644 --- a/tools/pve/post-pve-install.sh +++ b/tools/pve/post-pve-install.sh @@ -277,7 +277,37 @@ EOF # ---- PVE-ENTERPRISE ---- if component_exists_in_sources "pve-enterprise"; then - msg_ok "'pve-enterprise' repository already exists (skipped)" + CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "PVE-ENTERPRISE" \ + --menu "'pve-enterprise' repository already exists.\n\nWhat do you want to do?" 14 58 2 \ + "keep" "Keep as is" \ + "disable" "Comment out (disable) this repo" \ + "delete" "Delete this repo file" \ + 3>&2 2>&1 1>&3) + case $CHOICE in + keep) + msg_ok "Kept 'pve-enterprise' repository" + ;; + disable) + msg_info "Disabling (commenting) 'pve-enterprise' repository" + # Comment out every non-comment line in the file that has 'pve-enterprise' in Components + for file in /etc/apt/sources.list.d/*.sources; do + if grep -q "Components:.*pve-enterprise" "$file"; then + sed -i '/^\s*Types:/,/^$/s/^\([^#].*\)$/# \1/' "$file" + fi + done + msg_ok "Disabled 'pve-enterprise' repository" + ;; + delete) + msg_info "Deleting 'pve-enterprise' repository file" + for file in /etc/apt/sources.list.d/*.sources; do + if grep -q "Components:.*pve-enterprise" "$file"; then + rm -f "$file" + fi + done + msg_ok "Deleted 'pve-enterprise' repository file" + ;; + esac else CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "PVE-ENTERPRISE" \ From 5eb51cfd76dd2e5d2e6622c07e89dfda517a3831 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 6 Aug 2025 12:37:19 +0200 Subject: [PATCH 0406/1733] Update post-pve-install.sh --- tools/pve/post-pve-install.sh | 66 +++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/tools/pve/post-pve-install.sh b/tools/pve/post-pve-install.sh index 3e4e09834..e61676c9f 100644 --- a/tools/pve/post-pve-install.sh +++ b/tools/pve/post-pve-install.sh @@ -333,8 +333,70 @@ EOF fi # ---- PVE-NO-SUBSCRIPTION ---- - if component_exists_in_sources "pve-no-subscription"; then - msg_ok "'pve-no-subscription' repository already exists (skipped)" + # ---- PVE-NO-SUBSCRIPTION ---- + REPO_FILE="" + REPO_ACTIVE=0 + REPO_COMMENTED=0 + # Suche nach existierendem Block (aktiv oder auskommentiert) + for file in /etc/apt/sources.list.d/*.sources; do + if grep -q "Components:.*pve-no-subscription" "$file"; then + REPO_FILE="$file" + if grep -E '^[^#]*Components:.*pve-no-subscription' "$file" >/dev/null; then + REPO_ACTIVE=1 + elif grep -E '^#.*Components:.*pve-no-subscription' "$file" >/dev/null; then + REPO_COMMENTED=1 + fi + break + fi + done + + if [[ "$REPO_ACTIVE" -eq 1 ]]; then + CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "PVE-NO-SUBSCRIPTION" \ + --menu "'pve-no-subscription' repository is currently ENABLED.\n\nWhat do you want to do?" 14 58 3 \ + "keep" "Keep as is" \ + "disable" "Comment out (disable)" \ + "delete" "Delete repo file" \ + 3>&2 2>&1 1>&3) + case $CHOICE in + keep) + msg_ok "Kept 'pve-no-subscription' repository" + ;; + disable) + msg_info "Disabling (commenting) 'pve-no-subscription' repository" + sed -i '/^\s*Types:/,/^$/s/^\([^#].*\)$/# \1/' "$REPO_FILE" + msg_ok "Disabled 'pve-no-subscription' repository" + ;; + delete) + msg_info "Deleting 'pve-no-subscription' repository file" + rm -f "$REPO_FILE" + msg_ok "Deleted 'pve-no-subscription' repository file" + ;; + esac + + elif [[ "$REPO_COMMENTED" -eq 1 ]]; then + CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "PVE-NO-SUBSCRIPTION" \ + --menu "'pve-no-subscription' repository is currently DISABLED (commented out).\n\nWhat do you want to do?" 14 58 3 \ + "enable" "Uncomment (enable)" \ + "keep" "Keep disabled" \ + "delete" "Delete repo file" \ + 3>&2 2>&1 1>&3) + case $CHOICE in + enable) + msg_info "Enabling (uncommenting) 'pve-no-subscription' repository" + sed -i '/^#\s*Types:/,/^$/s/^#\s*//' "$REPO_FILE" + msg_ok "Enabled 'pve-no-subscription' repository" + ;; + keep) + msg_ok "Kept 'pve-no-subscription' repository disabled" + ;; + delete) + msg_info "Deleting 'pve-no-subscription' repository file" + rm -f "$REPO_FILE" + msg_ok "Deleted 'pve-no-subscription' repository file" + ;; + esac else CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PVE-NO-SUBSCRIPTION" \ --menu "The 'pve-no-subscription' repository provides access to all of the open-source components of Proxmox VE.\n\nAdd 'pve-no-subscription' repository (deb822)?" 14 58 2 \ From b892cc4cd21e6579216e1b9262889fc2ed349caa Mon Sep 17 00:00:00 2001 From: vhsdream Date: Wed, 6 Aug 2025 08:17:29 -0400 Subject: [PATCH 0407/1733] MediaManager: fix path issue --- install/mediamanager-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/mediamanager-install.sh b/install/mediamanager-install.sh index 80cb37435..ad2cb3456 100644 --- a/install/mediamanager-install.sh +++ b/install/mediamanager-install.sh @@ -77,7 +77,7 @@ sed -e "s/localhost:8/$LOCAL_IP:8/g" \ -e "/^token_secret/s/=.*/= \"$SECRET\"/" \ -e "s/admin@example.com/$EMAIL/" \ -e '/^admin_emails/s/, .*/]/' \ - /opt/mediamanager/config.example.toml >/opt/"$CONFIG_DIR"/config.toml + /opt/mediamanager/config.example.toml >"$CONFIG_DIR"/config.toml mkdir -p "$MEDIA_DIR"/{images,tv,movies,torrents} From e8ae7a9bb5c04b9f2bbf12f0745ddb2c77a8d899 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 6 Aug 2025 14:53:40 +0200 Subject: [PATCH 0408/1733] Update clean-lxcs.sh --- tools/pve/clean-lxcs.sh | 116 +++++++++++++++++++++++----------------- 1 file changed, 66 insertions(+), 50 deletions(-) diff --git a/tools/pve/clean-lxcs.sh b/tools/pve/clean-lxcs.sh index dc96eaa86..655943e04 100644 --- a/tools/pve/clean-lxcs.sh +++ b/tools/pve/clean-lxcs.sh @@ -1,12 +1,9 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) +# Copyright (c) 2021-2025 community-scripts ORG # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -set -eEuo pipefail - function header_info() { clear cat <<"EOF" @@ -15,10 +12,10 @@ function header_info() { / / / / _ \/ __ `/ __ \ / / | / / / /___/ / __/ /_/ / / / / / /___/ / /___ \____/_/\___/\__,_/_/ /_/ /_____/_/|_\____/ - EOF } +set -eEuo pipefail BL="\033[36m" RD="\033[01;31m" CM='\xE2\x9C\x94\033' @@ -27,82 +24,101 @@ CL="\033[m" header_info echo "Loading..." -whiptail --backtitle "Proxmox VE Helper Scripts" --title "Proxmox VE LXC Updater" \ - --yesno "This Will Clean logs, cache and update apt/apk lists on selected LXC Containers. Proceed?" 10 68 || exit + +whiptail --backtitle "Proxmox VE Helper Scripts" --title "Proxmox VE LXC Updater" --yesno "This Will Clean logs, cache and update apt lists on selected LXC Containers. Proceed?" 10 58 NODE=$(hostname) EXCLUDE_MENU=() MSG_MAX_LENGTH=0 + while read -r TAG ITEM; do OFFSET=2 ((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=${#ITEM}+OFFSET EXCLUDE_MENU+=("$TAG" "$ITEM " "OFF") done < <(pct list | awk 'NR>1') -excluded_containers=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Containers on $NODE" \ - --checklist "\nSelect containers to skip from cleaning:\n" 16 $((MSG_MAX_LENGTH + 23)) 6 "${EXCLUDE_MENU[@]}" \ - 3>&1 1>&2 2>&3 | tr -d '"') || exit -function clean_container() { - container=$1 +excluded_containers=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Containers on $NODE" --checklist "\nSelect containers to skip from cleaning:\n" \ + 16 $((MSG_MAX_LENGTH + 23)) 6 "${EXCLUDE_MENU[@]}" 3>&1 1>&2 2>&3 | tr -d '"') + +if [ $? -ne 0 ]; then + exit +fi + +function run_lxc_clean() { + local container=$1 header_info name=$(pct exec "$container" hostname) - os=$(pct config "$container" | awk '/^ostype/ {print $2}') - echo -e "${BL}[Info]${GN} Cleaning ${name} (${os}) ${CL} \n" - if [ "$os" = "alpine" ]; then - pct exec "$container" -- ash -c "wget -qO- https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/tools/pve/clean.sh | ash" - else - pct exec "$container" -- bash -c "apt-get -y --purge autoremove && apt-get -y autoclean && bash <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/tools/pve/clean.sh) && rm -rf /var/lib/apt/lists/* && apt-get update" - fi + echo -e "${BL}[Info]${GN} Cleaning ${name} ${CL} \n" + pct exec "$container" -- bash -c ' + BL="\033[36m" + GN="\033[1;92m" + CL="\033[m" + name=$(hostname) + echo -e "${BL}[Info]${GN} Cleaning $name${CL} \n" + + cache=$(find /var/cache/ -type f 2>/dev/null) + if [[ -z "$cache" ]]; then + echo -e "No cache files found. \n" + sleep 1 + else + find /var/cache -type f -delete 2>/dev/null + echo "Cache removed." + sleep 1 + fi + + echo -e "${BL}[Info]${GN} Cleaning $name${CL} \n" + logs=$(find /var/log/ -type f 2>/dev/null) + if [[ -z "$logs" ]]; then + echo -e "No log files found. \n" + sleep 1 + else + find /var/log -type f -delete 2>/dev/null + echo "Logs removed." + sleep 1 + fi + + echo -e "${BL}[Info]${GN} Cleaning $name${CL} \n" + echo -e "${GN}Populating apt lists${CL} \n" + apt-get -y --purge autoremove + apt-get -y autoclean + rm -rf /var/lib/apt/lists/* + apt-get update + ' } -for container in $(pct list | awk 'NR>1 {print $1}'); do - if [[ " ${excluded_containers[*]} " =~ " $container " ]]; then +for container in $(pct list | awk '{if(NR>1) print $1}'); do + if [[ " ${excluded_containers[@]} " =~ " $container " ]]; then header_info echo -e "${BL}[Info]${GN} Skipping ${BL}$container${CL}" sleep 1 continue fi - # locked? - if pct status "$container" | grep -q 'locked'; then - header_info - echo -e "${BL}[Info]${RD} Skipping locked container ${BL}$container${CL}" - sleep 1 - continue - fi - os=$(pct config "$container" | awk '/^ostype/ {print $2}') - if [ "$os" != "debian" ] && [ "$os" != "ubuntu" ] && [ "$os" != "alpine" ]; then + if [ "$os" != "debian" ] && [ "$os" != "ubuntu" ]; then header_info - name=$(pct exec "$container" hostname) - echo -e "${BL}[Info]${GN} Skipping ${name} ${RD}$container is not supported OS (${os})${CL} \n" + echo -e "${BL}[Info]${GN} Skipping ${RD}$container is not Debian or Ubuntu${CL} \n" sleep 1 continue fi - status=$(pct status "$container" | awk '{print $2}') + status=$(pct status "$container") template=$(pct config "$container" | grep -q "template:" && echo "true" || echo "false") - if [[ "$template" == "false" && "$status" == "stopped" ]]; then - if whiptail --backtitle "Proxmox VE Helper Scripts" \ - --title "Container $container is stopped" \ - --yesno "Container $container is stopped.\n\nStart and clean?" 10 58; then - echo -e "${BL}[Info]${GN} Starting${BL} $container ${CL} \n" - pct start "$container" - echo -e "${BL}[Info]${GN} Waiting for${BL} $container${CL}${GN} to start ${CL} \n" - sleep 5 - clean_container "$container" "$os" - echo -e "${BL}[Info]${GN} Shutting down${BL} $container ${CL} \n" - pct shutdown "$container" & - else - echo -e "${BL}[Info]${GN} Skipping stopped container ${BL}$container${CL}" - fi - elif [[ "$status" == "running" ]]; then - clean_container "$container" "$os" + if [ "$template" == "false" ] && [ "$status" == "status: stopped" ]; then + echo -e "${BL}[Info]${GN} Starting${BL} $container ${CL} \n" + pct start "$container" + echo -e "${BL}[Info]${GN} Waiting For${BL} $container${CL}${GN} To Start ${CL} \n" + sleep 5 + run_lxc_clean "$container" + echo -e "${BL}[Info]${GN} Shutting down${BL} $container ${CL} \n" + pct shutdown "$container" & + elif [ "$status" == "status: running" ]; then + run_lxc_clean "$container" fi done wait header_info -echo -e "${GN} Finished, selected containers cleaned. ${CL} \n" +echo -e "${GN} Finished, Selected Containers Cleaned. ${CL} \n" From cb21ca0552c0a990e06c340959a69e0c9b50116d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 6 Aug 2025 14:57:14 +0200 Subject: [PATCH 0409/1733] Update clean-lxcs.sh --- tools/pve/clean-lxcs.sh | 52 +++++++++++++++-------------------------- 1 file changed, 19 insertions(+), 33 deletions(-) diff --git a/tools/pve/clean-lxcs.sh b/tools/pve/clean-lxcs.sh index 655943e04..8db102f2e 100644 --- a/tools/pve/clean-lxcs.sh +++ b/tools/pve/clean-lxcs.sh @@ -25,7 +25,7 @@ CL="\033[m" header_info echo "Loading..." -whiptail --backtitle "Proxmox VE Helper Scripts" --title "Proxmox VE LXC Updater" --yesno "This Will Clean logs, cache and update apt lists on selected LXC Containers. Proceed?" 10 58 +whiptail --backtitle "Proxmox VE Helper Scripts" --title "Proxmox VE LXC Updater" --yesno "This will clean logs, cache and update package lists on selected LXC Containers. Proceed?" 10 58 NODE=$(hostname) EXCLUDE_MENU=() @@ -51,39 +51,24 @@ function run_lxc_clean() { echo -e "${BL}[Info]${GN} Cleaning ${name} ${CL} \n" pct exec "$container" -- bash -c ' - BL="\033[36m" - GN="\033[1;92m" - CL="\033[m" + BL="\033[36m"; GN="\033[1;92m"; CL="\033[m" name=$(hostname) - echo -e "${BL}[Info]${GN} Cleaning $name${CL} \n" - - cache=$(find /var/cache/ -type f 2>/dev/null) - if [[ -z "$cache" ]]; then - echo -e "No cache files found. \n" - sleep 1 - else - find /var/cache -type f -delete 2>/dev/null - echo "Cache removed." - sleep 1 - fi - - echo -e "${BL}[Info]${GN} Cleaning $name${CL} \n" - logs=$(find /var/log/ -type f 2>/dev/null) - if [[ -z "$logs" ]]; then - echo -e "No log files found. \n" - sleep 1 - else + if [ -e /etc/alpine-release ]; then + echo -e "${BL}[Info]${GN} Cleaning $name (Alpine)${CL}\n" + apk cache clean find /var/log -type f -delete 2>/dev/null - echo "Logs removed." - sleep 1 + find /tmp -mindepth 1 -delete 2>/dev/null + apk update + else + echo -e "${BL}[Info]${GN} Cleaning $name (Debian/Ubuntu)${CL}\n" + find /var/cache -type f -delete 2>/dev/null + find /var/log -type f -delete 2>/dev/null + find /tmp -mindepth 1 -delete 2>/dev/null + apt-get -y --purge autoremove + apt-get -y autoclean + rm -rf /var/lib/apt/lists/* + apt-get update fi - - echo -e "${BL}[Info]${GN} Cleaning $name${CL} \n" - echo -e "${GN}Populating apt lists${CL} \n" - apt-get -y --purge autoremove - apt-get -y autoclean - rm -rf /var/lib/apt/lists/* - apt-get update ' } @@ -96,9 +81,10 @@ for container in $(pct list | awk '{if(NR>1) print $1}'); do fi os=$(pct config "$container" | awk '/^ostype/ {print $2}') - if [ "$os" != "debian" ] && [ "$os" != "ubuntu" ]; then + # Supported: debian, ubuntu, alpine + if [ "$os" != "debian" ] && [ "$os" != "ubuntu" ] && [ "$os" != "alpine" ]; then header_info - echo -e "${BL}[Info]${GN} Skipping ${RD}$container is not Debian or Ubuntu${CL} \n" + echo -e "${BL}[Info]${GN} Skipping ${RD}$container is not Debian, Ubuntu or Alpine${CL} \n" sleep 1 continue fi From dde4b4e8ede1f1572db26a299033b6c8d5e0afa9 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 6 Aug 2025 14:58:41 +0200 Subject: [PATCH 0410/1733] Update clean-lxcs.sh --- tools/pve/clean-lxcs.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/pve/clean-lxcs.sh b/tools/pve/clean-lxcs.sh index 8db102f2e..7050f3422 100644 --- a/tools/pve/clean-lxcs.sh +++ b/tools/pve/clean-lxcs.sh @@ -48,7 +48,6 @@ function run_lxc_clean() { local container=$1 header_info name=$(pct exec "$container" hostname) - echo -e "${BL}[Info]${GN} Cleaning ${name} ${CL} \n" pct exec "$container" -- bash -c ' BL="\033[36m"; GN="\033[1;92m"; CL="\033[m" From dcc7a6872f570258251e3cf6c70b26130a5f4e47 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 6 Aug 2025 15:02:42 +0200 Subject: [PATCH 0411/1733] victoriatest --- ct/victoriametrics.sh | 66 +++++++++++++++++++++++++++ install/victoriametrics-install.sh | 73 ++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 ct/victoriametrics.sh create mode 100644 install/victoriametrics-install.sh diff --git a/ct/victoriametrics.sh b/ct/victoriametrics.sh new file mode 100644 index 000000000..bf66dfeea --- /dev/null +++ b/ct/victoriametrics.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/VictoriaMetrics/VictoriaMetrics + +APP="VictoriaMetrics" +var_tags="${var_tags:-database}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-16}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/victoriametrics ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + RELEASE=$(curl -fsSL https://api.github.com/repos/VictoriaMetrics/VictoriaMetrics/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') + if [[ ! -f ~/.victoriametrics ]] || [[ "${RELEASE}" != "$(cat ~/.victoriametrics)" ]]; then + msg_info "Stopping $APP" + systemctl stop victoriametrics + [[ -f /etc/systemd/system/victoriametrics-logs.service ]] && systemctl stop victoriametrics-logs + msg_ok "Stopped $APP" + + fetch_and_deploy_gh_release "victoriametrics" "VictoriaMetrics/VictoriaMetrics" "prebuild" "latest" "/opt/victoriametrics" "victoria-metrics-linux-amd64-v+([0-9.]).tar.gz" + fetch_and_deploy_gh_release "vmutils" "VictoriaMetrics/VictoriaMetrics" "prebuild" "latest" "/opt/victoriametrics" "vmutils-linux-amd64-v+([0-9.]).tar.gz" + + if [[ -f /etc/systemd/system/victoriametrics-logs.service ]]; then + fetch_and_deploy_gh_release "victorialogs" "VictoriaMetrics/VictoriaLogs" "prebuild" "latest" "/opt/victoriametrics" "victoria-logs-linux-amd64*.tar.gz" + fetch_and_deploy_gh_release "vlutils" "VictoriaMetrics/VictoriaLogs" "prebuild" "latest" "/opt/victoriametrics" "vlutils-linux-amd64*.tar.gz" + fi + chmod +x /opt/victoriametrics/* + + msg_info "Starting $APP" + systemctl start victoriametrics + [[ -f /etc/systemd/system/victoriametrics-logs.service ]] && systemctl start victoriametrics-logs + msg_ok "Started $APP" + + msg_ok "Updated Successfully" + else + msg_ok "No update required. ${APP} is already at ${RELEASE}" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8428/vmui${CL}" diff --git a/install/victoriametrics-install.sh b/install/victoriametrics-install.sh new file mode 100644 index 000000000..63843d3f9 --- /dev/null +++ b/install/victoriametrics-install.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/VictoriaMetrics/VictoriaMetrics + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +fetch_and_deploy_gh_release "victoriametrics" "VictoriaMetrics/VictoriaMetrics" "prebuild" "latest" "/opt/victoriametrics" "victoria-metrics-linux-amd64-v+([0-9.]).tar.gz" +fetch_and_deploy_gh_release "vmutils" "VictoriaMetrics/VictoriaMetrics" "prebuild" "latest" "/opt/victoriametrics" "vmutils-linux-amd64-v+([0-9.]).tar.gz" + +read -r -p "${TAB3}Would you like to add VictoriaLogs? " prompt + +if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then + fetch_and_deploy_gh_release "victorialogs" "VictoriaMetrics/VictoriaLogs" "prebuild" "latest" "/opt/victoriametrics" "victoria-logs-linux-amd64*.tar.gz" + fetch_and_deploy_gh_release "vlutils" "VictoriaMetrics/VictoriaLogs" "prebuild" "latest" "/opt/victoriametrics" "vlutils-linux-amd64*.tar.gz" +fi + +msg_info "Setup VictoriaMetrics" +mkdir -p /opt/victoriametrics/data +chmod +x /opt/victoriametrics/* +msg_ok "Setup VictoriaMetrics" + +msg_info "Creating Service" +cat </etc/systemd/system/victoriametrics.service +[Unit] +Description=VictoriaMetrics Service + +[Service] +Type=simple +Restart=always +User=root +WorkingDirectory=/opt/victoriametrics +ExecStart=/opt/victoriametrics/victoria-metrics-prod --storageDataPath="/opt/victoriametrics/data" + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now victoriametrics + +if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then + cat </etc/systemd/system/victoriametrics-logs.service +[Unit] +Description=VictoriaMetrics Service + +[Service] +Type=simple +Restart=always +User=root +WorkingDirectory=/opt/victoriametrics +ExecStart=/opt/victoriametrics/victoria-logs-prod + +[Install] +WantedBy=multi-user.target +EOF + systemctl enable -q --now victoriametrics-logs +fi +msg_ok "Created Service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 3728bdd2c05a6090cf962a588dbb011da4c678c5 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 6 Aug 2025 15:03:51 +0200 Subject: [PATCH 0412/1733] Update --- ct/victoriametrics.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/victoriametrics.sh b/ct/victoriametrics.sh index bf66dfeea..00e38edbf 100644 --- a/ct/victoriametrics.sh +++ b/ct/victoriametrics.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -27,7 +27,7 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - + RELEASE=$(curl -fsSL https://api.github.com/repos/VictoriaMetrics/VictoriaMetrics/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') if [[ ! -f ~/.victoriametrics ]] || [[ "${RELEASE}" != "$(cat ~/.victoriametrics)" ]]; then msg_info "Stopping $APP" From 2bcdb2aa08d123bf01931538cc46d33f996bd3a8 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 6 Aug 2025 13:26:07 +0000 Subject: [PATCH 0413/1733] Update .app files --- ct/headers/victoriametrics | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/victoriametrics diff --git a/ct/headers/victoriametrics b/ct/headers/victoriametrics new file mode 100644 index 000000000..5e705603d --- /dev/null +++ b/ct/headers/victoriametrics @@ -0,0 +1,6 @@ + _ ___ __ _ __ ___ __ _ +| | / (_)____/ /_____ _____(_)___ _/ |/ /__ / /______(_)_________ +| | / / / ___/ __/ __ \/ ___/ / __ `/ /|_/ / _ \/ __/ ___/ / ___/ ___/ +| |/ / / /__/ /_/ /_/ / / / / /_/ / / / / __/ /_/ / / / /__(__ ) +|___/_/\___/\__/\____/_/ /_/\__,_/_/ /_/\___/\__/_/ /_/\___/____/ + From 8487a578ddf6617f61630c68c97c5e7dde3c2bdc Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 6 Aug 2025 15:32:19 +0200 Subject: [PATCH 0414/1733] Update victoriametrics-install.sh --- install/victoriametrics-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/victoriametrics-install.sh b/install/victoriametrics-install.sh index 63843d3f9..6cfdf5d6f 100644 --- a/install/victoriametrics-install.sh +++ b/install/victoriametrics-install.sh @@ -13,7 +13,7 @@ setting_up_container network_check update_os -fetch_and_deploy_gh_release "victoriametrics" "VictoriaMetrics/VictoriaMetrics" "prebuild" "latest" "/opt/victoriametrics" "victoria-metrics-linux-amd64-v+([0-9.]).tar.gz" +fetch_and_deploy_gh_release "victoriametrics" "VictoriaMetrics/VictoriaMetrics" "prebuild" "latest" "/opt/victoriametrics" "victoria-metrics-linux-amd64-*.tar.gz" fetch_and_deploy_gh_release "vmutils" "VictoriaMetrics/VictoriaMetrics" "prebuild" "latest" "/opt/victoriametrics" "vmutils-linux-amd64-v+([0-9.]).tar.gz" read -r -p "${TAB3}Would you like to add VictoriaLogs? " prompt From 14f6395a85fead8fd434c79ac99dc72fe3680c1b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 6 Aug 2025 15:42:39 +0200 Subject: [PATCH 0415/1733] Update victoriametrics-install.sh --- install/victoriametrics-install.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/install/victoriametrics-install.sh b/install/victoriametrics-install.sh index 6cfdf5d6f..ce1a8fe79 100644 --- a/install/victoriametrics-install.sh +++ b/install/victoriametrics-install.sh @@ -13,7 +13,13 @@ setting_up_container network_check update_os -fetch_and_deploy_gh_release "victoriametrics" "VictoriaMetrics/VictoriaMetrics" "prebuild" "latest" "/opt/victoriametrics" "victoria-metrics-linux-amd64-*.tar.gz" +msg_info "Getting latest version of VictoriaMetrics" +asset_filename=$(curl -fsSL "https://api.github.com/repos/VictoriaMetrics/VictoriaMetrics/releases/latest" | + jq -r '.assets[].name' | + grep -E '^victoria-metrics-linux-amd64-v[0-9.]+\.tar\.gz$') +msg_ok "Got latest version of VictoriaMetrics" + +fetch_and_deploy_gh_release "victoriametrics" "VictoriaMetrics/VictoriaMetrics" "prebuild" "latest" "/opt/victoriametrics" "$asset_filename" fetch_and_deploy_gh_release "vmutils" "VictoriaMetrics/VictoriaMetrics" "prebuild" "latest" "/opt/victoriametrics" "vmutils-linux-amd64-v+([0-9.]).tar.gz" read -r -p "${TAB3}Would you like to add VictoriaLogs? " prompt From 4a39368a4f93d94b7c5578439aeedc87dec10585 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 6 Aug 2025 15:43:51 +0200 Subject: [PATCH 0416/1733] Update victoriametrics-install.sh --- install/victoriametrics-install.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/install/victoriametrics-install.sh b/install/victoriametrics-install.sh index ce1a8fe79..82a93ade8 100644 --- a/install/victoriametrics-install.sh +++ b/install/victoriametrics-install.sh @@ -14,13 +14,16 @@ network_check update_os msg_info "Getting latest version of VictoriaMetrics" -asset_filename=$(curl -fsSL "https://api.github.com/repos/VictoriaMetrics/VictoriaMetrics/releases/latest" | +victoriametrics_filename=$(curl -fsSL "https://api.github.com/repos/VictoriaMetrics/VictoriaMetrics/releases/latest" | jq -r '.assets[].name' | grep -E '^victoria-metrics-linux-amd64-v[0-9.]+\.tar\.gz$') +vmutils_filename=$(curl -fsSL "https://api.github.com/repos/VictoriaMetrics/VictoriaMetrics/releases/latest" | + jq -r '.assets[].name' | + grep -E '^vmutils-linux-amd64-v[0-9.]+\.tar\.gz$') msg_ok "Got latest version of VictoriaMetrics" -fetch_and_deploy_gh_release "victoriametrics" "VictoriaMetrics/VictoriaMetrics" "prebuild" "latest" "/opt/victoriametrics" "$asset_filename" -fetch_and_deploy_gh_release "vmutils" "VictoriaMetrics/VictoriaMetrics" "prebuild" "latest" "/opt/victoriametrics" "vmutils-linux-amd64-v+([0-9.]).tar.gz" +fetch_and_deploy_gh_release "victoriametrics" "VictoriaMetrics/VictoriaMetrics" "prebuild" "latest" "/opt/victoriametrics" "$victoriametrics_filename" +fetch_and_deploy_gh_release "vmutils" "VictoriaMetrics/VictoriaMetrics" "prebuild" "latest" "/opt/victoriametrics" "$vmutils_filename" read -r -p "${TAB3}Would you like to add VictoriaLogs? " prompt From af1d357aa17666aa85d51271d5daae62e339ae1d Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 7 Aug 2025 08:25:48 +0200 Subject: [PATCH 0417/1733] Update tracktor-install.sh --- install/tracktor-install.sh | 75 +++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/install/tracktor-install.sh b/install/tracktor-install.sh index 8b1378917..1deccea18 100644 --- a/install/tracktor-install.sh +++ b/install/tracktor-install.sh @@ -1 +1,76 @@ +#!/usr/bin/env bash +# Copyright (c) 2025 Community Scripts ORG +# Author: CrazyWolf13 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://tracktor.bytedge.in + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt-get install -y \ + sqlite3 \ + yq +msg_ok "Installed Dependencies" + +NODE_VERSION="20" setup_nodejs +fetch_and_deploy_gh_release "tududi" "chrisvel/tududi" + +msg_info "Configuring Tududi" +cd /opt/tududi +$STD npm install +export NODE_ENV=production +$STD npm run frontend:build +mv ./dist ./backend +mv ./public/locales ./backend/dist +mv ./public/favicon.* ./backend/dist +msg_ok "Configured Tududi" + +msg_info "Creating env and database" +DB_LOCATION="/opt/tududi-db" +UPLOAD_DIR="/opt/tududi-uploads" +mkdir -p {"$DB_LOCATION","$UPLOAD_DIR"} +SECRET="$(openssl rand -hex 64)" +sed -e 's/^GOOGLE/# &/' \ + -e '/TUDUDI_SESSION/s/^# //' \ + -e '/NODE_ENV/s/^# //' \ + -e "s/your_session_secret_here/$SECRET/" \ + -e 's/development/production/' \ + -e "\$a\DB_FILE=$DB_LOCATION/production.sqlite3" \ + -e "\$a\TUDUDI_UPLOAD_PATH=$UPLOAD_DIR" \ + /opt/tududi/backend/.env.example >/opt/tududi/backend/.env +export DB_FILE="$DB_LOCATION/production.sqlite3" +$STD npm run db:init +msg_ok "Created env and database" + +msg_info "Creating service" +cat </etc/systemd/system/tududi.service +[Unit] +Description=Tududi Service +After=network.target + +[Service] +Type=simple +WorkingDirectory=/opt/tududi +EnvironmentFile=/opt/tududi/backend/.env +ExecStart=/usr/bin/npm run start + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now tududi +msg_ok "Created service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 781d94dcab077ad7f2694727ade9de7c6ed69d84 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 7 Aug 2025 08:30:42 +0200 Subject: [PATCH 0418/1733] Update tracktor.sh --- ct/tracktor.sh | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/ct/tracktor.sh b/ct/tracktor.sh index 8b1378917..7cbaadce5 100644 --- a/ct/tracktor.sh +++ b/ct/tracktor.sh @@ -1 +1,62 @@ +#!/usr/bin/env bash +source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: CrazyWolf13 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://tracktor.bytedge.in/ +APP="tracktor" +var_tags="${var_tags:-car;monitoring}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-1024}" +var_disk="${var_disk:-5}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/tracktor ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + RELEASE=$(curl -fsSL https://api.github.com/repos/javedh-dev/tracktor/releases/latest | yq '.tag_name' | sed 's/^v//') + if [[ "${RELEASE}" != "$(cat ~/.tracktor 2>/dev/null)" ]] || [[ ! -f ~/.tracktor ]]; then + msg_info "Stopping Service" + systemctl stop tracktor + msg_ok "Stopped Service" + + msg_info "Updating ${APP}" + setup_nodejs + fetch_and_deploy_gh_release "tracktor" "javedh-dev/tracktor" + cd /opt/tracktor + $STD npm install + $STD npm run build + msg_ok "Updated $APP" + + msg_info "Starting Service" + systemctl start tracktor + msg_ok "Started Service" + msg_ok "Updated Successfully" + else + msg_ok "Already up to date" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" From d23b38e67275301d35feb7e9c6c5eba394062627 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 7 Aug 2025 08:32:58 +0200 Subject: [PATCH 0419/1733] Update post-pve-install.sh --- tools/pve/post-pve-install.sh | 42 +++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/tools/pve/post-pve-install.sh b/tools/pve/post-pve-install.sh index e61676c9f..b8ab2da1e 100644 --- a/tools/pve/post-pve-install.sh +++ b/tools/pve/post-pve-install.sh @@ -332,7 +332,40 @@ EOF esac fi - # ---- PVE-NO-SUBSCRIPTION ---- + # ---- CEPH-ENTERPRISE ---- + if grep -q "enterprise.proxmox.com.*ceph" /etc/apt/sources.list.d/*.sources 2>/dev/null; then + CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "CEPH-ENTERPRISE" \ + --menu "'ceph enterprise' repository already exists.\n\nWhat do you want to do?" 14 58 2 \ + "keep" "Keep as is" \ + "disable" "Comment out (disable) this repo" \ + "delete" "Delete this repo file" \ + 3>&2 2>&1 1>&3) + case $CHOICE in + keep) + msg_ok "Kept 'ceph enterprise' repository" + ;; + disable) + msg_info "Disabling (commenting) 'ceph enterprise' repository" + for file in /etc/apt/sources.list.d/*.sources; do + if grep -q "enterprise.proxmox.com.*ceph" "$file"; then + sed -i '/^\s*Types:/,/^$/s/^\([^#].*\)$/# \1/' "$file" + fi + done + msg_ok "Disabled 'ceph enterprise' repository" + ;; + delete) + msg_info "Deleting 'ceph enterprise' repository file" + for file in /etc/apt/sources.list.d/*.sources; do + if grep -q "enterprise.proxmox.com.*ceph" "$file"; then + rm -f "$file" + fi + done + msg_ok "Deleted 'ceph enterprise' repository file" + ;; + esac + fi + # ---- PVE-NO-SUBSCRIPTION ---- REPO_FILE="" REPO_ACTIVE=0 @@ -438,7 +471,12 @@ Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg EOF msg_ok "Added 'ceph package repositories'" ;; - no) msg_error "Selected no to Adding 'ceph package repositories'" ;; + no) + msg_error "Selected no to Adding 'ceph package repositories'" + find /etc/apt/sources.list.d/ -type f \( -name "*.sources" -o -name "*.list" \) \ + -exec sed -i '/enterprise.proxmox.com.*ceph/s/^/# /' {} \; + msg_ok "Disabled all Ceph Enterprise repositories" + ;; esac fi From e825dbea4384965b090129c92cb10e12cfe93417 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 7 Aug 2025 06:32:03 +0000 Subject: [PATCH 0420/1733] Update .app files --- ct/headers/tracktor | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/tracktor diff --git a/ct/headers/tracktor b/ct/headers/tracktor new file mode 100644 index 000000000..d4802c5aa --- /dev/null +++ b/ct/headers/tracktor @@ -0,0 +1,6 @@ + __ __ __ + / /__________ ______/ /__/ /_____ _____ + / __/ ___/ __ `/ ___/ //_/ __/ __ \/ ___/ +/ /_/ / / /_/ / /__/ ,< / /_/ /_/ / / +\__/_/ \__,_/\___/_/|_|\__/\____/_/ + From da516abeb305de20b983e1bd738ab2e22fe79045 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 7 Aug 2025 08:46:20 +0200 Subject: [PATCH 0421/1733] Update tracktor-install.sh --- install/tracktor-install.sh | 55 +++++++++++++------------------------ 1 file changed, 19 insertions(+), 36 deletions(-) diff --git a/install/tracktor-install.sh b/install/tracktor-install.sh index 1deccea18..c04865c68 100644 --- a/install/tracktor-install.sh +++ b/install/tracktor-install.sh @@ -13,58 +13,41 @@ setting_up_container network_check update_os -msg_info "Installing Dependencies" -$STD apt-get install -y \ - sqlite3 \ - yq -msg_ok "Installed Dependencies" +setup_nodejs +fetch_and_deploy_gh_release "tracktor" "javedh-dev/tracktor" -NODE_VERSION="20" setup_nodejs -fetch_and_deploy_gh_release "tududi" "chrisvel/tududi" -msg_info "Configuring Tududi" -cd /opt/tududi +msg_info "Configuring Tracktor" +cd /opt/tracktor $STD npm install -export NODE_ENV=production -$STD npm run frontend:build -mv ./dist ./backend -mv ./public/locales ./backend/dist -mv ./public/favicon.* ./backend/dist -msg_ok "Configured Tududi" +$STD npm run build +mkdir /opt/tracktor-data +cat </opt/tracktor.env +NODE_ENV=production +PUBLIC_DEMO_MODE=false +PUBLIC_API_BASE_URL=/ +DB_PATH=/opt/tracktor-data/vehicles.db -msg_info "Creating env and database" -DB_LOCATION="/opt/tududi-db" -UPLOAD_DIR="/opt/tududi-uploads" -mkdir -p {"$DB_LOCATION","$UPLOAD_DIR"} -SECRET="$(openssl rand -hex 64)" -sed -e 's/^GOOGLE/# &/' \ - -e '/TUDUDI_SESSION/s/^# //' \ - -e '/NODE_ENV/s/^# //' \ - -e "s/your_session_secret_here/$SECRET/" \ - -e 's/development/production/' \ - -e "\$a\DB_FILE=$DB_LOCATION/production.sqlite3" \ - -e "\$a\TUDUDI_UPLOAD_PATH=$UPLOAD_DIR" \ - /opt/tududi/backend/.env.example >/opt/tududi/backend/.env -export DB_FILE="$DB_LOCATION/production.sqlite3" -$STD npm run db:init -msg_ok "Created env and database" +EOF + +msg_ok "Configured Tracktor" msg_info "Creating service" -cat </etc/systemd/system/tududi.service +cat </etc/systemd/system/tracktor.service [Unit] -Description=Tududi Service +Description=Tracktor Service After=network.target [Service] Type=simple -WorkingDirectory=/opt/tududi -EnvironmentFile=/opt/tududi/backend/.env +WorkingDirectory=/opt/tracktor +EnvironmentFile=/opt/tracktor.env ExecStart=/usr/bin/npm run start [Install] WantedBy=multi-user.target EOF -systemctl enable -q --now tududi +systemctl enable -q --now tracktor msg_ok "Created service" motd_ssh From c02014a1628bd725749dc5c2c4cbeeab0818a57d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 7 Aug 2025 09:20:32 +0200 Subject: [PATCH 0422/1733] FSTrim Refactor --- tools/pve/clean-lxcs.sh | 1 + tools/pve/fstrim.sh | 98 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 tools/pve/fstrim.sh diff --git a/tools/pve/clean-lxcs.sh b/tools/pve/clean-lxcs.sh index 7050f3422..e6d06c229 100644 --- a/tools/pve/clean-lxcs.sh +++ b/tools/pve/clean-lxcs.sh @@ -1,6 +1,7 @@ #!/usr/bin/env bash # Copyright (c) 2021-2025 community-scripts ORG +# Author: tteck (tteckster) | MickLesk (CanbiZ) # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/tools/pve/fstrim.sh b/tools/pve/fstrim.sh new file mode 100644 index 000000000..97c5fe810 --- /dev/null +++ b/tools/pve/fstrim.sh @@ -0,0 +1,98 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE + +set -eEuo pipefail + +function header_info() { + clear + cat <<"EOF" + _______ __ __ ______ _ + / ____(_) /__ _______ _______/ /____ ____ ___ /_ __/____(_)___ ___ + / /_ / / / _ \/ ___/ / / / ___/ __/ _ \/ __ `__ \ / / / ___/ / __ `__ \ + / __/ / / / __(__ ) /_/ (__ ) /_/ __/ / / / / / / / / / / / / / / / / +/_/ /_/_/\___/____/\__, /____/\__/\___/_/ /_/ /_/ /_/ /_/ /_/_/ /_/ /_/ + /____/ +EOF +} + +BL="\033[36m" +RD="\033[01;31m" +CM='\xE2\x9C\x94\033' +GN="\033[1;92m" +CL="\033[m" + +header_info +echo "Loading..." + +ROOT_FS=$(df -Th "/" | awk 'NR==2 {print $2}') +if [ "$ROOT_FS" != "ext4" ]; then + whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "Warning" \ + --yesno "Root filesystem is not ext4 ($ROOT_FS).\nContinue anyway?" 10 58 || exit 1 +fi + +NODE=$(hostname) +EXCLUDE_MENU=() +MSG_MAX_LENGTH=0 + +while read -r TAG ITEM; do + OFFSET=2 + ((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=$((${#ITEM} + OFFSET)) + EXCLUDE_MENU+=("$TAG" "$ITEM " "OFF") +done < <(pct list | awk 'NR>1') + +excluded_containers_raw=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "Containers on $NODE" \ + --checklist "\nSelect containers to skip from trimming:\n" \ + 16 $((MSG_MAX_LENGTH + 23)) 6 "${EXCLUDE_MENU[@]}" 3>&1 1>&2 2>&3) + +[ $? -ne 0 ] && exit + +read -ra EXCLUDED <<<$(echo "$excluded_containers_raw" | tr -d '"') + +function trim_container() { + local container="$1" + header_info + echo -e "${BL}[Info]${GN} Trimming ${BL}$container${CL} \n" + + local before_trim after_trim + before_trim=$(lvs --noheadings -o lv_name,data_percent | awk -v ctid="vm-${container}-disk-0" '$1 == ctid {gsub(/%/, "", $2); print $2}') + echo -e "${RD}Data before trim $before_trim%${CL}" + + pct fstrim "$container" + + after_trim=$(lvs --noheadings -o lv_name,data_percent | awk -v ctid="vm-${container}-disk-0" '$1 == ctid {gsub(/%/, "", $2); print $2}') + echo -e "${GN}Data after trim $after_trim%${CL}" + + sleep 0.5 +} + +for container in $(pct list | awk 'NR>1 {print $1}'); do + if [[ " ${EXCLUDED[*]} " =~ " $container " ]]; then + header_info + echo -e "${BL}[Info]${GN} Skipping ${BL}$container${CL}" + sleep 0.5 + continue + fi + if pct config "$container" | grep -q "template:"; then + header_info + echo -e "${BL}[Info]${GN} Skipping ${container} ${RD}$container is a template${CL} \n" + sleep 0.5 + continue + fi + state=$(pct status "$container" | awk '{print $2}') + if [[ "$state" != "running" ]]; then + header_info + echo -e "${BL}[Info]${GN} Skipping $container (not running)${CL}" + sleep 0.5 + continue + fi + trim_container "$container" +done + +header_info +echo -e "${GN}Finished, LXC Containers Trimmed.${CL} \n" +exit 0 From 6ec661157e177dc095d290eeda79941496c2bce3 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 7 Aug 2025 09:49:53 +0200 Subject: [PATCH 0423/1733] Update fstrim.sh --- tools/pve/fstrim.sh | 74 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 64 insertions(+), 10 deletions(-) diff --git a/tools/pve/fstrim.sh b/tools/pve/fstrim.sh index 97c5fe810..1fa5929cc 100644 --- a/tools/pve/fstrim.sh +++ b/tools/pve/fstrim.sh @@ -12,7 +12,7 @@ function header_info() { _______ __ __ ______ _ / ____(_) /__ _______ _______/ /____ ____ ___ /_ __/____(_)___ ___ / /_ / / / _ \/ ___/ / / / ___/ __/ _ \/ __ `__ \ / / / ___/ / __ `__ \ - / __/ / / / __(__ ) /_/ (__ ) /_/ __/ / / / / / / / / / / / / / / / / + / __/ / / __(__ ) /_/ (__ ) /_/ __/ / / / / / / / / / / / / / / / / / /_/ /_/_/\___/____/\__, /____/\__/\___/_/ /_/ /_/ /_/ /_/ /_/_/ /_/ /_/ /____/ EOF @@ -27,32 +27,62 @@ CL="\033[m" header_info echo "Loading..." +whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "About fstrim (LXC)" \ + --msgbox "The 'fstrim' command releases unused blocks back to the storage device. This only makes sense for containers on SSD, NVMe, Thin-LVM, or storage with discard/TRIM support.\n\nIf your root filesystem or container disks are on classic HDDs, thick LVM, or unsupported storage types, running fstrim will have no effect.\n\nRecommended:\n- Use fstrim only on SSD, NVMe, or thin-provisioned storage with discard enabled.\n- For ZFS, ensure 'autotrim=on' is set on your pool.\n\nSee: https://pve.proxmox.com/wiki/Shrinking_LXC_disks" 16 88 + ROOT_FS=$(df -Th "/" | awk 'NR==2 {print $2}') if [ "$ROOT_FS" != "ext4" ]; then whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "Warning" \ - --yesno "Root filesystem is not ext4 ($ROOT_FS).\nContinue anyway?" 10 58 || exit 1 + --yesno "Root filesystem is not ext4 ($ROOT_FS).\nContinue anyway?" 12 80 || exit 1 fi NODE=$(hostname) EXCLUDE_MENU=() MSG_MAX_LENGTH=0 +# Build EXCLUDE_MENU for main checklist while read -r TAG ITEM; do OFFSET=2 ((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=$((${#ITEM} + OFFSET)) EXCLUDE_MENU+=("$TAG" "$ITEM " "OFF") -done < <(pct list | awk 'NR>1') +done < <(pct list | awk 'NR>1 {print $1, $2}') excluded_containers_raw=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "Containers on $NODE" \ --checklist "\nSelect containers to skip from trimming:\n" \ - 16 $((MSG_MAX_LENGTH + 23)) 6 "${EXCLUDE_MENU[@]}" 3>&1 1>&2 2>&3) + 20 $((MSG_MAX_LENGTH + 32)) 10 "${EXCLUDE_MENU[@]}" 3>&1 1>&2 2>&3) [ $? -ne 0 ] && exit read -ra EXCLUDED <<<$(echo "$excluded_containers_raw" | tr -d '"') +# Find all stopped containers +STOPPED_MENU=() +for container in $(pct list | awk 'NR>1 {print $1}'); do + state=$(pct status "$container" | awk '{print $2}') + if [[ "$state" != "running" ]]; then + CTNAME=$(pct config "$container" | awk -F ': ' '/^hostname:/ {print $2}') + STOPPED_MENU+=("$container" "$CTNAME" "OFF") + fi +done + +TO_START=() +if [ ${#STOPPED_MENU[@]} -gt 0 ]; then + selected=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "Stopped LXC Containers" \ + --checklist "\nSome LXC containers are currently stopped.\nWhich ones do you want to temporarily start for the trim operation?\n(They will be stopped again afterwards)\n" \ + 20 80 10 "${STOPPED_MENU[@]}" 3>&1 1>&2 2>&3) + [ $? -ne 0 ] && exit + read -ra TO_START <<<$(echo "$selected" | tr -d '"') +fi + +declare -A WAS_STOPPED +for ct in "${TO_START[@]}"; do + WAS_STOPPED["$ct"]=1 +done + function trim_container() { local container="$1" header_info @@ -73,24 +103,48 @@ function trim_container() { for container in $(pct list | awk 'NR>1 {print $1}'); do if [[ " ${EXCLUDED[*]} " =~ " $container " ]]; then header_info - echo -e "${BL}[Info]${GN} Skipping ${BL}$container${CL}" + echo -e "${BL}[Info]${GN} Skipping $container (excluded)${CL}" sleep 0.5 continue fi if pct config "$container" | grep -q "template:"; then header_info - echo -e "${BL}[Info]${GN} Skipping ${container} ${RD}$container is a template${CL} \n" + echo -e "${BL}[Info]${GN} Skipping $container (template)${CL}\n" sleep 0.5 continue fi state=$(pct status "$container" | awk '{print $2}') if [[ "$state" != "running" ]]; then - header_info - echo -e "${BL}[Info]${GN} Skipping $container (not running)${CL}" - sleep 0.5 - continue + if [[ -n "${WAS_STOPPED[$container]:-}" ]]; then + header_info + echo -e "${BL}[Info]${GN} Starting $container for trim...${CL}" + pct start "$container" + sleep 2 + else + header_info + echo -e "${BL}[Info]${GN} Skipping $container (not running, not selected)${CL}" + sleep 0.5 + continue + fi fi + trim_container "$container" + + # Optionally stop container again if we started it + if [[ -n "${WAS_STOPPED[$container]:-}" ]]; then + if whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "Stop container again?" \ + --yesno "Container $container was started for the trim operation.\n\nDo you want to stop it again now?" 10 60; then + header_info + echo -e "${BL}[Info]${GN} Stopping $container again...${CL}" + pct stop "$container" + sleep 1 + else + header_info + echo -e "${BL}[Info]${GN} Leaving $container running as requested.${CL}" + sleep 1 + fi + fi done header_info From 0a601a5a1615f1087013c8c085ba5ecfd735347e Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 7 Aug 2025 09:36:06 +0200 Subject: [PATCH 0424/1733] Update tracktor-install.sh --- install/tracktor-install.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/install/tracktor-install.sh b/install/tracktor-install.sh index c04865c68..3b23e5a31 100644 --- a/install/tracktor-install.sh +++ b/install/tracktor-install.sh @@ -27,9 +27,8 @@ NODE_ENV=production PUBLIC_DEMO_MODE=false PUBLIC_API_BASE_URL=/ DB_PATH=/opt/tracktor-data/vehicles.db - +PORT=3000 EOF - msg_ok "Configured Tracktor" msg_info "Creating service" From 81bd98f8aa0bc7e631a11aa052b0c06a14d4c4a9 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 7 Aug 2025 09:45:43 +0200 Subject: [PATCH 0425/1733] Update tracktor.json --- frontend/public/json/tracktor.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/tracktor.json b/frontend/public/json/tracktor.json index fa6319c6a..1f5ea1a66 100644 --- a/frontend/public/json/tracktor.json +++ b/frontend/public/json/tracktor.json @@ -8,7 +8,7 @@ "type": "ct", "updateable": true, "privileged": false, - "interface_port": 5173, + "interface_port": 3000, "documentation": "https://tracktor.bytedge.in/introduction.html", "config_path": "/etc/system/systemd/tracktor.service", "website": "https://tracktor.bytedge.in/", From d9d0780530f55ad653eb24f96675f2168eeaf13e Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 7 Aug 2025 10:12:14 +0200 Subject: [PATCH 0426/1733] Update tracktor-install.sh --- install/tracktor-install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/install/tracktor-install.sh b/install/tracktor-install.sh index 3b23e5a31..87526c3cd 100644 --- a/install/tracktor-install.sh +++ b/install/tracktor-install.sh @@ -19,6 +19,7 @@ fetch_and_deploy_gh_release "tracktor" "javedh-dev/tracktor" msg_info "Configuring Tracktor" cd /opt/tracktor +export NODE_ENV=production $STD npm install $STD npm run build mkdir /opt/tracktor-data @@ -41,7 +42,7 @@ After=network.target Type=simple WorkingDirectory=/opt/tracktor EnvironmentFile=/opt/tracktor.env -ExecStart=/usr/bin/npm run start +ExecStart=/usr/bin/npm start [Install] WantedBy=multi-user.target From 0af0621421dfd7856829f21a103a383f06eed774 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 7 Aug 2025 10:12:27 +0200 Subject: [PATCH 0427/1733] Update tracktor.sh --- ct/tracktor.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ct/tracktor.sh b/ct/tracktor.sh index 7cbaadce5..1c09bd6e1 100644 --- a/ct/tracktor.sh +++ b/ct/tracktor.sh @@ -38,6 +38,7 @@ function update_script() { setup_nodejs fetch_and_deploy_gh_release "tracktor" "javedh-dev/tracktor" cd /opt/tracktor + export NODE_ENV=production $STD npm install $STD npm run build msg_ok "Updated $APP" From 14f213ea19fbfd1dc7473a9e6b8211110db2e902 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 7 Aug 2025 13:14:01 +0200 Subject: [PATCH 0428/1733] finalize tandoor --- ct/tandoor.sh | 102 ++++++++++++++++++------------------- install/tandoor-install.sh | 7 ++- 2 files changed, 53 insertions(+), 56 deletions(-) diff --git a/ct/tandoor.sh b/ct/tandoor.sh index 248df63d5..8b7b874d7 100644 --- a/ct/tandoor.sh +++ b/ct/tandoor.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2025 community-scripts ORG # Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://tandoor.dev/ @@ -20,68 +20,66 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/tandoor ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/tandoor ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi - if [[ ! -f ~/.tandoor ]]; then - msg_error "v1 Installation found, please create an new LXC!" - exit - fi + if [[ ! -f ~/.tandoor ]]; then + msg_error "v1 Installation found, please export your data and create an new LXC." + exit + fi - RELEASE=$(curl -fsSL https://api.github.com/repos/TandoorRecipes/recipes/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - if [[ "${RELEASE}" != "$(cat ~/.tandoor 2>/dev/null)" ]] || [[ ! -f ~/.tandoor ]]; then - msg_info "Stopping $APP" - systemctl stop tandoor - msg_ok "Stopped $APP" + RELEASE=$(curl -fsSL https://api.github.com/repos/TandoorRecipes/recipes/releases/latest | jq -r '.tag_name' | sed 's/^v//') + if [[ "${RELEASE}" != "$(cat ~/.tandoor 2>/dev/null)" ]] || [[ ! -f ~/.tandoor ]]; then + msg_info "Stopping $APP" + systemctl stop tandoor + msg_ok "Stopped $APP" - msg_info "Creating Backup" - BACKUP_FILE="/opt/tandoor_backup_$(date +%F).tar.gz" - $STD tar -czf "$BACKUP_FILE" /opt/tandoor/{.env,start.sh} /opt/tandoor/database/ &>/dev/null - mv /opt/tandoor/.env /opt/.env - msg_ok "Backup Created" + msg_info "Creating Backup" + mv /opt/tandoor /opt/tandoor.bak + msg_ok "Backup Created" - NODE_VERSION="20" NODE_MODULE="yarn@latest" setup_nodejs - fetch_and_deploy_gh_release "tandoor" "TandoorRecipes/recipes" "tarball" "latest" "/opt/tandoor" - PYTHON_VERSION="3.13" setup_uv + NODE_VERSION="20" NODE_MODULE="yarn" setup_nodejs + PYTHON_VERSION="3.13" setup_uv + fetch_and_deploy_gh_release "tandoor" "TandoorRecipes/recipes" "tarball" "latest" "/opt/tandoor" - msg_info "Updating $APP to ${RELEASE}" - mkdir -p /opt/tandoor/{config,api,mediafiles,staticfiles} - mv /opt/.env /opt/tandoor/.env - cd /opt/tandoor - $STD uv venv .venv --python=python3 - $STD uv pip install -r requirements.txt --python .venv/bin/python - cd /opt/tandoor/vue3 - $STD yarn install - $STD yarn build - TANDOOR_VERSION="$(curl -fsSL https://api.github.com/repos/TandoorRecipes/recipes/releases/latest | jq -r .tag_name)" - cat </opt/tandoor/cookbook/version_info.py + msg_info "Updating $APP to ${RELEASE}" + cp -r /opt/tandoor.bak/{config,api,mediafiles,staticfiles} /opt/tandoor/ + mv /opt/.env /opt/tandoor/.env + cd /opt/tandoor + $STD uv venv .venv --python=python3 + $STD uv pip install -r requirements.txt --python .venv/bin/python + cd /opt/tandoor/vue3 + $STD yarn install + $STD yarn build + TANDOOR_VERSION="$(curl -fsSL https://api.github.com/repos/TandoorRecipes/recipes/releases/latest | jq -r .tag_name)" + cat </opt/tandoor/cookbook/version_info.py TANDOOR_VERSION = "$TANDOOR_VERSION" TANDOOR_REF = "bare-metal" VERSION_INFO = [] EOF - cd /opt/tandoor - $STD /opt/tandoor/.venv/bin/python manage.py migrate - $STD /opt/tandoor/.venv/bin/python manage.py collectstatic --no-input - msg_ok "Updated $APP to ${RELEASE}" + cd /opt/tandoor + $STD /opt/tandoor/.venv/bin/python manage.py migrate + $STD /opt/tandoor/.venv/bin/python manage.py collectstatic --no-input + msg_ok "Updated $APP to ${RELEASE}" - msg_info "Starting $APP" - systemctl start tandoor - systemctl reload nginx - msg_ok "Started $APP" + msg_info "Starting $APP" + systemctl start tandoor + systemctl reload nginx + msg_ok "Started $APP" - msg_info "Cleaning Up" - rm -rf "$BACKUP_FILE" - msg_ok "Cleanup Completed" - msg_ok "Update Successful" - else - msg_ok "No update required. ${APP} is already at v${RELEASE}" - fi - exit + msg_info "Cleaning Up" + rm -rf /opt/tandoor.bak + msg_ok "Cleanup Completed" + msg_ok "Update Successful" + else + msg_ok "No update required. ${APP} is already at v${RELEASE}" + fi + exit } start diff --git a/install/tandoor-install.sh b/install/tandoor-install.sh index 823a60d17..a70621a8b 100644 --- a/install/tandoor-install.sh +++ b/install/tandoor-install.sh @@ -1,8 +1,7 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck -# Author: tteck -# Co-Author: MickLesk (Canbiz) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: MickLesk (Canbiz) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://tandoor.dev/ @@ -31,7 +30,7 @@ $STD apt-get install -y --no-install-recommends \ libxmlsec1-openssl msg_ok "Installed Dependencies" -NODE_VERSION="20" NODE_MODULE="yarn@latest" setup_nodejs +NODE_VERSION="20" NODE_MODULE="yarn" setup_nodejs fetch_and_deploy_gh_release "tandoor" "TandoorRecipes/recipes" "tarball" "latest" "/opt/tandoor" PG_VERSION="16" setup_postgresql PYTHON_VERSION="3.13" setup_uv From c6066d2755b49393d88c2d3833a0efa544a77956 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 7 Aug 2025 13:19:46 +0200 Subject: [PATCH 0429/1733] Update fstrim.sh --- tools/pve/fstrim.sh | 85 ++++++++++++++++++++++----------------------- 1 file changed, 42 insertions(+), 43 deletions(-) diff --git a/tools/pve/fstrim.sh b/tools/pve/fstrim.sh index 1fa5929cc..0d53292b7 100644 --- a/tools/pve/fstrim.sh +++ b/tools/pve/fstrim.sh @@ -1,7 +1,6 @@ #!/usr/bin/env bash # Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE set -eEuo pipefail @@ -12,7 +11,7 @@ function header_info() { _______ __ __ ______ _ / ____(_) /__ _______ _______/ /____ ____ ___ /_ __/____(_)___ ___ / /_ / / / _ \/ ___/ / / / ___/ __/ _ \/ __ `__ \ / / / ___/ / __ `__ \ - / __/ / / __(__ ) /_/ (__ ) /_/ __/ / / / / / / / / / / / / / / / / / + / __/ / / / __(__ ) /_/ (__ ) /_/ __/ / / / / / / / / / / / / / / / / /_/ /_/_/\___/____/\__, /____/\__/\___/_/ /_/ /_/ /_/ /_/ /_/_/ /_/ /_/ /____/ EOF @@ -20,7 +19,6 @@ EOF BL="\033[36m" RD="\033[01;31m" -CM='\xE2\x9C\x94\033' GN="\033[1;92m" CL="\033[m" @@ -39,32 +37,39 @@ if [ "$ROOT_FS" != "ext4" ]; then fi NODE=$(hostname) -EXCLUDE_MENU=() -MSG_MAX_LENGTH=0 +MAX_NAME_LEN=0 +declare -A NAMES +declare -A STATUS -# Build EXCLUDE_MENU for main checklist -while read -r TAG ITEM; do - OFFSET=2 - ((${#ITEM} + OFFSET > MSG_MAX_LENGTH)) && MSG_MAX_LENGTH=$((${#ITEM} + OFFSET)) - EXCLUDE_MENU+=("$TAG" "$ITEM " "OFF") -done < <(pct list | awk 'NR>1 {print $1, $2}') +for CTID in $(pct list | awk 'NR>1 {print $1}'); do + NAME=$(pct config "$CTID" | awk -F': ' '/^hostname:/ {print $2}') + CTSTATUS=$(pct status "$CTID" | awk '{print $2}') + NAMES["$CTID"]="$NAME" + STATUS["$CTID"]="$CTSTATUS" + ((${#NAME} > MAX_NAME_LEN)) && MAX_NAME_LEN=${#NAME} +done + +FMT="%-5s | %-${MAX_NAME_LEN}s | %-8s" + +EXCLUDE_MENU=() +for CTID in "${!NAMES[@]}"; do + DESC=$(printf "$FMT" "$CTID" "${NAMES[$CTID]}" "${STATUS[$CTID]}") + EXCLUDE_MENU+=("$CTID" "$DESC" "OFF") +done excluded_containers_raw=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "Containers on $NODE" \ --checklist "\nSelect containers to skip from trimming:\n" \ - 20 $((MSG_MAX_LENGTH + 32)) 10 "${EXCLUDE_MENU[@]}" 3>&1 1>&2 2>&3) - + 20 $((MAX_NAME_LEN + 40)) 12 "${EXCLUDE_MENU[@]}" 3>&1 1>&2 2>&3) [ $? -ne 0 ] && exit read -ra EXCLUDED <<<$(echo "$excluded_containers_raw" | tr -d '"') -# Find all stopped containers STOPPED_MENU=() -for container in $(pct list | awk 'NR>1 {print $1}'); do - state=$(pct status "$container" | awk '{print $2}') - if [[ "$state" != "running" ]]; then - CTNAME=$(pct config "$container" | awk -F ': ' '/^hostname:/ {print $2}') - STOPPED_MENU+=("$container" "$CTNAME" "OFF") +for CTID in "${!NAMES[@]}"; do + if [[ "${STATUS[$CTID]}" == "stopped" ]]; then + DESC=$(printf "$FMT" "$CTID" "${NAMES[$CTID]}" "${STATUS[$CTID]}") + STOPPED_MENU+=("$CTID" "$DESC" "OFF") fi done @@ -72,8 +77,8 @@ TO_START=() if [ ${#STOPPED_MENU[@]} -gt 0 ]; then selected=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "Stopped LXC Containers" \ - --checklist "\nSome LXC containers are currently stopped.\nWhich ones do you want to temporarily start for the trim operation?\n(They will be stopped again afterwards)\n" \ - 20 80 10 "${STOPPED_MENU[@]}" 3>&1 1>&2 2>&3) + --checklist "\nSome LXC containers are currently stopped.\nWhich ones do you want to temporarily start for the trim operation?\n(They can be stopped again afterwards)\n" \ + 16 $((MAX_NAME_LEN + 40)) 8 "${STOPPED_MENU[@]}" 3>&1 1>&2 2>&3) [ $? -ne 0 ] && exit read -ra TO_START <<<$(echo "$selected" | tr -d '"') fi @@ -87,61 +92,55 @@ function trim_container() { local container="$1" header_info echo -e "${BL}[Info]${GN} Trimming ${BL}$container${CL} \n" - local before_trim after_trim before_trim=$(lvs --noheadings -o lv_name,data_percent | awk -v ctid="vm-${container}-disk-0" '$1 == ctid {gsub(/%/, "", $2); print $2}') echo -e "${RD}Data before trim $before_trim%${CL}" - pct fstrim "$container" - after_trim=$(lvs --noheadings -o lv_name,data_percent | awk -v ctid="vm-${container}-disk-0" '$1 == ctid {gsub(/%/, "", $2); print $2}') echo -e "${GN}Data after trim $after_trim%${CL}" - sleep 0.5 } -for container in $(pct list | awk 'NR>1 {print $1}'); do - if [[ " ${EXCLUDED[*]} " =~ " $container " ]]; then +for CTID in $(pct list | awk 'NR>1 {print $1}'); do + if [[ " ${EXCLUDED[*]} " =~ " $CTID " ]]; then header_info - echo -e "${BL}[Info]${GN} Skipping $container (excluded)${CL}" + echo -e "${BL}[Info]${GN} Skipping $CTID (excluded)${CL}" sleep 0.5 continue fi - if pct config "$container" | grep -q "template:"; then + if pct config "$CTID" | grep -q "template:"; then header_info - echo -e "${BL}[Info]${GN} Skipping $container (template)${CL}\n" + echo -e "${BL}[Info]${GN} Skipping $CTID (template)${CL}\n" sleep 0.5 continue fi - state=$(pct status "$container" | awk '{print $2}') - if [[ "$state" != "running" ]]; then - if [[ -n "${WAS_STOPPED[$container]:-}" ]]; then + if [[ "${STATUS[$CTID]}" != "running" ]]; then + if [[ -n "${WAS_STOPPED[$CTID]:-}" ]]; then header_info - echo -e "${BL}[Info]${GN} Starting $container for trim...${CL}" - pct start "$container" + echo -e "${BL}[Info]${GN} Starting $CTID for trim...${CL}" + pct start "$CTID" sleep 2 else header_info - echo -e "${BL}[Info]${GN} Skipping $container (not running, not selected)${CL}" + echo -e "${BL}[Info]${GN} Skipping $CTID (not running, not selected)${CL}" sleep 0.5 continue fi fi - trim_container "$container" + trim_container "$CTID" - # Optionally stop container again if we started it - if [[ -n "${WAS_STOPPED[$container]:-}" ]]; then + if [[ -n "${WAS_STOPPED[$CTID]:-}" ]]; then if whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "Stop container again?" \ - --yesno "Container $container was started for the trim operation.\n\nDo you want to stop it again now?" 10 60; then + --yesno "Container $CTID (${NAMES[$CTID]}) was started for the trim operation.\n\nDo you want to stop it again now?" 10 60; then header_info - echo -e "${BL}[Info]${GN} Stopping $container again...${CL}" - pct stop "$container" + echo -e "${BL}[Info]${GN} Stopping $CTID again...${CL}" + pct stop "$CTID" sleep 1 else header_info - echo -e "${BL}[Info]${GN} Leaving $container running as requested.${CL}" + echo -e "${BL}[Info]${GN} Leaving $CTID running as requested.${CL}" sleep 1 fi fi From 8496f217e3ab0f37b75996c9c9e2863fe896b3e2 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 7 Aug 2025 13:27:47 +0200 Subject: [PATCH 0430/1733] Update fstrim.sh --- tools/pve/fstrim.sh | 71 ++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 42 deletions(-) diff --git a/tools/pve/fstrim.sh b/tools/pve/fstrim.sh index 0d53292b7..55094acf9 100644 --- a/tools/pve/fstrim.sh +++ b/tools/pve/fstrim.sh @@ -1,8 +1,5 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE - set -eEuo pipefail function header_info() { @@ -10,8 +7,8 @@ function header_info() { cat <<"EOF" _______ __ __ ______ _ / ____(_) /__ _______ _______/ /____ ____ ___ /_ __/____(_)___ ___ - / /_ / / / _ \/ ___/ / / / ___/ __/ _ \/ __ `__ \ / / / ___/ / __ `__ \ - / __/ / / / __(__ ) /_/ (__ ) /_/ __/ / / / / / / / / / / / / / / / / + / /_ / / / _ \/ ___/ / / / ___/ __/ _ \/ __ `__ \ / / / ___/ / / __ `__ \ + / __/ / / __(__ ) /_/ (__ ) /_/ __/ / / / / / / / / / / / / / / / / / / /_/ /_/_/\___/____/\__, /____/\__/\___/_/ /_/ /_/ /_/ /_/ /_/_/ /_/ /_/ /____/ EOF @@ -37,42 +34,31 @@ if [ "$ROOT_FS" != "ext4" ]; then fi NODE=$(hostname) +EXCLUDE_MENU=() +STOPPED_MENU=() MAX_NAME_LEN=0 -declare -A NAMES -declare -A STATUS -for CTID in $(pct list | awk 'NR>1 {print $1}'); do - NAME=$(pct config "$CTID" | awk -F': ' '/^hostname:/ {print $2}') - CTSTATUS=$(pct status "$CTID" | awk '{print $2}') - NAMES["$CTID"]="$NAME" - STATUS["$CTID"]="$CTSTATUS" +while read -r CTID STATUS _ NAME _; do ((${#NAME} > MAX_NAME_LEN)) && MAX_NAME_LEN=${#NAME} -done +done < <(pct list | awk 'NR>1') FMT="%-5s | %-${MAX_NAME_LEN}s | %-8s" -EXCLUDE_MENU=() -for CTID in "${!NAMES[@]}"; do - DESC=$(printf "$FMT" "$CTID" "${NAMES[$CTID]}" "${STATUS[$CTID]}") +while read -r CTID STATUS _ NAME _; do + DESC=$(printf "$FMT" "$CTID" "$NAME" "$STATUS") EXCLUDE_MENU+=("$CTID" "$DESC" "OFF") -done + if [[ "$STATUS" == "stopped" ]]; then + STOPPED_MENU+=("$CTID" "$DESC" "OFF") + fi +done < <(pct list | awk 'NR>1') excluded_containers_raw=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "Containers on $NODE" \ --checklist "\nSelect containers to skip from trimming:\n" \ 20 $((MAX_NAME_LEN + 40)) 12 "${EXCLUDE_MENU[@]}" 3>&1 1>&2 2>&3) [ $? -ne 0 ] && exit - read -ra EXCLUDED <<<$(echo "$excluded_containers_raw" | tr -d '"') -STOPPED_MENU=() -for CTID in "${!NAMES[@]}"; do - if [[ "${STATUS[$CTID]}" == "stopped" ]]; then - DESC=$(printf "$FMT" "$CTID" "${NAMES[$CTID]}" "${STATUS[$CTID]}") - STOPPED_MENU+=("$CTID" "$DESC" "OFF") - fi -done - TO_START=() if [ ${#STOPPED_MENU[@]} -gt 0 ]; then selected=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ @@ -101,46 +87,47 @@ function trim_container() { sleep 0.5 } -for CTID in $(pct list | awk 'NR>1 {print $1}'); do - if [[ " ${EXCLUDED[*]} " =~ " $CTID " ]]; then +for container in $(pct list | awk 'NR>1 {print $1}'); do + if [[ " ${EXCLUDED[*]} " =~ " $container " ]]; then header_info - echo -e "${BL}[Info]${GN} Skipping $CTID (excluded)${CL}" + echo -e "${BL}[Info]${GN} Skipping $container (excluded)${CL}" sleep 0.5 continue fi - if pct config "$CTID" | grep -q "template:"; then + if pct config "$container" | grep -q "template:"; then header_info - echo -e "${BL}[Info]${GN} Skipping $CTID (template)${CL}\n" + echo -e "${BL}[Info]${GN} Skipping $container (template)${CL}\n" sleep 0.5 continue fi - if [[ "${STATUS[$CTID]}" != "running" ]]; then - if [[ -n "${WAS_STOPPED[$CTID]:-}" ]]; then + STATUS=$(pct list | awk -v id="$container" '$1==id{print $2}') + if [[ "$STATUS" != "running" ]]; then + if [[ -n "${WAS_STOPPED[$container]:-}" ]]; then header_info - echo -e "${BL}[Info]${GN} Starting $CTID for trim...${CL}" - pct start "$CTID" + echo -e "${BL}[Info]${GN} Starting $container for trim...${CL}" + pct start "$container" sleep 2 else header_info - echo -e "${BL}[Info]${GN} Skipping $CTID (not running, not selected)${CL}" + echo -e "${BL}[Info]${GN} Skipping $container (not running, not selected)${CL}" sleep 0.5 continue fi fi - trim_container "$CTID" + trim_container "$container" - if [[ -n "${WAS_STOPPED[$CTID]:-}" ]]; then + if [[ -n "${WAS_STOPPED[$container]:-}" ]]; then if whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "Stop container again?" \ - --yesno "Container $CTID (${NAMES[$CTID]}) was started for the trim operation.\n\nDo you want to stop it again now?" 10 60; then + --yesno "Container $container was started for the trim operation.\n\nDo you want to stop it again now?" 10 60; then header_info - echo -e "${BL}[Info]${GN} Stopping $CTID again...${CL}" - pct stop "$CTID" + echo -e "${BL}[Info]${GN} Stopping $container again...${CL}" + pct stop "$container" sleep 1 else header_info - echo -e "${BL}[Info]${GN} Leaving $CTID running as requested.${CL}" + echo -e "${BL}[Info]${GN} Leaving $container running as requested.${CL}" sleep 1 fi fi From 41008b09fa821f24e61b5ea387706cda6b2daf71 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 7 Aug 2025 13:29:24 +0200 Subject: [PATCH 0431/1733] Update fstrim.sh --- tools/pve/fstrim.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/pve/fstrim.sh b/tools/pve/fstrim.sh index 55094acf9..82c7378ee 100644 --- a/tools/pve/fstrim.sh +++ b/tools/pve/fstrim.sh @@ -24,7 +24,7 @@ echo "Loading..." whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "About fstrim (LXC)" \ - --msgbox "The 'fstrim' command releases unused blocks back to the storage device. This only makes sense for containers on SSD, NVMe, Thin-LVM, or storage with discard/TRIM support.\n\nIf your root filesystem or container disks are on classic HDDs, thick LVM, or unsupported storage types, running fstrim will have no effect.\n\nRecommended:\n- Use fstrim only on SSD, NVMe, or thin-provisioned storage with discard enabled.\n- For ZFS, ensure 'autotrim=on' is set on your pool.\n\nSee: https://pve.proxmox.com/wiki/Shrinking_LXC_disks" 16 88 + --msgbox "The 'fstrim' command releases unused blocks back to the storage device. This only makes sense for containers on SSD, NVMe, Thin-LVM, or storage with discard/TRIM support.\n\nIf your root filesystem or container disks are on classic HDDs, thick LVM, or unsupported storage types, running fstrim will have no effect.\n\nRecommended:\n- Use fstrim only on SSD, NVMe, or thin-provisioned storage with discard enabled.\n- For ZFS, ensure 'autotrim=on' is set on your pool.\n" 16 88 ROOT_FS=$(df -Th "/" | awk 'NR==2 {print $2}') if [ "$ROOT_FS" != "ext4" ]; then From 453ac9f24e34e7dedaff1bbf13ff683813c819e8 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 7 Aug 2025 13:38:00 +0200 Subject: [PATCH 0432/1733] Update fstrim.sh --- tools/pve/fstrim.sh | 81 +++++++++++++++++++++++++++------------------ 1 file changed, 48 insertions(+), 33 deletions(-) diff --git a/tools/pve/fstrim.sh b/tools/pve/fstrim.sh index 82c7378ee..679d59a19 100644 --- a/tools/pve/fstrim.sh +++ b/tools/pve/fstrim.sh @@ -7,8 +7,7 @@ function header_info() { cat <<"EOF" _______ __ __ ______ _ / ____(_) /__ _______ _______/ /____ ____ ___ /_ __/____(_)___ ___ - / /_ / / / _ \/ ___/ / / / ___/ __/ _ \/ __ `__ \ / / / ___/ / / __ `__ \ - / __/ / / __(__ ) /_/ (__ ) /_/ __/ / / / / / / / / / / / / / / / / / / + / /_ / / / _ \/ ___/ / / / ___/ __/ _ \/ __ `__ \ / / / ___/ / / / / / / /_/ /_/_/\___/____/\__, /____/\__/\___/_/ /_/ /_/ /_/ /_/ /_/_/ /_/ /_/ /____/ EOF @@ -24,7 +23,7 @@ echo "Loading..." whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "About fstrim (LXC)" \ - --msgbox "The 'fstrim' command releases unused blocks back to the storage device. This only makes sense for containers on SSD, NVMe, Thin-LVM, or storage with discard/TRIM support.\n\nIf your root filesystem or container disks are on classic HDDs, thick LVM, or unsupported storage types, running fstrim will have no effect.\n\nRecommended:\n- Use fstrim only on SSD, NVMe, or thin-provisioned storage with discard enabled.\n- For ZFS, ensure 'autotrim=on' is set on your pool.\n" 16 88 + --msgbox "The 'fstrim' command releases unused blocks back to the storage device. This only makes sense for containers on SSD, NVMe, Thin-LVM, or storage with discard/TRIM support.\n\nIf your root filesystem or container disks are on classic HDDs, thick LVM, or unsupported storage types, running fstrim will have no effect.\n\nRecommended:\n- Use fstrim only on SSD, NVMe, or thin-provisioned storage with discard enabled.\n- For ZFS, ensure 'autotrim=on' is set on your pool.\n\nSee: https://pve.proxmox.com/wiki/Shrinking_LXC_disks" 16 88 ROOT_FS=$(df -Th "/" | awk 'NR==2 {print $2}') if [ "$ROOT_FS" != "ext4" ]; then @@ -37,36 +36,51 @@ NODE=$(hostname) EXCLUDE_MENU=() STOPPED_MENU=() MAX_NAME_LEN=0 +MAX_STAT_LEN=0 -while read -r CTID STATUS _ NAME _; do +# Build arrays with one pct list +mapfile -t CTLINES < <(pct list | awk 'NR>1') + +for LINE in "${CTLINES[@]}"; do + CTID=$(awk '{print $1}' <<<"$LINE") + STATUS=$(awk '{print $2}' <<<"$LINE") + NAME=$(awk '{print $3}' <<<"$LINE") ((${#NAME} > MAX_NAME_LEN)) && MAX_NAME_LEN=${#NAME} -done < <(pct list | awk 'NR>1') + ((${#STATUS} > MAX_STAT_LEN)) && MAX_STAT_LEN=${#STATUS} +done -FMT="%-5s | %-${MAX_NAME_LEN}s | %-8s" +FMT="%-5s | %-${MAX_NAME_LEN}s | %-${MAX_STAT_LEN}s" -while read -r CTID STATUS _ NAME _; do +for LINE in "${CTLINES[@]}"; do + CTID=$(awk '{print $1}' <<<"$LINE") + STATUS=$(awk '{print $2}' <<<"$LINE") + NAME=$(awk '{print $3}' <<<"$LINE") DESC=$(printf "$FMT" "$CTID" "$NAME" "$STATUS") EXCLUDE_MENU+=("$CTID" "$DESC" "OFF") if [[ "$STATUS" == "stopped" ]]; then STOPPED_MENU+=("$CTID" "$DESC" "OFF") fi -done < <(pct list | awk 'NR>1') +done excluded_containers_raw=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "Containers on $NODE" \ --checklist "\nSelect containers to skip from trimming:\n" \ - 20 $((MAX_NAME_LEN + 40)) 12 "${EXCLUDE_MENU[@]}" 3>&1 1>&2 2>&3) + 20 $((MAX_NAME_LEN + MAX_STAT_LEN + 20)) 12 "${EXCLUDE_MENU[@]}" 3>&1 1>&2 2>&3) [ $? -ne 0 ] && exit read -ra EXCLUDED <<<$(echo "$excluded_containers_raw" | tr -d '"') TO_START=() if [ ${#STOPPED_MENU[@]} -gt 0 ]; then - selected=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ - --title "Stopped LXC Containers" \ - --checklist "\nSome LXC containers are currently stopped.\nWhich ones do you want to temporarily start for the trim operation?\n(They can be stopped again afterwards)\n" \ - 16 $((MAX_NAME_LEN + 40)) 8 "${STOPPED_MENU[@]}" 3>&1 1>&2 2>&3) - [ $? -ne 0 ] && exit - read -ra TO_START <<<$(echo "$selected" | tr -d '"') + echo "" + echo "Some containers are currently stopped." + for ((i = 0; i < ${#STOPPED_MENU[@]}; i += 3)); do + CTID="${STOPPED_MENU[i]}" + DESC="${STOPPED_MENU[i + 1]}" + read -rp "Temporarily start CT $DESC for fstrim? [y/N]: " answer + if [[ "$answer" =~ ^[Yy]$ ]]; then + TO_START+=("$CTID") + fi + done fi declare -A WAS_STOPPED @@ -87,47 +101,48 @@ function trim_container() { sleep 0.5 } -for container in $(pct list | awk 'NR>1 {print $1}'); do - if [[ " ${EXCLUDED[*]} " =~ " $container " ]]; then +for LINE in "${CTLINES[@]}"; do + CTID=$(awk '{print $1}' <<<"$LINE") + STATUS=$(awk '{print $2}' <<<"$LINE") + NAME=$(awk '{print $3}' <<<"$LINE") + if [[ " ${EXCLUDED[*]} " =~ " $CTID " ]]; then header_info - echo -e "${BL}[Info]${GN} Skipping $container (excluded)${CL}" + echo -e "${BL}[Info]${GN} Skipping $CTID ($NAME, excluded)${CL}" sleep 0.5 continue fi - if pct config "$container" | grep -q "template:"; then + if pct config "$CTID" | grep -q "template:"; then header_info - echo -e "${BL}[Info]${GN} Skipping $container (template)${CL}\n" + echo -e "${BL}[Info]${GN} Skipping $CTID ($NAME, template)${CL}\n" sleep 0.5 continue fi - STATUS=$(pct list | awk -v id="$container" '$1==id{print $2}') if [[ "$STATUS" != "running" ]]; then - if [[ -n "${WAS_STOPPED[$container]:-}" ]]; then + if [[ -n "${WAS_STOPPED[$CTID]:-}" ]]; then header_info - echo -e "${BL}[Info]${GN} Starting $container for trim...${CL}" - pct start "$container" + echo -e "${BL}[Info]${GN} Starting $CTID ($NAME) for trim...${CL}" + pct start "$CTID" sleep 2 else header_info - echo -e "${BL}[Info]${GN} Skipping $container (not running, not selected)${CL}" + echo -e "${BL}[Info]${GN} Skipping $CTID ($NAME, not running, not selected)${CL}" sleep 0.5 continue fi fi - trim_container "$container" + trim_container "$CTID" - if [[ -n "${WAS_STOPPED[$container]:-}" ]]; then - if whiptail --backtitle "Proxmox VE Helper Scripts" \ - --title "Stop container again?" \ - --yesno "Container $container was started for the trim operation.\n\nDo you want to stop it again now?" 10 60; then + if [[ -n "${WAS_STOPPED[$CTID]:-}" ]]; then + read -rp "Stop CT $CTID ($NAME) again after trim? [Y/n]: " answer + if [[ ! "$answer" =~ ^[Nn]$ ]]; then header_info - echo -e "${BL}[Info]${GN} Stopping $container again...${CL}" - pct stop "$container" + echo -e "${BL}[Info]${GN} Stopping $CTID ($NAME) again...${CL}" + pct stop "$CTID" sleep 1 else header_info - echo -e "${BL}[Info]${GN} Leaving $container running as requested.${CL}" + echo -e "${BL}[Info]${GN} Leaving $CTID ($NAME) running as requested.${CL}" sleep 1 fi fi From ace869dc6fe4817398c51807f5e3da1e4c7b7714 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 7 Aug 2025 14:03:15 +0200 Subject: [PATCH 0433/1733] Update fstrim.sh --- tools/pve/fstrim.sh | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/tools/pve/fstrim.sh b/tools/pve/fstrim.sh index 679d59a19..b3fef4781 100644 --- a/tools/pve/fstrim.sh +++ b/tools/pve/fstrim.sh @@ -7,7 +7,8 @@ function header_info() { cat <<"EOF" _______ __ __ ______ _ / ____(_) /__ _______ _______/ /____ ____ ___ /_ __/____(_)___ ___ - / /_ / / / _ \/ ___/ / / / ___/ __/ _ \/ __ `__ \ / / / ___/ / / / / / / + / /_ / / / _ \/ ___/ / / / ___/ __/ _ \/ __ `__ \ / / / ___/ / __ `__ \ + / __/ / / / __(__ ) /_/ (__ ) /_/ __/ / / / / / / / / / / / / / / / / /_/ /_/_/\___/____/\__, /____/\__/\___/_/ /_/ /_/ /_/ /_/ /_/_/ /_/ /_/ /____/ EOF @@ -49,13 +50,13 @@ for LINE in "${CTLINES[@]}"; do ((${#STATUS} > MAX_STAT_LEN)) && MAX_STAT_LEN=${#STATUS} done -FMT="%-5s | %-${MAX_NAME_LEN}s | %-${MAX_STAT_LEN}s" +FMT="%-${MAX_NAME_LEN}s | %-${MAX_STAT_LEN}s" for LINE in "${CTLINES[@]}"; do CTID=$(awk '{print $1}' <<<"$LINE") STATUS=$(awk '{print $2}' <<<"$LINE") NAME=$(awk '{print $3}' <<<"$LINE") - DESC=$(printf "$FMT" "$CTID" "$NAME" "$STATUS") + DESC=$(printf "$FMT" "$NAME" "$STATUS") EXCLUDE_MENU+=("$CTID" "$DESC" "OFF") if [[ "$STATUS" == "stopped" ]]; then STOPPED_MENU+=("$CTID" "$DESC" "OFF") @@ -71,12 +72,14 @@ read -ra EXCLUDED <<<$(echo "$excluded_containers_raw" | tr -d '"') TO_START=() if [ ${#STOPPED_MENU[@]} -gt 0 ]; then - echo "" - echo "Some containers are currently stopped." + + echo -e "${BL}[Info]${GN}Some containers are currently stopped.${CL}" for ((i = 0; i < ${#STOPPED_MENU[@]}; i += 3)); do CTID="${STOPPED_MENU[i]}" DESC="${STOPPED_MENU[i + 1]}" - read -rp "Temporarily start CT $DESC for fstrim? [y/N]: " answer + header_info + echo -e "${BL}[Info]${GN} Container $CTID ($DESC) is currently stopped.${CL}" + read -rp "Temporarily start for fstrim? [y/N]: " answer if [[ "$answer" =~ ^[Yy]$ ]]; then TO_START+=("$CTID") fi @@ -90,14 +93,19 @@ done function trim_container() { local container="$1" + local name="$2" header_info echo -e "${BL}[Info]${GN} Trimming ${BL}$container${CL} \n" local before_trim after_trim before_trim=$(lvs --noheadings -o lv_name,data_percent | awk -v ctid="vm-${container}-disk-0" '$1 == ctid {gsub(/%/, "", $2); print $2}') echo -e "${RD}Data before trim $before_trim%${CL}" - pct fstrim "$container" + local fstrim_output + fstrim_output=$(pct fstrim "$container" 2>&1) after_trim=$(lvs --noheadings -o lv_name,data_percent | awk -v ctid="vm-${container}-disk-0" '$1 == ctid {gsub(/%/, "", $2); print $2}') echo -e "${GN}Data after trim $after_trim%${CL}" + + # Logging + echo "$(date '+%Y-%m-%d %H:%M:%S') | CTID=$container | Name=$name | Before=$before_trim% | After=$after_trim% | fstrim: $fstrim_output" >>"$LOGFILE" sleep 0.5 } @@ -131,10 +139,10 @@ for LINE in "${CTLINES[@]}"; do fi fi - trim_container "$CTID" + trim_container "$CTID" "$NAME" if [[ -n "${WAS_STOPPED[$CTID]:-}" ]]; then - read -rp "Stop CT $CTID ($NAME) again after trim? [Y/n]: " answer + read -rp "Stop LXC $CTID ($NAME) again after trim? [Y/n]: " answer if [[ ! "$answer" =~ ^[Nn]$ ]]; then header_info echo -e "${BL}[Info]${GN} Stopping $CTID ($NAME) again...${CL}" @@ -150,4 +158,5 @@ done header_info echo -e "${GN}Finished, LXC Containers Trimmed.${CL} \n" +echo -e "${BL}If you want to see the complete log: cat $LOGFILE${CL}" exit 0 From a0ea3a2f176a5671e9f247fc3263218082585582 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 7 Aug 2025 13:44:35 +0200 Subject: [PATCH 0434/1733] Update tracktor.sh --- ct/tracktor.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ct/tracktor.sh b/ct/tracktor.sh index 1c09bd6e1..309601ba1 100644 --- a/ct/tracktor.sh +++ b/ct/tracktor.sh @@ -7,9 +7,9 @@ source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/ APP="tracktor" var_tags="${var_tags:-car;monitoring}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-1024}" -var_disk="${var_disk:-5}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-4096}" +var_disk="${var_disk:-6}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" From 4cb9dd8f1267296b670e12be0f1b7af512118fb0 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 7 Aug 2025 13:45:40 +0200 Subject: [PATCH 0435/1733] Update tracktor-install.sh --- install/tracktor-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/tracktor-install.sh b/install/tracktor-install.sh index 87526c3cd..a79c9844d 100644 --- a/install/tracktor-install.sh +++ b/install/tracktor-install.sh @@ -16,10 +16,10 @@ update_os setup_nodejs fetch_and_deploy_gh_release "tracktor" "javedh-dev/tracktor" - msg_info "Configuring Tracktor" cd /opt/tracktor export NODE_ENV=production +$STD npm cache clean --force $STD npm install $STD npm run build mkdir /opt/tracktor-data From d9a2047cff9de8916e8d11aeb756f1988ec24150 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 7 Aug 2025 13:45:51 +0200 Subject: [PATCH 0436/1733] Update tracktor.sh --- ct/tracktor.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ct/tracktor.sh b/ct/tracktor.sh index 309601ba1..6be1e3e82 100644 --- a/ct/tracktor.sh +++ b/ct/tracktor.sh @@ -39,6 +39,7 @@ function update_script() { fetch_and_deploy_gh_release "tracktor" "javedh-dev/tracktor" cd /opt/tracktor export NODE_ENV=production + $STD npm cache clean --force $STD npm install $STD npm run build msg_ok "Updated $APP" From 1e036cf10ea3a21d600d5a3ca1e6be220b0cf80d Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 7 Aug 2025 13:50:16 +0200 Subject: [PATCH 0437/1733] Update tracktor.sh --- ct/tracktor.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/ct/tracktor.sh b/ct/tracktor.sh index 6be1e3e82..5cb8d87ef 100644 --- a/ct/tracktor.sh +++ b/ct/tracktor.sh @@ -38,7 +38,6 @@ function update_script() { setup_nodejs fetch_and_deploy_gh_release "tracktor" "javedh-dev/tracktor" cd /opt/tracktor - export NODE_ENV=production $STD npm cache clean --force $STD npm install $STD npm run build From 6a0768ab485768f92539d2fb85f78b01fdcc0fd9 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 7 Aug 2025 13:50:26 +0200 Subject: [PATCH 0438/1733] Update tracktor-install.sh --- install/tracktor-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/install/tracktor-install.sh b/install/tracktor-install.sh index a79c9844d..d9423301f 100644 --- a/install/tracktor-install.sh +++ b/install/tracktor-install.sh @@ -18,7 +18,6 @@ fetch_and_deploy_gh_release "tracktor" "javedh-dev/tracktor" msg_info "Configuring Tracktor" cd /opt/tracktor -export NODE_ENV=production $STD npm cache clean --force $STD npm install $STD npm run build From 91ea3eb62083ca1d2b59d1e87497cb29885293f7 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 7 Aug 2025 14:07:02 +0200 Subject: [PATCH 0439/1733] Update fstrim.sh --- tools/pve/fstrim.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/pve/fstrim.sh b/tools/pve/fstrim.sh index b3fef4781..4ce4838a1 100644 --- a/tools/pve/fstrim.sh +++ b/tools/pve/fstrim.sh @@ -19,6 +19,11 @@ RD="\033[01;31m" GN="\033[1;92m" CL="\033[m" +LOGFILE="/var/log/fstrim.log" +touch "$LOGFILE" +chmod 600 "$LOGFILE" +echo -e "\n----- $(date '+%Y-%m-%d %H:%M:%S') | fstrim Run by $(whoami) on $(hostname) -----" >>"$LOGFILE" + header_info echo "Loading..." From cf9432778732e8a204cdfb3a28e1776017534b55 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 7 Aug 2025 14:09:09 +0200 Subject: [PATCH 0440/1733] Update fstrim.sh --- tools/pve/fstrim.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/pve/fstrim.sh b/tools/pve/fstrim.sh index 4ce4838a1..c78bf4e96 100644 --- a/tools/pve/fstrim.sh +++ b/tools/pve/fstrim.sh @@ -77,11 +77,12 @@ read -ra EXCLUDED <<<$(echo "$excluded_containers_raw" | tr -d '"') TO_START=() if [ ${#STOPPED_MENU[@]} -gt 0 ]; then - - echo -e "${BL}[Info]${GN}Some containers are currently stopped.${CL}" for ((i = 0; i < ${#STOPPED_MENU[@]}; i += 3)); do CTID="${STOPPED_MENU[i]}" DESC="${STOPPED_MENU[i + 1]}" + if [[ " ${EXCLUDED[*]} " =~ " $CTID " ]]; then + continue + fi header_info echo -e "${BL}[Info]${GN} Container $CTID ($DESC) is currently stopped.${CL}" read -rp "Temporarily start for fstrim? [y/N]: " answer From 320ecd397219151c937127197bfa19f386d6a29e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 7 Aug 2025 14:13:24 +0200 Subject: [PATCH 0441/1733] Update fstrim.sh --- tools/pve/fstrim.sh | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/tools/pve/fstrim.sh b/tools/pve/fstrim.sh index c78bf4e96..ccbb88c3b 100644 --- a/tools/pve/fstrim.sh +++ b/tools/pve/fstrim.sh @@ -102,16 +102,37 @@ function trim_container() { local name="$2" header_info echo -e "${BL}[Info]${GN} Trimming ${BL}$container${CL} \n" + local before_trim after_trim - before_trim=$(lvs --noheadings -o lv_name,data_percent | awk -v ctid="vm-${container}-disk-0" '$1 == ctid {gsub(/%/, "", $2); print $2}') - echo -e "${RD}Data before trim $before_trim%${CL}" + local lv_name="vm-${container}-disk-0" + if lvs --noheadings -o lv_name 2>/dev/null | grep -qw "$lv_name"; then + before_trim=$(lvs --noheadings -o lv_name,data_percent 2>/dev/null | awk -v ctid="$lv_name" '$1 == ctid {gsub(/%/, "", $2); print $2}') + [[ -n "$before_trim" ]] && echo -e "${RD}Data before trim $before_trim%${CL}" || echo -e "${RD}Data before trim: not available${CL}" + else + before_trim="" + echo -e "${RD}Data before trim: not available (non-LVM storage)${CL}" + fi + local fstrim_output fstrim_output=$(pct fstrim "$container" 2>&1) - after_trim=$(lvs --noheadings -o lv_name,data_percent | awk -v ctid="vm-${container}-disk-0" '$1 == ctid {gsub(/%/, "", $2); print $2}') - echo -e "${GN}Data after trim $after_trim%${CL}" + if echo "$fstrim_output" | grep -qi "not supported"; then + echo -e "${RD}fstrim NICHT unterstützt auf diesem Storage!${CL}" + elif echo "$fstrim_output" | grep -Eq '([0-9]+(\.[0-9]+)?\s*[KMGT]?B)'; then + echo -e "${GN}fstrim result: $fstrim_output${CL}" + else + echo -e "${RD}fstrim result: $fstrim_output${CL}" + fi + + if lvs --noheadings -o lv_name 2>/dev/null | grep -qw "$lv_name"; then + after_trim=$(lvs --noheadings -o lv_name,data_percent 2>/dev/null | awk -v ctid="$lv_name" '$1 == ctid {gsub(/%/, "", $2); print $2}') + [[ -n "$after_trim" ]] && echo -e "${GN}Data after trim $after_trim%${CL}" || echo -e "${GN}Data after trim: not available${CL}" + else + after_trim="" + echo -e "${GN}Data after trim: not available (non-LVM storage)${CL}" + fi # Logging - echo "$(date '+%Y-%m-%d %H:%M:%S') | CTID=$container | Name=$name | Before=$before_trim% | After=$after_trim% | fstrim: $fstrim_output" >>"$LOGFILE" + echo "$(date '+%Y-%m-%d %H:%M:%S') | CTID=$container | Name=$name | Before=${before_trim:-N/A}% | After=${after_trim:-N/A}% | fstrim: $fstrim_output" >>"$LOGFILE" sleep 0.5 } From 91465ecd7fc0e51eaa6e84a8d8336d255c22fcab Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 7 Aug 2025 14:41:36 +0200 Subject: [PATCH 0442/1733] Update fstrim.sh --- tools/pve/fstrim.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/pve/fstrim.sh b/tools/pve/fstrim.sh index ccbb88c3b..4bd68f442 100644 --- a/tools/pve/fstrim.sh +++ b/tools/pve/fstrim.sh @@ -29,7 +29,7 @@ echo "Loading..." whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "About fstrim (LXC)" \ - --msgbox "The 'fstrim' command releases unused blocks back to the storage device. This only makes sense for containers on SSD, NVMe, Thin-LVM, or storage with discard/TRIM support.\n\nIf your root filesystem or container disks are on classic HDDs, thick LVM, or unsupported storage types, running fstrim will have no effect.\n\nRecommended:\n- Use fstrim only on SSD, NVMe, or thin-provisioned storage with discard enabled.\n- For ZFS, ensure 'autotrim=on' is set on your pool.\n\nSee: https://pve.proxmox.com/wiki/Shrinking_LXC_disks" 16 88 + --msgbox "The 'fstrim' command releases unused blocks back to the storage device. This only makes sense for containers on SSD, NVMe, Thin-LVM, or storage with discard/TRIM support.\n\nIf your root filesystem or container disks are on classic HDDs, thick LVM, or unsupported storage types, running fstrim will have no effect.\n\nRecommended:\n- Use fstrim only on SSD, NVMe, or thin-provisioned storage with discard enabled.\n- For ZFS, ensure 'autotrim=on' is set on your pool.\n" 16 88 ROOT_FS=$(df -Th "/" | awk 'NR==2 {print $2}') if [ "$ROOT_FS" != "ext4" ]; then @@ -116,7 +116,7 @@ function trim_container() { local fstrim_output fstrim_output=$(pct fstrim "$container" 2>&1) if echo "$fstrim_output" | grep -qi "not supported"; then - echo -e "${RD}fstrim NICHT unterstützt auf diesem Storage!${CL}" + echo -e "${RD}fstrim isnt supported on this storage!${CL}" elif echo "$fstrim_output" | grep -Eq '([0-9]+(\.[0-9]+)?\s*[KMGT]?B)'; then echo -e "${GN}fstrim result: $fstrim_output${CL}" else From 93f4d570ea51ef0db09e777f1034ba21e704a7a7 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 7 Aug 2025 14:55:17 +0200 Subject: [PATCH 0443/1733] Update tracktor-install.sh --- install/tracktor-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/tracktor-install.sh b/install/tracktor-install.sh index d9423301f..f5bdc27f9 100644 --- a/install/tracktor-install.sh +++ b/install/tracktor-install.sh @@ -18,7 +18,7 @@ fetch_and_deploy_gh_release "tracktor" "javedh-dev/tracktor" msg_info "Configuring Tracktor" cd /opt/tracktor -$STD npm cache clean --force +rm package-lock.json $STD npm install $STD npm run build mkdir /opt/tracktor-data From 953226cb5b468edd9bdc80440351df5fda980606 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 7 Aug 2025 15:06:38 +0200 Subject: [PATCH 0444/1733] Update tracktor.sh --- ct/tracktor.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/tracktor.sh b/ct/tracktor.sh index 5cb8d87ef..f6aa2d982 100644 --- a/ct/tracktor.sh +++ b/ct/tracktor.sh @@ -38,7 +38,7 @@ function update_script() { setup_nodejs fetch_and_deploy_gh_release "tracktor" "javedh-dev/tracktor" cd /opt/tracktor - $STD npm cache clean --force + rm package-lock.json $STD npm install $STD npm run build msg_ok "Updated $APP" From 3491e9b6c3de4e336ba4aa19def8ba3cb1bdb274 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 7 Aug 2025 15:09:32 +0200 Subject: [PATCH 0445/1733] Update tracktor-install.sh --- install/tracktor-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/tracktor-install.sh b/install/tracktor-install.sh index f5bdc27f9..8ac4bd893 100644 --- a/install/tracktor-install.sh +++ b/install/tracktor-install.sh @@ -22,7 +22,7 @@ rm package-lock.json $STD npm install $STD npm run build mkdir /opt/tracktor-data -cat </opt/tracktor.env +cat </opt/tracktor/app/server/.env NODE_ENV=production PUBLIC_DEMO_MODE=false PUBLIC_API_BASE_URL=/ @@ -40,7 +40,7 @@ After=network.target [Service] Type=simple WorkingDirectory=/opt/tracktor -EnvironmentFile=/opt/tracktor.env +EnvironmentFile=/opt/tracktor/app/server/.env ExecStart=/usr/bin/npm start [Install] From 8421aea535d6c119829e32d16ebe1ba25e03d0e9 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 7 Aug 2025 15:10:48 +0200 Subject: [PATCH 0446/1733] Update tracktor.sh --- ct/tracktor.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ct/tracktor.sh b/ct/tracktor.sh index f6aa2d982..e50da7b5a 100644 --- a/ct/tracktor.sh +++ b/ct/tracktor.sh @@ -34,6 +34,10 @@ function update_script() { systemctl stop tracktor msg_ok "Stopped Service" + msg_info "Creating Backup" + cp /opt/tracktor/app/server/.env /opt/tracktor.env + msg_ok "Created Backup" + msg_info "Updating ${APP}" setup_nodejs fetch_and_deploy_gh_release "tracktor" "javedh-dev/tracktor" @@ -43,6 +47,10 @@ function update_script() { $STD npm run build msg_ok "Updated $APP" + msg_info "Restoring Backup" + cp /opt/tracktor.env /opt/tracktor/app/server/.env + msg_ok "Restored Backup" + msg_info "Starting Service" systemctl start tracktor msg_ok "Started Service" From 8ab39322cd57fe85fc8089de2ace21a5f6b1b687 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 7 Aug 2025 15:20:22 +0200 Subject: [PATCH 0447/1733] yq >> jq --- ct/mediamanager.sh | 90 +++++++++++++++++++++++----------------------- ct/palmr.sh | 2 +- ct/tracktor.sh | 4 +-- 3 files changed, 48 insertions(+), 48 deletions(-) diff --git a/ct/mediamanager.sh b/ct/mediamanager.sh index 5290a9b9d..cc67d8af5 100644 --- a/ct/mediamanager.sh +++ b/ct/mediamanager.sh @@ -20,52 +20,52 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/mediamanager ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - RELEASE=$(curl -fsSL https://api.github.com/repos/maxdorninger/MediaManager/releases/latest | yq '.tag_name' | sed 's/^v//') - if [[ "${RELEASE}" != "$(cat ~/.mediamanager 2>/dev/null)" ]] || [[ ! -f ~/.mediamanager ]]; then - msg_info "Stopping Service" - systemctl stop mediamanager - msg_ok "Stopped Service" - - msg_info "Updating ${APP}" - fetch_and_deploy_gh_release "MediaManager" "maxdorninger/MediaManager" "tarball" "latest" "/opt/mediamanager" - MM_DIR="/opt/mm" - export CONFIG_DIR="${MM_DIR}/config" - export FRONTEND_FILES_DIR="${MM_DIR}/web/build" - export BASE_PATH="" - export PUBLIC_VERSION="" - export PUBLIC_API_URL="${BASE_PATH}/api/v1" - export BASE_PATH="${BASE_PATH}/web" - cd /opt/mediamanager/web - $STD npm ci - $STD npm run build - rm -rf "$FRONTEND_FILES_DIR"/build - cp -r build "$FRONTEND_FILES_DIR" - - export BASE_PATH="" - export VIRTUAL_ENV="/opt/${MM_DIR}/venv" - cd /opt/mediamanager - rm -rf "$MM_DIR"/{media_manager,alembic*} - cp -r {media_manager,alembic*} "$MM_DIR" - $STD /usr/local/bin/uv sync --locked --active - msg_ok "Updated $APP" - - msg_info "Starting Service" - systemctl start mediamanager - msg_ok "Started Service" - - msg_ok "Updated Successfully" - else - msg_ok "Already up to date" - fi + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/mediamanager ]]; then + msg_error "No ${APP} Installation Found!" exit + fi + + RELEASE=$(curl -fsSL https://api.github.com/repos/maxdorninger/MediaManager/releases/latest | jq '.tag_name' | sed 's/^v//') + if [[ "${RELEASE}" != "$(cat ~/.mediamanager 2>/dev/null)" ]] || [[ ! -f ~/.mediamanager ]]; then + msg_info "Stopping Service" + systemctl stop mediamanager + msg_ok "Stopped Service" + + msg_info "Updating ${APP}" + fetch_and_deploy_gh_release "MediaManager" "maxdorninger/MediaManager" "tarball" "latest" "/opt/mediamanager" + MM_DIR="/opt/mm" + export CONFIG_DIR="${MM_DIR}/config" + export FRONTEND_FILES_DIR="${MM_DIR}/web/build" + export BASE_PATH="" + export PUBLIC_VERSION="" + export PUBLIC_API_URL="${BASE_PATH}/api/v1" + export BASE_PATH="${BASE_PATH}/web" + cd /opt/mediamanager/web + $STD npm ci + $STD npm run build + rm -rf "$FRONTEND_FILES_DIR"/build + cp -r build "$FRONTEND_FILES_DIR" + + export BASE_PATH="" + export VIRTUAL_ENV="/opt/${MM_DIR}/venv" + cd /opt/mediamanager + rm -rf "$MM_DIR"/{media_manager,alembic*} + cp -r {media_manager,alembic*} "$MM_DIR" + $STD /usr/local/bin/uv sync --locked --active + msg_ok "Updated $APP" + + msg_info "Starting Service" + systemctl start mediamanager + msg_ok "Started Service" + + msg_ok "Updated Successfully" + else + msg_ok "Already up to date" + fi + exit } start diff --git a/ct/palmr.sh b/ct/palmr.sh index 06014b5bb..52e8c5fc5 100644 --- a/ct/palmr.sh +++ b/ct/palmr.sh @@ -28,7 +28,7 @@ function update_script() { exit fi - RELEASE=$(curl -fsSL https://api.github.com/repos/kyantech/palmr/releases/latest | yq '.tag_name' | sed 's/^v//') + RELEASE=$(curl -fsSL https://api.github.com/repos/kyantech/palmr/releases/latest | jq '.tag_name' | sed 's/^v//') if [[ "${RELEASE}" != "$(cat ~/.palmr 2>/dev/null)" ]] || [[ ! -f ~/.palmr ]]; then msg_info "Stopping Services" systemctl stop palmr-frontend palmr-backend diff --git a/ct/tracktor.sh b/ct/tracktor.sh index e50da7b5a..93925523d 100644 --- a/ct/tracktor.sh +++ b/ct/tracktor.sh @@ -28,7 +28,7 @@ function update_script() { exit fi - RELEASE=$(curl -fsSL https://api.github.com/repos/javedh-dev/tracktor/releases/latest | yq '.tag_name' | sed 's/^v//') + RELEASE=$(curl -fsSL https://api.github.com/repos/javedh-dev/tracktor/releases/latest | jq '.tag_name' | sed 's/^v//') if [[ "${RELEASE}" != "$(cat ~/.tracktor 2>/dev/null)" ]] || [[ ! -f ~/.tracktor ]]; then msg_info "Stopping Service" systemctl stop tracktor @@ -40,7 +40,7 @@ function update_script() { msg_info "Updating ${APP}" setup_nodejs - fetch_and_deploy_gh_release "tracktor" "javedh-dev/tracktor" + fetch_and_deploy_gh_release "tracktor" "javedh-dev/tracktor" cd /opt/tracktor rm package-lock.json $STD npm install From beed0ce26e075f194eae72b02eb5d1825f39b1e7 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 7 Aug 2025 15:27:29 +0200 Subject: [PATCH 0448/1733] Update tracktor-install.sh --- install/tracktor-install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/install/tracktor-install.sh b/install/tracktor-install.sh index 8ac4bd893..5b709e64e 100644 --- a/install/tracktor-install.sh +++ b/install/tracktor-install.sh @@ -22,11 +22,12 @@ rm package-lock.json $STD npm install $STD npm run build mkdir /opt/tracktor-data +HOST_IP=$(hostname -I | awk '{print $1}') cat </opt/tracktor/app/server/.env NODE_ENV=production PUBLIC_DEMO_MODE=false -PUBLIC_API_BASE_URL=/ DB_PATH=/opt/tracktor-data/vehicles.db +PUBLIC_API_BASE_URL=http://$HOST_IP:3000 PORT=3000 EOF msg_ok "Configured Tracktor" From 5f4386e3d5b4edba54d40e90fde632070342f345 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 7 Aug 2025 15:28:12 +0200 Subject: [PATCH 0449/1733] Update tracktor.json --- frontend/public/json/tracktor.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/tracktor.json b/frontend/public/json/tracktor.json index 1f5ea1a66..2d7d0a07b 100644 --- a/frontend/public/json/tracktor.json +++ b/frontend/public/json/tracktor.json @@ -10,7 +10,7 @@ "privileged": false, "interface_port": 3000, "documentation": "https://tracktor.bytedge.in/introduction.html", - "config_path": "/etc/system/systemd/tracktor.service", + "config_path": "/opt/tracktor/app/server/.env", "website": "https://tracktor.bytedge.in/", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/tracktor.svg", "description": "Tracktor is an open-source web application for comprehensive vehicle management.\nEasily track ⛽ fuel consumption, 🛠️ maintenance, 🛡️ insurance, and 📄 regulatory documents for all your vehicles in one place. ", From b4d996ea31899b6f61ed97c1acf3adca0d5c779e Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 7 Aug 2025 15:39:08 +0200 Subject: [PATCH 0450/1733] Update tracktor.sh --- ct/tracktor.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ct/tracktor.sh b/ct/tracktor.sh index 93925523d..861de2b93 100644 --- a/ct/tracktor.sh +++ b/ct/tracktor.sh @@ -39,6 +39,7 @@ function update_script() { msg_ok "Created Backup" msg_info "Updating ${APP}" + rm -rf /opt/tracktor setup_nodejs fetch_and_deploy_gh_release "tracktor" "javedh-dev/tracktor" cd /opt/tracktor From 95cb20c634c6e7048afd9023e90fee3e485a6b87 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 8 Aug 2025 07:52:35 +0200 Subject: [PATCH 0451/1733] Update hortusfox.sh --- ct/hortusfox.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ct/hortusfox.sh b/ct/hortusfox.sh index c86c75e2c..2d32b3801 100644 --- a/ct/hortusfox.sh +++ b/ct/hortusfox.sh @@ -7,9 +7,9 @@ source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxV APP="HortusFox" var_tags="${var_tags:-plants}" -var_cpu="${var_cpu:-4}" -var_ram="${var_ram:-4096}" -var_disk="${var_disk:-15}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-5}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" From 5eecfb536641556d780c263c770577886b3656a0 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 8 Aug 2025 08:05:12 +0200 Subject: [PATCH 0452/1733] cleanup --- ct/alpine-teamspeak-server.sh | 64 --------- ct/hortusfox.sh | 73 ----------- ct/jeedom.sh | 46 ------- ct/palmr.sh | 79 ------------ ct/tandoor.sh | 92 ------------- ct/victoriametrics.sh | 66 ---------- frontend/public/json/hortusfox.json | 45 ------- frontend/public/json/jeedom.json | 44 ------- frontend/public/json/palmr.json | 44 ------- frontend/public/json/teamspeak-server.json | 40 ------ install/hortusfox-install.sh | 103 --------------- install/palmr-install.sh | 97 -------------- install/tandoor-install.sh | 143 --------------------- install/victoriametrics-install.sh | 82 ------------ 14 files changed, 1018 deletions(-) delete mode 100644 ct/alpine-teamspeak-server.sh delete mode 100644 ct/hortusfox.sh delete mode 100644 ct/jeedom.sh delete mode 100644 ct/palmr.sh delete mode 100644 ct/tandoor.sh delete mode 100644 ct/victoriametrics.sh delete mode 100644 frontend/public/json/hortusfox.json delete mode 100644 frontend/public/json/jeedom.json delete mode 100644 frontend/public/json/palmr.json delete mode 100644 frontend/public/json/teamspeak-server.json delete mode 100644 install/hortusfox-install.sh delete mode 100644 install/palmr-install.sh delete mode 100644 install/tandoor-install.sh delete mode 100644 install/victoriametrics-install.sh diff --git a/ct/alpine-teamspeak-server.sh b/ct/alpine-teamspeak-server.sh deleted file mode 100644 index 104b18f66..000000000 --- a/ct/alpine-teamspeak-server.sh +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: tremor021 (Slaviša Arežina) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://teamspeak.com/en/ - -APP="Alpine-TeamSpeak-Server" -var_tags="${var_tags:-alpine;communication}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-256}" -var_disk="${var_disk:-2}" -var_os="${var_os:-alpine}" -var_version="${var_version:-3.22}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - - if [[ ! -d /opt/teamspeak-server ]]; then - msg_error "No ${APP} installation found!" - exit 1 - fi - - # define custom command to scrape version - local CUSTOM_CMD="curl -fsSL https://teamspeak.com/en/downloads/#server \ - | sed -n '/teamspeak3-server_linux_amd64-/ { s/.*teamspeak3-server_linux_amd64-\([0-9]\+\.[0-9]\+\.[0-9]\+\).*/\1/p; q }'" - - if check_for_update "${APP}" "${CUSTOM_CMD}"; then - local release="$CHECK_UPDATE_RELEASE" - - msg_info "Updating ${APP} LXC to v${release}" - $STD apk -U upgrade - $STD service teamspeak stop - - curl -fsSL "https://files.teamspeak-services.com/releases/server/${release}/teamspeak3-server_linux_amd64-${release}.tar.bz2" -o ts3server.tar.bz2 - tar -xf ts3server.tar.bz2 - cp -ru teamspeak3-server_linux_amd64/* /opt/teamspeak-server/ - - rm -f ts3server.tar.bz2 - rm -rf teamspeak3-server_linux_amd64 - - echo "${release}" >~/.teamspeak-server - - $STD service teamspeak start - msg_ok "Updated ${APP} successfully to v${release}" - fi - - exit 0 -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following IP:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}${IP}:9987${CL}" diff --git a/ct/hortusfox.sh b/ct/hortusfox.sh deleted file mode 100644 index 2d32b3801..000000000 --- a/ct/hortusfox.sh +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: MickLesk (CanbiZ) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/danielbrendel/hortusfox-web - -APP="HortusFox" -var_tags="${var_tags:-plants}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-5}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/hortusfox ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - RELEASE=$(curl -fsSL https://api.github.com/repos/danielbrendel/hortusfox-web/releases/latest | jq -r .tag_name | sed 's/^v//') - if [[ ! -f ~/.hortusfox ]] || [[ "${RELEASE}" != "$(cat ~/.hortusfox)" ]]; then - msg_info "Stopping Service" - systemctl stop apache2 - msg_ok "Stopped Service" - - msg_info "Backing up current HortusFox installation" - cd /opt - mv /opt/hortusfox/ /opt/hortusfox-backup - msg_ok "Backed up current HortusFox installation" - - fetch_and_deploy_gh_release "hortusfox" "danielbrendel/hortusfox-web" - - msg_info "Updating HortusFox" - cd /opt/hortusfox - mv /opt/hortusfox-backup/.env /opt/hortusfox/.env - $STD composer install --no-dev --optimize-autoloader - php asatru migrate --no-interaction - php asatru plants:attributes - php asatru calendar:classes - msg_ok "Updated HortusFox" - - msg_info "Starting Service" - systemctl start apache2 - msg_ok "Started Service" - - msg_info "Cleaning up" - rm -r /opt/hortusfox-backup - msg_ok "Cleaned" - msg_ok "Updated Successfully" - else - msg_ok "No update required. ${APP} is already at v${RELEASE}" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" diff --git a/ct/jeedom.sh b/ct/jeedom.sh deleted file mode 100644 index 36f98a14d..000000000 --- a/ct/jeedom.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Mips2648 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://jeedom.com/ - -APP="Jeedom" -var_tags="${var_tags:-automation;smarthome}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-16}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - - if [[ ! -f /var/www/html/core/config/version ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - msg_info "Updating OS" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "OS updated, you can now update Jeedom from the Web UI." - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" diff --git a/ct/palmr.sh b/ct/palmr.sh deleted file mode 100644 index 52e8c5fc5..000000000 --- a/ct/palmr.sh +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: vhsdream -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/kyantech/Palmr - -APP="Palmr" -var_tags="${var_tags:-files}" -var_cpu="${var_cpu:-4}" -var_ram="${var_ram:-4096}" -var_disk="${var_disk:-6}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/palmr_data ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - RELEASE=$(curl -fsSL https://api.github.com/repos/kyantech/palmr/releases/latest | jq '.tag_name' | sed 's/^v//') - if [[ "${RELEASE}" != "$(cat ~/.palmr 2>/dev/null)" ]] || [[ ! -f ~/.palmr ]]; then - msg_info "Stopping Services" - systemctl stop palmr-frontend palmr-backend - msg_ok "Stopped Services" - - cp /opt/palmr/apps/server/.env /opt/palmr.env - rm -rf /opt/palmr - fetch_and_deploy_gh_release "Palmr" "kyantech/Palmr" "tarball" "latest" "/opt/palmr" - msg_info "Updating ${APP}" - PNPM="$(jq -r '.packageManager' /opt/palmr/package.json)" - NODE_VERSION="20" NODE_MODULE="$PNPM" setup_nodejs - cd /opt/palmr/apps/server - PALMR_DIR="/opt/palmr_data" - $STD pnpm install - mv /opt/palmr.env ./.env - $STD pnpm dlx prisma generate - $STD pnpm dlx prisma migrate deploy - $STD pnpm dlx prisma db push - $STD pnpm build - - cd /opt/palmr/apps/web - export NODE_ENV=production - export NEXT_TELEMETRY_DISABLED=1 - mv ./.env.example ./.env - $STD pnpm install - $STD pnpm build - chown -R palmr:palmr "$PALMR_DIR" /opt/palmr - msg_ok "Updated $APP" - - msg_info "Starting Services" - systemctl start palmr-backend palmr-frontend - msg_ok "Started Services" - - msg_ok "Updated Successfully" - else - msg_ok "Already up to date" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" diff --git a/ct/tandoor.sh b/ct/tandoor.sh deleted file mode 100644 index 8b7b874d7..000000000 --- a/ct/tandoor.sh +++ /dev/null @@ -1,92 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: MickLesk (Canbiz) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://tandoor.dev/ - -APP="Tandoor" -var_tags="${var_tags:-recipes}" -var_cpu="${var_cpu:-4}" -var_ram="${var_ram:-4096}" -var_disk="${var_disk:-10}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/tandoor ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if [[ ! -f ~/.tandoor ]]; then - msg_error "v1 Installation found, please export your data and create an new LXC." - exit - fi - - RELEASE=$(curl -fsSL https://api.github.com/repos/TandoorRecipes/recipes/releases/latest | jq -r '.tag_name' | sed 's/^v//') - if [[ "${RELEASE}" != "$(cat ~/.tandoor 2>/dev/null)" ]] || [[ ! -f ~/.tandoor ]]; then - msg_info "Stopping $APP" - systemctl stop tandoor - msg_ok "Stopped $APP" - - msg_info "Creating Backup" - mv /opt/tandoor /opt/tandoor.bak - msg_ok "Backup Created" - - NODE_VERSION="20" NODE_MODULE="yarn" setup_nodejs - PYTHON_VERSION="3.13" setup_uv - fetch_and_deploy_gh_release "tandoor" "TandoorRecipes/recipes" "tarball" "latest" "/opt/tandoor" - - msg_info "Updating $APP to ${RELEASE}" - cp -r /opt/tandoor.bak/{config,api,mediafiles,staticfiles} /opt/tandoor/ - mv /opt/.env /opt/tandoor/.env - cd /opt/tandoor - $STD uv venv .venv --python=python3 - $STD uv pip install -r requirements.txt --python .venv/bin/python - cd /opt/tandoor/vue3 - $STD yarn install - $STD yarn build - TANDOOR_VERSION="$(curl -fsSL https://api.github.com/repos/TandoorRecipes/recipes/releases/latest | jq -r .tag_name)" - cat </opt/tandoor/cookbook/version_info.py -TANDOOR_VERSION = "$TANDOOR_VERSION" -TANDOOR_REF = "bare-metal" -VERSION_INFO = [] -EOF - cd /opt/tandoor - $STD /opt/tandoor/.venv/bin/python manage.py migrate - $STD /opt/tandoor/.venv/bin/python manage.py collectstatic --no-input - msg_ok "Updated $APP to ${RELEASE}" - - msg_info "Starting $APP" - systemctl start tandoor - systemctl reload nginx - msg_ok "Started $APP" - - msg_info "Cleaning Up" - rm -rf /opt/tandoor.bak - msg_ok "Cleanup Completed" - msg_ok "Update Successful" - else - msg_ok "No update required. ${APP} is already at v${RELEASE}" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8002${CL}" diff --git a/ct/victoriametrics.sh b/ct/victoriametrics.sh deleted file mode 100644 index 00e38edbf..000000000 --- a/ct/victoriametrics.sh +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Slaviša Arežina (tremor021) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/VictoriaMetrics/VictoriaMetrics - -APP="VictoriaMetrics" -var_tags="${var_tags:-database}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-16}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/victoriametrics ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - RELEASE=$(curl -fsSL https://api.github.com/repos/VictoriaMetrics/VictoriaMetrics/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') - if [[ ! -f ~/.victoriametrics ]] || [[ "${RELEASE}" != "$(cat ~/.victoriametrics)" ]]; then - msg_info "Stopping $APP" - systemctl stop victoriametrics - [[ -f /etc/systemd/system/victoriametrics-logs.service ]] && systemctl stop victoriametrics-logs - msg_ok "Stopped $APP" - - fetch_and_deploy_gh_release "victoriametrics" "VictoriaMetrics/VictoriaMetrics" "prebuild" "latest" "/opt/victoriametrics" "victoria-metrics-linux-amd64-v+([0-9.]).tar.gz" - fetch_and_deploy_gh_release "vmutils" "VictoriaMetrics/VictoriaMetrics" "prebuild" "latest" "/opt/victoriametrics" "vmutils-linux-amd64-v+([0-9.]).tar.gz" - - if [[ -f /etc/systemd/system/victoriametrics-logs.service ]]; then - fetch_and_deploy_gh_release "victorialogs" "VictoriaMetrics/VictoriaLogs" "prebuild" "latest" "/opt/victoriametrics" "victoria-logs-linux-amd64*.tar.gz" - fetch_and_deploy_gh_release "vlutils" "VictoriaMetrics/VictoriaLogs" "prebuild" "latest" "/opt/victoriametrics" "vlutils-linux-amd64*.tar.gz" - fi - chmod +x /opt/victoriametrics/* - - msg_info "Starting $APP" - systemctl start victoriametrics - [[ -f /etc/systemd/system/victoriametrics-logs.service ]] && systemctl start victoriametrics-logs - msg_ok "Started $APP" - - msg_ok "Updated Successfully" - else - msg_ok "No update required. ${APP} is already at ${RELEASE}" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8428/vmui${CL}" diff --git a/frontend/public/json/hortusfox.json b/frontend/public/json/hortusfox.json deleted file mode 100644 index f878e5f45..000000000 --- a/frontend/public/json/hortusfox.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "name": "HortusFox", - "slug": "hortusfox", - "categories": [ - 21, - 26 - ], - "type": "ct", - "updateable": true, - "privileged": false, - "date_created": "2025-08-04", - "config_path": "/opt/hortusfox/.env", - "interface_port": 80, - "documentation": "https://github.com/danielbrendel/hortusfox-web", - "website": "https://www.hortusfox.com/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/hortusfox.webp", - "description": "HortusFox is a collaborative plant management system for plant enthusiasts. Manage, document and track your entire plant collection – self-hosted and privacy-friendly.", - "install_methods": [ - { - "type": "default", - "script": "ct/hortusfox.sh", - "resources": { - "cpu": 2, - "ram": 2048, - "hdd": 6, - "os": "Debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": "admin@example.com", - "password": null - }, - "notes": [ - { - "text": "Initial password is generated and saved in ~/hortusfox.creds after install.", - "type": "info" - }, - { - "text": "After installation, access the web UI on http:/// and log in as admin@example.com with the generated password.", - "type": "info" - } - ] -} diff --git a/frontend/public/json/jeedom.json b/frontend/public/json/jeedom.json deleted file mode 100644 index 60641af82..000000000 --- a/frontend/public/json/jeedom.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "name": "Jeedom", - "slug": "jeedom", - "categories": [ - 16 - ], - "date_created": "2025-03-06", - "type": "ct", - "updateable": false, - "privileged": false, - "interface_port": 80, - "documentation": "https://doc.jeedom.com", - "config_path": "", - "website": "https://jeedom.com/", - "logo": "https://jeedom.com/_next/image?url=%2Fassets%2Fimg%2Flogo.png&w=256&q=75", - "description": "Jeedom is a home automation system that is free, open, and cloudless. It allows users to manage and automate various aspects of their homes by creating objects, installing plugins for added functionalities, and connecting to a Market account for services. It also supports direct access URLs and user management.", - "install_methods": [ - { - "type": "default", - "script": "ct/jeedom.sh", - "resources": { - "cpu": 2, - "ram": 2048, - "hdd": 16, - "os": "Debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": "admin", - "password": "admin" - }, - "notes": [ - { - "text": "WARNING: Installation sources scripts outside of Community Scripts repo. Please check the source before installing.", - "type": "warning" - }, - { - "text": "Only OS packages are updateable. To update Jeedom, please use the web interface.", - "type": "info" - } - ] -} diff --git a/frontend/public/json/palmr.json b/frontend/public/json/palmr.json deleted file mode 100644 index 0f5fffd2c..000000000 --- a/frontend/public/json/palmr.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "name": "Palmr", - "slug": "palmr", - "categories": [ - 11 - ], - "date_created": "2025-08-03", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 3000, - "documentation": "https://palmr.kyantech.com.br/docs/3.1-beta", - "config_path": "/opt/palmr/apps/server/.env, /opt/palmr/apps/web/.env", - "website": "https://palmr.kyantech.com.br/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/palmr.webp", - "description": "Palmr is a fast and secure platform for sharing files, built with performance and privacy in mind.", - "install_methods": [ - { - "type": "default", - "script": "ct/palmr.sh", - "resources": { - "cpu": 4, - "ram": 4096, - "hdd": 6, - "os": "Debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "To use a bind mount for storage, create symlinks to your mount for both `uploads` and `temp-uploads` in `/opt/palmr_data`", - "type": "info" - }, - { - "text": "To use Palmr with a reverse proxy, uncomment `SECURE_SITE` in `/opt/palmr/apps/server/.env`", - "type": "info" - } - ] -} diff --git a/frontend/public/json/teamspeak-server.json b/frontend/public/json/teamspeak-server.json deleted file mode 100644 index 1e23459b3..000000000 --- a/frontend/public/json/teamspeak-server.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "Teamspeak-Server", - "slug": "teamspeak-server", - "categories": [ - 24 - ], - "date_created": "2025-07-21", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 9987, - "documentation": "https://support.teamspeak.com/hc/en-us/categories/360000302017-TeamSpeak-3", - "website": "https://teamspeak.com/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/teamspeak-light.webp", - "config_path": "", - "description": "TeamSpeak is a voice over IP (VoIP) application, primarily used by gamers and teams to chat in real time on dedicated servers. It delivers crystal‑clear, low‑latency voice communication.", - "install_methods": [ - { - "type": "alpine", - "script": "ct/alpine-teamspeak-server.sh", - "resources": { - "cpu": 1, - "ram": 256, - "hdd": 2, - "os": "alpine", - "version": "3.22" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "Use `journalctl -u teamspeak-server.service` inside LXC console to check for admin credentials!", - "type": "info" - } - ] -} diff --git a/install/hortusfox-install.sh b/install/hortusfox-install.sh deleted file mode 100644 index 57312198e..000000000 --- a/install/hortusfox-install.sh +++ /dev/null @@ -1,103 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: MickLesk (CanbiZ) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/danielbrendel/hortusfox-web - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt-get install -y apache2 -msg_ok "Installed Dependencies" - -PHP_MODULE="exif,mysql" PHP_APACHE="YES" PHP_FPM="NO" PHP_VERSION="8.3" setup_php -setup_mariadb -setup_composer - -msg_info "Setting up database" -DB_NAME=hortusfox -DB_USER=hortusfox -DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) -$STD mariadb -u root -e "CREATE DATABASE $DB_NAME;" -$STD mariadb -u root -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';" -$STD mariadb -u root -e "GRANT ALL PRIVILEGES ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" -{ - echo "HortusFox Database Credentials" - echo "Database: $DB_NAME" - echo "Username: $DB_USER" - echo "Password: $DB_PASS" -} >>~/hortusfox.creds -msg_ok "Set up database" - -fetch_and_deploy_gh_release "hortusfox" "danielbrendel/hortusfox-web" - -msg_info "Configuring .env" -cp /opt/hortusfox/.env.example /opt/hortusfox/.env -sed -i "s|^DB_HOST=.*|DB_HOST=localhost|" /opt/hortusfox/.env -sed -i "s|^DB_USER=.*|DB_USER=$DB_USER|" /opt/hortusfox/.env -sed -i "s|^DB_PASSWORD=.*|DB_PASSWORD=$DB_PASS|" /opt/hortusfox/.env -sed -i "s|^DB_DATABASE=.*|DB_DATABASE=$DB_NAME|" /opt/hortusfox/.env -sed -i "s|^DB_ENABLE=.*|DB_ENABLE=true|" /opt/hortusfox/.env -sed -i "s|^APP_TIMEZONE=.*|APP_TIMEZONE=Europe/Berlin|" /opt/hortusfox/.env -msg_ok ".env configured" - -msg_info "Installing Composer dependencies" -cd /opt/hortusfox -$STD composer install --no-dev --optimize-autoloader -msg_ok "Composer dependencies installed" - -msg_info "Running DB migration" -php asatru migrate:fresh -msg_ok "Migration finished" - -msg_info "Setting up HortusFox" -$STD mariadb -u root -D $DB_NAME -e "INSERT IGNORE INTO AppModel (workspace, language, created_at) VALUES ('Default Workspace', 'en', NOW());" -php asatru plants:attributes -php asatru calendar:classes -ADMIN_EMAIL="admin@example.com" -ADMIN_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13)" -ADMIN_HASH=$(php -r "echo password_hash('$ADMIN_PASS', PASSWORD_BCRYPT);") -$STD mariadb -u root -D $DB_NAME -e "INSERT IGNORE INTO UserModel (name, email, password, admin) VALUES ('Admin', '$ADMIN_EMAIL', '$ADMIN_HASH', 1);" -{ - echo "" - echo "HortusFox-Admin-Creds:" - echo "E-Mail: $ADMIN_EMAIL" - echo "Passwort: $ADMIN_PASS" -} >>~/hortusfox.creds -$STD mariadb -u root -D $DB_NAME -e "INSERT IGNORE INTO LocationsModel (name, active, created_at) VALUES ('Home', 1, NOW());" -msg_ok "Set up HortusFox" - -msg_info "Configuring Apache vHost" -cat </etc/apache2/sites-available/hortusfox.conf - - ServerAdmin webmaster@localhost - DocumentRoot /opt/hortusfox/public - - Options Indexes FollowSymLinks - AllowOverride All - Require all granted - - ErrorLog \${APACHE_LOG_DIR}/hortusfox_error.log - CustomLog \${APACHE_LOG_DIR}/hortusfox_access.log combined - -EOF -$STD a2dissite 000-default -$STD a2ensite hortusfox -$STD a2enmod rewrite -systemctl restart apache2 -msg_ok "Apache configured" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" diff --git a/install/palmr-install.sh b/install/palmr-install.sh deleted file mode 100644 index 30759aa23..000000000 --- a/install/palmr-install.sh +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2025 Community Scripts ORG -# Author: vhsdream -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/kyantech/Palmr - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing dependencies" -$STD apt-get install yq -y -msg_ok "Installed dependencies" - -fetch_and_deploy_gh_release "Palmr" "kyantech/Palmr" "tarball" "latest" "/opt/palmr" -PNPM="$(jq -r '.packageManager' /opt/palmr/package.json)" -NODE_VERSION="20" NODE_MODULE="$PNPM" setup_nodejs - -msg_info "Configuring palmr backend" -PALMR_DIR="/opt/palmr_data" -mkdir -p "$PALMR_DIR" -PALMR_DB="${PALMR_DIR}/palmr.db" -PALMR_KEY="$(openssl rand -hex 32)" -cd /opt/palmr/apps/server -sed -e 's/_ENCRYPTION=true/_ENCRYPTION=false/' \ - -e '/^# ENC/s/# //' \ - -e "s/ENCRYPTION_KEY=.*$/ENCRYPTION_KEY=$PALMR_KEY/" \ - -e "s|file:.*$|file:$PALMR_DB\"|" \ - -e '/db"$/a\# Uncomment below when using reverse proxy\ -# SECURE_SITE=true' \ - .env.example >./.env -$STD pnpm install -$STD pnpm dlx prisma generate -$STD pnpm dlx prisma migrate deploy -$STD pnpm dlx prisma db push -$STD pnpm db:seed -$STD pnpm build -msg_ok "Configured palmr backend" - -msg_info "Configuring palmr frontend" -cd /opt/palmr/apps/web -mv ./.env.example ./.env -export NODE_ENV=production -export NEXT_TELEMETRY_DISABLED=1 -$STD pnpm install -$STD pnpm build -msg_ok "Configured palmr frontend" - -msg_info "Creating user & services" -useradd -d "$PALMR_DIR" -M -s /usr/sbin/nologin -U palmr -chown -R palmr:palmr "$PALMR_DIR" /opt/palmr -cat </etc/systemd/system/palmr-backend.service -[Unit] -Description=palmr Backend Service -After=network.target - -[Service] -Type=simple -User=palmr -Group=palmr -WorkingDirectory=/opt/palmr_data -ExecStart=/usr/bin/node /opt/palmr/apps/server/dist/server.js - -[Install] -WantedBy=multi-user.target -EOF - -cat </etc/systemd/system/palmr-frontend.service -[Unit] -Description=palmr Frontend Service -After=network.target palmr-backend.service - -[Service] -Type=simple -User=palmr -Group=palmr -WorkingDirectory=/opt/palmr/apps/web -ExecStart=/usr/bin/pnpm start - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now palmr-backend palmr-frontend -msg_ok "Created user & services" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" diff --git a/install/tandoor-install.sh b/install/tandoor-install.sh deleted file mode 100644 index a70621a8b..000000000 --- a/install/tandoor-install.sh +++ /dev/null @@ -1,143 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: MickLesk (Canbiz) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://tandoor.dev/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies (Patience)" -$STD apt-get install -y --no-install-recommends \ - build-essential \ - python3 \ - libpq-dev \ - libmagic-dev \ - libzbar0 \ - nginx \ - libsasl2-dev \ - libldap2-dev \ - libssl-dev \ - pkg-config \ - libxmlsec1-dev \ - libxml2-dev \ - libxmlsec1-openssl -msg_ok "Installed Dependencies" - -NODE_VERSION="20" NODE_MODULE="yarn" setup_nodejs -fetch_and_deploy_gh_release "tandoor" "TandoorRecipes/recipes" "tarball" "latest" "/opt/tandoor" -PG_VERSION="16" setup_postgresql -PYTHON_VERSION="3.13" setup_uv - -msg_info "Set up PostgreSQL Database" -DB_NAME=db_recipes -DB_USER=tandoor -SECRET_KEY=$(openssl rand -base64 45 | sed 's/\//\\\//g') -DB_PASS="$(openssl rand -base64 18 | cut -c1-13)" -$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" -$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER TEMPLATE template0;" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC'" -{ - echo "Tandoor-Credentials" - echo "Tandoor Database Name: $DB_NAME" - echo "Tandoor Database User: $DB_USER" - echo "Tandoor Database Password: $DB_PASS" -} >>~/tandoor.creds -msg_ok "Set up PostgreSQL Database" - -msg_info "Setup Tandoor" -mkdir -p /opt/tandoor/{config,api,mediafiles,staticfiles} -cd /opt/tandoor -$STD uv venv .venv --python=python3 -$STD uv pip install -r requirements.txt --python .venv/bin/python -cd /opt/tandoor/vue3 -$STD yarn install -$STD yarn build -cat </opt/tandoor/.env -SECRET_KEY=$SECRET_KEY -TZ=Europe/Berlin - -DB_ENGINE=django.db.backends.postgresql -POSTGRES_HOST=localhost -POSTGRES_DB=$DB_NAME -POSTGRES_PORT=5432 -POSTGRES_USER=$DB_USER -POSTGRES_PASSWORD=$DB_PASS - -STATIC_URL=/staticfiles/ -MEDIA_URL=/mediafiles/ -EOF - -TANDOOR_VERSION="$(curl -s https://api.github.com/repos/TandoorRecipes/recipes/releases/latest | jq -r .tag_name)" -cat </opt/tandoor/cookbook/version_info.py -TANDOOR_VERSION = "$TANDOOR_VERSION" -TANDOOR_REF = "bare-metal" -VERSION_INFO = [] -EOF - -cd /opt/tandoor -$STD /opt/tandoor/.venv/bin/python manage.py migrate -$STD /opt/tandoor/.venv/bin/python manage.py collectstatic --no-input -msg_ok "Installed Tandoor" - -msg_info "Creating Services" -cat </etc/systemd/system/tandoor.service -[Unit] -Description=gunicorn daemon for tandoor -After=network.target - -[Service] -Type=simple -Restart=always -RestartSec=3 -WorkingDirectory=/opt/tandoor -EnvironmentFile=/opt/tandoor/.env -ExecStart=/opt/tandoor/.venv/bin/gunicorn --error-logfile /tmp/gunicorn_err.log --log-level debug --capture-output --bind unix:/opt/tandoor/tandoor.sock recipes.wsgi:application - -[Install] -WantedBy=multi-user.target -EOF - -cat <<'EOF' >/etc/nginx/conf.d/tandoor.conf -server { - listen 8002; - access_log /var/log/nginx/access.log; - error_log /var/log/nginx/error.log; - client_max_body_size 128M; - # serve media files - location /static/ { - alias /opt/tandoor/staticfiles/; - } - - location /media/ { - alias /opt/tandoor/mediafiles/; - } - - location / { - proxy_set_header Host $http_host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_pass http://unix:/opt/tandoor/tandoor.sock; - } -} -EOF -systemctl reload nginx -systemctl enable -q --now tandoor -msg_ok "Created Services" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" diff --git a/install/victoriametrics-install.sh b/install/victoriametrics-install.sh deleted file mode 100644 index 82a93ade8..000000000 --- a/install/victoriametrics-install.sh +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Slaviša Arežina (tremor021) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/VictoriaMetrics/VictoriaMetrics - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Getting latest version of VictoriaMetrics" -victoriametrics_filename=$(curl -fsSL "https://api.github.com/repos/VictoriaMetrics/VictoriaMetrics/releases/latest" | - jq -r '.assets[].name' | - grep -E '^victoria-metrics-linux-amd64-v[0-9.]+\.tar\.gz$') -vmutils_filename=$(curl -fsSL "https://api.github.com/repos/VictoriaMetrics/VictoriaMetrics/releases/latest" | - jq -r '.assets[].name' | - grep -E '^vmutils-linux-amd64-v[0-9.]+\.tar\.gz$') -msg_ok "Got latest version of VictoriaMetrics" - -fetch_and_deploy_gh_release "victoriametrics" "VictoriaMetrics/VictoriaMetrics" "prebuild" "latest" "/opt/victoriametrics" "$victoriametrics_filename" -fetch_and_deploy_gh_release "vmutils" "VictoriaMetrics/VictoriaMetrics" "prebuild" "latest" "/opt/victoriametrics" "$vmutils_filename" - -read -r -p "${TAB3}Would you like to add VictoriaLogs? " prompt - -if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then - fetch_and_deploy_gh_release "victorialogs" "VictoriaMetrics/VictoriaLogs" "prebuild" "latest" "/opt/victoriametrics" "victoria-logs-linux-amd64*.tar.gz" - fetch_and_deploy_gh_release "vlutils" "VictoriaMetrics/VictoriaLogs" "prebuild" "latest" "/opt/victoriametrics" "vlutils-linux-amd64*.tar.gz" -fi - -msg_info "Setup VictoriaMetrics" -mkdir -p /opt/victoriametrics/data -chmod +x /opt/victoriametrics/* -msg_ok "Setup VictoriaMetrics" - -msg_info "Creating Service" -cat </etc/systemd/system/victoriametrics.service -[Unit] -Description=VictoriaMetrics Service - -[Service] -Type=simple -Restart=always -User=root -WorkingDirectory=/opt/victoriametrics -ExecStart=/opt/victoriametrics/victoria-metrics-prod --storageDataPath="/opt/victoriametrics/data" - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now victoriametrics - -if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then - cat </etc/systemd/system/victoriametrics-logs.service -[Unit] -Description=VictoriaMetrics Service - -[Service] -Type=simple -Restart=always -User=root -WorkingDirectory=/opt/victoriametrics -ExecStart=/opt/victoriametrics/victoria-logs-prod - -[Install] -WantedBy=multi-user.target -EOF - systemctl enable -q --now victoriametrics-logs -fi -msg_ok "Created Service" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" From c2c4e3c1bf7dc27bba4618d213530e0788851a25 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 8 Aug 2025 06:05:28 +0000 Subject: [PATCH 0453/1733] Update .app files --- ct/headers/alpine-teamspeak-server | 6 ------ ct/headers/hortusfox | 6 ------ ct/headers/jeedom | 6 ------ ct/headers/palmr | 6 ------ ct/headers/tandoor | 6 ------ ct/headers/victoriametrics | 6 ------ 6 files changed, 36 deletions(-) delete mode 100644 ct/headers/alpine-teamspeak-server delete mode 100644 ct/headers/hortusfox delete mode 100644 ct/headers/jeedom delete mode 100644 ct/headers/palmr delete mode 100644 ct/headers/tandoor delete mode 100644 ct/headers/victoriametrics diff --git a/ct/headers/alpine-teamspeak-server b/ct/headers/alpine-teamspeak-server deleted file mode 100644 index cfa5aeb9f..000000000 --- a/ct/headers/alpine-teamspeak-server +++ /dev/null @@ -1,6 +0,0 @@ - ___ __ _ ______ _____ __ _____ - / | / /___ (_)___ ___ /_ __/__ ____ _____ ___ / ___/____ ___ ____ _/ /__ / ___/___ ______ _____ _____ - / /| | / / __ \/ / __ \/ _ \______/ / / _ \/ __ `/ __ `__ \\__ \/ __ \/ _ \/ __ `/ //_/_____\__ \/ _ \/ ___/ | / / _ \/ ___/ - / ___ |/ / /_/ / / / / / __/_____/ / / __/ /_/ / / / / / /__/ / /_/ / __/ /_/ / ,< /_____/__/ / __/ / | |/ / __/ / -/_/ |_/_/ .___/_/_/ /_/\___/ /_/ \___/\__,_/_/ /_/ /_/____/ .___/\___/\__,_/_/|_| /____/\___/_/ |___/\___/_/ - /_/ /_/ diff --git a/ct/headers/hortusfox b/ct/headers/hortusfox deleted file mode 100644 index 617179884..000000000 --- a/ct/headers/hortusfox +++ /dev/null @@ -1,6 +0,0 @@ - __ __ __ ______ - / / / /___ _____/ /___ _______/ ____/___ _ __ - / /_/ / __ \/ ___/ __/ / / / ___/ /_ / __ \| |/_/ - / __ / /_/ / / / /_/ /_/ (__ ) __/ / /_/ /> < -/_/ /_/\____/_/ \__/\__,_/____/_/ \____/_/|_| - diff --git a/ct/headers/jeedom b/ct/headers/jeedom deleted file mode 100644 index 47ff4dfb6..000000000 --- a/ct/headers/jeedom +++ /dev/null @@ -1,6 +0,0 @@ - __ __ - / /__ ___ ____/ /___ ____ ___ - __ / / _ \/ _ \/ __ / __ \/ __ `__ \ -/ /_/ / __/ __/ /_/ / /_/ / / / / / / -\____/\___/\___/\__,_/\____/_/ /_/ /_/ - diff --git a/ct/headers/palmr b/ct/headers/palmr deleted file mode 100644 index c485f3c60..000000000 --- a/ct/headers/palmr +++ /dev/null @@ -1,6 +0,0 @@ - ____ __ - / __ \____ _/ /___ ___ _____ - / /_/ / __ `/ / __ `__ \/ ___/ - / ____/ /_/ / / / / / / / / -/_/ \__,_/_/_/ /_/ /_/_/ - diff --git a/ct/headers/tandoor b/ct/headers/tandoor deleted file mode 100644 index 7f2db76a1..000000000 --- a/ct/headers/tandoor +++ /dev/null @@ -1,6 +0,0 @@ - ______ __ - /_ __/___ _____ ____/ /___ ____ _____ - / / / __ `/ __ \/ __ / __ \/ __ \/ ___/ - / / / /_/ / / / / /_/ / /_/ / /_/ / / -/_/ \__,_/_/ /_/\__,_/\____/\____/_/ - diff --git a/ct/headers/victoriametrics b/ct/headers/victoriametrics deleted file mode 100644 index 5e705603d..000000000 --- a/ct/headers/victoriametrics +++ /dev/null @@ -1,6 +0,0 @@ - _ ___ __ _ __ ___ __ _ -| | / (_)____/ /_____ _____(_)___ _/ |/ /__ / /______(_)_________ -| | / / / ___/ __/ __ \/ ___/ / __ `/ /|_/ / _ \/ __/ ___/ / ___/ ___/ -| |/ / / /__/ /_/ /_/ / / / / /_/ / / / / __/ /_/ / / / /__(__ ) -|___/_/\___/\__/\____/_/ /_/\__,_/_/ /_/\___/\__/_/ /_/\___/____/ - From b69da7ec62656545e2925e304f15ab627c84f898 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 8 Aug 2025 08:11:12 +0200 Subject: [PATCH 0454/1733] Update debian-install.sh --- install/debian-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/debian-install.sh b/install/debian-install.sh index e62591849..fcca74fcc 100644 --- a/install/debian-install.sh +++ b/install/debian-install.sh @@ -17,7 +17,7 @@ msg_info "Installing Dependencies" $STD apt-get install -y gpg msg_ok "Installed Dependencies" -#setup_mariadb +setup_mariadb #FFMPEG_VERSION="n7.1.1" FFMPEG_TYPE="full" setup_ffmpeg From 65ea17d3ea4e1f2d0e0434dd285d04fe7d9545f4 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 8 Aug 2025 08:21:38 +0200 Subject: [PATCH 0455/1733] Update tools.func --- misc/tools.func | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index b6451ad19..7091241f6 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -252,7 +252,7 @@ setup_mariadb() { fi msg_info "Setting up MariaDB $MARIADB_VERSION" - # grab dynamic latest LTS version + # Grab dynamic latest LTS version if [[ "$MARIADB_VERSION" == "latest" ]]; then MARIADB_VERSION=$(curl -fsSL http://mirror.mariadb.org/repo/ | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+/' | @@ -295,7 +295,18 @@ setup_mariadb() { >/etc/apt/sources.list.d/mariadb.list $STD apt-get update - $STD apt-get install -y mariadb-server mariadb-client + + local MARIADB_MAJOR_MINOR + MARIADB_MAJOR_MINOR=$(echo "$MARIADB_VERSION" | awk -F. '{print $1"."$2}') + if [[ -n "$MARIADB_MAJOR_MINOR" ]]; then + echo "mariadb-server-$MARIADB_MAJOR_MINOR mariadb-server/feedback boolean false" | debconf-set-selections + else + for ver in 12.1 12.0 11.4 11.3 11.2 11.1 11.0 10.11 10.6 10.5 10.4 10.3; do + echo "mariadb-server-$ver mariadb-server/feedback boolean false" | debconf-set-selections + done + fi + + DEBIAN_FRONTEND=noninteractive $STD apt-get install -y mariadb-server mariadb-client msg_ok "Setup MariaDB $MARIADB_VERSION" } From 1585751e3f991b543e0571808a241cdfa1faf7bf Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 8 Aug 2025 08:27:42 +0200 Subject: [PATCH 0456/1733] Update tools.func --- misc/tools.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/tools.func b/misc/tools.func index 7091241f6..f71f973f8 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -305,7 +305,7 @@ setup_mariadb() { echo "mariadb-server-$ver mariadb-server/feedback boolean false" | debconf-set-selections done fi - + echo "Test" DEBIAN_FRONTEND=noninteractive $STD apt-get install -y mariadb-server mariadb-client msg_ok "Setup MariaDB $MARIADB_VERSION" From e11e94e10445997c2314482668d2596dc9d4e46e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 8 Aug 2025 08:57:11 +0200 Subject: [PATCH 0457/1733] Update tools.func --- misc/tools.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/tools.func b/misc/tools.func index f71f973f8..5930d86b4 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -305,7 +305,7 @@ setup_mariadb() { echo "mariadb-server-$ver mariadb-server/feedback boolean false" | debconf-set-selections done fi - echo "Test" + echo "Test1" DEBIAN_FRONTEND=noninteractive $STD apt-get install -y mariadb-server mariadb-client msg_ok "Setup MariaDB $MARIADB_VERSION" From e933af8dfdaf7f2a6d4cc1096dc792637ff1d102 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 8 Aug 2025 08:56:48 +0200 Subject: [PATCH 0458/1733] Update push-to-gitea.yml --- .github/workflows/push-to-gitea.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/push-to-gitea.yml b/.github/workflows/push-to-gitea.yml index 76268ffc7..33118f075 100644 --- a/.github/workflows/push-to-gitea.yml +++ b/.github/workflows/push-to-gitea.yml @@ -21,7 +21,7 @@ jobs: git config --global user.name "Push From Github" git config --global user.email "actions@github.com" git remote add gitea https://$GITEA_USER:$GITEA_TOKEN@git.community-scripts.org/community-scripts/ProxmoxVED.git - git push gitea --all + git push gitea --force-with-lease gitea --all env: GITEA_USER: ${{ secrets.GITEA_USERNAME }} GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} From 91450991343eb44469381354d1de1f4e8df51ca1 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 8 Aug 2025 09:03:49 +0200 Subject: [PATCH 0459/1733] Update tools.func --- misc/tools.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/tools.func b/misc/tools.func index 5930d86b4..f71f973f8 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -305,7 +305,7 @@ setup_mariadb() { echo "mariadb-server-$ver mariadb-server/feedback boolean false" | debconf-set-selections done fi - echo "Test1" + echo "Test" DEBIAN_FRONTEND=noninteractive $STD apt-get install -y mariadb-server mariadb-client msg_ok "Setup MariaDB $MARIADB_VERSION" From 0dc063e73672126b33b265cc0848923f94abda8b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 8 Aug 2025 09:03:44 +0200 Subject: [PATCH 0460/1733] Update push-to-gitea.yml --- .github/workflows/push-to-gitea.yml | 36 ++++++++++++++++++----------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/.github/workflows/push-to-gitea.yml b/.github/workflows/push-to-gitea.yml index 33118f075..3df681d19 100644 --- a/.github/workflows/push-to-gitea.yml +++ b/.github/workflows/push-to-gitea.yml @@ -11,17 +11,25 @@ jobs: runs-on: ubuntu-latest steps: - - name: Checkout source repo - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Push to Gitea - run: | - git config --global user.name "Push From Github" - git config --global user.email "actions@github.com" - git remote add gitea https://$GITEA_USER:$GITEA_TOKEN@git.community-scripts.org/community-scripts/ProxmoxVED.git - git push gitea --force-with-lease gitea --all - env: - GITEA_USER: ${{ secrets.GITEA_USERNAME }} - GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} + - name: Checkout source repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Add Gitea remote + run: git remote add gitea https://$GITEA_USER:$GITEA_TOKEN@git.community-scripts.org/community-scripts/ProxmoxVED.git + env: + GITEA_USER: ${{ secrets.GITEA_USERNAME }} + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} + - name: Pull Gitea changes + run: | + git fetch gitea + git rebase gitea/main + env: + GITEA_USER: ${{ secrets.GITEA_USERNAME }} + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} + + - name: Push to Gitea + run: git push gitea --all + env: + GITEA_USER: ${{ secrets.GITEA_USERNAME }} + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} From 3af73cf81c7092a7ebe6c4e094d28714ee124ab5 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 8 Aug 2025 09:05:07 +0200 Subject: [PATCH 0461/1733] Update push-to-gitea.yml --- .github/workflows/push-to-gitea.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/push-to-gitea.yml b/.github/workflows/push-to-gitea.yml index 3df681d19..73d9a72b2 100644 --- a/.github/workflows/push-to-gitea.yml +++ b/.github/workflows/push-to-gitea.yml @@ -15,6 +15,10 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 + - name: Set Git identity for actions + run: | + git config --global user.name "Push From Github" + git config --global user.email "actions@github.com" - name: Add Gitea remote run: git remote add gitea https://$GITEA_USER:$GITEA_TOKEN@git.community-scripts.org/community-scripts/ProxmoxVED.git env: From c52a12df6aba7b30927f046b2bc8145ad69ae7ed Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 8 Aug 2025 09:05:44 +0200 Subject: [PATCH 0462/1733] Update tools.func --- misc/tools.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/tools.func b/misc/tools.func index f71f973f8..9f2a3c934 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -305,7 +305,7 @@ setup_mariadb() { echo "mariadb-server-$ver mariadb-server/feedback boolean false" | debconf-set-selections done fi - echo "Test" + echo "Test2" DEBIAN_FRONTEND=noninteractive $STD apt-get install -y mariadb-server mariadb-client msg_ok "Setup MariaDB $MARIADB_VERSION" From 8adbaba12ea3c047e90810a59cc762f0f8c77f39 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 8 Aug 2025 09:09:53 +0200 Subject: [PATCH 0463/1733] Update tools.func --- misc/tools.func | 1 - 1 file changed, 1 deletion(-) diff --git a/misc/tools.func b/misc/tools.func index 9f2a3c934..88a4faa73 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -305,7 +305,6 @@ setup_mariadb() { echo "mariadb-server-$ver mariadb-server/feedback boolean false" | debconf-set-selections done fi - echo "Test2" DEBIAN_FRONTEND=noninteractive $STD apt-get install -y mariadb-server mariadb-client msg_ok "Setup MariaDB $MARIADB_VERSION" From 65df148879d19b813cbe2cf5664f17d2d01f2c7d Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Tue, 12 Aug 2025 08:41:24 +0200 Subject: [PATCH 0464/1733] Update tracktor.sh --- ct/tracktor.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/tracktor.sh b/ct/tracktor.sh index 861de2b93..9cedd334c 100644 --- a/ct/tracktor.sh +++ b/ct/tracktor.sh @@ -41,7 +41,7 @@ function update_script() { msg_info "Updating ${APP}" rm -rf /opt/tracktor setup_nodejs - fetch_and_deploy_gh_release "tracktor" "javedh-dev/tracktor" + fetch_and_deploy_gh_release "tracktor" "javedh-dev/tracktor" "tarball" "latest" "/opt/tracktor" cd /opt/tracktor rm package-lock.json $STD npm install From 991d14549b9b39b71c461a6a50b8b9725b926b97 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Tue, 12 Aug 2025 08:42:26 +0200 Subject: [PATCH 0465/1733] Update tracktor-install.sh --- install/tracktor-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/tracktor-install.sh b/install/tracktor-install.sh index 5b709e64e..d2c5fde5f 100644 --- a/install/tracktor-install.sh +++ b/install/tracktor-install.sh @@ -14,7 +14,7 @@ network_check update_os setup_nodejs -fetch_and_deploy_gh_release "tracktor" "javedh-dev/tracktor" +fetch_and_deploy_gh_release "tracktor" "javedh-dev/tracktor" "tarball" "latest" "/opt/tracktor" msg_info "Configuring Tracktor" cd /opt/tracktor From 6a466332a654253cf3ec376c82853cbfd13e2e2f Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Tue, 12 Aug 2025 10:37:24 +0200 Subject: [PATCH 0466/1733] Update tracktor.sh --- ct/tracktor.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/tracktor.sh b/ct/tracktor.sh index 9cedd334c..4414a965f 100644 --- a/ct/tracktor.sh +++ b/ct/tracktor.sh @@ -28,7 +28,7 @@ function update_script() { exit fi - RELEASE=$(curl -fsSL https://api.github.com/repos/javedh-dev/tracktor/releases/latest | jq '.tag_name' | sed 's/^v//') + RELEASE=$(curl -fsSL https://api.github.com/repos/javedh-dev/tracktor/releases/latest | jq -r '.tag_name' | sed 's/^v//') if [[ "${RELEASE}" != "$(cat ~/.tracktor 2>/dev/null)" ]] || [[ ! -f ~/.tracktor ]]; then msg_info "Stopping Service" systemctl stop tracktor From fffce78c5a64720bd64f69a148e25118da4cde67 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Tue, 12 Aug 2025 10:39:26 +0200 Subject: [PATCH 0467/1733] Update tracktor.sh --- ct/tracktor.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ct/tracktor.sh b/ct/tracktor.sh index 4414a965f..7d05a67b0 100644 --- a/ct/tracktor.sh +++ b/ct/tracktor.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +source <(curl -FsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -39,7 +39,6 @@ function update_script() { msg_ok "Created Backup" msg_info "Updating ${APP}" - rm -rf /opt/tracktor setup_nodejs fetch_and_deploy_gh_release "tracktor" "javedh-dev/tracktor" "tarball" "latest" "/opt/tracktor" cd /opt/tracktor From d97b2997ef36eef5507147778f11b72734c3f6ba Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Tue, 12 Aug 2025 10:47:54 +0200 Subject: [PATCH 0468/1733] Update tracktor.sh --- ct/tracktor.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/tracktor.sh b/ct/tracktor.sh index 7d05a67b0..864210e38 100644 --- a/ct/tracktor.sh +++ b/ct/tracktor.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -FsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: CrazyWolf13 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE From d2e9b6152d57801d89fe5389d0b8229f9fef8906 Mon Sep 17 00:00:00 2001 From: Andrew Baumbach Date: Mon, 28 Jul 2025 15:59:36 -0400 Subject: [PATCH 0469/1733] chore: bring over connector scripts from main repo --- ct/twingate-connector.sh | 46 +++++++++++++ frontend/public/json/twingate-connector.json | 40 +++++++++++ install/twingate-connector-install.sh | 70 ++++++++++++++++++++ 3 files changed, 156 insertions(+) create mode 100644 ct/twingate-connector.sh create mode 100644 frontend/public/json/twingate-connector.json create mode 100644 install/twingate-connector-install.sh diff --git a/ct/twingate-connector.sh b/ct/twingate-connector.sh new file mode 100644 index 000000000..f87be0a02 --- /dev/null +++ b/ct/twingate-connector.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: twingate-andrewb +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://www.twingate.com/docs/ + +APP="twingate-connector" +var_tags="${var_tags:-network;connector;twingate}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-2}" +var_os="${var_os:-ubuntu}" +var_version="${var_version:-22.04}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -f /lib/systemd/system/twingate-connector.service ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + msg_info "Updating ${APP}" + + apt update + apt install -yq twingate-connector + systemctl restart twingate-connector + + msg_ok "Updated Successfully" + exit +} + +start +build_container +description + +msg_ok "All Finished! If you need to update your access or refresh tokens, they can be found in /etc/twingate/connector.conf" diff --git a/frontend/public/json/twingate-connector.json b/frontend/public/json/twingate-connector.json new file mode 100644 index 000000000..55eeaacbd --- /dev/null +++ b/frontend/public/json/twingate-connector.json @@ -0,0 +1,40 @@ +{ + "name": "twingate-connector", + "slug": "twingate-connector", + "categories": [ + 4 + ], + "date_created": "2025-07-17", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": null, + "documentation": "https://www.twingate.com/docs/", + "config_path": "/etc/twingate/connector.conf", + "website": "https://www.twingate.com", + "logo": "https://avatars.githubusercontent.com/u/97470429?s=200&v=4", + "description": "Twingate Connectors are lightweight software components that establish secure, least-privileged access between private network resources and authorized users without exposing the network to the internet. They act as outbound-only bridges between your protected resources and the Twingate infrastructure, ensuring zero-trust access without the need for a VPN.", + "install_methods": [ + { + "type": "default", + "script": "ct/twingate-connector.sh", + "resources": { + "cpu": 1, + "ram": 2048, + "hdd": 2, + "os": "Ubuntu", + "version": "22.04" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "If you need to update your access or refresh tokens, they can be found in /etc/twingate/connector.conf", + "type": "info" + } + ] +} diff --git a/install/twingate-connector-install.sh b/install/twingate-connector-install.sh new file mode 100644 index 000000000..7e82fd2fc --- /dev/null +++ b/install/twingate-connector-install.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: [Andrew Baumbach] +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: [https://raw.githubusercontent.com/twingate-andrewb/ProxmoxVE] // TODO: change this + +# Import Functions und Setup +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + + +while true; do + read -rp "Please enter your access token: " access_token + if [[ -z "$access_token" ]]; then + msg_error "Access token cannot be empty. Please try again." + else + break + fi +done + +while true; do + read -rp "Please enter your refresh token: " refresh_token + if [[ -z "$refresh_token" ]]; then + msg_error "Refresh token cannot be empty. Please try again." + else + break + fi +done + +while true; do + read -rp "Please enter your network name: " network + if [[ -z "$network" ]]; then + msg_error "Network cannot be empty. Please try again." + else + break + fi +done + +msg_info "Installing Twingate Connector..." +export TWINGATE_ACCESS_TOKEN="${access_token}" +export TWINGATE_REFRESH_TOKEN="${refresh_token}" +export TWINGATE_NETWORK="${network}" +export TWINGATE_LABEL_DEPLOYED_BY="proxmox" +curl -fsSL "https://binaries.twingate.com/connector/setup.sh" | bash >> /dev/null 2>&1 +if [[ $? -ne 0 ]]; then + msg_error "Failed to set up Twingate Connector. Please double check your tokens and network name." + exit 1 +fi +msg_ok "Twingate Connector installed!" + +msg_info "Starting Twingate Connector..." +# give the connector time to start +sleep 5s +msg_ok "Twingate Connector started!" + +echo -e "${INFO}${YW} Twingate Connector status: $(systemctl status twingate-connector) ${CL}" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Done cleaning up" From c4bedd9d280f96d408f0e7bfc257ffab830763ec Mon Sep 17 00:00:00 2001 From: Andrew Baumbach Date: Tue, 29 Jul 2025 09:45:12 -0400 Subject: [PATCH 0470/1733] chore: update author info --- install/twingate-connector-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/twingate-connector-install.sh b/install/twingate-connector-install.sh index 7e82fd2fc..6b267a4bc 100644 --- a/install/twingate-connector-install.sh +++ b/install/twingate-connector-install.sh @@ -1,9 +1,9 @@ #!/usr/bin/env bash # Copyright (c) 2021-2025 community-scripts ORG -# Author: [Andrew Baumbach] +# Author: twingate-andrewb # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: [https://raw.githubusercontent.com/twingate-andrewb/ProxmoxVE] // TODO: change this +# Source: https://www.twingate.com/docs/ # Import Functions und Setup source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" From de8994f60f60cab33a663e7c959c55a857d0cfba Mon Sep 17 00:00:00 2001 From: Andrew Baumbach Date: Wed, 6 Aug 2025 12:14:15 -0400 Subject: [PATCH 0471/1733] chore: remove dependency on external script --- install/twingate-connector-install.sh | 71 +++++++++++---------------- 1 file changed, 29 insertions(+), 42 deletions(-) diff --git a/install/twingate-connector-install.sh b/install/twingate-connector-install.sh index 6b267a4bc..9f114c701 100644 --- a/install/twingate-connector-install.sh +++ b/install/twingate-connector-install.sh @@ -1,11 +1,10 @@ #!/usr/bin/env bash # Copyright (c) 2021-2025 community-scripts ORG -# Author: twingate-andrewb +# Author: MickLesk (CanbiZ), twingate-andrewb # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.twingate.com/docs/ -# Import Functions und Setup source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 @@ -14,52 +13,40 @@ setting_up_container network_check update_os +install -d -m 0700 /etc/twingate -while true; do - read -rp "Please enter your access token: " access_token - if [[ -z "$access_token" ]]; then - msg_error "Access token cannot be empty. Please try again." - else - break - fi +while [[ -z "$access_token" ]]; do + read -rp "${TAB3}Please enter your access token: " access_token +done +while [[ -z "$refresh_token" ]]; do + read -rp "${TAB3}Please enter your refresh token: " refresh_token +done +while [[ -z "$network" ]]; do + read -rp "${TAB3}Please enter your network name: " network done -while true; do - read -rp "Please enter your refresh token: " refresh_token - if [[ -z "$refresh_token" ]]; then - msg_error "Refresh token cannot be empty. Please try again." - else - break - fi -done +msg_info "Setup Twingate Repository" +curl -fsSL "https://packages.twingate.com/apt/gpg.key" | gpg --dearmor -o /usr/share/keyrings/twingate-connector-keyring.gpg +echo "deb [signed-by=/usr/share/keyrings/twingate-connector-keyring.gpg] https://packages.twingate.com/apt/ /" > /etc/apt/sources.list.d/twingate.list +$STD apt-get update +msg_ok "Setup Twingate Repository" -while true; do - read -rp "Please enter your network name: " network - if [[ -z "$network" ]]; then - msg_error "Network cannot be empty. Please try again." - else - break - fi -done +msg_info "Setup Twingate Connector" +$STD apt-get install -y twingate-connector +msg_ok "Setup Twingate Connector" -msg_info "Installing Twingate Connector..." -export TWINGATE_ACCESS_TOKEN="${access_token}" -export TWINGATE_REFRESH_TOKEN="${refresh_token}" -export TWINGATE_NETWORK="${network}" -export TWINGATE_LABEL_DEPLOYED_BY="proxmox" -curl -fsSL "https://binaries.twingate.com/connector/setup.sh" | bash >> /dev/null 2>&1 -if [[ $? -ne 0 ]]; then - msg_error "Failed to set up Twingate Connector. Please double check your tokens and network name." - exit 1 -fi -msg_ok "Twingate Connector installed!" +msg_info "Writing config" +{ + echo "TWINGATE_NETWORK=${network}" + echo "TWINGATE_ACCESS_TOKEN=${access_token}" + echo "TWINGATE_REFRESH_TOKEN=${refresh_token}" +} > /etc/twingate/connector.conf +chmod 600 /etc/twingate/connector.conf +msg_ok "Config written" -msg_info "Starting Twingate Connector..." -# give the connector time to start -sleep 5s -msg_ok "Twingate Connector started!" - -echo -e "${INFO}${YW} Twingate Connector status: $(systemctl status twingate-connector) ${CL}" +msg_info "Starting Service" +systemctl enable -q --now twingate-connector +msg_ok "Service started" motd_ssh customize From 1af97d01fe2bb5be8f4fed370278d9ae34e8742d Mon Sep 17 00:00:00 2001 From: Andrew Baumbach Date: Wed, 6 Aug 2025 13:17:45 -0400 Subject: [PATCH 0472/1733] fix: unbound variable bug --- install/twingate-connector-install.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/install/twingate-connector-install.sh b/install/twingate-connector-install.sh index 9f114c701..a3d95be02 100644 --- a/install/twingate-connector-install.sh +++ b/install/twingate-connector-install.sh @@ -15,6 +15,10 @@ update_os install -d -m 0700 /etc/twingate +access_token="" +refresh_token="" +network="" + while [[ -z "$access_token" ]]; do read -rp "${TAB3}Please enter your access token: " access_token done From 9f3308f88ac1e3e81d2d686355785faf2b67d873 Mon Sep 17 00:00:00 2001 From: Zack Ruppert Date: Thu, 7 Aug 2025 19:31:39 -0400 Subject: [PATCH 0473/1733] feat: Add UHF Server Script --- ct/headers/uhf | 6 +++ ct/uhf.sh | 53 +++++++++++++++++++++++++++ frontend/public/json/uhf.json | 40 ++++++++++++++++++++ install/uhf-install.sh | 69 +++++++++++++++++++++++++++++++++++ 4 files changed, 168 insertions(+) create mode 100644 ct/headers/uhf create mode 100644 ct/uhf.sh create mode 100644 frontend/public/json/uhf.json create mode 100644 install/uhf-install.sh diff --git a/ct/headers/uhf b/ct/headers/uhf new file mode 100644 index 000000000..15b4377ec --- /dev/null +++ b/ct/headers/uhf @@ -0,0 +1,6 @@ + __ ____ ____ + / / / / /_ / __/ + / / / / __ \/ /_ +/ /_/ / / / / __/ +\____/_/ /_/_/ + diff --git a/ct/uhf.sh b/ct/uhf.sh new file mode 100644 index 000000000..d980b3364 --- /dev/null +++ b/ct/uhf.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: zackwithak13 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://www.uhfapp.com/server + +APP="UHF" +var_tags="${var_tags:-media}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-8}" +var_os="${var_os:-ubuntu}" +var_version="${var_version:-24.04}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/uhf-server ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Stopping ${APP}" + systemctl stop uhf-server + msg_ok "Stopped ${APP}" + + msg_info "Updating ${APP}" + fetch_and_deploy_gh_release "comskip" "swapplications/comskip" "prebuild" "latest" "/opt/comskip" "comskip-x64-*.zip" + fetch_and_deploy_gh_release "uhf-server" "swapplications/uhf-server-dist" "prebuild" "latest" "/opt/uhf-server" "UHF.Server-linux-x64-*.zip" + msg_ok "Updated ${APP}" + + msg_info "Starting ${APP}" + systemctl start uhf-server + msg_ok "Started ${APP}" + msg_ok "Updated Successfully" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:7568${CL}" diff --git a/frontend/public/json/uhf.json b/frontend/public/json/uhf.json new file mode 100644 index 000000000..01db49927 --- /dev/null +++ b/frontend/public/json/uhf.json @@ -0,0 +1,40 @@ +{ + "name": "UHF Server", + "slug": "uhf", + "categories": [ + 13 + ], + "date_created": "2025-08-07", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 7568, + "documentation": "https://www.uhfapp.com/server", + "website": "https://www.uhfapp.com/", + "logo": "https://framerusercontent.com/images/PF6RdbR6G8CxEeMglfuiHg4MGJs.png", + "config_path": "/etc/uhf-server/", + "description": "UHF Server is a powerful companion app that lets you seamlessly schedule and record your favorite shows from the UHF app.", + "install_methods": [ + { + "type": "default", + "script": "ct/uhf.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 8, + "os": "ubuntu", + "version": "24.04" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "With Privileged/Unprivileged Hardware Acceleration Support", + "type": "info" + } + ] +} diff --git a/install/uhf-install.sh b/install/uhf-install.sh new file mode 100644 index 000000000..d0594de18 --- /dev/null +++ b/install/uhf-install.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: zackwithak13 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://www.uhfapp.com/server + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Setting Up Hardware Acceleration" +$STD apt-get -y install {va-driver-all,ocl-icd-libopencl1,intel-opencl-icd,vainfo,intel-gpu-tools} +if [[ "$CTTYPE" == "0" ]]; then + chgrp video /dev/dri + chmod 755 /dev/dri + chmod 660 /dev/dri/* + $STD adduser $(id -u -n) video + $STD adduser $(id -u -n) render +fi +msg_ok "Set Up Hardware Acceleration" + +msg_info "Installing Dependencies" +$STD apt-get update +$STD apt-get -y install ffmpeg +msg_ok "Installed Dependencies" + +msg_info "Installing UHF Server" +mkdir -p /etc/uhf-server +mkdir -p /var/lib/uhf-server/data +mkdir -p /var/lib/uhf-server/recordings +env_path="/etc/uhf-server/.env" +echo "API_HOST=0.0.0.0 +API_PORT=7568 +RECORDINGS_DIR=/var/lib/uhf-server/recordings +DB_PATH=/var/lib/uhf-server/data/db.json +LOG_LEVEL=INFO" >"${env_path}" +fetch_and_deploy_gh_release "comskip" "swapplications/comskip" "prebuild" "latest" "/opt/comskip" "comskip-x64-*.zip" +fetch_and_deploy_gh_release "uhf-server" "swapplications/uhf-server-dist" "prebuild" "latest" "/opt/uhf-server" "UHF.Server-linux-x64-*.zip" +msg_ok "Installed UHF Server" + +msg_info "Creating Service" +service_path="/etc/systemd/system/uhf-server.service" +echo "[Unit] +Description=UHF Server service +After=syslog.target network-online.target +[Service] +Type=simple +User=root +Group=root +WorkingDirectory=/opt/uhf-server +EnvironmentFile=/etc/uhf-server/.env +ExecStart=/opt/uhf-server/uhf-server +[Install] +WantedBy=multi-user.target" >"${service_path}" +systemctl enable --now -q uhf-server.service +msg_ok "Created Service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From a559f539b180cf8cdd3f8a5d0a3c30b51d1ca532 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Fri, 8 Aug 2025 07:24:17 +0200 Subject: [PATCH 0474/1733] Delete ct/headers/uhf --- ct/headers/uhf | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 ct/headers/uhf diff --git a/ct/headers/uhf b/ct/headers/uhf deleted file mode 100644 index 15b4377ec..000000000 --- a/ct/headers/uhf +++ /dev/null @@ -1,6 +0,0 @@ - __ ____ ____ - / / / / /_ / __/ - / / / / __ \/ /_ -/ /_/ / / / / __/ -\____/_/ /_/_/ - From dc9d6cbe7874506af33e20b68c5b7784101f345c Mon Sep 17 00:00:00 2001 From: Zack Ruppert Date: Mon, 11 Aug 2025 10:58:01 -0400 Subject: [PATCH 0475/1733] Updates to address PR Requests - moved install messages - updated source to VED - update icon to use standard cdn - use cat standard instead of echo --- ct/uhf.sh | 4 +--- frontend/public/json/uhf.json | 2 +- install/uhf-install.sh | 19 ++++++++++--------- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/ct/uhf.sh b/ct/uhf.sh index d980b3364..d2f82254d 100644 --- a/ct/uhf.sh +++ b/ct/uhf.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: zackwithak13 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -34,12 +34,10 @@ function update_script() { msg_info "Updating ${APP}" fetch_and_deploy_gh_release "comskip" "swapplications/comskip" "prebuild" "latest" "/opt/comskip" "comskip-x64-*.zip" fetch_and_deploy_gh_release "uhf-server" "swapplications/uhf-server-dist" "prebuild" "latest" "/opt/uhf-server" "UHF.Server-linux-x64-*.zip" - msg_ok "Updated ${APP}" msg_info "Starting ${APP}" systemctl start uhf-server msg_ok "Started ${APP}" - msg_ok "Updated Successfully" exit } diff --git a/frontend/public/json/uhf.json b/frontend/public/json/uhf.json index 01db49927..fd9776e45 100644 --- a/frontend/public/json/uhf.json +++ b/frontend/public/json/uhf.json @@ -11,7 +11,7 @@ "interface_port": 7568, "documentation": "https://www.uhfapp.com/server", "website": "https://www.uhfapp.com/", - "logo": "https://framerusercontent.com/images/PF6RdbR6G8CxEeMglfuiHg4MGJs.png", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/uhf.webp", "config_path": "/etc/uhf-server/", "description": "UHF Server is a powerful companion app that lets you seamlessly schedule and record your favorite shows from the UHF app.", "install_methods": [ diff --git a/install/uhf-install.sh b/install/uhf-install.sh index d0594de18..44015c9a0 100644 --- a/install/uhf-install.sh +++ b/install/uhf-install.sh @@ -29,34 +29,35 @@ $STD apt-get update $STD apt-get -y install ffmpeg msg_ok "Installed Dependencies" -msg_info "Installing UHF Server" +msg_info "Setting Up UHF Server Environment" mkdir -p /etc/uhf-server mkdir -p /var/lib/uhf-server/data mkdir -p /var/lib/uhf-server/recordings -env_path="/etc/uhf-server/.env" -echo "API_HOST=0.0.0.0 +cat </etc/uhf-server/.env +API_HOST=0.0.0.0 API_PORT=7568 RECORDINGS_DIR=/var/lib/uhf-server/recordings DB_PATH=/var/lib/uhf-server/data/db.json -LOG_LEVEL=INFO" >"${env_path}" +LOG_LEVEL=INFO +EOF +msg_ok "Set Up UHF Server Environment" fetch_and_deploy_gh_release "comskip" "swapplications/comskip" "prebuild" "latest" "/opt/comskip" "comskip-x64-*.zip" fetch_and_deploy_gh_release "uhf-server" "swapplications/uhf-server-dist" "prebuild" "latest" "/opt/uhf-server" "UHF.Server-linux-x64-*.zip" -msg_ok "Installed UHF Server" msg_info "Creating Service" -service_path="/etc/systemd/system/uhf-server.service" +service_path="" +cat </etc/systemd/system/uhf-server.service echo "[Unit] Description=UHF Server service After=syslog.target network-online.target [Service] Type=simple -User=root -Group=root WorkingDirectory=/opt/uhf-server EnvironmentFile=/etc/uhf-server/.env ExecStart=/opt/uhf-server/uhf-server [Install] -WantedBy=multi-user.target" >"${service_path}" +WantedBy=multi-user.target +EOF systemctl enable --now -q uhf-server.service msg_ok "Created Service" From b846a0fb1de28c8973e4394db64b0a1b8ed32f7d Mon Sep 17 00:00:00 2001 From: Zack Ruppert Date: Mon, 11 Aug 2025 11:28:34 -0400 Subject: [PATCH 0476/1733] Remove Hardware Acceleration. UHF server ffmpeg usage is not documented well enough to enable by default. --- frontend/public/json/uhf.json | 7 +------ install/uhf-install.sh | 11 ----------- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/frontend/public/json/uhf.json b/frontend/public/json/uhf.json index fd9776e45..0429dc906 100644 --- a/frontend/public/json/uhf.json +++ b/frontend/public/json/uhf.json @@ -31,10 +31,5 @@ "username": null, "password": null }, - "notes": [ - { - "text": "With Privileged/Unprivileged Hardware Acceleration Support", - "type": "info" - } - ] + "notes": [] } diff --git a/install/uhf-install.sh b/install/uhf-install.sh index 44015c9a0..b0d8b1ff4 100644 --- a/install/uhf-install.sh +++ b/install/uhf-install.sh @@ -13,17 +13,6 @@ setting_up_container network_check update_os -msg_info "Setting Up Hardware Acceleration" -$STD apt-get -y install {va-driver-all,ocl-icd-libopencl1,intel-opencl-icd,vainfo,intel-gpu-tools} -if [[ "$CTTYPE" == "0" ]]; then - chgrp video /dev/dri - chmod 755 /dev/dri - chmod 660 /dev/dri/* - $STD adduser $(id -u -n) video - $STD adduser $(id -u -n) render -fi -msg_ok "Set Up Hardware Acceleration" - msg_info "Installing Dependencies" $STD apt-get update $STD apt-get -y install ffmpeg From 2641c82bc13b530017ff5b38c3d334c0d5751983 Mon Sep 17 00:00:00 2001 From: Zack Ruppert Date: Mon, 11 Aug 2025 11:29:20 -0400 Subject: [PATCH 0477/1733] Update OS to Debian. Install ffmpeg from source --- ct/uhf.sh | 6 +++--- install/uhf-install.sh | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/ct/uhf.sh b/ct/uhf.sh index d2f82254d..f2650ea8b 100644 --- a/ct/uhf.sh +++ b/ct/uhf.sh @@ -9,9 +9,9 @@ APP="UHF" var_tags="${var_tags:-media}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" -var_disk="${var_disk:-8}" -var_os="${var_os:-ubuntu}" -var_version="${var_version:-24.04}" +var_disk="${var_disk:-16}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" diff --git a/install/uhf-install.sh b/install/uhf-install.sh index b0d8b1ff4..a60d3c36f 100644 --- a/install/uhf-install.sh +++ b/install/uhf-install.sh @@ -14,8 +14,7 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt-get update -$STD apt-get -y install ffmpeg +setup_ffmpeg msg_ok "Installed Dependencies" msg_info "Setting Up UHF Server Environment" From 0dbd12bdfaac4be2eada108a22a957d24dfa4642 Mon Sep 17 00:00:00 2001 From: Zack Ruppert Date: Mon, 11 Aug 2025 11:46:37 -0400 Subject: [PATCH 0478/1733] Update the correct message in script --- ct/uhf.sh | 3 ++- install/uhf-install.sh | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ct/uhf.sh b/ct/uhf.sh index f2650ea8b..4a6c4b50d 100644 --- a/ct/uhf.sh +++ b/ct/uhf.sh @@ -31,13 +31,14 @@ function update_script() { systemctl stop uhf-server msg_ok "Stopped ${APP}" - msg_info "Updating ${APP}" fetch_and_deploy_gh_release "comskip" "swapplications/comskip" "prebuild" "latest" "/opt/comskip" "comskip-x64-*.zip" fetch_and_deploy_gh_release "uhf-server" "swapplications/uhf-server-dist" "prebuild" "latest" "/opt/uhf-server" "UHF.Server-linux-x64-*.zip" msg_info "Starting ${APP}" systemctl start uhf-server msg_ok "Started ${APP}" + + msg_ok "Updated Successfully" exit } diff --git a/install/uhf-install.sh b/install/uhf-install.sh index a60d3c36f..8b9ddf84d 100644 --- a/install/uhf-install.sh +++ b/install/uhf-install.sh @@ -29,6 +29,7 @@ DB_PATH=/var/lib/uhf-server/data/db.json LOG_LEVEL=INFO EOF msg_ok "Set Up UHF Server Environment" + fetch_and_deploy_gh_release "comskip" "swapplications/comskip" "prebuild" "latest" "/opt/comskip" "comskip-x64-*.zip" fetch_and_deploy_gh_release "uhf-server" "swapplications/uhf-server-dist" "prebuild" "latest" "/opt/uhf-server" "UHF.Server-linux-x64-*.zip" From 052a023fb7ba600563d4fc31641af16259e1463e Mon Sep 17 00:00:00 2001 From: Zack Ruppert Date: Mon, 11 Aug 2025 11:47:03 -0400 Subject: [PATCH 0479/1733] Add apt update to update script --- ct/uhf.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ct/uhf.sh b/ct/uhf.sh index 4a6c4b50d..3ba5f5e7d 100644 --- a/ct/uhf.sh +++ b/ct/uhf.sh @@ -31,6 +31,11 @@ function update_script() { systemctl stop uhf-server msg_ok "Stopped ${APP}" + msg_info "Updating APT packages" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Package list updated" + fetch_and_deploy_gh_release "comskip" "swapplications/comskip" "prebuild" "latest" "/opt/comskip" "comskip-x64-*.zip" fetch_and_deploy_gh_release "uhf-server" "swapplications/uhf-server-dist" "prebuild" "latest" "/opt/uhf-server" "UHF.Server-linux-x64-*.zip" @@ -38,6 +43,11 @@ function update_script() { systemctl start uhf-server msg_ok "Started ${APP}" + msg_info "Cleaning up" + $STD apt-get -y autoremove + $STD apt-get -y autoclean + msg_ok "Cleaned" + msg_ok "Updated Successfully" exit } From 296bfb349e57cc3655899295d4ea64d0fd7bdfc8 Mon Sep 17 00:00:00 2001 From: Zack Ruppert Date: Mon, 11 Aug 2025 11:48:59 -0400 Subject: [PATCH 0480/1733] Update frontend json. Change default hdd back to 8 --- ct/uhf.sh | 2 +- frontend/public/json/uhf.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ct/uhf.sh b/ct/uhf.sh index 3ba5f5e7d..308c62964 100644 --- a/ct/uhf.sh +++ b/ct/uhf.sh @@ -9,7 +9,7 @@ APP="UHF" var_tags="${var_tags:-media}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" -var_disk="${var_disk:-16}" +var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" diff --git a/frontend/public/json/uhf.json b/frontend/public/json/uhf.json index 0429dc906..d23d001a0 100644 --- a/frontend/public/json/uhf.json +++ b/frontend/public/json/uhf.json @@ -22,8 +22,8 @@ "cpu": 2, "ram": 2048, "hdd": 8, - "os": "ubuntu", - "version": "24.04" + "os": "Debian", + "version": "12" } } ], From 6600b4214c301080c4537412d50788ffdd8f570e Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 12 Aug 2025 11:11:43 +0000 Subject: [PATCH 0481/1733] Update .app files --- ct/headers/twingate-connector | 6 ++++++ ct/headers/uhf | 6 ++++++ 2 files changed, 12 insertions(+) create mode 100644 ct/headers/twingate-connector create mode 100644 ct/headers/uhf diff --git a/ct/headers/twingate-connector b/ct/headers/twingate-connector new file mode 100644 index 000000000..bca283700 --- /dev/null +++ b/ct/headers/twingate-connector @@ -0,0 +1,6 @@ + __ _ __ __ + / /__ __(_)___ ____ _____ _/ /____ _________ ____ ____ ___ _____/ /_____ _____ + / __/ | /| / / / __ \/ __ `/ __ `/ __/ _ \______/ ___/ __ \/ __ \/ __ \/ _ \/ ___/ __/ __ \/ ___/ +/ /_ | |/ |/ / / / / / /_/ / /_/ / /_/ __/_____/ /__/ /_/ / / / / / / / __/ /__/ /_/ /_/ / / +\__/ |__/|__/_/_/ /_/\__, /\__,_/\__/\___/ \___/\____/_/ /_/_/ /_/\___/\___/\__/\____/_/ + /____/ diff --git a/ct/headers/uhf b/ct/headers/uhf new file mode 100644 index 000000000..ba248924a --- /dev/null +++ b/ct/headers/uhf @@ -0,0 +1,6 @@ + __ ____ ________ + / / / / / / / ____/ + / / / / /_/ / /_ +/ /_/ / __ / __/ +\____/_/ /_/_/ + From 4acd08e25e3d3011543b13a2348f611e67768a71 Mon Sep 17 00:00:00 2001 From: Andrew Baumbach Date: Tue, 12 Aug 2025 10:51:31 -0400 Subject: [PATCH 0482/1733] chore: add labels for metrics --- install/twingate-connector-install.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/install/twingate-connector-install.sh b/install/twingate-connector-install.sh index a3d95be02..c6027854d 100644 --- a/install/twingate-connector-install.sh +++ b/install/twingate-connector-install.sh @@ -44,6 +44,8 @@ msg_info "Writing config" echo "TWINGATE_NETWORK=${network}" echo "TWINGATE_ACCESS_TOKEN=${access_token}" echo "TWINGATE_REFRESH_TOKEN=${refresh_token}" + echo "TWINGATE_LABEL_HOSTNAME=$(hostname)" + echo "TWINGATE_LABEL_DEPLOYED_BY=proxmox" } > /etc/twingate/connector.conf chmod 600 /etc/twingate/connector.conf msg_ok "Config written" From 919c5094148d7d6f294316efaddf9f7a0d9733e7 Mon Sep 17 00:00:00 2001 From: Arnaud <80671351+Buco7854@users.noreply.github.com> Date: Thu, 7 Aug 2025 23:39:03 +0200 Subject: [PATCH 0483/1733] feat: remove nag for mobile version of pve This commit removes the subscription nag for mobile version of pve. Basically this script adds new javascript inside the head section of the html (could not make it work in body probably related to the template). Then it waits for page to load and when it loaded, everytime the body is changed, it tries to get the nag and click on close button so that it disappear. Once it found the nag it stops watching for new change in the body. --- tools/pve/post-pve-install.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/pve/post-pve-install.sh b/tools/pve/post-pve-install.sh index b8ab2da1e..1657b6eed 100644 --- a/tools/pve/post-pve-install.sh +++ b/tools/pve/post-pve-install.sh @@ -515,7 +515,11 @@ post_routines_common() { yes) whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox --title "Support Subscriptions" "Supporting the software's development team is essential. Check their official website's Support Subscriptions for pricing. Without their dedicated work, we wouldn't have this exceptional software." 10 58 msg_info "Disabling subscription nag" - echo "DPkg::Post-Invoke { \"if [ -s /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js ] && ! grep -q -F 'NoMoreNagging' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js; then echo 'Removing subscription nag from UI...'; sed -i '/data\.status/{s/\\!//;s/active/NoMoreNagging/}' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js; fi\" };" >/etc/apt/apt.conf.d/no-nag-script + encoded_script=$(base64 -w0 <<'EOF' +[ -s /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js ] && ! grep -q -F 'NoMoreNagging' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js && echo 'Removing subscription nag from UI...' && sed -i '/data\.status/{s/\!//;s/active/NoMoreNagging/}' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js; [ -f /usr/share/pve-yew-mobile-gui/index.html.tpl ] && sed -i '/' /usr/share/pve-yew-mobile-gui/index.html.tpl +EOF +) + echo "DPkg::Post-Invoke { \"echo $encoded_script | base64 -d | bash\"; };" > /etc/apt/apt.conf.d/no-nag-script msg_ok "Disabled subscription nag (Delete browser cache)" ;; no) From c7c5fa582c68876b4be63652a4de520d2fe48659 Mon Sep 17 00:00:00 2001 From: Arnaud <80671351+Buco7854@users.noreply.github.com> Date: Fri, 8 Aug 2025 00:04:00 +0200 Subject: [PATCH 0484/1733] fix: use same -s operator as previous script instead of -f --- tools/pve/post-pve-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/pve/post-pve-install.sh b/tools/pve/post-pve-install.sh index 1657b6eed..c783d2fc3 100644 --- a/tools/pve/post-pve-install.sh +++ b/tools/pve/post-pve-install.sh @@ -516,7 +516,7 @@ post_routines_common() { whiptail --backtitle "Proxmox VE Helper Scripts" --msgbox --title "Support Subscriptions" "Supporting the software's development team is essential. Check their official website's Support Subscriptions for pricing. Without their dedicated work, we wouldn't have this exceptional software." 10 58 msg_info "Disabling subscription nag" encoded_script=$(base64 -w0 <<'EOF' -[ -s /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js ] && ! grep -q -F 'NoMoreNagging' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js && echo 'Removing subscription nag from UI...' && sed -i '/data\.status/{s/\!//;s/active/NoMoreNagging/}' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js; [ -f /usr/share/pve-yew-mobile-gui/index.html.tpl ] && sed -i '/' /usr/share/pve-yew-mobile-gui/index.html.tpl +[ -s /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js ] && ! grep -q -F 'NoMoreNagging' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js && echo 'Removing subscription nag from UI...' && sed -i '/data\.status/{s/\!//;s/active/NoMoreNagging/}' /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js; [ -s /usr/share/pve-yew-mobile-gui/index.html.tpl ] && sed -i '/' /usr/share/pve-yew-mobile-gui/index.html.tpl EOF ) echo "DPkg::Post-Invoke { \"echo $encoded_script | base64 -d | bash\"; };" > /etc/apt/apt.conf.d/no-nag-script From cf39acc554e4d07017025c93f0a2d369727a8f31 Mon Sep 17 00:00:00 2001 From: David Bennett Date: Thu, 14 Aug 2025 10:46:05 -0500 Subject: [PATCH 0485/1733] feat: add Resilio Sync LXC container script Adds new LXC container script for Resilio Sync peer-to-peer file synchronization. Includes container creation script, install script, and frontend json following project conventions. --- ct/resiliosync.sh | 44 +++++++++++++++++++++++++++ frontend/public/json/resiliosync.json | 35 +++++++++++++++++++++ install/resiliosync-install.sh | 32 +++++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 ct/resiliosync.sh create mode 100644 frontend/public/json/resiliosync.json create mode 100644 install/resiliosync-install.sh diff --git a/ct/resiliosync.sh b/ct/resiliosync.sh new file mode 100644 index 000000000..eff0f8d51 --- /dev/null +++ b/ct/resiliosync.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: David Bennett (dbinit) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://www.resilio.com/sync + +APP="Resilio Sync" +var_tags="${var_tags:-sync}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-8}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /var/lib/resilio-sync ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating ${APP} LXC" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Updated Successfully" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:8888${CL}" diff --git a/frontend/public/json/resiliosync.json b/frontend/public/json/resiliosync.json new file mode 100644 index 000000000..222786e12 --- /dev/null +++ b/frontend/public/json/resiliosync.json @@ -0,0 +1,35 @@ +{ + "name": "Resilio Sync", + "slug": "resiliosync", + "categories": [ + 11 + ], + "date_created": "2025-08-14", + "type": "ct", + "updateable": true, + "privileged": false, + "config_path": "/etc/resilio-sync/config.json", + "interface_port": 8888, + "documentation": "https://help.resilio.com/", + "website": "https://www.resilio.com/sync", + "logo": "https://www.resilio.com/images/sync-appicon-blue-bg.svg", + "description": "Fast, reliable, and simple file sync and share solution, powered by P2P technology. Sync files across all your devices without storing them in the cloud.", + "install_methods": [ + { + "type": "default", + "script": "ct/resiliosync.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 8, + "os": "debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} diff --git a/install/resiliosync-install.sh b/install/resiliosync-install.sh new file mode 100644 index 000000000..8b03ff9e2 --- /dev/null +++ b/install/resiliosync-install.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: David Bennett (dbinit) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://www.resilio.com/sync + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Resilio Sync" +curl -fsSL "https://linux-packages.resilio.com/resilio-sync/key.asc" >/etc/apt/trusted.gpg.d/resilio-sync.asc +echo "deb [signed-by=/etc/apt/trusted.gpg.d/resilio-sync.asc] http://linux-packages.resilio.com/resilio-sync/deb resilio-sync non-free" >/etc/apt/sources.list.d/resilio-sync.list +$STD apt-get update +$STD apt-get install -y resilio-sync +sed -i 's/127.0.0.1:8888/0.0.0.0:8888/g' /etc/resilio-sync/config.json +$STD systemctl enable resilio-sync +$STD systemctl restart resilio-sync +msg_ok "Installed Resilio Sync" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 211afc9130b9049986998e33d6ef2d23306c5d79 Mon Sep 17 00:00:00 2001 From: Andrew Stout Date: Thu, 7 Aug 2025 18:06:57 -0500 Subject: [PATCH 0486/1733] Added initial scripts for litellm --- ct/litellm.sh | 45 +++++++++++++++++ frontend/public/json/litellm.json | 35 ++++++++++++++ install/litellm-install.sh | 80 +++++++++++++++++++++++++++++++ 3 files changed, 160 insertions(+) create mode 100644 ct/litellm.sh create mode 100644 frontend/public/json/litellm.json create mode 100644 install/litellm-install.sh diff --git a/ct/litellm.sh b/ct/litellm.sh new file mode 100644 index 000000000..a8e9b6aff --- /dev/null +++ b/ct/litellm.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: stout01 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/BerriAI/litellm + +APP="litellm" +var_tags="${var_tags:-ai;interface}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -f /etc/systemd/system/litellm.service ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating $APP LXC" + $STD apt-get update + pip install litellm[proxy] --upgrade + msg_ok "Updated $APP LXC" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:4000${CL}" diff --git a/frontend/public/json/litellm.json b/frontend/public/json/litellm.json new file mode 100644 index 000000000..e77c72914 --- /dev/null +++ b/frontend/public/json/litellm.json @@ -0,0 +1,35 @@ +{ + "name": "LiteLLM", + "slug": "litellm", + "categories": [ + 20 + ], + "date_created": "2025-08-07", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 4000, + "documentation": "https://docs.litellm.ai/", + "config_path": "/opt/litellm.env", + "website": "https://www.litellm.ai/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/litellm-light.webp", + "description": "LiteLLM description is here", + "install_methods": [ + { + "type": "default", + "script": "ct/litellm.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 4, + "os": "Debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": "admin", + "password": "sk-1234" + }, + "notes": [] +} diff --git a/install/litellm-install.sh b/install/litellm-install.sh new file mode 100644 index 000000000..bbaa31f30 --- /dev/null +++ b/install/litellm-install.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: stout01 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/BerriAI/litellm + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Setup Python3" +$STD apt-get install -y \ + python3 \ + python3-dev \ + python3-pip +rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED +msg_ok "Setup Python3" + +msg_info "Installing ${APPLICATION}" +$STD pip install 'litellm[proxy]' +$STD pip install 'prisma' +msg_ok "Installed ${APPLICATION}" + +PG_VERSION="17" setup_postgresql + +msg_info "Setting up PostgreSQL" +DB_NAME="litellm_db" +DB_USER="${APPLICATION}" +DB_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" +$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" +$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" +# $STD sudo -u postgres psql -c "CREATE EXTENSION IF NOT EXISTS pg_trgm;" $DB_NAME +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" +{ + echo "${APPLICATION} Credentials" + echo "Database Name: $DB_NAME" + echo "Database User: $DB_USER" + echo "Database Password: $DB_PASS" +} >>~/litellm.creds +msg_ok "Set up PostgreSQL" + +msg_info "Creating Service" +mkdir -p /opt +cat </opt/${APPLICATION}.env +LITELLM_MASTER_KEY=sk-1234 +DATABASE_URL=postgresql://$DB_USER:$DB_PASS@127.0.0.1:5432/$DB_NAME +STORE_MODEL_IN_DB=true +USE_PRISMA_MIGRATE=true +EOF + +cat </etc/systemd/system/${APPLICATION}.service +[Unit] +Description=LiteLLM + +[Service] +Type=simple +EnvironmentFile=/opt/${APPLICATION}.env +ExecStart=litellm +Restart=always + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now "${APPLICATION}" +msg_ok "Created Service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 155e431b2a90fc27c1b4a4e550e59d93b20be886 Mon Sep 17 00:00:00 2001 From: Andrew Stout Date: Thu, 7 Aug 2025 18:57:07 -0500 Subject: [PATCH 0487/1733] Refactored to use config yaml --- install/litellm-install.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/install/litellm-install.sh b/install/litellm-install.sh index bbaa31f30..98c12eed8 100644 --- a/install/litellm-install.sh +++ b/install/litellm-install.sh @@ -48,21 +48,21 @@ msg_ok "Set up PostgreSQL" msg_info "Creating Service" mkdir -p /opt -cat </opt/${APPLICATION}.env -LITELLM_MASTER_KEY=sk-1234 -DATABASE_URL=postgresql://$DB_USER:$DB_PASS@127.0.0.1:5432/$DB_NAME -STORE_MODEL_IN_DB=true -USE_PRISMA_MIGRATE=true +cat </opt/"${APPLICATION}".yaml +general_settings: + master_key: sk-1234 + database_url: postgresql://$DB_USER:$DB_PASS@127.0.0.1:5432/$DB_NAME + store_model_in_db: true + use_prisma_migrate: true EOF -cat </etc/systemd/system/${APPLICATION}.service +cat </etc/systemd/system/"${APPLICATION}".service [Unit] Description=LiteLLM [Service] Type=simple -EnvironmentFile=/opt/${APPLICATION}.env -ExecStart=litellm +ExecStart=litellm --config /opt/"${APPLICATION}".yaml Restart=always [Install] From 758f198acd65217d06a8a6afabe3ef09efa1789e Mon Sep 17 00:00:00 2001 From: Andrew Stout Date: Thu, 7 Aug 2025 19:06:07 -0500 Subject: [PATCH 0488/1733] Added prisma migrate param --- install/litellm-install.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/install/litellm-install.sh b/install/litellm-install.sh index 98c12eed8..0634a7832 100644 --- a/install/litellm-install.sh +++ b/install/litellm-install.sh @@ -53,7 +53,6 @@ general_settings: master_key: sk-1234 database_url: postgresql://$DB_USER:$DB_PASS@127.0.0.1:5432/$DB_NAME store_model_in_db: true - use_prisma_migrate: true EOF cat </etc/systemd/system/"${APPLICATION}".service @@ -62,7 +61,7 @@ Description=LiteLLM [Service] Type=simple -ExecStart=litellm --config /opt/"${APPLICATION}".yaml +ExecStart=litellm --config /opt/${APPLICATION}.yaml --use_prisma_migrate Restart=always [Install] From 950e7403c4d62f3bc8fd365af5a02d2a3d76a490 Mon Sep 17 00:00:00 2001 From: Andrew Stout Date: Thu, 7 Aug 2025 19:17:55 -0500 Subject: [PATCH 0489/1733] Updated config file path --- frontend/public/json/litellm.json | 38 +++++++++++++++---------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/frontend/public/json/litellm.json b/frontend/public/json/litellm.json index e77c72914..fe5d10488 100644 --- a/frontend/public/json/litellm.json +++ b/frontend/public/json/litellm.json @@ -1,35 +1,35 @@ { - "name": "LiteLLM", - "slug": "litellm", "categories": [ 20 ], + "config_path": "/opt/litellm.yaml", "date_created": "2025-08-07", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 4000, - "documentation": "https://docs.litellm.ai/", - "config_path": "/opt/litellm.env", - "website": "https://www.litellm.ai/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/litellm-light.webp", + "default_credentials": { + "password": "sk-1234", + "username": "admin" + }, "description": "LiteLLM description is here", + "documentation": "https://docs.litellm.ai/", "install_methods": [ { - "type": "default", - "script": "ct/litellm.sh", "resources": { "cpu": 2, - "ram": 2048, "hdd": 4, "os": "Debian", + "ram": 2048, "version": "12" - } + }, + "script": "ct/litellm.sh", + "type": "default" } ], - "default_credentials": { - "username": "admin", - "password": "sk-1234" - }, - "notes": [] + "interface_port": 4000, + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/litellm-light.webp", + "name": "LiteLLM", + "notes": [], + "privileged": false, + "slug": "litellm", + "type": "ct", + "updateable": true, + "website": "https://www.litellm.ai/" } From 6789151eb256a355896b294c66b45f7759fb5a6e Mon Sep 17 00:00:00 2001 From: Andrew Stout Date: Thu, 7 Aug 2025 19:31:12 -0500 Subject: [PATCH 0490/1733] Add .DS_Store to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c6f9a448e..4effcb9e0 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .vscode/settings.json +.DS_Store From 0c0cf83cacdd05c16df2eb8d5e52b15e55a262bb Mon Sep 17 00:00:00 2001 From: Andrew Stout Date: Thu, 7 Aug 2025 19:31:59 -0500 Subject: [PATCH 0491/1733] Added note about updating the master key --- frontend/public/json/litellm.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/public/json/litellm.json b/frontend/public/json/litellm.json index fe5d10488..7ae250200 100644 --- a/frontend/public/json/litellm.json +++ b/frontend/public/json/litellm.json @@ -26,7 +26,12 @@ "interface_port": 4000, "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/litellm-light.webp", "name": "LiteLLM", - "notes": [], + "notes": [ + { + "text": "Update master key in the config file", + "type": "info" + } + ], "privileged": false, "slug": "litellm", "type": "ct", From de08adb1ca33947882ac87336d21bf39b18e733c Mon Sep 17 00:00:00 2001 From: Andrew Stout Date: Thu, 7 Aug 2025 20:56:27 -0500 Subject: [PATCH 0492/1733] Updated litellm description --- frontend/public/json/litellm.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/litellm.json b/frontend/public/json/litellm.json index 7ae250200..45ca3da49 100644 --- a/frontend/public/json/litellm.json +++ b/frontend/public/json/litellm.json @@ -8,7 +8,7 @@ "password": "sk-1234", "username": "admin" }, - "description": "LiteLLM description is here", + "description": "LLM proxy to call 100+ LLMs in a unified interface & track spend, set budgets per virtual key/user", "documentation": "https://docs.litellm.ai/", "install_methods": [ { From 29c6d816eeb07db859021854d494ed914ffd3ff6 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Fri, 8 Aug 2025 07:40:41 +0200 Subject: [PATCH 0493/1733] Delete .gitignore --- .gitignore | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 .gitignore diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 4effcb9e0..000000000 --- a/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -.vscode/settings.json -.DS_Store From 8b1d332217975f73f85b6b3b26cf6b198da0ad76 Mon Sep 17 00:00:00 2001 From: Andrew Stout Date: Fri, 8 Aug 2025 15:39:11 -0500 Subject: [PATCH 0494/1733] Reverted changes to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..c6f9a448e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.vscode/settings.json From 3df502a1c2f7656b06b22d3736e0d2411d461150 Mon Sep 17 00:00:00 2001 From: Andrew Stout Date: Fri, 8 Aug 2025 15:41:35 -0500 Subject: [PATCH 0495/1733] Changed url to point to the VED repo --- ct/litellm.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/litellm.sh b/ct/litellm.sh index a8e9b6aff..a541cb1bd 100644 --- a/ct/litellm.sh +++ b/ct/litellm.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: stout01 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -23,7 +23,7 @@ function update_script() { header_info check_container_storage check_container_resources - + if [[ ! -f /etc/systemd/system/litellm.service ]]; then msg_error "No ${APP} Installation Found!" exit From 6a20775d088ffd8281e0564672f4f07b78b12273 Mon Sep 17 00:00:00 2001 From: Andrew Stout Date: Fri, 8 Aug 2025 15:42:21 -0500 Subject: [PATCH 0496/1733] Removed LXC from message --- ct/litellm.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/litellm.sh b/ct/litellm.sh index a541cb1bd..c8e69fcf1 100644 --- a/ct/litellm.sh +++ b/ct/litellm.sh @@ -28,10 +28,10 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - msg_info "Updating $APP LXC" + msg_info "Updating $APP" $STD apt-get update pip install litellm[proxy] --upgrade - msg_ok "Updated $APP LXC" + msg_ok "Updated $APP" exit } From 9ff98daafc1bce6abe62ac62f5f8cecc43edcaea Mon Sep 17 00:00:00 2001 From: Andrew Stout Date: Fri, 8 Aug 2025 15:42:46 -0500 Subject: [PATCH 0497/1733] Removed unneeded commented line --- install/litellm-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/install/litellm-install.sh b/install/litellm-install.sh index 0634a7832..304798fba 100644 --- a/install/litellm-install.sh +++ b/install/litellm-install.sh @@ -34,7 +34,6 @@ DB_USER="${APPLICATION}" DB_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" $STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" -# $STD sudo -u postgres psql -c "CREATE EXTENSION IF NOT EXISTS pg_trgm;" $DB_NAME $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" From 7813fa19b861bc507e8abae355dda35cd8a45b57 Mon Sep 17 00:00:00 2001 From: Andrew Stout Date: Fri, 8 Aug 2025 15:46:44 -0500 Subject: [PATCH 0498/1733] Replaced app name variable with just the application name --- install/litellm-install.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/install/litellm-install.sh b/install/litellm-install.sh index 304798fba..41dc2fc66 100644 --- a/install/litellm-install.sh +++ b/install/litellm-install.sh @@ -47,26 +47,26 @@ msg_ok "Set up PostgreSQL" msg_info "Creating Service" mkdir -p /opt -cat </opt/"${APPLICATION}".yaml +cat </opt/litellm.yaml general_settings: master_key: sk-1234 database_url: postgresql://$DB_USER:$DB_PASS@127.0.0.1:5432/$DB_NAME store_model_in_db: true EOF -cat </etc/systemd/system/"${APPLICATION}".service +cat </etc/systemd/system/litellm.service [Unit] Description=LiteLLM [Service] Type=simple -ExecStart=litellm --config /opt/${APPLICATION}.yaml --use_prisma_migrate +ExecStart=litellm --config /opt/litellm.yaml --use_prisma_migrate Restart=always [Install] WantedBy=multi-user.target EOF -systemctl enable -q --now "${APPLICATION}" +systemctl enable -q --now litellm msg_ok "Created Service" motd_ssh From c094133ea1e3d2bc27d1c45b3df264abd645569f Mon Sep 17 00:00:00 2001 From: Andrew Stout Date: Fri, 8 Aug 2025 15:50:40 -0500 Subject: [PATCH 0499/1733] unsorted and unformatted litellm.json --- frontend/public/json/litellm.json | 38 +++++++++++++++---------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/frontend/public/json/litellm.json b/frontend/public/json/litellm.json index 45ca3da49..24ab079eb 100644 --- a/frontend/public/json/litellm.json +++ b/frontend/public/json/litellm.json @@ -1,40 +1,40 @@ { + "name": "LiteLLM", + "slug": "litellm", "categories": [ 20 ], - "config_path": "/opt/litellm.yaml", "date_created": "2025-08-07", - "default_credentials": { - "password": "sk-1234", - "username": "admin" - }, - "description": "LLM proxy to call 100+ LLMs in a unified interface & track spend, set budgets per virtual key/user", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 4000, "documentation": "https://docs.litellm.ai/", + "config_path": "/opt/litellm.yaml", + "website": "https://www.litellm.ai/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/litellm-light.webp", + "description": "LLM proxy to call 100+ LLMs in a unified interface & track spend, set budgets per virtual key/user", "install_methods": [ { + "type": "default", + "script": "ct/litellm.sh", "resources": { "cpu": 2, + "ram": 2048, "hdd": 4, "os": "Debian", - "ram": 2048, "version": "12" - }, - "script": "ct/litellm.sh", - "type": "default" + } } ], - "interface_port": 4000, - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/litellm-light.webp", - "name": "LiteLLM", + "default_credentials": { + "username": "admin", + "password": "sk-1234" + }, "notes": [ { "text": "Update master key in the config file", "type": "info" } - ], - "privileged": false, - "slug": "litellm", - "type": "ct", - "updateable": true, - "website": "https://www.litellm.ai/" + ] } From ec0de310e8e42c4131fbb84d4d2ac8f124fc57f8 Mon Sep 17 00:00:00 2001 From: Andrew Stout Date: Fri, 8 Aug 2025 16:49:55 -0500 Subject: [PATCH 0500/1733] Refactored litellm script to use uv --- ct/litellm.sh | 5 +++-- frontend/public/json/litellm.json | 2 +- install/litellm-install.sh | 24 +++++++++++------------- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/ct/litellm.sh b/ct/litellm.sh index c8e69fcf1..db6552936 100644 --- a/ct/litellm.sh +++ b/ct/litellm.sh @@ -28,9 +28,10 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi + msg_info "Updating $APP" - $STD apt-get update - pip install litellm[proxy] --upgrade + PYTHON_VERSION="3.13" setup_uv + $STD "$VENV_PATH/bin/python" -m pip install --upgrade litellm[proxy] prisma msg_ok "Updated $APP" exit } diff --git a/frontend/public/json/litellm.json b/frontend/public/json/litellm.json index 24ab079eb..b2e7b7d79 100644 --- a/frontend/public/json/litellm.json +++ b/frontend/public/json/litellm.json @@ -10,7 +10,7 @@ "privileged": false, "interface_port": 4000, "documentation": "https://docs.litellm.ai/", - "config_path": "/opt/litellm.yaml", + "config_path": "/opt/litellm/litellm.yaml", "website": "https://www.litellm.ai/", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/litellm-light.webp", "description": "LLM proxy to call 100+ LLMs in a unified interface & track spend, set budgets per virtual key/user", diff --git a/install/litellm-install.sh b/install/litellm-install.sh index 41dc2fc66..ed65b4ac6 100644 --- a/install/litellm-install.sh +++ b/install/litellm-install.sh @@ -13,18 +13,16 @@ setting_up_container network_check update_os -msg_info "Setup Python3" -$STD apt-get install -y \ - python3 \ - python3-dev \ - python3-pip -rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED -msg_ok "Setup Python3" +PYTHON_VERSION="3.13" setup_uv -msg_info "Installing ${APPLICATION}" -$STD pip install 'litellm[proxy]' -$STD pip install 'prisma' -msg_ok "Installed ${APPLICATION}" +msg_info "Setting up Virtual Environment" +mkdir -p /opt/litellm +cd /opt/litellm +$STD uv venv /opt/litellmtwo/.venv +$STD /opt/litellmtwo/.venv/bin/python -m ensurepip --upgrade +$STD /opt/litellmtwo/.venv/bin/python -m pip install --upgrade pip +$STD /opt/litellmtwo/.venv/bin/python -m pip install litellm[proxy] prisma +msg_ok "Installed LiteLLM" PG_VERSION="17" setup_postgresql @@ -47,7 +45,7 @@ msg_ok "Set up PostgreSQL" msg_info "Creating Service" mkdir -p /opt -cat </opt/litellm.yaml +cat </opt/litellm/litellm.yaml general_settings: master_key: sk-1234 database_url: postgresql://$DB_USER:$DB_PASS@127.0.0.1:5432/$DB_NAME @@ -60,7 +58,7 @@ Description=LiteLLM [Service] Type=simple -ExecStart=litellm --config /opt/litellm.yaml --use_prisma_migrate +ExecStart=/opt/litellm/.venv/bin/litellm --config /opt/litellm/litellm.yaml --use_prisma_migrate Restart=always [Install] From 9bf97a6995a9e8fc499d0ca42312c7a0b5b0c36a Mon Sep 17 00:00:00 2001 From: Andrew Stout Date: Fri, 8 Aug 2025 16:52:28 -0500 Subject: [PATCH 0501/1733] Updated urls for testing --- ct/litellm.sh | 2 +- misc/build.func | 4 ++-- misc/install.func | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ct/litellm.sh b/ct/litellm.sh index db6552936..99f9366c4 100644 --- a/ct/litellm.sh +++ b/ct/litellm.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/stout01/ProxmoxVED/refs/heads/ved-litellm-script/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: stout01 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/misc/build.func b/misc/build.func index c40a57bd1..2197741ee 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1129,7 +1129,7 @@ build_container() { if [ "$var_os" == "alpine" ]; then export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-install.func)" else - export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://raw.githubusercontent.com/stout01/ProxmoxVED/refs/heads/ved-litellm-script/misc/install.func)" fi export DIAGNOSTICS="$DIAGNOSTICS" export RANDOM_UUID="$RANDOM_UUID" @@ -1438,7 +1438,7 @@ EOF' fi msg_ok "Customized LXC Container" - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)" + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/stout01/ProxmoxVED/refs/heads/ved-litellm-script/install/${var_install}.sh)" } destroy_lxc() { diff --git a/misc/install.func b/misc/install.func index e3751c295..a4ef94642 100644 --- a/misc/install.func +++ b/misc/install.func @@ -196,7 +196,7 @@ EOF systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//') msg_ok "Customized Container" fi - echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update + echo "bash -c \"\$(curl -fsSL https://github.com/stout01/ProxmoxVED/refs/heads/ved-litellm-script/ct/${app}.sh)\"" >/usr/bin/update chmod +x /usr/bin/update if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then mkdir -p /root/.ssh From 2b8ac5458b5ae029c8a630c53fe4209846a9be8d Mon Sep 17 00:00:00 2001 From: Andrew Stout Date: Fri, 8 Aug 2025 16:58:56 -0500 Subject: [PATCH 0502/1733] Revert "Updated urls for testing" This reverts commit 89c3c77bf5c5ae858233d409f696014426ba21b1. --- ct/litellm.sh | 2 +- misc/build.func | 4 ++-- misc/install.func | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ct/litellm.sh b/ct/litellm.sh index 99f9366c4..db6552936 100644 --- a/ct/litellm.sh +++ b/ct/litellm.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/stout01/ProxmoxVED/refs/heads/ved-litellm-script/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: stout01 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/misc/build.func b/misc/build.func index 2197741ee..c40a57bd1 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1129,7 +1129,7 @@ build_container() { if [ "$var_os" == "alpine" ]; then export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-install.func)" else - export FUNCTIONS_FILE_PATH="$(curl -fsSL https://raw.githubusercontent.com/stout01/ProxmoxVED/refs/heads/ved-litellm-script/misc/install.func)" + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" fi export DIAGNOSTICS="$DIAGNOSTICS" export RANDOM_UUID="$RANDOM_UUID" @@ -1438,7 +1438,7 @@ EOF' fi msg_ok "Customized LXC Container" - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/stout01/ProxmoxVED/refs/heads/ved-litellm-script/install/${var_install}.sh)" + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)" } destroy_lxc() { diff --git a/misc/install.func b/misc/install.func index a4ef94642..e3751c295 100644 --- a/misc/install.func +++ b/misc/install.func @@ -196,7 +196,7 @@ EOF systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//') msg_ok "Customized Container" fi - echo "bash -c \"\$(curl -fsSL https://github.com/stout01/ProxmoxVED/refs/heads/ved-litellm-script/ct/${app}.sh)\"" >/usr/bin/update + echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update chmod +x /usr/bin/update if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then mkdir -p /root/.ssh From 0188bcfe3c297930fe8b628b135c4a4050d627bb Mon Sep 17 00:00:00 2001 From: Andrew Stout Date: Sat, 16 Aug 2025 15:27:11 -0500 Subject: [PATCH 0503/1733] use static text for db_user --- install/litellm-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/litellm-install.sh b/install/litellm-install.sh index ed65b4ac6..9c9d82f36 100644 --- a/install/litellm-install.sh +++ b/install/litellm-install.sh @@ -28,7 +28,7 @@ PG_VERSION="17" setup_postgresql msg_info "Setting up PostgreSQL" DB_NAME="litellm_db" -DB_USER="${APPLICATION}" +DB_USER="litellm" DB_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" $STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" From 79211aeb18fe89594512d8fb675d085214ea792a Mon Sep 17 00:00:00 2001 From: Andrew Stout Date: Sat, 16 Aug 2025 15:28:24 -0500 Subject: [PATCH 0504/1733] Add missing venv_path --- ct/litellm.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ct/litellm.sh b/ct/litellm.sh index db6552936..2fb7fcb43 100644 --- a/ct/litellm.sh +++ b/ct/litellm.sh @@ -30,6 +30,7 @@ function update_script() { fi msg_info "Updating $APP" + VENV_PATH="/opt/litellm/.venv" PYTHON_VERSION="3.13" setup_uv $STD "$VENV_PATH/bin/python" -m pip install --upgrade litellm[proxy] prisma msg_ok "Updated $APP" From 84358ce5b4a2ce3e79b7cf5cad386decf2521a4c Mon Sep 17 00:00:00 2001 From: Andrew Stout Date: Sat, 16 Aug 2025 15:30:47 -0500 Subject: [PATCH 0505/1733] Start and stop service while updating --- ct/litellm.sh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ct/litellm.sh b/ct/litellm.sh index 2fb7fcb43..975f93c8e 100644 --- a/ct/litellm.sh +++ b/ct/litellm.sh @@ -29,11 +29,19 @@ function update_script() { exit fi + msg_info "Stopping ${APP}" + systemctl stop litellm.service + msg_ok "Stopped ${APP}" + msg_info "Updating $APP" VENV_PATH="/opt/litellm/.venv" PYTHON_VERSION="3.13" setup_uv $STD "$VENV_PATH/bin/python" -m pip install --upgrade litellm[proxy] prisma - msg_ok "Updated $APP" + + msg_info "Starting ${APP}" + systemctl start litellm.service + msg_ok "Started ${APP}" + msg_ok "Updated Successfully" exit } From 9c1670fc9d4446baa06b84a67a2d0341c91cfde0 Mon Sep 17 00:00:00 2001 From: Andrew Stout Date: Sat, 16 Aug 2025 15:31:08 -0500 Subject: [PATCH 0506/1733] Reapply "Updated urls for testing" This reverts commit 2b8ac5458b5ae029c8a630c53fe4209846a9be8d. --- ct/litellm.sh | 2 +- misc/build.func | 4 ++-- misc/install.func | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ct/litellm.sh b/ct/litellm.sh index 975f93c8e..5b317fa51 100644 --- a/ct/litellm.sh +++ b/ct/litellm.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/stout01/ProxmoxVED/refs/heads/ved-litellm-script/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: stout01 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/misc/build.func b/misc/build.func index c40a57bd1..2197741ee 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1129,7 +1129,7 @@ build_container() { if [ "$var_os" == "alpine" ]; then export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-install.func)" else - export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://raw.githubusercontent.com/stout01/ProxmoxVED/refs/heads/ved-litellm-script/misc/install.func)" fi export DIAGNOSTICS="$DIAGNOSTICS" export RANDOM_UUID="$RANDOM_UUID" @@ -1438,7 +1438,7 @@ EOF' fi msg_ok "Customized LXC Container" - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)" + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/stout01/ProxmoxVED/refs/heads/ved-litellm-script/install/${var_install}.sh)" } destroy_lxc() { diff --git a/misc/install.func b/misc/install.func index e3751c295..a4ef94642 100644 --- a/misc/install.func +++ b/misc/install.func @@ -196,7 +196,7 @@ EOF systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//') msg_ok "Customized Container" fi - echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update + echo "bash -c \"\$(curl -fsSL https://github.com/stout01/ProxmoxVED/refs/heads/ved-litellm-script/ct/${app}.sh)\"" >/usr/bin/update chmod +x /usr/bin/update if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then mkdir -p /root/.ssh From a52b8e4bdd426a2414f8a447ea8ff6f8caec6178 Mon Sep 17 00:00:00 2001 From: Andrew Stout Date: Sat, 16 Aug 2025 15:38:04 -0500 Subject: [PATCH 0507/1733] Remove --use_prisma_migrate. The latest release removed this param --- install/litellm-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/litellm-install.sh b/install/litellm-install.sh index 9c9d82f36..9b7845f1c 100644 --- a/install/litellm-install.sh +++ b/install/litellm-install.sh @@ -58,7 +58,7 @@ Description=LiteLLM [Service] Type=simple -ExecStart=/opt/litellm/.venv/bin/litellm --config /opt/litellm/litellm.yaml --use_prisma_migrate +ExecStart=/opt/litellm/.venv/bin/litellm --config /opt/litellm/litellm.yaml Restart=always [Install] From 5f81621758aed4c7c7f5ad7f172d0ea276434c21 Mon Sep 17 00:00:00 2001 From: Andrew Stout Date: Sat, 16 Aug 2025 15:49:25 -0500 Subject: [PATCH 0508/1733] Fixed error in the folder paths --- install/litellm-install.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/install/litellm-install.sh b/install/litellm-install.sh index 9b7845f1c..008b5dc4e 100644 --- a/install/litellm-install.sh +++ b/install/litellm-install.sh @@ -18,10 +18,10 @@ PYTHON_VERSION="3.13" setup_uv msg_info "Setting up Virtual Environment" mkdir -p /opt/litellm cd /opt/litellm -$STD uv venv /opt/litellmtwo/.venv -$STD /opt/litellmtwo/.venv/bin/python -m ensurepip --upgrade -$STD /opt/litellmtwo/.venv/bin/python -m pip install --upgrade pip -$STD /opt/litellmtwo/.venv/bin/python -m pip install litellm[proxy] prisma +$STD uv venv /opt/litellm/.venv +$STD /opt/litellm/.venv/bin/python -m ensurepip --upgrade +$STD /opt/litellm/.venv/bin/python -m pip install --upgrade pip +$STD /opt/litellm/.venv/bin/python -m pip install litellm[proxy] prisma msg_ok "Installed LiteLLM" PG_VERSION="17" setup_postgresql From d597345310f6d45de275b69c7a17d8f21f70e930 Mon Sep 17 00:00:00 2001 From: Andrew Stout Date: Sat, 16 Aug 2025 16:32:24 -0500 Subject: [PATCH 0509/1733] Testing an older version --- install/litellm-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/litellm-install.sh b/install/litellm-install.sh index 008b5dc4e..c264d2520 100644 --- a/install/litellm-install.sh +++ b/install/litellm-install.sh @@ -21,7 +21,7 @@ cd /opt/litellm $STD uv venv /opt/litellm/.venv $STD /opt/litellm/.venv/bin/python -m ensurepip --upgrade $STD /opt/litellm/.venv/bin/python -m pip install --upgrade pip -$STD /opt/litellm/.venv/bin/python -m pip install litellm[proxy] prisma +$STD /opt/litellm/.venv/bin/python -m pip install litellm[proxy]==1.75.4 prisma msg_ok "Installed LiteLLM" PG_VERSION="17" setup_postgresql @@ -58,7 +58,7 @@ Description=LiteLLM [Service] Type=simple -ExecStart=/opt/litellm/.venv/bin/litellm --config /opt/litellm/litellm.yaml +ExecStart=/opt/litellm/.venv/bin/litellm --config /opt/litellm/litellm.yaml --use_prisma_migrate Restart=always [Install] From d8543f0404bf47b065104679504066d036589fa6 Mon Sep 17 00:00:00 2001 From: Andrew Stout Date: Sat, 16 Aug 2025 16:50:44 -0500 Subject: [PATCH 0510/1733] Use uv to run litellm --- install/litellm-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/litellm-install.sh b/install/litellm-install.sh index c264d2520..57380a442 100644 --- a/install/litellm-install.sh +++ b/install/litellm-install.sh @@ -58,7 +58,7 @@ Description=LiteLLM [Service] Type=simple -ExecStart=/opt/litellm/.venv/bin/litellm --config /opt/litellm/litellm.yaml --use_prisma_migrate +ExecStart=uv --directory=/opt/litellm run litellm --config /opt/litellm/litellm.yaml --use_prisma_migrate Restart=always [Install] From 0e460e8cc07cf0be5bccf1fd62553acd6f3a3c71 Mon Sep 17 00:00:00 2001 From: Andrew Stout Date: Sat, 16 Aug 2025 16:51:39 -0500 Subject: [PATCH 0511/1733] Test using latest again --- install/litellm-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/litellm-install.sh b/install/litellm-install.sh index 57380a442..9790280bc 100644 --- a/install/litellm-install.sh +++ b/install/litellm-install.sh @@ -21,7 +21,7 @@ cd /opt/litellm $STD uv venv /opt/litellm/.venv $STD /opt/litellm/.venv/bin/python -m ensurepip --upgrade $STD /opt/litellm/.venv/bin/python -m pip install --upgrade pip -$STD /opt/litellm/.venv/bin/python -m pip install litellm[proxy]==1.75.4 prisma +$STD /opt/litellm/.venv/bin/python -m pip install litellm[proxy] prisma msg_ok "Installed LiteLLM" PG_VERSION="17" setup_postgresql @@ -58,7 +58,7 @@ Description=LiteLLM [Service] Type=simple -ExecStart=uv --directory=/opt/litellm run litellm --config /opt/litellm/litellm.yaml --use_prisma_migrate +ExecStart=uv --directory=/opt/litellm run litellm --config /opt/litellm/litellm.yaml Restart=always [Install] From baae9af2fae1035cee71b159f36f1044c1779558 Mon Sep 17 00:00:00 2001 From: Andrew Stout Date: Sat, 16 Aug 2025 17:08:17 -0500 Subject: [PATCH 0512/1733] Update DB schema before creating service --- install/litellm-install.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/install/litellm-install.sh b/install/litellm-install.sh index 9790280bc..c8a1fc755 100644 --- a/install/litellm-install.sh +++ b/install/litellm-install.sh @@ -43,7 +43,7 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" } >>~/litellm.creds msg_ok "Set up PostgreSQL" -msg_info "Creating Service" +msg_info "Configuring LiteLLM" mkdir -p /opt cat </opt/litellm/litellm.yaml general_settings: @@ -52,6 +52,10 @@ general_settings: store_model_in_db: true EOF +uv --directory=/opt/litellm run litellm --config /opt/litellm/litellm.yaml --use_prisma_db_push --skip_server_startup +msg_ok "Configured LiteLLM" + +msg_info "Creating Service" cat </etc/systemd/system/litellm.service [Unit] Description=LiteLLM @@ -64,6 +68,7 @@ Restart=always [Install] WantedBy=multi-user.target EOF + systemctl enable -q --now litellm msg_ok "Created Service" From ab8d446e16f55a33aad5caf710bcd7dab3687e65 Mon Sep 17 00:00:00 2001 From: Andrew Stout Date: Sat, 16 Aug 2025 17:30:01 -0500 Subject: [PATCH 0513/1733] Moved updating message below setup_uv --- ct/litellm.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ct/litellm.sh b/ct/litellm.sh index 5b317fa51..4c5d7c9d7 100644 --- a/ct/litellm.sh +++ b/ct/litellm.sh @@ -33,9 +33,10 @@ function update_script() { systemctl stop litellm.service msg_ok "Stopped ${APP}" - msg_info "Updating $APP" VENV_PATH="/opt/litellm/.venv" PYTHON_VERSION="3.13" setup_uv + + msg_info "Updating $APP" $STD "$VENV_PATH/bin/python" -m pip install --upgrade litellm[proxy] prisma msg_info "Starting ${APP}" From 6dac15a71279de2de9a6c63f16b7715c034ee3dc Mon Sep 17 00:00:00 2001 From: Andrew Stout Date: Sat, 16 Aug 2025 17:40:06 -0500 Subject: [PATCH 0514/1733] Update db schema after updating litellm --- ct/litellm.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ct/litellm.sh b/ct/litellm.sh index 4c5d7c9d7..e196542b0 100644 --- a/ct/litellm.sh +++ b/ct/litellm.sh @@ -39,6 +39,10 @@ function update_script() { msg_info "Updating $APP" $STD "$VENV_PATH/bin/python" -m pip install --upgrade litellm[proxy] prisma + msg_info "Updating DB Schema" + uv --directory=/opt/litellm run litellm --config /opt/litellm/litellm.yaml --use_prisma_db_push --skip_server_startup + msg_ok "DB Schema Updated" + msg_info "Starting ${APP}" systemctl start litellm.service msg_ok "Started ${APP}" From bd5f2f52bce0d54022ce17e4564507694c161ee1 Mon Sep 17 00:00:00 2001 From: Andrew Stout Date: Sat, 16 Aug 2025 17:47:02 -0500 Subject: [PATCH 0515/1733] Reordered install steps --- install/litellm-install.sh | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/install/litellm-install.sh b/install/litellm-install.sh index c8a1fc755..b79341f99 100644 --- a/install/litellm-install.sh +++ b/install/litellm-install.sh @@ -13,18 +13,8 @@ setting_up_container network_check update_os -PYTHON_VERSION="3.13" setup_uv - -msg_info "Setting up Virtual Environment" -mkdir -p /opt/litellm -cd /opt/litellm -$STD uv venv /opt/litellm/.venv -$STD /opt/litellm/.venv/bin/python -m ensurepip --upgrade -$STD /opt/litellm/.venv/bin/python -m pip install --upgrade pip -$STD /opt/litellm/.venv/bin/python -m pip install litellm[proxy] prisma -msg_ok "Installed LiteLLM" - PG_VERSION="17" setup_postgresql +PYTHON_VERSION="3.13" setup_uv msg_info "Setting up PostgreSQL" DB_NAME="litellm_db" @@ -43,6 +33,15 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" } >>~/litellm.creds msg_ok "Set up PostgreSQL" +msg_info "Setting up Virtual Environment" +mkdir -p /opt/litellm +cd /opt/litellm +$STD uv venv /opt/litellm/.venv +$STD /opt/litellm/.venv/bin/python -m ensurepip --upgrade +$STD /opt/litellm/.venv/bin/python -m pip install --upgrade pip +$STD /opt/litellm/.venv/bin/python -m pip install litellm[proxy] prisma +msg_ok "Installed LiteLLM" + msg_info "Configuring LiteLLM" mkdir -p /opt cat </opt/litellm/litellm.yaml From 8a663669bd4ba9de84134348a12f93dac3b47a10 Mon Sep 17 00:00:00 2001 From: Andrew Stout Date: Sat, 16 Aug 2025 21:14:11 -0500 Subject: [PATCH 0516/1733] Revert "Reapply "Updated urls for testing"" This reverts commit 9c1670fc9d4446baa06b84a67a2d0341c91cfde0. --- ct/litellm.sh | 2 +- misc/build.func | 4 ++-- misc/install.func | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ct/litellm.sh b/ct/litellm.sh index e196542b0..847b7d8b7 100644 --- a/ct/litellm.sh +++ b/ct/litellm.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/stout01/ProxmoxVED/refs/heads/ved-litellm-script/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: stout01 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/misc/build.func b/misc/build.func index 2197741ee..c40a57bd1 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1129,7 +1129,7 @@ build_container() { if [ "$var_os" == "alpine" ]; then export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-install.func)" else - export FUNCTIONS_FILE_PATH="$(curl -fsSL https://raw.githubusercontent.com/stout01/ProxmoxVED/refs/heads/ved-litellm-script/misc/install.func)" + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" fi export DIAGNOSTICS="$DIAGNOSTICS" export RANDOM_UUID="$RANDOM_UUID" @@ -1438,7 +1438,7 @@ EOF' fi msg_ok "Customized LXC Container" - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/stout01/ProxmoxVED/refs/heads/ved-litellm-script/install/${var_install}.sh)" + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)" } destroy_lxc() { diff --git a/misc/install.func b/misc/install.func index a4ef94642..e3751c295 100644 --- a/misc/install.func +++ b/misc/install.func @@ -196,7 +196,7 @@ EOF systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//') msg_ok "Customized Container" fi - echo "bash -c \"\$(curl -fsSL https://github.com/stout01/ProxmoxVED/refs/heads/ved-litellm-script/ct/${app}.sh)\"" >/usr/bin/update + echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update chmod +x /usr/bin/update if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then mkdir -p /root/.ssh From 4469a6e6df45aecdbc4e046e51d66bcff0c554ac Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Sun, 10 Aug 2025 19:19:19 +0200 Subject: [PATCH 0517/1733] livebook --- ct/headers/livebook | 6 ++ ct/livebook.sh | 55 +++++++++++ frontend/public/json/livebook.json | 42 +++++++++ install/livebook-install.sh | 141 +++++++++++++++++++++++++++++ 4 files changed, 244 insertions(+) create mode 100644 ct/headers/livebook create mode 100755 ct/livebook.sh create mode 100644 frontend/public/json/livebook.json create mode 100644 install/livebook-install.sh diff --git a/ct/headers/livebook b/ct/headers/livebook new file mode 100644 index 000000000..7b50abfd0 --- /dev/null +++ b/ct/headers/livebook @@ -0,0 +1,6 @@ + __ _ __ __ + / / (_) _____ / /_ ____ ____ / /__ + / / / / | / / _ \/ __ \/ __ \/ __ \/ //_/ + / /___/ /| |/ / __/ /_/ / /_/ / /_/ / ,< +/_____/_/ |___/\___/_.___/\____/\____/_/|_| + diff --git a/ct/livebook.sh b/ct/livebook.sh new file mode 100755 index 000000000..bcc35e3c7 --- /dev/null +++ b/ct/livebook.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: dkuku +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/community-scripts/ProxmoxVE + +APP="Livebook" +var_tags="${var_tags:-development}" +var_disk="${var_disk:-4}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-1024}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /home/livebook ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating ${APP} LXC" + sudo -u livebook bash << 'EOF' +export HOME=/home/livebook +cd /home/livebook +source ~/.bashrc +mix local.hex --force >/dev/null 2>&1 +mix local.rebar --force >/dev/null 2>&1 +mix escript.install hex livebook --force >/dev/null 2>&1 +EOF + msg_ok "Updated Successfully" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" +echo -e "${INFO}${YW} Authentication:${CL}" +echo -e "${TAB}${RD}• Token authentication enabled by default${CL}" +echo -e "${TAB}${RD}• Token will be shown in logs: journalctl -u livebook.service${CL}" +echo -e "${TAB}${RD}• Generated token: /data/token.txt${CL}" +echo -e "${TAB}${RD}• Configuration: /data/.env${CL}" diff --git a/frontend/public/json/livebook.json b/frontend/public/json/livebook.json new file mode 100644 index 000000000..155d16906 --- /dev/null +++ b/frontend/public/json/livebook.json @@ -0,0 +1,42 @@ +{ + "name": "Livebook", + "slug": "livebook", + "categories": [ + 8 + ], + "date_created": "2025-08-10", + "type": "ct", + "updateable": true, + "privileged": false, + "config_path": "/data/.env", + "interface_port": 8080, + "documentation": "https://livebook.dev/", + "website": "https://livebook.dev/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/livebook.svg", + "description": "Livebook is a web application for writing interactive and collaborative code notebooks for Elixir.", + "install_methods": [ + { + "type": "default", + "script": "ct/livebook.sh", + "resources": { + "cpu": 2, + "ram": 1024, + "hdd": 4, + "os": "Debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": "Check /data/token.txt" + }, + "notes": [ + "Access token is stored in /data/token.txt", + "Default port is 8080", + "Working directory is /data", + "Home directory is /home/livebook", + "Elixir runtime with Mix.install/2 support", + "Service runs as livebook user" + ] +} diff --git a/install/livebook-install.sh b/install/livebook-install.sh new file mode 100644 index 000000000..61f96b92e --- /dev/null +++ b/install/livebook-install.sh @@ -0,0 +1,141 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: dkuku +# License: MIT | + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies (matching Livebook Dockerfile)" +$STD apt-get install --no-install-recommends -y \ + build-essential \ + ca-certificates \ + libncurses5-dev \ + git \ + wget \ + cmake \ + elixir +msg_ok "Installed Dependencies" + +msg_info "Creating Livebook User and Directories" +useradd -r -s /bin/bash -d /home/livebook livebook +mkdir -p /home/livebook /data +chown livebook:livebook /home/livebook /data +# Make sure user has permissions to home dir (for Mix.install/2 cache) +chmod 777 /home/livebook +msg_ok "Created Livebook User and Directories" + +msg_info "Installing Livebook" +sudo -u livebook bash << 'EOF' +export HOME=/home/livebook +cd /home/livebook +# Install hex and rebar for Mix.install/2 and Mix runtime (matching Dockerfile) +mix local.hex --force +mix local.rebar --force +# Following official Livebook escript installation instructions +mix escript.install hex livebook +echo 'export PATH="$HOME/.mix/escripts:$PATH"' >> ~/.bashrc +source ~/.bashrc +EOF +msg_ok "Installed Livebook" + +msg_info "Creating Livebook Service" +cat </etc/systemd/system/livebook.service +[Unit] +Description=Livebook +After=network.target + +[Service] +Type=exec +User=livebook +Group=livebook +WorkingDirectory=/data +Environment=HOME=/home/livebook +Environment=PATH=/home/livebook/.mix/escripts:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +Environment=LIVEBOOK_PORT=8080 +Environment=LIVEBOOK_IP="::" +Environment=LIVEBOOK_HOME=/data +ExecStart=/home/livebook/.mix/escripts/livebook server +Restart=always +RestartSec=5 +StandardOutput=journal +StandardError=journal + +[Install] +WantedBy=multi-user.target +EOF + +systemctl daemon-reload +systemctl enable livebook.service +msg_ok "Created Livebook Service" + +msg_info "Setting up Authentication" +# Generate a secure token for authentication +TOKEN=$(openssl rand -hex 32) +sudo -u livebook bash << EOF +cd /home/livebook +export HOME=/home/livebook +export PATH="\$HOME/.mix/escripts:\$PATH" +# Create environment file with authentication settings +cat > /data/.env << 'ENVEOF' +# Livebook Authentication Configuration +# Uncomment one of the following options: + +# Option 1: Password authentication (recommended for production) +# LIVEBOOK_PASSWORD=$TOKEN + +# Option 2: Token authentication (default - token will be shown in logs) +# LIVEBOOK_TOKEN_ENABLED=true + +# Option 3: Disable authentication (NOT recommended for production) +# LIVEBOOK_TOKEN_ENABLED=false + +# Current setting: Token authentication (default) +LIVEBOOK_TOKEN_ENABLED=true +ENVEOF + +# Save the token for easy access +echo "$TOKEN" > /data/token.txt +chmod 600 /data/token.txt +chown livebook:livebook /data/.env /data/token.txt +EOF +msg_ok "Set up Authentication" + +msg_info "Starting Livebook Service" +systemctl start livebook.service +msg_ok "Started Livebook Service" + +msg_info "Cleaning Up" +rm -f /tmp/erlang-solutions_2.0_all.deb +$STD apt-get autoremove -y +$STD apt-get autoclean +msg_ok "Cleaned Up" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" + +echo -e "\n${CREATING}${GN}Livebook Installation Complete!${CL}\n" +echo -e "${INFO}${YW}Authentication Information:${CL}" +echo -e "${TAB}${RD}• Default: Token authentication (auto-generated)${CL}" +echo -e "${TAB}${RD}• Token will be displayed in Livebook logs on startup${CL}" +echo -e "${TAB}${RD}• Generated token saved to: /data/token.txt${CL}" +echo -e "${TAB}${RD}• Configuration file: /data/.env${CL}\n" + +echo -e "${INFO}${YW}To configure authentication:${CL}" +echo -e "${TAB}${RD}1. Password auth: Edit /data/.env and uncomment LIVEBOOK_PASSWORD${CL}" +echo -e "${TAB}${RD}2. No auth: Edit /data/.env and set LIVEBOOK_TOKEN_ENABLED=false${CL}" +echo -e "${TAB}${RD}3. Restart service: systemctl restart livebook.service${CL}\n" + +echo -e "${INFO}${YW}Generated Token (for reference):${CL}" +echo -e "${TAB}${GN}$(cat /data/token.txt)${CL}\n" From ddf32895dcefa34058cb74b99feb713560a18659 Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Tue, 12 Aug 2025 21:39:42 +0200 Subject: [PATCH 0518/1733] livebook --- ct/livebook.sh | 97 +++++++++++----- frontend/public/json/livebook.json | 4 +- install/livebook-install.sh | 171 +++++++++++++++-------------- misc/build.func | 2 +- misc/install.func | 2 +- 5 files changed, 163 insertions(+), 113 deletions(-) diff --git a/ct/livebook.sh b/ct/livebook.sh index bcc35e3c7..177db87f8 100755 --- a/ct/livebook.sh +++ b/ct/livebook.sh @@ -1,17 +1,17 @@ #!/usr/bin/env bash -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: dkuku # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/community-scripts/ProxmoxVE +# Source: https://github.com/livebook-dev/livebook APP="Livebook" var_tags="${var_tags:-development}" var_disk="${var_disk:-4}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" +var_os="${var_os:-ubuntu}" +var_version="${var_version:-24}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" @@ -20,24 +20,69 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /home/livebook ]]; then - msg_error "No ${APP} Installation Found!" - exit + header_info + check_container_storage + check_container_resources + + # Check if Livebook is installed + if [[ ! -d /opt/${APP}_version.txt ]]; then + msg_error "No ${APP} Installation Found!" + exit 1 + fi + + # Get the latest version from GitHub + msg_info "Checking for updates..." + RELEASE=$(curl -fsSL https://api.github.com/repos/livebook-dev/livebook/releases/latest | grep "tag_name" | awk -F'"' '{print $4}') + + if [[ -z "$RELEASE" ]]; then + msg_error "Failed to fetch latest version information" + exit 1 + fi + + # Check if version file exists and compare versions + if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt 2>/dev/null)" ]]; then + msg_info "Updating ${APP} to v${RELEASE}" + + # Create backup of user data if it exists + if [[ -d /home/livebook ]]; then + msg_info "Creating backup of user data..." + $STD cp -r /home/livebook /home/livebook-backup fi - msg_info "Updating ${APP} LXC" - sudo -u livebook bash << 'EOF' -export HOME=/home/livebook -cd /home/livebook -source ~/.bashrc -mix local.hex --force >/dev/null 2>&1 -mix local.rebar --force >/dev/null 2>&1 -mix escript.install hex livebook --force >/dev/null 2>&1 -EOF - msg_ok "Updated Successfully" - exit + + # Perform the update + msg_info "Installing dependencies and updating Livebook..." + if ! sudo -u livebook bash -c ' + export HOME=/home/livebook + cd /home/livebook + mix local.hex --force >/dev/null 2>&1 + mix local.rebar --force >/dev/null 2>&1 + mix escript.install hex livebook --force >/dev/null 2>&1 + '; then + msg_error "Failed to update Livebook" + # Restore from backup if update failed + if [[ -d /home/livebook-backup ]]; then + msg_info "Restoring from backup..." + rm -rf /home/livebook + mv /home/livebook-backup /home/livebook + fi + exit 1 + fi + + # Save the new version + echo "$RELEASE" | $STD tee /opt/${APP}_version.txt >/dev/null + + # Cleanup backup if update was successful + if [[ -d /home/livebook-backup ]]; then + msg_info "Cleaning up backup..." + $STD rm -rf /home/livebook-backup + fi + + msg_ok "Successfully updated to v${RELEASE}" + else + msg_ok "No update required. ${APP} is already at v${RELEASE}." + fi + + exit } start @@ -47,9 +92,7 @@ description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" -echo -e "${INFO}${YW} Authentication:${CL}" -echo -e "${TAB}${RD}• Token authentication enabled by default${CL}" -echo -e "${TAB}${RD}• Token will be shown in logs: journalctl -u livebook.service${CL}" -echo -e "${TAB}${RD}• Generated token: /data/token.txt${CL}" -echo -e "${TAB}${RD}• Configuration: /data/.env${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" +echo -e "\n${INFO}${YW} To start Livebook, run the following command:${CL}" +echo -e "${TAB}${BGN}sudo -u livebook /root/.mix/escripts/livebook server${CL}" +echo -e "\n${INFO}${YW} To run it as a service, create a systemd service file.${CL}" diff --git a/frontend/public/json/livebook.json b/frontend/public/json/livebook.json index 155d16906..432a06174 100644 --- a/frontend/public/json/livebook.json +++ b/frontend/public/json/livebook.json @@ -22,8 +22,8 @@ "cpu": 2, "ram": 1024, "hdd": 4, - "os": "Debian", - "version": "12" + "os": "Ubuntu", + "version": "24" } } ], diff --git a/install/livebook-install.sh b/install/livebook-install.sh index 61f96b92e..eacc992b4 100644 --- a/install/livebook-install.sh +++ b/install/livebook-install.sh @@ -2,7 +2,8 @@ # Copyright (c) 2021-2025 community-scripts ORG # Author: dkuku -# License: MIT | +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/livebook-dev/livebook source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color @@ -16,34 +17,88 @@ msg_info "Installing Dependencies (matching Livebook Dockerfile)" $STD apt-get install --no-install-recommends -y \ build-essential \ ca-certificates \ - libncurses5-dev \ - git \ - wget \ cmake \ - elixir + curl \ + git \ + libncurses5-dev msg_ok "Installed Dependencies" msg_info "Creating Livebook User and Directories" -useradd -r -s /bin/bash -d /home/livebook livebook -mkdir -p /home/livebook /data -chown livebook:livebook /home/livebook /data -# Make sure user has permissions to home dir (for Mix.install/2 cache) -chmod 777 /home/livebook +useradd -r -s /bin/bash -d /opt livebook +mkdir -p /opt /data +chown livebook:livebook /opt /data + +chmod 777 /opt msg_ok "Created Livebook User and Directories" -msg_info "Installing Livebook" -sudo -u livebook bash << 'EOF' -export HOME=/home/livebook -cd /home/livebook -# Install hex and rebar for Mix.install/2 and Mix runtime (matching Dockerfile) -mix local.hex --force -mix local.rebar --force -# Following official Livebook escript installation instructions -mix escript.install hex livebook -echo 'export PATH="$HOME/.mix/escripts:$PATH"' >> ~/.bashrc -source ~/.bashrc +msg_info "Installing Erlang and Elixir" +# Create a temporary script +cat > /tmp/setup_elixir.sh << 'EOF' +#!/bin/bash +export HOME=/opt +cd /opt +curl -fsSO https://elixir-lang.org/install.sh +sh install.sh elixir@1.18.4 otp@27.3.4 >/dev/null 2>&1 + +# Create .env if it doesn't exist and set permissions +touch $HOME/.env +chmod 644 $HOME/.env + +# Add exports to .env +echo 'export HOME=/opt' >> $HOME/.env +echo 'export PATH="$HOME/.elixir-install/installs/otp/27.3.4/bin:$HOME/.elixir-install/installs/elixir/1.18.4-otp-27/bin:$PATH"' >> $HOME/.env EOF -msg_ok "Installed Livebook" + +# Make it executable and run as livebook user +chmod +x /tmp/setup_elixir.sh +$STD sudo -u livebook -H /tmp/setup_elixir.sh +rm /tmp/setup_elixir.sh +msg_ok "Installed Erlang 27.3.4 and Elixir 1.18.4" + +msg_info "Installing Livebook" + +cat > /tmp/install_livebook.sh << 'EOF' +#!/bin/bash +RELEASE=$(curl -fsSL https://api.github.com/repos/livebook-dev/livebook/releases/latest | grep "tag_name" | awk -F'"' '{print $4}') +echo "${RELEASE}" >/opt/Livebook_version.txt + +set -e # Exit on any error +source /opt/.env +cd $HOME + +# Install hex and rebar for Mix.install/2 and Mix runtime (matching Dockerfile) +echo "Installing hex..." +mix local.hex --force +echo "Installing rebar..." +mix local.rebar --force + +# Following official Livebook escript installation instructions +echo "Installing Livebook escript..." +MIX_ENV=prod mix escript.install hex livebook --force + +# Add escripts to PATH +echo 'export PATH="$HOME/.mix/escripts:$PATH"' >> ~/.env + +# Verify livebook was installed and make executable +if [ -f ~/.mix/escripts/livebook ]; then + chmod +x ~/.mix/escripts/livebook + echo "Livebook escript installed successfully" + ls -la ~/.mix/escripts/livebook +else + echo "ERROR: Livebook escript not found after installation" + ls -la ~/.mix/escripts/ || echo "No escripts directory found" + # Try to show what went wrong + echo "Mix environment:" + mix --version + echo "Available packages:" + mix hex.info livebook || echo "Could not get livebook info" + exit 1 +fi +EOF + +chmod +x /tmp/install_livebook.sh +$STD sudo -u livebook -H /tmp/install_livebook.sh +rm /tmp/install_livebook.sh msg_info "Creating Livebook Service" cat </etc/systemd/system/livebook.service @@ -56,12 +111,14 @@ Type=exec User=livebook Group=livebook WorkingDirectory=/data -Environment=HOME=/home/livebook -Environment=PATH=/home/livebook/.mix/escripts:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +Environment=MIX_ENV=prod +Environment=HOME=/opt +Environment=PATH=/opt/.mix/escripts:/opt/.elixir-install/installs/otp/27.3.4/bin:/opt/.elixir-install/installs/elixir/1.18.4-otp-27/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin Environment=LIVEBOOK_PORT=8080 Environment=LIVEBOOK_IP="::" Environment=LIVEBOOK_HOME=/data -ExecStart=/home/livebook/.mix/escripts/livebook server +Environment=LIVEBOOK_TOKEN_ENABLED=false +ExecStart=/bin/bash -c 'cd /opt && livebook server' Restart=always RestartSec=5 StandardOutput=journal @@ -71,71 +128,21 @@ StandardError=journal WantedBy=multi-user.target EOF -systemctl daemon-reload -systemctl enable livebook.service +$STD systemctl enable livebook.service msg_ok "Created Livebook Service" -msg_info "Setting up Authentication" -# Generate a secure token for authentication -TOKEN=$(openssl rand -hex 32) -sudo -u livebook bash << EOF -cd /home/livebook -export HOME=/home/livebook -export PATH="\$HOME/.mix/escripts:\$PATH" -# Create environment file with authentication settings -cat > /data/.env << 'ENVEOF' -# Livebook Authentication Configuration -# Uncomment one of the following options: - -# Option 1: Password authentication (recommended for production) -# LIVEBOOK_PASSWORD=$TOKEN - -# Option 2: Token authentication (default - token will be shown in logs) -# LIVEBOOK_TOKEN_ENABLED=true - -# Option 3: Disable authentication (NOT recommended for production) -# LIVEBOOK_TOKEN_ENABLED=false - -# Current setting: Token authentication (default) -LIVEBOOK_TOKEN_ENABLED=true -ENVEOF - -# Save the token for easy access -echo "$TOKEN" > /data/token.txt -chmod 600 /data/token.txt -chown livebook:livebook /data/.env /data/token.txt -EOF -msg_ok "Set up Authentication" - -msg_info "Starting Livebook Service" -systemctl start livebook.service -msg_ok "Started Livebook Service" - msg_info "Cleaning Up" -rm -f /tmp/erlang-solutions_2.0_all.deb +rm -f /opt/install.sh $STD apt-get autoremove -y $STD apt-get autoclean msg_ok "Cleaned Up" +msg_info "Starting Livebook Service" +$STD systemctl start livebook.service +msg_ok "Started Livebook Service" + + motd_ssh customize -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" - echo -e "\n${CREATING}${GN}Livebook Installation Complete!${CL}\n" -echo -e "${INFO}${YW}Authentication Information:${CL}" -echo -e "${TAB}${RD}• Default: Token authentication (auto-generated)${CL}" -echo -e "${TAB}${RD}• Token will be displayed in Livebook logs on startup${CL}" -echo -e "${TAB}${RD}• Generated token saved to: /data/token.txt${CL}" -echo -e "${TAB}${RD}• Configuration file: /data/.env${CL}\n" - -echo -e "${INFO}${YW}To configure authentication:${CL}" -echo -e "${TAB}${RD}1. Password auth: Edit /data/.env and uncomment LIVEBOOK_PASSWORD${CL}" -echo -e "${TAB}${RD}2. No auth: Edit /data/.env and set LIVEBOOK_TOKEN_ENABLED=false${CL}" -echo -e "${TAB}${RD}3. Restart service: systemctl restart livebook.service${CL}\n" - -echo -e "${INFO}${YW}Generated Token (for reference):${CL}" -echo -e "${TAB}${GN}$(cat /data/token.txt)${CL}\n" diff --git a/misc/build.func b/misc/build.func index c40a57bd1..49b20b7fa 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1438,7 +1438,7 @@ EOF' fi msg_ok "Customized LXC Container" - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)" + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/install/${var_install}.sh)" } destroy_lxc() { diff --git a/misc/install.func b/misc/install.func index e3751c295..48c196cf1 100644 --- a/misc/install.func +++ b/misc/install.func @@ -31,7 +31,7 @@ catch_errors() { # This function handles errors error_handler() { - source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/api.func) + source <(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/misc/api.func) printf "\e[?25h" local exit_code="$?" local line_number="$1" From b6db98b8dc1ec8667924ac6b94aaa6c214afbd6d Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Wed, 13 Aug 2025 19:49:06 +0200 Subject: [PATCH 0519/1733] update logic --- ct/livebook.sh | 49 ++--- frontend/public/json/livebook.json | 7 +- install.sh | 298 +++++++++++++++++++++++++++++ install/livebook-install.sh | 81 ++------ misc/build.func | 14 +- misc/install.func | 6 +- 6 files changed, 338 insertions(+), 117 deletions(-) create mode 100755 install.sh diff --git a/ct/livebook.sh b/ct/livebook.sh index 177db87f8..2899724d0 100755 --- a/ct/livebook.sh +++ b/ct/livebook.sh @@ -5,6 +5,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/head # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/livebook-dev/livebook +echo -e "Loading..." APP="Livebook" var_tags="${var_tags:-development}" var_disk="${var_disk:-4}" @@ -25,7 +26,7 @@ function update_script() { check_container_resources # Check if Livebook is installed - if [[ ! -d /opt/${APP}_version.txt ]]; then + if [[ ! -f /opt/${APP}_version.txt ]]; then msg_error "No ${APP} Installation Found!" exit 1 fi @@ -40,41 +41,24 @@ function update_script() { fi # Check if version file exists and compare versions - if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt 2>/dev/null)" ]]; then - msg_info "Updating ${APP} to v${RELEASE}" + if [[ "${RELEASE}" == "$(cat /opt/${APP}_version.txt 2>/dev/null)" ]]; then + #if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt 2>/dev/null)" ]]; then + msg_info "Updating ${APP} LXC" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Updated ${APP} LXC" - # Create backup of user data if it exists - if [[ -d /home/livebook ]]; then - msg_info "Creating backup of user data..." - $STD cp -r /home/livebook /home/livebook-backup - fi - - # Perform the update - msg_info "Installing dependencies and updating Livebook..." - if ! sudo -u livebook bash -c ' - export HOME=/home/livebook - cd /home/livebook - mix local.hex --force >/dev/null 2>&1 - mix local.rebar --force >/dev/null 2>&1 - mix escript.install hex livebook --force >/dev/null 2>&1 - '; then - msg_error "Failed to update Livebook" - # Restore from backup if update failed - if [[ -d /home/livebook-backup ]]; then - msg_info "Restoring from backup..." - rm -rf /home/livebook - mv /home/livebook-backup /home/livebook - fi - exit 1 - fi + msg_info "Updating ${APP} to ${RELEASE}" + source /opt/.env + cd /opt || exit 1 + mix escript.install hex livebook --force >/dev/null 2>&1 # Save the new version echo "$RELEASE" | $STD tee /opt/${APP}_version.txt >/dev/null # Cleanup backup if update was successful - if [[ -d /home/livebook-backup ]]; then - msg_info "Cleaning up backup..." - $STD rm -rf /home/livebook-backup + if [[ -d /opt-backup ]]; then + $STD rm -rf /opt-backup fi msg_ok "Successfully updated to v${RELEASE}" @@ -92,7 +76,4 @@ description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" -echo -e "\n${INFO}${YW} To start Livebook, run the following command:${CL}" -echo -e "${TAB}${BGN}sudo -u livebook /root/.mix/escripts/livebook server${CL}" -echo -e "\n${INFO}${YW} To run it as a service, create a systemd service file.${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" diff --git a/frontend/public/json/livebook.json b/frontend/public/json/livebook.json index 432a06174..f5599d2b7 100644 --- a/frontend/public/json/livebook.json +++ b/frontend/public/json/livebook.json @@ -29,14 +29,13 @@ ], "default_credentials": { "username": null, - "password": "Check /data/token.txt" + "password": null }, "notes": [ - "Access token is stored in /data/token.txt", "Default port is 8080", "Working directory is /data", - "Home directory is /home/livebook", + "Home directory is /opt", "Elixir runtime with Mix.install/2 support", - "Service runs as livebook user" + "Service runs as root user" ] } diff --git a/install.sh b/install.sh new file mode 100755 index 000000000..f1647a15f --- /dev/null +++ b/install.sh @@ -0,0 +1,298 @@ +#!/bin/sh +# See latest version at: +# https://github.com/elixir-lang/elixir-lang.github.com/blob/main/install.sh + +set -eu + +otp_version= +elixir_version= +force=false + +usage() { + cat<&2 + exit 1 + ;; + esac + done + + if [ -z "${elixir_version}" ]; then + usage + echo "error: missing elixir@VERSION argument" + exit 1 + fi + + if [ -z "${otp_version}" ]; then + usage + echo "error: missing otp@VERSION argument" + exit 1 + fi + + root_dir="$HOME/.elixir-install" + tmp_dir="$root_dir/tmp" + mkdir -p "$tmp_dir" + + if [ "${otp_version}" = latest ]; then + url=$(curl -fsS --head https://github.com/erlef/otp_builds/releases/latest | grep -i '^location:' | awk '{print $2}' | tr -d '\r\n') + tag=$(basename "$url") + otp_version="${tag#OTP-}" + fi + + if [ "${elixir_version}" = latest ]; then + url=$(curl -fsS --head https://github.com/elixir-lang/elixir/releases/latest | grep -i '^location:' | awk '{print $2}' | tr -d '\r\n') + tag=$(basename "$url") + elixir_version="${tag#v}" + fi + + case "${otp_version}" in + master|maint*) + branch_version=$(curl -fsS https://raw.githubusercontent.com/erlang/otp/refs/heads/${otp_version}/OTP_VERSION | tr -d '\n') + elixir_otp_release="${branch_version%%.*}" + ;; + *) + elixir_otp_release="${otp_version%%.*}" + ;; + esac + + case "$elixir_version" in + 1.14.*) + [ "${elixir_otp_release}" -ge 25 ] && elixir_otp_release=25 + ;; + 1.15.*|1.16.*) + [ "${elixir_otp_release}" -ge 26 ] && elixir_otp_release=26 + ;; + 1.17.*|1.18.*) + [ "${elixir_otp_release}" -ge 27 ] && elixir_otp_release=27 + ;; + 1.19.*) + [ "${elixir_otp_release}" -ge 28 ] && elixir_otp_release=28 + ;; + *) + [ "${elixir_otp_release}" -ge 28 ] && elixir_otp_release=28 + ;; + esac + + otp_dir="$root_dir/installs/otp/$otp_version" + elixir_dir="${root_dir}/installs/elixir/${elixir_version}-otp-${elixir_otp_release}" + + if unzip_available; then + install_otp & + pid_otp=$! + + install_elixir & + pid_elixir=$! + + wait $pid_otp + wait $pid_elixir + else + # if unzip is missing (e.g. official docker ubuntu image), install otp and elixir + # serially because we unzip elixir using OTP zip:extract/2. + install_otp + install_elixir + fi + + printf "checking OTP... " + export PATH="$otp_dir/bin:$PATH" + erl -noshell -eval 'io:put_chars(erlang:system_info(otp_release) ++ " ok\n"), halt().' + + printf "checking Elixir... " + "$elixir_dir/bin/elixir" -e 'IO.puts(System.version() <> " ok")' + + export PATH="$elixir_dir/bin:$PATH" +cat</dev/null 2>&1 +} + +main "$@" diff --git a/install/livebook-install.sh b/install/livebook-install.sh index eacc992b4..20ae054ec 100644 --- a/install/livebook-install.sh +++ b/install/livebook-install.sh @@ -23,82 +23,29 @@ $STD apt-get install --no-install-recommends -y \ libncurses5-dev msg_ok "Installed Dependencies" -msg_info "Creating Livebook User and Directories" -useradd -r -s /bin/bash -d /opt livebook -mkdir -p /opt /data -chown livebook:livebook /opt /data - -chmod 777 /opt -msg_ok "Created Livebook User and Directories" msg_info "Installing Erlang and Elixir" -# Create a temporary script -cat > /tmp/setup_elixir.sh << 'EOF' -#!/bin/bash +mkdir -p /opt /data export HOME=/opt -cd /opt +touch $HOME/.env +cd /opt || exit 1 curl -fsSO https://elixir-lang.org/install.sh sh install.sh elixir@1.18.4 otp@27.3.4 >/dev/null 2>&1 - -# Create .env if it doesn't exist and set permissions -touch $HOME/.env -chmod 644 $HOME/.env - -# Add exports to .env echo 'export HOME=/opt' >> $HOME/.env -echo 'export PATH="$HOME/.elixir-install/installs/otp/27.3.4/bin:$HOME/.elixir-install/installs/elixir/1.18.4-otp-27/bin:$PATH"' >> $HOME/.env -EOF - -# Make it executable and run as livebook user -chmod +x /tmp/setup_elixir.sh -$STD sudo -u livebook -H /tmp/setup_elixir.sh -rm /tmp/setup_elixir.sh +echo 'export PATH="/opt/.elixir-install/installs/otp/27.3.4/bin:/opt/.elixir-install/installs/elixir/1.18.4-otp-27/bin:$PATH"' >> $HOME/.env msg_ok "Installed Erlang 27.3.4 and Elixir 1.18.4" msg_info "Installing Livebook" - -cat > /tmp/install_livebook.sh << 'EOF' -#!/bin/bash RELEASE=$(curl -fsSL https://api.github.com/repos/livebook-dev/livebook/releases/latest | grep "tag_name" | awk -F'"' '{print $4}') echo "${RELEASE}" >/opt/Livebook_version.txt -set -e # Exit on any error source /opt/.env -cd $HOME - -# Install hex and rebar for Mix.install/2 and Mix runtime (matching Dockerfile) -echo "Installing hex..." -mix local.hex --force -echo "Installing rebar..." -mix local.rebar --force - -# Following official Livebook escript installation instructions -echo "Installing Livebook escript..." -MIX_ENV=prod mix escript.install hex livebook --force - -# Add escripts to PATH +cd /opt || exit 1 +mix local.hex --force >/dev/null 2>&1 +mix local.rebar --force >/dev/null 2>&1 +mix escript.install hex livebook --force >/dev/null 2>&1 echo 'export PATH="$HOME/.mix/escripts:$PATH"' >> ~/.env - -# Verify livebook was installed and make executable -if [ -f ~/.mix/escripts/livebook ]; then - chmod +x ~/.mix/escripts/livebook - echo "Livebook escript installed successfully" - ls -la ~/.mix/escripts/livebook -else - echo "ERROR: Livebook escript not found after installation" - ls -la ~/.mix/escripts/ || echo "No escripts directory found" - # Try to show what went wrong - echo "Mix environment:" - mix --version - echo "Available packages:" - mix hex.info livebook || echo "Could not get livebook info" - exit 1 -fi -EOF - -chmod +x /tmp/install_livebook.sh -$STD sudo -u livebook -H /tmp/install_livebook.sh -rm /tmp/install_livebook.sh +msg_ok "Installed Livebook" msg_info "Creating Livebook Service" cat </etc/systemd/system/livebook.service @@ -108,8 +55,8 @@ After=network.target [Service] Type=exec -User=livebook -Group=livebook +User=root +Group=root WorkingDirectory=/data Environment=MIX_ENV=prod Environment=HOME=/opt @@ -129,6 +76,7 @@ WantedBy=multi-user.target EOF $STD systemctl enable livebook.service +$STD systemctl start livebook.service msg_ok "Created Livebook Service" msg_info "Cleaning Up" @@ -137,11 +85,6 @@ $STD apt-get autoremove -y $STD apt-get autoclean msg_ok "Cleaned Up" -msg_info "Starting Livebook Service" -$STD systemctl start livebook.service -msg_ok "Started Livebook Service" - - motd_ssh customize diff --git a/misc/build.func b/misc/build.func index 49b20b7fa..c03764f82 100644 --- a/misc/build.func +++ b/misc/build.func @@ -16,10 +16,10 @@ variables() { CT_TYPE=${var_unprivileged:-$CT_TYPE} } -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) +source <(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/misc/api.func) if command -v curl >/dev/null 2>&1; then - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + source <(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/misc/core.func) load_functions #echo "(build.func) Loaded core.func via curl" elif command -v wget >/dev/null 2>&1; then @@ -988,7 +988,7 @@ install_script() { header_info echo -e "${INFO}${HOLD} ${GN}Using Config File on node $PVEHOST_NAME${CL}" METHOD="advanced" - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/config-file.func) + source <(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/misc/config-file.func) config_file ;; 5) @@ -1061,7 +1061,7 @@ check_container_storage() { } start() { - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) + source <(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/misc/tools.func) if command -v pveversion >/dev/null 2>&1; then install_script elif [ ! -z ${PHS_SILENT+x} ] && [[ "${PHS_SILENT}" == "1" ]]; then @@ -1127,9 +1127,9 @@ build_container() { TEMP_DIR=$(mktemp -d) pushd "$TEMP_DIR" >/dev/null if [ "$var_os" == "alpine" ]; then - export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-install.func)" + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/misc/alpine-install.func)" else - export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/misc/install.func)" fi export DIAGNOSTICS="$DIAGNOSTICS" export RANDOM_UUID="$RANDOM_UUID" @@ -1163,7 +1163,7 @@ build_container() { -unprivileged $CT_TYPE $PW " - bash -c "$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/create_lxc.sh)" || exit + bash -c "$(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/misc/create_lxc.sh)" || exit if [ $? -ne 0 ]; then exit 200 fi diff --git a/misc/install.func b/misc/install.func index 48c196cf1..b38ff3362 100644 --- a/misc/install.func +++ b/misc/install.func @@ -10,7 +10,7 @@ if ! command -v curl >/dev/null 2>&1; then apt-get update >/dev/null 2>&1 apt-get install -y curl >/dev/null 2>&1 fi -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) +source <(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/misc/core.func) load_functions # This function enables IPv6 if it's not disabled and sets verbose mode @@ -148,7 +148,7 @@ EOF $STD apt-get -o Dpkg::Options::="--force-confold" -y dist-upgrade rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED msg_ok "Updated Container OS" - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) + source <(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/misc/tools.func) } # This function modifies the message of the day (motd) and SSH settings @@ -196,7 +196,7 @@ EOF systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//') msg_ok "Customized Container" fi - echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update + echo "bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/ct/${app}.sh)\"" >/usr/bin/update chmod +x /usr/bin/update if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then mkdir -p /root/.ssh From 12d5850edec8610e9b56d0e9dbed5375612990df Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Wed, 13 Aug 2025 19:50:20 +0200 Subject: [PATCH 0520/1733] unused --- ct/livebook.sh | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ct/livebook.sh b/ct/livebook.sh index 2899724d0..79007951b 100755 --- a/ct/livebook.sh +++ b/ct/livebook.sh @@ -56,11 +56,6 @@ function update_script() { # Save the new version echo "$RELEASE" | $STD tee /opt/${APP}_version.txt >/dev/null - # Cleanup backup if update was successful - if [[ -d /opt-backup ]]; then - $STD rm -rf /opt-backup - fi - msg_ok "Successfully updated to v${RELEASE}" else msg_ok "No update required. ${APP} is already at v${RELEASE}." From 31e3851909b91f4fa336d89b86a278f18ee6aff4 Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Wed, 13 Aug 2025 20:00:59 +0200 Subject: [PATCH 0521/1733] switch to proper update --- ct/livebook.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ct/livebook.sh b/ct/livebook.sh index 79007951b..81bc3e2a3 100755 --- a/ct/livebook.sh +++ b/ct/livebook.sh @@ -41,8 +41,7 @@ function update_script() { fi # Check if version file exists and compare versions - if [[ "${RELEASE}" == "$(cat /opt/${APP}_version.txt 2>/dev/null)" ]]; then - #if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt 2>/dev/null)" ]]; then + if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt 2>/dev/null)" ]]; then msg_info "Updating ${APP} LXC" $STD apt-get update $STD apt-get -y upgrade @@ -51,14 +50,15 @@ function update_script() { msg_info "Updating ${APP} to ${RELEASE}" source /opt/.env cd /opt || exit 1 + rm /opt/.mix/escripts/livebook mix escript.install hex livebook --force >/dev/null 2>&1 # Save the new version echo "$RELEASE" | $STD tee /opt/${APP}_version.txt >/dev/null - msg_ok "Successfully updated to v${RELEASE}" + msg_ok "Successfully updated to ${RELEASE}" else - msg_ok "No update required. ${APP} is already at v${RELEASE}." + msg_ok "No update required. ${APP} is already at ${RELEASE}." fi exit From 9fb30f587f706e8abaa8b2f239e54538e0d563d2 Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Wed, 13 Aug 2025 20:07:00 +0200 Subject: [PATCH 0522/1733] reset function --- misc/build.func | 16 ++++++++-------- misc/install.func | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/misc/build.func b/misc/build.func index c03764f82..c40a57bd1 100644 --- a/misc/build.func +++ b/misc/build.func @@ -16,10 +16,10 @@ variables() { CT_TYPE=${var_unprivileged:-$CT_TYPE} } -source <(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/misc/api.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) if command -v curl >/dev/null 2>&1; then - source <(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/misc/core.func) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) load_functions #echo "(build.func) Loaded core.func via curl" elif command -v wget >/dev/null 2>&1; then @@ -988,7 +988,7 @@ install_script() { header_info echo -e "${INFO}${HOLD} ${GN}Using Config File on node $PVEHOST_NAME${CL}" METHOD="advanced" - source <(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/misc/config-file.func) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/config-file.func) config_file ;; 5) @@ -1061,7 +1061,7 @@ check_container_storage() { } start() { - source <(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/misc/tools.func) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) if command -v pveversion >/dev/null 2>&1; then install_script elif [ ! -z ${PHS_SILENT+x} ] && [[ "${PHS_SILENT}" == "1" ]]; then @@ -1127,9 +1127,9 @@ build_container() { TEMP_DIR=$(mktemp -d) pushd "$TEMP_DIR" >/dev/null if [ "$var_os" == "alpine" ]; then - export FUNCTIONS_FILE_PATH="$(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/misc/alpine-install.func)" + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-install.func)" else - export FUNCTIONS_FILE_PATH="$(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/misc/install.func)" + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" fi export DIAGNOSTICS="$DIAGNOSTICS" export RANDOM_UUID="$RANDOM_UUID" @@ -1163,7 +1163,7 @@ build_container() { -unprivileged $CT_TYPE $PW " - bash -c "$(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/misc/create_lxc.sh)" || exit + bash -c "$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/create_lxc.sh)" || exit if [ $? -ne 0 ]; then exit 200 fi @@ -1438,7 +1438,7 @@ EOF' fi msg_ok "Customized LXC Container" - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/install/${var_install}.sh)" + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)" } destroy_lxc() { diff --git a/misc/install.func b/misc/install.func index b38ff3362..e3751c295 100644 --- a/misc/install.func +++ b/misc/install.func @@ -10,7 +10,7 @@ if ! command -v curl >/dev/null 2>&1; then apt-get update >/dev/null 2>&1 apt-get install -y curl >/dev/null 2>&1 fi -source <(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/misc/core.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) load_functions # This function enables IPv6 if it's not disabled and sets verbose mode @@ -31,7 +31,7 @@ catch_errors() { # This function handles errors error_handler() { - source <(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/misc/api.func) + source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/api.func) printf "\e[?25h" local exit_code="$?" local line_number="$1" @@ -148,7 +148,7 @@ EOF $STD apt-get -o Dpkg::Options::="--force-confold" -y dist-upgrade rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED msg_ok "Updated Container OS" - source <(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/misc/tools.func) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) } # This function modifies the message of the day (motd) and SSH settings @@ -196,7 +196,7 @@ EOF systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//') msg_ok "Customized Container" fi - echo "bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/ct/${app}.sh)\"" >/usr/bin/update + echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update chmod +x /usr/bin/update if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then mkdir -p /root/.ssh From 95030997c5159f019ee6d123e892944e4fcd3b97 Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Wed, 13 Aug 2025 22:04:15 +0200 Subject: [PATCH 0523/1733] cr suggetions --- ct/headers/livebook | 6 - ct/livebook.sh | 10 +- frontend/public/json/livebook.json | 24 +-- install.sh | 298 ----------------------------- install/livebook-install.sh | 32 ++-- 5 files changed, 26 insertions(+), 344 deletions(-) delete mode 100644 ct/headers/livebook delete mode 100755 install.sh diff --git a/ct/headers/livebook b/ct/headers/livebook deleted file mode 100644 index 7b50abfd0..000000000 --- a/ct/headers/livebook +++ /dev/null @@ -1,6 +0,0 @@ - __ _ __ __ - / / (_) _____ / /_ ____ ____ / /__ - / / / / | / / _ \/ __ \/ __ \/ __ \/ //_/ - / /___/ /| |/ / __/ /_/ / /_/ / /_/ / ,< -/_____/_/ |___/\___/_.___/\____/\____/_/|_| - diff --git a/ct/livebook.sh b/ct/livebook.sh index 81bc3e2a3..3ed7697d8 100755 --- a/ct/livebook.sh +++ b/ct/livebook.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/refs/heads/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: dkuku # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -25,13 +25,11 @@ function update_script() { check_container_storage check_container_resources - # Check if Livebook is installed - if [[ ! -f /opt/${APP}_version.txt ]]; then + if [[ ! -f /opt/.mix/escripts/livebook ]]; then msg_error "No ${APP} Installation Found!" exit 1 fi - # Get the latest version from GitHub msg_info "Checking for updates..." RELEASE=$(curl -fsSL https://api.github.com/repos/livebook-dev/livebook/releases/latest | grep "tag_name" | awk -F'"' '{print $4}') @@ -40,7 +38,6 @@ function update_script() { exit 1 fi - # Check if version file exists and compare versions if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt 2>/dev/null)" ]]; then msg_info "Updating ${APP} LXC" $STD apt-get update @@ -51,9 +48,8 @@ function update_script() { source /opt/.env cd /opt || exit 1 rm /opt/.mix/escripts/livebook - mix escript.install hex livebook --force >/dev/null 2>&1 + mix escript.install hex livebook --force - # Save the new version echo "$RELEASE" | $STD tee /opt/${APP}_version.txt >/dev/null msg_ok "Successfully updated to ${RELEASE}" diff --git a/frontend/public/json/livebook.json b/frontend/public/json/livebook.json index f5599d2b7..db4159ca3 100644 --- a/frontend/public/json/livebook.json +++ b/frontend/public/json/livebook.json @@ -2,28 +2,28 @@ "name": "Livebook", "slug": "livebook", "categories": [ - 8 + 20 ], - "date_created": "2025-08-10", + "date_created": "2025-08-12", "type": "ct", "updateable": true, "privileged": false, - "config_path": "/data/.env", "interface_port": 8080, - "documentation": "https://livebook.dev/", - "website": "https://livebook.dev/", + "documentation": null, + "config_path": "/opt/.env", + "website": "https://livebook.dev", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/livebook.svg", - "description": "Livebook is a web application for writing interactive and collaborative code notebooks for Elixir.", + "description": "Elixir Livebook is an interactive, web-based notebook platform for Elixir that combines code, documentation, and visualizations in a single document. Similar to Jupyter notebooks, it allows developers to write and execute Elixir code in real-time, making it ideal for data exploration, prototyping, learning, and collaborative development. Livebook features rich markdown support, built-in charting capabilities, and seamless integration with the Elixir ecosystem.", "install_methods": [ { "type": "default", "script": "ct/livebook.sh", "resources": { - "cpu": 2, + "cpu": 1, "ram": 1024, "hdd": 4, "os": "Ubuntu", - "version": "24" + "version": "24.04" } } ], @@ -31,11 +31,5 @@ "username": null, "password": null }, - "notes": [ - "Default port is 8080", - "Working directory is /data", - "Home directory is /opt", - "Elixir runtime with Mix.install/2 support", - "Service runs as root user" - ] + "notes": [] } diff --git a/install.sh b/install.sh deleted file mode 100755 index f1647a15f..000000000 --- a/install.sh +++ /dev/null @@ -1,298 +0,0 @@ -#!/bin/sh -# See latest version at: -# https://github.com/elixir-lang/elixir-lang.github.com/blob/main/install.sh - -set -eu - -otp_version= -elixir_version= -force=false - -usage() { - cat<&2 - exit 1 - ;; - esac - done - - if [ -z "${elixir_version}" ]; then - usage - echo "error: missing elixir@VERSION argument" - exit 1 - fi - - if [ -z "${otp_version}" ]; then - usage - echo "error: missing otp@VERSION argument" - exit 1 - fi - - root_dir="$HOME/.elixir-install" - tmp_dir="$root_dir/tmp" - mkdir -p "$tmp_dir" - - if [ "${otp_version}" = latest ]; then - url=$(curl -fsS --head https://github.com/erlef/otp_builds/releases/latest | grep -i '^location:' | awk '{print $2}' | tr -d '\r\n') - tag=$(basename "$url") - otp_version="${tag#OTP-}" - fi - - if [ "${elixir_version}" = latest ]; then - url=$(curl -fsS --head https://github.com/elixir-lang/elixir/releases/latest | grep -i '^location:' | awk '{print $2}' | tr -d '\r\n') - tag=$(basename "$url") - elixir_version="${tag#v}" - fi - - case "${otp_version}" in - master|maint*) - branch_version=$(curl -fsS https://raw.githubusercontent.com/erlang/otp/refs/heads/${otp_version}/OTP_VERSION | tr -d '\n') - elixir_otp_release="${branch_version%%.*}" - ;; - *) - elixir_otp_release="${otp_version%%.*}" - ;; - esac - - case "$elixir_version" in - 1.14.*) - [ "${elixir_otp_release}" -ge 25 ] && elixir_otp_release=25 - ;; - 1.15.*|1.16.*) - [ "${elixir_otp_release}" -ge 26 ] && elixir_otp_release=26 - ;; - 1.17.*|1.18.*) - [ "${elixir_otp_release}" -ge 27 ] && elixir_otp_release=27 - ;; - 1.19.*) - [ "${elixir_otp_release}" -ge 28 ] && elixir_otp_release=28 - ;; - *) - [ "${elixir_otp_release}" -ge 28 ] && elixir_otp_release=28 - ;; - esac - - otp_dir="$root_dir/installs/otp/$otp_version" - elixir_dir="${root_dir}/installs/elixir/${elixir_version}-otp-${elixir_otp_release}" - - if unzip_available; then - install_otp & - pid_otp=$! - - install_elixir & - pid_elixir=$! - - wait $pid_otp - wait $pid_elixir - else - # if unzip is missing (e.g. official docker ubuntu image), install otp and elixir - # serially because we unzip elixir using OTP zip:extract/2. - install_otp - install_elixir - fi - - printf "checking OTP... " - export PATH="$otp_dir/bin:$PATH" - erl -noshell -eval 'io:put_chars(erlang:system_info(otp_release) ++ " ok\n"), halt().' - - printf "checking Elixir... " - "$elixir_dir/bin/elixir" -e 'IO.puts(System.version() <> " ok")' - - export PATH="$elixir_dir/bin:$PATH" -cat</dev/null 2>&1 -} - -main "$@" diff --git a/install/livebook-install.sh b/install/livebook-install.sh index 20ae054ec..677930032 100644 --- a/install/livebook-install.sh +++ b/install/livebook-install.sh @@ -14,36 +14,35 @@ network_check update_os msg_info "Installing Dependencies (matching Livebook Dockerfile)" -$STD apt-get install --no-install-recommends -y \ +$STD apt-get install -y \ build-essential \ ca-certificates \ cmake \ - curl \ git \ libncurses5-dev msg_ok "Installed Dependencies" - -msg_info "Installing Erlang and Elixir" +msg_info "Installing Erlang and Elixir +ELIXIR_VERSION=1.18.4-otp-27 +ERLANG_VERSION=27.3.4 mkdir -p /opt /data export HOME=/opt touch $HOME/.env cd /opt || exit 1 curl -fsSO https://elixir-lang.org/install.sh -sh install.sh elixir@1.18.4 otp@27.3.4 >/dev/null 2>&1 +$STD sh install.sh elixir@$ELXIR_VERSION otp@$ERLANG_VERSION echo 'export HOME=/opt' >> $HOME/.env -echo 'export PATH="/opt/.elixir-install/installs/otp/27.3.4/bin:/opt/.elixir-install/installs/elixir/1.18.4-otp-27/bin:$PATH"' >> $HOME/.env -msg_ok "Installed Erlang 27.3.4 and Elixir 1.18.4" +echo 'export PATH="/opt/.elixir-install/installs/otp/${ERLANG_VERSION}/bin:/opt/.elixir-install/installs/elixir/${ELIXIR_VERSION}/bin:$PATH"' >> $HOME/.env +msg_ok "Installed Erlang and Elixir" msg_info "Installing Livebook" RELEASE=$(curl -fsSL https://api.github.com/repos/livebook-dev/livebook/releases/latest | grep "tag_name" | awk -F'"' '{print $4}') -echo "${RELEASE}" >/opt/Livebook_version.txt source /opt/.env cd /opt || exit 1 -mix local.hex --force >/dev/null 2>&1 -mix local.rebar --force >/dev/null 2>&1 -mix escript.install hex livebook --force >/dev/null 2>&1 +$STD mix local.hex --force +$STD mix local.rebar --force +$STD mix escript.install hex livebook --force echo 'export PATH="$HOME/.mix/escripts:$PATH"' >> ~/.env msg_ok "Installed Livebook" @@ -75,17 +74,14 @@ StandardError=journal WantedBy=multi-user.target EOF -$STD systemctl enable livebook.service -$STD systemctl start livebook.service +systemctl enable -q --now livebook msg_ok "Created Livebook Service" +motd_ssh +customize + msg_info "Cleaning Up" rm -f /opt/install.sh $STD apt-get autoremove -y $STD apt-get autoclean msg_ok "Cleaned Up" - -motd_ssh -customize - -echo -e "\n${CREATING}${GN}Livebook Installation Complete!${CL}\n" From aa25f6d3e3c3aa2fb0ecffbd51289d616a078795 Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Sun, 17 Aug 2025 07:50:40 +0200 Subject: [PATCH 0524/1733] test new logic --- ct/livebook.sh | 3 +- install/livebook-install.sh | 61 +++++++++++++++++++++---------------- misc/build.func | 2 +- misc/install.func | 3 +- 4 files changed, 40 insertions(+), 29 deletions(-) diff --git a/ct/livebook.sh b/ct/livebook.sh index 3ed7697d8..ea387437e 100755 --- a/ct/livebook.sh +++ b/ct/livebook.sh @@ -1,5 +1,6 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/refs/heads/main/misc/build.func) +#source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/refs/heads/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: dkuku # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/install/livebook-install.sh b/install/livebook-install.sh index 677930032..84db422bd 100644 --- a/install/livebook-install.sh +++ b/install/livebook-install.sh @@ -22,31 +22,48 @@ $STD apt-get install -y \ libncurses5-dev msg_ok "Installed Dependencies" -msg_info "Installing Erlang and Elixir -ELIXIR_VERSION=1.18.4-otp-27 -ERLANG_VERSION=27.3.4 +msg_info "Installing Erlang and Elixir" + mkdir -p /opt /data export HOME=/opt -touch $HOME/.env cd /opt || exit 1 -curl -fsSO https://elixir-lang.org/install.sh -$STD sh install.sh elixir@$ELXIR_VERSION otp@$ERLANG_VERSION -echo 'export HOME=/opt' >> $HOME/.env -echo 'export PATH="/opt/.elixir-install/installs/otp/${ERLANG_VERSION}/bin:/opt/.elixir-install/installs/elixir/${ELIXIR_VERSION}/bin:$PATH"' >> $HOME/.env -msg_ok "Installed Erlang and Elixir" -msg_info "Installing Livebook" +curl -fsSO https://elixir-lang.org/install.sh +$STD sh install.sh elixir@latest otp@latest RELEASE=$(curl -fsSL https://api.github.com/repos/livebook-dev/livebook/releases/latest | grep "tag_name" | awk -F'"' '{print $4}') -source /opt/.env -cd /opt || exit 1 $STD mix local.hex --force $STD mix local.rebar --force $STD mix escript.install hex livebook --force -echo 'export PATH="$HOME/.mix/escripts:$PATH"' >> ~/.env -msg_ok "Installed Livebook" -msg_info "Creating Livebook Service" +# Get the actual installed versions from directory names +ERLANG_VERSION=$(ls /opt/.elixir-install/installs/otp/ | head -n1) +ELIXIR_VERSION=$(ls /opt/.elixir-install/installs/elixir/ | head -n1) + +# TODO remove +echo "Found Erlang version: $ERLANG_VERSION" +echo "Found Elixir version: $ELIXIR_VERSION" + +# Create .env file with all environment variables +cat < /opt/.env +export HOME=/opt +export LIVEBOOK_VERSION=$RELEASE +export ERLANG_VERSION=$ERLANG_VERSION +export ELIXIR_VERSION=$ELIXIR_VERSION +export LIVEBOOK_PORT=8080 +export LIVEBOOK_IP="::" +export LIVEBOOK_HOME=/data +export LIVEBOOK_TOKEN_ENABLED=false +export ESCRIPTS_BIN=/opt/.mix/escripts +export ERLANG_BIN="/opt/.elixir-install/installs/otp/\${ERLANG_VERSION}/bin" +export ELIXIR_BIN="/opt/.elixir-install/installs/elixir/\${ELIXIR_VERSION}/bin" +export PATH="\$ESCRIPTS_BIN:\$ERLANG_BIN:\$ELIXIR_BIN:\$PATH" +EOF + +msg_ok "Installed Erlang $ERLANG_VERSION and Elixir $ELIXIR_VERSION" + +msg_info "Installing Livebook" + cat </etc/systemd/system/livebook.service [Unit] Description=Livebook @@ -57,25 +74,17 @@ Type=exec User=root Group=root WorkingDirectory=/data -Environment=MIX_ENV=prod -Environment=HOME=/opt -Environment=PATH=/opt/.mix/escripts:/opt/.elixir-install/installs/otp/27.3.4/bin:/opt/.elixir-install/installs/elixir/1.18.4-otp-27/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin -Environment=LIVEBOOK_PORT=8080 -Environment=LIVEBOOK_IP="::" -Environment=LIVEBOOK_HOME=/data -Environment=LIVEBOOK_TOKEN_ENABLED=false -ExecStart=/bin/bash -c 'cd /opt && livebook server' +EnvironmentFile=-/opt/.env +ExecStart=/bin/bash -c 'source /opt/.env && cd /opt && livebook server' Restart=always RestartSec=5 -StandardOutput=journal -StandardError=journal [Install] WantedBy=multi-user.target EOF systemctl enable -q --now livebook -msg_ok "Created Livebook Service" +msg_ok "Installed Livebook" motd_ssh customize diff --git a/misc/build.func b/misc/build.func index c40a57bd1..49b20b7fa 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1438,7 +1438,7 @@ EOF' fi msg_ok "Customized LXC Container" - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)" + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/install/${var_install}.sh)" } destroy_lxc() { diff --git a/misc/install.func b/misc/install.func index e3751c295..9b6bd60e4 100644 --- a/misc/install.func +++ b/misc/install.func @@ -196,7 +196,8 @@ EOF systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//') msg_ok "Customized Container" fi - echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update + # echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update + echo "bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/ct/${app}.sh)\"" >/usr/bin/update chmod +x /usr/bin/update if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then mkdir -p /root/.ssh From 5871cdbe5b6fdf3132fd53146c0d658458408df8 Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Sun, 17 Aug 2025 08:29:29 +0200 Subject: [PATCH 0525/1733] export path --- install/livebook-install.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/install/livebook-install.sh b/install/livebook-install.sh index 84db422bd..1ee988184 100644 --- a/install/livebook-install.sh +++ b/install/livebook-install.sh @@ -32,10 +32,6 @@ curl -fsSO https://elixir-lang.org/install.sh $STD sh install.sh elixir@latest otp@latest RELEASE=$(curl -fsSL https://api.github.com/repos/livebook-dev/livebook/releases/latest | grep "tag_name" | awk -F'"' '{print $4}') -$STD mix local.hex --force -$STD mix local.rebar --force -$STD mix escript.install hex livebook --force - # Get the actual installed versions from directory names ERLANG_VERSION=$(ls /opt/.elixir-install/installs/otp/ | head -n1) ELIXIR_VERSION=$(ls /opt/.elixir-install/installs/elixir/ | head -n1) @@ -43,6 +39,11 @@ ELIXIR_VERSION=$(ls /opt/.elixir-install/installs/elixir/ | head -n1) # TODO remove echo "Found Erlang version: $ERLANG_VERSION" echo "Found Elixir version: $ELIXIR_VERSION" +export PATH="\$ERLANG_BIN:\$ELIXIR_BIN:\$PATH" + +$STD mix local.hex --force +$STD mix local.rebar --force +$STD mix escript.install hex livebook --force # Create .env file with all environment variables cat < /opt/.env @@ -63,7 +64,6 @@ EOF msg_ok "Installed Erlang $ERLANG_VERSION and Elixir $ELIXIR_VERSION" msg_info "Installing Livebook" - cat </etc/systemd/system/livebook.service [Unit] Description=Livebook From af23f69437ba63595a2aed1ba21216087544e37d Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Sun, 17 Aug 2025 08:38:11 +0200 Subject: [PATCH 0526/1733] update path again --- install/livebook-install.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/install/livebook-install.sh b/install/livebook-install.sh index 1ee988184..d1521c1ba 100644 --- a/install/livebook-install.sh +++ b/install/livebook-install.sh @@ -19,7 +19,8 @@ $STD apt-get install -y \ ca-certificates \ cmake \ git \ - libncurses5-dev + libncurses5-dev \ + curl msg_ok "Installed Dependencies" msg_info "Installing Erlang and Elixir" @@ -39,7 +40,9 @@ ELIXIR_VERSION=$(ls /opt/.elixir-install/installs/elixir/ | head -n1) # TODO remove echo "Found Erlang version: $ERLANG_VERSION" echo "Found Elixir version: $ELIXIR_VERSION" -export PATH="\$ERLANG_BIN:\$ELIXIR_BIN:\$PATH" +export ERLANG_BIN="/opt/.elixir-install/installs/otp/\${ERLANG_VERSION}/bin" +export ELIXIR_BIN="/opt/.elixir-install/installs/elixir/\${ELIXIR_VERSION}/bin" +export PATH=$ERLANG_BIN:$ELIXIR_BIN:$PATH $STD mix local.hex --force $STD mix local.rebar --force From 02c3ae2a1bf947610cf2c5806ab226ddf508dd79 Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Sun, 17 Aug 2025 08:51:39 +0200 Subject: [PATCH 0527/1733] variables --- install/livebook-install.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/install/livebook-install.sh b/install/livebook-install.sh index d1521c1ba..7db27a3f7 100644 --- a/install/livebook-install.sh +++ b/install/livebook-install.sh @@ -40,9 +40,9 @@ ELIXIR_VERSION=$(ls /opt/.elixir-install/installs/elixir/ | head -n1) # TODO remove echo "Found Erlang version: $ERLANG_VERSION" echo "Found Elixir version: $ELIXIR_VERSION" -export ERLANG_BIN="/opt/.elixir-install/installs/otp/\${ERLANG_VERSION}/bin" -export ELIXIR_BIN="/opt/.elixir-install/installs/elixir/\${ELIXIR_VERSION}/bin" -export PATH=$ERLANG_BIN:$ELIXIR_BIN:$PATH +export ERLANG_BIN="/opt/.elixir-install/installs/otp/$ERLANG_VERSION/bin" +export ELIXIR_BIN="/opt/.elixir-install/installs/elixir/$ELIXIR_VERSION/bin" +export PATH="$ERLANG_BIN:$ELIXIR_BIN:$PATH" $STD mix local.hex --force $STD mix local.rebar --force From d1c5ac135f347b0b78b6947dff616714c13d5525 Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Sun, 17 Aug 2025 09:30:10 +0200 Subject: [PATCH 0528/1733] restore path --- ct/livebook.sh | 3 +-- misc/build.func | 2 +- misc/install.func | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/ct/livebook.sh b/ct/livebook.sh index ea387437e..3ed7697d8 100755 --- a/ct/livebook.sh +++ b/ct/livebook.sh @@ -1,6 +1,5 @@ #!/usr/bin/env bash -#source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/refs/heads/main/misc/build.func) -source <(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/refs/heads/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: dkuku # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/misc/build.func b/misc/build.func index 49b20b7fa..c40a57bd1 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1438,7 +1438,7 @@ EOF' fi msg_ok "Customized LXC Container" - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/install/${var_install}.sh)" + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)" } destroy_lxc() { diff --git a/misc/install.func b/misc/install.func index 9b6bd60e4..e3751c295 100644 --- a/misc/install.func +++ b/misc/install.func @@ -196,8 +196,7 @@ EOF systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//') msg_ok "Customized Container" fi - # echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update - echo "bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/ct/${app}.sh)\"" >/usr/bin/update + echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update chmod +x /usr/bin/update if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then mkdir -p /root/.ssh From dd3fccdc76cc6db525c006bdf7f0a8aba30f5b09 Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Sun, 17 Aug 2025 10:52:58 +0200 Subject: [PATCH 0529/1733] run as livebook user --- ct/livebook.sh | 11 +++-------- install/livebook-install.sh | 26 ++++++++++++++++++-------- misc/build.func | 3 ++- misc/install.func | 2 +- 4 files changed, 24 insertions(+), 18 deletions(-) diff --git a/ct/livebook.sh b/ct/livebook.sh index 3ed7697d8..fa936b006 100755 --- a/ct/livebook.sh +++ b/ct/livebook.sh @@ -1,11 +1,11 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/refs/heads/main/misc/build.func) +# source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/refs/heads/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: dkuku # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/livebook-dev/livebook -echo -e "Loading..." APP="Livebook" var_tags="${var_tags:-development}" var_disk="${var_disk:-4}" @@ -33,11 +33,6 @@ function update_script() { msg_info "Checking for updates..." RELEASE=$(curl -fsSL https://api.github.com/repos/livebook-dev/livebook/releases/latest | grep "tag_name" | awk -F'"' '{print $4}') - if [[ -z "$RELEASE" ]]; then - msg_error "Failed to fetch latest version information" - exit 1 - fi - if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt 2>/dev/null)" ]]; then msg_info "Updating ${APP} LXC" $STD apt-get update @@ -47,10 +42,10 @@ function update_script() { msg_info "Updating ${APP} to ${RELEASE}" source /opt/.env cd /opt || exit 1 - rm /opt/.mix/escripts/livebook mix escript.install hex livebook --force echo "$RELEASE" | $STD tee /opt/${APP}_version.txt >/dev/null + chown -R livebook:livebook /opt /data msg_ok "Successfully updated to ${RELEASE}" else diff --git a/install/livebook-install.sh b/install/livebook-install.sh index 7db27a3f7..0ff20ee6f 100644 --- a/install/livebook-install.sh +++ b/install/livebook-install.sh @@ -23,6 +23,10 @@ $STD apt-get install -y \ curl msg_ok "Installed Dependencies" +msg_info "Creating livebook user" +adduser --system --group --home /opt --shell /bin/bash livebook +msg_ok "Created livebook user" + msg_info "Installing Erlang and Elixir" mkdir -p /opt /data @@ -33,13 +37,9 @@ curl -fsSO https://elixir-lang.org/install.sh $STD sh install.sh elixir@latest otp@latest RELEASE=$(curl -fsSL https://api.github.com/repos/livebook-dev/livebook/releases/latest | grep "tag_name" | awk -F'"' '{print $4}') -# Get the actual installed versions from directory names ERLANG_VERSION=$(ls /opt/.elixir-install/installs/otp/ | head -n1) ELIXIR_VERSION=$(ls /opt/.elixir-install/installs/elixir/ | head -n1) -# TODO remove -echo "Found Erlang version: $ERLANG_VERSION" -echo "Found Elixir version: $ELIXIR_VERSION" export ERLANG_BIN="/opt/.elixir-install/installs/otp/$ERLANG_VERSION/bin" export ELIXIR_BIN="/opt/.elixir-install/installs/elixir/$ELIXIR_VERSION/bin" export PATH="$ERLANG_BIN:$ELIXIR_BIN:$PATH" @@ -48,7 +48,13 @@ $STD mix local.hex --force $STD mix local.rebar --force $STD mix escript.install hex livebook --force -# Create .env file with all environment variables +LIVEBOOK_PASSWORD=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c16) + +cat < /opt/livebook.creds +Livebook-Credentials +Livebook Password: $LIVEBOOK_PASSWORD +EOF + cat < /opt/.env export HOME=/opt export LIVEBOOK_VERSION=$RELEASE @@ -57,7 +63,7 @@ export ELIXIR_VERSION=$ELIXIR_VERSION export LIVEBOOK_PORT=8080 export LIVEBOOK_IP="::" export LIVEBOOK_HOME=/data -export LIVEBOOK_TOKEN_ENABLED=false +export LIVEBOOK_PASSWORD="$LIVEBOOK_PASSWORD" export ESCRIPTS_BIN=/opt/.mix/escripts export ERLANG_BIN="/opt/.elixir-install/installs/otp/\${ERLANG_VERSION}/bin" export ELIXIR_BIN="/opt/.elixir-install/installs/elixir/\${ELIXIR_VERSION}/bin" @@ -74,8 +80,8 @@ After=network.target [Service] Type=exec -User=root -Group=root +User=livebook +Group=livebook WorkingDirectory=/data EnvironmentFile=-/opt/.env ExecStart=/bin/bash -c 'source /opt/.env && cd /opt && livebook server' @@ -86,6 +92,10 @@ RestartSec=5 WantedBy=multi-user.target EOF +msg_info "Setting ownership and permissions" +chown -R livebook:livebook /opt /data +msg_ok "Set ownership and permissions" + systemctl enable -q --now livebook msg_ok "Installed Livebook" diff --git a/misc/build.func b/misc/build.func index c40a57bd1..f72fb5185 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1438,7 +1438,8 @@ EOF' fi msg_ok "Customized LXC Container" - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)" + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/install/${var_install}.sh)" + } destroy_lxc() { diff --git a/misc/install.func b/misc/install.func index e3751c295..a75297aa0 100644 --- a/misc/install.func +++ b/misc/install.func @@ -196,7 +196,7 @@ EOF systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//') msg_ok "Customized Container" fi - echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update + echo "bash -c \"\$(curl -fsSL https://github.com/dkuku/ProxmoxVED/refs/heads/livebook/ct/${app}.sh)\"" >/usr/bin/update chmod +x /usr/bin/update if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then mkdir -p /root/.ssh From dec63bf34a4eb582ffaaa90d31179fe6598332a8 Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Sun, 17 Aug 2025 11:01:25 +0200 Subject: [PATCH 0530/1733] reset private urls --- ct/livebook.sh | 3 +-- misc/build.func | 3 +-- misc/install.func | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/ct/livebook.sh b/ct/livebook.sh index fa936b006..7f9e848ef 100755 --- a/ct/livebook.sh +++ b/ct/livebook.sh @@ -1,6 +1,5 @@ #!/usr/bin/env bash -# source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/refs/heads/main/misc/build.func) -source <(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/refs/heads/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: dkuku # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/misc/build.func b/misc/build.func index f72fb5185..c40a57bd1 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1438,8 +1438,7 @@ EOF' fi msg_ok "Customized LXC Container" - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/install/${var_install}.sh)" - + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)" } destroy_lxc() { diff --git a/misc/install.func b/misc/install.func index a75297aa0..e3751c295 100644 --- a/misc/install.func +++ b/misc/install.func @@ -196,7 +196,7 @@ EOF systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//') msg_ok "Customized Container" fi - echo "bash -c \"\$(curl -fsSL https://github.com/dkuku/ProxmoxVED/refs/heads/livebook/ct/${app}.sh)\"" >/usr/bin/update + echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update chmod +x /usr/bin/update if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then mkdir -p /root/.ssh From 953d9f0527515eb40f6ee482383fc98eed9d16a3 Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Sun, 17 Aug 2025 11:07:28 +0200 Subject: [PATCH 0531/1733] add info about the password --- frontend/public/json/livebook.json | 7 ++++++- install/livebook-install.sh | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/frontend/public/json/livebook.json b/frontend/public/json/livebook.json index db4159ca3..8b0a3589d 100644 --- a/frontend/public/json/livebook.json +++ b/frontend/public/json/livebook.json @@ -31,5 +31,10 @@ "username": null, "password": null }, - "notes": [] + "notes": [ + { + "text": "Show Livebook password: `cat /opt/livebook.creds`", + "type": "info" + } + ] } diff --git a/install/livebook-install.sh b/install/livebook-install.sh index 0ff20ee6f..cc225a08e 100644 --- a/install/livebook-install.sh +++ b/install/livebook-install.sh @@ -99,6 +99,9 @@ msg_ok "Set ownership and permissions" systemctl enable -q --now livebook msg_ok "Installed Livebook" +msg_info "Livebook password stored in /opt/livebook.creds" +msg_ok "Installation completed successfully" + motd_ssh customize From a89a5351e36fc02107f1f69829d014cf128b5d13 Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Sun, 17 Aug 2025 11:21:56 +0200 Subject: [PATCH 0532/1733] remove curl from deps --- install/livebook-install.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/install/livebook-install.sh b/install/livebook-install.sh index cc225a08e..c87e2ca47 100644 --- a/install/livebook-install.sh +++ b/install/livebook-install.sh @@ -19,8 +19,7 @@ $STD apt-get install -y \ ca-certificates \ cmake \ git \ - libncurses5-dev \ - curl + libncurses5-dev msg_ok "Installed Dependencies" msg_info "Creating livebook user" From 319a779d4913157b551d000f3e89416cebc2a9b9 Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Sun, 17 Aug 2025 20:19:55 +0200 Subject: [PATCH 0533/1733] cr suggetions --- ct/livebook.sh | 15 ++++++++------- install/livebook-install.sh | 36 ++++++++++++++++++------------------ misc/build.func | 2 +- misc/install.func | 2 +- 4 files changed, 28 insertions(+), 27 deletions(-) diff --git a/ct/livebook.sh b/ct/livebook.sh index 7f9e848ef..733cd0e33 100755 --- a/ct/livebook.sh +++ b/ct/livebook.sh @@ -1,5 +1,6 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/refs/heads/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/misc/build.func) +#source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/refs/heads/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: dkuku # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -24,7 +25,7 @@ function update_script() { check_container_storage check_container_resources - if [[ ! -f /opt/.mix/escripts/livebook ]]; then + if [[ ! -f /opt/livebook/.mix/escripts/livebook ]]; then msg_error "No ${APP} Installation Found!" exit 1 fi @@ -32,19 +33,19 @@ function update_script() { msg_info "Checking for updates..." RELEASE=$(curl -fsSL https://api.github.com/repos/livebook-dev/livebook/releases/latest | grep "tag_name" | awk -F'"' '{print $4}') - if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt 2>/dev/null)" ]]; then + if [[ "${RELEASE}" != "$(cat /opt/.livebook 2>/dev/null)" ]]; then msg_info "Updating ${APP} LXC" $STD apt-get update $STD apt-get -y upgrade msg_ok "Updated ${APP} LXC" msg_info "Updating ${APP} to ${RELEASE}" - source /opt/.env - cd /opt || exit 1 + source /opt/livebook/.env + cd /opt/livebook || exit 1 mix escript.install hex livebook --force - echo "$RELEASE" | $STD tee /opt/${APP}_version.txt >/dev/null - chown -R livebook:livebook /opt /data + echo "$RELEASE" | $STD tee /opt/.livebook + chown -R livebook:livebook /opt/livebook /data msg_ok "Successfully updated to ${RELEASE}" else diff --git a/install/livebook-install.sh b/install/livebook-install.sh index c87e2ca47..aa2e1b031 100644 --- a/install/livebook-install.sh +++ b/install/livebook-install.sh @@ -13,7 +13,7 @@ setting_up_container network_check update_os -msg_info "Installing Dependencies (matching Livebook Dockerfile)" +msg_info "Installing Dependencies" $STD apt-get install -y \ build-essential \ ca-certificates \ @@ -23,24 +23,24 @@ $STD apt-get install -y \ msg_ok "Installed Dependencies" msg_info "Creating livebook user" -adduser --system --group --home /opt --shell /bin/bash livebook +adduser --system --group --home /opt/livebook --shell /bin/bash livebook msg_ok "Created livebook user" msg_info "Installing Erlang and Elixir" -mkdir -p /opt /data -export HOME=/opt -cd /opt || exit 1 +mkdir -p /opt/livebook /data +export HOME=/opt/livebook +cd /opt/livebook || exit 1 curl -fsSO https://elixir-lang.org/install.sh $STD sh install.sh elixir@latest otp@latest RELEASE=$(curl -fsSL https://api.github.com/repos/livebook-dev/livebook/releases/latest | grep "tag_name" | awk -F'"' '{print $4}') -ERLANG_VERSION=$(ls /opt/.elixir-install/installs/otp/ | head -n1) -ELIXIR_VERSION=$(ls /opt/.elixir-install/installs/elixir/ | head -n1) +ERLANG_VERSION=$(ls /opt/livebook/.elixir-install/installs/otp/ | head -n1) +ELIXIR_VERSION=$(ls /opt/livebook/.elixir-install/installs/elixir/ | head -n1) -export ERLANG_BIN="/opt/.elixir-install/installs/otp/$ERLANG_VERSION/bin" -export ELIXIR_BIN="/opt/.elixir-install/installs/elixir/$ELIXIR_VERSION/bin" +export ERLANG_BIN="/opt/livebook/.elixir-install/installs/otp/$ERLANG_VERSION/bin" +export ELIXIR_BIN="/opt/livebook/.elixir-install/installs/elixir/$ELIXIR_VERSION/bin" export PATH="$ERLANG_BIN:$ELIXIR_BIN:$PATH" $STD mix local.hex --force @@ -49,13 +49,13 @@ $STD mix escript.install hex livebook --force LIVEBOOK_PASSWORD=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c16) -cat < /opt/livebook.creds +cat < /opt/livebook/livebook.creds Livebook-Credentials Livebook Password: $LIVEBOOK_PASSWORD EOF -cat < /opt/.env -export HOME=/opt +cat < /opt/livebook/.env +export HOME=/opt/livebook export LIVEBOOK_VERSION=$RELEASE export ERLANG_VERSION=$ERLANG_VERSION export ELIXIR_VERSION=$ELIXIR_VERSION @@ -63,9 +63,9 @@ export LIVEBOOK_PORT=8080 export LIVEBOOK_IP="::" export LIVEBOOK_HOME=/data export LIVEBOOK_PASSWORD="$LIVEBOOK_PASSWORD" -export ESCRIPTS_BIN=/opt/.mix/escripts -export ERLANG_BIN="/opt/.elixir-install/installs/otp/\${ERLANG_VERSION}/bin" -export ELIXIR_BIN="/opt/.elixir-install/installs/elixir/\${ELIXIR_VERSION}/bin" +export ESCRIPTS_BIN=/opt/livebook/.mix/escripts +export ERLANG_BIN="/opt/livebook/.elixir-install/installs/otp/\${ERLANG_VERSION}/bin" +export ELIXIR_BIN="/opt/livebook/.elixir-install/installs/elixir/\${ELIXIR_VERSION}/bin" export PATH="\$ESCRIPTS_BIN:\$ERLANG_BIN:\$ELIXIR_BIN:\$PATH" EOF @@ -82,8 +82,8 @@ Type=exec User=livebook Group=livebook WorkingDirectory=/data -EnvironmentFile=-/opt/.env -ExecStart=/bin/bash -c 'source /opt/.env && cd /opt && livebook server' +EnvironmentFile=-/opt/livebook/.env +ExecStart=/bin/bash -c 'source /opt/livebook/.env && cd /opt/livebook && livebook server' Restart=always RestartSec=5 @@ -92,7 +92,7 @@ WantedBy=multi-user.target EOF msg_info "Setting ownership and permissions" -chown -R livebook:livebook /opt /data +chown -R livebook:livebook /opt/livebook /data msg_ok "Set ownership and permissions" systemctl enable -q --now livebook diff --git a/misc/build.func b/misc/build.func index c40a57bd1..49b20b7fa 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1438,7 +1438,7 @@ EOF' fi msg_ok "Customized LXC Container" - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)" + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/install/${var_install}.sh)" } destroy_lxc() { diff --git a/misc/install.func b/misc/install.func index e3751c295..d1facfa48 100644 --- a/misc/install.func +++ b/misc/install.func @@ -196,7 +196,7 @@ EOF systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//') msg_ok "Customized Container" fi - echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update + echo "bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/ct/${app}.sh)\"" >/usr/bin/update chmod +x /usr/bin/update if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then mkdir -p /root/.ssh From e24f867d9d70f4aea7b42caeb4d1a238f4e242b2 Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Sun, 17 Aug 2025 20:57:56 +0200 Subject: [PATCH 0534/1733] reset private urls --- ct/livebook.sh | 3 +-- misc/build.func | 2 +- misc/install.func | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/ct/livebook.sh b/ct/livebook.sh index 733cd0e33..d934ec25a 100755 --- a/ct/livebook.sh +++ b/ct/livebook.sh @@ -1,6 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/misc/build.func) -#source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/refs/heads/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/refs/heads/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: dkuku # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/misc/build.func b/misc/build.func index 49b20b7fa..c40a57bd1 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1438,7 +1438,7 @@ EOF' fi msg_ok "Customized LXC Container" - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/install/${var_install}.sh)" + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)" } destroy_lxc() { diff --git a/misc/install.func b/misc/install.func index d1facfa48..e3751c295 100644 --- a/misc/install.func +++ b/misc/install.func @@ -196,7 +196,7 @@ EOF systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//') msg_ok "Customized Container" fi - echo "bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/ct/${app}.sh)\"" >/usr/bin/update + echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update chmod +x /usr/bin/update if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then mkdir -p /root/.ssh From fa12a13c151e3883d21ec38fd54b2d8db5668eb3 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 18 Aug 2025 05:51:49 +0000 Subject: [PATCH 0535/1733] Update .app files --- ct/headers/litellm | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/litellm diff --git a/ct/headers/litellm b/ct/headers/litellm new file mode 100644 index 000000000..0b7dba63a --- /dev/null +++ b/ct/headers/litellm @@ -0,0 +1,6 @@ + ___ __ ____ + / (_) /____ / / /___ ___ + / / / __/ _ \/ / / __ `__ \ + / / / /_/ __/ / / / / / / / +/_/_/\__/\___/_/_/_/ /_/ /_/ + From a06f218b03f425f078121dd9fc57eeaa4fd53271 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 08:06:08 +0200 Subject: [PATCH 0536/1733] Merge branch 'main' of https://github.com/community-scripts/ProxmoxVED --- ct/twingate-connector.sh | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/ct/twingate-connector.sh b/ct/twingate-connector.sh index f87be0a02..681c96a34 100644 --- a/ct/twingate-connector.sh +++ b/ct/twingate-connector.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: twingate-andrewb # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -11,7 +11,7 @@ var_cpu="${var_cpu:-1}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-2}" var_os="${var_os:-ubuntu}" -var_version="${var_version:-22.04}" +var_version="${var_version:-24.04}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" @@ -25,16 +25,14 @@ function update_script() { check_container_resources if [[ ! -f /lib/systemd/system/twingate-connector.service ]]; then - msg_error "No ${APP} Installation Found!" - exit + msg_error "No ${APP} Installation Found!" + exit fi msg_info "Updating ${APP}" - - apt update - apt install -yq twingate-connector - systemctl restart twingate-connector - + $STD apt update + $STD apt install -yq twingate-connector + $STD systemctl restart twingate-connector msg_ok "Updated Successfully" exit } From b64a4601d7193cea820781a114ec8e6d370b2396 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 08:10:09 +0200 Subject: [PATCH 0537/1733] Update frigate-install.sh --- install/frigate-install.sh | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/install/frigate-install.sh b/install/frigate-install.sh index 52884f075..862c8d839 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -15,11 +15,7 @@ update_os msg_info "Installing Dependencies (Patience)" $STD apt-get install -y \ - git automake build-essential xz-utils libtool ccache pkg-config \ - libgtk-3-dev libavcodec-dev libavformat-dev libswscale-dev libv4l-dev libxvidcore-dev libx264-dev \ - libjpeg-dev libpng-dev libtiff-dev gfortran openexr libatlas-base-dev libssl-dev libtbb-dev \ - libopenexr-dev libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev gcc gfortran \ - libopenblas-dev liblapack-dev libusb-1.0-0-dev jq moreutils tclsh libhdf5-dev libopenexr-dev nginx + $STD apt-get install -y {git,ca-certificates,automake,build-essential,xz-utils,libtool,ccache,pkg-config,libgtk-3-dev,libavcodec-dev,libavformat-dev,libswscale-dev,libv4l-dev,libxvidcore-dev,libx264-dev,libjpeg-dev,libpng-dev,libtiff-dev,gfortran,openexr,libatlas-base-dev,libssl-dev,libtbbmalloc2,libtbb-dev,libdc1394-dev,libopenexr-dev,libgstreamer-plugins-base1.0-dev,libgstreamer1.0-dev,gcc,gfortran,libopenblas-dev,liblapack-dev,libusb-1.0-0-dev,jq,moreutils} msg_ok "Installed Dependencies" msg_info "Setup Python3" @@ -99,7 +95,7 @@ msg_ok "NGINX with Custom Modules Built" NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs fetch_and_deploy_gh_release "go2rtc" "AlexxIT/go2rtc" "singlefile" "latest" "/usr/local/go2rtc/bin" "go2rtc_linux_amd64" -fetch_and_deploy_gh_release "frigate" "blakeblackshear/frigate" "tarball" "v0.16.0-beta4" "/opt/frigate" +fetch_and_deploy_gh_release "frigate" "blakeblackshear/frigate" "tarball" "latest" "/opt/frigate" fetch_and_deploy_gh_release "libusb" "libusb/libusb" "tarball" "v1.0.29" "/opt/frigate/libusb" msg_info "Setting Up Hardware Acceleration" @@ -242,6 +238,32 @@ systemctl daemon-reload systemctl enable -q --now frigate msg_ok "Frigate service enabled" +msg_info "Setting environmental variables" +cat <>/root/.bashrc +export TERM='xterm-256color' +export DEFAULT_FFMPEG_VERSION='7.0' +export NVIDIA_VISIBLE_DEVICES='all' +export ENV NVIDIA_DRIVER_CAPABILITIES='compute,video,utility' +export PATH="/usr/local/go2rtc/bin:/usr/local/tempio/bin:/usr/local/nginx/sbin:${PATH}" +export TOKENIZERS_PARALLELISM=true +export TRANSFORMERS_NO_ADVISORY_WARNINGS=1 +export OPENCV_FFMPEG_LOGLEVEL=8 +export HAILORT_LOGGER_PATH=NONE +export INCLUDED_FFMPEG_VERSIONS="${DEFAULT_FFMPEG_VERSION}:5.0" +export S6_LOGGING_SCRIPT="T 1 n0 s10000000 T" +export S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0 +export CCACHE_DIR=/root/.ccache +export CCACHE_MAXSIZE=2G +EOF +msg_ok "Environment set" + +msg_info "Building Nginx with Custom Modules" +$STD bash /opt/frigate/docker/main/build_nginx.sh +sed -i 's/if \[\[ "\$VERSION_ID" == "12" \]\]; then/if \[\[ "\$VERSION_ID" == "13" \]\]; then/' /opt/frigate/docker/main/build_nginx.sh +sed -e '/s6-notifyoncheck/ s/^#*/#/' -i /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/run +ln -sf /usr/local/nginx/sbin/nginx /usr/local/bin/nginx +msg_ok "Built Nginx" + # msg_info "Setup Frigate" # ln -sf /usr/local/go2rtc/bin/go2rtc /usr/local/bin/go2rtc # cd /opt/frigate From 709ed8917e08f38e0d3c19c66bc6e7aaaef2b6a9 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 08:10:53 +0200 Subject: [PATCH 0538/1733] Update twingate-connector.sh --- ct/twingate-connector.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/twingate-connector.sh b/ct/twingate-connector.sh index 681c96a34..09181aa7f 100644 --- a/ct/twingate-connector.sh +++ b/ct/twingate-connector.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: twingate-andrewb # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE From ea4316169c9706dcdfa7102ee7b68c57f3a4d9d1 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 08:25:06 +0200 Subject: [PATCH 0539/1733] little mods --- ct/twingate-connector.sh | 4 ++-- frontend/public/json/twingate-connector.json | 12 ++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/ct/twingate-connector.sh b/ct/twingate-connector.sh index 09181aa7f..f1a05c9aa 100644 --- a/ct/twingate-connector.sh +++ b/ct/twingate-connector.sh @@ -8,8 +8,8 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV APP="twingate-connector" var_tags="${var_tags:-network;connector;twingate}" var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-2}" +var_ram="${var_ram:-1024}" +var_disk="${var_disk:-3}" var_os="${var_os:-ubuntu}" var_version="${var_version:-24.04}" var_unprivileged="${var_unprivileged:-1}" diff --git a/frontend/public/json/twingate-connector.json b/frontend/public/json/twingate-connector.json index 55eeaacbd..e9aa195ed 100644 --- a/frontend/public/json/twingate-connector.json +++ b/frontend/public/json/twingate-connector.json @@ -12,7 +12,7 @@ "documentation": "https://www.twingate.com/docs/", "config_path": "/etc/twingate/connector.conf", "website": "https://www.twingate.com", - "logo": "https://avatars.githubusercontent.com/u/97470429?s=200&v=4", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/twingate.webp", "description": "Twingate Connectors are lightweight software components that establish secure, least-privileged access between private network resources and authorized users without exposing the network to the internet. They act as outbound-only bridges between your protected resources and the Twingate infrastructure, ensuring zero-trust access without the need for a VPN.", "install_methods": [ { @@ -20,10 +20,10 @@ "script": "ct/twingate-connector.sh", "resources": { "cpu": 1, - "ram": 2048, - "hdd": 2, + "ram": 1024, + "hdd": 3, "os": "Ubuntu", - "version": "22.04" + "version": "24.04" } } ], @@ -32,6 +32,10 @@ "password": null }, "notes": [ + { + "text": "You can get your Twingate access or refresh tokens from the Twingate Admin Console. `https://auth.twingate.com/signup-v2`", + "type": "info" + }, { "text": "If you need to update your access or refresh tokens, they can be found in /etc/twingate/connector.conf", "type": "info" From bb4ccbf06df2065e81615f3a893ef038874e3185 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 08:33:05 +0200 Subject: [PATCH 0540/1733] Update viseron-install.sh --- install/viseron-install.sh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/install/viseron-install.sh b/install/viseron-install.sh index 7253f90cf..fc9aa2d73 100644 --- a/install/viseron-install.sh +++ b/install/viseron-install.sh @@ -35,6 +35,15 @@ msg_ok "Installed Dependencies" # fi # msg_ok "Hardware Acceleration Configured" +msg_info "Setting up Python Environment" +PYTHON_VERSION="3.12" setup_uv /opt/viseron +msg_ok "Python Environment Setup" + +msg_info "Installing Viseron" +RELEASE=$(curl -fsSL https://api.github.com/repos/roflcoopter/viseron/releases/latest | jq -r '.tag_name' | sed 's/^v//') +uv pip install "viseron==${RELEASE}" +msg_ok "Installed Viseron $RELEASE" + fetch_and_deploy_gh_release "viseron" "roflcoopter/viseron" "tarball" "latest" "/opt/viseron" msg_info "Setting up Viseron (Patience)" From 1cf9b0699067a55ee3a121ec53f61704b5a1ae3b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 08:35:08 +0200 Subject: [PATCH 0541/1733] Update frigate-install.sh --- install/frigate-install.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/install/frigate-install.sh b/install/frigate-install.sh index 862c8d839..5805fffdc 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -14,8 +14,7 @@ network_check update_os msg_info "Installing Dependencies (Patience)" -$STD apt-get install -y \ - $STD apt-get install -y {git,ca-certificates,automake,build-essential,xz-utils,libtool,ccache,pkg-config,libgtk-3-dev,libavcodec-dev,libavformat-dev,libswscale-dev,libv4l-dev,libxvidcore-dev,libx264-dev,libjpeg-dev,libpng-dev,libtiff-dev,gfortran,openexr,libatlas-base-dev,libssl-dev,libtbbmalloc2,libtbb-dev,libdc1394-dev,libopenexr-dev,libgstreamer-plugins-base1.0-dev,libgstreamer1.0-dev,gcc,gfortran,libopenblas-dev,liblapack-dev,libusb-1.0-0-dev,jq,moreutils} +$STD apt-get install -y {git,ca-certificates,automake,build-essential,xz-utils,libtool,ccache,pkg-config,libgtk-3-dev,libavcodec-dev,libavformat-dev,libswscale-dev,libv4l-dev,libxvidcore-dev,libx264-dev,libjpeg-dev,libpng-dev,libtiff-dev,gfortran,openexr,libatlas-base-dev,libssl-dev,libtbbmalloc2,libtbb-dev,libdc1394-dev,libopenexr-dev,libgstreamer-plugins-base1.0-dev,libgstreamer1.0-dev,gcc,gfortran,libopenblas-dev,liblapack-dev,libusb-1.0-0-dev,jq,moreutils} msg_ok "Installed Dependencies" msg_info "Setup Python3" From 86088220f5fd3af6ee41ce21f632d0c1e0bd4f37 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 08:37:15 +0200 Subject: [PATCH 0542/1733] Update viseron-install.sh --- install/viseron-install.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/install/viseron-install.sh b/install/viseron-install.sh index fc9aa2d73..d27eed4cc 100644 --- a/install/viseron-install.sh +++ b/install/viseron-install.sh @@ -44,15 +44,15 @@ RELEASE=$(curl -fsSL https://api.github.com/repos/roflcoopter/viseron/releases/l uv pip install "viseron==${RELEASE}" msg_ok "Installed Viseron $RELEASE" -fetch_and_deploy_gh_release "viseron" "roflcoopter/viseron" "tarball" "latest" "/opt/viseron" +# fetch_and_deploy_gh_release "viseron" "roflcoopter/viseron" "tarball" "latest" "/opt/viseron" -msg_info "Setting up Viseron (Patience)" -cd /opt/viseron -uv venv .venv -$STD uv pip install --upgrade pip setuptools wheel -$STD uv pip install -r requirements.txt --python /opt/viseron/.venv/bin/python -ln -s /opt/viseron/.venv/bin/viseron /usr/local/bin/viseron -msg_ok "Setup Viseron" +# msg_info "Setting up Viseron (Patience)" +# cd /opt/viseron +# uv venv .venv +# $STD uv pip install --upgrade pip setuptools wheel +# $STD uv pip install -r requirements.txt --python /opt/viseron/.venv/bin/python +# ln -s /opt/viseron/.venv/bin/viseron /usr/local/bin/viseron +# msg_ok "Setup Viseron" msg_info "Creating Configuration Directory" mkdir -p /config From c831e7e9f963068dfe0c865a62874d2d49ca81f2 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 08:41:40 +0200 Subject: [PATCH 0543/1733] Update viseron-install.sh --- install/viseron-install.sh | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/install/viseron-install.sh b/install/viseron-install.sh index d27eed4cc..313ee0005 100644 --- a/install/viseron-install.sh +++ b/install/viseron-install.sh @@ -39,9 +39,16 @@ msg_info "Setting up Python Environment" PYTHON_VERSION="3.12" setup_uv /opt/viseron msg_ok "Python Environment Setup" +msg_info "Setting up Python Environment" +mkdir -p /opt/viseron +cd /opt/viseron +uv venv . +msg_ok "Python Environment Setup" + msg_info "Installing Viseron" -RELEASE=$(curl -fsSL https://api.github.com/repos/roflcoopter/viseron/releases/latest | jq -r '.tag_name' | sed 's/^v//') -uv pip install "viseron==${RELEASE}" +RELEASE=$(curl -fsSL https://api.github.com/repos/roflcoopter/viseron/releases/latest | + jq -r '.tag_name' | sed 's/^v//') +/opt/viseron/bin/uv pip install "viseron==${RELEASE}" msg_ok "Installed Viseron $RELEASE" # fetch_and_deploy_gh_release "viseron" "roflcoopter/viseron" "tarball" "latest" "/opt/viseron" From 82d9d6235b0348f085911c46022dc19824826e31 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 08:46:17 +0200 Subject: [PATCH 0544/1733] Update frigate-install.sh --- install/frigate-install.sh | 74 ++------------------------------------ 1 file changed, 2 insertions(+), 72 deletions(-) diff --git a/install/frigate-install.sh b/install/frigate-install.sh index 5805fffdc..980fabca3 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -18,80 +18,10 @@ $STD apt-get install -y {git,ca-certificates,automake,build-essential,xz-utils,l msg_ok "Installed Dependencies" msg_info "Setup Python3" -$STD apt-get install -y \ - python3 python3-dev python3-setuptools python3-distutils python3-pip python3-venv -$STD pip install --upgrade pip --break-system-packages +$STD apt-get install -y {python3,python3-dev,python3-setuptools,python3-distutils,python3-pip} +$STD pip install --upgrade pip msg_ok "Setup Python3" -msg_info "Setup NGINX" -apt-get update -apt-get -y build-dep nginx -apt-get -y install wget build-essential ccache patch ca-certificates -update-ca-certificates -f -export PATH="/usr/lib/ccache:$PATH" - -cd /tmp -wget -nv https://nginx.org/download/nginx-1.29.0.tar.gz -tar -xf nginx-1.29.0.tar.gz -cd nginx-1.29.0 - -mkdir /tmp/nginx-vod -wget -nv https://github.com/kaltura/nginx-vod-module/archive/refs/tags/1.31.tar.gz -tar -xf 1.31.tar.gz -C /tmp/nginx-vod --strip-components=1 -sed -i 's/MAX_CLIPS (128)/MAX_CLIPS (1080)/g' /tmp/nginx-vod/vod/media_set.h -patch -d /tmp/nginx-vod -p1 <<'EOF' ---- a/vod/avc_hevc_parser.c -+++ b/vod/avc_hevc_parser.c -@@ -3,6 +3,9 @@ - bool_t - avc_hevc_parser_rbsp_trailing_bits(bit_reader_state_t* reader) - { -+ // https://github.com/blakeblackshear/frigate/issues/4572 -+ return TRUE; -+ - uint32_t one_bit; - - if (reader->stream.eof_reached) -EOF -# secure-token module -mkdir /tmp/nginx-secure-token -wget -nv https://github.com/kaltura/nginx-secure-token-module/archive/refs/tags/1.5.tar.gz -tar -xf 1.5.tar.gz -C /tmp/nginx-secure-token --strip-components=1 - -# ngx-devel-kit -mkdir /tmp/ngx-devel-kit -wget -nv https://github.com/vision5/ngx_devel_kit/archive/refs/tags/v0.3.3.tar.gz -tar -xf v0.3.3.tar.gz -C /tmp/ngx-devel-kit --strip-components=1 - -# set-misc module -mkdir /tmp/nginx-set-misc -wget -nv https://github.com/openresty/set-misc-nginx-module/archive/refs/tags/v0.33.tar.gz -tar -xf v0.33.tar.gz -C /tmp/nginx-set-misc --strip-components=1 - -# configure & build -cd /tmp/nginx-1.29.0 -./configure --prefix=/usr/local/nginx \ - --with-file-aio \ - --with-http_sub_module \ - --with-http_ssl_module \ - --with-http_auth_request_module \ - --with-http_realip_module \ - --with-threads \ - --add-module=/tmp/ngx-devel-kit \ - --add-module=/tmp/nginx-set-misc \ - --add-module=/tmp/nginx-vod \ - --add-module=/tmp/nginx-secure-token \ - --with-cc-opt="-O3 -Wno-error=implicit-fallthrough" - -make CC="ccache gcc" -j"$(nproc)" -make install -ln -sf /usr/local/nginx/sbin/nginx /usr/local/bin/nginx - -# cleanup -rm -rf /tmp/nginx-1.29.0* /tmp/nginx-vod /tmp/nginx-secure-token /tmp/ngx-devel-kit /tmp/nginx-set-misc - -msg_ok "NGINX with Custom Modules Built" - NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs fetch_and_deploy_gh_release "go2rtc" "AlexxIT/go2rtc" "singlefile" "latest" "/usr/local/go2rtc/bin" "go2rtc_linux_amd64" fetch_and_deploy_gh_release "frigate" "blakeblackshear/frigate" "tarball" "latest" "/opt/frigate" From 24728236d6544b0d14e5045e5c580c5fb08ea7cc Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 08:50:22 +0200 Subject: [PATCH 0545/1733] Update viseron-install.sh --- install/viseron-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/viseron-install.sh b/install/viseron-install.sh index 313ee0005..4984e9d3b 100644 --- a/install/viseron-install.sh +++ b/install/viseron-install.sh @@ -41,8 +41,8 @@ msg_ok "Python Environment Setup" msg_info "Setting up Python Environment" mkdir -p /opt/viseron -cd /opt/viseron -uv venv . +uv venv --python "python3.12" /opt/viseron +/opt/viseron/bin/uv pip install --upgrade pip setuptools wheel msg_ok "Python Environment Setup" msg_info "Installing Viseron" From af2e10edb67a4961f925c4a53b68ac2bcf8aef00 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 09:07:39 +0200 Subject: [PATCH 0546/1733] fixes --- install/frigate-install.sh | 16 ++++++++-------- install/viseron-install.sh | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/install/frigate-install.sh b/install/frigate-install.sh index 980fabca3..fdd601f76 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -27,14 +27,14 @@ fetch_and_deploy_gh_release "go2rtc" "AlexxIT/go2rtc" "singlefile" "latest" "/us fetch_and_deploy_gh_release "frigate" "blakeblackshear/frigate" "tarball" "latest" "/opt/frigate" fetch_and_deploy_gh_release "libusb" "libusb/libusb" "tarball" "v1.0.29" "/opt/frigate/libusb" -msg_info "Setting Up Hardware Acceleration" -$STD apt-get -y install {va-driver-all,ocl-icd-libopencl1,intel-opencl-icd,vainfo,intel-gpu-tools} -if [[ "$CTTYPE" == "0" ]]; then - chgrp video /dev/dri - chmod 755 /dev/dri - chmod 660 /dev/dri/* -fi -msg_ok "Set Up Hardware Acceleration" +# msg_info "Setting Up Hardware Acceleration" +# $STD apt-get -y install {va-driver-all,ocl-icd-libopencl1,intel-opencl-icd,vainfo,intel-gpu-tools} +# if [[ "$CTTYPE" == "0" ]]; then +# chgrp video /dev/dri +# chmod 755 /dev/dri +# chmod 660 /dev/dri/* +# fi +# msg_ok "Set Up Hardware Acceleration" msg_info "Setting up Python venv" cd /opt/frigate diff --git a/install/viseron-install.sh b/install/viseron-install.sh index 4984e9d3b..f4cdb35fd 100644 --- a/install/viseron-install.sh +++ b/install/viseron-install.sh @@ -23,7 +23,7 @@ $STD apt-get install -y \ libgstreamer1.0-0 libgstreamer-plugins-base1.0-0 \ gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-libav \ build-essential python3-dev python3-gi pkg-config libcairo2-dev gir1.2-glib-2.0 \ - cmake gfortran libopenblas-dev liblapack-dev libgirepository1.0-dev + cmake gfortran libopenblas-dev liblapack-dev libgirepository1.0-dev git msg_ok "Installed Dependencies" @@ -35,20 +35,20 @@ msg_ok "Installed Dependencies" # fi # msg_ok "Hardware Acceleration Configured" -msg_info "Setting up Python Environment" -PYTHON_VERSION="3.12" setup_uv /opt/viseron -msg_ok "Python Environment Setup" +PYTHON_VERSION="3.12" setup_uv msg_info "Setting up Python Environment" mkdir -p /opt/viseron uv venv --python "python3.12" /opt/viseron -/opt/viseron/bin/uv pip install --upgrade pip setuptools wheel +uv pip install --python /opt/viseron/bin/python --upgrade pip setuptools wheel msg_ok "Python Environment Setup" msg_info "Installing Viseron" RELEASE=$(curl -fsSL https://api.github.com/repos/roflcoopter/viseron/releases/latest | - jq -r '.tag_name' | sed 's/^v//') -/opt/viseron/bin/uv pip install "viseron==${RELEASE}" + jq -r '.tag_name') +uv pip install --python /opt/viseron/bin/python \ + "git+https://github.com/roflcoopter/viseron.git@${RELEASE}" +uv pip install --python /opt/viseron/bin/python -r https://raw.githubusercontent.com/roflcoopter/viseron/${RELEASE}/requirements.txt msg_ok "Installed Viseron $RELEASE" # fetch_and_deploy_gh_release "viseron" "roflcoopter/viseron" "tarball" "latest" "/opt/viseron" @@ -133,7 +133,7 @@ Type=simple User=root WorkingDirectory=/opt/viseron Environment=PATH=/opt/viseron/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin -ExecStart=/opt/viseron/.venv/bin/viseron --config /config/viseron.yaml +ExecStart=/opt/viseron/bin/viseron --config /config/viseron.yaml Restart=always RestartSec=10 From 64e3fbe983f36baf27630dcb72c25c1e7b9b5280 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 09:23:47 +0200 Subject: [PATCH 0547/1733] Update viseron-install.sh --- install/viseron-install.sh | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/install/viseron-install.sh b/install/viseron-install.sh index f4cdb35fd..63e16e6fe 100644 --- a/install/viseron-install.sh +++ b/install/viseron-install.sh @@ -37,18 +37,16 @@ msg_ok "Installed Dependencies" PYTHON_VERSION="3.12" setup_uv +fetch_and_deploy_gh_release "viseron" "roflcoopter/viseron" "tarball" "latest" "/opt/viseron" + msg_info "Setting up Python Environment" -mkdir -p /opt/viseron uv venv --python "python3.12" /opt/viseron uv pip install --python /opt/viseron/bin/python --upgrade pip setuptools wheel msg_ok "Python Environment Setup" msg_info "Installing Viseron" -RELEASE=$(curl -fsSL https://api.github.com/repos/roflcoopter/viseron/releases/latest | - jq -r '.tag_name') -uv pip install --python /opt/viseron/bin/python \ - "git+https://github.com/roflcoopter/viseron.git@${RELEASE}" -uv pip install --python /opt/viseron/bin/python -r https://raw.githubusercontent.com/roflcoopter/viseron/${RELEASE}/requirements.txt +uv pip install --python /opt/viseron/bin/python -e . +uv pip install --python /opt/viseron/bin/python -r requirements.txt msg_ok "Installed Viseron $RELEASE" # fetch_and_deploy_gh_release "viseron" "roflcoopter/viseron" "tarball" "latest" "/opt/viseron" @@ -133,7 +131,7 @@ Type=simple User=root WorkingDirectory=/opt/viseron Environment=PATH=/opt/viseron/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin -ExecStart=/opt/viseron/bin/viseron --config /config/viseron.yaml +ExecStart=/opt/viseron/bin/python -m viseron --config /config/viseron.yaml Restart=always RestartSec=10 From f7f22e260715aec009874644785dfe44f41b0058 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 09:27:19 +0200 Subject: [PATCH 0548/1733] Update frigate-install.sh --- install/frigate-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/frigate-install.sh b/install/frigate-install.sh index fdd601f76..8674bbd90 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -18,7 +18,7 @@ $STD apt-get install -y {git,ca-certificates,automake,build-essential,xz-utils,l msg_ok "Installed Dependencies" msg_info "Setup Python3" -$STD apt-get install -y {python3,python3-dev,python3-setuptools,python3-distutils,python3-pip} +$STD apt-get install -y {python3,python3-dev,python3-setuptools,python3-distutils,python3-pip,python3-venv} $STD pip install --upgrade pip msg_ok "Setup Python3" From 779762c3dd782223324f6c40733013e3c30cdb9c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 09:50:50 +0200 Subject: [PATCH 0549/1733] fixes --- install/frigate-install.sh | 2 -- install/viseron-install.sh | 24 ++++++------------------ 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/install/frigate-install.sh b/install/frigate-install.sh index 8674bbd90..8dc59553a 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -140,7 +140,6 @@ StandardError=journal [Install] WantedBy=multi-user.target EOF -systemctl daemon-reload systemctl enable -q --now go2rtc msg_ok "go2rtc service enabled" @@ -163,7 +162,6 @@ StandardError=journal [Install] WantedBy=multi-user.target EOF -systemctl daemon-reload systemctl enable -q --now frigate msg_ok "Frigate service enabled" diff --git a/install/viseron-install.sh b/install/viseron-install.sh index 63e16e6fe..a57f6356c 100644 --- a/install/viseron-install.sh +++ b/install/viseron-install.sh @@ -17,7 +17,6 @@ PYTHON_VERSION="3.12" setup_uv msg_info "Installing Dependencies" $STD apt-get install -y \ - python3 python3-pip python3-venv \ python3-opencv jq \ libgl1-mesa-glx libglib2.0-0 \ libgstreamer1.0-0 libgstreamer-plugins-base1.0-0 \ @@ -36,28 +35,17 @@ msg_ok "Installed Dependencies" # msg_ok "Hardware Acceleration Configured" PYTHON_VERSION="3.12" setup_uv - fetch_and_deploy_gh_release "viseron" "roflcoopter/viseron" "tarball" "latest" "/opt/viseron" msg_info "Setting up Python Environment" -uv venv --python "python3.12" /opt/viseron -uv pip install --python /opt/viseron/bin/python --upgrade pip setuptools wheel +uv venv --python "python3.12" /opt/viseron/.venv +uv pip install --python /opt/viseron/.venv/bin/python --upgrade pip setuptools wheel msg_ok "Python Environment Setup" -msg_info "Installing Viseron" -uv pip install --python /opt/viseron/bin/python -e . -uv pip install --python /opt/viseron/bin/python -r requirements.txt -msg_ok "Installed Viseron $RELEASE" - -# fetch_and_deploy_gh_release "viseron" "roflcoopter/viseron" "tarball" "latest" "/opt/viseron" - -# msg_info "Setting up Viseron (Patience)" -# cd /opt/viseron -# uv venv .venv -# $STD uv pip install --upgrade pip setuptools wheel -# $STD uv pip install -r requirements.txt --python /opt/viseron/.venv/bin/python -# ln -s /opt/viseron/.venv/bin/viseron /usr/local/bin/viseron -# msg_ok "Setup Viseron" +msg_info "Setup Viseron (Patience)" +UV_HTTP_TIMEOUT=600 uv pip install --python /opt/viseron/.venv/bin/python -e /opt/viseron/. +UV_HTTP_TIMEOUT=600 uv pip install --python /opt/viseron/.venv/bin/python -r /opt/viseron/requirements.txt +msg_ok "Setup Viseron" msg_info "Creating Configuration Directory" mkdir -p /config From f3790114303075e9180dfc4bf8adfa36296237dd Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 09:51:40 +0200 Subject: [PATCH 0550/1733] Update frigate-install.sh --- install/frigate-install.sh | 222 +------------------------------------ 1 file changed, 1 insertion(+), 221 deletions(-) diff --git a/install/frigate-install.sh b/install/frigate-install.sh index 8dc59553a..bc4eba13a 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -176,7 +176,7 @@ export TOKENIZERS_PARALLELISM=true export TRANSFORMERS_NO_ADVISORY_WARNINGS=1 export OPENCV_FFMPEG_LOGLEVEL=8 export HAILORT_LOGGER_PATH=NONE -export INCLUDED_FFMPEG_VERSIONS="${DEFAULT_FFMPEG_VERSION}:5.0" +export INCLUDED_FFMPEG_VERSIONS="7.0:5.0" export S6_LOGGING_SCRIPT="T 1 n0 s10000000 T" export S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0 export CCACHE_DIR=/root/.ccache @@ -191,226 +191,6 @@ sed -e '/s6-notifyoncheck/ s/^#*/#/' -i /opt/frigate/docker/main/rootfs/etc/s6-o ln -sf /usr/local/nginx/sbin/nginx /usr/local/bin/nginx msg_ok "Built Nginx" -# msg_info "Setup Frigate" -# ln -sf /usr/local/go2rtc/bin/go2rtc /usr/local/bin/go2rtc -# cd /opt/frigate -# $STD pip install -r /opt/frigate/docker/main/requirements.txt --break-system-packages -# $STD pip install -r /opt/frigate/docker/main/requirements-ov.txt --break-system-packages -# $STD pip3 wheel --wheel-dir=/wheels -r /opt/frigate/docker/main/requirements-wheels.txt -# pip3 install -U /wheels/*.whl -# cp -a /opt/frigate/docker/main/rootfs/. / -# export TARGETARCH="amd64" -# export DEBIAN_FRONTEND=noninteractive -# echo "libedgetpu1-max libedgetpu/accepted-eula select true" | debconf-set-selections -# echo "libedgetpu1-max libedgetpu/install-confirm-max select true" | debconf-set-selections - -# msg_info "Ensure /etc/apt/sources.list.d/debian.sources exists with deb-src" -# mkdir -p /etc/apt/sources.list.d -# cat >/etc/apt/sources.list.d/debian.sources <<'EOF' -# Types: deb deb-src -# URIs: http://deb.debian.org/debian -# Suites: bookworm -# Components: main -# Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg -# EOF -# msg_ok "Stub /etc/apt/sources.list.d/debian.sources created" - -# msg_info "Updating APT cache" -# $STD apt-get update -# msg_ok "APT cache updated" - -# msg_info "Building Nginx with Custom Modules" -# $STD bash /opt/frigate/docker/main/build_nginx.sh -# sed -e '/s6-notifyoncheck/ s/^#*/#/' -i /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/run -# ln -sf /usr/local/nginx/sbin/nginx /usr/local/bin/nginx -# msg_ok "Built Nginx" - -# msg_info "Cleanup stub debian.sources" -# rm -f /etc/apt/sources.list.d/debian.sources -# $STD apt-get update -# msg_ok "Removed stub and updated APT cache" - -# $STD /opt/frigate/docker/main/install_deps.sh -# $STD apt update -# $STD ln -svf /usr/lib/btbn-ffmpeg/bin/ffmpeg /usr/local/bin/ffmpeg -# $STD ln -svf /usr/lib/btbn-ffmpeg/bin/ffprobe /usr/local/bin/ffprobe -# $STD pip3 install -U /wheels/*.whl -# ldconfig -# $STD pip3 install -r /opt/frigate/docker/main/requirements-dev.txt -# $STD /opt/frigate/.devcontainer/initialize.sh -# $STD make version -# cd /opt/frigate/web -# $STD npm install -# $STD npm run build -# cp -r /opt/frigate/web/dist/* /opt/frigate/web/ -# cp -r /opt/frigate/config/. /config -# sed -i '/^s6-svc -O \.$/s/^/#/' /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/frigate/run -# cat </config/config.yml -# mqtt: -# enabled: false -# cameras: -# test: -# ffmpeg: -# #hwaccel_args: preset-vaapi -# inputs: -# - path: /media/frigate/person-bicycle-car-detection.mp4 -# input_args: -re -stream_loop -1 -fflags +genpts -# roles: -# - detect -# - rtmp -# detect: -# height: 1080 -# width: 1920 -# fps: 5 -# EOF -# ln -sf /config/config.yml /opt/frigate/config/config.yml -# if [[ "$CTTYPE" == "0" ]]; then -# sed -i -e 's/^kvm:x:104:$/render:x:104:root,frigate/' -e 's/^render:x:105:root$/kvm:x:105:/' /etc/group -# else -# sed -i -e 's/^kvm:x:104:$/render:x:104:frigate/' -e 's/^render:x:105:$/kvm:x:105:/' /etc/group -# fi -# echo "tmpfs /tmp/cache tmpfs defaults 0 0" >>/etc/fstab -# msg_ok "Installed Frigate" - -# # read -p "Semantic Search requires a dedicated GPU and at least 16GB RAM. Would you like to install it? (y/n): " semantic_choice -# # if [[ "$semantic_choice" == "y" ]]; then -# # msg_info "Configuring Semantic Search & AI Models" -# # mkdir -p /opt/frigate/models/semantic_search -# # curl -fsSL -o /opt/frigate/models/semantic_search/clip_model.pt https://huggingface.co/openai/clip-vit-base-patch32/resolve/main/pytorch_model.bin -# # msg_ok "Semantic Search Models Installed" -# # else -# # msg_ok "Skipped Semantic Search Setup" -# # fi - -# msg_info "Building and Installing libUSB without udev" -# wget -qO /tmp/libusb.zip https://github.com/libusb/libusb/archive/v1.0.29.zip -# unzip -q /tmp/libusb.zip -d /tmp/ -# cd /tmp/libusb-1.0.29 -# ./bootstrap.sh -# ./configure --disable-udev --enable-shared -# make -j$(nproc --all) -# make install -# ldconfig -# rm -rf /tmp/libusb.zip /tmp/libusb-1.0.29 -# msg_ok "Installed libUSB without udev" - -# msg_info "Installing Coral Object Detection Model (Patience)" -# cd /opt/frigate -# export CCACHE_DIR=/root/.ccache -# export CCACHE_MAXSIZE=2G -# cd libusb -# $STD ./bootstrap.sh -# $STD ./configure --disable-udev --enable-shared -# $STD make -j $(nproc --all) -# cd /opt/frigate/libusb/libusb -# mkdir -p /usr/local/lib -# $STD /bin/bash ../libtool --mode=install /usr/bin/install -c libusb-1.0.la '/usr/local/lib' -# mkdir -p /usr/local/include/libusb-1.0 -# $STD /usr/bin/install -c -m 644 libusb.h '/usr/local/include/libusb-1.0' -# ldconfig -# cd / -# wget -qO edgetpu_model.tflite https://github.com/google-coral/test_data/raw/release-frogfish/ssdlite_mobiledet_coco_qat_postprocess_edgetpu.tflite -# wget -qO cpu_model.tflite https://github.com/google-coral/test_data/raw/release-frogfish/ssdlite_mobiledet_coco_qat_postprocess.tflite -# cp /opt/frigate/labelmap.txt /labelmap.txt -# wget -qO yamnet-tflite-classification-tflite-v1.tar.gz https://www.kaggle.com/api/v1/models/google/yamnet/tfLite/classification-tflite/1/download -# tar xzf yamnet-tflite-classification-tflite-v1.tar.gz -# rm -rf yamnet-tflite-classification-tflite-v1.tar.gz -# mv 1.tflite cpu_audio_model.tflite -# cp /opt/frigate/audio-labelmap.txt /audio-labelmap.txt -# mkdir -p /media/frigate -# wget -qO /media/frigate/person-bicycle-car-detection.mp4 https://github.com/intel-iot-devkit/sample-videos/raw/master/person-bicycle-car-detection.mp4 -# msg_ok "Installed Coral Object Detection Model" - -# msg_info "Installing Tempio" -# sed -i 's|/rootfs/usr/local|/usr/local|g' /opt/frigate/docker/main/install_tempio.sh -# TARGETARCH="amd64" -# $STD /opt/frigate/docker/main/install_tempio.sh -# chmod +x /usr/local/tempio/bin/tempio -# ln -sf /usr/local/tempio/bin/tempio /usr/local/bin/tempio -# msg_ok "Installed Tempio" - -# msg_info "Creating Services" -# cat </etc/systemd/system/create_directories.service -# [Unit] -# Description=Create necessary directories for logs - -# [Service] -# Type=oneshot -# ExecStart=/bin/bash -c '/bin/mkdir -p /dev/shm/logs/{frigate,go2rtc,nginx} && /bin/touch /dev/shm/logs/{frigate/current,go2rtc/current,nginx/current} && /bin/chmod -R 777 /dev/shm/logs' - -# [Install] -# WantedBy=multi-user.target -# EOF -# systemctl enable -q --now create_directories -# sleep 3 -# cat </etc/systemd/system/go2rtc.service -# [Unit] -# Description=go2rtc service -# After=network.target -# After=create_directories.service -# StartLimitIntervalSec=0 - -# [Service] -# Type=simple -# Restart=always -# RestartSec=1 -# User=root -# ExecStartPre=+rm /dev/shm/logs/go2rtc/current -# ExecStart=/bin/bash -c "bash /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/run 2> >(/usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S ' >&2) | /usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S '" -# StandardOutput=file:/dev/shm/logs/go2rtc/current -# StandardError=file:/dev/shm/logs/go2rtc/current - -# [Install] -# WantedBy=multi-user.target -# EOF -# systemctl enable -q --now go2rtc -# sleep 3 -# cat </etc/systemd/system/frigate.service -# [Unit] -# Description=Frigate service -# After=go2rtc.service -# After=create_directories.service -# StartLimitIntervalSec=0 - -# [Service] -# Type=simple -# Restart=always -# RestartSec=1 -# User=root -# # Environment=PLUS_API_KEY= -# ExecStartPre=+rm /dev/shm/logs/frigate/current -# ExecStart=/bin/bash -c "bash /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/frigate/run 2> >(/usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S ' >&2) | /usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S '" -# StandardOutput=file:/dev/shm/logs/frigate/current -# StandardError=file:/dev/shm/logs/frigate/current - -# [Install] -# WantedBy=multi-user.target -# EOF -# systemctl enable -q --now frigate -# sleep 3 -# cat </etc/systemd/system/nginx.service -# [Unit] -# Description=Nginx service -# After=frigate.service -# After=create_directories.service -# StartLimitIntervalSec=0 - -# [Service] -# Type=simple -# Restart=always -# RestartSec=1 -# User=root -# ExecStartPre=+rm /dev/shm/logs/nginx/current -# ExecStart=/bin/bash -c "bash /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/run 2> >(/usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S ' >&2) | /usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S '" -# StandardOutput=file:/dev/shm/logs/nginx/current -# StandardError=file:/dev/shm/logs/nginx/current - -# [Install] -# WantedBy=multi-user.target -# EOF -# systemctl enable -q --now nginx -# msg_ok "Configured Services" - motd_ssh customize From d01ae72a5f7b0a1f677cfbc9bbe4763ccf85c73a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 10:21:02 +0200 Subject: [PATCH 0551/1733] fixes --- install/frigate-install.sh | 2 ++ install/viseron-install.sh | 41 ++++++++++++++++++++++++++++---------- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/install/frigate-install.sh b/install/frigate-install.sh index bc4eba13a..6078c1890 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -152,6 +152,8 @@ After=go2rtc.service network.target [Service] WorkingDirectory=/opt/frigate Environment="PATH=/opt/frigate/venv/bin" +Environment="PYTHONPATH=/opt/frigate" +ExecStartPre=/bin/mkdir -p /media/frigate/recordings /media/frigate/clips /media/frigate/snapshots ExecStart=/opt/frigate/venv/bin/python3 -u -m frigate Restart=always RestartSec=5 diff --git a/install/viseron-install.sh b/install/viseron-install.sh index a57f6356c..f2ae8d018 100644 --- a/install/viseron-install.sh +++ b/install/viseron-install.sh @@ -13,8 +13,6 @@ setting_up_container network_check update_os -PYTHON_VERSION="3.12" setup_uv - msg_info "Installing Dependencies" $STD apt-get install -y \ python3-opencv jq \ @@ -23,9 +21,28 @@ $STD apt-get install -y \ gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-libav \ build-essential python3-dev python3-gi pkg-config libcairo2-dev gir1.2-glib-2.0 \ cmake gfortran libopenblas-dev liblapack-dev libgirepository1.0-dev git - msg_ok "Installed Dependencies" +PYTHON_VERSION="3.12" setup_uv +PG_VERSION="16" setup_postgresql + +msg_info "Setting up PostgreSQL Database" +DB_NAME=viseron +DB_USER=viseron_usr +DB_PASS="$(openssl rand -base64 18 | cut -c1-13)" +$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" +$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC'" +{ + echo "Hanko-Credentials" + echo "Hanko Database User: $DB_USER" + echo "Hanko Database Password: $DB_PASS" + echo "Hanko Database Name: $DB_NAME" +} >>~/hanko.creds +msg_ok "Set up PostgreSQL Database" + # msg_info "Setting up Hardware Acceleration" # if [[ "$CTTYPE" == "0" ]]; then # chgrp video /dev/dri @@ -45,14 +62,10 @@ msg_ok "Python Environment Setup" msg_info "Setup Viseron (Patience)" UV_HTTP_TIMEOUT=600 uv pip install --python /opt/viseron/.venv/bin/python -e /opt/viseron/. UV_HTTP_TIMEOUT=600 uv pip install --python /opt/viseron/.venv/bin/python -r /opt/viseron/requirements.txt +mkdir -p /config/{recordings,snapshots,segments,event_clips,thumbnails} +for d in recordings snapshots segments event_clips thumbnails; do ln -s "/config/$d" "/$d" 2>/dev/null || true; done msg_ok "Setup Viseron" -msg_info "Creating Configuration Directory" -mkdir -p /config -mkdir -p /config/recordings -mkdir -p /config/logs -msg_ok "Created Configuration Directory" - msg_info "Creating Default Configuration" cat </config/viseron.yaml # Viseron Configuration @@ -105,6 +118,14 @@ motion_detection: enabled: true threshold: 25 sensitivity: 0.8 + +storage: + connection_string: postgresql://$DB_USER:$DB_PASS@localhost:5432/$DB_NAME + recordings: /recordings + snapshots: /snapshots + segments: /segments + event_clips: /event_clips + thumbnails: /thumbnails EOF msg_ok "Created Default Configuration" @@ -119,7 +140,7 @@ Type=simple User=root WorkingDirectory=/opt/viseron Environment=PATH=/opt/viseron/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin -ExecStart=/opt/viseron/bin/python -m viseron --config /config/viseron.yaml +ExecStart=/opt/viseron/.venv/bin/python -m viseron --config /config/viseron.yaml Restart=always RestartSec=10 From 5a735d09e58fe6944a4c5b35ef1893a8cbdd49ff Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 11:29:10 +0200 Subject: [PATCH 0552/1733] test --- install/frigate-install.sh | 18 +++++++++++++++--- install/viseron-install.sh | 12 ++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/install/frigate-install.sh b/install/frigate-install.sh index 6078c1890..3d5be25ae 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -14,7 +14,7 @@ network_check update_os msg_info "Installing Dependencies (Patience)" -$STD apt-get install -y {git,ca-certificates,automake,build-essential,xz-utils,libtool,ccache,pkg-config,libgtk-3-dev,libavcodec-dev,libavformat-dev,libswscale-dev,libv4l-dev,libxvidcore-dev,libx264-dev,libjpeg-dev,libpng-dev,libtiff-dev,gfortran,openexr,libatlas-base-dev,libssl-dev,libtbbmalloc2,libtbb-dev,libdc1394-dev,libopenexr-dev,libgstreamer-plugins-base1.0-dev,libgstreamer1.0-dev,gcc,gfortran,libopenblas-dev,liblapack-dev,libusb-1.0-0-dev,jq,moreutils} +$STD apt-get install -y {git,ffmpeg,ca-certificates,automake,build-essential,xz-utils,libtool,ccache,pkg-config,libgtk-3-dev,libavcodec-dev,libavformat-dev,libswscale-dev,libv4l-dev,libxvidcore-dev,libx264-dev,libjpeg-dev,libpng-dev,libtiff-dev,gfortran,openexr,libatlas-base-dev,libssl-dev,libtbbmalloc2,libtbb-dev,libdc1394-dev,libopenexr-dev,libgstreamer-plugins-base1.0-dev,libgstreamer1.0-dev,gcc,gfortran,libopenblas-dev,liblapack-dev,libusb-1.0-0-dev,jq,moreutils} msg_ok "Installed Dependencies" msg_info "Setup Python3" @@ -48,7 +48,7 @@ msg_ok "Python venv ready" msg_info "Building Web UI" cd /opt/frigate/web -$STD npm install +$STD npm ci $STD npm run build msg_ok "Web UI built" @@ -75,6 +75,11 @@ mkdir -p /config ln -sf /opt/frigate/config/config.yml /config/config.yml mkdir -p /media/frigate wget -qO /media/frigate/person-bicycle-car-detection.mp4 https://github.com/intel-iot-devkit/sample-videos/raw/master/person-bicycle-car-detection.mp4 +cat <<'EOF' >/opt/frigate/frigate/version.py +VERSION = "0.16.0" +EOF +ln -sf /usr/lib/ffmpeg/7.0/bin/ffmpeg /usr/bin/ffmpeg +ln -sf /usr/lib/ffmpeg/7.0/bin/ffprobe /usr/bin/ffprobe msg_ok "Config ready" msg_info "Building and Installing libUSB without udev" @@ -121,6 +126,13 @@ chmod +x /usr/local/tempio/bin/tempio ln -sf /usr/local/tempio/bin/tempio /usr/local/bin/tempio msg_ok "Installed Tempio" +msg_info "Copying model files" +cp /opt/frigate/cpu_model.tflite / +cp /opt/frigate/edgetpu_model.tflite / +cp /opt/frigate/audio-labelmap.txt / +cp /opt/frigate/labelmap.txt / +msg_ok "Copied model files" + # ------------------------------------------------------------ # systemd Units msg_info "Creating systemd service for go2rtc" @@ -187,8 +199,8 @@ EOF msg_ok "Environment set" msg_info "Building Nginx with Custom Modules" -$STD bash /opt/frigate/docker/main/build_nginx.sh sed -i 's/if \[\[ "\$VERSION_ID" == "12" \]\]; then/if \[\[ "\$VERSION_ID" == "13" \]\]; then/' /opt/frigate/docker/main/build_nginx.sh +$STD bash /opt/frigate/docker/main/build_nginx.sh sed -e '/s6-notifyoncheck/ s/^#*/#/' -i /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/run ln -sf /usr/local/nginx/sbin/nginx /usr/local/bin/nginx msg_ok "Built Nginx" diff --git a/install/viseron-install.sh b/install/viseron-install.sh index f2ae8d018..f15a0f424 100644 --- a/install/viseron-install.sh +++ b/install/viseron-install.sh @@ -60,6 +60,18 @@ uv pip install --python /opt/viseron/.venv/bin/python --upgrade pip setuptools w msg_ok "Python Environment Setup" msg_info "Setup Viseron (Patience)" +if ls /dev/nvidia* >/dev/null 2>&1; then + msg_info "GPU detected → Installing PyTorch with CUDA" + UV_HTTP_TIMEOUT=600 uv pip install --python /opt/viseron/.venv/bin/python \ + torch==2.8.0 torchvision==0.19.0 torchaudio==2.8.0 + msg_ok "Installed Torch with CUDA" +else + msg_info "No GPU detected → Installing CPU-only PyTorch" + UV_HTTP_TIMEOUT=600 uv pip install --python /opt/viseron/.venv/bin/python \ + torch==2.8.0+cpu torchvision==0.19.0+cpu torchaudio==2.8.0+cpu \ + --extra-index-url https://download.pytorch.org/whl/cpu + msg_ok "Installed Torch CPU-only" +fi UV_HTTP_TIMEOUT=600 uv pip install --python /opt/viseron/.venv/bin/python -e /opt/viseron/. UV_HTTP_TIMEOUT=600 uv pip install --python /opt/viseron/.venv/bin/python -r /opt/viseron/requirements.txt mkdir -p /config/{recordings,snapshots,segments,event_clips,thumbnails} From 60274543040686df018ac4abaf5b476fbf66d85e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 11:35:50 +0200 Subject: [PATCH 0553/1733] ente --- ct/ente.sh | 42 ++++++++++++++ install/ente-install.sh | 126 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 168 insertions(+) create mode 100644 ct/ente.sh create mode 100644 install/ente-install.sh diff --git a/ct/ente.sh b/ct/ente.sh new file mode 100644 index 000000000..3074d79bd --- /dev/null +++ b/ct/ente.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://www.debian.org/ + +APP="Ente" +var_tags="${var_tags:-photos}" +var_cpu="${var_cpu:-4}" +var_ram="${var_ram:-4096}" +var_disk="${var_disk:-10}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /var ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating $APP LXC" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Updated $APP LXC" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!" +msg_custom "🚀" "${GN}" "${APP} setup has been successfully initialized!" diff --git a/install/ente-install.sh b/install/ente-install.sh new file mode 100644 index 000000000..edcd8b0cd --- /dev/null +++ b/install/ente-install.sh @@ -0,0 +1,126 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: MickLesk +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/ente-io/ente + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt-get install -y \ + libsodium23 libsodium-dev pkg-config caddy +msg_ok "Installed Dependencies" + +PG_VERSION="17" setup_postgresql +setup_go +NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs +fetch_and_deploy_gh_release "ente" "ente-io/ente" "tarball" "latest" "/opt/ente" + +msg_info "Setting up PostgreSQL" +DB_NAME="ente_db" +DB_USER="ente" +DB_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" +$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" +$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" +{ + echo "Ente Credentials" + echo "Database Name: $DB_NAME" + echo "Database User: $DB_USER" + echo "Database Password: $DB_PASS" +} >>~/ente.creds +msg_ok "Set up PostgreSQL" + +msg_info "Building Museum (server)" +cd /opt/ente/server +$STD corepack enable +$STD go mod tidy +$STD go build cmd/museum/main.go +cp config/example.yaml museum.yaml +msg_ok "Built Museum" + +msg_info "Generating Secrets" +$STD go run tools/gen-random-keys/main.go >secrets.txt +msg_ok "Generated Secrets" + +msg_info "Creating Museum Service" +cat </etc/systemd/system/ente-museum.service +[Unit] +Description=Ente Museum Server +After=network.target postgresql.service + +[Service] +WorkingDirectory=/opt/ente/server +ExecStart=/opt/ente/server/main +Restart=always +Environment="DATABASE_URL=postgresql://$DB_USER:$DB_PASS@127.0.0.1:5432/$DB_NAME" + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now ente-museum +msg_ok "Created Museum Service" + +msg_info "Building Web Applications" +cd /opt/ente/web +$STD yarn install +export NEXT_PUBLIC_ENTE_ENDPOINT=http://localhost:8080 +export NEXT_PUBLIC_ENTE_ALBUMS_ENDPOINT=http://localhost:3002 +$STD yarn build +$STD yarn build:accounts +$STD yarn build:auth +$STD yarn build:cast +mkdir -p /var/www/ente/apps +cp -r apps/photos/out /var/www/ente/apps/photos +cp -r apps/accounts/out /var/www/ente/apps/accounts +cp -r apps/auth/out /var/www/ente/apps/auth +cp -r apps/cast/out /var/www/ente/apps/cast +msg_ok "Built Web Applications" + +msg_info "Configuring Caddy" +cat </etc/caddy/Caddyfile +:3000 { + root * /var/www/ente/apps/photos + file_server + try_files {path} {path}.html /index.html +} +:3001 { + root * /var/www/ente/apps/accounts + file_server + try_files {path} {path}.html /index.html +} +:3002 { + root * /var/www/ente/apps/photos + file_server + try_files {path} {path}.html /index.html +} +:3003 { + root * /var/www/ente/apps/auth + file_server + try_files {path} {path}.html /index.html +} +:3004 { + root * /var/www/ente/apps/cast + file_server + try_files {path} {path}.html /index.html +} +EOF +systemctl reload caddy +msg_ok "Configured Caddy" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 8adac20faab7a8aec0090b4d6cce71defffe77a9 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 11:40:53 +0200 Subject: [PATCH 0554/1733] fix libsodium --- install/ente-install.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/install/ente-install.sh b/install/ente-install.sh index edcd8b0cd..4b0b38391 100644 --- a/install/ente-install.sh +++ b/install/ente-install.sh @@ -44,6 +44,8 @@ msg_info "Building Museum (server)" cd /opt/ente/server $STD corepack enable $STD go mod tidy +export CGO_CFLAGS="$(pkg-config --cflags libsodium)" +export CGO_LDFLAGS="$(pkg-config --libs libsodium)" $STD go build cmd/museum/main.go cp config/example.yaml museum.yaml msg_ok "Built Museum" From d6aa91c7aafef2cf3faf0f92ed4a056d8b3c943d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 11:50:41 +0200 Subject: [PATCH 0555/1733] Update ente-install.sh --- install/ente-install.sh | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/install/ente-install.sh b/install/ente-install.sh index 4b0b38391..72cdf9270 100644 --- a/install/ente-install.sh +++ b/install/ente-install.sh @@ -44,8 +44,17 @@ msg_info "Building Museum (server)" cd /opt/ente/server $STD corepack enable $STD go mod tidy -export CGO_CFLAGS="$(pkg-config --cflags libsodium)" -export CGO_LDFLAGS="$(pkg-config --libs libsodium)" +export CGO_ENABLED=1 +CGO_CFLAGS="$(pkg-config --cflags libsodium || true)" +CGO_LDFLAGS="$(pkg-config --libs libsodium || true)" +if [ -z "$CGO_CFLAGS" ]; then + CGO_CFLAGS="-I/usr/include" +fi +if [ -z "$CGO_LDFLAGS" ]; then + CGO_LDFLAGS="-lsodium" +fi +export CGO_CFLAGS +export CGO_LDFLAGS $STD go build cmd/museum/main.go cp config/example.yaml museum.yaml msg_ok "Built Museum" From 6b5f70b6e2590f7af597f2a6714db19f95f43ddf Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 11:54:48 +0200 Subject: [PATCH 0556/1733] Create copyparty.json --- frontend/public/json/copyparty.json | 40 +++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 frontend/public/json/copyparty.json diff --git a/frontend/public/json/copyparty.json b/frontend/public/json/copyparty.json new file mode 100644 index 000000000..ccc14f4ea --- /dev/null +++ b/frontend/public/json/copyparty.json @@ -0,0 +1,40 @@ +{ + "name": "Copyparty", + "slug": "copyparty", + "categories": [ + 1 + ], + "date_created": "2025-08-18", + "type": "addon", + "updateable": true, + "privileged": false, + "interface_port": null, + "documentation": "https://github.com/9001/copyparty?tab=readme-ov-file#the-browser", + "website": "https://github.com/9001/copyparty", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/copyparty.webp", + "config_path": "", + "description": "This script automatically adds IP address as tags to LXC containers using a Systemd service. The service also updates the tags if a LXC IP address is changed.", + "install_methods": [ + { + "type": "default", + "script": "tools/addon/copyparty.sh", + "resources": { + "cpu": null, + "ram": null, + "hdd": null, + "os": null, + "version": null + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Execute within the Proxmox shell or in LXC", + "type": "info" + } + ] +} From 120e5ac5242b82021fd6c8f4a671bd6ed5caaea5 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 12:00:20 +0200 Subject: [PATCH 0557/1733] copyparty --- frontend/public/json/copyparty.json | 2 +- tools/addon/copyparty.sh | 41 ++++++++++------------------- 2 files changed, 15 insertions(+), 28 deletions(-) diff --git a/frontend/public/json/copyparty.json b/frontend/public/json/copyparty.json index ccc14f4ea..bc1fb40c0 100644 --- a/frontend/public/json/copyparty.json +++ b/frontend/public/json/copyparty.json @@ -13,7 +13,7 @@ "website": "https://github.com/9001/copyparty", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/copyparty.webp", "config_path": "", - "description": "This script automatically adds IP address as tags to LXC containers using a Systemd service. The service also updates the tags if a LXC IP address is changed.", + "description": "Copyparty is a lightweight, portable HTTP file server with a browser-based interface. It supports drag-and-drop uploads, downloads, deduplication, media playback, and advanced search, making it ideal for quickly sharing and managing files.", "install_methods": [ { "type": "default", diff --git a/tools/addon/copyparty.sh b/tools/addon/copyparty.sh index 79bd509fe..c7bbd32da 100644 --- a/tools/addon/copyparty.sh +++ b/tools/addon/copyparty.sh @@ -64,7 +64,8 @@ function setup_user_and_dirs() { if [[ "$OS" == "Debian" ]]; then useradd -r -s /sbin/nologin -d "$DATA_PATH" "$SVC_USER" else - adduser -D -H -h "$DATA_PATH" -s /sbin/nologin "$SVC_USER" + addgroup -S "$SVC_GROUP" 2>/dev/null || true + adduser -S -D -H -G "$SVC_GROUP" -h "$DATA_PATH" -s /sbin/nologin "$SVC_USER" 2>/dev/null || true fi fi mkdir -p "$DATA_PATH" "$LOG_PATH" @@ -190,42 +191,28 @@ msg_ok "Config written" # --- Systemd/OpenRC Service --- msg_info "Creating service" if [[ "$OS" == "Debian" ]]; then - cat <"$SERVICE_PATH_DEB" -[Unit] -Description=CopyParty file server - -[Service] -Type=simple -User=$SVC_USER -Group=$SVC_GROUP -WorkingDirectory=$USER_DATA_PATH -Environment=PYTHONUNBUFFERED=x -LogsDirectory=copyparty -ExecStart=/usr/bin/python3 $BIN_PATH -c $CONF_PATH -Restart=always - -[Install] -WantedBy=multi-user.target -EOF - systemctl daemon-reload - systemctl enable --now copyparty &>/dev/null -else - cat <"$SERVICE_PATH_ALP" + cat <<'EOF' >"$SERVICE_PATH_ALP" #!/sbin/openrc-run -command="/usr/bin/python3" -command_args="$BIN_PATH -c $CONF_PATH" +name="copyparty" +description="Copyparty file server" + +command="$(command -v python3)" +command_args="/usr/local/bin/copyparty-sfx.py -c /etc/copyparty.conf" command_background=true -directory="$USER_DATA_PATH" -pidfile="$USER_DATA_PATH/copyparty.pid" +directory="/var/lib/copyparty" +pidfile="/run/copyparty.pid" +output_log="/var/log/copyparty/copyparty.log" +error_log="/var/log/copyparty/copyparty.err" depend() { need net } EOF + chmod +x "$SERVICE_PATH_ALP" rc-update add copyparty default &>/dev/null - rc-service copyparty start &>/dev/null + rc-service copyparty restart &>/dev/null fi msg_ok "Service created and started" From aa413772cd165c2bbfe06b4eafad3a1dc8a4bb76 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 12:01:46 +0200 Subject: [PATCH 0558/1733] add source --- tools/addon/copyparty.sh | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/tools/addon/copyparty.sh b/tools/addon/copyparty.sh index c7bbd32da..78041b97d 100644 --- a/tools/addon/copyparty.sh +++ b/tools/addon/copyparty.sh @@ -3,6 +3,7 @@ # Copyright (c) 2021-2025 community-scripts ORG # Author: MickLesk # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/9001/copyparty function header_info() { clear @@ -37,7 +38,6 @@ SVC_GROUP="copyparty" SRC_URL="https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py" DEFAULT_PORT=3923 -# OS Detection if [[ -f "/etc/alpine-release" ]]; then OS="Alpine" PKG_MANAGER="apk add --no-cache" @@ -57,7 +57,6 @@ function msg_info() { echo -e "${INFO} ${YW}$1...${CL}"; } function msg_ok() { echo -e "${CM} ${GN}$1${CL}"; } function msg_error() { echo -e "${CROSS} ${RD}$1${CL}"; } -# User/Group/Dirs function setup_user_and_dirs() { msg_info "Creating $SVC_USER user and directories" if ! id "$SVC_USER" &>/dev/null; then @@ -97,7 +96,6 @@ function update_copyparty() { exit 0 } -# --- Existing Install/Update/Uninstall Check --- if [[ -f "$BIN_PATH" ]]; then echo -e "${YW}⚠️ $APP is already installed.${CL}" echo -n "Uninstall $APP? (y/N): " @@ -116,7 +114,6 @@ if [[ -f "$BIN_PATH" ]]; then fi fi -# --- Deps --- msg_info "Installing dependencies" if [[ "$OS" == "Debian" ]]; then $PKG_MANAGER python3 curl &>/dev/null @@ -125,17 +122,14 @@ else fi msg_ok "Dependencies installed" -# --- User/Dirs --- setup_user_and_dirs -# --- Download Binary --- msg_info "Downloading $APP" curl -fsSL "$SRC_URL" -o "$BIN_PATH" chmod +x "$BIN_PATH" chown "$SVC_USER:$SVC_GROUP" "$BIN_PATH" msg_ok "Downloaded to $BIN_PATH" -# --- Config: Interaktiv, Auth, Rootdir, Port --- echo -n "Enter port for $APP (default: $DEFAULT_PORT): " read -r PORT PORT=${PORT:-$DEFAULT_PORT} @@ -163,7 +157,6 @@ else msg_ok "Configured with admin user: $ADMIN_USER" fi -# --- Generate /etc/copyparty.conf --- msg_info "Writing config to $CONF_PATH" cat </etc/copyparty.conf [global] @@ -188,7 +181,6 @@ chmod 640 "$CONF_PATH" chown "$SVC_USER:$SVC_GROUP" "$CONF_PATH" msg_ok "Config written" -# --- Systemd/OpenRC Service --- msg_info "Creating service" if [[ "$OS" == "Debian" ]]; then cat <<'EOF' >"$SERVICE_PATH_ALP" @@ -216,7 +208,6 @@ EOF fi msg_ok "Service created and started" -# IP detection (as root, maybe interface up/loopback fallback) IFACE=$(ip -4 route | awk '/default/ {print $5; exit}') IP=$(ip -4 addr show "$IFACE" | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) [[ -z "$IP" ]] && IP=$(hostname -I | awk '{print $1}') From 510829520d7b37aa7958bc4b0e240bd58ef95454 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 12:05:29 +0200 Subject: [PATCH 0559/1733] Update copyparty.sh --- tools/addon/copyparty.sh | 49 +++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/tools/addon/copyparty.sh b/tools/addon/copyparty.sh index 78041b97d..897ad753e 100644 --- a/tools/addon/copyparty.sh +++ b/tools/addon/copyparty.sh @@ -158,31 +158,38 @@ else fi msg_info "Writing config to $CONF_PATH" -cat </etc/copyparty.conf -[global] - p: $PORT - ansi - e2dsa - e2ts - theme: 2 - grid - -[accounts] - $ADMIN_USER: $ADMIN_PASS - -[/] - $USER_DATA_PATH - accs: - rw: * - rwmda: $ADMIN_USER -EOF +msg_info "Writing config to $CONF_PATH" +{ + echo "[global]" + echo " p: $PORT" + echo " ansi" + echo " e2dsa" + echo " e2ts" + echo " theme: 2" + echo " grid" + echo + if [[ -n "$ADMIN_USER" && -n "$ADMIN_PASS" ]]; then + echo "[accounts]" + echo " $ADMIN_USER: $ADMIN_PASS" + echo + fi + echo "[/]" + echo " $USER_DATA_PATH" + echo " accs:" + if [[ -n "$ADMIN_USER" ]]; then + echo " rw: *" + echo " rwmda: $ADMIN_USER" + else + echo " rw: *" + fi +} >"$CONF_PATH" chmod 640 "$CONF_PATH" chown "$SVC_USER:$SVC_GROUP" "$CONF_PATH" msg_ok "Config written" msg_info "Creating service" -if [[ "$OS" == "Debian" ]]; then +if [[ "$OS" == "Alpine" ]]; then cat <<'EOF' >"$SERVICE_PATH_ALP" #!/sbin/openrc-run @@ -203,8 +210,8 @@ depend() { EOF chmod +x "$SERVICE_PATH_ALP" - rc-update add copyparty default &>/dev/null - rc-service copyparty restart &>/dev/null + rc-update add copyparty default >/dev/null 2>&1 + rc-service copyparty restart >/dev/null 2>&1 fi msg_ok "Service created and started" From 54934da806433ca89f80d595a23875acf91dfbea Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 12:06:14 +0200 Subject: [PATCH 0560/1733] Update copyparty.sh --- tools/addon/copyparty.sh | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/tools/addon/copyparty.sh b/tools/addon/copyparty.sh index 897ad753e..98f7ad72d 100644 --- a/tools/addon/copyparty.sh +++ b/tools/addon/copyparty.sh @@ -189,7 +189,28 @@ chown "$SVC_USER:$SVC_GROUP" "$CONF_PATH" msg_ok "Config written" msg_info "Creating service" -if [[ "$OS" == "Alpine" ]]; then +if [[ "$OS" == "Debian" ]]; then + cat <"$SERVICE_PATH_DEB" +[Unit] +Description=Copyparty file server +After=network.target + +[Service] +User=$SVC_USER +Group=$SVC_GROUP +WorkingDirectory=$DATA_PATH +ExecStart=/usr/bin/python3 /usr/local/bin/copyparty-sfx.py -c /etc/copyparty.conf +Restart=always +StandardOutput=append:/var/log/copyparty/copyparty.log +StandardError=append:/var/log/copyparty/copyparty.err + +[Install] +WantedBy=multi-user.target +EOF + + systemctl enable -q --now copyparty + +elif [[ "$OS" == "Alpine" ]]; then cat <<'EOF' >"$SERVICE_PATH_ALP" #!/sbin/openrc-run From 89e7dd5e519c9cf28505ef898e77800cd3e82e76 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 12:06:50 +0200 Subject: [PATCH 0561/1733] add gcc --- install/ente-install.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/install/ente-install.sh b/install/ente-install.sh index 72cdf9270..d24342bf6 100644 --- a/install/ente-install.sh +++ b/install/ente-install.sh @@ -15,7 +15,11 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - libsodium23 libsodium-dev pkg-config caddy + libsodium23 \ + libsodium-dev \ + pkg-config \ + caddy \ + gcc msg_ok "Installed Dependencies" PG_VERSION="17" setup_postgresql From 7624e540e4c2a11e396602c895e6e7b5f2a67a95 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 13:33:14 +0200 Subject: [PATCH 0562/1733] Update ente-install.sh --- install/ente-install.sh | 65 +++++++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 16 deletions(-) diff --git a/install/ente-install.sh b/install/ente-install.sh index d24342bf6..b65277581 100644 --- a/install/ente-install.sh +++ b/install/ente-install.sh @@ -60,30 +60,46 @@ fi export CGO_CFLAGS export CGO_LDFLAGS $STD go build cmd/museum/main.go -cp config/example.yaml museum.yaml msg_ok "Built Museum" msg_info "Generating Secrets" -$STD go run tools/gen-random-keys/main.go >secrets.txt +SECRET_ENC=$($STD go run tools/gen-random-keys/main.go | grep "encryption" | awk '{print $2}') +SECRET_HASH=$($STD go run tools/gen-random-keys/main.go | grep "hash" | awk '{print $2}') +SECRET_JWT=$($STD go run tools/gen-random-keys/main.go | grep "jwt" | awk '{print $2}') msg_ok "Generated Secrets" -msg_info "Creating Museum Service" -cat </etc/systemd/system/ente-museum.service -[Unit] -Description=Ente Museum Server -After=network.target postgresql.service +msg_info "Creating museum.yaml" +cat </opt/ente/server/museum.yaml +db: + host: 127.0.0.1 + port: 5432 + name: $DB_NAME + user: $DB_USER + password: $DB_PASS -[Service] -WorkingDirectory=/opt/ente/server -ExecStart=/opt/ente/server/main -Restart=always -Environment="DATABASE_URL=postgresql://$DB_USER:$DB_PASS@127.0.0.1:5432/$DB_NAME" +s3: + are_local_buckets: true + use_path_style_urls: true + local-dev: + key: dummy + secret: dummy + endpoint: localhost:3200 + region: eu-central-2 + bucket: ente-dev -[Install] -WantedBy=multi-user.target +apps: + public-albums: http://localhost:3002 + cast: http://localhost:3004 + accounts: http://localhost:3001 + +key: + encryption: $SECRET_ENC + hash: $SECRET_HASH + +jwt: + secret: $SECRET_JWT EOF -systemctl enable -q --now ente-museum -msg_ok "Created Museum Service" +msg_ok "Created museum.yaml" msg_info "Building Web Applications" cd /opt/ente/web @@ -101,6 +117,23 @@ cp -r apps/auth/out /var/www/ente/apps/auth cp -r apps/cast/out /var/www/ente/apps/cast msg_ok "Built Web Applications" +msg_info "Creating Museum Service" +cat </etc/systemd/system/ente-museum.service +[Unit] +Description=Ente Museum Server +After=network.target postgresql.service + +[Service] +WorkingDirectory=/opt/ente/server +ExecStart=/opt/ente/server/main -config /opt/ente/server/museum.yaml +Restart=always + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now ente-museum +msg_ok "Created Museum Service" + msg_info "Configuring Caddy" cat </etc/caddy/Caddyfile :3000 { From b2f6b12ccf0bc954b34a1f2896095442f41520da Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 14:33:26 +0200 Subject: [PATCH 0563/1733] Update litellm.sh --- ct/litellm.sh | 54 +++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/ct/litellm.sh b/ct/litellm.sh index 847b7d8b7..8d2b7f733 100644 --- a/ct/litellm.sh +++ b/ct/litellm.sh @@ -5,7 +5,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/BerriAI/litellm -APP="litellm" +APP="LiteLLM" var_tags="${var_tags:-ai;interface}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" @@ -20,34 +20,34 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources + header_info + check_container_storage + check_container_resources - if [[ ! -f /etc/systemd/system/litellm.service ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - msg_info "Stopping ${APP}" - systemctl stop litellm.service - msg_ok "Stopped ${APP}" - - VENV_PATH="/opt/litellm/.venv" - PYTHON_VERSION="3.13" setup_uv - - msg_info "Updating $APP" - $STD "$VENV_PATH/bin/python" -m pip install --upgrade litellm[proxy] prisma - - msg_info "Updating DB Schema" - uv --directory=/opt/litellm run litellm --config /opt/litellm/litellm.yaml --use_prisma_db_push --skip_server_startup - msg_ok "DB Schema Updated" - - msg_info "Starting ${APP}" - systemctl start litellm.service - msg_ok "Started ${APP}" - msg_ok "Updated Successfully" + if [[ ! -f /etc/systemd/system/litellm.service ]]; then + msg_error "No ${APP} Installation Found!" exit + fi + + msg_info "Stopping ${APP}" + systemctl stop litellm + msg_ok "Stopped ${APP}" + + VENV_PATH="/opt/litellm/.venv" + PYTHON_VERSION="3.13" setup_uv + + msg_info "Updating $APP" + $STD "$VENV_PATH/bin/python" -m pip install --upgrade litellm[proxy] prisma + + msg_info "Updating DB Schema" + $STD uv --directory=/opt/litellm run litellm --config /opt/litellm/litellm.yaml --use_prisma_db_push --skip_server_startup + msg_ok "DB Schema Updated" + + msg_info "Starting ${APP}" + systemctl start litellm + msg_ok "Started ${APP}" + msg_ok "Updated Successfully" + exit } start From 8946dd0c99a60769dc57b8f7cfac2b07a84ee3d2 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 14:39:16 +0200 Subject: [PATCH 0564/1733] Update frigate-install.sh --- install/frigate-install.sh | 225 ++++++++++++++++++++++++------------- 1 file changed, 148 insertions(+), 77 deletions(-) diff --git a/install/frigate-install.sh b/install/frigate-install.sh index 3d5be25ae..08a19b76d 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -14,7 +14,7 @@ network_check update_os msg_info "Installing Dependencies (Patience)" -$STD apt-get install -y {git,ffmpeg,ca-certificates,automake,build-essential,xz-utils,libtool,ccache,pkg-config,libgtk-3-dev,libavcodec-dev,libavformat-dev,libswscale-dev,libv4l-dev,libxvidcore-dev,libx264-dev,libjpeg-dev,libpng-dev,libtiff-dev,gfortran,openexr,libatlas-base-dev,libssl-dev,libtbbmalloc2,libtbb-dev,libdc1394-dev,libopenexr-dev,libgstreamer-plugins-base1.0-dev,libgstreamer1.0-dev,gcc,gfortran,libopenblas-dev,liblapack-dev,libusb-1.0-0-dev,jq,moreutils} +$STD apt-get install -y {git,ca-certificates,automake,build-essential,xz-utils,libtool,ccache,pkg-config,libgtk-3-dev,libavcodec-dev,libavformat-dev,libswscale-dev,libv4l-dev,libxvidcore-dev,libx264-dev,libjpeg-dev,libpng-dev,libtiff-dev,gfortran,openexr,libatlas-base-dev,libssl-dev,libtbb-dev,libdc1394-dev,libopenexr-dev,libgstreamer-plugins-base1.0-dev,libgstreamer1.0-dev,gcc,gfortran,libopenblas-dev,liblapack-dev,libusb-1.0-0-dev,jq,moreutils} msg_ok "Installed Dependencies" msg_info "Setup Python3" @@ -36,23 +36,32 @@ fetch_and_deploy_gh_release "libusb" "libusb/libusb" "tarball" "v1.0.29" "/opt/f # fi # msg_ok "Set Up Hardware Acceleration" -msg_info "Setting up Python venv" +msg_info "Setting up Python" cd /opt/frigate -python3 -m venv venv -source venv/bin/activate -$STD pip install --upgrade pip wheel --break-system-packages -$STD pip install -r docker/main/requirements.txt --break-system-packages -$STD pip install -r docker/main/requirements-wheels.txt --break-system-packages -$STD pip install -r docker/main/requirements-ov.txt --break-system-packages +$STD pip3 wheel --wheel-dir=/wheels -r /opt/frigate/docker/main/requirements-wheels.txt +cp -a /opt/frigate/docker/main/rootfs/. / +export TARGETARCH="amd64" +echo 'libc6 libraries/restart-without-asking boolean true' | debconf-set-selections +$STD apt update +$STD ln -svf /usr/lib/btbn-ffmpeg/bin/ffmpeg /usr/local/bin/ffmpeg +$STD ln -svf /usr/lib/btbn-ffmpeg/bin/ffprobe /usr/local/bin/ffprobe +$STD pip3 install -U /wheels/*.whl +ldconfig +$STD pip3 install -r /opt/frigate/docker/main/requirements-dev.txt +$STD /opt/frigate/.devcontainer/initialize.sh +$STD make version msg_ok "Python venv ready" msg_info "Building Web UI" cd /opt/frigate/web $STD npm ci $STD npm run build +cp -r /opt/frigate/web/dist/* /opt/frigate/web/ +cp -r /opt/frigate/config/. /config msg_ok "Web UI built" msg_info "Writing default config" +sed -i '/^s6-svc -O \.$/s/^/#/' /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/frigate/run mkdir -p /opt/frigate/config cat </opt/frigate/config/config.yml mqtt: @@ -72,16 +81,54 @@ cameras: fps: 5 EOF mkdir -p /config -ln -sf /opt/frigate/config/config.yml /config/config.yml +ln -sf /config/config.yml /opt/frigate/config/config.yml +if [[ "$CTTYPE" == "0" ]]; then + sed -i -e 's/^kvm:x:104:$/render:x:104:root,frigate/' -e 's/^render:x:105:root$/kvm:x:105:/' /etc/group +else + sed -i -e 's/^kvm:x:104:$/render:x:104:frigate/' -e 's/^render:x:105:$/kvm:x:105:/' /etc/group +fi +echo "tmpfs /tmp/cache tmpfs defaults 0 0" >>/etc/fstab mkdir -p /media/frigate wget -qO /media/frigate/person-bicycle-car-detection.mp4 https://github.com/intel-iot-devkit/sample-videos/raw/master/person-bicycle-car-detection.mp4 cat <<'EOF' >/opt/frigate/frigate/version.py VERSION = "0.16.0" EOF -ln -sf /usr/lib/ffmpeg/7.0/bin/ffmpeg /usr/bin/ffmpeg -ln -sf /usr/lib/ffmpeg/7.0/bin/ffprobe /usr/bin/ffprobe msg_ok "Config ready" +if grep -q -o -m1 -E 'avx[^ ]*' /proc/cpuinfo; then + msg_ok "AVX Support Detected" + msg_info "Installing Openvino Object Detection Model (Resilience)" + $STD pip install -r /opt/frigate/docker/main/requirements-ov.txt + cd /opt/frigate/models + export ENABLE_ANALYTICS=NO + $STD /usr/local/bin/omz_downloader --name ssdlite_mobilenet_v2 --num_attempts 2 + $STD /usr/local/bin/omz_converter --name ssdlite_mobilenet_v2 --precision FP16 --mo /usr/local/bin/mo + cd / + cp -r /opt/frigate/models/public/ssdlite_mobilenet_v2 openvino-model + curl -fsSL "https://github.com/openvinotoolkit/open_model_zoo/raw/master/data/dataset_classes/coco_91cl_bkgr.txt" -o "openvino-model/coco_91cl_bkgr.txt" + sed -i 's/truck/car/g' openvino-model/coco_91cl_bkgr.txt + cat <>/config/config.yml +detectors: + ov: + type: openvino + device: CPU + model: + path: /openvino-model/FP16/ssdlite_mobilenet_v2.xml +model: + width: 300 + height: 300 + input_tensor: nhwc + input_pixel_format: bgr + labelmap_path: /openvino-model/coco_91cl_bkgr.txt +EOF + msg_ok "Installed Openvino Object Detection Model" +else + cat <>/config/config.yml +model: + path: /cpu_model.tflite +EOF +fi + msg_info "Building and Installing libUSB without udev" wget -qO /tmp/libusb.zip https://github.com/libusb/libusb/archive/v1.0.29.zip unzip -q /tmp/libusb.zip -d /tmp/ @@ -94,24 +141,35 @@ ldconfig rm -rf /tmp/libusb.zip /tmp/libusb-1.0.29 msg_ok "Installed libUSB without udev" -# Coral Object Detection Models -msg_info "Installing Coral Object Detection Models" +msg_info "Installing Coral Object Detection Model (Patience)" cd /opt/frigate export CCACHE_DIR=/root/.ccache export CCACHE_MAXSIZE=2G - -# edgetpu / cpu Modelle -wget -qO edgetpu_model.tflite https://github.com/google-coral/test_data/raw/release-frogfish/ssdlite_mobiledet_coco_qat_postprocess_edgetpu.tflite -wget -qO cpu_model.tflite https://github.com/google-coral/test_data/raw/release-frogfish/ssdlite_mobiledet_coco_qat_postprocess.tflite +curl -fsSL "https://github.com/libusb/libusb/archive/v1.0.26.zip" -o "v1.0.26.zip" +$STD unzip v1.0.26.zip +rm v1.0.26.zip +cd libusb-1.0.26 +$STD ./bootstrap.sh +$STD ./configure --disable-udev --enable-shared +$STD make -j $(nproc --all) +cd /opt/frigate/libusb-1.0.26/libusb +mkdir -p /usr/local/lib +$STD /bin/bash ../libtool --mode=install /usr/bin/install -c libusb-1.0.la '/usr/local/lib' +mkdir -p /usr/local/include/libusb-1.0 +$STD /usr/bin/install -c -m 644 libusb.h '/usr/local/include/libusb-1.0' +ldconfig +cd / +curl -fsSL "https://github.com/google-coral/test_data/raw/release-frogfish/ssdlite_mobiledet_coco_qat_postprocess_edgetpu.tflite" -o "edgetpu_model.tflite" +curl -fsSL "https://github.com/google-coral/test_data/raw/release-frogfish/ssdlite_mobiledet_coco_qat_postprocess.tflite" -o "cpu_model.tflite" cp /opt/frigate/labelmap.txt /labelmap.txt - -# Audio-Modelle -wget -qO yamnet-tflite-classification-tflite-v1.tar.gz https://www.kaggle.com/api/v1/models/google/yamnet/tfLite/classification-tflite/1/download +curl -fsSL "https://www.kaggle.com/api/v1/models/google/yamnet/tfLite/classification-tflite/1/download" -o "yamnet-tflite-classification-tflite-v1.tar.gz" tar xzf yamnet-tflite-classification-tflite-v1.tar.gz rm -rf yamnet-tflite-classification-tflite-v1.tar.gz mv 1.tflite cpu_audio_model.tflite cp /opt/frigate/audio-labelmap.txt /audio-labelmap.txt -msg_ok "Installed Coral Object Detection Models" +mkdir -p /media/frigate +curl -fsSL "https://github.com/intel-iot-devkit/sample-videos/raw/master/person-bicycle-car-detection.mp4" -o "/media/frigate/person-bicycle-car-detection.mp4" +msg_ok "Installed Coral Object Detection Model" # ------------------------------------------------------------ # Tempio installieren @@ -121,7 +179,7 @@ export TARGETARCH="amd64" export DEBIAN_FRONTEND=noninteractive echo "libedgetpu1-max libedgetpu/accepted-eula select true" | debconf-set-selections echo "libedgetpu1-max libedgetpu/install-confirm-max select true" | debconf-set-selections -/opt/frigate/docker/main/install_tempio.sh +$STD /opt/frigate/docker/main/install_tempio.sh chmod +x /usr/local/tempio/bin/tempio ln -sf /usr/local/tempio/bin/tempio /usr/local/bin/tempio msg_ok "Installed Tempio" @@ -133,82 +191,95 @@ cp /opt/frigate/audio-labelmap.txt / cp /opt/frigate/labelmap.txt / msg_ok "Copied model files" -# ------------------------------------------------------------ -# systemd Units -msg_info "Creating systemd service for go2rtc" -cat </etc/systemd/system/go2rtc.service +msg_info "Building Nginx with Custom Modules" +sed -i 's/if \[\[ "$VERSION_ID" == "12" \]\]; then/if [[ -f \/etc\/apt\/sources.list.d\/debian.sources ]]; then/' /opt/frigate/docker/main/build_nginx.sh +$STD /opt/frigate/docker/main/build_nginx.sh +sed -e '/s6-notifyoncheck/ s/^#*/#/' -i /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/run +ln -sf /usr/local/nginx/sbin/nginx /usr/local/bin/nginx +msg_ok "Built Nginx" + +msg_info "Creating Services" +cat </etc/systemd/system/create_directories.service [Unit] -Description=go2rtc -After=network.target +Description=Create necessary directories for logs [Service] -ExecStart=/usr/local/bin/go2rtc +Type=oneshot +ExecStart=/bin/bash -c '/bin/mkdir -p /dev/shm/logs/{frigate,go2rtc,nginx} && /bin/touch /dev/shm/logs/{frigate/current,go2rtc/current,nginx/current} && /bin/chmod -R 777 /dev/shm/logs' + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now create_directories +sleep 3 +cat </etc/systemd/system/go2rtc.service +[Unit] +Description=go2rtc service +After=network.target +After=create_directories.service +StartLimitIntervalSec=0 + +[Service] +Type=simple Restart=always -RestartSec=2 +RestartSec=1 User=root -StandardOutput=journal -StandardError=journal +Environment=DEFAULT_FFMPEG_VERSION=7.0 +Environment=INCLUDED_FFMPEG_VERSIONS=5.0 +ExecStartPre=+rm /dev/shm/logs/go2rtc/current +ExecStart=/bin/bash -c "bash /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/run 2> >(/usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S ' >&2) | /usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S '" +StandardOutput=file:/dev/shm/logs/go2rtc/current +StandardError=file:/dev/shm/logs/go2rtc/current [Install] WantedBy=multi-user.target EOF systemctl enable -q --now go2rtc -msg_ok "go2rtc service enabled" - -msg_info "Creating systemd service for Frigate" +sleep 3 cat </etc/systemd/system/frigate.service [Unit] Description=Frigate service -After=go2rtc.service network.target +After=go2rtc.service +After=create_directories.service +StartLimitIntervalSec=0 [Service] -WorkingDirectory=/opt/frigate -Environment="PATH=/opt/frigate/venv/bin" -Environment="PYTHONPATH=/opt/frigate" -ExecStartPre=/bin/mkdir -p /media/frigate/recordings /media/frigate/clips /media/frigate/snapshots -ExecStart=/opt/frigate/venv/bin/python3 -u -m frigate +Type=simple Restart=always -RestartSec=5 +RestartSec=1 User=root -StandardOutput=journal -StandardError=journal +# Environment=PLUS_API_KEY= +Environment=DEFAULT_FFMPEG_VERSION=7.0 +Environment=INCLUDED_FFMPEG_VERSIONS=5.0 +ExecStartPre=+rm /dev/shm/logs/frigate/current +ExecStart=/bin/bash -c "bash /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/frigate/run 2> >(/usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S ' >&2) | /usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S '" +StandardOutput=file:/dev/shm/logs/frigate/current +StandardError=file:/dev/shm/logs/frigate/current [Install] WantedBy=multi-user.target EOF systemctl enable -q --now frigate -msg_ok "Frigate service enabled" +sleep 3 +cat </etc/systemd/system/nginx.service +[Unit] +Description=Nginx service +After=frigate.service +After=create_directories.service +StartLimitIntervalSec=0 -msg_info "Setting environmental variables" -cat <>/root/.bashrc -export TERM='xterm-256color' -export DEFAULT_FFMPEG_VERSION='7.0' -export NVIDIA_VISIBLE_DEVICES='all' -export ENV NVIDIA_DRIVER_CAPABILITIES='compute,video,utility' -export PATH="/usr/local/go2rtc/bin:/usr/local/tempio/bin:/usr/local/nginx/sbin:${PATH}" -export TOKENIZERS_PARALLELISM=true -export TRANSFORMERS_NO_ADVISORY_WARNINGS=1 -export OPENCV_FFMPEG_LOGLEVEL=8 -export HAILORT_LOGGER_PATH=NONE -export INCLUDED_FFMPEG_VERSIONS="7.0:5.0" -export S6_LOGGING_SCRIPT="T 1 n0 s10000000 T" -export S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0 -export CCACHE_DIR=/root/.ccache -export CCACHE_MAXSIZE=2G +[Service] +Type=simple +Restart=always +RestartSec=1 +User=root +ExecStartPre=+rm /dev/shm/logs/nginx/current +ExecStart=/bin/bash -c "bash /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/run 2> >(/usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S ' >&2) | /usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S '" +StandardOutput=file:/dev/shm/logs/nginx/current +StandardError=file:/dev/shm/logs/nginx/current + +[Install] +WantedBy=multi-user.target EOF -msg_ok "Environment set" - -msg_info "Building Nginx with Custom Modules" -sed -i 's/if \[\[ "\$VERSION_ID" == "12" \]\]; then/if \[\[ "\$VERSION_ID" == "13" \]\]; then/' /opt/frigate/docker/main/build_nginx.sh -$STD bash /opt/frigate/docker/main/build_nginx.sh -sed -e '/s6-notifyoncheck/ s/^#*/#/' -i /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/run -ln -sf /usr/local/nginx/sbin/nginx /usr/local/bin/nginx -msg_ok "Built Nginx" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" +systemctl enable -q --now nginx +msg_ok "Configured Services" From 8fed150591b1bcfc19f88ddd56cc113726f59d01 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 14:51:51 +0200 Subject: [PATCH 0565/1733] Update frigate-install.sh --- install/frigate-install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install/frigate-install.sh b/install/frigate-install.sh index 08a19b76d..76f65c88b 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -38,6 +38,7 @@ fetch_and_deploy_gh_release "libusb" "libusb/libusb" "tarball" "v1.0.29" "/opt/f msg_info "Setting up Python" cd /opt/frigate +mkdir -p /opt/frigate/models $STD pip3 wheel --wheel-dir=/wheels -r /opt/frigate/docker/main/requirements-wheels.txt cp -a /opt/frigate/docker/main/rootfs/. / export TARGETARCH="amd64" From 26155629630ecf873ca7f4708c9ee756d71ab9f0 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 15:06:07 +0200 Subject: [PATCH 0566/1733] refactor: paperless --- ct/paperless-ngx.sh | 96 +++++++++++++++ install/paperless-ngx-install.sh | 199 +++++++++++++++++++++++++++++++ 2 files changed, 295 insertions(+) create mode 100644 ct/paperless-ngx.sh create mode 100644 install/paperless-ngx-install.sh diff --git a/ct/paperless-ngx.sh b/ct/paperless-ngx.sh new file mode 100644 index 000000000..d3904274b --- /dev/null +++ b/ct/paperless-ngx.sh @@ -0,0 +1,96 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://docs.paperless-ngx.com/ + +APP="Paperless-ngx" +var_tags="${var_tags:-document;management}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-10}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + if [[ ! -d /opt/paperless ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + RELEASE=$(curl -fsSL https://api.github.com/repos/paperless-ngx/paperless-ngx/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + + UPD=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "SUPPORT" --radiolist --cancel-button Exit-Script "Spacebar = Select" 11 58 2 \ + "1" "Update Paperless-ngx to $RELEASE" ON \ + "2" "Paperless-ngx Credentials" OFF \ + 3>&1 1>&2 2>&3) + header_info + check_container_storage + check_container_resources + if [ "$UPD" == "1" ]; then + if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then + if [[ "$(gs --version 2>/dev/null)" != "10.04.0" ]]; then + msg_info "Updating Ghostscript (Patience)" + cd /tmp + curl -fsSL "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs10040/ghostscript-10.04.0.tar.gz" -o $(basename "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs10040/ghostscript-10.04.0.tar.gz") + tar -xzf ghostscript-10.04.0.tar.gz + cd ghostscript-10.04.0 + $STD ./configure + $STD make + $STD sudo make install + rm -rf /tmp/ghostscript* + msg_ok "Ghostscript updated to 10.04.0" + fi + msg_info "Stopping all Paperless-ngx Services" + systemctl stop paperless-consumer paperless-webserver paperless-scheduler paperless-task-queue.service + msg_ok "Stopped all Paperless-ngx Services" + + msg_info "Updating to ${RELEASE}" + cd ~ + curl -fsSL "https://github.com/paperless-ngx/paperless-ngx/releases/download/$RELEASE/paperless-ngx-$RELEASE.tar.xz" -o $(basename "https://github.com/paperless-ngx/paperless-ngx/releases/download/$RELEASE/paperless-ngx-$RELEASE.tar.xz") + tar -xf paperless-ngx-$RELEASE.tar.xz + cp -r /opt/paperless/paperless.conf paperless-ngx/ + cp -r paperless-ngx/* /opt/paperless/ + cd /opt/paperless + $STD pip install -r requirements.txt + cd /opt/paperless/src + $STD /usr/bin/python3 manage.py migrate + echo "${RELEASE}" >/opt/${APP}_version.txt + msg_ok "Updated to ${RELEASE}" + + msg_info "Cleaning up" + cd ~ + rm paperless-ngx-$RELEASE.tar.xz + rm -rf paperless-ngx + msg_ok "Cleaned" + + msg_info "Starting all Paperless-ngx Services" + systemctl start paperless-consumer paperless-webserver paperless-scheduler paperless-task-queue.service + sleep 1 + msg_ok "Started all Paperless-ngx Services" + msg_ok "Updated Successfully!\n" + else + msg_ok "No update required. ${APP} is already at ${RELEASE}" + fi + exit + fi + if [ "$UPD" == "2" ]; then + cat paperless.creds + exit + fi +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}" diff --git a/install/paperless-ngx-install.sh b/install/paperless-ngx-install.sh new file mode 100644 index 000000000..c8e89c4a6 --- /dev/null +++ b/install/paperless-ngx-install.sh @@ -0,0 +1,199 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://docs.paperless-ngx.com/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies (Patience)" +$STD apt-get install -y \ + redis \ + build-essential \ + imagemagick \ + fonts-liberation \ + optipng \ + libpq-dev \ + libmagic-dev \ + mime-support \ + libzbar0 \ + poppler-utils \ + default-libmysqlclient-dev \ + automake \ + libtool \ + pkg-config \ + libtiff-dev \ + libpng-dev \ + libleptonica-dev +msg_ok "Installed Dependencies" + +PG_VERSION="16" setup_postgresql +PYTHON_VERSION="3.13" setup_uv +fetch_and_deploy_gh_release "paperless" "paperless-ngx/paperless-ngx" "tarball" "latest" "/opt/paperless" +fetch_and_deploy_gh_release "jbig2enc" "ie13/jbig2enc" "tarball" "latest" "/opt/jbig2enc" +setup_gs + +msg_info "Installing OCR Dependencies (Patience)" +$STD apt-get install -y \ + unpaper \ + icc-profiles-free \ + qpdf \ + liblept5 \ + libxml2 \ + pngquant \ + zlib1g \ + tesseract-ocr \ + tesseract-ocr-eng +$STD sudo make install +msg_ok "Installed OCR Dependencies" + +msg_info "Setup JBIG2" +cd /opt/jbig2enc +$STD bash ./autogen.sh +$STD bash ./configure +$STD make +$STD make install +rm -rf /opt/jbig2enc +msg_ok "Installed JBIG2" + +msg_info "Setting up PostgreSQL database" +DB_NAME=paperlessdb +DB_USER=paperless +DB_PASS="$(openssl rand -base64 18 | cut -c1-13)" +SECRET_KEY="$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 32)" +$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" +$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC'" +echo "" >>~/paperless.creds +echo -e "Paperless-ngx Database User: \e[32m$DB_USER\e[0m" >>~/paperless.creds +echo -e "Paperless-ngx Database Password: \e[32m$DB_PASS\e[0m" >>~/paperless.creds +echo -e "Paperless-ngx Database Name: \e[32m$DB_NAME\e[0m" >>~/paperless.creds + +msg_info "Installing Natural Language Toolkit (Patience)" +$STD uv run -- python -m nltk.downloader -d /usr/share/nltk_data all +sed -i -e 's/rights="none" pattern="PDF"/rights="read|write" pattern="PDF"/' /etc/ImageMagick-6/policy.xml +msg_ok "Installed Natural Language Toolkit" + +msg_info "Setup Paperless-ngx" +cd /opt/paperless +$STD uv sync --all-extras +curl -fsSL "https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/paperless.conf.example" -o /opt/paperless/paperless.conf +mkdir -p {consume,data,media,static} +sed -i \ + -e 's|#PAPERLESS_REDIS=redis://localhost:6379|PAPERLESS_REDIS=redis://localhost:6379|' \ + -e "s|#PAPERLESS_CONSUMPTION_DIR=../consume|PAPERLESS_CONSUMPTION_DIR=/opt/paperless/consume|" \ + -e "s|#PAPERLESS_DATA_DIR=../data|PAPERLESS_DATA_DIR=/opt/paperless/data|" \ + -e "s|#PAPERLESS_MEDIA_ROOT=../media|PAPERLESS_MEDIA_ROOT=/opt/paperless/media|" \ + -e "s|#PAPERLESS_STATICDIR=../static|PAPERLESS_STATICDIR=/opt/paperless/static|" \ + -e 's|#PAPERLESS_DBHOST=localhost|PAPERLESS_DBHOST=localhost|' \ + -e 's|#PAPERLESS_DBPORT=5432|PAPERLESS_DBPORT=5432|' \ + -e "s|#PAPERLESS_DBNAME=paperless|PAPERLESS_DBNAME=$DB_NAME|" \ + -e "s|#PAPERLESS_DBUSER=paperless|PAPERLESS_DBUSER=$DB_USER|" \ + -e "s|#PAPERLESS_DBPASS=paperless|PAPERLESS_DBPASS=$DB_PASS|" \ + -e "s|#PAPERLESS_SECRET_KEY=change-me|PAPERLESS_SECRET_KEY=$SECRET_KEY|" \ + /opt/paperless/paperless.conf +cd /opt/paperless/src +$STD uv run -- python manage.py migrate +msg_ok "Setup Paperless-ngx" + +msg_info "Setting up admin Paperless-ngx User & Password" +cat <>~/paperless.creds +echo -e "Paperless-ngx WebUI User: \e[32madmin\e[0m" >>~/paperless.creds +echo -e "Paperless-ngx WebUI Password: \e[32m$DB_PASS\e[0m" >>~/paperless.creds +echo "" >>~/paperless.creds +msg_ok "Set up admin Paperless-ngx User & Password" + +msg_info "Creating Services" +cat </etc/systemd/system/paperless-scheduler.service +[Unit] +Description=Paperless Celery beat +Requires=redis.service + +[Service] +WorkingDirectory=/opt/paperless/src +ExecStart=uv run -- celery --app paperless beat --loglevel INFO + +[Install] +WantedBy=multi-user.target +EOF + +cat </etc/systemd/system/paperless-task-queue.service +[Unit] +Description=Paperless Celery Workers +Requires=redis.service +After=postgresql.service + +[Service] +WorkingDirectory=/opt/paperless/src +ExecStart=uv run -- celery --app paperless worker --loglevel INFO + +[Install] +WantedBy=multi-user.target +EOF + +cat </etc/systemd/system/paperless-consumer.service +[Unit] +Description=Paperless consumer +Requires=redis.service + +[Service] +WorkingDirectory=/opt/paperless/src +ExecStartPre=/bin/sleep 2 +ExecStart=uv run -- python manage.py document_consumer + +[Install] +WantedBy=multi-user.target +EOF + +cat </etc/systemd/system/paperless-webserver.service +[Unit] +Description=Paperless webserver +After=network.target +Wants=network.target +Requires=redis.service + +[Service] +WorkingDirectory=/opt/paperless/src +ExecStart=uv run -- granian --interface asginl --ws "paperless.asgi:application" +Environment=GRANIAN_HOST=:: +Environment=GRANIAN_PORT=8000 +Environment=GRANIAN_WORKERS=1 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now paperless-webserver paperless-scheduler paperless-task-queue paperless-consumer +msg_ok "Created Services" + +read -r -p "${TAB3}Would you like to add Adminer? " prompt +if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then + setup_adminer +fi + +motd_ssh +customize + +msg_info "Cleaning up" +rm -rf /opt/paperless/docker +rm -rf /tmp/ghostscript* +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 596da1450e833103279a2687ec161cdd09c90561 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 15:08:02 +0200 Subject: [PATCH 0567/1733] Update paperless-ngx.sh --- ct/paperless-ngx.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/paperless-ngx.sh b/ct/paperless-ngx.sh index d3904274b..201bb9d1d 100644 --- a/ct/paperless-ngx.sh +++ b/ct/paperless-ngx.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE From 9077f611021600045cccdc1bcaec65c2ef43b9e9 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 15:17:05 +0200 Subject: [PATCH 0568/1733] refd --- ct/paperless-ngx.sh | 93 +++++++++++++++++--------------------- install/frigate-install.sh | 12 ++--- 2 files changed, 48 insertions(+), 57 deletions(-) diff --git a/ct/paperless-ngx.sh b/ct/paperless-ngx.sh index 201bb9d1d..0d51673d5 100644 --- a/ct/paperless-ngx.sh +++ b/ct/paperless-ngx.sh @@ -20,70 +20,61 @@ color catch_errors function update_script() { + header_info + check_container_storage + check_container_resources if [[ ! -d /opt/paperless ]]; then msg_error "No ${APP} Installation Found!" exit fi - RELEASE=$(curl -fsSL https://api.github.com/repos/paperless-ngx/paperless-ngx/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + RELEASE=$(curl -fsSL https://api.github.com/repos/paperless-ngx/paperless-ngx/releases/latest | jq -r .tag_name | sed 's/^v//') + if [[ "${RELEASE}" != "$(cat ~/.paperless 2>/dev/null)" ]] || [[ ! -f ~/.paperless ]]; then + PYTHON_VERSION="3.13" setup_uv + fetch_and_deploy_gh_release "paperless" "paperless-ngx/paperless-ngx" "tarball" "latest" "/opt/paperless" + fetch_and_deploy_gh_release "jbig2enc" "ie13/jbig2enc" "tarball" "latest" "/opt/jbig2enc" + setup_gs - UPD=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "SUPPORT" --radiolist --cancel-button Exit-Script "Spacebar = Select" 11 58 2 \ - "1" "Update Paperless-ngx to $RELEASE" ON \ - "2" "Paperless-ngx Credentials" OFF \ - 3>&1 1>&2 2>&3) - header_info - check_container_storage - check_container_resources - if [ "$UPD" == "1" ]; then - if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then - if [[ "$(gs --version 2>/dev/null)" != "10.04.0" ]]; then - msg_info "Updating Ghostscript (Patience)" - cd /tmp - curl -fsSL "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs10040/ghostscript-10.04.0.tar.gz" -o $(basename "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs10040/ghostscript-10.04.0.tar.gz") - tar -xzf ghostscript-10.04.0.tar.gz - cd ghostscript-10.04.0 - $STD ./configure - $STD make - $STD sudo make install - rm -rf /tmp/ghostscript* - msg_ok "Ghostscript updated to 10.04.0" - fi - msg_info "Stopping all Paperless-ngx Services" - systemctl stop paperless-consumer paperless-webserver paperless-scheduler paperless-task-queue.service - msg_ok "Stopped all Paperless-ngx Services" + msg_info "Stopping all Paperless-ngx Services" + systemctl stop paperless-consumer paperless-webserver paperless-scheduler paperless-task-queue.service + msg_ok "Stopped all Paperless-ngx Services" + if grep -q "uv run" /etc/systemd/system/paperless-webserver.service; then msg_info "Updating to ${RELEASE}" - cd ~ - curl -fsSL "https://github.com/paperless-ngx/paperless-ngx/releases/download/$RELEASE/paperless-ngx-$RELEASE.tar.xz" -o $(basename "https://github.com/paperless-ngx/paperless-ngx/releases/download/$RELEASE/paperless-ngx-$RELEASE.tar.xz") - tar -xf paperless-ngx-$RELEASE.tar.xz - cp -r /opt/paperless/paperless.conf paperless-ngx/ - cp -r paperless-ngx/* /opt/paperless/ cd /opt/paperless - $STD pip install -r requirements.txt + $STD uv sync --all-extras cd /opt/paperless/src - $STD /usr/bin/python3 manage.py migrate - echo "${RELEASE}" >/opt/${APP}_version.txt + $STD uv run -- python manage.py migrate msg_ok "Updated to ${RELEASE}" - - msg_info "Cleaning up" - cd ~ - rm paperless-ngx-$RELEASE.tar.xz - rm -rf paperless-ngx - msg_ok "Cleaned" - - msg_info "Starting all Paperless-ngx Services" - systemctl start paperless-consumer paperless-webserver paperless-scheduler paperless-task-queue.service - sleep 1 - msg_ok "Started all Paperless-ngx Services" - msg_ok "Updated Successfully!\n" else - msg_ok "No update required. ${APP} is already at ${RELEASE}" + msg_info "Migrating old Paperless-ngx installation to uv" + rm -rf /opt/paperless/venv + find /opt/paperless -name "__pycache__" -type d -exec rm -rf {} + + sed -i 's|ExecStart=.*manage.py document_consumer|ExecStart=uv run -- python manage.py document_consumer|' /etc/systemd/system/paperless-consumer.service + sed -i 's|ExecStart=celery|ExecStart=uv run -- celery|' /etc/systemd/system/paperless-scheduler.service + sed -i 's|ExecStart=celery|ExecStart=uv run -- celery|' /etc/systemd/system/paperless-task-queue.service + sed -i 's|ExecStart=granian|ExecStart=uv run -- granian|' /etc/systemd/system/paperless-webserver.service + $STD systemctl daemon-reexec + $STD systemctl daemon-reload + cd /opt/paperless + $STD uv sync --all-extras + cd /opt/paperless/src + $STD uv run -- python manage.py migrate + msg_ok "Paperless-ngx migration and update to ${RELEASE} completed" fi - exit - fi - if [ "$UPD" == "2" ]; then - cat paperless.creds - exit + + msg_info "Cleaning up" + cd ~ + msg_ok "Cleaned" + + msg_info "Starting all Paperless-ngx Services" + systemctl start paperless-consumer paperless-webserver paperless-scheduler paperless-task-queue.service + sleep 1 + msg_ok "Started all Paperless-ngx Services" + msg_ok "Updated Successfully!\n" + else + msg_ok "No update required. ${APP} is already at v${RELEASE}" fi + exit } start diff --git a/install/frigate-install.sh b/install/frigate-install.sh index 76f65c88b..cbd8357cf 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -185,12 +185,12 @@ chmod +x /usr/local/tempio/bin/tempio ln -sf /usr/local/tempio/bin/tempio /usr/local/bin/tempio msg_ok "Installed Tempio" -msg_info "Copying model files" -cp /opt/frigate/cpu_model.tflite / -cp /opt/frigate/edgetpu_model.tflite / -cp /opt/frigate/audio-labelmap.txt / -cp /opt/frigate/labelmap.txt / -msg_ok "Copied model files" +# msg_info "Copying model files" +# cp /opt/frigate/cpu_model.tflite / +# cp /opt/frigate/edgetpu_model.tflite / +# cp /opt/frigate/audio-labelmap.txt / +# cp /opt/frigate/labelmap.txt / +# msg_ok "Copied model files" msg_info "Building Nginx with Custom Modules" sed -i 's/if \[\[ "$VERSION_ID" == "12" \]\]; then/if [[ -f \/etc\/apt\/sources.list.d\/debian.sources ]]; then/' /opt/frigate/docker/main/build_nginx.sh From 1d42004d887c954344a18796f3e0855e9a94220f Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 18 Aug 2025 13:17:28 +0000 Subject: [PATCH 0569/1733] Update .app files --- ct/headers/ente | 6 ++++++ ct/headers/litellm | 12 ++++++------ ct/headers/paperless-ngx | 6 ++++++ 3 files changed, 18 insertions(+), 6 deletions(-) create mode 100644 ct/headers/ente create mode 100644 ct/headers/paperless-ngx diff --git a/ct/headers/ente b/ct/headers/ente new file mode 100644 index 000000000..f700a1f53 --- /dev/null +++ b/ct/headers/ente @@ -0,0 +1,6 @@ + ______ __ + / ____/___ / /____ + / __/ / __ \/ __/ _ \ + / /___/ / / / /_/ __/ +/_____/_/ /_/\__/\___/ + diff --git a/ct/headers/litellm b/ct/headers/litellm index 0b7dba63a..1360a8ff9 100644 --- a/ct/headers/litellm +++ b/ct/headers/litellm @@ -1,6 +1,6 @@ - ___ __ ____ - / (_) /____ / / /___ ___ - / / / __/ _ \/ / / __ `__ \ - / / / /_/ __/ / / / / / / / -/_/_/\__/\___/_/_/_/ /_/ /_/ - + __ _ __ __ __ __ ___ + / / (_) /____ / / / / / |/ / + / / / / __/ _ \/ / / / / /|_/ / + / /___/ / /_/ __/ /___/ /___/ / / / +/_____/_/\__/\___/_____/_____/_/ /_/ + diff --git a/ct/headers/paperless-ngx b/ct/headers/paperless-ngx new file mode 100644 index 000000000..177cf5741 --- /dev/null +++ b/ct/headers/paperless-ngx @@ -0,0 +1,6 @@ + ____ __ + / __ \____ _____ ___ _____/ /__ __________ ____ ____ __ __ + / /_/ / __ `/ __ \/ _ \/ ___/ / _ \/ ___/ ___/_____/ __ \/ __ `/ |/_/ + / ____/ /_/ / /_/ / __/ / / / __(__ |__ )_____/ / / / /_/ /> < +/_/ \__,_/ .___/\___/_/ /_/\___/____/____/ /_/ /_/\__, /_/|_| + /_/ /____/ From d9b0104f8841c8b3cdc4018da453fbbdcf8718c3 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 15:55:43 +0200 Subject: [PATCH 0570/1733] Update paperless-ngx.sh --- ct/paperless-ngx.sh | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/ct/paperless-ngx.sh b/ct/paperless-ngx.sh index 0d51673d5..4ba8a9f07 100644 --- a/ct/paperless-ngx.sh +++ b/ct/paperless-ngx.sh @@ -49,10 +49,21 @@ function update_script() { msg_info "Migrating old Paperless-ngx installation to uv" rm -rf /opt/paperless/venv find /opt/paperless -name "__pycache__" -type d -exec rm -rf {} + - sed -i 's|ExecStart=.*manage.py document_consumer|ExecStart=uv run -- python manage.py document_consumer|' /etc/systemd/system/paperless-consumer.service - sed -i 's|ExecStart=celery|ExecStart=uv run -- celery|' /etc/systemd/system/paperless-scheduler.service - sed -i 's|ExecStart=celery|ExecStart=uv run -- celery|' /etc/systemd/system/paperless-task-queue.service - sed -i 's|ExecStart=granian|ExecStart=uv run -- granian|' /etc/systemd/system/paperless-webserver.service + declare -A PATCHES=( + ["paperless-consumer.service"]="ExecStart=.*manage.py document_consumer|ExecStart=uv run -- python manage.py document_consumer" + ["paperless-scheduler.service"]="ExecStart=celery|ExecStart=uv run -- celery" + ["paperless-task-queue.service"]="ExecStart=celery|ExecStart=uv run -- celery" + ["paperless-webserver.service"]="ExecStart=.*|ExecStart=uv run -- granian --interface asginl --ws \"paperless.asgi:application\"" + ) + for svc in "${!PATCHES[@]}"; do + path=$(systemctl show -p FragmentPath "$svc" | cut -d= -f2) + if [[ -n "$path" && -f "$path" ]]; then + sed -i "s|${PATCHES[$svc]%|*}|${PATCHES[$svc]#*|}|" "$path" + msg_ok "Patched $svc" + else + msg_error "Service file for $svc not found!" + fi + done $STD systemctl daemon-reexec $STD systemctl daemon-reload cd /opt/paperless From a61c0e9cc4b6a93a76b01009fcc3d4c96623ecb7 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 15:55:43 +0200 Subject: [PATCH 0571/1733] Update paperless-ngx.sh --- ct/paperless-ngx.sh | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/ct/paperless-ngx.sh b/ct/paperless-ngx.sh index 0d51673d5..4ba8a9f07 100644 --- a/ct/paperless-ngx.sh +++ b/ct/paperless-ngx.sh @@ -49,10 +49,21 @@ function update_script() { msg_info "Migrating old Paperless-ngx installation to uv" rm -rf /opt/paperless/venv find /opt/paperless -name "__pycache__" -type d -exec rm -rf {} + - sed -i 's|ExecStart=.*manage.py document_consumer|ExecStart=uv run -- python manage.py document_consumer|' /etc/systemd/system/paperless-consumer.service - sed -i 's|ExecStart=celery|ExecStart=uv run -- celery|' /etc/systemd/system/paperless-scheduler.service - sed -i 's|ExecStart=celery|ExecStart=uv run -- celery|' /etc/systemd/system/paperless-task-queue.service - sed -i 's|ExecStart=granian|ExecStart=uv run -- granian|' /etc/systemd/system/paperless-webserver.service + declare -A PATCHES=( + ["paperless-consumer.service"]="ExecStart=.*manage.py document_consumer|ExecStart=uv run -- python manage.py document_consumer" + ["paperless-scheduler.service"]="ExecStart=celery|ExecStart=uv run -- celery" + ["paperless-task-queue.service"]="ExecStart=celery|ExecStart=uv run -- celery" + ["paperless-webserver.service"]="ExecStart=.*|ExecStart=uv run -- granian --interface asginl --ws \"paperless.asgi:application\"" + ) + for svc in "${!PATCHES[@]}"; do + path=$(systemctl show -p FragmentPath "$svc" | cut -d= -f2) + if [[ -n "$path" && -f "$path" ]]; then + sed -i "s|${PATCHES[$svc]%|*}|${PATCHES[$svc]#*|}|" "$path" + msg_ok "Patched $svc" + else + msg_error "Service file for $svc not found!" + fi + done $STD systemctl daemon-reexec $STD systemctl daemon-reload cd /opt/paperless From dd08bca358c0992b2adba9026debeaf0fa6f539c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 15:57:03 +0200 Subject: [PATCH 0572/1733] Update paperless-ngx.sh --- ct/paperless-ngx.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/paperless-ngx.sh b/ct/paperless-ngx.sh index 4ba8a9f07..e3bea2fea 100644 --- a/ct/paperless-ngx.sh +++ b/ct/paperless-ngx.sh @@ -32,7 +32,7 @@ function update_script() { PYTHON_VERSION="3.13" setup_uv fetch_and_deploy_gh_release "paperless" "paperless-ngx/paperless-ngx" "tarball" "latest" "/opt/paperless" fetch_and_deploy_gh_release "jbig2enc" "ie13/jbig2enc" "tarball" "latest" "/opt/jbig2enc" - setup_gs + #setup_gs msg_info "Stopping all Paperless-ngx Services" systemctl stop paperless-consumer paperless-webserver paperless-scheduler paperless-task-queue.service From 5eb747aafb7b1a694ee90f4e0a2cddd91dbe05cb Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 15:57:03 +0200 Subject: [PATCH 0573/1733] Update paperless-ngx.sh --- ct/paperless-ngx.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/paperless-ngx.sh b/ct/paperless-ngx.sh index 4ba8a9f07..e3bea2fea 100644 --- a/ct/paperless-ngx.sh +++ b/ct/paperless-ngx.sh @@ -32,7 +32,7 @@ function update_script() { PYTHON_VERSION="3.13" setup_uv fetch_and_deploy_gh_release "paperless" "paperless-ngx/paperless-ngx" "tarball" "latest" "/opt/paperless" fetch_and_deploy_gh_release "jbig2enc" "ie13/jbig2enc" "tarball" "latest" "/opt/jbig2enc" - setup_gs + #setup_gs msg_info "Stopping all Paperless-ngx Services" systemctl stop paperless-consumer paperless-webserver paperless-scheduler paperless-task-queue.service From a51695e3008cc5739b0fe580c82e28bf50d38019 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 16:22:51 +0200 Subject: [PATCH 0574/1733] Update paperless-ngx.sh --- ct/paperless-ngx.sh | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/ct/paperless-ngx.sh b/ct/paperless-ngx.sh index e3bea2fea..c462c67e5 100644 --- a/ct/paperless-ngx.sh +++ b/ct/paperless-ngx.sh @@ -49,12 +49,14 @@ function update_script() { msg_info "Migrating old Paperless-ngx installation to uv" rm -rf /opt/paperless/venv find /opt/paperless -name "__pycache__" -type d -exec rm -rf {} + + declare -A PATCHES=( ["paperless-consumer.service"]="ExecStart=.*manage.py document_consumer|ExecStart=uv run -- python manage.py document_consumer" ["paperless-scheduler.service"]="ExecStart=celery|ExecStart=uv run -- celery" ["paperless-task-queue.service"]="ExecStart=celery|ExecStart=uv run -- celery" - ["paperless-webserver.service"]="ExecStart=.*|ExecStart=uv run -- granian --interface asginl --ws \"paperless.asgi:application\"" + ["paperless-webserver.service"]="ExecStart=.*granian.*|ExecStart=uv run -- granian --interface asgi --host 0.0.0.0 --port 8000 --ws paperless.asgi:application" ) + for svc in "${!PATCHES[@]}"; do path=$(systemctl show -p FragmentPath "$svc" | cut -d= -f2) if [[ -n "$path" && -f "$path" ]]; then @@ -64,13 +66,18 @@ function update_script() { msg_error "Service file for $svc not found!" fi done - $STD systemctl daemon-reexec + $STD systemctl daemon-reload cd /opt/paperless $STD uv sync --all-extras cd /opt/paperless/src $STD uv run -- python manage.py migrate + msg_ok "Paperless-ngx migration and update to ${RELEASE} completed" + + # msg_info "Collecting static files (Patience)" + # $STD uv run -- python manage.py collectstatic --noinput --clear --link + # msg_ok "Collected static files" fi msg_info "Cleaning up" From b1a3b4d3cf4aa258df2a2ce81dba05de0239066e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 16:22:51 +0200 Subject: [PATCH 0575/1733] Update paperless-ngx.sh --- ct/paperless-ngx.sh | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/ct/paperless-ngx.sh b/ct/paperless-ngx.sh index e3bea2fea..c462c67e5 100644 --- a/ct/paperless-ngx.sh +++ b/ct/paperless-ngx.sh @@ -49,12 +49,14 @@ function update_script() { msg_info "Migrating old Paperless-ngx installation to uv" rm -rf /opt/paperless/venv find /opt/paperless -name "__pycache__" -type d -exec rm -rf {} + + declare -A PATCHES=( ["paperless-consumer.service"]="ExecStart=.*manage.py document_consumer|ExecStart=uv run -- python manage.py document_consumer" ["paperless-scheduler.service"]="ExecStart=celery|ExecStart=uv run -- celery" ["paperless-task-queue.service"]="ExecStart=celery|ExecStart=uv run -- celery" - ["paperless-webserver.service"]="ExecStart=.*|ExecStart=uv run -- granian --interface asginl --ws \"paperless.asgi:application\"" + ["paperless-webserver.service"]="ExecStart=.*granian.*|ExecStart=uv run -- granian --interface asgi --host 0.0.0.0 --port 8000 --ws paperless.asgi:application" ) + for svc in "${!PATCHES[@]}"; do path=$(systemctl show -p FragmentPath "$svc" | cut -d= -f2) if [[ -n "$path" && -f "$path" ]]; then @@ -64,13 +66,18 @@ function update_script() { msg_error "Service file for $svc not found!" fi done - $STD systemctl daemon-reexec + $STD systemctl daemon-reload cd /opt/paperless $STD uv sync --all-extras cd /opt/paperless/src $STD uv run -- python manage.py migrate + msg_ok "Paperless-ngx migration and update to ${RELEASE} completed" + + # msg_info "Collecting static files (Patience)" + # $STD uv run -- python manage.py collectstatic --noinput --clear --link + # msg_ok "Collected static files" fi msg_info "Cleaning up" From 70f76ddbef01854a7d0d251c70f2f9fa7aff53dd Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 16:25:43 +0200 Subject: [PATCH 0576/1733] jq --- ct/paperless-ngx.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ct/paperless-ngx.sh b/ct/paperless-ngx.sh index c462c67e5..8c8eee0e2 100644 --- a/ct/paperless-ngx.sh +++ b/ct/paperless-ngx.sh @@ -27,6 +27,9 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi + if ! command -v jq &>/dev/null; then + $STD apt-get install -y jq + fi RELEASE=$(curl -fsSL https://api.github.com/repos/paperless-ngx/paperless-ngx/releases/latest | jq -r .tag_name | sed 's/^v//') if [[ "${RELEASE}" != "$(cat ~/.paperless 2>/dev/null)" ]] || [[ ! -f ~/.paperless ]]; then PYTHON_VERSION="3.13" setup_uv From 28411398a7e34a420d27b17428799244478338e1 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 16:35:19 +0200 Subject: [PATCH 0577/1733] fixes --- ct/paperless-ngx.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ct/paperless-ngx.sh b/ct/paperless-ngx.sh index 8c8eee0e2..dbed4c25a 100644 --- a/ct/paperless-ngx.sh +++ b/ct/paperless-ngx.sh @@ -54,16 +54,16 @@ function update_script() { find /opt/paperless -name "__pycache__" -type d -exec rm -rf {} + declare -A PATCHES=( - ["paperless-consumer.service"]="ExecStart=.*manage.py document_consumer|ExecStart=uv run -- python manage.py document_consumer" - ["paperless-scheduler.service"]="ExecStart=celery|ExecStart=uv run -- celery" - ["paperless-task-queue.service"]="ExecStart=celery|ExecStart=uv run -- celery" - ["paperless-webserver.service"]="ExecStart=.*granian.*|ExecStart=uv run -- granian --interface asgi --host 0.0.0.0 --port 8000 --ws paperless.asgi:application" + ["paperless-consumer.service"]="ExecStart=uv run -- python manage.py document_consumer" + ["paperless-scheduler.service"]="ExecStart=uv run -- celery beat --loglevel INFO" + ["paperless-task-queue.service"]="ExecStart=uv run -- celery worker --loglevel INFO" + ["paperless-webserver.service"]="ExecStart=uv run -- granian --interface asgi --host 0.0.0.0 --port 8000 --ws paperless.asgi:application" ) for svc in "${!PATCHES[@]}"; do path=$(systemctl show -p FragmentPath "$svc" | cut -d= -f2) if [[ -n "$path" && -f "$path" ]]; then - sed -i "s|${PATCHES[$svc]%|*}|${PATCHES[$svc]#*|}|" "$path" + sed -i "s|^ExecStart=.*|${PATCHES[$svc]}|" "$path" msg_ok "Patched $svc" else msg_error "Service file for $svc not found!" @@ -88,7 +88,7 @@ function update_script() { msg_ok "Cleaned" msg_info "Starting all Paperless-ngx Services" - systemctl start paperless-consumer paperless-webserver paperless-scheduler paperless-task-queue.service + systemctl start paperless-consumer paperless-webserver paperless-scheduler paperless-task-queue sleep 1 msg_ok "Started all Paperless-ngx Services" msg_ok "Updated Successfully!\n" From 5c36039c90d1bbfee8cfc066ae1d97d0f0704e42 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 16:48:16 +0200 Subject: [PATCH 0578/1733] hint for rollback --- ct/paperless-ngx.sh | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/ct/paperless-ngx.sh b/ct/paperless-ngx.sh index dbed4c25a..7ef0698f0 100644 --- a/ct/paperless-ngx.sh +++ b/ct/paperless-ngx.sh @@ -35,10 +35,10 @@ function update_script() { PYTHON_VERSION="3.13" setup_uv fetch_and_deploy_gh_release "paperless" "paperless-ngx/paperless-ngx" "tarball" "latest" "/opt/paperless" fetch_and_deploy_gh_release "jbig2enc" "ie13/jbig2enc" "tarball" "latest" "/opt/jbig2enc" - #setup_gs + setup_gs msg_info "Stopping all Paperless-ngx Services" - systemctl stop paperless-consumer paperless-webserver paperless-scheduler paperless-task-queue.service + systemctl stop paperless-consumer paperless-webserver paperless-scheduler paperless-task-queue msg_ok "Stopped all Paperless-ngx Services" if grep -q "uv run" /etc/systemd/system/paperless-webserver.service; then @@ -49,6 +49,17 @@ function update_script() { $STD uv run -- python manage.py migrate msg_ok "Updated to ${RELEASE}" else + msg_warn "You are about to migrate your Paperless-ngx installation to uv!" + msg_custom "🔒" "It is strongly recommended to take a Proxmox snapshot first:" + echo -e " 1. Stop the container: pct stop " + echo -e " 2. Create a snapshot: pct snapshot pre-paperless-uv-migration" + echo -e " 3. Start the container again\n" + + read -rp "Have you created a snapshot? [y/N]: " confirm + if [[ ! "$confirm" =~ ^([yY]|[yY][eE][sS])$ ]]; then + msg_error "Migration aborted. Please create a snapshot first." + exit 1 + fi msg_info "Migrating old Paperless-ngx installation to uv" rm -rf /opt/paperless/venv find /opt/paperless -name "__pycache__" -type d -exec rm -rf {} + @@ -72,21 +83,13 @@ function update_script() { $STD systemctl daemon-reload cd /opt/paperless + msg_info "Running Paperless-ngx UV sync" $STD uv sync --all-extras cd /opt/paperless/src $STD uv run -- python manage.py migrate - msg_ok "Paperless-ngx migration and update to ${RELEASE} completed" - - # msg_info "Collecting static files (Patience)" - # $STD uv run -- python manage.py collectstatic --noinput --clear --link - # msg_ok "Collected static files" fi - msg_info "Cleaning up" - cd ~ - msg_ok "Cleaned" - msg_info "Starting all Paperless-ngx Services" systemctl start paperless-consumer paperless-webserver paperless-scheduler paperless-task-queue sleep 1 From db57a7290a6e96f16e838ac95f57854707524271 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 18 Aug 2025 16:52:08 +0200 Subject: [PATCH 0579/1733] silent setup_gs --- misc/tools.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/tools.func b/misc/tools.func index 88a4faa73..f87974264 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -1422,7 +1422,7 @@ function setup_gs() { rm -rf "$TMP_DIR" } $STD apt-get install -y build-essential libpng-dev zlib1g-dev - ./configure >/dev/null && make && sudo make install >/dev/null + $STD ./configure >/dev/null && $STD make && $STD sudo make install local EXIT_CODE=$? hash -r if [[ ! -x "$(command -v gs)" ]]; then From 4db639458d2918b95098ec2c5e44ee2d8c91a1c0 Mon Sep 17 00:00:00 2001 From: Frederik Botha Date: Mon, 21 Jul 2025 17:16:08 +0100 Subject: [PATCH 0580/1733] Dispatcharr install files --- ct/dispatcharr.sh | 120 +++++++++++ frontend/public/json/dispatcharr.json | 34 ++++ install/dispatcharr-install.sh | 275 ++++++++++++++++++++++++++ 3 files changed, 429 insertions(+) create mode 100644 ct/dispatcharr.sh create mode 100644 frontend/public/json/dispatcharr.json create mode 100644 install/dispatcharr-install.sh diff --git a/ct/dispatcharr.sh b/ct/dispatcharr.sh new file mode 100644 index 000000000..cc75af519 --- /dev/null +++ b/ct/dispatcharr.sh @@ -0,0 +1,120 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/ekke85/ProxmoxVED/refs/heads/dispatcharr/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: ekke85 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/Dispatcharr/Dispatcharr + +APP="Dispatcharr" +APP_NAME=${APP,,} +var_tags="${var_tags:-media;arr}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-1024}" +var_disk="${var_disk:-8}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -d "/opt/dispatcharr" ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + RELEASE=$(curl -fsSL https://api.github.com/repos/Dispatcharr/Dispatcharr/releases/latest | jq -r '.tag_name' | sed 's/^v//') + if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then + msg_ok "Starting update" + APP_DIR="/opt/dispatcharr" + APP_USER="dispatcharr" + APP_GROUP="dispatcharr" + + + + msg_info "Stopping $APP" + systemctl stop dispatcharr-celery + systemctl stop dispatcharr-celerybeat + systemctl stop dispatcharr-daphne + systemctl stop dispatcharr + msg_ok "Stopped $APP" + + msg_info "Creating Backup" + BACKUP_FILE="/opt/dispatcharr_$(date +%F).tar.gz" + msg_info "Source and Database backup" + set -o allexport + source /etc/$APP_NAME/$APP_NAME.env + set +o allexport + PGPASSWORD=$POSTGRES_PASSWORD pg_dump -U $POSTGRES_USER -h $POSTGRES_HOST $POSTGRES_DB > /opt/$POSTGRES_DB-`date +%F`.sql + $STD tar -czf "$BACKUP_FILE" /opt/dispatcharr /opt/Dispatcharr_version.txt /opt/$POSTGRES_DB-`date +%F`.sql &>/dev/null + msg_ok "Backup Created" + + msg_info "Updating $APP to v${RELEASE}" + rm -rf /opt/dispatcharr + fetch_and_deploy_gh_release "dispatcharr" "Dispatcharr/Dispatcharr" + chown -R "$APP_USER:$APP_GROUP" "$APP_DIR" + sed -i 's/program\[\x27channel_id\x27\]/program["channel_id"]/g' "${APP_DIR}/apps/output/views.py" + + msg_ok "Dispatcharr Updated to $RELEASE" + + msg_info "Creating Python Virtual Environment" + cd $APP_DIR + python3 -m venv env + source env/bin/activate + $STD pip install --upgrade pip + $STD pip install -r requirements.txt + $STD pip install gunicorn + ln -sf /usr/bin/ffmpeg $APP_DIR/env/bin/ffmpeg + msg_ok "Python Environment Setup" + + msg_info "Building Frontend" + cd $APP_DIR/frontend + $STD npm install --legacy-peer-deps + $STD npm run build + msg_ok "Built Frontend" + + msg_info "Running Django Migrations" + cd $APP_DIR + source env/bin/activate + set -o allexport + source /etc/$APP_NAME/$APP_NAME.env + set +o allexport + $STD python manage.py migrate --noinput + $STD python manage.py collectstatic --noinput + msg_ok "Migrations Complete" + + msg_info "Starting $APP" + systemctl start dispatcharr-celery + systemctl start dispatcharr-celerybeat + systemctl start dispatcharr-daphne + systemctl start dispatcharr + msg_ok "Started $APP" + echo "${RELEASE}" > "/opt/${APP}_version.txt" + + msg_info "Cleaning Up" + rm -rf /opt/$POSTGRES_DB-`date +%F`.sql + msg_ok "Cleanup Completed" + + msg_ok "Update Successful, Backup saved to $BACKUP_FILE" + + else + msg_ok "No update required. ${APP} is already at v${RELEASE}" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9191${CL}" diff --git a/frontend/public/json/dispatcharr.json b/frontend/public/json/dispatcharr.json new file mode 100644 index 000000000..03cd36bb5 --- /dev/null +++ b/frontend/public/json/dispatcharr.json @@ -0,0 +1,34 @@ +{ + "name": "Dispatcharr", + "slug": "dispatcharr", + "categories": [ + 14 + ], + "date_created": "2025-07-01", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 9191, + "documentation": "https://dispatcharr.github.io/Dispatcharr-Docs/", + "website": "https://dispatcharr.github.io/Dispatcharr-Docs/", + "logo": "https://raw.githubusercontent.com/Dispatcharr/Dispatcharr/refs/heads/main/frontend/src/images/logo.png", + "description": "Dispatcharr is an open-source powerhouse for managing IPTV streams and EPG data with elegance and control. Born from necessity and built with passion, it started as a personal project by OkinawaBoss and evolved with contributions from legends like dekzter, SergeantPanda and Bucatini.", + "install_methods": [ + { + "type": "default", + "script": "ct/dispatcharr.sh", + "resources": { + "cpu": 1, + "ram": 1024, + "hdd": 8, + "os": "debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] + diff --git a/install/dispatcharr-install.sh b/install/dispatcharr-install.sh new file mode 100644 index 000000000..7174b4ac7 --- /dev/null +++ b/install/dispatcharr-install.sh @@ -0,0 +1,275 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: ekke85 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/Dispatcharr/Dispatcharr + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + + +APPLICATION="Dispatcharr" +APP_NAME="dispatcharr" +APP_USER="dispatcharr" +APP_GROUP="dispatcharr" +APP_DIR="/opt/dispatcharr" +GUNICORN_RUNTIME_DIR="dispatcharr" +GUNICORN_PORT="5656" +NGINX_HTTP_PORT="9191" +WEBSOCKET_PORT="8001" + +msg_info "Creating ${APP_USER} user" +groupadd -f $APP_GROUP +useradd -M -s /usr/sbin/nologin -g $APP_GROUP $APP_USER || true +msg_ok "Created ${APP_USER} user" + +setup_uv +NODE_VERSION="22" setup_nodejs +PG_VERSION="16" setup_postgresql + +msg_info "Installing Dependencies" +$STD apt-get install -y \ + git \ + curl \ + wget \ + build-essential \ + gcc \ + libpcre3-dev \ + libpq-dev \ + python3-dev \ + python3-venv \ + python3-pip \ + nginx \ + redis-server \ + ffmpeg \ + procps \ + streamlink +msg_ok "Installed Dependencies" + +msg_info "Configuring PostgreSQL" + +POSTGRES_PASSWORD=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) + +{ + echo "POSTGRES_DB=dispatcharr" + echo "POSTGRES_USER=dispatch" + echo "POSTGRES_PASSWORD=$POSTGRES_PASSWORD" + echo "POSTGRES_HOST=localhost" +} >> ~/.$APP_NAME.creds + +source ~/.$APP_NAME.creds + +su - postgres -c "psql -tc \"SELECT 1 FROM pg_roles WHERE rolname='${POSTGRES_USER}'\"" | grep -q 1 || \ + su - postgres -c "psql -c \"CREATE USER ${POSTGRES_USER} WITH PASSWORD '${POSTGRES_PASSWORD}';\"" + +su - postgres -c "psql -tc \"SELECT 1 FROM pg_database WHERE datname='${POSTGRES_DB}'\"" | grep -q 1 || \ + su - postgres -c "psql -c \"CREATE DATABASE ${POSTGRES_DB} OWNER ${POSTGRES_USER};\"" + +su - postgres -c "psql -d ${POSTGRES_DB} -c \"ALTER SCHEMA public OWNER TO ${POSTGRES_USER};\"" + + + +msg_ok "Configured PostgreSQL" + +msg_info "Fetching latest Dispatcharr release version" +LATEST_VERSION=$(curl -fsSL https://api.github.com/repos/Dispatcharr/Dispatcharr/releases/latest | grep '"tag_name":' | cut -d '"' -f4) + +if [[ -z "$LATEST_VERSION" ]]; then + msg_error "Failed to fetch latest release version from GitHub." + exit 1 +fi + +msg_info "Downloading Dispatcharr $LATEST_VERSION" +fetch_and_deploy_gh_release "dispatcharr" "Dispatcharr/Dispatcharr" +echo "$LATEST_VERSION" > "/opt/${APPLICATION}_version.txt" +mkdir -p /data/{db,epgs,logos,m3us,recordings,uploads} +mkdir -p /etc/$APP_NAME +cp ~/.$APP_NAME.creds /etc/$APP_NAME/$APP_NAME.env +chown -R "$APP_USER:$APP_GROUP" {/etc/$APP_NAME,$APP_DIR,/data} + + +sed -i 's/program\[\x27channel_id\x27\]/program["channel_id"]/g' "${APP_DIR}/apps/output/views.py" + +msg_ok "Downloaded Dispatcharr $LATEST_VERSION" + +msg_info "Install Python Requirements" +cd $APP_DIR +python3 -m venv env +source env/bin/activate + +$STD pip install --upgrade pip +$STD pip install -r requirements.txt +$STD pip install gunicorn +ln -sf /usr/bin/ffmpeg $APP_DIR/env/bin/ffmpeg +msg_ok "Python Requirements Installed" + +msg_info "Building Frontend" +cd $APP_DIR/frontend +$STD npm install --legacy-peer-deps +$STD npm run build +msg_ok "Built Frontend" + +msg_info "Running Django Migrations" +cd $APP_DIR +source env/bin/activate +set -o allexport +source /etc/$APP_NAME/$APP_NAME.env +set +o allexport + +$STD python manage.py migrate --noinput +$STD python manage.py collectstatic --noinput +msg_ok "Migrations Complete" + +msg_info "Configuring Nginx" +cat </etc/nginx/sites-available/dispatcharr.conf +server { + listen $NGINX_HTTP_PORT; + + location / { + include proxy_params; + proxy_pass http://127.0.0.1:$GUNICORN_PORT; + } + + location /static/ { + alias $APP_DIR/static/; + } + + location /assets/ { + alias $APP_DIR/frontend/dist/assets/; + } + + location /media/ { + alias $APP_DIR/media/; + } + + location /ws/ { + proxy_pass http://127.0.0.1:$WEBSOCKET_PORT; + proxy_http_version 1.1; + proxy_set_header Upgrade \$http_upgrade; + proxy_set_header Connection "Upgrade"; + proxy_set_header Host \$host; + } +} +EOF + +ln -sf /etc/nginx/sites-available/dispatcharr.conf /etc/nginx/sites-enabled/dispatcharr.conf +rm -f /etc/nginx/sites-enabled/default +nginx -t +systemctl restart nginx +systemctl enable nginx +msg_ok "Configured Nginx" + +msg_info "Creating systemd services" + +cat </etc/systemd/system/dispatcharr.service +[Unit] +Description=Gunicorn for Dispatcharr +After=network.target postgresql.service redis-server.service + +[Service] +User=$APP_USER +Group=$APP_GROUP +WorkingDirectory=$APP_DIR +RuntimeDirectory=$GUNICORN_RUNTIME_DIR +RuntimeDirectoryMode=0775 +Environment="PATH=$APP_DIR/env/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin" +EnvironmentFile=/etc/$APP_NAME/$APP_NAME.env +ExecStart=$APP_DIR/env/bin/gunicorn \\ + --workers=4 \\ + --worker-class=gevent \\ + --timeout=300 \\ + --bind 0.0.0.0:$GUNICORN_PORT \ + dispatcharr.wsgi:application +Restart=always +KillMode=mixed + +[Install] +WantedBy=multi-user.target +EOF + +cat </etc/systemd/system/dispatcharr-celery.service +[Unit] +Description=Celery Worker for Dispatcharr +After=network.target redis-server.service +Requires=dispatcharr.service + +[Service] +User=$APP_USER +Group=$APP_GROUP +WorkingDirectory=$APP_DIR +Environment="PATH=$APP_DIR/env/bin" +EnvironmentFile=/etc/$APP_NAME/$APP_NAME.env +Environment="CELERY_BROKER_URL=redis://localhost:6379/0" +ExecStart=$APP_DIR/env/bin/celery -A dispatcharr worker -l info -c 4 +Restart=always +KillMode=mixed + +[Install] +WantedBy=multi-user.target +EOF + +cat </etc/systemd/system/dispatcharr-celerybeat.service +[Unit] +Description=Celery Beat Scheduler for Dispatcharr +After=network.target redis-server.service +Requires=dispatcharr.service + +[Service] +User=$APP_USER +Group=$APP_GROUP +WorkingDirectory=$APP_DIR +Environment="PATH=$APP_DIR/env/bin" +EnvironmentFile=/etc/$APP_NAME/$APP_NAME.env +Environment="CELERY_BROKER_URL=redis://localhost:6379/0" +ExecStart=$APP_DIR/env/bin/celery -A dispatcharr beat -l info +Restart=always +KillMode=mixed + +[Install] +WantedBy=multi-user.target +EOF + +cat </etc/systemd/system/dispatcharr-daphne.service +[Unit] +Description=Daphne for Dispatcharr (ASGI) +After=network.target +Requires=dispatcharr.service + +[Service] +User=$APP_USER +Group=$APP_GROUP +WorkingDirectory=$APP_DIR +Environment="PATH=$APP_DIR/env/bin" +EnvironmentFile=/etc/$APP_NAME/$APP_NAME.env +ExecStart=$APP_DIR/env/bin/daphne -b 0.0.0.0 -p $WEBSOCKET_PORT dispatcharr.asgi:application +Restart=always +KillMode=mixed + +[Install] +WantedBy=multi-user.target +EOF + +msg_ok "Created systemd services" + + +msg_info "Starting Dispatcharr Services" +systemctl daemon-reexec +systemctl daemon-reload +systemctl enable dispatcharr dispatcharr-celery dispatcharr-celerybeat dispatcharr-daphne +systemctl restart dispatcharr dispatcharr-celery dispatcharr-celerybeat dispatcharr-daphne +msg_ok "Started Dispatcharr Services" + + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 804dc8900e7a4bd59fb7021b1247644f756f178f Mon Sep 17 00:00:00 2001 From: Frederik Botha Date: Mon, 21 Jul 2025 18:07:01 +0100 Subject: [PATCH 0581/1733] Updated the build.func location in the ct file --- ct/dispatcharr.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/dispatcharr.sh b/ct/dispatcharr.sh index cc75af519..492858590 100644 --- a/ct/dispatcharr.sh +++ b/ct/dispatcharr.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/ekke85/ProxmoxVED/refs/heads/dispatcharr/misc/build.func) +source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: ekke85 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE From 6aac6df21b762ce34c69faac3935470fae71dc9d Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 19 Aug 2025 06:05:26 +0000 Subject: [PATCH 0582/1733] Update .app files --- ct/headers/dispatcharr | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/dispatcharr diff --git a/ct/headers/dispatcharr b/ct/headers/dispatcharr new file mode 100644 index 000000000..a8ad53965 --- /dev/null +++ b/ct/headers/dispatcharr @@ -0,0 +1,6 @@ + ____ _ __ __ + / __ \(_)________ ____ _/ /______/ /_ ____ ___________ + / / / / / ___/ __ \/ __ `/ __/ ___/ __ \/ __ `/ ___/ ___/ + / /_/ / (__ ) /_/ / /_/ / /_/ /__/ / / / /_/ / / / / +/_____/_/____/ .___/\__,_/\__/\___/_/ /_/\__,_/_/ /_/ + /_/ From bb9656b8ea6e644c9612daeebb70f085f5d27bee Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 19 Aug 2025 06:08:45 +0000 Subject: [PATCH 0583/1733] Update .app files --- ct/headers/freepbx | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/freepbx diff --git a/ct/headers/freepbx b/ct/headers/freepbx new file mode 100644 index 000000000..25541c2ed --- /dev/null +++ b/ct/headers/freepbx @@ -0,0 +1,6 @@ + ______ ____ ____ _ __ + / ____/_______ ___ / __ \/ __ ) |/ / + / /_ / ___/ _ \/ _ \/ /_/ / __ | / + / __/ / / / __/ __/ ____/ /_/ / | +/_/ /_/ \___/\___/_/ /_____/_/|_| + From be4c31bb2e489f9f66009353ca089727fc864b0b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 09:00:19 +0200 Subject: [PATCH 0584/1733] cleanup dispatcharr --- install/dispatcharr-install.sh | 165 ++++++++++++--------------------- 1 file changed, 57 insertions(+), 108 deletions(-) diff --git a/install/dispatcharr-install.sh b/install/dispatcharr-install.sh index 7174b4ac7..79546a2e4 100644 --- a/install/dispatcharr-install.sh +++ b/install/dispatcharr-install.sh @@ -13,38 +13,17 @@ setting_up_container network_check update_os - -APPLICATION="Dispatcharr" -APP_NAME="dispatcharr" -APP_USER="dispatcharr" -APP_GROUP="dispatcharr" -APP_DIR="/opt/dispatcharr" -GUNICORN_RUNTIME_DIR="dispatcharr" -GUNICORN_PORT="5656" -NGINX_HTTP_PORT="9191" -WEBSOCKET_PORT="8001" - -msg_info "Creating ${APP_USER} user" -groupadd -f $APP_GROUP -useradd -M -s /usr/sbin/nologin -g $APP_GROUP $APP_USER || true -msg_ok "Created ${APP_USER} user" - -setup_uv -NODE_VERSION="22" setup_nodejs -PG_VERSION="16" setup_postgresql +# msg_info "Creating ${APP_USER} user" +# groupadd -f $APP_GROUP +# useradd -M -s /usr/sbin/nologin -g $APP_GROUP $APP_USER || true +# msg_ok "Created ${APP_USER} user" msg_info "Installing Dependencies" $STD apt-get install -y \ - git \ - curl \ - wget \ build-essential \ gcc \ libpcre3-dev \ libpq-dev \ - python3-dev \ - python3-venv \ - python3-pip \ nginx \ redis-server \ ffmpeg \ @@ -52,74 +31,61 @@ $STD apt-get install -y \ streamlink msg_ok "Installed Dependencies" -msg_info "Configuring PostgreSQL" - -POSTGRES_PASSWORD=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) +setup_uv +NODE_VERSION="22" setup_nodejs +PG_VERSION="16" setup_postgresql +msg_info "Set up PostgreSQL Database" +DB_NAME=dispatcharr_db +DB_USER=dispatcharr_usr +DB_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" +DB_URL="postgresql://${DB_USER}:${DB_PASS}@localhost:5432/${DB_NAME}" +$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" +$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" { - echo "POSTGRES_DB=dispatcharr" - echo "POSTGRES_USER=dispatch" - echo "POSTGRES_PASSWORD=$POSTGRES_PASSWORD" - echo "POSTGRES_HOST=localhost" -} >> ~/.$APP_NAME.creds + echo "Dispatcharr-Credentials" + echo "Dispatcharr Database Name: $DB_NAME" + echo "Dispatcharr Database User: $DB_USER" + echo "Dispatcharr Database Password: $DB_PASS" +} >>~/dispatcharr.creds +msg_ok "Set up PostgreSQL Database" -source ~/.$APP_NAME.creds - -su - postgres -c "psql -tc \"SELECT 1 FROM pg_roles WHERE rolname='${POSTGRES_USER}'\"" | grep -q 1 || \ - su - postgres -c "psql -c \"CREATE USER ${POSTGRES_USER} WITH PASSWORD '${POSTGRES_PASSWORD}';\"" - -su - postgres -c "psql -tc \"SELECT 1 FROM pg_database WHERE datname='${POSTGRES_DB}'\"" | grep -q 1 || \ - su - postgres -c "psql -c \"CREATE DATABASE ${POSTGRES_DB} OWNER ${POSTGRES_USER};\"" - -su - postgres -c "psql -d ${POSTGRES_DB} -c \"ALTER SCHEMA public OWNER TO ${POSTGRES_USER};\"" - - - -msg_ok "Configured PostgreSQL" - -msg_info "Fetching latest Dispatcharr release version" -LATEST_VERSION=$(curl -fsSL https://api.github.com/repos/Dispatcharr/Dispatcharr/releases/latest | grep '"tag_name":' | cut -d '"' -f4) - -if [[ -z "$LATEST_VERSION" ]]; then - msg_error "Failed to fetch latest release version from GitHub." - exit 1 -fi - -msg_info "Downloading Dispatcharr $LATEST_VERSION" fetch_and_deploy_gh_release "dispatcharr" "Dispatcharr/Dispatcharr" -echo "$LATEST_VERSION" > "/opt/${APPLICATION}_version.txt" -mkdir -p /data/{db,epgs,logos,m3us,recordings,uploads} -mkdir -p /etc/$APP_NAME -cp ~/.$APP_NAME.creds /etc/$APP_NAME/$APP_NAME.env -chown -R "$APP_USER:$APP_GROUP" {/etc/$APP_NAME,$APP_DIR,/data} +mkdir -p /data/{db,epgs,logos,m3us,recordings,uploads} +mkdir -p /etc/dispatcharr +cp ~/.dispatcharr.creds /etc/dispatcharr/dispatcharr.env +chown -R "$APP_USER:$APP_GROUP" {/etc/dispatcharr,/opt/dispatcharr,/data} sed -i 's/program\[\x27channel_id\x27\]/program["channel_id"]/g' "${APP_DIR}/apps/output/views.py" msg_ok "Downloaded Dispatcharr $LATEST_VERSION" msg_info "Install Python Requirements" -cd $APP_DIR +cd /opt/dispatcharr python3 -m venv env source env/bin/activate $STD pip install --upgrade pip $STD pip install -r requirements.txt $STD pip install gunicorn -ln -sf /usr/bin/ffmpeg $APP_DIR/env/bin/ffmpeg +ln -sf /usr/bin/ffmpeg /opt/dispatcharr/env/bin/ffmpeg msg_ok "Python Requirements Installed" msg_info "Building Frontend" -cd $APP_DIR/frontend +cd /opt/dispatcharr/frontend $STD npm install --legacy-peer-deps $STD npm run build msg_ok "Built Frontend" msg_info "Running Django Migrations" -cd $APP_DIR +cd /opt/dispatcharr source env/bin/activate set -o allexport -source /etc/$APP_NAME/$APP_NAME.env +source /etc/dispatcharr/dispatcharr.env set +o allexport $STD python manage.py migrate --noinput @@ -129,27 +95,27 @@ msg_ok "Migrations Complete" msg_info "Configuring Nginx" cat </etc/nginx/sites-available/dispatcharr.conf server { - listen $NGINX_HTTP_PORT; + listen 9191; location / { include proxy_params; - proxy_pass http://127.0.0.1:$GUNICORN_PORT; + proxy_pass http://127.0.0.1:5656; } location /static/ { - alias $APP_DIR/static/; + alias /opt/dispatcharr/static/; } location /assets/ { - alias $APP_DIR/frontend/dist/assets/; + alias /opt/dispatcharr/frontend/dist/assets/; } location /media/ { - alias $APP_DIR/media/; + alias /opt/dispatcharr/media/; } location /ws/ { - proxy_pass http://127.0.0.1:$WEBSOCKET_PORT; + proxy_pass http://127.0.0.1:8001; proxy_http_version 1.1; proxy_set_header Upgrade \$http_upgrade; proxy_set_header Connection "Upgrade"; @@ -173,18 +139,16 @@ Description=Gunicorn for Dispatcharr After=network.target postgresql.service redis-server.service [Service] -User=$APP_USER -Group=$APP_GROUP -WorkingDirectory=$APP_DIR -RuntimeDirectory=$GUNICORN_RUNTIME_DIR +WorkingDirectory=/opt/dispatcharr +RuntimeDirectory=dispatcharr RuntimeDirectoryMode=0775 -Environment="PATH=$APP_DIR/env/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin" -EnvironmentFile=/etc/$APP_NAME/$APP_NAME.env -ExecStart=$APP_DIR/env/bin/gunicorn \\ +Environment="PATH=/opt/dispatcharr/env/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin" +EnvironmentFile=/etc/dispatcharr/dispatcharr.env +ExecStart=/opt/dispatcharr/env/bin/gunicorn \\ --workers=4 \\ --worker-class=gevent \\ --timeout=300 \\ - --bind 0.0.0.0:$GUNICORN_PORT \ + --bind 0.0.0.0:5656 \ dispatcharr.wsgi:application Restart=always KillMode=mixed @@ -200,13 +164,11 @@ After=network.target redis-server.service Requires=dispatcharr.service [Service] -User=$APP_USER -Group=$APP_GROUP -WorkingDirectory=$APP_DIR -Environment="PATH=$APP_DIR/env/bin" -EnvironmentFile=/etc/$APP_NAME/$APP_NAME.env +WorkingDirectory=/opt/dispatcharr +Environment="PATH=/opt/dispatcharr/env/bin" +EnvironmentFile=/etc/dispatcharr/dispatcharr.env Environment="CELERY_BROKER_URL=redis://localhost:6379/0" -ExecStart=$APP_DIR/env/bin/celery -A dispatcharr worker -l info -c 4 +ExecStart=/opt/dispatcharr/env/bin/celery -A dispatcharr worker -l info -c 4 Restart=always KillMode=mixed @@ -221,13 +183,11 @@ After=network.target redis-server.service Requires=dispatcharr.service [Service] -User=$APP_USER -Group=$APP_GROUP -WorkingDirectory=$APP_DIR -Environment="PATH=$APP_DIR/env/bin" -EnvironmentFile=/etc/$APP_NAME/$APP_NAME.env +WorkingDirectory=/opt/dispatcharr +Environment="PATH=/opt/dispatcharr/env/bin" +EnvironmentFile=/etc/dispatcharr/dispatcharr.env Environment="CELERY_BROKER_URL=redis://localhost:6379/0" -ExecStart=$APP_DIR/env/bin/celery -A dispatcharr beat -l info +ExecStart=/opt/dispatcharr/env/bin/celery -A dispatcharr beat -l info Restart=always KillMode=mixed @@ -242,30 +202,19 @@ After=network.target Requires=dispatcharr.service [Service] -User=$APP_USER -Group=$APP_GROUP -WorkingDirectory=$APP_DIR -Environment="PATH=$APP_DIR/env/bin" -EnvironmentFile=/etc/$APP_NAME/$APP_NAME.env -ExecStart=$APP_DIR/env/bin/daphne -b 0.0.0.0 -p $WEBSOCKET_PORT dispatcharr.asgi:application +WorkingDirectory=/opt/dispatcharr +Environment="PATH=/opt/dispatcharr/env/bin" +EnvironmentFile=/etc/dispatcharr/dispatcharr.env +ExecStart=/opt/dispatcharr/env/bin/daphne -b 0.0.0.0 -p 8001 dispatcharr.asgi:application Restart=always KillMode=mixed [Install] WantedBy=multi-user.target EOF - -msg_ok "Created systemd services" - - -msg_info "Starting Dispatcharr Services" -systemctl daemon-reexec -systemctl daemon-reload -systemctl enable dispatcharr dispatcharr-celery dispatcharr-celerybeat dispatcharr-daphne -systemctl restart dispatcharr dispatcharr-celery dispatcharr-celerybeat dispatcharr-daphne +systemctl enable -q --now dispatcharr dispatcharr-celery dispatcharr-celerybeat dispatcharr-daphne msg_ok "Started Dispatcharr Services" - motd_ssh customize From 086b24bbf10d99768b9e7b1303d6611bacf69a1c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 09:02:24 +0200 Subject: [PATCH 0585/1733] cl --- ct/twingate-connector.sh | 44 -------------- frontend/public/json/twingate-connector.json | 44 -------------- install/twingate-connector-install.sh | 63 -------------------- 3 files changed, 151 deletions(-) delete mode 100644 ct/twingate-connector.sh delete mode 100644 frontend/public/json/twingate-connector.json delete mode 100644 install/twingate-connector-install.sh diff --git a/ct/twingate-connector.sh b/ct/twingate-connector.sh deleted file mode 100644 index f1a05c9aa..000000000 --- a/ct/twingate-connector.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: twingate-andrewb -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://www.twingate.com/docs/ - -APP="twingate-connector" -var_tags="${var_tags:-network;connector;twingate}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-1024}" -var_disk="${var_disk:-3}" -var_os="${var_os:-ubuntu}" -var_version="${var_version:-24.04}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - - if [[ ! -f /lib/systemd/system/twingate-connector.service ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - msg_info "Updating ${APP}" - $STD apt update - $STD apt install -yq twingate-connector - $STD systemctl restart twingate-connector - msg_ok "Updated Successfully" - exit -} - -start -build_container -description - -msg_ok "All Finished! If you need to update your access or refresh tokens, they can be found in /etc/twingate/connector.conf" diff --git a/frontend/public/json/twingate-connector.json b/frontend/public/json/twingate-connector.json deleted file mode 100644 index e9aa195ed..000000000 --- a/frontend/public/json/twingate-connector.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "name": "twingate-connector", - "slug": "twingate-connector", - "categories": [ - 4 - ], - "date_created": "2025-07-17", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": null, - "documentation": "https://www.twingate.com/docs/", - "config_path": "/etc/twingate/connector.conf", - "website": "https://www.twingate.com", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/twingate.webp", - "description": "Twingate Connectors are lightweight software components that establish secure, least-privileged access between private network resources and authorized users without exposing the network to the internet. They act as outbound-only bridges between your protected resources and the Twingate infrastructure, ensuring zero-trust access without the need for a VPN.", - "install_methods": [ - { - "type": "default", - "script": "ct/twingate-connector.sh", - "resources": { - "cpu": 1, - "ram": 1024, - "hdd": 3, - "os": "Ubuntu", - "version": "24.04" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "You can get your Twingate access or refresh tokens from the Twingate Admin Console. `https://auth.twingate.com/signup-v2`", - "type": "info" - }, - { - "text": "If you need to update your access or refresh tokens, they can be found in /etc/twingate/connector.conf", - "type": "info" - } - ] -} diff --git a/install/twingate-connector-install.sh b/install/twingate-connector-install.sh deleted file mode 100644 index c6027854d..000000000 --- a/install/twingate-connector-install.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: MickLesk (CanbiZ), twingate-andrewb -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://www.twingate.com/docs/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -install -d -m 0700 /etc/twingate - -access_token="" -refresh_token="" -network="" - -while [[ -z "$access_token" ]]; do - read -rp "${TAB3}Please enter your access token: " access_token -done -while [[ -z "$refresh_token" ]]; do - read -rp "${TAB3}Please enter your refresh token: " refresh_token -done -while [[ -z "$network" ]]; do - read -rp "${TAB3}Please enter your network name: " network -done - -msg_info "Setup Twingate Repository" -curl -fsSL "https://packages.twingate.com/apt/gpg.key" | gpg --dearmor -o /usr/share/keyrings/twingate-connector-keyring.gpg -echo "deb [signed-by=/usr/share/keyrings/twingate-connector-keyring.gpg] https://packages.twingate.com/apt/ /" > /etc/apt/sources.list.d/twingate.list -$STD apt-get update -msg_ok "Setup Twingate Repository" - -msg_info "Setup Twingate Connector" -$STD apt-get install -y twingate-connector -msg_ok "Setup Twingate Connector" - -msg_info "Writing config" -{ - echo "TWINGATE_NETWORK=${network}" - echo "TWINGATE_ACCESS_TOKEN=${access_token}" - echo "TWINGATE_REFRESH_TOKEN=${refresh_token}" - echo "TWINGATE_LABEL_HOSTNAME=$(hostname)" - echo "TWINGATE_LABEL_DEPLOYED_BY=proxmox" -} > /etc/twingate/connector.conf -chmod 600 /etc/twingate/connector.conf -msg_ok "Config written" - -msg_info "Starting Service" -systemctl enable -q --now twingate-connector -msg_ok "Service started" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Done cleaning up" From c528bf6e8f00799cc743c8c504891daf76d6f2a3 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 09:48:42 +0200 Subject: [PATCH 0586/1733] removed --- ct/swizzin.sh | 43 ----------------- frontend/public/json/dispatcharr.json | 66 +++++++++++++-------------- frontend/public/json/swizzin.json | 48 ------------------- install/swizzin-install.sh | 34 -------------- 4 files changed, 33 insertions(+), 158 deletions(-) delete mode 100644 ct/swizzin.sh delete mode 100644 frontend/public/json/swizzin.json delete mode 100644 install/swizzin-install.sh diff --git a/ct/swizzin.sh b/ct/swizzin.sh deleted file mode 100644 index de10cb1bb..000000000 --- a/ct/swizzin.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: EEJoshua -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://swizzin.ltd/ - -APP="Swizzin" -var_tags="${var_tags:-seedbox}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-4096}" -var_disk="${var_disk:-20}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if ! command -v sudo box >/dev/null 2>&1; then - msg_error "No ${APP} installation found!\n" - exit - fi - msg_info "Running 'sudo box update' inside the container\n" - $STD sudo box update - msg_ok "Update finished\n" - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW}If installed panel, access through the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" diff --git a/frontend/public/json/dispatcharr.json b/frontend/public/json/dispatcharr.json index 03cd36bb5..b833cde65 100644 --- a/frontend/public/json/dispatcharr.json +++ b/frontend/public/json/dispatcharr.json @@ -1,34 +1,34 @@ { - "name": "Dispatcharr", - "slug": "dispatcharr", - "categories": [ - 14 - ], - "date_created": "2025-07-01", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 9191, - "documentation": "https://dispatcharr.github.io/Dispatcharr-Docs/", - "website": "https://dispatcharr.github.io/Dispatcharr-Docs/", - "logo": "https://raw.githubusercontent.com/Dispatcharr/Dispatcharr/refs/heads/main/frontend/src/images/logo.png", - "description": "Dispatcharr is an open-source powerhouse for managing IPTV streams and EPG data with elegance and control. Born from necessity and built with passion, it started as a personal project by OkinawaBoss and evolved with contributions from legends like dekzter, SergeantPanda and Bucatini.", - "install_methods": [ - { - "type": "default", - "script": "ct/dispatcharr.sh", - "resources": { - "cpu": 1, - "ram": 1024, - "hdd": 8, - "os": "debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] - + "name": "Dispatcharr", + "slug": "dispatcharr", + "categories": [ + 14 + ], + "date_created": "2025-07-01", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 9191, + "documentation": "https://dispatcharr.github.io/Dispatcharr-Docs/", + "website": "https://dispatcharr.github.io/Dispatcharr-Docs/", + "logo": "https://raw.githubusercontent.com/Dispatcharr/Dispatcharr/refs/heads/main/frontend/src/images/logo.png", + "description": "Dispatcharr is an open-source powerhouse for managing IPTV streams and EPG data with elegance and control. Born from necessity and built with passion, it started as a personal project by OkinawaBoss and evolved with contributions from legends like dekzter, SergeantPanda and Bucatini.", + "install_methods": [ + { + "type": "default", + "script": "ct/dispatcharr.sh", + "resources": { + "cpu": 1, + "ram": 1024, + "hdd": 8, + "os": "debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} diff --git a/frontend/public/json/swizzin.json b/frontend/public/json/swizzin.json deleted file mode 100644 index 2f08f31f6..000000000 --- a/frontend/public/json/swizzin.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "name": "Swizzin", - "slug": "swizzin", - "categories": [ - 15 - ], - "date_created": "2025-08-01", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 80, - "documentation": "https://swizzin.ltd/getting-started", - "config_path": "/etc/swizzin/", - "website": "https://swizzin.ltd/", - "logo": "https://swizzin.ltd/img/logo-sm.png", - "description": "Swizzin is a light-weight, modular, and user-friendly seedbox solution for Debian-based servers. It allows for the easy installation and management of a wide variety of applications commonly used for torrenting and media management, such as rTorrent, Sonarr, Radarr, and Plex, all accessible through a command-line utility or a web-based dashboard.", - "install_methods": [ - { - "type": "default", - "script": "ct/swizzin.sh", - "resources": { - "cpu": 2, - "ram": 4096, - "hdd": 20, - "os": "Debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "It is very recommended to install at least the 'panel' for web access, and 'nginx' for easy access to other apps.", - "type": "warning" - }, - { - "text": "Installation might take a long time if choosing to install many apps. Be patient.", - "type": "warning" - }, - { - "text": "Swizzin is a management suite, not a single application. Use the 'box' command inside the container to install/manage individual apps like rTorrent, Sonarr, etc. A full list can be found in documentation.", - "type": "info" - } - ] -} \ No newline at end of file diff --git a/install/swizzin-install.sh b/install/swizzin-install.sh deleted file mode 100644 index aff5c85dc..000000000 --- a/install/swizzin-install.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: EEJoshua -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://swizzin.ltd/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_warn "WARNING: This script will run an external installer from a third-party source (https://swizzin.ltd/)." -msg_warn "The following code is NOT maintained or audited by our repository." -msg_warn "If you have any doubts or concerns, please review the installer code before proceeding:" -msg_custom "${TAB3}${GATEWAY}${BGN}${CL}" "\e[1;34m" "→ https://s5n.sh" -echo -read -r -p "${TAB3}Do you want to continue? [y/N]: " CONFIRM -if [[ ! "$CONFIRM" =~ ^([yY][eE][sS]|[yY])$ ]]; then - msg_error "Aborted by user. No changes have been made." - exit 10 -fi -bash <(curl -sL s5n.sh) - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" From 3c6f697ba5a29170f54f6d0bc3a9171b880b0e3e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 10:08:55 +0200 Subject: [PATCH 0587/1733] Update dispatcharr.json --- frontend/public/json/dispatcharr.json | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/public/json/dispatcharr.json b/frontend/public/json/dispatcharr.json index b833cde65..46abd5550 100644 --- a/frontend/public/json/dispatcharr.json +++ b/frontend/public/json/dispatcharr.json @@ -12,6 +12,7 @@ "documentation": "https://dispatcharr.github.io/Dispatcharr-Docs/", "website": "https://dispatcharr.github.io/Dispatcharr-Docs/", "logo": "https://raw.githubusercontent.com/Dispatcharr/Dispatcharr/refs/heads/main/frontend/src/images/logo.png", + "config_path": "", "description": "Dispatcharr is an open-source powerhouse for managing IPTV streams and EPG data with elegance and control. Born from necessity and built with passion, it started as a personal project by OkinawaBoss and evolved with contributions from legends like dekzter, SergeantPanda and Bucatini.", "install_methods": [ { From 7a0a12bb876a8dbe0409381048e46750c2616040 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 10:45:29 +0200 Subject: [PATCH 0588/1733] fix service update paperless --- ct/paperless-ngx.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ct/paperless-ngx.sh b/ct/paperless-ngx.sh index 7ef0698f0..21d11e9a1 100644 --- a/ct/paperless-ngx.sh +++ b/ct/paperless-ngx.sh @@ -66,9 +66,9 @@ function update_script() { declare -A PATCHES=( ["paperless-consumer.service"]="ExecStart=uv run -- python manage.py document_consumer" - ["paperless-scheduler.service"]="ExecStart=uv run -- celery beat --loglevel INFO" - ["paperless-task-queue.service"]="ExecStart=uv run -- celery worker --loglevel INFO" - ["paperless-webserver.service"]="ExecStart=uv run -- granian --interface asgi --host 0.0.0.0 --port 8000 --ws paperless.asgi:application" + ["paperless-scheduler.service"]="ExecStart=uv run -- celery --app paperless beat --loglevel INFO" + ["paperless-task-queue.service"]="ExecStart=uv run -- celery --app paperless worker --loglevel INFO" + ["paperless-webserver.service"]="ExecStart=uv run -- granian --interface asgi --ws \"paperless.asgi:application\"" ) for svc in "${!PATCHES[@]}"; do From df29d2d2965087e00a2d287737f227e72eaa28b2 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 19 Aug 2025 08:45:45 +0000 Subject: [PATCH 0589/1733] Update .app files --- ct/headers/swizzin | 6 ------ ct/headers/twingate-connector | 6 ------ 2 files changed, 12 deletions(-) delete mode 100644 ct/headers/swizzin delete mode 100644 ct/headers/twingate-connector diff --git a/ct/headers/swizzin b/ct/headers/swizzin deleted file mode 100644 index 473b027ed..000000000 --- a/ct/headers/swizzin +++ /dev/null @@ -1,6 +0,0 @@ - _____ _ _ - / ___/ __(_)_______ (_)___ - \__ \ | /| / / /_ /_ / / / __ \ - ___/ / |/ |/ / / / /_/ /_/ / / / / -/____/|__/|__/_/ /___/___/_/_/ /_/ - diff --git a/ct/headers/twingate-connector b/ct/headers/twingate-connector deleted file mode 100644 index bca283700..000000000 --- a/ct/headers/twingate-connector +++ /dev/null @@ -1,6 +0,0 @@ - __ _ __ __ - / /__ __(_)___ ____ _____ _/ /____ _________ ____ ____ ___ _____/ /_____ _____ - / __/ | /| / / / __ \/ __ `/ __ `/ __/ _ \______/ ___/ __ \/ __ \/ __ \/ _ \/ ___/ __/ __ \/ ___/ -/ /_ | |/ |/ / / / / / /_/ / /_/ / /_/ __/_____/ /__/ /_/ / / / / / / / __/ /__/ /_/ /_/ / / -\__/ |__/|__/_/_/ /_/\__, /\__,_/\__/\___/ \___/\____/_/ /_/_/ /_/\___/\___/\__/\____/_/ - /____/ From 058f01471036436e10ca9e7d6966940a14571e9c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 10:47:34 +0200 Subject: [PATCH 0590/1733] Update create_lxc.sh --- misc/create_lxc.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index 03de6aaa3..ea1749d25 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -389,8 +389,9 @@ if [[ "${PCT_RAM_SIZE:-2048}" -lt 1024 ]]; then msg_warn "Configured RAM (${PCT_RAM_SIZE}MB) is below 1024MB – some apps may not work properly." fi -if [[ "${PCT_UNPRIVILEGED:-1}" == "1" && " ${PCT_OPTIONS[*]} " == *"fuse=1"* ]]; then - msg_warn "Unprivileged container with FUSE may fail unless extra device mappings are configured." -fi +# comment out 19.08.2025 - PCT Options not correct, message every new lxc (without fuse too) +# if [[ "${PCT_UNPRIVILEGED:-1}" == "1" && " ${PCT_OPTIONS[*]} " == *"fuse=1"* ]]; then +# msg_warn "Unprivileged container with FUSE may fail unless extra device mappings are configured." +# fi msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created." From 9414137350ddf8218ad2941a43bd02d29967c3b2 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 10:49:18 +0200 Subject: [PATCH 0591/1733] Update push-to-gitea.yml --- .github/workflows/push-to-gitea.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/push-to-gitea.yml b/.github/workflows/push-to-gitea.yml index 73d9a72b2..163ea0358 100644 --- a/.github/workflows/push-to-gitea.yml +++ b/.github/workflows/push-to-gitea.yml @@ -33,7 +33,7 @@ jobs: GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} - name: Push to Gitea - run: git push gitea --all + run: git push gitea main --force env: GITEA_USER: ${{ secrets.GITEA_USERNAME }} GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} From 563130ea85e552775459fa8b28bfc298059f400a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 10:51:41 +0200 Subject: [PATCH 0592/1733] Update paperless-ngx-install.sh --- install/paperless-ngx-install.sh | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/install/paperless-ngx-install.sh b/install/paperless-ngx-install.sh index c8e89c4a6..929578020 100644 --- a/install/paperless-ngx-install.sh +++ b/install/paperless-ngx-install.sh @@ -114,11 +114,15 @@ user.is_superuser = True user.is_staff = True user.save() EOF - -echo "" >>~/paperless.creds -echo -e "Paperless-ngx WebUI User: \e[32madmin\e[0m" >>~/paperless.creds -echo -e "Paperless-ngx WebUI Password: \e[32m$DB_PASS\e[0m" >>~/paperless.creds -echo "" >>~/paperless.creds +{ + echo "Paperless-ngx-Credentials" + echo "Paperless-ngx Database Name: $DB_NAME" + echo "Paperless-ngx Database User: $DB_USER" + echo "Paperless-ngx Database Password: $DB_PASS" + echo "Paperless-ngx Secret Key: $SECRET_KEY\n" + echo "Paperless-ngx WebUI User: admin" + echo "Paperless-ngx WebUI Password: $DB_PASS" +} >>~/paperless-ngx.creds msg_ok "Set up admin Paperless-ngx User & Password" msg_info "Creating Services" From a3efa4e350df5f9e5f9ce735d0e1a95c4b71ba69 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 10:54:10 +0200 Subject: [PATCH 0593/1733] Update push-to-gitea.yml --- .github/workflows/push-to-gitea.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/push-to-gitea.yml b/.github/workflows/push-to-gitea.yml index 163ea0358..c1fb72d51 100644 --- a/.github/workflows/push-to-gitea.yml +++ b/.github/workflows/push-to-gitea.yml @@ -27,7 +27,7 @@ jobs: - name: Pull Gitea changes run: | git fetch gitea - git rebase gitea/main + git merge --strategy=ours gitea/main env: GITEA_USER: ${{ secrets.GITEA_USERNAME }} GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} From fef85f7064db80c73e6a696dab843f9eedacf123 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 10:55:20 +0200 Subject: [PATCH 0594/1733] Update paperless-ngx-install.sh --- install/paperless-ngx-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/paperless-ngx-install.sh b/install/paperless-ngx-install.sh index 929578020..4ddc0fc86 100644 --- a/install/paperless-ngx-install.sh +++ b/install/paperless-ngx-install.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) +# Author: tteck (tteckster) | MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://docs.paperless-ngx.com/ From 507e732ca9dcc4e3bb770b9f7fdca2a45ad25730 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 10:57:57 +0200 Subject: [PATCH 0595/1733] Update dispatcharr-install.sh --- install/dispatcharr-install.sh | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/install/dispatcharr-install.sh b/install/dispatcharr-install.sh index 79546a2e4..29e9457c7 100644 --- a/install/dispatcharr-install.sh +++ b/install/dispatcharr-install.sh @@ -55,16 +55,12 @@ msg_ok "Set up PostgreSQL Database" fetch_and_deploy_gh_release "dispatcharr" "Dispatcharr/Dispatcharr" -mkdir -p /data/{db,epgs,logos,m3us,recordings,uploads} -mkdir -p /etc/dispatcharr -cp ~/.dispatcharr.creds /etc/dispatcharr/dispatcharr.env -chown -R "$APP_USER:$APP_GROUP" {/etc/dispatcharr,/opt/dispatcharr,/data} - -sed -i 's/program\[\x27channel_id\x27\]/program["channel_id"]/g' "${APP_DIR}/apps/output/views.py" - -msg_ok "Downloaded Dispatcharr $LATEST_VERSION" +#chown -R "$APP_USER:$APP_GROUP" {/etc/dispatcharr,/opt/dispatcharr,/data} msg_info "Install Python Requirements" +mkdir -p /data/{db,epgs,logos,m3us,recordings,uploads} +mkdir -p /etc/dispatcharr +sed -i 's/program\[\x27channel_id\x27\]/program["channel_id"]/g' "/opt/dispatcharr/apps/output/views.py" cd /opt/dispatcharr python3 -m venv env source env/bin/activate From b342474da89fa7bf89faf443e25ac6835ff87cac Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 11:14:37 +0200 Subject: [PATCH 0596/1733] patch granian host --- ct/paperless-ngx.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ct/paperless-ngx.sh b/ct/paperless-ngx.sh index 21d11e9a1..997e7b16e 100644 --- a/ct/paperless-ngx.sh +++ b/ct/paperless-ngx.sh @@ -75,6 +75,11 @@ function update_script() { path=$(systemctl show -p FragmentPath "$svc" | cut -d= -f2) if [[ -n "$path" && -f "$path" ]]; then sed -i "s|^ExecStart=.*|${PATCHES[$svc]}|" "$path" + if [[ "$svc" == "paperless-webserver.service" ]]; then + grep -q "^Environment=GRANIAN_HOST=" "$path" || echo 'Environment=GRANIAN_HOST=::' >>"$path" + grep -q "^Environment=GRANIAN_PORT=" "$path" || echo 'Environment=GRANIAN_PORT=8000' >>"$path" + grep -q "^Environment=GRANIAN_WORKERS=" "$path" || echo 'Environment=GRANIAN_WORKERS=1' >>"$path" + fi msg_ok "Patched $svc" else msg_error "Service file for $svc not found!" From 3fd51d7a40248e5889f571c9ad79475348b27d1b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 11:32:44 +0200 Subject: [PATCH 0597/1733] fetch local templates --- misc/create_lxc.sh | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index ea1749d25..72e0583ad 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -258,34 +258,38 @@ fi # Update LXC template list TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" -msg_info "Updating LXC Template List" -if ! pveam update >/dev/null 2>&1; then - TEMPLATE_FALLBACK=$(pveam list "$TEMPLATE_STORAGE" | awk "/$TEMPLATE_SEARCH/ {print \$2}" | sort -t - -k 2 -V | tail -n1) - if [[ -z "$TEMPLATE_FALLBACK" ]]; then - msg_error "Failed to update LXC template list and no local template matching '$TEMPLATE_SEARCH' found." - exit 201 - fi - msg_info "Skipping template update – using local fallback: $TEMPLATE_FALLBACK" -else - msg_ok "LXC Template List Updated" -fi - -# Get LXC template string -TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" -mapfile -t TEMPLATES < <(pveam available -section system | sed -n "s/.*\($TEMPLATE_SEARCH.*\)/\1/p" | sort -t - -k 2 -V) +msg_info "Searching for online LXC template for '$TEMPLATE_SEARCH'..." +mapfile -t TEMPLATES < <( + pveam update >/dev/null 2>&1 && + pveam available -section system | sed -n "s/.*\($TEMPLATE_SEARCH.*\)/\1/p" | sort -t - -k 2 -V +) +# If nothing found online, search local templates if [ ${#TEMPLATES[@]} -eq 0 ]; then - msg_error "No matching LXC template found for '${TEMPLATE_SEARCH}'. Make sure your host can reach the Proxmox template repository." - exit 207 + msg_info "Online search failed or no template found. Checking for local fallbacks..." + mapfile -t TEMPLATES < <( + pveam list "$TEMPLATE_STORAGE" | awk "/$TEMPLATE_SEARCH/ {print \$2}" | sort -t - -k 2 -V + ) + + if [ ${#TEMPLATES[@]} -eq 0 ]; then + msg_error "No online or local LXC template found for '${TEMPLATE_SEARCH}'. Please check network or install a template manually." + exit 207 + fi + msg_ok "Found local fallback template." +else + msg_ok "Found online template." fi +# Use the newest available template (last in sorted list) TEMPLATE="${TEMPLATES[-1]}" TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || echo "/var/lib/vz/template/cache/$TEMPLATE")" + msg_debug "TEMPLATE_SEARCH=$TEMPLATE_SEARCH" msg_debug "TEMPLATES=(${TEMPLATES[*]})" msg_debug "Selected TEMPLATE=$TEMPLATE" msg_debug "TEMPLATE_PATH=$TEMPLATE_PATH" +# Validate that template file exists and is not corrupted TEMPLATE_VALID=1 if ! pveam list "$TEMPLATE_STORAGE" | grep -q "$TEMPLATE"; then TEMPLATE_VALID=0 From b3a6a0daefeaf02f6be8a6879e681e2c1cd83166 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 11:33:19 +0200 Subject: [PATCH 0598/1733] Update debian.sh --- ct/debian.sh | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/ct/debian.sh b/ct/debian.sh index 4bcf91d6c..b7efad6c3 100644 --- a/ct/debian.sh +++ b/ct/debian.sh @@ -11,7 +11,7 @@ var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" -var_version="${var_version:-12}" +var_version="${var_version:-11}" var_unprivileged="${var_unprivileged:-1}" var_fuse="${var_fuse:-no}" var_tun="${var_tun:-no}" @@ -22,18 +22,18 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /var ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - msg_info "Updating $APP LXC" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "Updated $APP LXC" + header_info + check_container_storage + check_container_resources + if [[ ! -d /var ]]; then + msg_error "No ${APP} Installation Found!" exit + fi + msg_info "Updating $APP LXC" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Updated $APP LXC" + exit } start From cd1e08b669812965be9d3d52dafa1c49b08beb05 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 11:35:08 +0200 Subject: [PATCH 0599/1733] Update create_lxc.sh --- misc/create_lxc.sh | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index 72e0583ad..f54016af1 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -264,11 +264,11 @@ mapfile -t TEMPLATES < <( pveam available -section system | sed -n "s/.*\($TEMPLATE_SEARCH.*\)/\1/p" | sort -t - -k 2 -V ) -# If nothing found online, search local templates +# If nothing found online, search local templates (file names only) if [ ${#TEMPLATES[@]} -eq 0 ]; then msg_info "Online search failed or no template found. Checking for local fallbacks..." mapfile -t TEMPLATES < <( - pveam list "$TEMPLATE_STORAGE" | awk "/$TEMPLATE_SEARCH/ {print \$2}" | sort -t - -k 2 -V + pveam list "$TEMPLATE_STORAGE" | awk -v s="$TEMPLATE_SEARCH" '$2 ~ s {print $2}' | sort -t - -k 2 -V ) if [ ${#TEMPLATES[@]} -eq 0 ]; then @@ -289,17 +289,15 @@ msg_debug "TEMPLATES=(${TEMPLATES[*]})" msg_debug "Selected TEMPLATE=$TEMPLATE" msg_debug "TEMPLATE_PATH=$TEMPLATE_PATH" -# Validate that template file exists and is not corrupted +# Validation: only run download logic if template came from online list TEMPLATE_VALID=1 -if ! pveam list "$TEMPLATE_STORAGE" | grep -q "$TEMPLATE"; then - TEMPLATE_VALID=0 -elif [ ! -s "$TEMPLATE_PATH" ]; then +if [ ! -s "$TEMPLATE_PATH" ]; then TEMPLATE_VALID=0 elif ! tar --use-compress-program=zstdcat -tf "$TEMPLATE_PATH" >/dev/null 2>&1; then TEMPLATE_VALID=0 fi -if [ "$TEMPLATE_VALID" -eq 0 ]; then +if [ "$TEMPLATE_VALID" -eq 0 ] && [[ " ${TEMPLATES[*]} " =~ ".tar." ]]; then msg_warn "Template $TEMPLATE not found or appears to be corrupted. Re-downloading." [[ -f "$TEMPLATE_PATH" ]] && rm -f "$TEMPLATE_PATH" for attempt in {1..3}; do From 454d95bd39e465417f8d1ad8e7e38d85e762bddb Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 11:37:52 +0200 Subject: [PATCH 0600/1733] Update create_lxc.sh --- misc/create_lxc.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index f54016af1..e171edb58 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -265,10 +265,11 @@ mapfile -t TEMPLATES < <( ) # If nothing found online, search local templates (file names only) +# If nothing found online, search local templates (extract filename from volume ID) if [ ${#TEMPLATES[@]} -eq 0 ]; then msg_info "Online search failed or no template found. Checking for local fallbacks..." mapfile -t TEMPLATES < <( - pveam list "$TEMPLATE_STORAGE" | awk -v s="$TEMPLATE_SEARCH" '$2 ~ s {print $2}' | sort -t - -k 2 -V + pveam list "$TEMPLATE_STORAGE" | awk -v s="$TEMPLATE_SEARCH" '$1 ~ s {print $1}' | sed 's/.*\///' | sort -t - -k 2 -V ) if [ ${#TEMPLATES[@]} -eq 0 ]; then From f37ff17081d116dd77f3cf6f8032ff9d835689f6 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 11:37:57 +0200 Subject: [PATCH 0601/1733] Update create_lxc.sh --- misc/create_lxc.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index e171edb58..be70a4874 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -264,7 +264,6 @@ mapfile -t TEMPLATES < <( pveam available -section system | sed -n "s/.*\($TEMPLATE_SEARCH.*\)/\1/p" | sort -t - -k 2 -V ) -# If nothing found online, search local templates (file names only) # If nothing found online, search local templates (extract filename from volume ID) if [ ${#TEMPLATES[@]} -eq 0 ]; then msg_info "Online search failed or no template found. Checking for local fallbacks..." From 2564c1c0df057b6c80873a48ddd048bd83ca0393 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 11:39:30 +0200 Subject: [PATCH 0602/1733] Update create_lxc.sh --- misc/create_lxc.sh | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index be70a4874..3b51e5411 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -258,29 +258,30 @@ fi # Update LXC template list TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" -msg_info "Searching for online LXC template for '$TEMPLATE_SEARCH'..." +# 1. Check local templates first +msg_info "Checking for local template for '$TEMPLATE_SEARCH'..." mapfile -t TEMPLATES < <( - pveam update >/dev/null 2>&1 && - pveam available -section system | sed -n "s/.*\($TEMPLATE_SEARCH.*\)/\1/p" | sort -t - -k 2 -V + pveam list "$TEMPLATE_STORAGE" | awk -v s="$TEMPLATE_SEARCH" '$1 ~ s {print $1}' | sed 's/.*\///' | sort -t - -k 2 -V ) -# If nothing found online, search local templates (extract filename from volume ID) -if [ ${#TEMPLATES[@]} -eq 0 ]; then - msg_info "Online search failed or no template found. Checking for local fallbacks..." +if [ ${#TEMPLATES[@]} -gt 0 ]; then + msg_ok "Found local template." +else + # 2. If no local match, try online + msg_info "No local template found. Searching online..." mapfile -t TEMPLATES < <( - pveam list "$TEMPLATE_STORAGE" | awk -v s="$TEMPLATE_SEARCH" '$1 ~ s {print $1}' | sed 's/.*\///' | sort -t - -k 2 -V + pveam update >/dev/null 2>&1 && + pveam available -section system | sed -n "s/.*\($TEMPLATE_SEARCH.*\)/\1/p" | sort -t - -k 2 -V ) if [ ${#TEMPLATES[@]} -eq 0 ]; then msg_error "No online or local LXC template found for '${TEMPLATE_SEARCH}'. Please check network or install a template manually." exit 207 fi - msg_ok "Found local fallback template." -else msg_ok "Found online template." fi -# Use the newest available template (last in sorted list) +# 3. Use the newest template (last in sorted list) TEMPLATE="${TEMPLATES[-1]}" TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || echo "/var/lib/vz/template/cache/$TEMPLATE")" @@ -289,7 +290,7 @@ msg_debug "TEMPLATES=(${TEMPLATES[*]})" msg_debug "Selected TEMPLATE=$TEMPLATE" msg_debug "TEMPLATE_PATH=$TEMPLATE_PATH" -# Validation: only run download logic if template came from online list +# 4. Validate template (exists & not corrupted) TEMPLATE_VALID=1 if [ ! -s "$TEMPLATE_PATH" ]; then TEMPLATE_VALID=0 @@ -297,8 +298,8 @@ elif ! tar --use-compress-program=zstdcat -tf "$TEMPLATE_PATH" >/dev/null 2>&1; TEMPLATE_VALID=0 fi -if [ "$TEMPLATE_VALID" -eq 0 ] && [[ " ${TEMPLATES[*]} " =~ ".tar." ]]; then - msg_warn "Template $TEMPLATE not found or appears to be corrupted. Re-downloading." +if [ "$TEMPLATE_VALID" -eq 0 ]; then + msg_warn "Template $TEMPLATE is missing or corrupted. Re-downloading." [[ -f "$TEMPLATE_PATH" ]] && rm -f "$TEMPLATE_PATH" for attempt in {1..3}; do msg_info "Attempt $attempt: Downloading LXC template..." From dc13b2e2855b7b04961c1cb68323a3fad1797cc7 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 11:40:06 +0200 Subject: [PATCH 0603/1733] Update create_lxc.sh --- misc/create_lxc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index 3b51e5411..98cc70161 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -6,7 +6,7 @@ # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # This sets verbose mode if the global variable is set to "yes" -# if [ "$VERBOSE" == "yes" ]; then set -x; fi +if [ "$CREATE_LXC_VERBOSE" == "yes" ]; then set -x; fi if command -v curl >/dev/null 2>&1; then source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) From 08ab05e07ebc5b47e62aebdbbc3cafb055b273ff Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 11:45:52 +0200 Subject: [PATCH 0604/1733] - --- vm/debian-13-vm.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/vm/debian-13-vm.sh b/vm/debian-13-vm.sh index eeb0d1068..b222ff90a 100644 --- a/vm/debian-13-vm.sh +++ b/vm/debian-13-vm.sh @@ -11,9 +11,9 @@ function header_info { cat <<"EOF" ____ __ _ ________ / __ \___ / /_ (_)___ _____ < /__ / - / / / / _ \/ __ \/ / __ `/ __ \ / / /_ < - / /_/ / __/ /_/ / / /_/ / / / / / /___/ / -/_____/\___/_.___/_/\__,_/_/ /_/ /_//____/ + / / / / _ \/ __ \/ / __ `/ __ \ / / /_ < + / /_/ / __/ /_/ / / /_/ / / / / / /___/ / +/_____/\___/_.___/_/\__,_/_/ /_/ /_//____/ (Trixie) EOF } @@ -474,9 +474,9 @@ msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." msg_info "Retrieving the URL for the Debian 13 Qcow2 Disk Image" if [ "$CLOUD_INIT" == "yes" ]; then - URL=https://cloud.debian.org/images/cloud/trixie/daily/latest/debian-13-genericcloud-amd64-daily.qcow2 + URL=https://cloud.debian.org/images/cloud/trixie/latest/debian-13-genericcloud-amd64.qcow2 else - URL=https://cloud.debian.org/images/cloud/trixie/daily/latest/debian-13-nocloud-amd64-daily.qcow2 + URL=https://cloud.debian.org/images/cloud/trixie/latest/debian-13-nocloud-amd64.qcow2 fi sleep 2 msg_ok "${CL}${BL}${URL}${CL}" @@ -540,7 +540,7 @@ DESCRIPTION=$( spend Coffee

- + GitHub From 18a88c362d1914c4343cd38733f7a8c089863b0c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 11:46:33 +0200 Subject: [PATCH 0605/1733] Update debian-13-vm.sh --- vm/debian-13-vm.sh | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/vm/debian-13-vm.sh b/vm/debian-13-vm.sh index b222ff90a..05aa50bcd 100644 --- a/vm/debian-13-vm.sh +++ b/vm/debian-13-vm.sh @@ -138,36 +138,37 @@ function check_root() { fi } +# This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported. +# Supported: Proxmox VE 8.0.x – 8.9.x and 9.0 (NOT 9.1+) pve_check() { local PVE_VER PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" - # Check for Proxmox VE 8.x + # Check for Proxmox VE 8.x: allow 8.0–8.9 if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" - if ((MINOR < 1 || MINOR > 4)); then + if ((MINOR < 0 || MINOR > 9)); then msg_error "This version of Proxmox VE is not supported." - echo -e "Required: Proxmox VE version 8.1 – 8.4" + msg_error "Supported: Proxmox VE version 8.0 – 8.9" exit 1 fi return 0 fi - # Check for Proxmox VE 9.x (Beta) — require confirmation + # Check for Proxmox VE 9.x: allow ONLY 9.0 if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then - if whiptail --title "Proxmox 9.x Detected (Beta)" \ - --yesno "You are using Proxmox VE $PVE_VER, which is currently in Beta state.\n\nThis version is experimentally supported.\n\nDo you want to proceed anyway?" 12 70; then - msg_ok "Confirmed: Continuing with Proxmox VE $PVE_VER" - return 0 - else - msg_error "Aborted by user: Proxmox VE 9.x was not confirmed." + local MINOR="${BASH_REMATCH[1]}" + if ((MINOR != 0)); then + msg_error "This version of Proxmox VE is not yet supported." + msg_error "Supported: Proxmox VE version 9.0" exit 1 fi + return 0 fi # All other unsupported versions msg_error "This version of Proxmox VE is not supported." - echo -e "Supported versions: Proxmox VE 8.1 – 8.4 or 9.x (Beta, with confirmation)" + msg_error "Supported versions: Proxmox VE 8.0 – 8.x or 9.0" exit 1 } From b421208a02a8706dda9b2485a625a8a8098d291f Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 12:37:16 +0200 Subject: [PATCH 0606/1733] glances --- tools/addon/glances.sh | 125 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 tools/addon/glances.sh diff --git a/tools/addon/glances.sh b/tools/addon/glances.sh new file mode 100644 index 000000000..90012a9df --- /dev/null +++ b/tools/addon/glances.sh @@ -0,0 +1,125 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# License: MIT +# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE + +function header_info { + clear + cat <<"EOF" + ________ + / ____/ /___ _____ ________ _____ + / / __/ / __ `/ __ \/ ___/ _ \/ ___/ +/ /_/ / / /_/ / / / / /__/ __(__ ) +\____/_/\__,_/_/ /_/\___/\___/____/ + +EOF +} +IP=$(hostname -I | awk '{print $1}') +YW=$(echo "\033[33m") +BL=$(echo "\033[36m") +RD=$(echo "\033[01;31m") +BGN=$(echo "\033[4;92m") +GN=$(echo "\033[1;92m") +DGN=$(echo "\033[32m") +CL=$(echo "\033[m") +BFR="\\r\\033[K" +HOLD=" " +CM="${GN}✓${CL}" +APP="Glances" +hostname="$(hostname)" +silent() { "$@" >/dev/null 2>&1; } +set -e +spinner() { + local chars="/-\|" + local spin_i=0 + printf "\e[?25l" + while true; do + printf "\r \e[36m%s\e[0m" "${chars:spin_i++%${#chars}:1}" + sleep 0.1 + done +} + +msg_info() { + local msg="$1" + echo -ne " ${HOLD} ${YW}${msg} " + spinner & + SPINNER_PID=$! +} + +msg_ok() { + if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi + printf "\e[?25h" + local msg="$1" + echo -e "${BFR} ${CM} ${GN}${msg}${CL}" +} + +install() { + header_info + while true; do + read -p "This will Install ${APP} on $hostname. Proceed(y/n)?" yn + case $yn in + [Yy]*) break ;; + [Nn]*) exit ;; + *) echo "Please answer yes or no." ;; + esac + done + header_info + read -r -p "Verbose mode? " prompt + if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then + STD="" + else + STD="silent" + fi + msg_info "Installing $APP" + rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED + $STD bash -c "$(curl -fsSL https://raw.githubusercontent.com/nicolargo/glancesautoinstall/master/install.sh)" + cat </etc/systemd/system/glances.service +[Unit] +Description=Glances - An eye on your system +After=network.target + +[Service] +Type=simple +ExecStart=/usr/local/bin/glances -w +Restart=on-failure + +[Install] +WantedBy=multi-user.target +EOF + systemctl enable -q --now glances.service + msg_ok "Installed $APP on $hostname" + + echo -e "${APP} should be reachable by going to the following URL. + ${BL}http://$IP:61208${CL} \n" +} +uninstall() { + header_info + msg_info "Uninstalling $APP" + if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi + systemctl disable -q --now glances + bash -c "$(curl -fsSL https://raw.githubusercontent.com/nicolargo/glancesautoinstall/master/uninstall.sh)" + rm -rf /etc/systemd/system/glances.service + msg_ok "Uninstalled $APP" + msg_ok "Completed Successfully!\n" +} + +OPTIONS=(Install "Install $APP" + Uninstall "Uninstall $APP") + +CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "$APP" --menu "Select an option:" 10 58 2 \ + "${OPTIONS[@]}" 3>&1 1>&2 2>&3) + +case $CHOICE in +"Install") + install + ;; +"Uninstall") + uninstall + ;; +*) + echo "Exiting..." + exit 0 + ;; +esac From 28441cbcd58bff1460bb4f5052dc4ec6650128ba Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 12:42:00 +0200 Subject: [PATCH 0607/1733] refactor: glances --- tools/addon/glances.sh | 195 ++++++++++++++++++++++------------------- 1 file changed, 105 insertions(+), 90 deletions(-) diff --git a/tools/addon/glances.sh b/tools/addon/glances.sh index 90012a9df..9ae6b3cc5 100644 --- a/tools/addon/glances.sh +++ b/tools/addon/glances.sh @@ -5,76 +5,39 @@ # License: MIT # https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -function header_info { - clear - cat <<"EOF" - ________ - / ____/ /___ _____ ________ _____ - / / __/ / __ `/ __ \/ ___/ _ \/ ___/ -/ /_/ / / /_/ / / / / /__/ __(__ ) -\____/_/\__,_/_/ /_/\___/\___/____/ +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/core.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/tools.func) -EOF -} -IP=$(hostname -I | awk '{print $1}') -YW=$(echo "\033[33m") -BL=$(echo "\033[36m") -RD=$(echo "\033[01;31m") -BGN=$(echo "\033[4;92m") -GN=$(echo "\033[1;92m") -DGN=$(echo "\033[32m") -CL=$(echo "\033[m") -BFR="\\r\\033[K" -HOLD=" " -CM="${GN}✓${CL}" APP="Glances" +IP=$(hostname -I | awk '{print $1}') hostname="$(hostname)" -silent() { "$@" >/dev/null 2>&1; } -set -e -spinner() { - local chars="/-\|" - local spin_i=0 - printf "\e[?25l" - while true; do - printf "\r \e[36m%s\e[0m" "${chars:spin_i++%${#chars}:1}" - sleep 0.1 - done -} -msg_info() { - local msg="$1" - echo -ne " ${HOLD} ${YW}${msg} " - spinner & - SPINNER_PID=$! -} +header_info "$APP" +catch_errors -msg_ok() { - if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi - printf "\e[?25h" - local msg="$1" - echo -e "${BFR} ${CM} ${GN}${msg}${CL}" -} +# install on Debian/Ubuntu +install_glances_debian() { + msg_info "Installing dependencies" + $STD apt-get update + $STD apt-get install -y gcc lm-sensors wireless-tools + msg_ok "Installed dependencies" -install() { - header_info - while true; do - read -p "This will Install ${APP} on $hostname. Proceed(y/n)?" yn - case $yn in - [Yy]*) break ;; - [Nn]*) exit ;; - *) echo "Please answer yes or no." ;; - esac - done - header_info - read -r -p "Verbose mode? " prompt - if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then - STD="" - else - STD="silent" - fi - msg_info "Installing $APP" - rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED - $STD bash -c "$(curl -fsSL https://raw.githubusercontent.com/nicolargo/glancesautoinstall/master/install.sh)" + msg_info "Setting up Python + uv" + setup_uv PYTHON_VERSION="3.12" + msg_ok "Setup Python + uv" + + msg_info "Installing $APP (with web UI)" + cd /opt + mkdir -p glances + cd glances + uv venv + source .venv/bin/activate + uv pip install --upgrade pip wheel setuptools + uv pip install "glances[web]" + deactivate + msg_ok "Installed $APP" + + msg_info "Creating systemd service" cat </etc/systemd/system/glances.service [Unit] Description=Glances - An eye on your system @@ -82,44 +45,96 @@ After=network.target [Service] Type=simple -ExecStart=/usr/local/bin/glances -w +ExecStart=/opt/glances/.venv/bin/glances -w Restart=on-failure +WorkingDirectory=/opt/glances [Install] WantedBy=multi-user.target EOF - systemctl enable -q --now glances.service - msg_ok "Installed $APP on $hostname" + systemctl enable -q --now glances + msg_ok "Created systemd service" - echo -e "${APP} should be reachable by going to the following URL. - ${BL}http://$IP:61208${CL} \n" + echo -e "\n$APP is now running at: http://$IP:61208\n" } -uninstall() { - header_info + +# uninstall on Debian/Ubuntu +uninstall_glances_debian() { msg_info "Uninstalling $APP" - if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi - systemctl disable -q --now glances - bash -c "$(curl -fsSL https://raw.githubusercontent.com/nicolargo/glancesautoinstall/master/uninstall.sh)" - rm -rf /etc/systemd/system/glances.service - msg_ok "Uninstalled $APP" - msg_ok "Completed Successfully!\n" + systemctl disable -q --now glances || true + rm -f /etc/systemd/system/glances.service + rm -rf /opt/glances + msg_ok "Removed $APP" } +# install on Alpine +install_glances_alpine() { + msg_info "Installing dependencies" + $STD apk update + $STD apk add --no-cache gcc musl-dev python3 py3-pip py3-virtualenv lm-sensors wireless-tools + msg_ok "Installed dependencies" + + msg_info "Setting up Python + uv" + setup_uv PYTHON_VERSION="3.12" + msg_ok "Setup Python + uv" + + msg_info "Installing $APP (with web UI)" + cd /opt + mkdir -p glances + cd glances + uv venv + source .venv/bin/activate + uv pip install --upgrade pip wheel setuptools + uv pip install "glances[web]" + deactivate + msg_ok "Installed $APP" + + msg_info "Creating OpenRC service" + cat <<'EOF' >/etc/init.d/glances +#!/sbin/openrc-run +command="/opt/glances/.venv/bin/glances" +command_args="-w" +command_background="yes" +pidfile="/run/glances.pid" +name="glances" +description="Glances monitoring tool" +EOF + chmod +x /etc/init.d/glances + rc-update add glances default + rc-service glances start + msg_ok "Created OpenRC service" + + echo -e "\n$APP is now running at: http://$IP:61208\n" +} + +# uninstall on Alpine +uninstall_glances_alpine() { + msg_info "Uninstalling $APP" + rc-service glances stop || true + rc-update del glances || true + rm -f /etc/init.d/glances + rm -rf /opt/glances + msg_ok "Removed $APP" +} + +# options menu OPTIONS=(Install "Install $APP" Uninstall "Uninstall $APP") CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "$APP" --menu "Select an option:" 10 58 2 \ - "${OPTIONS[@]}" 3>&1 1>&2 2>&3) + "${OPTIONS[@]}" 3>&1 1>&2 2>&3 || true) -case $CHOICE in -"Install") - install - ;; -"Uninstall") - uninstall - ;; -*) - echo "Exiting..." - exit 0 - ;; -esac +# OS detection +if grep -qi "alpine" /etc/os-release; then + case "$CHOICE" in + Install) install_glances_alpine ;; + Uninstall) uninstall_glances_alpine ;; + *) exit 0 ;; + esac +else + case "$CHOICE" in + Install) install_glances_debian ;; + Uninstall) uninstall_glances_debian ;; + *) exit 0 ;; + esac +fi From e1937486577e91c8899be32a67d1164014ed37f5 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 12:44:16 +0200 Subject: [PATCH 0608/1733] Update glances.sh --- tools/addon/glances.sh | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/tools/addon/glances.sh b/tools/addon/glances.sh index 9ae6b3cc5..adee17b5a 100644 --- a/tools/addon/glances.sh +++ b/tools/addon/glances.sh @@ -15,7 +15,6 @@ hostname="$(hostname)" header_info "$APP" catch_errors -# install on Debian/Ubuntu install_glances_debian() { msg_info "Installing dependencies" $STD apt-get update @@ -58,6 +57,21 @@ EOF echo -e "\n$APP is now running at: http://$IP:61208\n" } +# update on Debian/Ubuntu +update_glances_debian() { + if [[ ! -d /opt/glances/.venv ]]; then + msg_error "$APP is not installed" + exit 1 + fi + msg_info "Updating $APP" + cd /opt/glances + source .venv/bin/activate + uv pip install --upgrade "glances[web]" + deactivate + systemctl restart glances + msg_ok "Updated $APP" +} + # uninstall on Debian/Ubuntu uninstall_glances_debian() { msg_info "Uninstalling $APP" @@ -107,6 +121,21 @@ EOF echo -e "\n$APP is now running at: http://$IP:61208\n" } +# update on Alpine +update_glances_alpine() { + if [[ ! -d /opt/glances/.venv ]]; then + msg_error "$APP is not installed" + exit 1 + fi + msg_info "Updating $APP" + cd /opt/glances + source .venv/bin/activate + uv pip install --upgrade "glances[web]" + deactivate + rc-service glances restart + msg_ok "Updated $APP" +} + # uninstall on Alpine uninstall_glances_alpine() { msg_info "Uninstalling $APP" @@ -119,21 +148,24 @@ uninstall_glances_alpine() { # options menu OPTIONS=(Install "Install $APP" + Update "Update $APP" Uninstall "Uninstall $APP") -CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "$APP" --menu "Select an option:" 10 58 2 \ +CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "$APP" --menu "Select an option:" 12 58 3 \ "${OPTIONS[@]}" 3>&1 1>&2 2>&3 || true) # OS detection if grep -qi "alpine" /etc/os-release; then case "$CHOICE" in Install) install_glances_alpine ;; + Update) update_glances_alpine ;; Uninstall) uninstall_glances_alpine ;; *) exit 0 ;; esac else case "$CHOICE" in Install) install_glances_debian ;; + Update) update_glances_debian ;; Uninstall) uninstall_glances_debian ;; *) exit 0 ;; esac From 7fd2744efa7ae2aa87945583e7c1bc9ba7f385ac Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 12:45:09 +0200 Subject: [PATCH 0609/1733] Update glances.sh --- tools/addon/glances.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/addon/glances.sh b/tools/addon/glances.sh index adee17b5a..15a594eea 100644 --- a/tools/addon/glances.sh +++ b/tools/addon/glances.sh @@ -13,7 +13,6 @@ IP=$(hostname -I | awk '{print $1}') hostname="$(hostname)" header_info "$APP" -catch_errors install_glances_debian() { msg_info "Installing dependencies" From 5ff16518d26b11fe74d845052f2a19983d260bf7 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 12:46:15 +0200 Subject: [PATCH 0610/1733] Update glances.sh --- tools/addon/glances.sh | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/tools/addon/glances.sh b/tools/addon/glances.sh index 15a594eea..50eb7871e 100644 --- a/tools/addon/glances.sh +++ b/tools/addon/glances.sh @@ -1,18 +1,36 @@ #!/usr/bin/env bash # Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) -# License: MIT -# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Author: tteck (tteckster) | MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/core.func) -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/tools.func) +function header_info { + clear + cat <<"EOF" + ________ + / ____/ /___ _____ ________ _____ + / / __/ / __ `/ __ \/ ___/ _ \/ ___/ +/ /_/ / / /_/ / / / / /__/ __(__ ) +\____/_/\__,_/_/ /_/\___/\___/____/ + +EOF +} APP="Glances" IP=$(hostname -I | awk '{print $1}') hostname="$(hostname)" +YW=$(echo "\033[33m") +GN=$(echo "\033[1;92m") +RD=$(echo "\033[01;31m") +BL=$(echo "\033[36m") +CL=$(echo "\033[m") +CM="${GN}✔️${CL}" +CROSS="${RD}✖️${CL}" +INFO="${BL}ℹ️${CL}" -header_info "$APP" +function msg_info() { echo -e "${INFO} ${YW}$1...${CL}"; } +function msg_ok() { echo -e "${CM} ${GN}$1${CL}"; } +function msg_error() { echo -e "${CROSS} ${RD}$1${CL}"; } install_glances_debian() { msg_info "Installing dependencies" From 47cbeed528a1d566fb1b39fb85fae9cf0eb8cbec Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 12:48:40 +0200 Subject: [PATCH 0611/1733] Update glances.sh --- tools/addon/glances.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/addon/glances.sh b/tools/addon/glances.sh index 50eb7871e..1bd6e4da6 100644 --- a/tools/addon/glances.sh +++ b/tools/addon/glances.sh @@ -39,6 +39,7 @@ install_glances_debian() { msg_ok "Installed dependencies" msg_info "Setting up Python + uv" + source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/tools.func) setup_uv PYTHON_VERSION="3.12" msg_ok "Setup Python + uv" From 5c41d05c5ac5315fa17e781ab7da41274ef2bb27 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 12:49:37 +0200 Subject: [PATCH 0612/1733] Update glances.sh --- tools/addon/glances.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/addon/glances.sh b/tools/addon/glances.sh index 1bd6e4da6..67bbfc54a 100644 --- a/tools/addon/glances.sh +++ b/tools/addon/glances.sh @@ -34,8 +34,8 @@ function msg_error() { echo -e "${CROSS} ${RD}$1${CL}"; } install_glances_debian() { msg_info "Installing dependencies" - $STD apt-get update - $STD apt-get install -y gcc lm-sensors wireless-tools + apt-get update + apt-get install -y gcc lm-sensors wireless-tools msg_ok "Installed dependencies" msg_info "Setting up Python + uv" @@ -102,8 +102,8 @@ uninstall_glances_debian() { # install on Alpine install_glances_alpine() { msg_info "Installing dependencies" - $STD apk update - $STD apk add --no-cache gcc musl-dev python3 py3-pip py3-virtualenv lm-sensors wireless-tools + apk update + apk add --no-cache gcc musl-dev python3 py3-pip py3-virtualenv lm-sensors wireless-tools msg_ok "Installed dependencies" msg_info "Setting up Python + uv" From 10775c356c0bc8c082fdb218edd2fc1ccf32264d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 12:53:09 +0200 Subject: [PATCH 0613/1733] Update glances.sh --- tools/addon/glances.sh | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tools/addon/glances.sh b/tools/addon/glances.sh index 67bbfc54a..0e5999e36 100644 --- a/tools/addon/glances.sh +++ b/tools/addon/glances.sh @@ -34,8 +34,8 @@ function msg_error() { echo -e "${CROSS} ${RD}$1${CL}"; } install_glances_debian() { msg_info "Installing dependencies" - apt-get update - apt-get install -y gcc lm-sensors wireless-tools + apt-get update >/dev/null 2>&1 + apt-get install -y gcc lm-sensors wireless-tools >/dev/null 2>&1 msg_ok "Installed dependencies" msg_info "Setting up Python + uv" @@ -48,9 +48,9 @@ install_glances_debian() { mkdir -p glances cd glances uv venv - source .venv/bin/activate - uv pip install --upgrade pip wheel setuptools - uv pip install "glances[web]" + source .venv/bin/activate >/dev/null 2>&1 + uv pip install --upgrade pip wheel setuptools >/dev/null 2>&1 + uv pip install "glances[web]" >/dev/null 2>&1 deactivate msg_ok "Installed $APP" @@ -84,7 +84,7 @@ update_glances_debian() { msg_info "Updating $APP" cd /opt/glances source .venv/bin/activate - uv pip install --upgrade "glances[web]" + uv pip install --upgrade "glances[web]" >/dev/null 2>&1 deactivate systemctl restart glances msg_ok "Updated $APP" @@ -102,8 +102,8 @@ uninstall_glances_debian() { # install on Alpine install_glances_alpine() { msg_info "Installing dependencies" - apk update - apk add --no-cache gcc musl-dev python3 py3-pip py3-virtualenv lm-sensors wireless-tools + apk update >/dev/null 2>&1 + apk add --no-cache gcc musl-dev python3 py3-pip py3-virtualenv lm-sensors wireless-tools >/dev/null 2>&1 msg_ok "Installed dependencies" msg_info "Setting up Python + uv" @@ -116,8 +116,8 @@ install_glances_alpine() { cd glances uv venv source .venv/bin/activate - uv pip install --upgrade pip wheel setuptools - uv pip install "glances[web]" + uv pip install --upgrade pip wheel setuptools >/dev/null 2>&1 + uv pip install "glances[web]" >/dev/null 2>&1 deactivate msg_ok "Installed $APP" @@ -148,7 +148,7 @@ update_glances_alpine() { msg_info "Updating $APP" cd /opt/glances source .venv/bin/activate - uv pip install --upgrade "glances[web]" + uv pip install --upgrade "glances[web]" >/dev/null 2>&1 deactivate rc-service glances restart msg_ok "Updated $APP" From 6dee16a15dee2b42a1ee6b1a4c684ab3392f2722 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 12:54:20 +0200 Subject: [PATCH 0614/1733] Update glances.sh --- tools/addon/glances.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/addon/glances.sh b/tools/addon/glances.sh index 0e5999e36..bd735e342 100644 --- a/tools/addon/glances.sh +++ b/tools/addon/glances.sh @@ -107,6 +107,7 @@ install_glances_alpine() { msg_ok "Installed dependencies" msg_info "Setting up Python + uv" + source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/tools.func) setup_uv PYTHON_VERSION="3.12" msg_ok "Setup Python + uv" From ed15393554209ab1b81006a1889fe4b09f2120d9 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 13:06:00 +0200 Subject: [PATCH 0615/1733] extend setup_uv --- misc/tools.func | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index f87974264..6014c0d51 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -1283,8 +1283,20 @@ function setup_uv() { local UV_TAR case "$ARCH" in - x86_64) UV_TAR="uv-x86_64-unknown-linux-gnu.tar.gz" ;; - aarch64) UV_TAR="uv-aarch64-unknown-linux-gnu.tar.gz" ;; + x86_64) + if grep -qi "alpine" /etc/os-release; then + UV_TAR="uv-x86_64-unknown-linux-musl.tar.gz" + else + UV_TAR="uv-x86_64-unknown-linux-gnu.tar.gz" + fi + ;; + aarch64) + if grep -qi "alpine" /etc/os-release; then + UV_TAR="uv-aarch64-unknown-linux-musl.tar.gz" + else + UV_TAR="uv-aarch64-unknown-linux-gnu.tar.gz" + fi + ;; *) msg_error "Unsupported architecture: $ARCH" rm -rf "$TMP_DIR" From c486b7b0354ab971fd84c039f615cbe513d9d1a5 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 13:18:21 +0200 Subject: [PATCH 0616/1733] Update glances.sh --- tools/addon/glances.sh | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/tools/addon/glances.sh b/tools/addon/glances.sh index bd735e342..0f1b76f11 100644 --- a/tools/addon/glances.sh +++ b/tools/addon/glances.sh @@ -17,8 +17,6 @@ EOF } APP="Glances" -IP=$(hostname -I | awk '{print $1}') -hostname="$(hostname)" YW=$(echo "\033[33m") GN=$(echo "\033[1;92m") RD=$(echo "\033[01;31m") @@ -32,6 +30,17 @@ function msg_info() { echo -e "${INFO} ${YW}$1...${CL}"; } function msg_ok() { echo -e "${CM} ${GN}$1${CL}"; } function msg_error() { echo -e "${CROSS} ${RD}$1${CL}"; } +get_local_ip() { + if command -v hostname >/dev/null 2>&1 && hostname -I 2>/dev/null; then + hostname -I | awk '{print $1}' + elif command -v ip >/dev/null 2>&1; then + ip -4 addr show scope global | awk '/inet / {print $2}' | cut -d/ -f1 | head -n1 + else + echo "127.0.0.1" + fi +} +IP=$(get_local_ip) + install_glances_debian() { msg_info "Installing dependencies" apt-get update >/dev/null 2>&1 @@ -103,7 +112,9 @@ uninstall_glances_debian() { install_glances_alpine() { msg_info "Installing dependencies" apk update >/dev/null 2>&1 - apk add --no-cache gcc musl-dev python3 py3-pip py3-virtualenv lm-sensors wireless-tools >/dev/null 2>&1 + $STD apk add --no-cache \ + gcc musl-dev linux-headers python3-dev \ + python3 py3-pip py3-virtualenv lm-sensors wireless-tools >/dev/null 2>&1 msg_ok "Installed dependencies" msg_info "Setting up Python + uv" From 6468f332e8f7d06bad84c8265b28387b60a21ab1 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 13:20:04 +0200 Subject: [PATCH 0617/1733] Update build.func --- misc/build.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index c40a57bd1..8a58664fa 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1219,7 +1219,7 @@ EOF if [[ "${#combo_devices[@]}" -eq 1 && -e /dev/dri/card0 ]]; then combo_devices+=("/dev/dri/card0") fi - echo "combo_devices=${combo_devices[*]}" + #echo "combo_devices=${combo_devices[*]}" pci_addr=$(basename "$bypath" | cut -d- -f1 --complement | sed 's/-render//' || true) pci_info=$(lspci -nn | grep "${pci_addr#0000:}" || true) From 2587aa41451d5fdb7deb2518d27c832cae96440b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 13:22:19 +0200 Subject: [PATCH 0618/1733] feat: use storage as name --- misc/create_lxc.sh | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index 98cc70161..7ca7e40a0 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -63,9 +63,10 @@ function check_storage_support() { local -a VALID_STORAGES=() while IFS= read -r line; do - local STORAGE=$(awk '{print $1}' <<<"$line") - [[ "$STORAGE" == "storage" || -z "$STORAGE" ]] && continue - VALID_STORAGES+=("$STORAGE") + local STORAGE_NAME + STORAGE_NAME=$(awk '{print $1}' <<<"$line") + [[ -z "$STORAGE_NAME" ]] && continue + VALID_STORAGES+=("$STORAGE_NAME") done < <(pvesm status -content "$CONTENT" 2>/dev/null | awk 'NR>1') [[ ${#VALID_STORAGES[@]} -gt 0 ]] @@ -124,11 +125,12 @@ function select_storage() { while read -r TAG TYPE _ TOTAL USED FREE _; do [[ -n "$TAG" && -n "$TYPE" ]] || continue - local DISPLAY="${TAG} (${TYPE})" + local STORAGE_NAME="$TAG" + local DISPLAY="${STORAGE_NAME} (${TYPE})" local USED_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$USED") local FREE_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$FREE") local INFO="Free: ${FREE_FMT}B Used: ${USED_FMT}B" - STORAGE_MAP["$DISPLAY"]="$TAG" + STORAGE_MAP["$DISPLAY"]="$STORAGE_NAME" MENU+=("$DISPLAY" "$INFO" "OFF") ((${#DISPLAY} > COL_WIDTH)) && COL_WIDTH=${#DISPLAY} done < <(pvesm status -content "$CONTENT" | awk 'NR>1') From b9c7b658e535aacc7e6eef658a266037b92a7c4e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 13:39:25 +0200 Subject: [PATCH 0619/1733] better msg's --- misc/create_lxc.sh | 49 ++++++++++++++++++++-------------------------- 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index 7ca7e40a0..6cdfed5cd 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -205,39 +205,35 @@ if qm status "$CTID" &>/dev/null || pct status "$CTID" &>/dev/null; then fi # This checks for the presence of valid Container Storage and Template Storage locations -msg_info "Validating Storage" +msg_info "Validating storage" if ! check_storage_support "rootdir"; then - msg_error "No valid storage found for 'rootdir' (Container)." - msg_debug "check_storage_support('rootdir') → success" + msg_error "No valid storage found for 'rootdir' [Container]" exit 1 fi if ! check_storage_support "vztmpl"; then - msg_error "No valid storage found for 'vztmpl' (Template)." - msg_debug "check_storage_support('vztmpl') → success" + msg_error "No valid storage found for 'vztmpl' [Template]" exit 1 fi -msg_ok "Valid Storage Found" +msg_info "Checking template storage" while true; do if select_storage template; then TEMPLATE_STORAGE="$STORAGE_RESULT" TEMPLATE_STORAGE_INFO="$STORAGE_INFO" - msg_debug "TEMPLATE_STORAGE=$TEMPLATE_STORAGE" - msg_debug "TEMPLATE_STORAGE_INFO=$TEMPLATE_STORAGE_INFO" + msg_ok "Storage ${BL}$TEMPLATE_STORAGE${CL} ($TEMPLATE_STORAGE_INFO) [Template]" break fi done +msg_info "Checking container storage" while true; do if select_storage container; then CONTAINER_STORAGE="$STORAGE_RESULT" CONTAINER_STORAGE_INFO="$STORAGE_INFO" - msg_debug "CONTAINER_STORAGE=$CONTAINER_STORAGE" - msg_debug "CONTAINER_STORAGE_INFO=$CONTAINER_STORAGE_INFO" + msg_ok "Storage ${BL}$CONTAINER_STORAGE${CL} ($CONTAINER_STORAGE_INFO) [Container]" break fi done -msg_ok "Validated Storage | Container: ${BL}$CONTAINER_STORAGE${CL} ($CONTAINER_STORAGE_INFO)" # Check free space on selected container storage STORAGE_FREE=$(pvesm status | awk -v s="$CONTAINER_STORAGE" '$1 == s { print $6 }') @@ -246,11 +242,11 @@ if [ "$STORAGE_FREE" -lt "$REQUIRED_KB" ]; then msg_error "Not enough space on '$CONTAINER_STORAGE'. Needed: ${PCT_DISK_SIZE:-8}G." exit 214 fi + # Check Cluster Quorum if in Cluster if [ -f /etc/pve/corosync.conf ]; then - msg_info "Checking Proxmox cluster quorum status" + msg_info "Checking cluster quorum" if ! pvecm status | awk -F':' '/^Quorate/ { exit ($2 ~ /Yes/) ? 0 : 1 }'; then - printf "\e[?25h" msg_error "Cluster is not quorate. Start all nodes or configure quorum device (QDevice)." exit 210 fi @@ -261,31 +257,28 @@ fi TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" # 1. Check local templates first -msg_info "Checking for local template for '$TEMPLATE_SEARCH'..." +msg_info "Searching for template '$TEMPLATE_SEARCH'" mapfile -t TEMPLATES < <( - pveam list "$TEMPLATE_STORAGE" | awk -v s="$TEMPLATE_SEARCH" '$1 ~ s {print $1}' | sed 's/.*\///' | sort -t - -k 2 -V + pveam list "$TEMPLATE_STORAGE" | awk -v s="$TEMPLATE_SEARCH" '$1 ~ s {print $1}' | + sed 's/.*\///' | sort -t - -k 2 -V ) if [ ${#TEMPLATES[@]} -gt 0 ]; then - msg_ok "Found local template." + TEMPLATE_SOURCE="local" else - # 2. If no local match, try online - msg_info "No local template found. Searching online..." + msg_info "No local template found, checking online repository" + pveam update >/dev/null 2>&1 mapfile -t TEMPLATES < <( - pveam update >/dev/null 2>&1 && - pveam available -section system | sed -n "s/.*\($TEMPLATE_SEARCH.*\)/\1/p" | sort -t - -k 2 -V + pveam available -section system | sed -n "s/.*\($TEMPLATE_SEARCH.*\)/\1/p" | + sort -t - -k 2 -V ) - - if [ ${#TEMPLATES[@]} -eq 0 ]; then - msg_error "No online or local LXC template found for '${TEMPLATE_SEARCH}'. Please check network or install a template manually." - exit 207 - fi - msg_ok "Found online template." + TEMPLATE_SOURCE="online" fi -# 3. Use the newest template (last in sorted list) TEMPLATE="${TEMPLATES[-1]}" -TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || echo "/var/lib/vz/template/cache/$TEMPLATE")" +TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || + echo "/var/lib/vz/template/cache/$TEMPLATE")" +msg_ok "Template ${BL}$TEMPLATE${CL} [$TEMPLATE_SOURCE]" msg_debug "TEMPLATE_SEARCH=$TEMPLATE_SEARCH" msg_debug "TEMPLATES=(${TEMPLATES[*]})" From 3a20ca1f9b70be723fc060e704bc3e3c5f63e8d0 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Tue, 19 Aug 2025 13:39:29 +0200 Subject: [PATCH 0620/1733] Traefik fixes --- install/traefik-install.sh | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/install/traefik-install.sh b/install/traefik-install.sh index 7507a8a85..0e3a4db23 100644 --- a/install/traefik-install.sh +++ b/install/traefik-install.sh @@ -110,7 +110,7 @@ EOF msg_ok "Created Traefik configuration" msg_info "Creating Service" -cat </etc/systemd/system/traefik.service +cat <<'EOF' >/etc/systemd/system/traefik.service [Unit] Description=Traefik is an open-source Edge Router that makes publishing your services a fun and easy experience @@ -128,7 +128,7 @@ systemctl enable -q --now traefik msg_ok "Created Service" msg_info "Creating site templates" -cat </etc/traefik/template.yaml.tpl +cat <<'EOF' >/etc/traefik/template.yaml.tpl http: routers: ${hostname}: @@ -142,9 +142,10 @@ http: servers: - url: "${URL}" EOF -msg_ok: "Template Created" -msg_info: "Creating Helper Scripts" -cat </usr/bin/addsite +msg_ok "Template Created" + +msg_info "Creating Helper Scripts" +cat <<'EOF' >/usr/bin/addsite #!/bin/bash function setup_site() { @@ -164,7 +165,7 @@ function setup_site() { setup_site EOF -cat </usr/bin/ensite +cat <<'EOF' >/usr/bin/ensite #!/bin/bash function ensite() { @@ -193,7 +194,7 @@ function ensite() { ensite EOF -cat </usr/bin/dissite +cat <<'EOF' >/usr/bin/dissite #!/bin/bash function dissite() { @@ -223,7 +224,7 @@ function dissite() { dissite EOF -cat </usr/bin/editsite +cat <<'EOF' >/usr/bin/editsite #!/bin/bash function edit_site() { From eaebea6c35f649e85902bb64f21d956dd8fda483 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Tue, 19 Aug 2025 13:40:36 +0200 Subject: [PATCH 0621/1733] Traefik fixes --- install/traefik-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/traefik-install.sh b/install/traefik-install.sh index 0e3a4db23..eb2fcb24c 100644 --- a/install/traefik-install.sh +++ b/install/traefik-install.sh @@ -28,7 +28,7 @@ echo "${RELEASE}" >/opt/${APPLICATION}_version.txt msg_ok "Installed Traefik v${RELEASE}" msg_info "Creating Traefik configuration" -cat </etc/traefik/traefik.yaml +cat <<'EOF' >/etc/traefik/traefik.yaml providers: file: directory: /etc/traefik/conf.d/ From 429a50d498c673286fba489764d7b1be95a3758f Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 19 Aug 2025 13:45:05 +0200 Subject: [PATCH 0622/1733] better msgs --- misc/create_lxc.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index 6cdfed5cd..db73f0a33 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -225,7 +225,6 @@ while true; do fi done -msg_info "Checking container storage" while true; do if select_storage container; then CONTAINER_STORAGE="$STORAGE_RESULT" @@ -310,8 +309,6 @@ if [ "$TEMPLATE_VALID" -eq 0 ]; then done fi -msg_ok "LXC Template '$TEMPLATE' is ready to use." - msg_info "Creating LXC Container" # Check and fix subuid/subgid grep -q "root:100000:65536" /etc/subuid || echo "root:100000:65536" >>/etc/subuid From 9e23e43fc8022084647e5f43d4021eca46598ee1 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Tue, 19 Aug 2025 13:55:06 +0200 Subject: [PATCH 0623/1733] Traefik fixes --- install/traefik-install.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/install/traefik-install.sh b/install/traefik-install.sh index eb2fcb24c..3f3dc2fbf 100644 --- a/install/traefik-install.sh +++ b/install/traefik-install.sh @@ -165,6 +165,7 @@ function setup_site() { setup_site EOF + cat <<'EOF' >/usr/bin/ensite #!/bin/bash @@ -194,6 +195,7 @@ function ensite() { ensite EOF + cat <<'EOF' >/usr/bin/dissite #!/bin/bash From 587cfd883a6cddf27e1b6dab540afd50cc34d323 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Tue, 19 Aug 2025 15:25:26 +0200 Subject: [PATCH 0624/1733] Traefik fixes --- install/traefik-install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install/traefik-install.sh b/install/traefik-install.sh index 3f3dc2fbf..9191d6c61 100644 --- a/install/traefik-install.sh +++ b/install/traefik-install.sh @@ -149,6 +149,7 @@ cat <<'EOF' >/usr/bin/addsite #!/bin/bash function setup_site() { + set -e hostname="$(whiptail --inputbox "Enter the hostname of the Site" 8 78 --title "Hostname" 3>&1 1>&2 2>&3)" exitstatus=$? [[ "$exitstatus" = 1 ]] && return; From 43a2047e1199466161dd0f033572088a9faee5bc Mon Sep 17 00:00:00 2001 From: tremor021 Date: Tue, 19 Aug 2025 15:32:17 +0200 Subject: [PATCH 0625/1733] Traefik fixes --- install/traefik-install.sh | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/install/traefik-install.sh b/install/traefik-install.sh index 9191d6c61..e65811ed0 100644 --- a/install/traefik-install.sh +++ b/install/traefik-install.sh @@ -145,26 +145,18 @@ EOF msg_ok "Template Created" msg_info "Creating Helper Scripts" -cat <<'EOF' >/usr/bin/addsite -#!/bin/bash +install -Dm0755 /dev/stdin /usr/local/bin/addsite <<'EOF' +#!/usr/bin/env bash -function setup_site() { - set -e - hostname="$(whiptail --inputbox "Enter the hostname of the Site" 8 78 --title "Hostname" 3>&1 1>&2 2>&3)" - exitstatus=$? - [[ "$exitstatus" = 1 ]] && return; - FQDN="$(whiptail --inputbox "Enter the FQDN of the Site" 8 78 --title "FQDN" 3>&1 1>&2 2>&3)" - exitstatus=$? - [[ "$exitstatus" = 1 ]] && return; - URL="$(whiptail --inputbox "Enter the URL of the Site (For example http://192.168.x.x:8080)" 8 78 --title "URL" 3>&1 1>&2 2>&3)" - exitstatus=$? - [[ "$exitstatus" = 1 ]] && return; - filename="/etc/traefik/sites-available/${hostname}.yaml" - export hostname FQDN URL - envsubst '${hostname} ${FQDN} ${URL}' < /etc/traefik/template.yaml.tpl > ${filename} -} - -setup_site +set -e +hostname="$(whiptail --inputbox 'Enter the hostname of the Site' 8 78 --title 'Hostname' 3>&1 1>&2 2>&3)" || exit 0 +FQDN="$(whiptail --inputbox 'Enter the FQDN of the Site' 8 78 --title 'FQDN' 3>&1 1>&2 2>&3)" || exit 0 +URL="$(whiptail --inputbox 'Enter the URL of the Site (e.g. http://192.168.x.x:8080)' 8 78 --title 'URL' 3>&1 1>&2 2>&3)" || exit 0 +mkdir -p /etc/traefik/sites-available +filename="/etc/traefik/sites-available/${hostname}.yaml" +export hostname FQDN URL +envsubst '${hostname} ${FQDN} ${URL}' < /etc/traefik/template.yaml.tpl > "$filename" +echo "Wrote $filename" EOF cat <<'EOF' >/usr/bin/ensite From 320d4e7eb21dda2b88ecbd878716f780246a112b Mon Sep 17 00:00:00 2001 From: tremor021 Date: Tue, 19 Aug 2025 15:36:28 +0200 Subject: [PATCH 0626/1733] Traefik fixes --- install/traefik-install.sh | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/install/traefik-install.sh b/install/traefik-install.sh index e65811ed0..3f3dc2fbf 100644 --- a/install/traefik-install.sh +++ b/install/traefik-install.sh @@ -145,18 +145,25 @@ EOF msg_ok "Template Created" msg_info "Creating Helper Scripts" -install -Dm0755 /dev/stdin /usr/local/bin/addsite <<'EOF' -#!/usr/bin/env bash +cat <<'EOF' >/usr/bin/addsite +#!/bin/bash -set -e -hostname="$(whiptail --inputbox 'Enter the hostname of the Site' 8 78 --title 'Hostname' 3>&1 1>&2 2>&3)" || exit 0 -FQDN="$(whiptail --inputbox 'Enter the FQDN of the Site' 8 78 --title 'FQDN' 3>&1 1>&2 2>&3)" || exit 0 -URL="$(whiptail --inputbox 'Enter the URL of the Site (e.g. http://192.168.x.x:8080)' 8 78 --title 'URL' 3>&1 1>&2 2>&3)" || exit 0 -mkdir -p /etc/traefik/sites-available -filename="/etc/traefik/sites-available/${hostname}.yaml" -export hostname FQDN URL -envsubst '${hostname} ${FQDN} ${URL}' < /etc/traefik/template.yaml.tpl > "$filename" -echo "Wrote $filename" +function setup_site() { + hostname="$(whiptail --inputbox "Enter the hostname of the Site" 8 78 --title "Hostname" 3>&1 1>&2 2>&3)" + exitstatus=$? + [[ "$exitstatus" = 1 ]] && return; + FQDN="$(whiptail --inputbox "Enter the FQDN of the Site" 8 78 --title "FQDN" 3>&1 1>&2 2>&3)" + exitstatus=$? + [[ "$exitstatus" = 1 ]] && return; + URL="$(whiptail --inputbox "Enter the URL of the Site (For example http://192.168.x.x:8080)" 8 78 --title "URL" 3>&1 1>&2 2>&3)" + exitstatus=$? + [[ "$exitstatus" = 1 ]] && return; + filename="/etc/traefik/sites-available/${hostname}.yaml" + export hostname FQDN URL + envsubst '${hostname} ${FQDN} ${URL}' < /etc/traefik/template.yaml.tpl > ${filename} +} + +setup_site EOF cat <<'EOF' >/usr/bin/ensite From c2cdecbe19877ba0fef851d94974f1308e7456dd Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 20 Aug 2025 08:24:06 +0200 Subject: [PATCH 0627/1733] debug output --- misc/create_lxc.sh | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index db73f0a33..c986bd7b1 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -6,7 +6,7 @@ # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # This sets verbose mode if the global variable is set to "yes" -if [ "$CREATE_LXC_VERBOSE" == "yes" ]; then set -x; fi +if [ "$VERBOSE" == "yes" ]; then set -x; fi if command -v curl >/dev/null 2>&1; then source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) @@ -254,11 +254,24 @@ fi # Update LXC template list TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" +case "$PCT_OSTYPE" in + debian|ubuntu) + TEMPLATE_PATTERN="-standard_" + ;; + alpine|fedora|rocky|centos) + TEMPLATE_PATTERN="-default_" + ;; + *) + TEMPLATE_PATTERN="" + ;; +esac + # 1. Check local templates first msg_info "Searching for template '$TEMPLATE_SEARCH'" mapfile -t TEMPLATES < <( - pveam list "$TEMPLATE_STORAGE" | awk -v s="$TEMPLATE_SEARCH" '$1 ~ s {print $1}' | + pveam list "$TEMPLATE_STORAGE" | + awk -v s="$TEMPLATE_SEARCH" -v p="$TEMPLATE_PATTERN" '$1 ~ s && $1 ~ p {print $1}' | sed 's/.*\///' | sort -t - -k 2 -V ) @@ -268,12 +281,15 @@ else msg_info "No local template found, checking online repository" pveam update >/dev/null 2>&1 mapfile -t TEMPLATES < <( - pveam available -section system | sed -n "s/.*\($TEMPLATE_SEARCH.*\)/\1/p" | + pveam update >/dev/null 2>&1 && + pveam available -section system | + sed -n "s/.*\($TEMPLATE_SEARCH.*$TEMPLATE_PATTERN.*\)/\1/p" | sort -t - -k 2 -V ) TEMPLATE_SOURCE="online" fi + TEMPLATE="${TEMPLATES[-1]}" TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || echo "/var/lib/vz/template/cache/$TEMPLATE")" From c1265d940c31aaf190018c1cc682efb1b0d30d0c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 20 Aug 2025 10:56:17 +0200 Subject: [PATCH 0628/1733] test --- ct/debian.sh | 2 +- install/debian-install.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/debian.sh b/ct/debian.sh index b7efad6c3..a21a5e3f7 100644 --- a/ct/debian.sh +++ b/ct/debian.sh @@ -11,7 +11,7 @@ var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" -var_version="${var_version:-11}" +var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" var_fuse="${var_fuse:-no}" var_tun="${var_tun:-no}" diff --git a/install/debian-install.sh b/install/debian-install.sh index fcca74fcc..ca9567b9e 100644 --- a/install/debian-install.sh +++ b/install/debian-install.sh @@ -35,7 +35,7 @@ setup_mariadb #msg_ok "Get Release $RELEASE" #NODE_VERSION="24" NODE_MODULE="yarn" setup_nodejs -#PG_VERSION="16" setup_postgresql +PG_VERSION="16" setup_postgresql motd_ssh customize From afb98a7e4aa885d0fc4af8214befc0947ee557c4 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 20 Aug 2025 10:57:13 +0200 Subject: [PATCH 0629/1733] Update debian.sh --- ct/debian.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ct/debian.sh b/ct/debian.sh index a21a5e3f7..3cc0b73d5 100644 --- a/ct/debian.sh +++ b/ct/debian.sh @@ -7,9 +7,9 @@ source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxV APP="Debian" var_tags="${var_tags:-os}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-512}" -var_disk="${var_disk:-2}" +var_cpu="${var_cpu:-4}" +var_ram="${var_ram:-4096}" +var_disk="${var_disk:-15}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" From b7b0da453379e7f182bbc6be54bae4138f92eb89 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 20 Aug 2025 11:29:33 +0200 Subject: [PATCH 0630/1733] add netdata for ref --- tools/addon/netdata.sh | 123 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 tools/addon/netdata.sh diff --git a/tools/addon/netdata.sh b/tools/addon/netdata.sh new file mode 100644 index 000000000..250b8f1c1 --- /dev/null +++ b/tools/addon/netdata.sh @@ -0,0 +1,123 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# License: MIT +# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE + +function header_info { + clear + cat <<"EOF" + _ __ __ ____ __ + / | / /__ / /_/ __ \____ _/ /_____ _ + / |/ / _ \/ __/ / / / __ `/ __/ __ `/ + / /| / __/ /_/ /_/ / /_/ / /_/ /_/ / +/_/ |_/\___/\__/_____/\__,_/\__/\__,_/ + +EOF +} + +YW=$(echo "\033[33m") +BL=$(echo "\033[36m") +RD=$(echo "\033[01;31m") +GN=$(echo "\033[1;92m") +CL=$(echo "\033[m") +BFR="\\r\\033[K" +HOLD="-" +CM="${GN}✓${CL}" +silent() { "$@" >/dev/null 2>&1; } +set -e +header_info +echo "Loading..." +function msg_info() { + local msg="$1" + echo -ne " ${HOLD} ${YW}${msg}..." +} + +function msg_ok() { + local msg="$1" + echo -e "${BFR} ${CM} ${GN}${msg}${CL}" +} + +install() { + header_info + while true; do + read -p "Are you sure you want to install NetData on Proxmox VE host. Proceed(y/n)?" yn + case $yn in + [Yy]*) break ;; + [Nn]*) exit ;; + *) echo "Please answer yes or no." ;; + esac + done + header_info + read -r -p "Verbose mode? " prompt + if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then + STD="" + else + STD="silent" + fi + header_info + + msg_info "Setting up repository" + $STD apt-get install -y debian-keyring + curl -fsSL "https://repo.netdata.cloud/repos/repoconfig/debian/bookworm/netdata-repo_5-1+debian12_all.deb" -o $(basename "https://repo.netdata.cloud/repos/repoconfig/debian/bookworm/netdata-repo_5-1+debian12_all.deb") + $STD dpkg -i netdata-repo_5-1+debian12_all.deb + rm -rf netdata-repo_5-1+debian12_all.deb + msg_ok "Set up repository" + + msg_info "Installing Netdata" + $STD apt-get update + $STD apt-get install -y netdata + msg_ok "Installed Netdata" + msg_ok "Completed Successfully!\n" + echo -e "\n Netdata should be reachable at${BL} http://$(hostname -I | awk '{print $1}'):19999 ${CL}\n" +} + +uninstall() { + header_info + read -r -p "Verbose mode? " prompt + if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then + STD="" + else + STD="silent" + fi + header_info + + msg_info "Uninstalling Netdata" + systemctl stop netdata + rm -rf /var/log/netdata /var/lib/netdata /var/cache/netdata /etc/netdata/go.d + rm -rf /etc/apt/trusted.gpg.d/netdata-archive-keyring.gpg /etc/apt/sources.list.d/netdata.list + $STD apt-get remove --purge -y netdata netdata-repo + systemctl daemon-reload + $STD apt autoremove -y + $STD userdel netdata + msg_ok "Uninstalled Netdata" + msg_ok "Completed Successfully!\n" +} + +if ! pveversion | grep -Eq "pve-manager/8\.[0-4](\.[0-9]+)*"; then + echo -e "This version of Proxmox Virtual Environment is not supported" + echo -e "Requires PVE Version 8.0 or higher" + echo -e "Exiting..." + sleep 2 + exit +fi + +OPTIONS=(Install "Install NetData on Proxmox VE" + Uninstall "Uninstall NetData from Proxmox VE") + +CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "NetData" --menu "Select an option:" 10 58 2 \ + "${OPTIONS[@]}" 3>&1 1>&2 2>&3) + +case $CHOICE in +"Install") + install + ;; +"Uninstall") + uninstall + ;; +*) + echo "Exiting..." + exit 0 + ;; +esac From 30d6cf8b4a86f35f0d9ad9eda797d7bcc34b8070 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 20 Aug 2025 11:36:41 +0200 Subject: [PATCH 0631/1733] Update netdata.sh --- tools/addon/netdata.sh | 115 +++++++++++++++++++++++++++++------------ 1 file changed, 81 insertions(+), 34 deletions(-) diff --git a/tools/addon/netdata.sh b/tools/addon/netdata.sh index 250b8f1c1..5e9b63260 100644 --- a/tools/addon/netdata.sh +++ b/tools/addon/netdata.sh @@ -39,30 +39,91 @@ function msg_ok() { echo -e "${BFR} ${CM} ${GN}${msg}${CL}" } +pve_check() { + if ! command -v pveversion >/dev/null 2>&1; then + msg_error "This script can only be run on a Proxmox VE host." + exit 1 + fi + + local PVE_VER + PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" + + # Proxmox VE 8.x: allow 8.0 – 8.9 + if [[ "$PVE_VER" =~ ^8\.([0-9]+)$ ]]; then + local MINOR="${BASH_REMATCH[1]}" + if ((MINOR < 0 || MINOR > 9)); then + msg_error "Unsupported Proxmox VE version: $PVE_VER" + msg_error "Supported versions: 8.0 – 8.9 or 9.0" + exit 1 + fi + return 0 + fi + + # Proxmox VE 9.x: allow only 9.0 + if [[ "$PVE_VER" =~ ^9\.([0-9]+)$ ]]; then + local MINOR="${BASH_REMATCH[1]}" + if ((MINOR != 0)); then + msg_error "Unsupported Proxmox VE version: $PVE_VER" + msg_error "Supported versions: 8.0 – 8.9 or 9.0" + exit 1 + fi + return 0 + fi + + msg_error "Unsupported Proxmox VE version: $PVE_VER" + msg_error "Supported versions: 8.0 – 8.9 or 9.0" + exit 1 +} + +detect_codename() { + source /etc/os-release + if [[ "$ID" != "debian" ]]; then + msg_error "Unsupported base OS: $ID (only Proxmox VE / Debian supported)." + exit 1 + fi + CODENAME="${VERSION_CODENAME:-}" + if [[ -z "$CODENAME" ]]; then + msg_error "Could not detect Debian codename." + exit 1 + fi + echo "$CODENAME" +} + +get_latest_repo_pkg() { + local REPO_URL=$1 + curl -fsSL "$REPO_URL" | + grep -oP 'netdata-repo_[^"]+all\.deb' | + sort -V | + tail -n1 +} + install() { header_info while true; do - read -p "Are you sure you want to install NetData on Proxmox VE host. Proceed(y/n)?" yn + read -p "Are you sure you want to install NetData on Proxmox VE host. Proceed(y/n)? " yn case $yn in [Yy]*) break ;; [Nn]*) exit ;; *) echo "Please answer yes or no." ;; esac done - header_info + read -r -p "Verbose mode? " prompt - if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then - STD="" - else - STD="silent" - fi - header_info + [[ ${prompt,,} =~ ^(y|yes)$ ]] && STD="" || STD="silent" + + CODENAME=$(detect_codename) + REPO_URL="https://repo.netdata.cloud/repos/repoconfig/debian/${CODENAME}/" msg_info "Setting up repository" $STD apt-get install -y debian-keyring - curl -fsSL "https://repo.netdata.cloud/repos/repoconfig/debian/bookworm/netdata-repo_5-1+debian12_all.deb" -o $(basename "https://repo.netdata.cloud/repos/repoconfig/debian/bookworm/netdata-repo_5-1+debian12_all.deb") - $STD dpkg -i netdata-repo_5-1+debian12_all.deb - rm -rf netdata-repo_5-1+debian12_all.deb + PKG=$(get_latest_repo_pkg "$REPO_URL") + if [[ -z "$PKG" ]]; then + msg_error "Could not find netdata-repo package for Debian $CODENAME" + exit 1 + fi + curl -fsSL "${REPO_URL}${PKG}" -o "$PKG" + $STD dpkg -i "$PKG" + rm -f "$PKG" msg_ok "Set up repository" msg_info "Installing Netdata" @@ -76,46 +137,32 @@ install() { uninstall() { header_info read -r -p "Verbose mode? " prompt - if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then - STD="" - else - STD="silent" - fi - header_info + [[ ${prompt,,} =~ ^(y|yes)$ ]] && STD="" || STD="silent" msg_info "Uninstalling Netdata" - systemctl stop netdata + systemctl stop netdata || true rm -rf /var/log/netdata /var/lib/netdata /var/cache/netdata /etc/netdata/go.d rm -rf /etc/apt/trusted.gpg.d/netdata-archive-keyring.gpg /etc/apt/sources.list.d/netdata.list $STD apt-get remove --purge -y netdata netdata-repo systemctl daemon-reload $STD apt autoremove -y - $STD userdel netdata + $STD userdel netdata || true msg_ok "Uninstalled Netdata" msg_ok "Completed Successfully!\n" } -if ! pveversion | grep -Eq "pve-manager/8\.[0-4](\.[0-9]+)*"; then - echo -e "This version of Proxmox Virtual Environment is not supported" - echo -e "Requires PVE Version 8.0 or higher" - echo -e "Exiting..." - sleep 2 - exit -fi +header_info +pve_check OPTIONS=(Install "Install NetData on Proxmox VE" Uninstall "Uninstall NetData from Proxmox VE") -CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "NetData" --menu "Select an option:" 10 58 2 \ - "${OPTIONS[@]}" 3>&1 1>&2 2>&3) +CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "NetData" \ + --menu "Select an option:" 10 58 2 "${OPTIONS[@]}" 3>&1 1>&2 2>&3) case $CHOICE in -"Install") - install - ;; -"Uninstall") - uninstall - ;; +"Install") install ;; +"Uninstall") uninstall ;; *) echo "Exiting..." exit 0 From d5f620bfe63dbd3cd65419f5cc6790959501e03b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 20 Aug 2025 11:38:22 +0200 Subject: [PATCH 0632/1733] Update netdata.sh --- tools/addon/netdata.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/addon/netdata.sh b/tools/addon/netdata.sh index 5e9b63260..f3e54ff6d 100644 --- a/tools/addon/netdata.sh +++ b/tools/addon/netdata.sh @@ -39,6 +39,8 @@ function msg_ok() { echo -e "${BFR} ${CM} ${GN}${msg}${CL}" } +function msg_error() { echo -e "${RD}✗ $1${CL}"; } + pve_check() { if ! command -v pveversion >/dev/null 2>&1; then msg_error "This script can only be run on a Proxmox VE host." From c74b2d58da6fa25c224576fa7e8b933fbff5e160 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 20 Aug 2025 11:44:20 +0200 Subject: [PATCH 0633/1733] Update netdata.sh --- tools/addon/netdata.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/addon/netdata.sh b/tools/addon/netdata.sh index f3e54ff6d..1f2d598fa 100644 --- a/tools/addon/netdata.sh +++ b/tools/addon/netdata.sh @@ -51,11 +51,11 @@ pve_check() { PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" # Proxmox VE 8.x: allow 8.0 – 8.9 - if [[ "$PVE_VER" =~ ^8\.([0-9]+)$ ]]; then + if [[ "$PVE_VER" =~ ^9\.([0-9]+)(\.[0-9]+)?$ ]]; then local MINOR="${BASH_REMATCH[1]}" - if ((MINOR < 0 || MINOR > 9)); then + if ((MINOR != 0)); then msg_error "Unsupported Proxmox VE version: $PVE_VER" - msg_error "Supported versions: 8.0 – 8.9 or 9.0" + msg_error "Supported versions: 8.0 – 8.9 or 9.0.x" exit 1 fi return 0 From fb3f4f4eb4eea902fe887d834addf317847cb1b5 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 20 Aug 2025 12:30:28 +0200 Subject: [PATCH 0634/1733] paperless prebuild --- ct/paperless-ngx.sh | 2 +- install/paperless-ngx-install.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/paperless-ngx.sh b/ct/paperless-ngx.sh index 997e7b16e..849cffde8 100644 --- a/ct/paperless-ngx.sh +++ b/ct/paperless-ngx.sh @@ -33,7 +33,7 @@ function update_script() { RELEASE=$(curl -fsSL https://api.github.com/repos/paperless-ngx/paperless-ngx/releases/latest | jq -r .tag_name | sed 's/^v//') if [[ "${RELEASE}" != "$(cat ~/.paperless 2>/dev/null)" ]] || [[ ! -f ~/.paperless ]]; then PYTHON_VERSION="3.13" setup_uv - fetch_and_deploy_gh_release "paperless" "paperless-ngx/paperless-ngx" "tarball" "latest" "/opt/paperless" + fetch_and_deploy_gh_release "paperless" "paperless-ngx/paperless-ngx" "prebuild" "latest" "/opt/paperless" "paperless*tar.xz" fetch_and_deploy_gh_release "jbig2enc" "ie13/jbig2enc" "tarball" "latest" "/opt/jbig2enc" setup_gs diff --git a/install/paperless-ngx-install.sh b/install/paperless-ngx-install.sh index 4ddc0fc86..d668909fc 100644 --- a/install/paperless-ngx-install.sh +++ b/install/paperless-ngx-install.sh @@ -36,7 +36,7 @@ msg_ok "Installed Dependencies" PG_VERSION="16" setup_postgresql PYTHON_VERSION="3.13" setup_uv -fetch_and_deploy_gh_release "paperless" "paperless-ngx/paperless-ngx" "tarball" "latest" "/opt/paperless" +fetch_and_deploy_gh_release "paperless" "paperless-ngx/paperless-ngx" "prebuild" "latest" "/opt/paperless" "paperless*tar.xz" fetch_and_deploy_gh_release "jbig2enc" "ie13/jbig2enc" "tarball" "latest" "/opt/jbig2enc" setup_gs From 70f72e3432a21e40856a37bf6b1d3502d705a7a6 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 20 Aug 2025 12:34:21 +0200 Subject: [PATCH 0635/1733] Update tools.func --- misc/tools.func | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/misc/tools.func b/misc/tools.func index 6014c0d51..7c7e54926 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -1434,7 +1434,9 @@ function setup_gs() { rm -rf "$TMP_DIR" } $STD apt-get install -y build-essential libpng-dev zlib1g-dev - $STD ./configure >/dev/null && $STD make && $STD sudo make install + $STD ./configure >/dev/null + $STD make + $STD sudo make install local EXIT_CODE=$? hash -r if [[ ! -x "$(command -v gs)" ]]; then From f9bb13d36c4cb73ed10bdbf5753ac23fe79b36ea Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 20 Aug 2025 13:18:45 +0200 Subject: [PATCH 0636/1733] Update paperless-ngx.sh --- ct/paperless-ngx.sh | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/ct/paperless-ngx.sh b/ct/paperless-ngx.sh index 849cffde8..b698a581c 100644 --- a/ct/paperless-ngx.sh +++ b/ct/paperless-ngx.sh @@ -32,17 +32,26 @@ function update_script() { fi RELEASE=$(curl -fsSL https://api.github.com/repos/paperless-ngx/paperless-ngx/releases/latest | jq -r .tag_name | sed 's/^v//') if [[ "${RELEASE}" != "$(cat ~/.paperless 2>/dev/null)" ]] || [[ ! -f ~/.paperless ]]; then - PYTHON_VERSION="3.13" setup_uv - fetch_and_deploy_gh_release "paperless" "paperless-ngx/paperless-ngx" "prebuild" "latest" "/opt/paperless" "paperless*tar.xz" - fetch_and_deploy_gh_release "jbig2enc" "ie13/jbig2enc" "tarball" "latest" "/opt/jbig2enc" - setup_gs - msg_info "Stopping all Paperless-ngx Services" systemctl stop paperless-consumer paperless-webserver paperless-scheduler paperless-task-queue msg_ok "Stopped all Paperless-ngx Services" if grep -q "uv run" /etc/systemd/system/paperless-webserver.service; then + + msg_info "backing up data" + mkdir -p /opt/paperless/backup + cp -r /opt/paperless/data /opt/paperless/backup/ + cp -r /opt/paperless/media /opt/paperless/backup/ + cp -r /opt/paperless/paperless.conf /opt/paperless/backup/ + msg_ok "Backup completed" + + PYTHON_VERSION="3.13" setup_uv + fetch_and_deploy_gh_release "paperless" "paperless-ngx/paperless-ngx" "prebuild" "latest" "/opt/paperless" "paperless*tar.xz" + fetch_and_deploy_gh_release "jbig2enc" "ie13/jbig2enc" "tarball" "latest" "/opt/jbig2enc" + setup_gs + msg_info "Updating to ${RELEASE}" + cp -r /opt/paperless/backup/* /opt/paperless/ cd /opt/paperless $STD uv sync --all-extras cd /opt/paperless/src @@ -87,8 +96,21 @@ function update_script() { done $STD systemctl daemon-reload + msg_info "backing up data" + mkdir -p /opt/paperless/backup + cp -r /opt/paperless/data /opt/paperless/backup/ + cp -r /opt/paperless/media /opt/paperless/backup/ + cp -r /opt/paperless/paperless.conf /opt/paperless/backup/ + msg_ok "Backup completed" + + PYTHON_VERSION="3.13" setup_uv + fetch_and_deploy_gh_release "paperless" "paperless-ngx/paperless-ngx" "prebuild" "latest" "/opt/paperless" "paperless*tar.xz" + fetch_and_deploy_gh_release "jbig2enc" "ie13/jbig2enc" "tarball" "latest" "/opt/jbig2enc" + setup_gs + + msg_info "Updating Paperless-ngx" + cp -r /opt/paperless/backup/* /opt/paperless/ cd /opt/paperless - msg_info "Running Paperless-ngx UV sync" $STD uv sync --all-extras cd /opt/paperless/src $STD uv run -- python manage.py migrate From 6e1c22246718f8888eeaf7f703432c54ebf7c6f6 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 20 Aug 2025 13:45:40 +0200 Subject: [PATCH 0637/1733] Update paperless-ngx.sh --- ct/paperless-ngx.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ct/paperless-ngx.sh b/ct/paperless-ngx.sh index b698a581c..8c0b38e8c 100644 --- a/ct/paperless-ngx.sh +++ b/ct/paperless-ngx.sh @@ -85,9 +85,12 @@ function update_script() { if [[ -n "$path" && -f "$path" ]]; then sed -i "s|^ExecStart=.*|${PATCHES[$svc]}|" "$path" if [[ "$svc" == "paperless-webserver.service" ]]; then - grep -q "^Environment=GRANIAN_HOST=" "$path" || echo 'Environment=GRANIAN_HOST=::' >>"$path" - grep -q "^Environment=GRANIAN_PORT=" "$path" || echo 'Environment=GRANIAN_PORT=8000' >>"$path" - grep -q "^Environment=GRANIAN_WORKERS=" "$path" || echo 'Environment=GRANIAN_WORKERS=1' >>"$path" + grep -q "^Environment=GRANIAN_HOST=" "$path" || + sed -i '/^\[Service\]/a Environment=GRANIAN_HOST=::' "$path" + grep -q "^Environment=GRANIAN_PORT=" "$path" || + sed -i '/^\[Service\]/a Environment=GRANIAN_PORT=8000' "$path" + grep -q "^Environment=GRANIAN_WORKERS=" "$path" || + sed -i '/^\[Service\]/a Environment=GRANIAN_WORKERS=1' "$path" fi msg_ok "Patched $svc" else From 206d919c61ca601d81cccb4fffcc38898ae614ba Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 20 Aug 2025 11:46:01 +0000 Subject: [PATCH 0638/1733] Update .app files --- tools/headers/glances | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 tools/headers/glances diff --git a/tools/headers/glances b/tools/headers/glances new file mode 100644 index 000000000..18ac97577 --- /dev/null +++ b/tools/headers/glances @@ -0,0 +1,6 @@ + ________ + / ____/ /___ _____ ________ _____ + / / __/ / __ `/ __ \/ ___/ _ \/ ___/ +/ /_/ / / /_/ / / / / /__/ __(__ ) +\____/_/\__,_/_/ /_/\___/\___/____/ + From 05347048c538b593588a1b3f7a543d6662684a6d Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 20 Aug 2025 13:59:07 +0200 Subject: [PATCH 0639/1733] Add Alpine RustDesk Server script --- ct/alpine-rustdeskserver.sh | 62 +++++++++++++ install/alpine-rustdeskserver-install.sh | 111 +++++++++++++++++++++++ 2 files changed, 173 insertions(+) create mode 100644 ct/alpine-rustdeskserver.sh create mode 100644 install/alpine-rustdeskserver-install.sh diff --git a/ct/alpine-rustdeskserver.sh b/ct/alpine-rustdeskserver.sh new file mode 100644 index 000000000..0cf945f7d --- /dev/null +++ b/ct/alpine-rustdeskserver.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/rustdesk/rustdesk-server + +APP="Alpine-RustDesk Server" +var_tags="${var_tags:-alpine;monitoring}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-512}" +var_disk="${var_disk:-3}" +var_os="${var_os:-alpine}" +var_version="${var_version:-3.22}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + + if [[ ! -d /opt/rustdesk-server ]]; then + msg_error "No ${APP} Installation Found!" + exit 1 + fi + RELEASE=$(curl -s https://api.github.com/repos/TwiN/RustDesk Server/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') + if [ "${RELEASE}" != "$(cat /opt/RustDesk Server_version.txt)" ] || [ ! -f /opt/RustDesk Server_version.txt ]; then + msg_info "Updating ${APP} LXC" + $STD apk -U upgrade + $STD service RustDesk Server stop + mv /opt/RustDesk Server/config/config.yaml /opt + rm -rf /opt/RustDesk Server/* + temp_file=$(mktemp) + curl -fsSL "https://github.com/TwiN/RustDesk Server/archive/refs/tags/v${RELEASE}.tar.gz" -o "$temp_file" + tar zxf "$temp_file" --strip-components=1 -C /opt/RustDesk Server + cd /opt/RustDesk Server + $STD go mod tidy + CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o RustDesk Server . + setcap CAP_NET_RAW+ep RustDesk Server + mv /opt/config.yaml config + rm -f "$temp_file" + echo "${RELEASE}" >/opt/RustDesk Server_version.txt + $STD service RustDesk Server start + msg_ok "Updated Successfully" + else + msg_ok "No update required. ${APP} is already at ${RELEASE}" + fi + + exit 0 +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following IP:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" diff --git a/install/alpine-rustdeskserver-install.sh b/install/alpine-rustdeskserver-install.sh new file mode 100644 index 000000000..d6e33d6de --- /dev/null +++ b/install/alpine-rustdeskserver-install.sh @@ -0,0 +1,111 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/rustdesk/rustdesk-server + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +RELEASE=$(curl -s https://api.github.com/repos/rustdesk/rustdesk-server/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') +msg_info "Installing RustDesk Server v${RELEASE}" +temp_file1=$(mktemp) +curl -fsSL "https://github.com/rustdesk/rustdesk-server/releases/download/${RELEASE}/rustdesk-server-linux-amd64.zip" -o "$temp_file" +unzip "$temp_file1" +mv amd64 /opt/rustdesk-server +mkdir -p /root/.config/rustdesk +cd /opt/rustdesk-server +$STD ./rustdesk-utils genkeypair | tee \ + >(grep "Public Key" | awk '{print $3}' > /root/.config/rustdesk/id_ed25519.pub) \ + >(grep "Secret Key" | awk '{print $3}' > /root/.config/rustdesk/id_ed25519) +chmod 600 /root/.config/rustdesk/id_ed25519 +chmod 644 /root/.config/rustdesk/id_ed25519.pub +echo "${RELEASE}" >~/.rustdesk-server +msg_ok "Installed RustDesk Server v${RELEASE}" + +APIRELEASE=$(curl -s https://api.github.com/repos/lejianwen/rustdesk-api/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') +msg_info "Installing RustDesk API v${APIRELEASE}" +temp_file2=$(mktemp) +curl -fsSL "https://github.com/lejianwen/rustdesk-api/releases/download/v${APIRELEASE}/linux-amd64.tar.gz" -o $temp_file2 +tar zxvf "$temp_file2" +mv release /opt/rustdesk-api +msg_ok "Installed RustDesk API v${APIRELEASE}" + +msg_info "Enabling RustDesk Server Services" +cat </etc/init.d/rustdesk-server-hbbs +#!/sbin/openrc-run +description="RustDesk HBBS Service" +directory="/opt/rustdesk-server" +command="/opt/rustdesk-server/hbbs" +command_args="" +command_background="true" +command_user="root" +pidfile="/var/run/rustdesk-server-hbbs.pid" +output_log="/var/log/rustdesk-hbbs.log" +error_log="/var/log/rustdesk-hbbs.err" + +depend() { + use net +} +EOF + +cat </etc/init.d/rustdesk-server-hbbr +#!/sbin/openrc-run +description="RustDesk HBBR Service" +directory="/opt/rustdesk-server" +command="/opt/rustdesk-server/hbbr" +command_args="" +command_background="true" +command_user="root" +pidfile="/var/run/rustdesk-server-hbbr.pid" +output_log="/var/log/rustdesk-hbbr.log" +error_log="/var/log/rustdesk-hbbr.err" + +depend() { + use net +} +EOF + +cat </etc/init.d/rustdesk-api +#!/sbin/openrc-run +description="RustDesk API Service" +directory="/opt/rustdesk-api" +command="/opt/rustdesk-api/apimain" +command_args="" +command_background="true" +command_user="root" +pidfile="/var/run/rustdesk-server-hbbr.pid" +output_log="/var/log/rustdesk-api.log" +error_log="/var/log/rustdesk-api.err" + +depend() { + use net +} +EOF +chmod +x /etc/init.d/rustdesk-server-hbbs +chmod +x /etc/init.d/rustdesk-server-hbbr +chmod +x /etc/init.d/rustdesk-api +$STD rc-update add rustdesk-server-hbbs default +$STD rc-update add rustdesk-server-hbbr default +$STD rc-update add rustdesk-api default +msg_ok "Enabled RustDesk Server Services" + +msg_info "Starting RustDesk Server" +$STD service rustdesk-server-hbbs start +$STD service rustdesk-server-hbbr start +$STD service rustdesk-api start +msg_ok "Started RustDesk Server" + +motd_ssh +customize + +msg_info "Cleaning up" +rm -f "$temp_file1" "$temp_file2" +$STD apk cache clean +msg_ok "Cleaned" From 5961178d110e1fbe02f3dc969c3a025ee07470be Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 20 Aug 2025 14:00:59 +0200 Subject: [PATCH 0640/1733] Update RustDesk --- ct/alpine-rustdeskserver.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/alpine-rustdeskserver.sh b/ct/alpine-rustdeskserver.sh index 0cf945f7d..c8b2c90b7 100644 --- a/ct/alpine-rustdeskserver.sh +++ b/ct/alpine-rustdeskserver.sh @@ -5,7 +5,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/rustdesk/rustdesk-server -APP="Alpine-RustDesk Server" +APP="Alpine-RustDeskServer" var_tags="${var_tags:-alpine;monitoring}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" From 8ac7b7630dfa906eb8353e09e2d7e3dd2bb08a9e Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 20 Aug 2025 14:12:59 +0200 Subject: [PATCH 0641/1733] Update --- ct/alpine-rustdeskserver.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/alpine-rustdeskserver.sh b/ct/alpine-rustdeskserver.sh index c8b2c90b7..1897d7dfd 100644 --- a/ct/alpine-rustdeskserver.sh +++ b/ct/alpine-rustdeskserver.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE From 2fff0d528b59474bb9ad5dcf073166c1c1545784 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 20 Aug 2025 14:15:07 +0200 Subject: [PATCH 0642/1733] Update --- install/alpine-rustdeskserver-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/alpine-rustdeskserver-install.sh b/install/alpine-rustdeskserver-install.sh index d6e33d6de..12edc0209 100644 --- a/install/alpine-rustdeskserver-install.sh +++ b/install/alpine-rustdeskserver-install.sh @@ -16,7 +16,7 @@ update_os RELEASE=$(curl -s https://api.github.com/repos/rustdesk/rustdesk-server/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') msg_info "Installing RustDesk Server v${RELEASE}" temp_file1=$(mktemp) -curl -fsSL "https://github.com/rustdesk/rustdesk-server/releases/download/${RELEASE}/rustdesk-server-linux-amd64.zip" -o "$temp_file" +curl -fsSL "https://github.com/rustdesk/rustdesk-server/releases/download/${RELEASE}/rustdesk-server-linux-amd64.zip" -o "$temp_file1" unzip "$temp_file1" mv amd64 /opt/rustdesk-server mkdir -p /root/.config/rustdesk @@ -32,7 +32,7 @@ msg_ok "Installed RustDesk Server v${RELEASE}" APIRELEASE=$(curl -s https://api.github.com/repos/lejianwen/rustdesk-api/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') msg_info "Installing RustDesk API v${APIRELEASE}" temp_file2=$(mktemp) -curl -fsSL "https://github.com/lejianwen/rustdesk-api/releases/download/v${APIRELEASE}/linux-amd64.tar.gz" -o $temp_file2 +curl -fsSL "https://github.com/lejianwen/rustdesk-api/releases/download/v${APIRELEASE}/linux-amd64.tar.gz" -o "$temp_file2" tar zxvf "$temp_file2" mv release /opt/rustdesk-api msg_ok "Installed RustDesk API v${APIRELEASE}" From 24169a9eaba9b97224efe50debbab89362fb7125 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 20 Aug 2025 14:22:10 +0200 Subject: [PATCH 0643/1733] Update RustDesk --- install/alpine-rustdeskserver-install.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/install/alpine-rustdeskserver-install.sh b/install/alpine-rustdeskserver-install.sh index 12edc0209..316282097 100644 --- a/install/alpine-rustdeskserver-install.sh +++ b/install/alpine-rustdeskserver-install.sh @@ -21,11 +21,12 @@ unzip "$temp_file1" mv amd64 /opt/rustdesk-server mkdir -p /root/.config/rustdesk cd /opt/rustdesk-server -$STD ./rustdesk-utils genkeypair | tee \ - >(grep "Public Key" | awk '{print $3}' > /root/.config/rustdesk/id_ed25519.pub) \ - >(grep "Secret Key" | awk '{print $3}' > /root/.config/rustdesk/id_ed25519) +./rustdesk-utils genkeypair > /tmp/rustdesk_keys.txt +grep "Public Key" /tmp/rustdesk_keys.txt | awk '{print $3}' > /root/.config/rustdesk/id_ed25519.pub +grep "Secret Key" /tmp/rustdesk_keys.txt | awk '{print $3}' > /root/.config/rustdesk/id_ed25519 chmod 600 /root/.config/rustdesk/id_ed25519 chmod 644 /root/.config/rustdesk/id_ed25519.pub +rm /tmp/rustdesk_keys.txt echo "${RELEASE}" >~/.rustdesk-server msg_ok "Installed RustDesk Server v${RELEASE}" From c19bfbe24025a78fb8909e333b246954111f84f9 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 20 Aug 2025 14:42:31 +0200 Subject: [PATCH 0644/1733] Update RustDesk --- install/alpine-rustdeskserver-install.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/install/alpine-rustdeskserver-install.sh b/install/alpine-rustdeskserver-install.sh index 316282097..c89293355 100644 --- a/install/alpine-rustdeskserver-install.sh +++ b/install/alpine-rustdeskserver-install.sh @@ -17,7 +17,7 @@ RELEASE=$(curl -s https://api.github.com/repos/rustdesk/rustdesk-server/releases msg_info "Installing RustDesk Server v${RELEASE}" temp_file1=$(mktemp) curl -fsSL "https://github.com/rustdesk/rustdesk-server/releases/download/${RELEASE}/rustdesk-server-linux-amd64.zip" -o "$temp_file1" -unzip "$temp_file1" +$STD unzip "$temp_file1" mv amd64 /opt/rustdesk-server mkdir -p /root/.config/rustdesk cd /opt/rustdesk-server @@ -34,7 +34,7 @@ APIRELEASE=$(curl -s https://api.github.com/repos/lejianwen/rustdesk-api/release msg_info "Installing RustDesk API v${APIRELEASE}" temp_file2=$(mktemp) curl -fsSL "https://github.com/lejianwen/rustdesk-api/releases/download/v${APIRELEASE}/linux-amd64.tar.gz" -o "$temp_file2" -tar zxvf "$temp_file2" +$STD tar zxvf "$temp_file2" mv release /opt/rustdesk-api msg_ok "Installed RustDesk API v${APIRELEASE}" @@ -81,7 +81,7 @@ command="/opt/rustdesk-api/apimain" command_args="" command_background="true" command_user="root" -pidfile="/var/run/rustdesk-server-hbbr.pid" +pidfile="/var/run/rustdesk-api.pid" output_log="/var/log/rustdesk-api.log" error_log="/var/log/rustdesk-api.err" From 59a7ddf581e93bee87c4477c1ba0429338b86d46 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 20 Aug 2025 14:53:59 +0200 Subject: [PATCH 0645/1733] Update RustDesk --- install/alpine-rustdeskserver-install.sh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/install/alpine-rustdeskserver-install.sh b/install/alpine-rustdeskserver-install.sh index c89293355..e60652aa9 100644 --- a/install/alpine-rustdeskserver-install.sh +++ b/install/alpine-rustdeskserver-install.sh @@ -36,6 +36,15 @@ temp_file2=$(mktemp) curl -fsSL "https://github.com/lejianwen/rustdesk-api/releases/download/v${APIRELEASE}/linux-amd64.tar.gz" -o "$temp_file2" $STD tar zxvf "$temp_file2" mv release /opt/rustdesk-api +cd /opt/rustdesk-api +ADMINPASS=$(head -c 16 /dev/urandom | xxd -p -c 16) +$STD ./apimain reset-admin-pwd "$ADMINPASS" +{ + echo "RustDesk WebUI" + echo "" + echo "Username: admin" + echo "Password: $ADMINPASS" +} >>~/rustdesk.creds msg_ok "Installed RustDesk API v${APIRELEASE}" msg_info "Enabling RustDesk Server Services" From c668718209ac8707bb296e13bb176d20eeea0705 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 20 Aug 2025 20:48:35 +0200 Subject: [PATCH 0646/1733] Update RustDesk --- ct/alpine-rustdeskserver.sh | 56 ++++++++++++++---------- install/alpine-rustdeskserver-install.sh | 1 + 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/ct/alpine-rustdeskserver.sh b/ct/alpine-rustdeskserver.sh index 1897d7dfd..333548ed5 100644 --- a/ct/alpine-rustdeskserver.sh +++ b/ct/alpine-rustdeskserver.sh @@ -26,29 +26,41 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit 1 fi - RELEASE=$(curl -s https://api.github.com/repos/TwiN/RustDesk Server/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') - if [ "${RELEASE}" != "$(cat /opt/RustDesk Server_version.txt)" ] || [ ! -f /opt/RustDesk Server_version.txt ]; then - msg_info "Updating ${APP} LXC" - $STD apk -U upgrade - $STD service RustDesk Server stop - mv /opt/RustDesk Server/config/config.yaml /opt - rm -rf /opt/RustDesk Server/* - temp_file=$(mktemp) - curl -fsSL "https://github.com/TwiN/RustDesk Server/archive/refs/tags/v${RELEASE}.tar.gz" -o "$temp_file" - tar zxf "$temp_file" --strip-components=1 -C /opt/RustDesk Server - cd /opt/RustDesk Server - $STD go mod tidy - CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o RustDesk Server . - setcap CAP_NET_RAW+ep RustDesk Server - mv /opt/config.yaml config - rm -f "$temp_file" - echo "${RELEASE}" >/opt/RustDesk Server_version.txt - $STD service RustDesk Server start - msg_ok "Updated Successfully" - else - msg_ok "No update required. ${APP} is already at ${RELEASE}" - fi + APIRELEASE=$(curl -s https://api.github.com/repos/lejianwen/rustdesk-api/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') + RELEASE=$(curl -s https://api.github.com/repos/rustdesk/rustdesk-server/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + if [ "${RELEASE}" != "$(cat ~/.rustdesk-server 2>/dev/null)" ] || [ ! -f ~/.rustdesk-server ]; then + msg_info "Updating RustDesk Server to v${RELEASE}" + $STD apk -U upgrade + $STD service rustdesk-server-hbbs stop + $STD service rustdesk-server-hbbr stop + temp_file1=$(mktemp) + curl -fsSL "https://github.com/rustdesk/rustdesk-server/releases/download/${RELEASE}/rustdesk-server-linux-amd64.zip" -o "$temp_file1" + $STD unzip "$temp_file1" + cp -r amd64/* /opt/rustdesk-server/ + echo "${RELEASE}" >~/.rustdesk-server + $STD service rustdesk-server-hbbs start + $STD service rustdesk-server-hbbr start + rm -rf amd64 + rm -f $temp_file1 + msg_ok "Updated RustDesk Server successfully" + else + msg_ok "No update required. ${APP} is already at v${RELEASE}" + fi + if [ "${APIRELEASE}" != "$(cat ~/.rustdesk-api)" ] || [ ! -f ~/.rustdesk-api ]; then + msg_info "Updating RustDesk API to v${APIRELEASE}" + + temp_file2=$(mktemp) + curl -fsSL "https://github.com/lejianwen/rustdesk-api/releases/download/v${APIRELEASE}/linux-amd64.tar.gz" -o "$temp_file2" + $STD tar zxvf "$temp_file2" + cp -r release/* /opt/rustdesk-api + echo "${APIRELEASE}" >~/.rustdesk-api + rm -rf release + rm -f $temp_file2 + msg_ok "Updated RustDesk API" + else + msg_ok "No update required. RustDesk API is already at v${APIRELEASE}" + fi exit 0 } diff --git a/install/alpine-rustdeskserver-install.sh b/install/alpine-rustdeskserver-install.sh index e60652aa9..b26f31347 100644 --- a/install/alpine-rustdeskserver-install.sh +++ b/install/alpine-rustdeskserver-install.sh @@ -45,6 +45,7 @@ $STD ./apimain reset-admin-pwd "$ADMINPASS" echo "Username: admin" echo "Password: $ADMINPASS" } >>~/rustdesk.creds +echo "${APIRELEASE}" >~/.rustdesk-api msg_ok "Installed RustDesk API v${APIRELEASE}" msg_info "Enabling RustDesk Server Services" From 7ec9aa87b8a7f678991999019a4a7f61ba944a3c Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 20 Aug 2025 20:50:50 +0200 Subject: [PATCH 0647/1733] Update RustDesk --- ct/alpine-rustdeskserver.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ct/alpine-rustdeskserver.sh b/ct/alpine-rustdeskserver.sh index 333548ed5..30fa99ab4 100644 --- a/ct/alpine-rustdeskserver.sh +++ b/ct/alpine-rustdeskserver.sh @@ -49,12 +49,13 @@ function update_script() { fi if [ "${APIRELEASE}" != "$(cat ~/.rustdesk-api)" ] || [ ! -f ~/.rustdesk-api ]; then msg_info "Updating RustDesk API to v${APIRELEASE}" - + $STD service rustdesk-api stop temp_file2=$(mktemp) curl -fsSL "https://github.com/lejianwen/rustdesk-api/releases/download/v${APIRELEASE}/linux-amd64.tar.gz" -o "$temp_file2" $STD tar zxvf "$temp_file2" cp -r release/* /opt/rustdesk-api echo "${APIRELEASE}" >~/.rustdesk-api + $STD service rustdesk-api start rm -rf release rm -f $temp_file2 msg_ok "Updated RustDesk API" From 806523023e27c696f7b7791a10ff437951d00558 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 20 Aug 2025 20:58:28 +0200 Subject: [PATCH 0648/1733] Update RustDesk --- ct/alpine-rustdeskserver.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/alpine-rustdeskserver.sh b/ct/alpine-rustdeskserver.sh index 30fa99ab4..9f1b1d003 100644 --- a/ct/alpine-rustdeskserver.sh +++ b/ct/alpine-rustdeskserver.sh @@ -72,4 +72,4 @@ description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following IP:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:21114${CL}" From b6958e6d86b2ec67208ce446904b214933577abe Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 20 Aug 2025 21:01:26 +0200 Subject: [PATCH 0649/1733] Update RustDesk --- ct/alpine-rustdeskserver.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/ct/alpine-rustdeskserver.sh b/ct/alpine-rustdeskserver.sh index 9f1b1d003..1494be484 100644 --- a/ct/alpine-rustdeskserver.sh +++ b/ct/alpine-rustdeskserver.sh @@ -21,7 +21,6 @@ catch_errors function update_script() { header_info - if [[ ! -d /opt/rustdesk-server ]]; then msg_error "No ${APP} Installation Found!" exit 1 From 327d270294bd9ed0440cfe13e883894490698cd8 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 20 Aug 2025 21:11:36 +0200 Subject: [PATCH 0650/1733] Add RustDesk json --- frontend/public/json/rustdeskserver.json | 55 ++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 frontend/public/json/rustdeskserver.json diff --git a/frontend/public/json/rustdeskserver.json b/frontend/public/json/rustdeskserver.json new file mode 100644 index 000000000..7dd43605c --- /dev/null +++ b/frontend/public/json/rustdeskserver.json @@ -0,0 +1,55 @@ +{ + "name": "RustDesk Server", + "slug": "rustdeskserver", + "categories": [ + 21 + ], + "date_created": "2025-02-13", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 21114, + "documentation": "https://rustdesk.com/docs/en/", + "website": "https://rustdesk.com/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/rustdesk.webp", + "config_path": "", + "description": "RustDesk is a full-featured open source remote control alternative for self-hosting and security with minimal configuration.", + "install_methods": [ + { + "type": "default", + "script": "ct/rustdeskserver.sh", + "resources": { + "cpu": 1, + "ram": 512, + "hdd": 2, + "os": "debian", + "version": "12" + } + }, + { + "type": "alpine", + "script": "ct/alpine-rustdeskserver.sh", + "resources": { + "cpu": 1, + "ram": 512, + "hdd": 2, + "os": "alpine", + "version": "3.22" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Check our configuration guide for help: `https://github.com/community-scripts/ProxmoxVE/discussions/2388`", + "type": "info" + }, + { + "text": "Login credentials: `cat ~/rustdesk.creds`", + "type": "info" + } + ] +} From 3f102060c037e6310355190ff205c3408503f2a7 Mon Sep 17 00:00:00 2001 From: Henrique Goncalves Date: Tue, 12 Aug 2025 07:30:00 -0300 Subject: [PATCH 0651/1733] feat: add dependency-check --- frontend/public/json/dependency-check.json | 48 +++ tools/pve/dependency-check.sh | 363 +++++++++++++++++++++ 2 files changed, 411 insertions(+) create mode 100644 frontend/public/json/dependency-check.json create mode 100644 tools/pve/dependency-check.sh diff --git a/frontend/public/json/dependency-check.json b/frontend/public/json/dependency-check.json new file mode 100644 index 000000000..13b31fe15 --- /dev/null +++ b/frontend/public/json/dependency-check.json @@ -0,0 +1,48 @@ +{ + "name": "Proxmox VE VM Startup Dependency Check", + "slug": "dependency-check", + "categories": [ + 1 + ], + "date_created": "2025-08-12", + "type": "pve", + "updateable": false, + "privileged": false, + "interface_port": null, + "documentation": null, + "website": null, + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/proxmox.webp", + "config_path": "/etc/default/pve-auto-hook", + "description": "This script checks for the presence of required dependencies before starting a VM or LXC container in Proxmox. It ensures that all referenced storages are available and, additionally, supports the usage of tags to check for specific dependencies. If any required dependency is missing, the VM or container will not start until the issue is resolved. This script is designed to be used as a Proxmox hookscript, which can be applied to both QEMU VMs and LXC containers.", + "install_methods": [ + { + "type": "default", + "script": "tools/pve/dependency-check.sh", + "resources": { + "cpu": null, + "ram": null, + "hdd": null, + "os": null, + "version": null + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Execute within the Proxmox shell", + "type": "info" + }, + { + "text": "To wait until a certain host is available, tag the VM or container with `dep_ping_` where `` is the name or IP of the host to ping. The script will wait until the host is reachable before proceeding with the startup.", + "type": "info" + }, + { + "text": "To wait until a certain TCP port is open, tag the VM or container with `dep_tcp__` where `` is the name or IP of the host and `` is the TCP port number. The script will wait until the port is open before proceeding with the startup.", + "type": "info" + } + ] +} diff --git a/tools/pve/dependency-check.sh b/tools/pve/dependency-check.sh new file mode 100644 index 000000000..b7798d95a --- /dev/null +++ b/tools/pve/dependency-check.sh @@ -0,0 +1,363 @@ +#!/usr/bin/env bash + +# Copyright (c) 2023 community-scripts ORG +# This script is designed to install the Proxmox Dependency Check Hookscript. +# It sets up a dependency-checking hookscript and automates its +# application to all new and existing guests using a systemd watcher. +# License: MIT + +function header_info { + clear + cat <<"EOF" + ____ _ ____ _ _ + | _ \ ___ _ __ ___ _ __ __| | ___ _ __ ___ _ _ / ___| |__ ___ ___| | __ + | | | |/ _ \ '_ \ / _ \ '_ \ / _` |/ _ \ '_ \ / __| | | | | | '_ \ / _ \/ __| |/ / + | |_| | __/ |_) | __/ | | | (_| | __/ | | | (__| |_| | |___| | | | __/ (__| < + |____/ \___| .__/ \___|_| |_|\__,_|\___|_| |_|\___|\__, |\____|_| |_|\___|\___|_|\_\ + |_| |___/ +EOF +} + +# Color variables +YW=$(echo "\033[33m") +GN=$(echo "\033[1;92m") +RD=$(echo "\033[01;31m") +CL=$(echo "\033[m") +BFR="\\r\\033[K" +HOLD=" " +CM="${GN}✓${CL}" +CROSS="${RD}✗${CL}" + +# Spinner for progress indication (simplified) +spinner() { + local pid=$! + local delay=0.1 + local spinstr='|/-\' + while [ "$(ps a | awk '{print $1}' | grep $pid)" ]; do + local temp=${spinstr#?} + printf " [%c] " "$spinstr" + local spinstr=$temp${spinstr%"$temp"} + sleep $delay + printf "\b\b\b\b\b\b" + done + printf " \b\b\b\b" +} + +# Message functions +msg_info() { + echo -ne " ${YW}›${CL} $1..." +} + +msg_ok() { + echo -e "${BFR} ${CM} $1${CL}" +} + +msg_error() { + echo -e "${BFR} ${CROSS} $1${CL}" +} +# --- End of base script functions --- + + +# --- Installation Functions --- + +# Function to create the actual hookscript that runs before guest startup +create_dependency_hookscript() { + msg_info "Creating dependency-check hookscript" + mkdir -p /var/lib/vz/snippets + cat <<'EOF' > /var/lib/vz/snippets/dependency-check.sh +#!/bin/bash +# Proxmox Hookscript for Pre-Start Dependency Checking +# Works for both QEMU VMs and LXC Containers + +# --- Configuration --- +POLL_INTERVAL=5 # Seconds to wait between checks +MAX_ATTEMPTS=60 # Max number of attempts before failing (60 * 5s = 5 minutes) +# --- End Configuration --- + +VMID=$1 +PHASE=$2 + +# Function for logging to syslog with a consistent format +log() { + echo "[hookscript-dep-check] VMID $VMID: $1" +} + +# This script only runs in the 'pre-start' phase +if [ "$PHASE" != "pre-start" ]; then + exit 0 +fi + +log "--- Starting Pre-Start Dependency Check ---" + +# --- Determine Guest Type (QEMU or LXC) --- +GUEST_TYPE="" +CONFIG_CMD="" +if qm config "$VMID" >/dev/null 2>&1; then + GUEST_TYPE="qemu" + CONFIG_CMD="qm config" + log "Guest type is QEMU (VM)." +elif pct config "$VMID" >/dev/null 2>&1; then + GUEST_TYPE="lxc" + CONFIG_CMD="pct config" + log "Guest type is LXC (Container)." +else + log "ERROR: Could not determine guest type for $VMID. Aborting." + exit 1 +fi + +GUEST_CONFIG=$($CONFIG_CMD "$VMID") + +# --- 1. Storage Availability Check --- +log "Checking storage availability..." +# Grep for all disk definitions (scsi, sata, virtio, ide, rootfs, mp) +# and extract the storage identifier (the field between the colons). +# Sort -u gets the unique list of storage pools. +STORAGE_IDS=$(echo "$GUEST_CONFIG" | grep -E '^(scsi|sata|virtio|ide|rootfs|mp)[0-9]*:' | awk -F'[:]' '{print $2}' | awk '{print$1}' | sort -u) + +if [ -z "$STORAGE_IDS" ]; then + log "No storage dependencies found to check." +else + for STORAGE_ID in $STORAGE_IDS; do + log "Checking status of storage: '$STORAGE_ID'" + ATTEMPTS=0 + while true; do + # Grep for the storage ID line in pvesm status and check the 'Active' column (3rd column) + STATUS=$(pvesm status | grep "^\s*$STORAGE_ID\s" | awk '{print $3}') + if [ "$STATUS" == "active" ]; then + log "Storage '$STORAGE_ID' is active." + break + fi + + ATTEMPTS=$((ATTEMPTS + 1)) + if [ $ATTEMPTS -ge $MAX_ATTEMPTS ]; then + log "ERROR: Timeout waiting for storage '$STORAGE_ID' to become active. Aborting start." + exit 1 + fi + + log "Storage '$STORAGE_ID' is not active (current status: '${STATUS:-inactive/unknown}'). Waiting ${POLL_INTERVAL}s... (Attempt ${ATTEMPTS}/${MAX_ATTEMPTS})" + sleep $POLL_INTERVAL + done + done +fi +log "All storage dependencies are met." + + +# --- 2. Custom Tag-Based Dependency Check --- +log "Checking for custom tag-based dependencies..." +TAGS=$(echo "$GUEST_CONFIG" | grep '^tags:' | awk '{print $2}') + +if [ -z "$TAGS" ]; then + log "No tags found. Skipping custom dependency check." +else + # Replace colons with spaces to loop through tags + for TAG in ${TAGS//;/ }; do + # Check if the tag matches our dependency format 'dep_*' + if [[ $TAG == dep_* ]]; then + log "Found dependency tag: '$TAG'" + + # Split tag into parts using underscore as delimiter + IFS='_' read -ra PARTS <<< "$TAG" + DEP_TYPE="${PARTS[1]}" + + ATTEMPTS=0 + while true; do + CHECK_PASSED=false + case "$DEP_TYPE" in + "tcp") + HOST="${PARTS[2]}" + PORT="${PARTS[3]}" + if [ -z "$HOST" ] || [ -z "$PORT" ]; then + log "ERROR: Malformed TCP dependency tag '$TAG'. Skipping." + CHECK_PASSED=true # Skip to avoid infinite loop + # nc -z is great for this. -w sets a timeout. + elif nc -z -w 2 "$HOST" "$PORT"; then + log "TCP dependency met: Host $HOST port $PORT is open." + CHECK_PASSED=true + fi + ;; + + "ping") + HOST="${PARTS[2]}" + if [ -z "$HOST" ]; then + log "ERROR: Malformed PING dependency tag '$TAG'. Skipping." + CHECK_PASSED=true # Skip to avoid infinite loop + # ping -c 1 (one packet) -W 2 (2-second timeout) + elif ping -c 1 -W 2 "$HOST" >/dev/null 2>&1; then + log "Ping dependency met: Host $HOST is reachable." + CHECK_PASSED=true + fi + ;; + + *) + log "WARNING: Unknown dependency type '$DEP_TYPE' in tag '$TAG'. Ignoring." + CHECK_PASSED=true # Mark as passed to avoid getting stuck + ;; + esac + + if $CHECK_PASSED; then + break + fi + + ATTEMPTS=$((ATTEMPTS + 1)) + if [ $ATTEMPTS -ge $MAX_ATTEMPTS ]; then + log "ERROR: Timeout waiting for dependency '$TAG'. Aborting start." + exit 1 + fi + + log "Dependency '$TAG' not met. Waiting ${POLL_INTERVAL}s... (Attempt ${ATTEMPTS}/${MAX_ATTEMPTS})" + sleep $POLL_INTERVAL + done + fi + done +fi + +log "All custom dependencies are met." +log "--- Dependency Check Complete. Proceeding with start. ---" +exit 0 +EOF + chmod +x /var/lib/vz/snippets/dependency-check.sh + msg_ok "Created dependency-check hookscript" +} + +# Function to create the config file for exclusions +create_exclusion_config() { + msg_info "Creating exclusion configuration file" + if [ -f /etc/default/pve-auto-hook ]; then + msg_ok "Exclusion file already exists, skipping." + else + cat <<'EOF' > /etc/default/pve-auto-hook +# +# Configuration for the Proxmox Automatic Hookscript Applicator +# +# Add VM or LXC IDs here to prevent the hookscript from being added. +# Separate IDs with spaces. +# +# Example: +# IGNORE_IDS="9000 9001 105" +# + +IGNORE_IDS="" +EOF + msg_ok "Created exclusion configuration file" + fi +} + +# Function to create the script that applies the hook +create_applicator_script() { + msg_info "Creating the hookscript applicator script" + cat <<'EOF' > /usr/local/bin/pve-apply-hookscript.sh +#!/bin/bash +HOOKSCRIPT_VOLUME_ID="local:snippets/dependency-check.sh" +CONFIG_FILE="/etc/default/pve-auto-hook" +LOG_TAG="pve-auto-hook-list" + +log() { + systemd-cat -t "$LOG_TAG" <<< "$1" +} + +if [ -f "$CONFIG_FILE" ]; then + source "$CONFIG_FILE" +fi + +# Process QEMU VMs +qm list | awk 'NR>1 {print $1}' | while read -r VMID; do + is_ignored=false + for id_to_ignore in $IGNORE_IDS; do + if [ "$id_to_ignore" == "$VMID" ]; then is_ignored=true; break; fi + done + if $is_ignored; then continue; fi + if qm config "$VMID" | grep -q '^hookscript:'; then continue; fi + log "Hookscript not found for VM $VMID. Applying..." + qm set "$VMID" --hookscript "$HOOKSCRIPT_VOLUME_ID" +done + +# Process LXC Containers +pct list | awk 'NR>1 {print $1}' | while read -r VMID; do + is_ignored=false + for id_to_ignore in $IGNORE_IDS; do + if [ "$id_to_ignore" == "$VMID" ]; then is_ignored=true; break; fi + done + if $is_ignored; then continue; fi + if pct config "$VMID" | grep -q '^hookscript:'; then continue; fi + log "Hookscript not found for LXC $VMID. Applying..." + pct set "$VMID" --hookscript "$HOOKSCRIPT_VOLUME_ID" +done +EOF + chmod +x /usr/local/bin/pve-apply-hookscript.sh + msg_ok "Created applicator script" +} + +# Function to set up the systemd watcher and service +create_systemd_units() { + msg_info "Creating systemd watcher and service units" + cat <<'EOF' > /etc/systemd/system/pve-auto-hook.path +[Unit] +Description=Watch for new Proxmox guest configs to apply hookscript + +[Path] +PathModified=/etc/pve/qemu-server/ +PathModified=/etc/pve/lxc/ + +[Install] +WantedBy=multi-user.target +EOF + + cat <<'EOF' > /etc/systemd/system/pve-auto-hook.service +[Unit] +Description=Automatically add hookscript to new Proxmox guests + +[Service] +Type=oneshot +ExecStart=/usr/local/bin/pve-apply-hookscript.sh +EOF + msg_ok "Created systemd units" +} + + +# --- Main Execution --- +header_info + +if ! command -v pveversion >/dev/null 2>&1; then + msg_error "This script must be run on a Proxmox VE host." + exit 1 +fi + +echo -e "\nThis script will install a service to automatically apply a" +echo -e "dependency-checking hookscript to all new and existing Proxmox guests." +echo -e "${YW}This includes creating files in:${CL}" +echo -e " - /var/lib/vz/snippets/" +echo -e " - /usr/local/bin/" +echo -e " - /etc/default/" +echo -e " - /etc/systemd/system/\n" + +read -p "Do you want to proceed with the installation? (y/n): " -n 1 -r +echo +if [[ ! $REPLY =~ ^[Yy]$ ]]; then + msg_error "Installation cancelled." + exit 1 +fi + +echo -e "\n" +create_dependency_hookscript +create_exclusion_config +create_applicator_script +create_systemd_units + +msg_info "Reloading systemd and enabling the watcher" +(systemctl daemon-reload && systemctl enable --now pve-auto-hook.path) >/dev/null 2>&1 & +spinner +msg_ok "Systemd watcher enabled and running" + +msg_info "Performing initial run to update existing guests" +/usr/local/bin/pve-apply-hookscript.sh >/dev/null 2>&1 & +spinner +msg_ok "Initial run complete" + +echo -e "\n\n${GN}Installation successful!${CL}" +echo -e "The service is now active and will monitor for new guests." +echo -e "To ${YW}exclude${CL} a VM or LXC, add its ID to the ${YW}IGNORE_IDS${CL} variable in:" +echo -e " ${YW}/etc/default/pve-auto-hook${CL}" +echo -e "\nYou can monitor the service's activity with:" +echo -e " ${YW}journalctl -fu pve-auto-hook.service${CL}\n" + +exit 0 From ed551624e31346a58065e0cf164f9a8e367e3b8c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 21 Aug 2025 09:05:00 +0200 Subject: [PATCH 0652/1733] Update tools.func --- misc/tools.func | 44 ++++++++++++++++---------------------------- 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 7c7e54926..697f69fcd 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -901,68 +901,56 @@ function fetch_and_deploy_gh_release() { local assets url_match="" assets=$(echo "$json" | jq -r '.assets[].browser_download_url') - echo "[DEBUG] Listing all available assets from release:" - for u in $assets; do - echo " -> $u" - done - - # 1. Pattern match + # If explicit filename pattern is provided (param $6), match that first if [[ -n "$asset_pattern" ]]; then for u in $assets; do - filename_candidate="${u##*/}" - if [[ "$filename_candidate" == *"$asset_pattern"* ]]; then + case "${u##*/}" in + $asset_pattern) url_match="$u" break - fi + ;; + esac done fi - # 2. Arch match (only if no pattern match) + # If no match via explicit pattern, fall back to architecture heuristic if [[ -z "$url_match" ]]; then for u in $assets; do - filename_candidate="${u##*/}" - echo " [DEBUG] Checking asset: $filename_candidate" - if [[ "$filename_candidate" == *"$arch"* && "$filename_candidate" == *.deb ]]; then + if [[ "$u" =~ ($arch|amd64|x86_64|aarch64|arm64).*\.deb$ ]]; then url_match="$u" break fi done fi - # 3. Fallback + # Fallback: any .deb file if [[ -z "$url_match" ]]; then for u in $assets; do - filename_candidate="${u##*/}" - if [[ "$filename_candidate" == *.deb ]]; then - url_match="$u" - break - fi + [[ "$u" =~ \.deb$ ]] && url_match="$u" && break done fi if [[ -z "$url_match" ]]; then - echo "[DEBUG] ❌ No suitable .deb asset found!" + msg_error "No suitable .deb asset found for $app" rm -rf "$tmpdir" return 1 fi - echo "[DEBUG] Final selected asset: $url_match" filename="${url_match##*/}" - curl $download_timeout -fsSL -o "$tmpdir/$filename" "$url_match" || { - echo "[DEBUG] ❌ Download failed: $url_match" + msg_error "Download failed: $url_match" rm -rf "$tmpdir" return 1 } chmod 644 "$tmpdir/$filename" - if ! $STD apt-get install -y "$tmpdir/$filename"; then - if ! $STD dpkg -i "$tmpdir/$filename"; then - echo "[DEBUG] ❌ Both apt and dpkg installation failed" + $STD apt-get install -y "$tmpdir/$filename" || { + $STD dpkg -i "$tmpdir/$filename" || { + msg_error "Both apt and dpkg installation failed" rm -rf "$tmpdir" return 1 - fi - fi + } + } ### Prebuild Mode ### elif [[ "$mode" == "prebuild" ]]; then From cdf6605ad05479ecc7431605a8c1cc10f32b1b89 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 21 Aug 2025 10:23:42 +0200 Subject: [PATCH 0653/1733] Delete copyparty.json --- frontend/public/json/copyparty.json | 40 ----------------------------- 1 file changed, 40 deletions(-) delete mode 100644 frontend/public/json/copyparty.json diff --git a/frontend/public/json/copyparty.json b/frontend/public/json/copyparty.json deleted file mode 100644 index bc1fb40c0..000000000 --- a/frontend/public/json/copyparty.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "Copyparty", - "slug": "copyparty", - "categories": [ - 1 - ], - "date_created": "2025-08-18", - "type": "addon", - "updateable": true, - "privileged": false, - "interface_port": null, - "documentation": "https://github.com/9001/copyparty?tab=readme-ov-file#the-browser", - "website": "https://github.com/9001/copyparty", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/copyparty.webp", - "config_path": "", - "description": "Copyparty is a lightweight, portable HTTP file server with a browser-based interface. It supports drag-and-drop uploads, downloads, deduplication, media playback, and advanced search, making it ideal for quickly sharing and managing files.", - "install_methods": [ - { - "type": "default", - "script": "tools/addon/copyparty.sh", - "resources": { - "cpu": null, - "ram": null, - "hdd": null, - "os": null, - "version": null - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "Execute within the Proxmox shell or in LXC", - "type": "info" - } - ] -} From af8adf0dfea7efca06f55d17262b7b29d52f11fa Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 21 Aug 2025 10:24:19 +0200 Subject: [PATCH 0654/1733] Delete add-iptag.json --- frontend/public/json/add-iptag.json | 49 ----------------------------- 1 file changed, 49 deletions(-) delete mode 100644 frontend/public/json/add-iptag.json diff --git a/frontend/public/json/add-iptag.json b/frontend/public/json/add-iptag.json deleted file mode 100644 index da84c8509..000000000 --- a/frontend/public/json/add-iptag.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "name": "Proxmox VE LXC IP-Tag", - "slug": "add-lxc-iptag", - "categories": [ - 1 - ], - "date_created": "2025-04-02", - "type": "addon", - "updateable": false, - "privileged": false, - "interface_port": null, - "documentation": null, - "website": null, - "logo": "https://raw.githubusercontent.com/selfhst/icons/refs/heads/main/svg/proxmox.svg", - "config_path": "", - "description": "This script automatically adds IP address as tags to LXC containers using a Systemd service. The service also updates the tags if a LXC IP address is changed.", - "install_methods": [ - { - "type": "default", - "script": "tools/addon/add-iptag.sh", - "resources": { - "cpu": null, - "ram": null, - "hdd": null, - "os": null, - "version": null - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "Execute within the Proxmox shell", - "type": "info" - }, - { - "text": "Configuration: `nano /opt/iptag/iptag.conf`. iptag.service must be restarted after change.", - "type": "info" - }, - { - "text": "The Proxmox Node must contain ipcalc and net-tools. `apt-get install -y ipcalc net-tools`", - "type": "warning" - } - ] -} - From 4c83fe789909c357686a065a00c1d609f4307fdc Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 21 Aug 2025 10:27:22 +0200 Subject: [PATCH 0655/1733] removed --- ct/paperless-ngx.sh | 141 --------------------- install/paperless-ngx-install.sh | 203 ------------------------------- 2 files changed, 344 deletions(-) delete mode 100644 ct/paperless-ngx.sh delete mode 100644 install/paperless-ngx-install.sh diff --git a/ct/paperless-ngx.sh b/ct/paperless-ngx.sh deleted file mode 100644 index 8c0b38e8c..000000000 --- a/ct/paperless-ngx.sh +++ /dev/null @@ -1,141 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://docs.paperless-ngx.com/ - -APP="Paperless-ngx" -var_tags="${var_tags:-document;management}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-10}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/paperless ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - if ! command -v jq &>/dev/null; then - $STD apt-get install -y jq - fi - RELEASE=$(curl -fsSL https://api.github.com/repos/paperless-ngx/paperless-ngx/releases/latest | jq -r .tag_name | sed 's/^v//') - if [[ "${RELEASE}" != "$(cat ~/.paperless 2>/dev/null)" ]] || [[ ! -f ~/.paperless ]]; then - msg_info "Stopping all Paperless-ngx Services" - systemctl stop paperless-consumer paperless-webserver paperless-scheduler paperless-task-queue - msg_ok "Stopped all Paperless-ngx Services" - - if grep -q "uv run" /etc/systemd/system/paperless-webserver.service; then - - msg_info "backing up data" - mkdir -p /opt/paperless/backup - cp -r /opt/paperless/data /opt/paperless/backup/ - cp -r /opt/paperless/media /opt/paperless/backup/ - cp -r /opt/paperless/paperless.conf /opt/paperless/backup/ - msg_ok "Backup completed" - - PYTHON_VERSION="3.13" setup_uv - fetch_and_deploy_gh_release "paperless" "paperless-ngx/paperless-ngx" "prebuild" "latest" "/opt/paperless" "paperless*tar.xz" - fetch_and_deploy_gh_release "jbig2enc" "ie13/jbig2enc" "tarball" "latest" "/opt/jbig2enc" - setup_gs - - msg_info "Updating to ${RELEASE}" - cp -r /opt/paperless/backup/* /opt/paperless/ - cd /opt/paperless - $STD uv sync --all-extras - cd /opt/paperless/src - $STD uv run -- python manage.py migrate - msg_ok "Updated to ${RELEASE}" - else - msg_warn "You are about to migrate your Paperless-ngx installation to uv!" - msg_custom "🔒" "It is strongly recommended to take a Proxmox snapshot first:" - echo -e " 1. Stop the container: pct stop " - echo -e " 2. Create a snapshot: pct snapshot pre-paperless-uv-migration" - echo -e " 3. Start the container again\n" - - read -rp "Have you created a snapshot? [y/N]: " confirm - if [[ ! "$confirm" =~ ^([yY]|[yY][eE][sS])$ ]]; then - msg_error "Migration aborted. Please create a snapshot first." - exit 1 - fi - msg_info "Migrating old Paperless-ngx installation to uv" - rm -rf /opt/paperless/venv - find /opt/paperless -name "__pycache__" -type d -exec rm -rf {} + - - declare -A PATCHES=( - ["paperless-consumer.service"]="ExecStart=uv run -- python manage.py document_consumer" - ["paperless-scheduler.service"]="ExecStart=uv run -- celery --app paperless beat --loglevel INFO" - ["paperless-task-queue.service"]="ExecStart=uv run -- celery --app paperless worker --loglevel INFO" - ["paperless-webserver.service"]="ExecStart=uv run -- granian --interface asgi --ws \"paperless.asgi:application\"" - ) - - for svc in "${!PATCHES[@]}"; do - path=$(systemctl show -p FragmentPath "$svc" | cut -d= -f2) - if [[ -n "$path" && -f "$path" ]]; then - sed -i "s|^ExecStart=.*|${PATCHES[$svc]}|" "$path" - if [[ "$svc" == "paperless-webserver.service" ]]; then - grep -q "^Environment=GRANIAN_HOST=" "$path" || - sed -i '/^\[Service\]/a Environment=GRANIAN_HOST=::' "$path" - grep -q "^Environment=GRANIAN_PORT=" "$path" || - sed -i '/^\[Service\]/a Environment=GRANIAN_PORT=8000' "$path" - grep -q "^Environment=GRANIAN_WORKERS=" "$path" || - sed -i '/^\[Service\]/a Environment=GRANIAN_WORKERS=1' "$path" - fi - msg_ok "Patched $svc" - else - msg_error "Service file for $svc not found!" - fi - done - - $STD systemctl daemon-reload - msg_info "backing up data" - mkdir -p /opt/paperless/backup - cp -r /opt/paperless/data /opt/paperless/backup/ - cp -r /opt/paperless/media /opt/paperless/backup/ - cp -r /opt/paperless/paperless.conf /opt/paperless/backup/ - msg_ok "Backup completed" - - PYTHON_VERSION="3.13" setup_uv - fetch_and_deploy_gh_release "paperless" "paperless-ngx/paperless-ngx" "prebuild" "latest" "/opt/paperless" "paperless*tar.xz" - fetch_and_deploy_gh_release "jbig2enc" "ie13/jbig2enc" "tarball" "latest" "/opt/jbig2enc" - setup_gs - - msg_info "Updating Paperless-ngx" - cp -r /opt/paperless/backup/* /opt/paperless/ - cd /opt/paperless - $STD uv sync --all-extras - cd /opt/paperless/src - $STD uv run -- python manage.py migrate - msg_ok "Paperless-ngx migration and update to ${RELEASE} completed" - fi - - msg_info "Starting all Paperless-ngx Services" - systemctl start paperless-consumer paperless-webserver paperless-scheduler paperless-task-queue - sleep 1 - msg_ok "Started all Paperless-ngx Services" - msg_ok "Updated Successfully!\n" - else - msg_ok "No update required. ${APP} is already at v${RELEASE}" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}" diff --git a/install/paperless-ngx-install.sh b/install/paperless-ngx-install.sh deleted file mode 100644 index d668909fc..000000000 --- a/install/paperless-ngx-install.sh +++ /dev/null @@ -1,203 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) | MickLesk (CanbiZ) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://docs.paperless-ngx.com/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies (Patience)" -$STD apt-get install -y \ - redis \ - build-essential \ - imagemagick \ - fonts-liberation \ - optipng \ - libpq-dev \ - libmagic-dev \ - mime-support \ - libzbar0 \ - poppler-utils \ - default-libmysqlclient-dev \ - automake \ - libtool \ - pkg-config \ - libtiff-dev \ - libpng-dev \ - libleptonica-dev -msg_ok "Installed Dependencies" - -PG_VERSION="16" setup_postgresql -PYTHON_VERSION="3.13" setup_uv -fetch_and_deploy_gh_release "paperless" "paperless-ngx/paperless-ngx" "prebuild" "latest" "/opt/paperless" "paperless*tar.xz" -fetch_and_deploy_gh_release "jbig2enc" "ie13/jbig2enc" "tarball" "latest" "/opt/jbig2enc" -setup_gs - -msg_info "Installing OCR Dependencies (Patience)" -$STD apt-get install -y \ - unpaper \ - icc-profiles-free \ - qpdf \ - liblept5 \ - libxml2 \ - pngquant \ - zlib1g \ - tesseract-ocr \ - tesseract-ocr-eng -$STD sudo make install -msg_ok "Installed OCR Dependencies" - -msg_info "Setup JBIG2" -cd /opt/jbig2enc -$STD bash ./autogen.sh -$STD bash ./configure -$STD make -$STD make install -rm -rf /opt/jbig2enc -msg_ok "Installed JBIG2" - -msg_info "Setting up PostgreSQL database" -DB_NAME=paperlessdb -DB_USER=paperless -DB_PASS="$(openssl rand -base64 18 | cut -c1-13)" -SECRET_KEY="$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 32)" -$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" -$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC'" -echo "" >>~/paperless.creds -echo -e "Paperless-ngx Database User: \e[32m$DB_USER\e[0m" >>~/paperless.creds -echo -e "Paperless-ngx Database Password: \e[32m$DB_PASS\e[0m" >>~/paperless.creds -echo -e "Paperless-ngx Database Name: \e[32m$DB_NAME\e[0m" >>~/paperless.creds - -msg_info "Installing Natural Language Toolkit (Patience)" -$STD uv run -- python -m nltk.downloader -d /usr/share/nltk_data all -sed -i -e 's/rights="none" pattern="PDF"/rights="read|write" pattern="PDF"/' /etc/ImageMagick-6/policy.xml -msg_ok "Installed Natural Language Toolkit" - -msg_info "Setup Paperless-ngx" -cd /opt/paperless -$STD uv sync --all-extras -curl -fsSL "https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/paperless.conf.example" -o /opt/paperless/paperless.conf -mkdir -p {consume,data,media,static} -sed -i \ - -e 's|#PAPERLESS_REDIS=redis://localhost:6379|PAPERLESS_REDIS=redis://localhost:6379|' \ - -e "s|#PAPERLESS_CONSUMPTION_DIR=../consume|PAPERLESS_CONSUMPTION_DIR=/opt/paperless/consume|" \ - -e "s|#PAPERLESS_DATA_DIR=../data|PAPERLESS_DATA_DIR=/opt/paperless/data|" \ - -e "s|#PAPERLESS_MEDIA_ROOT=../media|PAPERLESS_MEDIA_ROOT=/opt/paperless/media|" \ - -e "s|#PAPERLESS_STATICDIR=../static|PAPERLESS_STATICDIR=/opt/paperless/static|" \ - -e 's|#PAPERLESS_DBHOST=localhost|PAPERLESS_DBHOST=localhost|' \ - -e 's|#PAPERLESS_DBPORT=5432|PAPERLESS_DBPORT=5432|' \ - -e "s|#PAPERLESS_DBNAME=paperless|PAPERLESS_DBNAME=$DB_NAME|" \ - -e "s|#PAPERLESS_DBUSER=paperless|PAPERLESS_DBUSER=$DB_USER|" \ - -e "s|#PAPERLESS_DBPASS=paperless|PAPERLESS_DBPASS=$DB_PASS|" \ - -e "s|#PAPERLESS_SECRET_KEY=change-me|PAPERLESS_SECRET_KEY=$SECRET_KEY|" \ - /opt/paperless/paperless.conf -cd /opt/paperless/src -$STD uv run -- python manage.py migrate -msg_ok "Setup Paperless-ngx" - -msg_info "Setting up admin Paperless-ngx User & Password" -cat <>~/paperless-ngx.creds -msg_ok "Set up admin Paperless-ngx User & Password" - -msg_info "Creating Services" -cat </etc/systemd/system/paperless-scheduler.service -[Unit] -Description=Paperless Celery beat -Requires=redis.service - -[Service] -WorkingDirectory=/opt/paperless/src -ExecStart=uv run -- celery --app paperless beat --loglevel INFO - -[Install] -WantedBy=multi-user.target -EOF - -cat </etc/systemd/system/paperless-task-queue.service -[Unit] -Description=Paperless Celery Workers -Requires=redis.service -After=postgresql.service - -[Service] -WorkingDirectory=/opt/paperless/src -ExecStart=uv run -- celery --app paperless worker --loglevel INFO - -[Install] -WantedBy=multi-user.target -EOF - -cat </etc/systemd/system/paperless-consumer.service -[Unit] -Description=Paperless consumer -Requires=redis.service - -[Service] -WorkingDirectory=/opt/paperless/src -ExecStartPre=/bin/sleep 2 -ExecStart=uv run -- python manage.py document_consumer - -[Install] -WantedBy=multi-user.target -EOF - -cat </etc/systemd/system/paperless-webserver.service -[Unit] -Description=Paperless webserver -After=network.target -Wants=network.target -Requires=redis.service - -[Service] -WorkingDirectory=/opt/paperless/src -ExecStart=uv run -- granian --interface asginl --ws "paperless.asgi:application" -Environment=GRANIAN_HOST=:: -Environment=GRANIAN_PORT=8000 -Environment=GRANIAN_WORKERS=1 - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now paperless-webserver paperless-scheduler paperless-task-queue paperless-consumer -msg_ok "Created Services" - -read -r -p "${TAB3}Would you like to add Adminer? " prompt -if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then - setup_adminer -fi - -motd_ssh -customize - -msg_info "Cleaning up" -rm -rf /opt/paperless/docker -rm -rf /tmp/ghostscript* -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" From 55efd85a1127b450a320adc4146508398b4b524a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 21 Aug 2025 10:31:39 +0200 Subject: [PATCH 0656/1733] Update create_lxc.sh --- misc/create_lxc.sh | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index c986bd7b1..72200f8fa 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -6,7 +6,7 @@ # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # This sets verbose mode if the global variable is set to "yes" -if [ "$VERBOSE" == "yes" ]; then set -x; fi +if [ "$CREATE_LXC_VERBOSE" == "yes" ]; then set -x; fi if command -v curl >/dev/null 2>&1; then source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) @@ -255,18 +255,17 @@ fi # Update LXC template list TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" case "$PCT_OSTYPE" in - debian|ubuntu) - TEMPLATE_PATTERN="-standard_" - ;; - alpine|fedora|rocky|centos) - TEMPLATE_PATTERN="-default_" - ;; - *) - TEMPLATE_PATTERN="" - ;; +debian | ubuntu) + TEMPLATE_PATTERN="-standard_" + ;; +alpine | fedora | rocky | centos) + TEMPLATE_PATTERN="-default_" + ;; +*) + TEMPLATE_PATTERN="" + ;; esac - # 1. Check local templates first msg_info "Searching for template '$TEMPLATE_SEARCH'" mapfile -t TEMPLATES < <( @@ -284,12 +283,11 @@ else pveam update >/dev/null 2>&1 && pveam available -section system | sed -n "s/.*\($TEMPLATE_SEARCH.*$TEMPLATE_PATTERN.*\)/\1/p" | - sort -t - -k 2 -V + sort -t - -k 2 -V ) TEMPLATE_SOURCE="online" fi - TEMPLATE="${TEMPLATES[-1]}" TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || echo "/var/lib/vz/template/cache/$TEMPLATE")" From ab311f2bd2ca98b68c0dd6157d866ad37af13bc9 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 21 Aug 2025 11:10:35 +0200 Subject: [PATCH 0657/1733] Update healthchecks-install.sh --- install/healthchecks-install.sh | 71 +++++++++++++-------------------- 1 file changed, 28 insertions(+), 43 deletions(-) diff --git a/install/healthchecks-install.sh b/install/healthchecks-install.sh index 5ce285fe7..cf0eca1ea 100644 --- a/install/healthchecks-install.sh +++ b/install/healthchecks-install.sh @@ -21,12 +21,6 @@ $STD apt-get install -y \ libssl-dev msg_ok "Installed Dependencies" -msg_info "Setup Python3" -$STD apt-get install -y \ - python3 python3-dev python3-pip -$STD pip install --upgrade pip -msg_ok "Setup Python3" - setup_uv PG_VERSION=16 setup_postgresql @@ -49,54 +43,45 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC'" } >>~/healthchecks.creds msg_ok "Set up Database" -msg_info "Setup healthchecks" fetch_and_deploy_gh_release "healthchecks" "healthchecks/healthchecks" "source" +msg_info "Setup healthchecks" cd /opt/healthchecks mkdir -p /opt/healthchecks/static-collected/ -$STD uv venv .venv -$STD source .venv/bin/activate -$STD uv pip install wheel -$STD uv pip install gunicorn -$STD uv pip install -r requirements.txt +$STD uv pip install wheel gunicorn -r requirements.txt LOCAL_IP=$(hostname -I | awk '{print $1}') -cat </opt/healthchecks/.env -ALLOWED_HOSTS=localhost,127.0.0.1,${LOCAL_IP},healthchecks -DB=postgres -DB_HOST=localhost -DB_PORT=5432 -DB_NAME=${DB_NAME} -DB_USER=${DB_USER} -DB_PASSWORD=${DB_PASS} -DB_CONN_MAX_AGE=0 -DB_SSLMODE=prefer -DB_TARGET_SESSION_ATTRS=read-write -DATABASE_URL=postgres://${DB_USER}:${DB_PASS}@localhost:5432/${DB_NAME}?sslmode=prefer +cat </opt/healthchecks/hc/local_settings.py +DEBUG = False -DEFAULT_FROM_EMAIL=healthchecks@example.org -EMAIL_HOST=localhost -EMAIL_HOST_PASSWORD= -EMAIL_HOST_USER= -EMAIL_PORT=587 -EMAIL_USE_TLS=True -EMAIL_USE_VERIFICATION=True +ALLOWED_HOSTS = ["${LOCAL_IP}", "127.0.0.1", "localhost"] +CSRF_TRUSTED_ORIGINS = ["https://${LOCAL_IP}"] -# Django & Healthchecks Konfiguration -SECRET_KEY=${SECRET_KEY} -DEBUG=True +SECRET_KEY = "${SECRET_KEY}" -SITE_ROOT=http://${LOCAL_IP}:8000 -SITE_NAME=MyChecks -STATIC_ROOT=/opt/healthchecks/static-collected +SITE_ROOT = "https://${LOCAL_IP}" +SITE_NAME = "MyChecks" +DEFAULT_FROM_EMAIL = "healthchecks@${LOCAL_IP}" +STATIC_ROOT = "/opt/healthchecks/static-collected" + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql', + 'NAME': '${DB_NAME}', + 'USER': '${DB_USER}', + 'PASSWORD': '${DB_PASS}', + 'HOST': '127.0.0.1', + 'PORT': '5432', + 'TEST': {'CHARSET': 'UTF8'} + } +} EOF - -$STD .venv/bin/python3 manage.py makemigrations -$STD .venv/bin/python3 manage.py migrate --noinput -$STD .venv/bin/python3 manage.py collectstatic --noinput +$STD uv run -- python manage.py makemigrations +$STD uv run -- python manage.py migrate --noinput +$STD uv run -- python manage.py collectstatic --noinput ADMIN_EMAIL="admin@helper-scripts.local" ADMIN_PASSWORD="$DB_PASS" -cat < Date: Thu, 21 Aug 2025 11:14:22 +0200 Subject: [PATCH 0658/1733] Update healthchecks-install.sh --- install/healthchecks-install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/install/healthchecks-install.sh b/install/healthchecks-install.sh index cf0eca1ea..6496d149c 100644 --- a/install/healthchecks-install.sh +++ b/install/healthchecks-install.sh @@ -44,10 +44,11 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC'" msg_ok "Set up Database" fetch_and_deploy_gh_release "healthchecks" "healthchecks/healthchecks" "source" + msg_info "Setup healthchecks" cd /opt/healthchecks mkdir -p /opt/healthchecks/static-collected/ -$STD uv pip install wheel gunicorn -r requirements.txt +$STD uv pip install wheel gunicorn -r requirements.txt --system LOCAL_IP=$(hostname -I | awk '{print $1}') cat </opt/healthchecks/hc/local_settings.py DEBUG = False From 1514caac1113210f7fbb35f3beca7addb39d88cc Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 21 Aug 2025 11:53:07 +0200 Subject: [PATCH 0659/1733] fixes --- install/healthchecks-install.sh | 10 ++++++---- misc/tools.func | 6 +++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/install/healthchecks-install.sh b/install/healthchecks-install.sh index 6496d149c..ab265b5c1 100644 --- a/install/healthchecks-install.sh +++ b/install/healthchecks-install.sh @@ -49,20 +49,22 @@ msg_info "Setup healthchecks" cd /opt/healthchecks mkdir -p /opt/healthchecks/static-collected/ $STD uv pip install wheel gunicorn -r requirements.txt --system + LOCAL_IP=$(hostname -I | awk '{print $1}') cat </opt/healthchecks/hc/local_settings.py DEBUG = False ALLOWED_HOSTS = ["${LOCAL_IP}", "127.0.0.1", "localhost"] -CSRF_TRUSTED_ORIGINS = ["https://${LOCAL_IP}"] +CSRF_TRUSTED_ORIGINS = ["http://${LOCAL_IP}", "https://${LOCAL_IP}"] SECRET_KEY = "${SECRET_KEY}" -SITE_ROOT = "https://${LOCAL_IP}" +SITE_ROOT = "http://${LOCAL_IP}:8000" SITE_NAME = "MyChecks" DEFAULT_FROM_EMAIL = "healthchecks@${LOCAL_IP}" STATIC_ROOT = "/opt/healthchecks/static-collected" +COMPRESS_OFFLINE = True DATABASES = { 'default': { @@ -76,9 +78,11 @@ DATABASES = { } } EOF + $STD uv run -- python manage.py makemigrations $STD uv run -- python manage.py migrate --noinput $STD uv run -- python manage.py collectstatic --noinput +$STD uv run -- python manage.py compress ADMIN_EMAIL="admin@helper-scripts.local" ADMIN_PASSWORD="$DB_PASS" @@ -98,9 +102,7 @@ After=network.target postgresql.service [Service] WorkingDirectory=/opt/healthchecks/ -EnvironmentFile=/opt/healthchecks/.env ExecStart=/usr/local/bin/uv run -- gunicorn hc.wsgi:application --bind 127.0.0.1:8000 - Restart=always [Install] diff --git a/misc/tools.func b/misc/tools.func index 697f69fcd..6bb819b98 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -163,7 +163,7 @@ function setup_postgresql() { if [[ "$CURRENT_PG_VERSION" == "$PG_VERSION" ]]; then : # PostgreSQL is already at the desired version – no action needed else - msg_info "Detected PostgreSQL $CURRENT_PG_VERSION, preparing upgrade to $PG_VERSION" + $STD msg_info "Detected PostgreSQL $CURRENT_PG_VERSION, preparing upgrade to $PG_VERSION" NEED_PG_INSTALL=true fi else @@ -173,9 +173,9 @@ function setup_postgresql() { if [[ "$NEED_PG_INSTALL" == true ]]; then if [[ -n "$CURRENT_PG_VERSION" ]]; then - msg_info "Dumping PostgreSQL $CURRENT_PG_VERSION data" + $STD msg_info "Dumping PostgreSQL $CURRENT_PG_VERSION data" su - postgres -c "pg_dumpall > /var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql" - msg_ok "Data dump completed" + $STD msg_ok "Data dump completed" systemctl stop postgresql fi From c71d10830bf0eebe4edc8b77904a7b5d1c147b5a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 21 Aug 2025 13:01:56 +0200 Subject: [PATCH 0660/1733] Update healthchecks-install.sh --- install/healthchecks-install.sh | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/install/healthchecks-install.sh b/install/healthchecks-install.sh index ab265b5c1..fe1a95ba4 100644 --- a/install/healthchecks-install.sh +++ b/install/healthchecks-install.sh @@ -18,7 +18,8 @@ $STD apt-get install -y \ gcc \ libpq-dev \ libcurl4-openssl-dev \ - libssl-dev + libssl-dev \ + caddy msg_ok "Installed Dependencies" setup_uv @@ -94,6 +95,18 @@ if not User.objects.filter(email="${ADMIN_EMAIL}").exists(): EOF msg_ok "Installed healthchecks" +msg_info "Configuring Caddy" +cat </etc/caddy/Caddyfile +{ + email admin@example.com +} + +${LOCAL_IP} { + reverse_proxy 127.0.0.1:8000 +} +EOF +msg_ok "Configured Caddy" + msg_info "Creating Service" cat </etc/systemd/system/healthchecks.service [Unit] @@ -108,7 +121,7 @@ Restart=always [Install] WantedBy=multi-user.target EOF -systemctl enable -q --now healthchecks +systemctl enable -q --now healthchecks caddy msg_ok "Created Service" motd_ssh From 5fea4a4b194b5faa0398f4422f8eeb383779cd7d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 21 Aug 2025 13:17:25 +0200 Subject: [PATCH 0661/1733] Update healthchecks-install.sh --- install/healthchecks-install.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/install/healthchecks-install.sh b/install/healthchecks-install.sh index fe1a95ba4..051118468 100644 --- a/install/healthchecks-install.sh +++ b/install/healthchecks-install.sh @@ -30,6 +30,8 @@ DB_NAME=healthchecks_db DB_USER=hc_user DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) SECRET_KEY="$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | cut -c1-32)" +ADMIN_EMAIL="admin@helper-scripts.local" +ADMIN_PASSWORD="$DB_PASS" $STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" @@ -41,6 +43,8 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC'" echo "healthchecks Database User: $DB_USER" echo "healthchecks Database Password: $DB_PASS" echo "healthchecks Database Name: $DB_NAME" + echo "healthchecks Admin Email: $ADMIN_EMAIL" + echo "healthchecks Admin Password: $ADMIN_PASSWORD" } >>~/healthchecks.creds msg_ok "Set up Database" @@ -85,8 +89,6 @@ $STD uv run -- python manage.py migrate --noinput $STD uv run -- python manage.py collectstatic --noinput $STD uv run -- python manage.py compress -ADMIN_EMAIL="admin@helper-scripts.local" -ADMIN_PASSWORD="$DB_PASS" cat < Date: Thu, 21 Aug 2025 13:23:17 +0200 Subject: [PATCH 0662/1733] Update healthchecks.sh --- ct/healthchecks.sh | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ct/healthchecks.sh b/ct/healthchecks.sh index 8b55f7fdb..44218b6cb 100644 --- a/ct/healthchecks.sh +++ b/ct/healthchecks.sh @@ -20,15 +20,15 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -f /etc/systemd/system/healthchecks.service ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - msg_error "No Update." + header_info + check_container_storage + check_container_resources + if [[ ! -f /etc/systemd/system/healthchecks.service ]]; then + msg_error "No ${APP} Installation Found!" exit + fi + msg_error "No Update." + exit } start @@ -38,4 +38,4 @@ description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}" +echo -e "${TAB}${GATEWAY}${BGN}https://${IP}${CL}" From d8160d9a9fc159686940cbaa0533de900a356332 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 21 Aug 2025 11:23:41 +0000 Subject: [PATCH 0663/1733] Update .app files --- ct/headers/alpine-rustdeskserver | 6 ++++++ ct/headers/paperless-ngx | 6 ------ 2 files changed, 6 insertions(+), 6 deletions(-) create mode 100644 ct/headers/alpine-rustdeskserver delete mode 100644 ct/headers/paperless-ngx diff --git a/ct/headers/alpine-rustdeskserver b/ct/headers/alpine-rustdeskserver new file mode 100644 index 000000000..1c99e61fc --- /dev/null +++ b/ct/headers/alpine-rustdeskserver @@ -0,0 +1,6 @@ + ___ __ _ ____ __ ____ __ _____ + / | / /___ (_)___ ___ / __ \__ _______/ /_/ __ \___ _____/ /__/ ___/___ ______ _____ _____ + / /| | / / __ \/ / __ \/ _ \______/ /_/ / / / / ___/ __/ / / / _ \/ ___/ //_/\__ \/ _ \/ ___/ | / / _ \/ ___/ + / ___ |/ / /_/ / / / / / __/_____/ _, _/ /_/ (__ ) /_/ /_/ / __(__ ) ,< ___/ / __/ / | |/ / __/ / +/_/ |_/_/ .___/_/_/ /_/\___/ /_/ |_|\__,_/____/\__/_____/\___/____/_/|_|/____/\___/_/ |___/\___/_/ + /_/ diff --git a/ct/headers/paperless-ngx b/ct/headers/paperless-ngx deleted file mode 100644 index 177cf5741..000000000 --- a/ct/headers/paperless-ngx +++ /dev/null @@ -1,6 +0,0 @@ - ____ __ - / __ \____ _____ ___ _____/ /__ __________ ____ ____ __ __ - / /_/ / __ `/ __ \/ _ \/ ___/ / _ \/ ___/ ___/_____/ __ \/ __ `/ |/_/ - / ____/ /_/ / /_/ / __/ / / / __(__ |__ )_____/ / / / /_/ /> < -/_/ \__,_/ .___/\___/_/ /_/\___/____/____/ /_/ /_/\__, /_/|_| - /_/ /____/ From 65d2d1205d094f82f6845bd943be03fdd0f7b1f2 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 22 Aug 2025 10:35:25 +0200 Subject: [PATCH 0664/1733] Update create_lxc.sh --- misc/create_lxc.sh | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index 72200f8fa..210ff177d 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -346,35 +346,38 @@ flock -w 60 9 || { msg_debug "pct create command: pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" &>/dev/null; then - msg_error "Container creation failed. Checking if template is corrupted or incomplete." + msg_error "Container creation failed on ${TEMPLATE_STORAGE}. Checking template..." if [[ ! -s "$TEMPLATE_PATH" || "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then msg_error "Template file too small or missing – re-downloading." rm -f "$TEMPLATE_PATH" + pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" elif ! zstdcat "$TEMPLATE_PATH" | tar -tf - &>/dev/null; then msg_error "Template appears to be corrupted – re-downloading." rm -f "$TEMPLATE_PATH" + pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" else - msg_error "Template is valid, but container creation still failed." - exit 209 + # --- NEW FALLBACK LOGIC --- + if [[ "$TEMPLATE_STORAGE" != "local" ]]; then + msg_warn "Retrying container creation with fallback to local storage..." + LOCAL_TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" + if [ ! -f "$LOCAL_TEMPLATE_PATH" ]; then + msg_info "Downloading template to local..." + pveam download local "$TEMPLATE" + fi + if pct create "$CTID" "local:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" &>/dev/null; then + msg_ok "Container successfully created using fallback to local." + else + msg_error "Container creation failed even with fallback to local." + exit 209 + fi + # Skip the rest of error handling since fallback worked + continue + else + msg_error "Template is valid, but container creation still failed on local." + exit 209 + fi fi - - # Retry download - for attempt in {1..3}; do - msg_info "Attempt $attempt: Re-downloading template..." - if timeout 120 pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null; then - msg_ok "Template re-download successful." - break - fi - if [ "$attempt" -eq 3 ]; then - msg_error "Three failed attempts. Aborting." - exit 208 - fi - sleep $((attempt * 5)) - done - - sleep 1 # I/O-Sync-Delay - msg_ok "Re-downloaded LXC Template" fi if ! pct list | awk '{print $1}' | grep -qx "$CTID"; then From 2bf4eed425919d93e882c03d4cc516db97bb7b04 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 22 Aug 2025 10:37:22 +0200 Subject: [PATCH 0665/1733] test --- ct/debian.sh | 4 ++-- install/debian-install.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ct/debian.sh b/ct/debian.sh index 3cc0b73d5..d3bbda9b8 100644 --- a/ct/debian.sh +++ b/ct/debian.sh @@ -13,8 +13,8 @@ var_disk="${var_disk:-15}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" -var_fuse="${var_fuse:-no}" -var_tun="${var_tun:-no}" +#var_fuse="${var_fuse:-no}" +#var_tun="${var_tun:-no}" header_info "$APP" variables diff --git a/install/debian-install.sh b/install/debian-install.sh index ca9567b9e..e62591849 100644 --- a/install/debian-install.sh +++ b/install/debian-install.sh @@ -17,7 +17,7 @@ msg_info "Installing Dependencies" $STD apt-get install -y gpg msg_ok "Installed Dependencies" -setup_mariadb +#setup_mariadb #FFMPEG_VERSION="n7.1.1" FFMPEG_TYPE="full" setup_ffmpeg @@ -35,7 +35,7 @@ setup_mariadb #msg_ok "Get Release $RELEASE" #NODE_VERSION="24" NODE_MODULE="yarn" setup_nodejs -PG_VERSION="16" setup_postgresql +#PG_VERSION="16" setup_postgresql motd_ssh customize From d768bd885e5e33d2a75223197befe6ce6f1b7922 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 22 Aug 2025 10:38:58 +0200 Subject: [PATCH 0666/1733] Update create_lxc.sh --- misc/create_lxc.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index 210ff177d..99753a4aa 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -345,6 +345,7 @@ flock -w 60 9 || { } msg_debug "pct create command: pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" + if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" &>/dev/null; then msg_error "Container creation failed on ${TEMPLATE_STORAGE}. Checking template..." @@ -359,19 +360,21 @@ if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[ else # --- NEW FALLBACK LOGIC --- if [[ "$TEMPLATE_STORAGE" != "local" ]]; then - msg_warn "Retrying container creation with fallback to local storage..." + msg_warn "Retrying container creation with fallback to local template storage..." LOCAL_TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" if [ ! -f "$LOCAL_TEMPLATE_PATH" ]; then msg_info "Downloading template to local..." + msg_debug "pveam download local $TEMPLATE" pveam download local "$TEMPLATE" fi + msg_debug "pct create command (fallback): pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" if pct create "$CTID" "local:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" &>/dev/null; then - msg_ok "Container successfully created using fallback to local." + msg_ok "Container successfully created using fallback to local template storage." else msg_error "Container creation failed even with fallback to local." exit 209 fi - # Skip the rest of error handling since fallback worked + # Skip remaining error handling since fallback worked continue else msg_error "Template is valid, but container creation still failed on local." From 07864be2a09e349ccdec8cd6f50f047034b53752 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 22 Aug 2025 10:39:55 +0200 Subject: [PATCH 0667/1733] Update ubuntu-install.sh --- install/ubuntu-install.sh | 96 --------------------------------------- 1 file changed, 96 deletions(-) diff --git a/install/ubuntu-install.sh b/install/ubuntu-install.sh index ed5dc8170..97283d838 100644 --- a/install/ubuntu-install.sh +++ b/install/ubuntu-install.sh @@ -17,102 +17,6 @@ msg_info "Installing Dependencies" $STD apt-get install -y jq msg_ok "Installed Dependencies" -# echo "Getting aceberg/WatchYourLAN..." -# fetch_and_deploy_gh_release aceberg/WatchYourLAN -# echo "Got Version: $RELEASE" - -# echo "Getting actualbudget/actual..." -# RELEASE=$(get_gh_release actualbudget/actual) -# echo "Got Version: $RELEASE" - -# echo "Getting agl/jbig2enc..." -# RELEASE=$(get_gh_release agl/jbig2enc) -# echo "Got Version: $RELEASE" - -# echo "Getting alexta69/metube..." -# RELEASE=$(get_gh_release alexta69/metube) -# echo "Got Version: $RELEASE" - -# echo "Getting AlexxIT/go2rtc..." -# RELEASE=$(get_gh_release AlexxIT/go2rtc) -# echo "Got Version: $RELEASE" - -# echo "Getting apache/tika..." -# RELEASE=$(get_gh_release apache/tika) -# echo "Got Version: $RELEASE" - -# echo "Getting ArtifexSoftware/ghostpdl-downloads..." -# RELEASE=$(get_gh_release ArtifexSoftware/ghostpdl-downloads) -# echo "Got Version: $RELEASE" - -# echo "Getting Athou/commafeed..." -# RELEASE=$(get_gh_release Athou/commafeed) -# echo "Got Version: $RELEASE" - -# echo "Getting authelia/authelia..." -# RELEASE=$(get_gh_release authelia/authelia) -# echo "Got Version: $RELEASE" - -# echo "Getting azukaar/Cosmos-Server..." -# RELEASE=$(get_gh_release azukaar/Cosmos-Server) -# echo "Got Version: $RELEASE" - -# echo "Getting bastienwirtz/homer..." -# RELEASE=$(get_gh_release bastienwirtz/homer) -# echo "Got Version: $RELEASE" - -# echo "Getting benjaminjonard/koillection..." -# RELEASE=$(get_gh_release benjaminjonard/koillection) -# echo "Got Version: $RELEASE" - -# echo "Getting benzino77/tasmocompiler..." -# RELEASE=$(get_gh_release benzino77/tasmocompiler) -# echo "Got Version: $RELEASE" - -# echo "Getting blakeblackshear/frigate..." -# RELEASE=$(get_gh_release blakeblackshear/frigate) -# echo "Got Version: $RELEASE" - -# echo "Getting bluenviron/mediamtx..." -# RELEASE=$(get_gh_release bluenviron/mediamtx) -# echo "Got Version: $RELEASE" - -# echo "Getting BookStackApp/BookStack..." -# RELEASE=$(get_gh_release BookStackApp/BookStack) -# echo "Got Version: $RELEASE" - -# echo "Getting browserless/chrome..." -# RELEASE=$(get_gh_release browserless/chrome) -# echo "Got Version: $RELEASE" - -# echo "Getting Bubka/2FAuth..." -# RELEASE=$(get_gh_release Bubka/2FAuth) -# echo "Got Version: $RELEASE" - -# echo "Getting caddyserver/xcaddy..." -# RELEASE=$(get_gh_release caddyserver/xcaddy) -# echo "Got Version: $RELEASE" - -# echo "Getting clusterzx/paperless-ai..." -# RELEASE=$(get_gh_release clusterzx/paperless-ai) -# echo "Got Version: $RELEASE" - -# echo "Getting cockpit-project/cockpit..." -# RELEASE=$(get_gh_release cockpit-project/cockpit) -# echo "Got Version: $RELEASE" - -# echo "Getting community-scripts/ProxmoxVE..." -# RELEASE=$(get_gh_release community-scripts/ProxmoxVE) -# echo "Got Version: $RELEASE" - -# echo "Getting CorentinTh/it-tools..." -# RELEASE=$(get_gh_release CorentinTh/it-tools) -# echo "Got Version: $RELEASE" - -# echo "Getting dani-garcia/bw_web_builds..." -# RELEASE=$(get_gh_release dani-garcia/bw_web_builds) -# echo "Got Version: $RELEASE" - motd_ssh customize From 262b93f15410e5665caaa32359a217d897fa5607 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 22 Aug 2025 11:48:01 +0200 Subject: [PATCH 0668/1733] Update create_lxc.sh --- misc/create_lxc.sh | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index 99753a4aa..b7c8db099 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -234,6 +234,26 @@ while true; do fi done +# Storage Content Validation +msg_info "Validating content types of storage '$CONTAINER_STORAGE'" +STORAGE_CONTENT=$(grep -A4 -E "^(zfspool|dir|lvmthin|lvm): $CONTAINER_STORAGE" /etc/pve/storage.cfg | grep content | awk '{$1=""; print $0}' | xargs) + +msg_debug "Storage '$CONTAINER_STORAGE' has content types: $STORAGE_CONTENT" + +# check if rootdir supported +if ! grep -qw "rootdir" <<<"$STORAGE_CONTENT"; then + msg_error "Storage '$CONTAINER_STORAGE' does not support 'rootdir'. Cannot create LXC." + exit 217 +fi + +# check if template storage is compatible +TEMPLATE_CONTENT=$(grep -A4 -E "^[^:]+: $TEMPLATE_STORAGE" /etc/pve/storage.cfg | grep content | awk '{$1=""; print $0}' | xargs) +msg_debug "Template storage '$TEMPLATE_STORAGE' has content types: $TEMPLATE_CONTENT" + +if ! grep -qw "vztmpl" <<<"$TEMPLATE_CONTENT"; then + msg_warn "Template storage '$TEMPLATE_STORAGE' does not declare 'vztmpl'. This may cause pct create to fail." +fi + # Check free space on selected container storage STORAGE_FREE=$(pvesm status | awk -v s="$CONTAINER_STORAGE" '$1 == s { print $6 }') REQUIRED_KB=$((${PCT_DISK_SIZE:-8} * 1024 * 1024)) From 7eec53900fb4e7f8924aedf82d024222c2171a58 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 22 Aug 2025 11:58:19 +0200 Subject: [PATCH 0669/1733] Update create_lxc.sh --- misc/create_lxc.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index b7c8db099..7a5d18cf9 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -245,13 +245,18 @@ if ! grep -qw "rootdir" <<<"$STORAGE_CONTENT"; then msg_error "Storage '$CONTAINER_STORAGE' does not support 'rootdir'. Cannot create LXC." exit 217 fi +msg_ok "Storage '$CONTAINER_STORAGE' supports 'rootdir'" # check if template storage is compatible +msg_info "Validating content types of template storage '$TEMPLATE_STORAGE'" TEMPLATE_CONTENT=$(grep -A4 -E "^[^:]+: $TEMPLATE_STORAGE" /etc/pve/storage.cfg | grep content | awk '{$1=""; print $0}' | xargs) + msg_debug "Template storage '$TEMPLATE_STORAGE' has content types: $TEMPLATE_CONTENT" if ! grep -qw "vztmpl" <<<"$TEMPLATE_CONTENT"; then msg_warn "Template storage '$TEMPLATE_STORAGE' does not declare 'vztmpl'. This may cause pct create to fail." +else + msg_ok "Template storage '$TEMPLATE_STORAGE' supports 'vztmpl'" fi # Check free space on selected container storage From e136c161cc1724b746d00ab836f9deff5796a7b2 Mon Sep 17 00:00:00 2001 From: Andrej Kocijan Date: Fri, 22 Aug 2025 12:03:34 +0200 Subject: [PATCH 0670/1733] Added Redlib install/update script --- ct/redlib.sh | 67 ++++++++++++++++++++ frontend/public/json/redlib.json | 35 +++++++++++ install/redlib-install.sh | 105 +++++++++++++++++++++++++++++++ 3 files changed, 207 insertions(+) create mode 100644 ct/redlib.sh create mode 100644 frontend/public/json/redlib.json create mode 100644 install/redlib-install.sh diff --git a/ct/redlib.sh b/ct/redlib.sh new file mode 100644 index 000000000..23d702bc0 --- /dev/null +++ b/ct/redlib.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: andrej-kocijan (Andrej Kocijan) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/redlib-org/redlib + +APP="Redlib" +var_tags="${var_tags:-alpine;frontend}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-512}" +var_disk="${var_disk:-1}" +var_os="${var_os:-alpine}" +var_version="${var_version:-3.22}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_resources + + if [[ ! -d /opt/redlib ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + RELEASE=$(curl -s https://api.github.com/repos/redlib-org/redlib/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then + msg_info "Updating Alpine Packages" + $STD apk -U upgrade + msg_ok "Updated Alpine Packages" + + msg_info "Stopping ${APP} Service" + $STD rc-service redlib stop + msg_info "Stopped ${APP} Service" + + msg_info "Updating ${APP}" + $STD curl -fsSL -o /tmp/redlib-x86_64-unknown-linux-musl.tar.gz \ + "https://github.com/redlib-org/redlib/releases/latest/download/redlib-x86_64-unknown-linux-musl.tar.gz" + $STD tar -xzf /tmp/redlib-x86_64-unknown-linux-musl.tar.gz -C /opt/redlib + $STD rm /tmp/redlib-x86_64-unknown-linux-musl.tar.gz + msg_ok "Updated ${APP}" + + msg_info "Starting ${APP} Service" + $STD rc-service redlib start + msg_ok "Started ${APP} Service" + + echo "${RELEASE}" >/opt/${APP}_version.txt + msg_ok "Update Successful" + else + msg_ok "No update required. ${APP} is already at ${RELEASE}" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5252${CL}" diff --git a/frontend/public/json/redlib.json b/frontend/public/json/redlib.json new file mode 100644 index 000000000..40a0b7e8d --- /dev/null +++ b/frontend/public/json/redlib.json @@ -0,0 +1,35 @@ +{ + "name": "Redlib", + "slug": "redlib", + "categories": [ + 10 + ], + "date_created": "2025-08-22", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 5252, + "documentation": null, + "website": "https://github.com/redlib-org/redlib", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/redlib.webp", + "config_path": "/opt/redlib/redlib.conf", + "description": "An alternative private front-end to Reddit. Redlib hopes to provide an easier way to browse Reddit, without the ads, trackers, and bloat.", + "install_methods": [ + { + "type": "default", + "script": "ct/redlib.sh", + "resources": { + "cpu": 1, + "ram": 512, + "hdd": 1, + "os": "alpine", + "version": "3.22" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} diff --git a/install/redlib-install.sh b/install/redlib-install.sh new file mode 100644 index 000000000..f6208d916 --- /dev/null +++ b/install/redlib-install.sh @@ -0,0 +1,105 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: andrej-kocijan (Andrej Kocijan) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/redlib-org/redlib + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Downloading Redlib" +RELEASE=$(curl -s https://api.github.com/repos/redlib-org/redlib/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') +$STD curl -fsSL -o /tmp/redlib-x86_64-unknown-linux-musl.tar.gz \ +"https://github.com/redlib-org/redlib/releases/latest/download/redlib-x86_64-unknown-linux-musl.tar.gz" +msg_ok "Downloaded Redlib" + +msg_info "Installing Redlib" +mkdir /opt/redlib +$STD tar -xzf /tmp/redlib-x86_64-unknown-linux-musl.tar.gz -C /opt/redlib +$STD rm /tmp/redlib-x86_64-unknown-linux-musl.tar.gz +echo "${RELEASE}" >/opt/Redlib_version.txt +cat </opt/redlib/redlib.conf +############################################ +# Redlib Instance Configuration File +# Uncomment and edit values as needed +############################################ + +## Instance settings +ADDRESS=0.0.0.0 +PORT=5252 # Integer (0-65535) - Internal port +#REDLIB_SFW_ONLY=off # ["on", "off"] - Filter all NSFW content +#REDLIB_BANNER= # String - Displayed on instance info page +#REDLIB_ROBOTS_DISABLE_INDEXING=off # ["on", "off"] - Disable search engine indexing +#REDLIB_PUSHSHIFT_FRONTEND=undelete.pullpush.io # Pushshift frontend for removed links +#REDLIB_ENABLE_RSS=off # ["on", "off"] - Enable RSS feed generation +#REDLIB_FULL_URL= # String - Needed for proper RSS URLs + +## Default user settings +#REDLIB_DEFAULT_THEME=system # Theme (system, light, dark, black, dracula, nord, laserwave, violet, gold, rosebox, gruvboxdark, gruvboxlight, tokyoNight, icebergDark, doomone, libredditBlack, libredditDark, libredditLight) +#REDLIB_DEFAULT_FRONT_PAGE=default # ["default", "popular", "all"] +#REDLIB_DEFAULT_LAYOUT=card # ["card", "clean", "compact"] +#REDLIB_DEFAULT_WIDE=off # ["on", "off"] +#REDLIB_DEFAULT_POST_SORT=hot # ["hot", "new", "top", "rising", "controversial"] +#REDLIB_DEFAULT_COMMENT_SORT=confidence # ["confidence", "top", "new", "controversial", "old"] +#REDLIB_DEFAULT_BLUR_SPOILER=off # ["on", "off"] +#REDLIB_DEFAULT_SHOW_NSFW=off # ["on", "off"] +#REDLIB_DEFAULT_BLUR_NSFW=off # ["on", "off"] +#REDLIB_DEFAULT_USE_HLS=off # ["on", "off"] +#REDLIB_DEFAULT_HIDE_HLS_NOTIFICATION=off # ["on", "off"] +#REDLIB_DEFAULT_AUTOPLAY_VIDEOS=off # ["on", "off"] +#REDLIB_DEFAULT_SUBSCRIPTIONS= # Example: sub1+sub2+sub3 +#REDLIB_DEFAULT_HIDE_AWARDS=off # ["on", "off"] +#REDLIB_DEFAULT_DISABLE_VISIT_REDDIT_CONFIRMATION=off # ["on", "off"] +#REDLIB_DEFAULT_HIDE_SCORE=off # ["on", "off"] +#REDLIB_DEFAULT_HIDE_SIDEBAR_AND_SUMMARY=off # ["on", "off"] +#REDLIB_DEFAULT_FIXED_NAVBAR=on # ["on", "off"] +#REDLIB_DEFAULT_REMOVE_DEFAULT_FEEDS=off # ["on", "off"] +EOF +msg_ok "Installed Redlib" + +msg_info "Creating Redlib Service" +cat </etc/init.d/redlib +#!/sbin/openrc-run + +name="Redlib" +description="Redlib Service" +command="/opt/redlib/redlib" +pidfile="/run/redlib.pid" +supervisor="supervise-daemon" +command_background="yes" + +depend() { + need net +} + +start_pre() { + + set -a + . /opt/redlib/redlib.conf + set +a + + : ${ADDRESS:=0.0.0.0} + : ${PORT:=5252} + + command_args="-a ${ADDRESS} -p ${PORT}" +} +EOF +$STD chmod +x /etc/init.d/redlib +msg_ok "Created Redlib Service" + +msg_info "Enabling Redlib Service" +$STD rc-update add redlib default +msg_ok "Enabled Redlib Service" + +msg_info "Starting Redlib Service" +$STD rc-service redlib start +msg_ok "Started Redlib Service" + +motd_ssh +customize From 63bd61d695ef31554d4d3840d78485ee88003ab8 Mon Sep 17 00:00:00 2001 From: Andrej Kocijan Date: Fri, 22 Aug 2025 12:13:56 +0200 Subject: [PATCH 0671/1733] Fixed stopping service message --- ct/redlib.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/redlib.sh b/ct/redlib.sh index 23d702bc0..97bfb9455 100644 --- a/ct/redlib.sh +++ b/ct/redlib.sh @@ -36,7 +36,7 @@ function update_script() { msg_info "Stopping ${APP} Service" $STD rc-service redlib stop - msg_info "Stopped ${APP} Service" + msg_ok "Stopped ${APP} Service" msg_info "Updating ${APP}" $STD curl -fsSL -o /tmp/redlib-x86_64-unknown-linux-musl.tar.gz \ From 006bbd124cbe8ac6efbcc556c8ae2365ce770575 Mon Sep 17 00:00:00 2001 From: Andrej Kocijan Date: Fri, 22 Aug 2025 12:59:31 +0200 Subject: [PATCH 0672/1733] Add documentation to frontend json MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Slaviša Arežina <58952836+tremor021@users.noreply.github.com> --- frontend/public/json/redlib.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/redlib.json b/frontend/public/json/redlib.json index 40a0b7e8d..0b2c7cced 100644 --- a/frontend/public/json/redlib.json +++ b/frontend/public/json/redlib.json @@ -9,7 +9,7 @@ "updateable": true, "privileged": false, "interface_port": 5252, - "documentation": null, + "documentation": "https://github.com/redlib-org/redlib/blob/main/README.md", "website": "https://github.com/redlib-org/redlib", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/redlib.webp", "config_path": "/opt/redlib/redlib.conf", From 33df8b7b3a1f8ed44c743dd6d2f51ece867c15fe Mon Sep 17 00:00:00 2001 From: Andrej Kocijan Date: Fri, 22 Aug 2025 13:08:15 +0200 Subject: [PATCH 0673/1733] Minor fixes --- ct/redlib.sh | 4 ++-- install/redlib-install.sh | 7 ++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/ct/redlib.sh b/ct/redlib.sh index 97bfb9455..264e7962c 100644 --- a/ct/redlib.sh +++ b/ct/redlib.sh @@ -29,7 +29,7 @@ function update_script() { fi RELEASE=$(curl -s https://api.github.com/repos/redlib-org/redlib/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then + if [[ "${RELEASE}" != "$(cat ~/.redlib 2>/dev/null)" ]] || [[ ! -f ~/.redlib ]]; then msg_info "Updating Alpine Packages" $STD apk -U upgrade msg_ok "Updated Alpine Packages" @@ -49,7 +49,7 @@ function update_script() { $STD rc-service redlib start msg_ok "Started ${APP} Service" - echo "${RELEASE}" >/opt/${APP}_version.txt + echo "${RELEASE}" >~/.redlib msg_ok "Update Successful" else msg_ok "No update required. ${APP} is already at ${RELEASE}" diff --git a/install/redlib-install.sh b/install/redlib-install.sh index f6208d916..97c115191 100644 --- a/install/redlib-install.sh +++ b/install/redlib-install.sh @@ -23,7 +23,7 @@ msg_info "Installing Redlib" mkdir /opt/redlib $STD tar -xzf /tmp/redlib-x86_64-unknown-linux-musl.tar.gz -C /opt/redlib $STD rm /tmp/redlib-x86_64-unknown-linux-musl.tar.gz -echo "${RELEASE}" >/opt/Redlib_version.txt +echo "${RELEASE}" >~/.redlib cat </opt/redlib/redlib.conf ############################################ # Redlib Instance Configuration File @@ -91,11 +91,8 @@ start_pre() { } EOF $STD chmod +x /etc/init.d/redlib -msg_ok "Created Redlib Service" - -msg_info "Enabling Redlib Service" $STD rc-update add redlib default -msg_ok "Enabled Redlib Service" +msg_ok "Created Redlib Service" msg_info "Starting Redlib Service" $STD rc-service redlib start From e3f81af80d41e912822f0022d33c4dd1890f7892 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 22 Aug 2025 13:36:08 +0200 Subject: [PATCH 0674/1733] Update alpine-install.sh --- install/alpine-install.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/install/alpine-install.sh b/install/alpine-install.sh index 2916be18f..4922f1641 100644 --- a/install/alpine-install.sh +++ b/install/alpine-install.sh @@ -21,5 +21,7 @@ $STD apk add nano $STD apk add mc msg_ok "Installed Dependencies" +fetch_and_deploy_gh_release "redlib" "redlib-org/redlib" "prebuild" "latest" "/opt/redlib" "redlib-x86_64-unknown-linux-musl.tar.gz" + motd_ssh customize From 9b2f62a3a39aa70771492aaa22a87fa174cd309f Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 22 Aug 2025 13:38:12 +0200 Subject: [PATCH 0675/1733] Update alpine-install.func --- misc/alpine-install.func | 1 + 1 file changed, 1 insertion(+) diff --git a/misc/alpine-install.func b/misc/alpine-install.func index 85c3c2a16..450e92091 100644 --- a/misc/alpine-install.func +++ b/misc/alpine-install.func @@ -83,6 +83,7 @@ network_check() { update_os() { msg_info "Updating Container OS" $STD apk update && $STD apk upgrade + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) msg_ok "Updated Container OS" } From 302978c20a8ff02f0b89e46c232fd5684e11c366 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 22 Aug 2025 13:52:31 +0200 Subject: [PATCH 0676/1733] uv shell --- misc/tools.func | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/misc/tools.func b/misc/tools.func index 6bb819b98..166d4e6f7 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -1339,7 +1339,11 @@ function setup_uv() { } rm -rf "$TMP_DIR" - ensure_usr_local_bin_persist + #ensure_usr_local_bin_persist + $STD uv python update-shell || { + msg_error "Failed to update uv shell integration" + return 1 + } msg_ok "Setup uv $LATEST_VERSION" # Optional: install specific Python version From 807edc284525e5ba41ff32bea92c989f62241b3c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 22 Aug 2025 13:54:04 +0200 Subject: [PATCH 0677/1733] verbose psql --- misc/tools.func | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 166d4e6f7..43711d9e4 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -167,7 +167,6 @@ function setup_postgresql() { NEED_PG_INSTALL=true fi else - msg_info "Setup PostgreSQL $PG_VERSION" NEED_PG_INSTALL=true fi @@ -205,12 +204,12 @@ function setup_postgresql() { $STD msg_ok "PostgreSQL $PG_VERSION started" if [[ -n "$CURRENT_PG_VERSION" ]]; then - msg_info "Restoring dumped data" + $STD msg_info "Restoring dumped data" su - postgres -c "psql < /var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql" - msg_ok "Data restored" + $STD msg_ok "Data restored" fi - msg_ok "PostgreSQL $PG_VERSION installed" + $STD msg_ok "PostgreSQL $PG_VERSION installed" fi # Install optional PostgreSQL modules @@ -218,13 +217,13 @@ function setup_postgresql() { IFS=',' read -ra MODULES <<<"$PG_MODULES" for module in "${MODULES[@]}"; do local pkg="postgresql-${PG_VERSION}-${module}" - msg_info "Setup PostgreSQL module/s: $pkg" + $STD msg_info "Setup PostgreSQL module/s: $pkg" $STD apt-get install -y "$pkg" || { msg_error "Failed to install $pkg" continue } done - msg_ok "Setup PostgreSQL modules" + $STD msg_ok "Setup PostgreSQL modules" fi } @@ -1412,7 +1411,7 @@ function setup_gs() { return fi - msg_info "Setup Ghostscript $LATEST_VERSION_DOTTED" + msg_info "Setup Ghostscript $LATEST_VERSION_DOTTED (Patience)" curl -fsSL "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs${LATEST_VERSION}/ghostscript-${LATEST_VERSION_DOTTED}.tar.gz" -o "$TMP_DIR/ghostscript.tar.gz" if ! tar -xzf "$TMP_DIR/ghostscript.tar.gz" -C "$TMP_DIR"; then From 882562d9e07ca77148e82b055b07931d5461518c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 22 Aug 2025 13:55:06 +0200 Subject: [PATCH 0678/1733] Update tools.func --- misc/tools.func | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 43711d9e4..844fabd1a 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -187,21 +187,16 @@ function setup_postgresql() { echo "deb https://apt.postgresql.org/pub/repos/apt ${DISTRO}-pgdg main" \ >/etc/apt/sources.list.d/pgdg.list - $STD msg_ok "Repository added" - $STD apt-get update + $STD msg_ok "Repository added" msg_info "Setup PostgreSQL $PG_VERSION" $STD apt-get install -y "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}" - msg_ok "Setup PostgreSQL $PG_VERSION" if [[ -n "$CURRENT_PG_VERSION" ]]; then $STD apt-get purge -y "postgresql-${CURRENT_PG_VERSION}" "postgresql-client-${CURRENT_PG_VERSION}" || true fi - - $STD msg_info "Starting PostgreSQL $PG_VERSION" systemctl enable -q --now postgresql - $STD msg_ok "PostgreSQL $PG_VERSION started" if [[ -n "$CURRENT_PG_VERSION" ]]; then $STD msg_info "Restoring dumped data" From e988634b34ddde5b0d4c7837f8b7d0cacf3d3343 Mon Sep 17 00:00:00 2001 From: Andrej Kocijan Date: Fri, 22 Aug 2025 16:35:58 +0200 Subject: [PATCH 0679/1733] Source build.func from correct repo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Slaviša Arežina <58952836+tremor021@users.noreply.github.com> --- ct/redlib.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/redlib.sh b/ct/redlib.sh index 264e7962c..e62e91e1a 100644 --- a/ct/redlib.sh +++ b/ct/redlib.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: andrej-kocijan (Andrej Kocijan) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE From b39235d15ec26fe8f410c9445ab8277469ae7944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Fri, 22 Aug 2025 17:46:21 +0200 Subject: [PATCH 0680/1733] Update redlib-install.sh --- install/redlib-install.sh | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/install/redlib-install.sh b/install/redlib-install.sh index 97c115191..5472985de 100644 --- a/install/redlib-install.sh +++ b/install/redlib-install.sh @@ -5,7 +5,7 @@ # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/redlib-org/redlib -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"e color verb_ip6 catch_errors @@ -13,17 +13,9 @@ setting_up_container network_check update_os -msg_info "Downloading Redlib" -RELEASE=$(curl -s https://api.github.com/repos/redlib-org/redlib/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') -$STD curl -fsSL -o /tmp/redlib-x86_64-unknown-linux-musl.tar.gz \ -"https://github.com/redlib-org/redlib/releases/latest/download/redlib-x86_64-unknown-linux-musl.tar.gz" -msg_ok "Downloaded Redlib" +fetch_and_deploy_gh_release "redlib" "redlib-org/redlib" "prebuild" "latest" "/opt/redlib" "redlib-x86_64-unknown-linux-musl.tar.gz" -msg_info "Installing Redlib" -mkdir /opt/redlib -$STD tar -xzf /tmp/redlib-x86_64-unknown-linux-musl.tar.gz -C /opt/redlib -$STD rm /tmp/redlib-x86_64-unknown-linux-musl.tar.gz -echo "${RELEASE}" >~/.redlib +msg_info "Configuring Redlib" cat </opt/redlib/redlib.conf ############################################ # Redlib Instance Configuration File @@ -61,7 +53,7 @@ PORT=5252 # Integer (0-65535) - Internal port #REDLIB_DEFAULT_FIXED_NAVBAR=on # ["on", "off"] #REDLIB_DEFAULT_REMOVE_DEFAULT_FEEDS=off # ["on", "off"] EOF -msg_ok "Installed Redlib" +msg_ok "Configured Redlib" msg_info "Creating Redlib Service" cat </etc/init.d/redlib From 982b2221284fdd0636db2dc38688649a0f6ba23a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Fri, 22 Aug 2025 17:50:51 +0200 Subject: [PATCH 0681/1733] Update redlib.sh --- ct/redlib.sh | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/ct/redlib.sh b/ct/redlib.sh index e62e91e1a..2a68a5959 100644 --- a/ct/redlib.sh +++ b/ct/redlib.sh @@ -38,18 +38,12 @@ function update_script() { $STD rc-service redlib stop msg_ok "Stopped ${APP} Service" - msg_info "Updating ${APP}" - $STD curl -fsSL -o /tmp/redlib-x86_64-unknown-linux-musl.tar.gz \ - "https://github.com/redlib-org/redlib/releases/latest/download/redlib-x86_64-unknown-linux-musl.tar.gz" - $STD tar -xzf /tmp/redlib-x86_64-unknown-linux-musl.tar.gz -C /opt/redlib - $STD rm /tmp/redlib-x86_64-unknown-linux-musl.tar.gz - msg_ok "Updated ${APP}" + fetch_and_deploy_gh_release "redlib" "redlib-org/redlib" "prebuild" "latest" "/opt/redlib" "redlib-x86_64-unknown-linux-musl.tar.gz" msg_info "Starting ${APP} Service" $STD rc-service redlib start msg_ok "Started ${APP} Service" - echo "${RELEASE}" >~/.redlib msg_ok "Update Successful" else msg_ok "No update required. ${APP} is already at ${RELEASE}" From 414e22416e0442e7eb91c603511563f91536251d Mon Sep 17 00:00:00 2001 From: Andrej Kocijan Date: Fri, 22 Aug 2025 17:54:48 +0200 Subject: [PATCH 0682/1733] Removed version check as fetch_and_deploy_gh_release already does that --- ct/redlib.sh | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/ct/redlib.sh b/ct/redlib.sh index 2a68a5959..7cde4fe8c 100644 --- a/ct/redlib.sh +++ b/ct/redlib.sh @@ -28,26 +28,21 @@ function update_script() { exit fi - RELEASE=$(curl -s https://api.github.com/repos/redlib-org/redlib/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - if [[ "${RELEASE}" != "$(cat ~/.redlib 2>/dev/null)" ]] || [[ ! -f ~/.redlib ]]; then - msg_info "Updating Alpine Packages" - $STD apk -U upgrade - msg_ok "Updated Alpine Packages" + msg_info "Updating Alpine Packages" + $STD apk -U upgrade + msg_ok "Updated Alpine Packages" - msg_info "Stopping ${APP} Service" - $STD rc-service redlib stop - msg_ok "Stopped ${APP} Service" + msg_info "Stopping ${APP} Service" + $STD rc-service redlib stop + msg_ok "Stopped ${APP} Service" - fetch_and_deploy_gh_release "redlib" "redlib-org/redlib" "prebuild" "latest" "/opt/redlib" "redlib-x86_64-unknown-linux-musl.tar.gz" + fetch_and_deploy_gh_release "redlib" "redlib-org/redlib" "prebuild" "latest" "/opt/redlib" "redlib-x86_64-unknown-linux-musl.tar.gz" - msg_info "Starting ${APP} Service" - $STD rc-service redlib start - msg_ok "Started ${APP} Service" + msg_info "Starting ${APP} Service" + $STD rc-service redlib start + msg_ok "Started ${APP} Service" - msg_ok "Update Successful" - else - msg_ok "No update required. ${APP} is already at ${RELEASE}" - fi + msg_ok "Update Successful" exit } From d114a4ad697ecb9872b63e45dca78a2c302fe8f8 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 22 Aug 2025 16:21:09 +0000 Subject: [PATCH 0683/1733] Update .app files --- ct/headers/redlib | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/redlib diff --git a/ct/headers/redlib b/ct/headers/redlib new file mode 100644 index 000000000..b43c70b7d --- /dev/null +++ b/ct/headers/redlib @@ -0,0 +1,6 @@ + ____ _____ __ + / __ \___ ____/ / (_) /_ + / /_/ / _ \/ __ / / / __ \ + / _, _/ __/ /_/ / / / /_/ / +/_/ |_|\___/\__,_/_/_/_.___/ + From a06839993c5313f700ac5d7838580fbf037d5859 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Fri, 22 Aug 2025 18:25:43 +0200 Subject: [PATCH 0684/1733] Update redlib --- ct/{redlib.sh => alpine-redlib.sh} | 2 +- install/{redlib-install.sh => alpine-redlib-install.sh} | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) rename ct/{redlib.sh => alpine-redlib.sh} (98%) rename install/{redlib-install.sh => alpine-redlib-install.sh} (98%) diff --git a/ct/redlib.sh b/ct/alpine-redlib.sh similarity index 98% rename from ct/redlib.sh rename to ct/alpine-redlib.sh index 7cde4fe8c..88f9fd3df 100644 --- a/ct/redlib.sh +++ b/ct/alpine-redlib.sh @@ -5,7 +5,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/redlib-org/redlib -APP="Redlib" +APP="Alpine-Redlib" var_tags="${var_tags:-alpine;frontend}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" diff --git a/install/redlib-install.sh b/install/alpine-redlib-install.sh similarity index 98% rename from install/redlib-install.sh rename to install/alpine-redlib-install.sh index 5472985de..de64b12c2 100644 --- a/install/redlib-install.sh +++ b/install/alpine-redlib-install.sh @@ -92,3 +92,7 @@ msg_ok "Started Redlib Service" motd_ssh customize + +msg_info "Cleaning up" +$STD apk cache clean +msg_ok "Cleaned" From 685d75429cf8b75cf32900e0804c5e9491301276 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Fri, 22 Aug 2025 18:41:01 +0200 Subject: [PATCH 0685/1733] fix --- install/alpine-redlib-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/alpine-redlib-install.sh b/install/alpine-redlib-install.sh index de64b12c2..0f7177210 100644 --- a/install/alpine-redlib-install.sh +++ b/install/alpine-redlib-install.sh @@ -5,7 +5,7 @@ # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/redlib-org/redlib -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"e +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors From 0d1638e3914ca2c7afa44842ced926435bbbd6bf Mon Sep 17 00:00:00 2001 From: tremor021 Date: Fri, 22 Aug 2025 18:45:42 +0200 Subject: [PATCH 0686/1733] Fix redlib json --- frontend/public/json/redlib.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/redlib.json b/frontend/public/json/redlib.json index 0b2c7cced..215b190f3 100644 --- a/frontend/public/json/redlib.json +++ b/frontend/public/json/redlib.json @@ -17,7 +17,7 @@ "install_methods": [ { "type": "default", - "script": "ct/redlib.sh", + "script": "ct/alpine-redlib.sh", "resources": { "cpu": 1, "ram": 512, From f4be7b6f24d8e7c8d3a45dff79c0168654bc61de Mon Sep 17 00:00:00 2001 From: Lucas Fell Date: Thu, 14 Aug 2025 23:03:32 -0300 Subject: [PATCH 0687/1733] wip: add ghostfolio --- ct/ghostfolio.sh | 72 +++++++++++++++ frontend/public/json/ghostfolio.json | 46 ++++++++++ install/ghostfolio-install.sh | 132 +++++++++++++++++++++++++++ 3 files changed, 250 insertions(+) create mode 100644 ct/ghostfolio.sh create mode 100644 frontend/public/json/ghostfolio.json create mode 100644 install/ghostfolio-install.sh diff --git a/ct/ghostfolio.sh b/ct/ghostfolio.sh new file mode 100644 index 000000000..19316226a --- /dev/null +++ b/ct/ghostfolio.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: lucasfell +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://ghostfol.io/ + +APP="Ghostfolio" +var_tags="${var_tags:-finance;investment}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-8}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -f /opt/ghostfolio/dist/apps/api/main.js ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + msg_info "Stopping $APP" + systemctl stop ghostfolio + msg_ok "Stopped $APP" + + msg_info "Creating Backup" + tar -czf "/opt/ghostfolio_backup_$(date +%F).tar.gz" /opt/ghostfolio + msg_ok "Backup Created" + + msg_info "Updating $APP" + cd /opt/ghostfolio + git fetch --all + RELEASE=$(git describe --tags --abbrev=0 origin/main) + if [[ "${RELEASE}" != "$(cat /opt/ghostfolio_version.txt)" ]] || [[ ! -f /opt/ghostfolio_version.txt ]]; then + git checkout ${RELEASE} + npm ci + npm run build:production + npm run database:migrate + echo "${RELEASE}" >/opt/ghostfolio_version.txt + msg_ok "Updated $APP to ${RELEASE}" + else + msg_ok "No update required. ${APP} is already at ${RELEASE}" + fi + + msg_info "Starting $APP" + systemctl start ghostfolio + msg_ok "Started $APP" + + msg_info "Cleaning Up" + npm cache clean --force + msg_ok "Cleanup Completed" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3333${CL}" diff --git a/frontend/public/json/ghostfolio.json b/frontend/public/json/ghostfolio.json new file mode 100644 index 000000000..48230624c --- /dev/null +++ b/frontend/public/json/ghostfolio.json @@ -0,0 +1,46 @@ +{ + "name": "Ghostfolio", + "slug": "ghostfolio", + "categories": [23], + "date_created": "2025-08-14", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 3333, + "documentation": "https://github.com/ghostfolio/ghostfolio?tab=readme-ov-file#self-hosting", + "website": "https://ghostfol.io/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/ghostfolio.webp", + "config_path": "/opt/ghostfolio/.env", + "description": "Ghostfolio is an open source wealth management software built with web technology. The application empowers busy people to keep track of stocks, ETFs or cryptocurrencies and make solid, data-driven investment decisions.", + "install_methods": [ + { + "type": "default", + "script": "ct/ghostfolio.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 8, + "os": "debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Create your first user account by visiting the web interface and clicking 'Get Started'. The first user will automatically get admin privileges.", + "type": "info" + }, + { + "text": "Database and Redis credentials: `cat ~/ghostfolio.creds`", + "type": "info" + }, + { + "text": "Configuration file: `/opt/ghostfolio/.env`", + "type": "info" + } + ] +} diff --git a/install/ghostfolio-install.sh b/install/ghostfolio-install.sh new file mode 100644 index 000000000..58809daaf --- /dev/null +++ b/install/ghostfolio-install.sh @@ -0,0 +1,132 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: lucasfell +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://ghostfol.io/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt-get install -y \ + git \ + build-essential \ + python3 \ + openssl \ + curl \ + ca-certificates +msg_ok "Installed Dependencies" + +PG_VERSION="15" setup_postgresql + +msg_info "Installing Redis" +$STD apt-get install -y redis-server +systemctl enable -q --now redis-server +msg_ok "Installed Redis" + +NODE_VERSION="22" setup_nodejs + +msg_info "Setting up Database" +DB_NAME=ghostfolio +DB_USER=ghostfolio +DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) +REDIS_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) +ACCESS_TOKEN_SALT=$(openssl rand -base64 32) +JWT_SECRET_KEY=$(openssl rand -base64 32) + +$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME;" +$STD sudo -u postgres psql -c "CREATE USER $DB_USER WITH ENCRYPTED PASSWORD '$DB_PASS';" +$STD sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER;" +$STD sudo -u postgres psql -c "ALTER USER $DB_USER CREATEDB;" + +{ + echo "Ghostfolio Credentials" + echo "Database User: $DB_USER" + echo "Database Password: $DB_PASS" + echo "Database Name: $DB_NAME" + echo "Redis Password: $REDIS_PASS" + echo "Access Token Salt: $ACCESS_TOKEN_SALT" + echo "JWT Secret Key: $JWT_SECRET_KEY" +} >>~/ghostfolio.creds +msg_ok "Set up Database" + +msg_info "Configuring Redis" +sed -i "s/# requirepass foobared/requirepass $REDIS_PASS/" /etc/redis/redis.conf +systemctl restart redis-server +msg_ok "Configured Redis" + +msg_info "Cloning Ghostfolio" +cd /opt +$STD git clone https://github.com/ghostfolio/ghostfolio.git +cd ghostfolio +RELEASE=$(git describe --tags --abbrev=0) +git checkout $RELEASE +msg_ok "Cloned Ghostfolio $RELEASE" + +msg_info "Installing Ghostfolio Dependencies" +$STD npm ci +msg_ok "Installed Dependencies" + +msg_info "Building Ghostfolio" +$STD npm run build:production +msg_ok "Built Ghostfolio" + +msg_info "Setting up Environment" +cat </opt/ghostfolio/.env +DATABASE_URL=postgresql://$DB_USER:$DB_PASS@localhost:5432/$DB_NAME?connect_timeout=300&sslmode=prefer +REDIS_HOST=localhost +REDIS_PORT=6379 +REDIS_PASSWORD=$REDIS_PASS +ACCESS_TOKEN_SALT=$ACCESS_TOKEN_SALT +JWT_SECRET_KEY=$JWT_SECRET_KEY +NODE_ENV=production +PORT=3333 +HOST=0.0.0.0 +EOF +msg_ok "Set up Environment" + +msg_info "Running Database Migrations" +cd /opt/ghostfolio/dist/apps/api +$STD npx prisma migrate deploy +$STD npx prisma db seed +msg_ok "Database Migrations Complete" + +msg_info "Creating Service" +cat </etc/systemd/system/ghostfolio.service +[Unit] +Description=Ghostfolio Investment Tracker +After=network.target postgresql.service redis-server.service +Wants=postgresql.service redis-server.service + +[Service] +Type=simple +User=root +WorkingDirectory=/opt/ghostfolio/dist/apps/api +Environment=NODE_ENV=production +EnvironmentFile=/opt/ghostfolio/.env +ExecStart=/usr/bin/node main.js +Restart=always +RestartSec=10 + +[Install] +WantedBy=multi-user.target +EOF + +systemctl enable -q --now ghostfolio +echo "$RELEASE" >/opt/ghostfolio_version.txt +msg_ok "Created Service" + +motd_ssh +customize + +msg_info "Cleaning up" +npm cache clean --force +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From ff6b151929fdde0843ae120d2b1562a344a7396d Mon Sep 17 00:00:00 2001 From: Lucas Fell Date: Thu, 14 Aug 2025 23:31:06 -0300 Subject: [PATCH 0688/1733] increase ram to 4gb during build add optional coingecko env keys --- ct/ghostfolio.sh | 2 +- frontend/public/json/ghostfolio.json | 10 ++++- install/ghostfolio-install.sh | 66 +++++++++++++++++++++++++++- 3 files changed, 75 insertions(+), 3 deletions(-) diff --git a/ct/ghostfolio.sh b/ct/ghostfolio.sh index 19316226a..1ad15294f 100644 --- a/ct/ghostfolio.sh +++ b/ct/ghostfolio.sh @@ -8,7 +8,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV APP="Ghostfolio" var_tags="${var_tags:-finance;investment}" var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" +var_ram="${var_ram:-4096}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-12}" diff --git a/frontend/public/json/ghostfolio.json b/frontend/public/json/ghostfolio.json index 48230624c..224bd7bb0 100644 --- a/frontend/public/json/ghostfolio.json +++ b/frontend/public/json/ghostfolio.json @@ -18,7 +18,7 @@ "script": "ct/ghostfolio.sh", "resources": { "cpu": 2, - "ram": 2048, + "ram": 4096, "hdd": 8, "os": "debian", "version": "12" @@ -41,6 +41,14 @@ { "text": "Configuration file: `/opt/ghostfolio/.env`", "type": "info" + }, + { + "text": "Optional: CoinGecko API keys can be added during installation or later in the .env file for enhanced cryptocurrency data.", + "type": "info" + }, + { + "text": "Build process requires 4GB RAM (runtime: ~2GB). A temporary swap file will be created automatically if insufficient memory is detected.", + "type": "warning" } ] } diff --git a/install/ghostfolio-install.sh b/install/ghostfolio-install.sh index 58809daaf..8ead06112 100644 --- a/install/ghostfolio-install.sh +++ b/install/ghostfolio-install.sh @@ -53,6 +53,14 @@ $STD sudo -u postgres psql -c "ALTER USER $DB_USER CREATEDB;" echo "Redis Password: $REDIS_PASS" echo "Access Token Salt: $ACCESS_TOKEN_SALT" echo "JWT Secret Key: $JWT_SECRET_KEY" + if [[ -n "$COINGECKO_DEMO_KEY" ]]; then + echo "CoinGecko Demo API Key: $COINGECKO_DEMO_KEY" + fi + if [[ -n "$COINGECKO_PRO_KEY" ]]; then + echo "CoinGecko Pro API Key: $COINGECKO_PRO_KEY" + fi + echo "" + echo "To add CoinGecko API keys later, edit: /opt/ghostfolio/.env" } >>~/ghostfolio.creds msg_ok "Set up Database" @@ -69,14 +77,55 @@ RELEASE=$(git describe --tags --abbrev=0) git checkout $RELEASE msg_ok "Cloned Ghostfolio $RELEASE" +msg_info "Checking Build Resources" +current_ram=$(free -m | awk 'NR==2{print $2}') +if [[ "$current_ram" -lt 3584 ]]; then + msg_warn "Current RAM: ${current_ram}MB. Ghostfolio build requires ~4GB RAM for optimal performance." + msg_info "Creating temporary swap file for build process" + + # Check if swap already exists + if ! swapon --noheadings --show | grep -q 'swap'; then + TEMP_SWAP_FILE="/tmp/ghostfolio_build_swap" + $STD dd if=/dev/zero of="$TEMP_SWAP_FILE" bs=1M count=1024 + $STD chmod 600 "$TEMP_SWAP_FILE" + $STD mkswap "$TEMP_SWAP_FILE" + $STD swapon "$TEMP_SWAP_FILE" + SWAP_CREATED=true + msg_ok "Created 1GB temporary swap file" + else + msg_ok "Existing swap detected" + SWAP_CREATED=false + fi +else + msg_ok "Sufficient RAM available for build" + SWAP_CREATED=false +fi + msg_info "Installing Ghostfolio Dependencies" +export NODE_OPTIONS="--max-old-space-size=3584" $STD npm ci msg_ok "Installed Dependencies" -msg_info "Building Ghostfolio" +msg_info "Building Ghostfolio (This may take several minutes)" $STD npm run build:production msg_ok "Built Ghostfolio" +# Clean up temporary swap if we created it +if [[ "$SWAP_CREATED" == "true" ]]; then + msg_info "Cleaning up temporary swap file" + $STD swapoff "$TEMP_SWAP_FILE" + $STD rm -f "$TEMP_SWAP_FILE" + msg_ok "Removed temporary swap file" +fi + +msg_info "Optional CoinGecko API Configuration" +echo +echo -e "${YW}CoinGecko API keys are optional but provide better cryptocurrency data.${CL}" +echo -e "${YW}You can skip this and add them later by editing /opt/ghostfolio/.env${CL}" +echo +read -rp "${TAB3}Enter CoinGecko Demo API key (optional, press Enter to skip): " COINGECKO_DEMO_KEY +read -rp "${TAB3}Enter CoinGecko Pro API key (optional, press Enter to skip): " COINGECKO_PRO_KEY + msg_info "Setting up Environment" cat </opt/ghostfolio/.env DATABASE_URL=postgresql://$DB_USER:$DB_PASS@localhost:5432/$DB_NAME?connect_timeout=300&sslmode=prefer @@ -89,6 +138,15 @@ NODE_ENV=production PORT=3333 HOST=0.0.0.0 EOF + +if [[ -n "$COINGECKO_DEMO_KEY" ]]; then + echo "API_KEY_COINGECKO_DEMO=$COINGECKO_DEMO_KEY" >>/opt/ghostfolio/.env +fi + +if [[ -n "$COINGECKO_PRO_KEY" ]]; then + echo "API_KEY_COINGECKO_PRO=$COINGECKO_PRO_KEY" >>/opt/ghostfolio/.env +fi + msg_ok "Set up Environment" msg_info "Running Database Migrations" @@ -130,3 +188,9 @@ npm cache clean --force $STD apt-get -y autoremove $STD apt-get -y autoclean msg_ok "Cleaned" + +msg_info "Installation Complete" +echo -e "${INFO}${YW}Ghostfolio is now running and accessible at http://$(hostname -I | awk '{print $1}'):3333${CL}" +echo -e "${INFO}${YW}Runtime memory usage: ~2GB (you can reduce container memory to 2GB if desired)${CL}" +echo -e "${INFO}${YW}First user to register will automatically get admin privileges${CL}" +msg_ok "Setup Complete" From 6242c3894f17556221b3a7ab7392c71d48e16a3a Mon Sep 17 00:00:00 2001 From: Lucas Fell Date: Fri, 15 Aug 2025 00:26:02 -0300 Subject: [PATCH 0689/1733] fix env and migrations --- install/ghostfolio-install.sh | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/install/ghostfolio-install.sh b/install/ghostfolio-install.sh index 8ead06112..d03da5493 100644 --- a/install/ghostfolio-install.sh +++ b/install/ghostfolio-install.sh @@ -44,6 +44,10 @@ $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME;" $STD sudo -u postgres psql -c "CREATE USER $DB_USER WITH ENCRYPTED PASSWORD '$DB_PASS';" $STD sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER;" $STD sudo -u postgres psql -c "ALTER USER $DB_USER CREATEDB;" +$STD sudo -u postgres psql -d $DB_NAME -c "GRANT ALL ON SCHEMA public TO $DB_USER;" +$STD sudo -u postgres psql -d $DB_NAME -c "GRANT CREATE ON SCHEMA public TO $DB_USER;" +$STD sudo -u postgres psql -d $DB_NAME -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO $DB_USER;" +$STD sudo -u postgres psql -d $DB_NAME -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO $DB_USER;" { echo "Ghostfolio Credentials" @@ -53,10 +57,10 @@ $STD sudo -u postgres psql -c "ALTER USER $DB_USER CREATEDB;" echo "Redis Password: $REDIS_PASS" echo "Access Token Salt: $ACCESS_TOKEN_SALT" echo "JWT Secret Key: $JWT_SECRET_KEY" - if [[ -n "$COINGECKO_DEMO_KEY" ]]; then + if [[ -n "${COINGECKO_DEMO_KEY:-}" ]]; then echo "CoinGecko Demo API Key: $COINGECKO_DEMO_KEY" fi - if [[ -n "$COINGECKO_PRO_KEY" ]]; then + if [[ -n "${COINGECKO_PRO_KEY:-}" ]]; then echo "CoinGecko Pro API Key: $COINGECKO_PRO_KEY" fi echo "" @@ -139,18 +143,18 @@ PORT=3333 HOST=0.0.0.0 EOF -if [[ -n "$COINGECKO_DEMO_KEY" ]]; then +if [[ -n "${COINGECKO_DEMO_KEY:-}" ]]; then echo "API_KEY_COINGECKO_DEMO=$COINGECKO_DEMO_KEY" >>/opt/ghostfolio/.env fi -if [[ -n "$COINGECKO_PRO_KEY" ]]; then +if [[ -n "${COINGECKO_PRO_KEY:-}" ]]; then echo "API_KEY_COINGECKO_PRO=$COINGECKO_PRO_KEY" >>/opt/ghostfolio/.env fi msg_ok "Set up Environment" msg_info "Running Database Migrations" -cd /opt/ghostfolio/dist/apps/api +cd /opt/ghostfolio $STD npx prisma migrate deploy $STD npx prisma db seed msg_ok "Database Migrations Complete" From fd2fbd36db662723751353fa0a5bbac73919638d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Fri, 15 Aug 2025 14:45:50 +0200 Subject: [PATCH 0690/1733] Update install/ghostfolio-install.sh --- install/ghostfolio-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/install/ghostfolio-install.sh b/install/ghostfolio-install.sh index d03da5493..b3b0efbdc 100644 --- a/install/ghostfolio-install.sh +++ b/install/ghostfolio-install.sh @@ -150,7 +150,6 @@ fi if [[ -n "${COINGECKO_PRO_KEY:-}" ]]; then echo "API_KEY_COINGECKO_PRO=$COINGECKO_PRO_KEY" >>/opt/ghostfolio/.env fi - msg_ok "Set up Environment" msg_info "Running Database Migrations" From 54a69a9922eddd475a1113a09c1ce4788fe3ca12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Fri, 15 Aug 2025 14:46:14 +0200 Subject: [PATCH 0691/1733] Update install/ghostfolio-install.sh --- install/ghostfolio-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/install/ghostfolio-install.sh b/install/ghostfolio-install.sh index b3b0efbdc..6e5cf2326 100644 --- a/install/ghostfolio-install.sh +++ b/install/ghostfolio-install.sh @@ -39,7 +39,6 @@ DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) REDIS_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) ACCESS_TOKEN_SALT=$(openssl rand -base64 32) JWT_SECRET_KEY=$(openssl rand -base64 32) - $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME;" $STD sudo -u postgres psql -c "CREATE USER $DB_USER WITH ENCRYPTED PASSWORD '$DB_PASS';" $STD sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER;" From bc28c336e4033acf7d17dfb3184c18acddeeea65 Mon Sep 17 00:00:00 2001 From: Lucas Fell Date: Fri, 15 Aug 2025 09:54:29 -0300 Subject: [PATCH 0692/1733] apply suggestions --- ct/ghostfolio.sh | 27 +++++++----- frontend/public/json/ghostfolio.json | 4 +- install/ghostfolio-install.sh | 62 +++------------------------- 3 files changed, 26 insertions(+), 67 deletions(-) diff --git a/ct/ghostfolio.sh b/ct/ghostfolio.sh index 1ad15294f..6595d5f76 100644 --- a/ct/ghostfolio.sh +++ b/ct/ghostfolio.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: lucasfell # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -38,18 +38,25 @@ function update_script() { msg_ok "Backup Created" msg_info "Updating $APP" - cd /opt/ghostfolio - git fetch --all - RELEASE=$(git describe --tags --abbrev=0 origin/main) - if [[ "${RELEASE}" != "$(cat /opt/ghostfolio_version.txt)" ]] || [[ ! -f /opt/ghostfolio_version.txt ]]; then - git checkout ${RELEASE} + systemctl stop ghostfolio + + if [[ -d /opt/ghostfolio ]]; then + rm -rf /opt/ghostfolio_backup + mv /opt/ghostfolio /opt/ghostfolio_backup + fi + + if fetch_and_deploy_gh_release "ghostfolio" "ghostfolio/ghostfolio" "tarball" "latest" "/opt/ghostfolio"; then + cd /opt/ghostfolio npm ci npm run build:production - npm run database:migrate - echo "${RELEASE}" >/opt/ghostfolio_version.txt - msg_ok "Updated $APP to ${RELEASE}" + npx prisma migrate deploy + msg_ok "Updated $APP" else - msg_ok "No update required. ${APP} is already at ${RELEASE}" + if [[ -d /opt/ghostfolio_backup ]]; then + rm -rf /opt/ghostfolio + mv /opt/ghostfolio_backup /opt/ghostfolio + fi + msg_ok "No update required or update failed. ${APP} is up to date" fi msg_info "Starting $APP" diff --git a/frontend/public/json/ghostfolio.json b/frontend/public/json/ghostfolio.json index 224bd7bb0..84a2868e0 100644 --- a/frontend/public/json/ghostfolio.json +++ b/frontend/public/json/ghostfolio.json @@ -1,7 +1,9 @@ { "name": "Ghostfolio", "slug": "ghostfolio", - "categories": [23], + "categories": [ + 23 + ], "date_created": "2025-08-14", "type": "ct", "updateable": true, diff --git a/install/ghostfolio-install.sh b/install/ghostfolio-install.sh index 6e5cf2326..3ecd67074 100644 --- a/install/ghostfolio-install.sh +++ b/install/ghostfolio-install.sh @@ -15,7 +15,6 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - git \ build-essential \ python3 \ openssl \ @@ -24,14 +23,12 @@ $STD apt-get install -y \ msg_ok "Installed Dependencies" PG_VERSION="15" setup_postgresql +NODE_VERSION="22" setup_nodejs msg_info "Installing Redis" $STD apt-get install -y redis-server -systemctl enable -q --now redis-server msg_ok "Installed Redis" -NODE_VERSION="22" setup_nodejs - msg_info "Setting up Database" DB_NAME=ghostfolio DB_USER=ghostfolio @@ -47,7 +44,6 @@ $STD sudo -u postgres psql -d $DB_NAME -c "GRANT ALL ON SCHEMA public TO $DB_USE $STD sudo -u postgres psql -d $DB_NAME -c "GRANT CREATE ON SCHEMA public TO $DB_USER;" $STD sudo -u postgres psql -d $DB_NAME -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO $DB_USER;" $STD sudo -u postgres psql -d $DB_NAME -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO $DB_USER;" - { echo "Ghostfolio Credentials" echo "Database User: $DB_USER" @@ -72,55 +68,16 @@ sed -i "s/# requirepass foobared/requirepass $REDIS_PASS/" /etc/redis/redis.conf systemctl restart redis-server msg_ok "Configured Redis" -msg_info "Cloning Ghostfolio" -cd /opt -$STD git clone https://github.com/ghostfolio/ghostfolio.git -cd ghostfolio -RELEASE=$(git describe --tags --abbrev=0) -git checkout $RELEASE -msg_ok "Cloned Ghostfolio $RELEASE" - -msg_info "Checking Build Resources" -current_ram=$(free -m | awk 'NR==2{print $2}') -if [[ "$current_ram" -lt 3584 ]]; then - msg_warn "Current RAM: ${current_ram}MB. Ghostfolio build requires ~4GB RAM for optimal performance." - msg_info "Creating temporary swap file for build process" - - # Check if swap already exists - if ! swapon --noheadings --show | grep -q 'swap'; then - TEMP_SWAP_FILE="/tmp/ghostfolio_build_swap" - $STD dd if=/dev/zero of="$TEMP_SWAP_FILE" bs=1M count=1024 - $STD chmod 600 "$TEMP_SWAP_FILE" - $STD mkswap "$TEMP_SWAP_FILE" - $STD swapon "$TEMP_SWAP_FILE" - SWAP_CREATED=true - msg_ok "Created 1GB temporary swap file" - else - msg_ok "Existing swap detected" - SWAP_CREATED=false - fi -else - msg_ok "Sufficient RAM available for build" - SWAP_CREATED=false -fi +fetch_and_deploy_gh_release "ghostfolio" "ghostfolio/ghostfolio" "tarball" "latest" "/opt/ghostfolio" msg_info "Installing Ghostfolio Dependencies" -export NODE_OPTIONS="--max-old-space-size=3584" -$STD npm ci +npm ci msg_ok "Installed Dependencies" msg_info "Building Ghostfolio (This may take several minutes)" -$STD npm run build:production +npm run build:production msg_ok "Built Ghostfolio" -# Clean up temporary swap if we created it -if [[ "$SWAP_CREATED" == "true" ]]; then - msg_info "Cleaning up temporary swap file" - $STD swapoff "$TEMP_SWAP_FILE" - $STD rm -f "$TEMP_SWAP_FILE" - msg_ok "Removed temporary swap file" -fi - msg_info "Optional CoinGecko API Configuration" echo echo -e "${YW}CoinGecko API keys are optional but provide better cryptocurrency data.${CL}" @@ -153,8 +110,8 @@ msg_ok "Set up Environment" msg_info "Running Database Migrations" cd /opt/ghostfolio -$STD npx prisma migrate deploy -$STD npx prisma db seed +npx prisma migrate deploy +npx prisma db seed msg_ok "Database Migrations Complete" msg_info "Creating Service" @@ -179,7 +136,6 @@ WantedBy=multi-user.target EOF systemctl enable -q --now ghostfolio -echo "$RELEASE" >/opt/ghostfolio_version.txt msg_ok "Created Service" motd_ssh @@ -190,9 +146,3 @@ npm cache clean --force $STD apt-get -y autoremove $STD apt-get -y autoclean msg_ok "Cleaned" - -msg_info "Installation Complete" -echo -e "${INFO}${YW}Ghostfolio is now running and accessible at http://$(hostname -I | awk '{print $1}'):3333${CL}" -echo -e "${INFO}${YW}Runtime memory usage: ~2GB (you can reduce container memory to 2GB if desired)${CL}" -echo -e "${INFO}${YW}First user to register will automatically get admin privileges${CL}" -msg_ok "Setup Complete" From eadc3b6969d27b164ae134792fea2e91e0459bdf Mon Sep 17 00:00:00 2001 From: Lucas Fell Date: Fri, 15 Aug 2025 10:53:33 -0300 Subject: [PATCH 0693/1733] fix install --- install/ghostfolio-install.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/install/ghostfolio-install.sh b/install/ghostfolio-install.sh index 3ecd67074..9495cfef0 100644 --- a/install/ghostfolio-install.sh +++ b/install/ghostfolio-install.sh @@ -71,6 +71,7 @@ msg_ok "Configured Redis" fetch_and_deploy_gh_release "ghostfolio" "ghostfolio/ghostfolio" "tarball" "latest" "/opt/ghostfolio" msg_info "Installing Ghostfolio Dependencies" +cd /opt/ghostfolio npm ci msg_ok "Installed Dependencies" @@ -78,13 +79,13 @@ msg_info "Building Ghostfolio (This may take several minutes)" npm run build:production msg_ok "Built Ghostfolio" -msg_info "Optional CoinGecko API Configuration" +msg_ok "Optional CoinGecko API Configuration" echo echo -e "${YW}CoinGecko API keys are optional but provide better cryptocurrency data.${CL}" echo -e "${YW}You can skip this and add them later by editing /opt/ghostfolio/.env${CL}" echo -read -rp "${TAB3}Enter CoinGecko Demo API key (optional, press Enter to skip): " COINGECKO_DEMO_KEY -read -rp "${TAB3}Enter CoinGecko Pro API key (optional, press Enter to skip): " COINGECKO_PRO_KEY +read -rp "${TAB3}CoinGecko Demo API key (press Enter to skip): " COINGECKO_DEMO_KEY +read -rp "${TAB3}CoinGecko Pro API key (press Enter to skip): " COINGECKO_PRO_KEY msg_info "Setting up Environment" cat </opt/ghostfolio/.env From 16f5e1bd3799bf04ebcfcd41f3f892d2c8d4400e Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sun, 24 Aug 2025 06:43:00 +0000 Subject: [PATCH 0694/1733] Update .app files --- ct/headers/alpine-redlib | 6 ++++++ ct/headers/redlib | 6 ------ ct/headers/resiliosync | 6 ++++++ 3 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 ct/headers/alpine-redlib delete mode 100644 ct/headers/redlib create mode 100644 ct/headers/resiliosync diff --git a/ct/headers/alpine-redlib b/ct/headers/alpine-redlib new file mode 100644 index 000000000..7d8f1130b --- /dev/null +++ b/ct/headers/alpine-redlib @@ -0,0 +1,6 @@ + ___ __ _ ____ _____ __ + / | / /___ (_)___ ___ / __ \___ ____/ / (_) /_ + / /| | / / __ \/ / __ \/ _ \______/ /_/ / _ \/ __ / / / __ \ + / ___ |/ / /_/ / / / / / __/_____/ _, _/ __/ /_/ / / / /_/ / +/_/ |_/_/ .___/_/_/ /_/\___/ /_/ |_|\___/\__,_/_/_/_.___/ + /_/ diff --git a/ct/headers/redlib b/ct/headers/redlib deleted file mode 100644 index b43c70b7d..000000000 --- a/ct/headers/redlib +++ /dev/null @@ -1,6 +0,0 @@ - ____ _____ __ - / __ \___ ____/ / (_) /_ - / /_/ / _ \/ __ / / / __ \ - / _, _/ __/ /_/ / / / /_/ / -/_/ |_|\___/\__,_/_/_/_.___/ - diff --git a/ct/headers/resiliosync b/ct/headers/resiliosync new file mode 100644 index 000000000..500ed27e1 --- /dev/null +++ b/ct/headers/resiliosync @@ -0,0 +1,6 @@ + ____ _ ___ _____ + / __ \___ _____(_) (_)___ / ___/__ ______ _____ + / /_/ / _ \/ ___/ / / / __ \ \__ \/ / / / __ \/ ___/ + / _, _/ __(__ ) / / / /_/ / ___/ / /_/ / / / / /__ +/_/ |_|\___/____/_/_/_/\____/ /____/\__, /_/ /_/\___/ + /____/ From 3c307e1e9df40444e45f321649e956c0d5d598ae Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sun, 24 Aug 2025 09:48:55 -0400 Subject: [PATCH 0695/1733] MediaManager: fix start script path, move email prompt to start --- install/mediamanager-install.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/install/mediamanager-install.sh b/install/mediamanager-install.sh index ad2cb3456..f58d12ff6 100644 --- a/install/mediamanager-install.sh +++ b/install/mediamanager-install.sh @@ -13,6 +13,11 @@ setting_up_container network_check update_os +read -r -p "Enter the email address of your first admin user: " admin_email +if [[ "$admin_email" ]]; then + EMAIL="$admin_email" +fi + msg_info "Installing dependencies" $STD apt-get install -y yq msg_ok "Installed dependencies" @@ -60,11 +65,6 @@ $STD /usr/local/bin/uv venv "$VIRTUAL_ENV" $STD /usr/local/bin/uv sync --locked --active msg_ok "Configured MediaManager" -read -r -p "Enter the email address of your first admin user: " admin_email -if [[ "$admin_email" ]]; then - EMAIL="$admin_email" -fi - msg_info "Creating config and start script" LOCAL_IP="$(hostname -I | awk '{print $1}')" SECRET="$(openssl rand -hex 32)" @@ -81,7 +81,7 @@ sed -e "s/localhost:8/$LOCAL_IP:8/g" \ mkdir -p "$MEDIA_DIR"/{images,tv,movies,torrents} -cat </opt/"$MM_DIR"/start.sh +cat <"$MM_DIR"/start.sh #!/usr/bin/env bash export CONFIG_DIR="$CONFIG_DIR" From 2176ffc1c6aec899eca6c8612c78b20058ef11a3 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sun, 24 Aug 2025 10:00:52 -0400 Subject: [PATCH 0696/1733] MediaManager: fix more path errors --- install/mediamanager-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/mediamanager-install.sh b/install/mediamanager-install.sh index f58d12ff6..f37a4a110 100644 --- a/install/mediamanager-install.sh +++ b/install/mediamanager-install.sh @@ -88,12 +88,12 @@ export CONFIG_DIR="$CONFIG_DIR" export FRONTEND_FILES_DIR="$FRONTEND_FILES_DIR" export BASE_PATH="" -cd /opt/"$MM_DIR" +cd "$MM_DIR" source ./venv/bin/activate /usr/local/bin/uv run alembic upgrade head /usr/local/bin/uv run fastapi run ./media_manager/main.py --port 8000 EOF -chmod +x /opt/"$MM_DIR"/start.sh +chmod +x "$MM_DIR"/start.sh msg_ok "Created config and start script" msg_info "Creating service" From e779b9fcc2e4c97d93b8da7c6280844e5499bbfe Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sun, 24 Aug 2025 10:21:14 -0400 Subject: [PATCH 0697/1733] MediaManager: fix WorkingDir and add forward slash to sed command --- install/mediamanager-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/mediamanager-install.sh b/install/mediamanager-install.sh index f37a4a110..cb89b396c 100644 --- a/install/mediamanager-install.sh +++ b/install/mediamanager-install.sh @@ -69,7 +69,7 @@ msg_info "Creating config and start script" LOCAL_IP="$(hostname -I | awk '{print $1}')" SECRET="$(openssl rand -hex 32)" sed -e "s/localhost:8/$LOCAL_IP:8/g" \ - -e "s|/data/|$MEDIA_DIR|g" \ + -e "s|/data/|$MEDIA_DIR/|g" \ -e 's/"db"/"localhost"/' \ -e "s/user = \"MediaManager\"/user = \"$DB_USER\"/" \ -e "s/password = \"MediaManager\"/password = \"$DB_PASS\"/" \ @@ -104,7 +104,7 @@ After=network.target [Service] Type=simple -WorkingDirectory=/opt/"$MM_DIR" +WorkingDirectory="$MM_DIR" ExecStart=/usr/bin/bash start.sh [Install] From 817d622c2c30e542eb4fa5fce8dcf9380c80d693 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sun, 24 Aug 2025 10:24:56 -0400 Subject: [PATCH 0698/1733] MediaManager: increase resources, add note about media dir --- ct/mediamanager.sh | 2 +- frontend/public/json/mediamanager.json | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ct/mediamanager.sh b/ct/mediamanager.sh index cc67d8af5..f23230b48 100644 --- a/ct/mediamanager.sh +++ b/ct/mediamanager.sh @@ -8,7 +8,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV APP="MediaManager" var_tags="${var_tags:-arr}" var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" +var_ram="${var_ram:-3072}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-12}" diff --git a/frontend/public/json/mediamanager.json b/frontend/public/json/mediamanager.json index 5df6c4b78..c8590b012 100644 --- a/frontend/public/json/mediamanager.json +++ b/frontend/public/json/mediamanager.json @@ -21,7 +21,7 @@ "script": "ct/mediamanager.sh", "resources": { "cpu": 2, - "ram": 2048, + "ram": 3072, "hdd": 4, "os": "Debian", "version": "12" @@ -36,6 +36,10 @@ { "text": "During the installation, provide the email address of the first admin user", "type": "info" + }, + { + "text": "You're probably going to want to use a bind mount for the media directories", + "type": "info" } ] } From 6465201cd189516145862e17ac7b7ac62f91a713 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sun, 24 Aug 2025 10:26:34 -0400 Subject: [PATCH 0699/1733] MediaManager: move gh deploy outside of msg block --- ct/mediamanager.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/mediamanager.sh b/ct/mediamanager.sh index f23230b48..1909772e1 100644 --- a/ct/mediamanager.sh +++ b/ct/mediamanager.sh @@ -34,8 +34,8 @@ function update_script() { systemctl stop mediamanager msg_ok "Stopped Service" - msg_info "Updating ${APP}" fetch_and_deploy_gh_release "MediaManager" "maxdorninger/MediaManager" "tarball" "latest" "/opt/mediamanager" + msg_info "Updating ${APP}" MM_DIR="/opt/mm" export CONFIG_DIR="${MM_DIR}/config" export FRONTEND_FILES_DIR="${MM_DIR}/web/build" From 5f96851a5d166bafabeaf511c78e67a58d945165 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sun, 24 Aug 2025 10:33:39 -0400 Subject: [PATCH 0700/1733] MediaManager: remove quotes from var in service file --- install/mediamanager-install.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/install/mediamanager-install.sh b/install/mediamanager-install.sh index cb89b396c..860057e29 100644 --- a/install/mediamanager-install.sh +++ b/install/mediamanager-install.sh @@ -11,13 +11,14 @@ verb_ip6 catch_errors setting_up_container network_check -update_os read -r -p "Enter the email address of your first admin user: " admin_email if [[ "$admin_email" ]]; then EMAIL="$admin_email" fi +update_os + msg_info "Installing dependencies" $STD apt-get install -y yq msg_ok "Installed dependencies" @@ -104,7 +105,7 @@ After=network.target [Service] Type=simple -WorkingDirectory="$MM_DIR" +WorkingDirectory=${MM_DIR} ExecStart=/usr/bin/bash start.sh [Install] From 624c80e0fd2ce3214731faa43865be03ebe4ad6d Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sun, 24 Aug 2025 10:52:27 -0400 Subject: [PATCH 0701/1733] MediaManager: make email prompt stand out a bit more --- install/mediamanager-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/mediamanager-install.sh b/install/mediamanager-install.sh index 860057e29..3864c6d25 100644 --- a/install/mediamanager-install.sh +++ b/install/mediamanager-install.sh @@ -12,7 +12,7 @@ catch_errors setting_up_container network_check -read -r -p "Enter the email address of your first admin user: " admin_email +read -r -p "${TAB3}${YW}Enter the email address of your first admin user: ${CL}" admin_email if [[ "$admin_email" ]]; then EMAIL="$admin_email" fi From 682a1f800bf65edc6332e196dae4d8f96d2c6f57 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sun, 24 Aug 2025 10:55:56 -0400 Subject: [PATCH 0702/1733] remove colour vars --- install/mediamanager-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/mediamanager-install.sh b/install/mediamanager-install.sh index 3864c6d25..ee68d8850 100644 --- a/install/mediamanager-install.sh +++ b/install/mediamanager-install.sh @@ -12,7 +12,7 @@ catch_errors setting_up_container network_check -read -r -p "${TAB3}${YW}Enter the email address of your first admin user: ${CL}" admin_email +read -r -p "${TAB3}Enter the email address of your first admin user: " admin_email if [[ "$admin_email" ]]; then EMAIL="$admin_email" fi From b66e55fba23e9b2e869567d7d04b59bce5dada08 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Sun, 24 Aug 2025 19:51:23 +0200 Subject: [PATCH 0703/1733] Update environment configuration for Tracktor --- install/tracktor-install.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/install/tracktor-install.sh b/install/tracktor-install.sh index d2c5fde5f..3beefa4b0 100644 --- a/install/tracktor-install.sh +++ b/install/tracktor-install.sh @@ -23,10 +23,10 @@ $STD npm install $STD npm run build mkdir /opt/tracktor-data HOST_IP=$(hostname -I | awk '{print $1}') -cat </opt/tracktor/app/server/.env +cat </opt/tracktor/app/backend/.env NODE_ENV=production PUBLIC_DEMO_MODE=false -DB_PATH=/opt/tracktor-data/vehicles.db +DB_PATH=/opt/tracktor-data/tracktor.db PUBLIC_API_BASE_URL=http://$HOST_IP:3000 PORT=3000 EOF @@ -41,7 +41,7 @@ After=network.target [Service] Type=simple WorkingDirectory=/opt/tracktor -EnvironmentFile=/opt/tracktor/app/server/.env +EnvironmentFile=/opt/tracktor/app/backend/.env ExecStart=/usr/bin/npm start [Install] From e3e6d86b6ed72a914d121b326b5eb1bf04d499dd Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Sun, 24 Aug 2025 19:51:58 +0200 Subject: [PATCH 0704/1733] Update backup and restore paths in tracktor.sh --- ct/tracktor.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/tracktor.sh b/ct/tracktor.sh index 864210e38..1386344db 100644 --- a/ct/tracktor.sh +++ b/ct/tracktor.sh @@ -35,7 +35,7 @@ function update_script() { msg_ok "Stopped Service" msg_info "Creating Backup" - cp /opt/tracktor/app/server/.env /opt/tracktor.env + cp /opt/tracktor/app/backend/.env /opt/tracktor.env msg_ok "Created Backup" msg_info "Updating ${APP}" @@ -48,7 +48,7 @@ function update_script() { msg_ok "Updated $APP" msg_info "Restoring Backup" - cp /opt/tracktor.env /opt/tracktor/app/server/.env + cp /opt/tracktor.env /opt/tracktor/app/backend/.env msg_ok "Restored Backup" msg_info "Starting Service" From 74ec341965ae4da45eb79822e5e1027d445c05ec Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sun, 24 Aug 2025 19:42:14 -0400 Subject: [PATCH 0705/1733] MediaManager: make sure to update UV --- ct/mediamanager.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ct/mediamanager.sh b/ct/mediamanager.sh index 1909772e1..d633f657a 100644 --- a/ct/mediamanager.sh +++ b/ct/mediamanager.sh @@ -28,6 +28,8 @@ function update_script() { exit fi + setup_uv + RELEASE=$(curl -fsSL https://api.github.com/repos/maxdorninger/MediaManager/releases/latest | jq '.tag_name' | sed 's/^v//') if [[ "${RELEASE}" != "$(cat ~/.mediamanager 2>/dev/null)" ]] || [[ ! -f ~/.mediamanager ]]; then msg_info "Stopping Service" From a9a963f9e1eedd91d3123d2e828a0391f18758b0 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sun, 24 Aug 2025 20:19:09 -0400 Subject: [PATCH 0706/1733] Autocaliweb --- ct/autocaliweb.sh | 76 +++++++ frontend/public/json/autocaliweb.json | 35 +++ install/autocaliweb-install.sh | 310 ++++++++++++++++++++++++++ 3 files changed, 421 insertions(+) create mode 100644 ct/autocaliweb.sh create mode 100644 frontend/public/json/autocaliweb.json create mode 100644 install/autocaliweb-install.sh diff --git a/ct/autocaliweb.sh b/ct/autocaliweb.sh new file mode 100644 index 000000000..cc728dd6e --- /dev/null +++ b/ct/autocaliweb.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: vhsdream +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/gelbphoenix/autocaliweb + +APP="Autocaliweb" +var_tags="${var_tags:-ebooks}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-6}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/autocaliweb ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + setup_uv + + RELEASE=$(curl -fsSL https://api.github.com/repos/gelbphoenix/autocaliweb/releases/latest | jq '.tag_name' | sed 's/^v//') + if [[ "${RELEASE}" != "$(cat ~/.autocaliweb 2>/dev/null)" ]] || [[ ! -f ~/.autocaliweb ]]; then + msg_info "Stopping Services" + systemctl stop autocaliweb metadata-change-detector acw-ingestor acw-auto-zipper + msg_ok "Stopped Services" + + INSTALL_DIR="/opt/autocaliweb" + VIRTUAL_ENV="${INSTALL_DIR}/venv" + $STD tar -cf ~/autocaliweb_bkp.tar "$INSTALL_DIR"/{metadata_change_logs,dirs.json,scripts/ingest_watcher.sh,scripts/auto_zipper_wrapper.sh,scripts/metadata_change_detector_wrapper.sh} + fetch_and_deploy_gh_release "autocaliweb" "gelbphoenix/autocaliweb" "tarball" "latest" "/opt/autocaliweb" + msg_info "Updating ${APP}" + cd "$INSTALL_DIR" + $STD uv sync --all-extras --active + cd "$INSTALL_DIR"/koreader/plugins + PLUGIN_DIGEST="$(find acwsync.koplugin -type f -name "*.lua" -o -name "*.json" | sort | xargs sha256sum | sha256sum | cut -d' ' -f1)" + echo "Plugin files digest: $PLUGIN_DIGEST" >acwsync.koplugin/${PLUGIN_DIGEST}.digest + echo "Build date: $(date)" >>acwsync.koplugin/${PLUGIN_DIGEST}.digest + echo "Files included:" >>acwsync.koplugin/${PLUGIN_DIGEST}.digest + zip -r koplugin.zip acwsync.koplugin/ + cp -r koplugin.zip "$INSTALL_DIR"/cps/static + mkdir -p "$INSTALL_DIR"/metadata_temp + $STD tar -xf ~/autocaliweb_bkp.tar --directory / + chown -R acw:acw "$INSTALL_DIR" + msg_ok "Updated $APP" + + msg_info "Starting Services" + systemctl start autocaliweb metadata-change-detector acw-ingestor acw-auto-zipper + msg_ok "Started Services" + + msg_ok "Updated Successfully" + else + msg_ok "Already up to date" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8083${CL}" diff --git a/frontend/public/json/autocaliweb.json b/frontend/public/json/autocaliweb.json new file mode 100644 index 000000000..792645a2f --- /dev/null +++ b/frontend/public/json/autocaliweb.json @@ -0,0 +1,35 @@ +{ + "name": "Autocaliweb", + "slug": "autocaliweb", + "categories": [ + 13 + ], + "date_created": "2025-08-30", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 8083, + "documentation": "https://github.com/gelbphoenix/autocaliweb/wiki", + "config_path": "/etc/autocaliweb", + "website": "https://github.com/gelbphoenix/autocaliweb", + "logo": "https://github.com/gelbphoenix/autocaliweb/raw/refs/heads/master/cps/static/icon-dark.svg", + "description": "A modern web management system for eBooks, eComics and PDFs", + "install_methods": [ + { + "type": "default", + "script": "ct/autocaliweb.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 6, + "os": "Debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": "admin", + "password": "admin123" + }, + "notes": [] +} diff --git a/install/autocaliweb-install.sh b/install/autocaliweb-install.sh new file mode 100644 index 000000000..e9ed0dd45 --- /dev/null +++ b/install/autocaliweb-install.sh @@ -0,0 +1,310 @@ +#!/usr/bin/env bash + +# Copyright (c) 2025 Community Scripts ORG +# Author: vhsdream +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/gelbphoenix/autocaliweb + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing dependencies" +$STD apt-get install -y --no-install-recommends \ + git \ + sqlite3 \ + build-essential \ + libldap2-dev \ + libssl-dev \ + libsasl-dev \ + imagemagick \ + ghostscript \ + libmagic1 \ + libxi6 \ + libxslt1.1 \ + libxtst6 \ + libxrandr2 \ + libxkbfile1 \ + libxcomposite1 \ + libopengl0 \ + libnss3 \ + libxkbcommon0 \ + libegl1 \ + libxdamage1 \ + libgl1 \ + libglx-mesa0 \ + xz-utils \ + xdg-utils \ + inotify-tools \ + binutils \ + unrar-free \ + zip +msg_ok "Installed dependencies" + +fetch_and_deploy_gh_release "kepubify" "pgaskin/kepubify" "singlefile" "latest" "/usr/bin" "kepubify-linux-64bit" +fetch_and_deploy_gh_release "calibre" "kovidgoyal/calibre" "prebuild" "latest" "/opt/calibre" "calibre-*-x86_64.txz" + +setup_uv + +fetch_and_deploy_gh_release "autocaliweb" "gelbphoenix/autocaliweb" "tarball" "latest" "/opt/autocaliweb" + +msg_info "Configuring Autocaliweb" +INSTALL_DIR="/opt/autocaliweb" +CONFIG_DIR="/etc/autocaliweb" +CALIBRE_LIB_DIR="/opt/calibre-library" +INGEST_DIR="/opt/acw-book-ingest" +SERVICE_USER="acw" +SERVICE_GROUP="acw" +SCRIPTS_DIR="${INSTALL_DIR}/scripts" +VIRTUAL_ENV="${INSTALL_DIR}/venv" + +mkdir -p "$CONFIG_DIR"/{.config/calibre/plugins,log_archive,.acw_conversion_tmp} +mkdir -p "$CONFIG_DIR"/processed_books/{converted,imported,failed,fixed_originals} +mkdir -p "$INSTALL_DIR"/{metadata_change_logs,metadata_temp} +mkdir -p {"$CALIBRE_LIB_DIR","$INGEST_DIR"} + +cd "$INSTALL_DIR" +$STD uv venv "$VIRTUAL_ENV" +$STD uv sync --all-extras --active +cat <./dirs.json +{ + "ingest_folder": "$INGEST_DIR", + "calibre_library_dir": "$CALIBRE_LIB_DIR", + "tmp_conversion_dir": "$CONFIG_DIR/.acw_conversion_tmp" +} +EOF +useradd -s /usr/sbin/nologin -d "$CONFIG_DIR" -M "SERVICE_USER" +msg_ok "Configured Autocaliweb" + +msg_info "Creating ACWSync Plugin for KOReader" +cd "$INSTALL_DIR"/koreader/plugins +PLUGIN_DIGEST="$(find acwsync.koplugin -type f -name "*.lua" -o -name "*.json" | sort | xargs sha256sum | sha256sum | cut -d' ' -f1)" +echo "Plugin files digest: $PLUGIN_DIGEST" >acwsync.koplugin/${PLUGIN_DIGEST}.digest +echo "Build date: $(date)" >>acwsync.koplugin/${PLUGIN_DIGEST}.digest +echo "Files included:" >>acwsync.koplugin/${PLUGIN_DIGEST}.digest +zip -r koplugin.zip acwsync.koplugin/ +cp -r koplugin.zip "$INSTALL_DIR"/cps/static +msg_ok "Created ACWSync Plugin" + +msg_info "Initializing databases" +KEPUBIFY_PATH=$(command -v kepubify 2>/dev/null || echo "/usr/bin/kepubify") +EBOOK_CONVERT_PATH=$(command -v ebook-convert 2>/dev/null || echo "/usr/bin/ebook-convert") +CALIBRE_BIN_DIR=$(dirname "$EBOOK_CONVERT_PATH") +cp "$INSTALL_DIR"/library/metadata.db "$CALIBRE_LIB_DIR"/metadata.db + +cp "$INSTALL_DIR"/library/app.db "$CONFIG_DIR"/app.db +sqlite3 "$CONFIG_DIR/app.db" <"$SCRIPTS_DIR"/ingest_watcher.sh +#!/bin/bash + +WATCH_FOLDER=\$(grep -o '"ingest_folder": "[^"]*' \${INSTALL_DIR}/dirs.json | grep -o '[^"]*\$') +echo "[acw-ingest-service] Watching folder: \$WATCH_FOLDER" + +# Monitor the folder for new files +/usr/bin/inotifywait -m -r --format="%e %w%f" -e close_write -e moved_to "\$WATCH_FOLDER" | +while read -r events filepath ; do + echo "[acw-ingest-service] New files detected - \$filepath - Starting Ingest Processor..." + # Use the Python interpreter from the virtual environment + \${VIRTUAL_ENV}/bin/python \${SCRIPTS_DIR}/ingest_processor.py "\$filepath" +done +EOF + +# auto-zipper +cat <"$SCRIPTS_DIR"/auto_zipper_wrapper.sh +#!/bin/bash + +# Source virtual environment +source ${VIRTUAL_ENV}/bin/activate + +WAKEUP="23:59" + +while true; do + # Replace expr with modern Bash arithmetic (safer and less prone to parsing issues) + # fix: expr: non-integer argument and sleep: missing operand + SECS=\$(( \$(date -d "\$WAKEUP" +%s) - \$(date -d "now" +%s) )) + if [[ \$SECS -lt 0 ]]; then + SECS=\$(( \$(date -d "tomorrow \$WAKEUP" +%s) - \$(date -d "now" +%s) )) + fi + echo "[acw-auto-zipper] Next run in \$SECS seconds." + sleep \$SECS & + wait \$! + + # Use virtual environment python + python ${SCRIPTS_DIR}/auto_zip.py + + if [[ \$? == 1 ]]; then + echo "[acw-auto-zipper] Error occurred during script initialisation." + elif [[ \$? == 2 ]]; then + echo "[acw-auto-zipper] Error occurred while zipping today's files." + elif [[ \$? == 3 ]]; then + echo "[acw-auto-zipper] Error occurred while trying to remove zipped files." + fi + + sleep 60 +done +EOF + +# metadata change detector +cat <"$SCRIPTS_DIR"/metadata_change_detector_wrapper.sh +#!/bin/bash +# metadata_change_detector_wrapper.sh - Wrapper for periodic metadata enforcement + +# Source virtual environment +source ${VIRTUAL_ENV}/bin/activate + +# Configuration +CHECK_INTERVAL=300 # Check every 5 minutes (300 seconds) +METADATA_LOGS_DIR="${INSTALL_DIR}/metadata_change_logs" + +echo "[metadata-change-detector] Starting metadata change detector service..." +echo "[metadata-change-detector] Checking for changes every \$CHECK_INTERVAL seconds" + +while true; do + # Check if there are any log files to process + if [ -d "\$METADATA_LOGS_DIR" ] && [ "\$(ls -A \$METADATA_LOGS_DIR 2>/dev/null)" ]; then + echo "[metadata-change-detector] Found metadata change logs, processing..." + + # Process each log file + for log_file in "\$METADATA_LOGS_DIR"/*.json; do + if [ -f "\$log_file" ]; then + log_name=\$(basename "\$log_file") + echo "[metadata-change-detector] Processing log: \$log_name" + + # Call cover_enforcer.py with the log file + ${INSTALL_DIR}/venv/bin/python ${SCRIPTS_DIR}/cover_enforcer.py --log "\$log_name" + + if [ \$? -eq 0 ]; then + echo "[metadata-change-detector] Successfully processed \$log_name" + else + echo "[metadata-change-detector] Error processing \$log_name" + fi + fi + done + else + echo "[metadata-change-detector] No metadata changes detected" + fi + + echo "[metadata-change-detector] Sleeping for \$CHECK_INTERVAL seconds..." + sleep \$CHECK_INTERVAL +done +EOF +chmod +x "$SCRIPTS_DIR"/{ingest_watcher.sh,auto_zipper_wrapper.sh,metadata_change_detector_wrapper.sh} +chown -R "$SERVICE_USER":"$SERVICE_GROUP" {"$INSTALL_DIR","$CONFIG_DIR","$INGEST_DIR","$CALIBRE_LIB_DIR"} + +# systemd service files +SYS_PATH="/etc/systemd/system" +cat <"$SYS_PATH"/autocaliweb.service +[Unit] +Description=Autocaliweb +After=network.target +Wants=network-online.target +After=network-online.target + +[Service] +Type=simple +User=$SERVICE_USER +Group=$SERVICE_GROUP +WorkingDirectory=$INSTALL_DIR +Environment=PATH=$INSTALL_DIR/venv/bin:/usr/bin:/bin +Environment=PYTHONPATH=$SCRIPTS_DIR:$INSTALL_DIR +Environment=PYTHONDONTWRITEBYTECODE=1 +Environment=PYTHONUNBUFFERED=1 +Environment=CALIBRE_DBPATH=$CONFIG_DIR +ExecStart=$INSTALL_DIR/venv/bin/python $INSTALL_DIR/cps.py -p $CONFIG_DIR/app.db + +Restart=always +RestartSec=10 +StandardOutput=journal +StandardError=journal + +[Install] +WantedBy=multi-user.target +EOF + +cat <"$SYS_PATH"/acw-ingestor.service +[Unit] +Description=Autocaliweb Ingest Processor Service +After=autocaliweb.service +Requires=autocaliweb.service + +[Service] +User=${SERVICE_USER} +Group=${SERVICE_GROUP} +WorkingDirectory=${INSTALL_DIR} +Environment=CALIBRE_DBPATH=${CONFIG_DIR} +Environment=HOME=${CONFIG_DIR} +ExecStart=/bin/bash ${SCRIPTS_DIR}/ingest_watcher.sh +Restart=always +StandardOutput=journal +StandardError=journal + +[Install] +WantedBy=multi-user.target +EOF + +cat <"$SYS_PATH"/acw-auto-zipper.service +[Unit] +Description=Autocaliweb Auto Zipper Service +After=network.target + +[Service] +User=${SERVICE_USER} +Group=${SERVICE_GROUP} +WorkingDirectory=${INSTALL_DIR} +Environment=CALIBRE_DBPATH=${CONFIG_DIR} +ExecStart=${SCRIPTS_DIR}/auto_zipper_wrapper.sh +Restart=always +StandardOutput=journal +StandardError=journal + +[Install] +WantedBy=multi-user.target +EOF + +cat <"$SYS_PATH"/metadata-change-detector.service +[Unit] +Description=Autocaliweb Metadata Change Detector +After=network.target + +[Service] +User=${SERVICE_USER} +Group=${SERVICE_GROUP} +WorkingDirectory=${INSTALL_DIR} +ExecStart=/bin/bash ${SCRIPTS_DIR}/metadata_change_detector_wrapper.sh +Restart=always +StandardOutput=journal +StandardError=journal +Environment=CALIBRE_DBPATH=${CONFIG_DIR} +Environment=HOME=${CONFIG_DIR} +[Install] +WantedBy=multi-user.target +EOF + +systemctl -q enable --now autocaliweb acw-ingestor acw-auto-zipper metadata-change-detector +msg_ok "Created scripts and service files" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From c74acde6a0b983604566556acee8834826e98bb0 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sun, 24 Aug 2025 20:28:58 -0400 Subject: [PATCH 0707/1733] Autocaliweb: typo fix --- install/autocaliweb-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/autocaliweb-install.sh b/install/autocaliweb-install.sh index e9ed0dd45..fa08c860b 100644 --- a/install/autocaliweb-install.sh +++ b/install/autocaliweb-install.sh @@ -20,7 +20,7 @@ $STD apt-get install -y --no-install-recommends \ build-essential \ libldap2-dev \ libssl-dev \ - libsasl-dev \ + libsasl2-dev \ imagemagick \ ghostscript \ libmagic1 \ From 1f44bd8e4034cd4986ab1d5c5c171dfe842aee3f Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sun, 24 Aug 2025 20:47:13 -0400 Subject: [PATCH 0708/1733] Autocaliweb: manual install of Calibre; symlink plugin dir --- install/autocaliweb-install.sh | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/install/autocaliweb-install.sh b/install/autocaliweb-install.sh index fa08c860b..e9b121eb6 100644 --- a/install/autocaliweb-install.sh +++ b/install/autocaliweb-install.sh @@ -46,7 +46,15 @@ $STD apt-get install -y --no-install-recommends \ msg_ok "Installed dependencies" fetch_and_deploy_gh_release "kepubify" "pgaskin/kepubify" "singlefile" "latest" "/usr/bin" "kepubify-linux-64bit" -fetch_and_deploy_gh_release "calibre" "kovidgoyal/calibre" "prebuild" "latest" "/opt/calibre" "calibre-*-x86_64.txz" + +msg_info "Installing Calibre" +CALIBRE_RELEASE="$(curl -s https://api.github.com/repos/kovidgoyal/calibre/releases/latest | grep -o '"tag_name": "[^"]*' | cut -d'"' -f4)" +CALIBRE_VERSION=${CALIBRE_RELEASE#v} +curl -fsSL https://github.com/kovidgoyal/releases/download/${CALIBRE_RELEASE}/calibre-${CALIBRE_VERSION}-x86_64.txz -o /tmp/calibre.txz +$STD tar -xf /tmp/calibre.txz /opt/calibre +rm /tmp/calibre.txz +$STD /opt/calibre/calibre_postinstall +msg_ok "Calibre installed" setup_uv @@ -78,6 +86,7 @@ cat <./dirs.json } EOF useradd -s /usr/sbin/nologin -d "$CONFIG_DIR" -M "SERVICE_USER" +ln -sf "$CONFIG_DIR"/.config/calibre/plugins "$CONFIG_DIR"/calibre_plugins msg_ok "Configured Autocaliweb" msg_info "Creating ACWSync Plugin for KOReader" From 0ce12acf2692fd0bae86956f4da81f718dece696 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sun, 24 Aug 2025 20:54:38 -0400 Subject: [PATCH 0709/1733] Autocaliweb: fix Calibre download --- install/autocaliweb-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/autocaliweb-install.sh b/install/autocaliweb-install.sh index e9b121eb6..4c6e64008 100644 --- a/install/autocaliweb-install.sh +++ b/install/autocaliweb-install.sh @@ -50,7 +50,7 @@ fetch_and_deploy_gh_release "kepubify" "pgaskin/kepubify" "singlefile" "latest" msg_info "Installing Calibre" CALIBRE_RELEASE="$(curl -s https://api.github.com/repos/kovidgoyal/calibre/releases/latest | grep -o '"tag_name": "[^"]*' | cut -d'"' -f4)" CALIBRE_VERSION=${CALIBRE_RELEASE#v} -curl -fsSL https://github.com/kovidgoyal/releases/download/${CALIBRE_RELEASE}/calibre-${CALIBRE_VERSION}-x86_64.txz -o /tmp/calibre.txz +curl -fsSL https://github.com/kovidgoyal/calibre/releases/download/${CALIBRE_RELEASE}/calibre-${CALIBRE_VERSION}-x86_64.txz -o /tmp/calibre.txz $STD tar -xf /tmp/calibre.txz /opt/calibre rm /tmp/calibre.txz $STD /opt/calibre/calibre_postinstall From 15f835a6c2340cada88abf0df52fdd3047ccc957 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Mon, 25 Aug 2025 08:49:35 +0200 Subject: [PATCH 0710/1733] Remove package-lock.json during installation Remove package-lock.json before npm install. --- install/tracktor-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/install/tracktor-install.sh b/install/tracktor-install.sh index 3beefa4b0..00c6c1d7e 100644 --- a/install/tracktor-install.sh +++ b/install/tracktor-install.sh @@ -18,7 +18,6 @@ fetch_and_deploy_gh_release "tracktor" "javedh-dev/tracktor" "tarball" "latest" msg_info "Configuring Tracktor" cd /opt/tracktor -rm package-lock.json $STD npm install $STD npm run build mkdir /opt/tracktor-data From 6914a23c16710c2ffda7e69600e681b09282d5e8 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Mon, 25 Aug 2025 09:04:40 +0200 Subject: [PATCH 0711/1733] Remove package-lock.json deletion in tracktor.sh --- ct/tracktor.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/ct/tracktor.sh b/ct/tracktor.sh index 1386344db..c568f28d9 100644 --- a/ct/tracktor.sh +++ b/ct/tracktor.sh @@ -42,7 +42,6 @@ function update_script() { setup_nodejs fetch_and_deploy_gh_release "tracktor" "javedh-dev/tracktor" "tarball" "latest" "/opt/tracktor" cd /opt/tracktor - rm package-lock.json $STD npm install $STD npm run build msg_ok "Updated $APP" From aefaaa9a5d001350f2d2975149ba64387b629bd0 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 25 Aug 2025 07:05:06 +0000 Subject: [PATCH 0712/1733] Update .app files --- ct/headers/autocaliweb | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/autocaliweb diff --git a/ct/headers/autocaliweb b/ct/headers/autocaliweb new file mode 100644 index 000000000..a5aa7d63b --- /dev/null +++ b/ct/headers/autocaliweb @@ -0,0 +1,6 @@ + ___ __ ___ __ + / | __ __/ /_____ _________ _/ (_) _____ / /_ + / /| |/ / / / __/ __ \/ ___/ __ `/ / / | /| / / _ \/ __ \ + / ___ / /_/ / /_/ /_/ / /__/ /_/ / / /| |/ |/ / __/ /_/ / +/_/ |_\__,_/\__/\____/\___/\__,_/_/_/ |__/|__/\___/_.___/ + From 0334933d5c0145db32a22fda711c72f505f06b1f Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 25 Aug 2025 09:51:50 +0200 Subject: [PATCH 0713/1733] Update tools.func --- misc/tools.func | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 844fabd1a..1413c26cd 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -379,24 +379,6 @@ function setup_mysql() { # PHP_MAX_EXECUTION_TIME - (default: 300) # ------------------------------------------------------------------------------ -# ------------------------------------------------------------------------------ -# Installs PHP with selected modules and configures Apache/FPM support. -# -# Description: -# - Adds Sury PHP repo if needed -# - Installs default and user-defined modules -# - Patches php.ini for CLI, Apache, and FPM as needed -# -# Variables: -# PHP_VERSION - PHP version to install (default: 8.4) -# PHP_MODULE - Additional comma-separated modules -# PHP_APACHE - Set YES to enable PHP with Apache -# PHP_FPM - Set YES to enable PHP-FPM -# PHP_MEMORY_LIMIT - (default: 512M) -# PHP_UPLOAD_MAX_FILESIZE - (default: 128M) -# PHP_POST_MAX_SIZE - (default: 128M) -# PHP_MAX_EXECUTION_TIME - (default: 300) -# ------------------------------------------------------------------------------ function setup_php() { local PHP_VERSION="${PHP_VERSION:-8.4}" local PHP_MODULE="${PHP_MODULE:-}" From 2d550fb4b557453444e7075bd23bef35c0a44420 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 25 Aug 2025 13:58:20 +0200 Subject: [PATCH 0714/1733] finalize --- ct/healthchecks.sh | 39 ++++++++++++++++++++++---- frontend/public/json/healthchecks.json | 23 +++++++++------ install/healthchecks-install.sh | 2 +- 3 files changed, 49 insertions(+), 15 deletions(-) diff --git a/ct/healthchecks.sh b/ct/healthchecks.sh index 44218b6cb..f8e5e45fc 100644 --- a/ct/healthchecks.sh +++ b/ct/healthchecks.sh @@ -7,9 +7,9 @@ source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxV APP="healthchecks" var_tags="${var_tags:-monitoring}" -var_cpu="${var_cpu:-4}" -var_ram="${var_ram:-4096}" -var_disk="${var_disk:-20}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-5}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" @@ -23,11 +23,40 @@ function update_script() { header_info check_container_storage check_container_resources - if [[ ! -f /etc/systemd/system/healthchecks.service ]]; then + + if [[ ! -d /opt/healthchecks ]]; then msg_error "No ${APP} Installation Found!" exit fi - msg_error "No Update." + + RELEASE=$(curl -fsSL https://api.github.com/repos/healthchecks/healthchecks/releases/latest | jq '.tag_name' | sed 's/^"v//;s/"$//') + if [[ "${RELEASE}" != "$(cat ~/.healthchecks 2>/dev/null)" ]] || [[ ! -f ~/.healthchecks ]]; then + msg_info "Stopping $APP" + systemctl stop healthchecks + msg_ok "Stopped $APP" + + setup_uv + fetch_and_deploy_gh_release "healthchecks" "healthchecks/healthchecks" + + msg_info "Updating $APP to v${RELEASE}" + cd /opt/healthchecks + mkdir -p /opt/healthchecks/static-collected/ + $STD uv pip install wheel gunicorn -r requirements.txt --system + $STD uv run -- python manage.py makemigrations + $STD uv run -- python manage.py migrate --noinput + $STD uv run -- python manage.py collectstatic --noinput + $STD uv run -- python manage.py compress + msg_ok "Updated $APP to v${RELEASE}" + + msg_info "Starting $APP" + systemctl start healthchecks + systemctl restart caddy + msg_ok "Started $APP" + + msg_ok "Update Successful" + else + msg_ok "No update required. ${APP} is already at v${RELEASE}" + fi exit } diff --git a/frontend/public/json/healthchecks.json b/frontend/public/json/healthchecks.json index ac5f3fe20..a0187d17d 100644 --- a/frontend/public/json/healthchecks.json +++ b/frontend/public/json/healthchecks.json @@ -4,24 +4,24 @@ "categories": [ 9 ], - "date_created": "2025-07-02", + "date_created": "2025-08-25", "type": "ct", "updateable": true, "privileged": false, - "config_path": "/opt/healthchecks/.env", + "config_path": "/opt/healthchecks/hc/local_settings.py", "interface_port": 3000, "documentation": "https://healthchecks.io/", "website": "https://healthchecks.io/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/healthchecks.svg", - "description": "Healthchecks is an open-source self-hosted application.", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/healthchecks.webp", + "description": "Healthchecks is a cron job monitoring service. It listens for HTTP requests and email messages (\"pings\") from your cron jobs and scheduled tasks (\"checks\"). When a ping does not arrive on time, Healthchecks sends out alerts. Healthchecks comes with a web dashboard, API, 25+ integrations for delivering notifications, monthly email reports, WebAuthn 2FA support, team management features: projects, team members, read-only access.", "install_methods": [ { "type": "default", "script": "ct/healthchecks.sh", "resources": { - "cpu": 1, - "ram": 1024, - "hdd": 2, + "cpu": 2, + "ram": 2048, + "hdd": 5, "os": "Debian", "version": "12" } @@ -31,5 +31,10 @@ "username": null, "password": null }, - "notes": [] -} \ No newline at end of file + "notes": [ + { + "text": "if you change your LXC-IP, you need to update /etc/caddy/Caddyfile & /opt/healthchecks/hc/local_settings.py", + "type": "info" + } + ] +} diff --git a/install/healthchecks-install.sh b/install/healthchecks-install.sh index 051118468..2507149da 100644 --- a/install/healthchecks-install.sh +++ b/install/healthchecks-install.sh @@ -48,7 +48,7 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC'" } >>~/healthchecks.creds msg_ok "Set up Database" -fetch_and_deploy_gh_release "healthchecks" "healthchecks/healthchecks" "source" +fetch_and_deploy_gh_release "healthchecks" "healthchecks/healthchecks" msg_info "Setup healthchecks" cd /opt/healthchecks From 9b7fbdf7d73a91598480d2f4b9ae4463f8d92cb1 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 25 Aug 2025 14:25:18 +0200 Subject: [PATCH 0715/1733] cleanup --- .../backup/check_and_update_json_date.yml | 60 ---- .github/workflows/backup/shellcheck.yml | 60 ---- .../workflows/backup/update_json_date.yml.bak | 90 ------ .../backup/validate-formatting.yaml.bak | 133 -------- .../workflows/backup/validate-scripts.yml.bak | 234 -------------- .github/workflows/changelog-pr.yaml | 286 ------------------ .github/workflows/close-discussion.yaml | 164 ---------- .github/workflows/close-ttek-issue.yaml | 53 ---- .github/workflows/live/changelog-pr.yml | 228 -------------- .github/workflows/live/close-discussion.yml | 122 -------- .../live/create-docker-for-runner.yml | 37 --- .github/workflows/live/delete-json-branch.yml | 28 -- .github/workflows/live/github-release.yml | 57 ---- .github/workflows/live/script-test.yml | 177 ----------- .github/workflows/live/script_format.yml | 243 --------------- .github/workflows/live/update-json-date.yml | 133 -------- .github/workflows/live/validate-filenames.yml | 161 ---------- 17 files changed, 2266 deletions(-) delete mode 100644 .github/workflows/backup/check_and_update_json_date.yml delete mode 100644 .github/workflows/backup/shellcheck.yml delete mode 100644 .github/workflows/backup/update_json_date.yml.bak delete mode 100644 .github/workflows/backup/validate-formatting.yaml.bak delete mode 100644 .github/workflows/backup/validate-scripts.yml.bak delete mode 100644 .github/workflows/changelog-pr.yaml delete mode 100644 .github/workflows/close-discussion.yaml delete mode 100644 .github/workflows/close-ttek-issue.yaml delete mode 100644 .github/workflows/live/changelog-pr.yml delete mode 100644 .github/workflows/live/close-discussion.yml delete mode 100644 .github/workflows/live/create-docker-for-runner.yml delete mode 100644 .github/workflows/live/delete-json-branch.yml delete mode 100644 .github/workflows/live/github-release.yml delete mode 100644 .github/workflows/live/script-test.yml delete mode 100644 .github/workflows/live/script_format.yml delete mode 100644 .github/workflows/live/update-json-date.yml delete mode 100644 .github/workflows/live/validate-filenames.yml diff --git a/.github/workflows/backup/check_and_update_json_date.yml b/.github/workflows/backup/check_and_update_json_date.yml deleted file mode 100644 index cde3cbbad..000000000 --- a/.github/workflows/backup/check_and_update_json_date.yml +++ /dev/null @@ -1,60 +0,0 @@ -name: Update date_created in JSON files - -on: - # Dieser Trigger wird für das Öffnen von PRs sowie für das Aktualisieren von offenen PRs verwendet - pull_request: - types: [opened, synchronize] - schedule: - # Dieser Trigger wird 4x am Tag ausgelöst, um sicherzustellen, dass das Datum aktualisiert wird - - cron: "0 0,6,12,18 * * *" # Führt alle 6 Stunden aus - workflow_dispatch: # Manuelle Ausführung des Workflows möglich - -jobs: - update-date: - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Install yq - run: | - sudo apt-get update - sudo apt-get install -y yq - - - name: Set the current date - id: set_date - run: echo "TODAY=$(date -u +%Y-%m-%d)" >> $GITHUB_ENV - - - name: Check for changes in PR - run: | - # Hole den PR-Branch - PR_BRANCH="refs/pull/${{ github.event.pull_request.number }}/merge" - git fetch origin $PR_BRANCH - - # Liste alle JSON-Dateien im PR auf, die geändert wurden - CHANGED_JSON_FILES=$(git diff --name-only origin/main...$PR_BRANCH | grep '.json') - - if [ -z "$CHANGED_JSON_FILES" ]; then - echo "No JSON files changed in this PR." - exit 0 - fi - - # Gehe alle geänderten JSON-Dateien durch und aktualisiere das Datum - for file in $CHANGED_JSON_FILES; do - echo "Updating date_created in $file" - # Setze das aktuelle Datum - yq eval ".date_created = \"${{ env.TODAY }}\"" -i "$file" - git add "$file" - done - - - name: Commit and push changes - run: | - # Prüfe, ob es Änderungen gibt und committe sie - git config user.name "json-updater-bot" - git config user.email "github-actions[bot]@users.noreply.github.com" - - git commit -m "Update date_created to ${{ env.TODAY }}" || echo "No changes to commit" - - # Push zurück in den PR-Branch - git push origin $PR_BRANCH diff --git a/.github/workflows/backup/shellcheck.yml b/.github/workflows/backup/shellcheck.yml deleted file mode 100644 index 4385fc8e9..000000000 --- a/.github/workflows/backup/shellcheck.yml +++ /dev/null @@ -1,60 +0,0 @@ -name: Shellcheck - -on: - push: - branches: - - main - pull_request: - workflow_dispatch: - schedule: - - cron: "5 1 * * *" - -jobs: - shellcheck: - name: Shellcheck - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - - name: Get changed files - id: changed-files - uses: tj-actions/changed-files@v45 - with: - files: | - **.sh - - - name: Download ShellCheck - shell: bash - env: - INPUT_VERSION: "v0.10.0" - run: | - set -euo pipefail - if [[ "${{ runner.os }}" == "macOS" ]]; then - osvariant="darwin" - else - osvariant="linux" - fi - - baseurl="https://github.com/koalaman/shellcheck/releases/download" - curl -Lso "${{ github.workspace }}/sc.tar.xz" \ - "${baseurl}/${INPUT_VERSION}/shellcheck-${INPUT_VERSION}.${osvariant}.x86_64.tar.xz" - - tar -xf "${{ github.workspace }}/sc.tar.xz" -C "${{ github.workspace }}" - mv "${{ github.workspace }}/shellcheck-${INPUT_VERSION}/shellcheck" \ - "${{ github.workspace }}/shellcheck" - - - name: Verify ShellCheck binary - run: | - ls -l "${{ github.workspace }}/shellcheck" - - - name: Display ShellCheck version - run: | - "${{ github.workspace }}/shellcheck" --version - - - name: Run ShellCheck - if: steps.changed-files.outputs.any_changed == 'true' - env: - ALL_CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }} - run: | - echo "${ALL_CHANGED_FILES}" | xargs "${{ github.workspace }}/shellcheck" diff --git a/.github/workflows/backup/update_json_date.yml.bak b/.github/workflows/backup/update_json_date.yml.bak deleted file mode 100644 index 710125285..000000000 --- a/.github/workflows/backup/update_json_date.yml.bak +++ /dev/null @@ -1,90 +0,0 @@ -name: Auto Update JSON-Date - -on: - push: - branches: - - main - workflow_dispatch: - -jobs: - update-json-dates: - runs-on: ubuntu-latest - - permissions: - contents: write - pull-requests: write - - steps: - - name: Generate a token - id: generate-token - uses: actions/create-github-app-token@v2 - with: - app-id: ${{ vars.APP_ID }} - private-key: ${{ secrets.APP_PRIVATE_KEY }} - owner: community-scripts - repositories: ProxmoxVED - - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 # Full history for proper detection - - - name: Set up Git - run: | - git config --global user.name "GitHub Actions" - git config --global user.email "github-actions[bot]@users.noreply.github.com" - - - name: Find JSON files with incorrect date_created - id: find_wrong_json - run: | - TODAY=$(date -u +"%Y-%m-%d") - > incorrect_json_files.txt - - for FILE in json/*.json; do - if [[ -f "$FILE" ]]; then - DATE_IN_JSON=$(jq -r '.date_created' "$FILE" 2>/dev/null || echo "") - - if [[ "$DATE_IN_JSON" != "$TODAY" ]]; then - echo "$FILE" >> incorrect_json_files.txt - fi - fi - done - - if [[ -s incorrect_json_files.txt ]]; then - echo "CHANGED=true" >> $GITHUB_ENV - else - echo "CHANGED=false" >> $GITHUB_ENV - fi - - - name: Run update script - if: env.CHANGED == 'true' - run: | - chmod +x .github/workflows/scripts/update-json.sh - while read -r FILE; do - .github/workflows/scripts/update-json.sh "$FILE" - done < incorrect_json_files.txt - - - name: Commit and create PR if changes exist - if: env.CHANGED == 'true' - run: | - git add json/*.json - git commit -m "Auto-update date_created in incorrect JSON files" - git checkout -b pr-fix-json-dates - git push origin pr-fix-json-dates --force - gh pr create --title "[core] Fix incorrect JSON date_created fields" \ - --body "This PR is auto-generated to fix incorrect `date_created` fields in JSON files." \ - --head pr-fix-json-dates \ - --base main \ - --label "automated pr" - env: - GH_TOKEN: ${{ steps.generate-token.outputs.token }} - - - name: Approve pull request - if: env.CHANGED == 'true' - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - PR_NUMBER=$(gh pr list --head "pr-fix-json-dates" --json number --jq '.[].number') - if [ -n "$PR_NUMBER" ]; then - gh pr review $PR_NUMBER --approve - fi diff --git a/.github/workflows/backup/validate-formatting.yaml.bak b/.github/workflows/backup/validate-formatting.yaml.bak deleted file mode 100644 index 8eadd0acf..000000000 --- a/.github/workflows/backup/validate-formatting.yaml.bak +++ /dev/null @@ -1,133 +0,0 @@ -name: Validate script formatting - -on: - push: - branches: - - main - pull_request_target: - paths: - - "**/*.sh" - - "**/*.func" - -jobs: - shfmt: - name: Check changed files - runs-on: ubuntu-latest - permissions: - - pull-requests: write - - steps: - - name: Get pull request information - if: github.event_name == 'pull_request_target' - uses: actions/github-script@v7 - id: pr - with: - script: | - const { data: pullRequest } = await github.rest.pulls.get({ - ...context.repo, - pull_number: context.payload.pull_request.number, - }); - return pullRequest; - - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 # Ensure the full history is fetched for accurate diffing - ref: ${{ github.event_name == 'pull_request_target' && fromJSON(steps.pr.outputs.result).merge_commit_sha || '' }} - - - name: Get changed files - id: changed-files - run: | - if ${{ github.event_name == 'pull_request_target' }}; then - echo "files=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ steps.pr.outputs.result && fromJSON(steps.pr.outputs.result).merge_commit_sha }} | grep -E '\.(sh|func)$' | xargs)" >> $GITHUB_OUTPUT - else - echo "files=$(git diff --name-only ${{ github.event.before }} ${{ github.event.after }} | grep -E '\.(sh|func)$' | xargs)" >> $GITHUB_OUTPUT - fi - - - name: Set up Go - if: steps.changed-files.outputs.files != '' - uses: actions/setup-go@v5 - - - name: Install shfmt - if: steps.changed-files.outputs.files != '' - run: | - go install mvdan.cc/sh/v3/cmd/shfmt@latest - echo "$GOPATH/bin" >> $GITHUB_PATH - - - name: Run shfmt - if: steps.changed-files.outputs.files != '' - id: shfmt - run: | - set +e - - - shfmt_output=$(shfmt -d ${{ steps.changed-files.outputs.files }}) - if [[ $? -eq 0 ]]; then - exit 0 - else - echo "diff=\"$(echo -n "$shfmt_output" | base64 -w 0)\"" >> $GITHUB_OUTPUT - printf "%s" "$shfmt_output" - exit 1 - fi - - - name: Post comment with results - if: always() && steps.changed-files.outputs.files != '' && github.event_name == 'pull_request_target' - uses: actions/github-script@v7 - with: - script: | - const result = "${{ job.status }}" === "success" ? "success" : "failure"; - const diff = Buffer.from( - ${{ steps.shfmt.outputs.diff }}, - "base64", - ).toString(); - const issueNumber = context.payload.pull_request - ? context.payload.pull_request.number - : null; - const commentIdentifier = "validate-formatting"; - let newCommentBody = `\n### Script formatting\n\n`; - - if (result === "failure") { - newCommentBody += - `:x: We found issues in the formatting of the following changed files:\n\n\`\`\`diff\n${diff}\n\`\`\`\n`; - } else { - newCommentBody += `:rocket: All changed shell scripts are formatted correctly!\n`; - } - - newCommentBody += `\n\n`; - - if (issueNumber) { - const { data: comments } = await github.rest.issues.listComments({ - ...context.repo, - issue_number: issueNumber, - }); - - const existingComment = comments.find( - (comment) => comment.user.login === "github-actions[bot]", - - ); - - if (existingComment) { - if (existingComment.body.includes(commentIdentifier)) { - const re = new RegExp( - String.raw`[\s\S]*?`, - "", - ); - newCommentBody = existingComment.body.replace(re, newCommentBody); - } else { - newCommentBody = existingComment.body + "\n\n---\n\n" + newCommentBody; - } - - await github.rest.issues.updateComment({ - ...context.repo, - comment_id: existingComment.id, - body: newCommentBody, - }); - } else { - await github.rest.issues.createComment({ - ...context.repo, - issue_number: issueNumber, - body: newCommentBody, - }); - } - } diff --git a/.github/workflows/backup/validate-scripts.yml.bak b/.github/workflows/backup/validate-scripts.yml.bak deleted file mode 100644 index acb86132f..000000000 --- a/.github/workflows/backup/validate-scripts.yml.bak +++ /dev/null @@ -1,234 +0,0 @@ -name: Validate scripts -on: - push: - branches: - - main - pull_request_target: - paths: - - "ct/*.sh" - - "install/*.sh" - -jobs: - check-scripts: - name: Check changed files - runs-on: ubuntu-latest - permissions: - pull-requests: write - - steps: - - name: Debug event payload - run: | - echo "Event name: ${{ github.event_name }}" - echo "Payload: $(cat $GITHUB_EVENT_PATH)" - - - name: Get pull request information - if: github.event_name == 'pull_request_target' - uses: actions/github-script@v7 - id: pr - with: - script: | - const { data: pullRequest } = await github.rest.pulls.get({ - ...context.repo, - pull_number: context.payload.pull_request.number, - }); - return pullRequest; - - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - ref: ${{ github.event_name == 'pull_request_target' && fromJSON(steps.pr.outputs.result).merge_commit_sha || '' }} - - - name: Get changed files - id: changed-files - run: | - if [ "${{ github.event_name }}" == "pull_request_target" ]; then - echo "files=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ steps.pr.outputs.result && fromJSON(steps.pr.outputs.result).merge_commit_sha }} | grep -E '\.(sh|func)$' | xargs)" >> $GITHUB_OUTPUT - else - echo "files=$(git diff --name-only ${{ github.event.before }} ${{ github.event.after }} | grep -E '\.(sh|func)$' | xargs)" >> $GITHUB_OUTPUT - fi - - - name: Check build.func line - if: always() && steps.changed-files.outputs.files != '' - id: build-func - run: | - NON_COMPLIANT_FILES="" - for FILE in ${{ steps.changed-files.outputs.files }}; do - if [[ "$FILE" == ct/* ]] && [[ $(sed -n '2p' "$FILE") != "source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func)" ]]; then - NON_COMPLIANT_FILES="$NON_COMPLIANT_FILES $FILE" - fi - done - - if [ -n "$NON_COMPLIANT_FILES" ]; then - echo "files=$NON_COMPLIANT_FILES" >> $GITHUB_OUTPUT - echo "Build.func line missing or incorrect in files:" - for FILE in $NON_COMPLIANT_FILES; do - echo "$FILE" - done - exit 1 - fi - - - name: Check executable permissions - if: always() && steps.changed-files.outputs.files != '' - id: check-executable - run: | - NON_COMPLIANT_FILES="" - for FILE in ${{ steps.changed-files.outputs.files }}; do - if [[ ! -x "$FILE" ]]; then - NON_COMPLIANT_FILES="$NON_COMPLIANT_FILES $FILE" - fi - done - - if [ -n "$NON_COMPLIANT_FILES" ]; then - echo "files=$NON_COMPLIANT_FILES" >> $GITHUB_OUTPUT - echo "Files not executable:" - for FILE in $NON_COMPLIANT_FILES; do - echo "$FILE" - done - exit 1 - fi - - - name: Check copyright - if: always() && steps.changed-files.outputs.files != '' - id: check-copyright - run: | - NON_COMPLIANT_FILES="" - for FILE in ${{ steps.changed-files.outputs.files }}; do - if ! sed -n '3p' "$FILE" | grep -qE "^# Copyright \(c\) [0-9]{4}(-[0-9]{4})? (tteck \| community-scripts ORG|community-scripts ORG|tteck)$"; then - NON_COMPLIANT_FILES="$NON_COMPLIANT_FILES $FILE" - fi - done - - if [ -n "$NON_COMPLIANT_FILES" ]; then - echo "files=$NON_COMPLIANT_FILES" >> $GITHUB_OUTPUT - echo "Copyright header missing or not on line 3 in files:" - for FILE in $NON_COMPLIANT_FILES; do - echo "$FILE" - done - exit 1 - fi - - - name: Check author - if: always() && steps.changed-files.outputs.files != '' - id: check-author - run: | - NON_COMPLIANT_FILES="" - for FILE in ${{ steps.changed-files.outputs.files }}; do - if ! sed -n '4p' "$FILE" | grep -qE "^# Author: .+"; then - NON_COMPLIANT_FILES="$NON_COMPLIANT_FILES $FILE" - fi - done - - if [ -n "$NON_COMPLIANT_FILES" ]; then - echo "files=$NON_COMPLIANT_FILES" >> $GITHUB_OUTPUT - echo "Author header missing or invalid on line 4 in files:" - for FILE in $NON_COMPLIANT_FILES; do - echo "$FILE" - done - exit 1 - fi - - - name: Check license - if: always() && steps.changed-files.outputs.files != '' - id: check-license - run: | - NON_COMPLIANT_FILES="" - for FILE in ${{ steps.changed-files.outputs.files }}; do - if [[ "$(sed -n '5p' "$FILE")" != "# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE" ]]; then - NON_COMPLIANT_FILES="$NON_COMPLIANT_FILES $FILE" - fi - done - - if [ -n "$NON_COMPLIANT_FILES" ]; then - echo "files=$NON_COMPLIANT_FILES" >> $GITHUB_OUTPUT - echo "License header missing or not on line 5 in files:" - for FILE in $NON_COMPLIANT_FILES; do - echo "$FILE" - done - exit 1 - fi - - - name: Check source - if: always() && steps.changed-files.outputs.files != '' - id: check-source - run: | - NON_COMPLIANT_FILES="" - for FILE in ${{ steps.changed-files.outputs.files }}; do - if ! sed -n '6p' "$FILE" | grep -qE "^# Source: .+"; then - NON_COMPLIANT_FILES="$NON_COMPLIANT_FILES $FILE" - fi - done - - if [ -n "$NON_COMPLIANT_FILES" ]; then - echo "files=$NON_COMPLIANT_FILES" >> $GITHUB_OUTPUT - echo "Source header missing or not on line 6 in files:" - for FILE in $NON_COMPLIANT_FILES; do - echo "$FILE" - done - exit 1 - fi - - - name: Post results and comment - if: always() && steps.changed-files.outputs.files != '' && github.event_name == 'pull_request_target' - uses: actions/github-script@v7 - with: - script: | - const result = '${{ job.status }}' === 'success' ? 'success' : 'failure'; - const nonCompliantFiles = { - 'Invalid build.func source': "${{ steps.build-func.outputs.files || '' }}", - 'Not executable': "${{ steps.check-executable.outputs.files || '' }}", - 'Copyright header line missing or invalid': "${{ steps.check-copyright.outputs.files || '' }}", - 'Author header line missing or invalid': "${{ steps.check-author.outputs.files || '' }}", - 'License header line missing or invalid': "${{ steps.check-license.outputs.files || '' }}", - 'Source header line missing or invalid': "${{ steps.check-source.outputs.files || '' }}" - }; - - const issueNumber = context.payload.pull_request ? context.payload.pull_request.number : null; - const commentIdentifier = 'validate-scripts'; - let newCommentBody = `\n### Script validation\n\n`; - - if (result === 'failure') { - newCommentBody += ':x: We found issues in the following changed files:\n\n'; - for (const [check, files] of Object.entries(nonCompliantFiles)) { - if (files) { - newCommentBody += `**${check}:**\n`; - files.trim().split(' ').forEach(file => { - newCommentBody += `- ${file}: ${check}\n`; - }); - newCommentBody += `\n`; - } - } - } else { - newCommentBody += `:rocket: All changed shell scripts passed validation!\n`; - } - - newCommentBody += `\n\n`; - - if (issueNumber) { - const { data: comments } = await github.rest.issues.listComments({ - ...context.repo, - issue_number: issueNumber - }); - - const existingComment = comments.find(comment => - comment.body.includes(``) && - comment.user.login === 'github-actions[bot]' - ); - - if (existingComment) { - const re = new RegExp(String.raw`[\\s\\S]*?`, "m"); - newCommentBody = existingComment.body.replace(re, newCommentBody); - - await github.rest.issues.updateComment({ - ...context.repo, - comment_id: existingComment.id, - body: newCommentBody - }); - } else { - await github.rest.issues.createComment({ - ...context.repo, - issue_number: issueNumber, - body: newCommentBody - }); - } - } diff --git a/.github/workflows/changelog-pr.yaml b/.github/workflows/changelog-pr.yaml deleted file mode 100644 index 80959d54e..000000000 --- a/.github/workflows/changelog-pr.yaml +++ /dev/null @@ -1,286 +0,0 @@ -name: Create Changelog Pull Request - -on: - push: - branches: ["main"] - workflow_dispatch: - -jobs: - update-changelog-pull-request: - if: github.repository == 'community-scripts/ProxmoxVED' - runs-on: ubuntu-latest - env: - CONFIG_PATH: .github/changelog-pr-config.json - BRANCH_NAME: github-action-update-changelog - AUTOMATED_PR_LABEL: "automated pr" - permissions: - contents: write - pull-requests: write - steps: - - name: Generate a token for PR creation - id: generate-token - uses: actions/create-github-app-token@v2 - with: - app-id: ${{ vars.APP_ID }} - private-key: ${{ secrets.APP_PRIVATE_KEY }} - owner: community-scripts - repositories: ProxmoxVED - - - name: Generate a token for PR approval and merge - id: generate-token-merge - uses: actions/create-github-app-token@v2 - with: - app-id: ${{ secrets.APP_ID_APPROVE_AND_MERGE }} - private-key: ${{ secrets.APP_KEY_APPROVE_AND_MERGE }} - owner: community-scripts - repositories: ProxmoxVED - - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Get latest dates in changelog - run: | - DATES=$(grep -E '^## [0-9]{4}-[0-9]{2}-[0-9]{2}' CHANGELOG.md | head -n 2 | awk '{print $2}') - - LATEST_DATE=$(echo "$DATES" | sed -n '1p') - SECOND_LATEST_DATE=$(echo "$DATES" | sed -n '2p') - TODAY=$(date -u +%Y-%m-%d) - - echo "TODAY=$TODAY" >> $GITHUB_ENV - if [[ "$LATEST_DATE" == "$TODAY" ]]; then - echo "LATEST_DATE=$SECOND_LATEST_DATE" >> $GITHUB_ENV - else - echo "LATEST_DATE=$LATEST_DATE" >> $GITHUB_ENV - fi - - - name: Get categorized pull requests - id: get-categorized-prs - uses: actions/github-script@v7 - with: - script: | - async function main() { - const fs = require('fs').promises; - const path = require('path'); - - const configPath = path.resolve(process.env.CONFIG_PATH); - const fileContent = await fs.readFile(configPath, 'utf-8'); - const changelogConfig = JSON.parse(fileContent); - - const categorizedPRs = changelogConfig.map(obj => ({ - ...obj, - notes: [], - subCategories: obj.subCategories ?? ( - obj.labels.includes("update script") ? [ - { title: "🐞 Bug Fixes", labels: ["bugfix"], notes: [] }, - { title: "✨ New Features", labels: ["feature"], notes: [] }, - { title: "💥 Breaking Changes", labels: ["breaking change"], notes: [] }, - { title: "🔧 Refactor", labels: ["refactor"], notes: [] }, - ] : - obj.labels.includes("maintenance") ? [ - { title: "🐞 Bug Fixes", labels: ["bugfix"], notes: [] }, - { title: "✨ New Features", labels: ["feature"], notes: [] }, - { title: "💥 Breaking Changes", labels: ["breaking change"], notes: [] }, - { title: "📡 API", labels: ["api"], notes: [] }, - { title: "Github", labels: ["github"], notes: [] }, - { title: "📝 Documentation", labels: ["documentation"], notes: [] }, - { title: "🔧 Refactor", labels: ["refactor"], notes: [] } - ] : - obj.labels.includes("website") ? [ - { title: "🐞 Bug Fixes", labels: ["bugfix"], notes: [] }, - { title: "✨ New Features", labels: ["feature"], notes: [] }, - { title: "💥 Breaking Changes", labels: ["breaking change"], notes: [] }, - { title: "Script Information", labels: ["json"], notes: [] } - ] : [] - ) - })); - - const latestDateInChangelog = new Date(process.env.LATEST_DATE); - latestDateInChangelog.setUTCHours(23, 59, 59, 999); - - const { data: pulls } = await github.rest.pulls.list({ - owner: context.repo.owner, - repo: "ProxmoxVE", - base: "main", - state: "closed", - sort: "updated", - direction: "desc", - per_page: 100, - }); - - const filteredPRs = pulls.filter(pr => - pr.merged_at && - new Date(pr.merged_at) > latestDateInChangelog && - !pr.labels.some(label => - ["invalid", "wontdo", process.env.AUTOMATED_PR_LABEL].includes(label.name.toLowerCase()) - ) - ); - - for (const pr of filteredPRs) { - const prLabels = pr.labels.map(label => label.name.toLowerCase()); - if (pr.user.login.includes("push-app-to-main[bot]")) { - - const scriptName = pr.title; - try { - const { data: relatedIssues } = await github.rest.issues.listForRepo({ - owner: context.repo.owner, - repo: "ProxmoxVED", - state: "all", - labels: ["Started Migration To ProxmoxVE"] - }); - - const matchingIssue = relatedIssues.find(issue => - issue.title.toLowerCase().includes(scriptName.toLowerCase()) - ); - - if (matchingIssue) { - const issueAuthor = matchingIssue.user.login; - const issueAuthorUrl = `https://github.com/${issueAuthor}`; - prNote = `- ${pr.title} [@${issueAuthor}](${issueAuthorUrl}) ([#${pr.number}](${pr.html_url}))`; - } - else { - prNote = `- ${pr.title} ([#${pr.number}](${pr.html_url}))`; - } - } catch (error) { - console.error(`Error fetching related issues: ${error}`); - prNote = `- ${pr.title} ([#${pr.number}](${pr.html_url}))`; - } - }else{ - prNote = `- ${pr.title} [@${pr.user.login}](https://github.com/${pr.user.login}) ([#${pr.number}](${pr.html_url}))`; - } - - - if (prLabels.includes("new script")) { - const newScriptCategory = categorizedPRs.find(category => - category.title === "New Scripts" || category.labels.includes("new script")); - if (newScriptCategory) { - newScriptCategory.notes.push(prNote); - } - } else { - - let categorized = false; - const priorityCategories = categorizedPRs.slice(); - for (const category of priorityCategories) { - if (categorized) break; - if (category.labels.some(label => prLabels.includes(label))) { - if (category.subCategories && category.subCategories.length > 0) { - const subCategory = category.subCategories.find(sub => - sub.labels.some(label => prLabels.includes(label)) - ); - - if (subCategory) { - subCategory.notes.push(prNote); - } else { - category.notes.push(prNote); - } - } else { - category.notes.push(prNote); - } - categorized = true; - } - } - } - - } - - return categorizedPRs; - } - - return await main(); - - - name: Update CHANGELOG.md - uses: actions/github-script@v7 - with: - script: | - const fs = require('fs').promises; - const path = require('path'); - - const today = process.env.TODAY; - const latestDateInChangelog = process.env.LATEST_DATE; - const changelogPath = path.resolve('CHANGELOG.md'); - const categorizedPRs = ${{ steps.get-categorized-prs.outputs.result }}; - - let newReleaseNotes = `## ${today}\n\n`; - for (const { title, notes, subCategories } of categorizedPRs) { - const hasSubcategories = subCategories && subCategories.length > 0; - const hasMainNotes = notes.length > 0; - const hasSubNotes = hasSubcategories && subCategories.some(sub => sub.notes && sub.notes.length > 0); - - if (hasMainNotes || hasSubNotes) { - newReleaseNotes += `### ${title}\n\n`; - } - - if (hasMainNotes) { - newReleaseNotes += ` ${notes.join("\n")}\n\n`; - } - if (hasSubcategories) { - for (const { title: subTitle, notes: subNotes } of subCategories) { - if (subNotes && subNotes.length > 0) { - newReleaseNotes += ` - #### ${subTitle}\n\n`; - newReleaseNotes += ` ${subNotes.join("\n ")}\n\n`; - } - } - } - } - const changelogContent = await fs.readFile(changelogPath, 'utf-8'); - const changelogIncludesTodaysReleaseNotes = changelogContent.includes(`\n## ${today}`); - - const regex = changelogIncludesTodaysReleaseNotes - ? new RegExp(`## ${today}.*(?=## ${latestDateInChangelog})`, "gs") - : new RegExp(`(?=## ${latestDateInChangelog})`, "gs"); - - const newChangelogContent = changelogContent.replace(regex, newReleaseNotes); - await fs.writeFile(changelogPath, newChangelogContent); - - - name: Check for changes - id: verify-diff - run: | - git diff --quiet . || echo "changed=true" >> $GITHUB_ENV - - - name: Commit and push changes - if: env.changed == 'true' - run: | - git config --global user.name "github-actions[bot]" - git config --global user.email "github-actions[bot]@users.noreply.github.com" - git add CHANGELOG.md - git commit -m "Update CHANGELOG.md" - git checkout -b $BRANCH_NAME || git checkout $BRANCH_NAME - git push origin $BRANCH_NAME --force - - - name: Create pull request if not exists - if: env.changed == 'true' - env: - GH_TOKEN: ${{ steps.generate-token.outputs.token }} - run: | - PR_EXISTS=$(gh pr list --head "${BRANCH_NAME}" --json number --jq '.[].number') - if [ -z "$PR_EXISTS" ]; then - gh pr create --title "[Github Action] Update CHANGELOG.md" \ - --body "This PR is auto-generated by a Github Action to update the CHANGELOG.md file." \ - --head $BRANCH_NAME \ - --base main \ - --label "$AUTOMATED_PR_LABEL" - fi - - - name: Approve pull request - if: env.changed == 'true' - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - PR_NUMBER=$(gh pr list --head "${BRANCH_NAME}" --json number --jq '.[].number') - if [ -n "$PR_NUMBER" ]; then - gh pr review $PR_NUMBER --approve - fi - - - name: Approve pull request and merge - if: env.changed == 'true' - env: - GH_TOKEN: ${{ steps.generate-token-merge.outputs.token }} - run: | - git config --global user.name "github-actions-automege[bot]" - git config --global user.email "github-actions-automege[bot]@users.noreply.github.com" - PR_NUMBER=$(gh pr list --head "${BRANCH_NAME}" --json number --jq '.[].number') - if [ -n "$PR_NUMBER" ]; then - gh pr review $PR_NUMBER --approve - gh pr merge $PR_NUMBER --squash --admin - fi diff --git a/.github/workflows/close-discussion.yaml b/.github/workflows/close-discussion.yaml deleted file mode 100644 index 9b0352f43..000000000 --- a/.github/workflows/close-discussion.yaml +++ /dev/null @@ -1,164 +0,0 @@ -name: Close Discussion on PR Merge - -on: - push: - branches: - - main - -permissions: - contents: read - discussions: write - -jobs: - close-discussion: - if: github.repository == 'community-scripts/ProxmoxVED' - runs-on: ubuntu-latest - - steps: - - name: Checkout Repository - uses: actions/checkout@v4 - - - name: Set Up Node.js - uses: actions/setup-node@v4 - with: - node-version: "20" - - - name: Install Dependencies - run: npm install zx @octokit/graphql - - - name: Close Discussion - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GITHUB_SHA: ${{ github.sha }} - GITHUB_REPOSITORY: ${{ github.repository }} - run: | - npx zx << 'EOF' - import { graphql } from "@octokit/graphql"; - - (async function () { - try { - const token = process.env.GITHUB_TOKEN; - const commitSha = process.env.GITHUB_SHA; - const [owner, repo] = process.env.GITHUB_REPOSITORY.split("/"); - - if (!token || !commitSha || !owner || !repo) { - console.log("Missing required environment variables."); - process.exit(1); - } - - const graphqlWithAuth = graphql.defaults({ - headers: { authorization: `Bearer ${token}` }, - }); - - // Find PR from commit SHA - const searchQuery = ` - query($owner: String!, $repo: String!, $sha: GitObjectID!) { - repository(owner: $owner, name: $repo) { - object(oid: $sha) { - ... on Commit { - associatedPullRequests(first: 1) { - nodes { - number - body - } - } - } - } - } - } - `; - - const prResult = await graphqlWithAuth(searchQuery, { - owner, - repo, - sha: commitSha, - }); - - const pr = prResult.repository.object.associatedPullRequests.nodes[0]; - if (!pr) { - console.log("No PR found for this commit."); - return; - } - - const prNumber = pr.number; - const prBody = pr.body; - - const match = prBody.match(/#(\d+)/); - if (!match) { - console.log("No discussion ID found in PR body."); - return; - } - - const discussionNumber = match[1]; - console.log(`Extracted Discussion Number: ${discussionNumber}`); - - // Fetch GraphQL discussion ID - const discussionQuery = ` - query($owner: String!, $repo: String!, $number: Int!) { - repository(owner: $owner, name: $repo) { - discussion(number: $number) { - id - } - } - } - `; - - // - try { - const discussionResponse = await graphqlWithAuth(discussionQuery, { - owner, - repo, - number: parseInt(discussionNumber, 10), - }); - - const discussionQLId = discussionResponse.repository.discussion.id; - if (!discussionQLId) { - console.log("Failed to fetch discussion GraphQL ID."); - return; - } - } catch (error) { - console.error("Discussion not found or error occurred while fetching discussion:", error); - return; - } - - // Post comment - const commentMutation = ` - mutation($discussionId: ID!, $body: String!) { - addDiscussionComment(input: { discussionId: $discussionId, body: $body }) { - comment { id body } - } - } - `; - - const commentResponse = await graphqlWithAuth(commentMutation, { - discussionId: discussionQLId, - body: `Merged with PR #${prNumber}`, - }); - - const commentId = commentResponse.addDiscussionComment.comment.id; - if (!commentId) { - console.log("Failed to post the comment."); - return; - } - - console.log(`Comment Posted Successfully! Comment ID: ${commentId}`); - - // Mark comment as answer - const markAnswerMutation = ` - mutation($id: ID!) { - markDiscussionCommentAsAnswer(input: { id: $id }) { - discussion { id title } - } - } - `; - - await graphqlWithAuth(markAnswerMutation, { id: commentId }); - - console.log("Comment marked as answer successfully!"); - - } catch (error) { - console.error("Error:", error); - process.exit(1); - } - })(); - EOF diff --git a/.github/workflows/close-ttek-issue.yaml b/.github/workflows/close-ttek-issue.yaml deleted file mode 100644 index 037d60757..000000000 --- a/.github/workflows/close-ttek-issue.yaml +++ /dev/null @@ -1,53 +0,0 @@ -name: Auto-Close tteck Issues - -on: - issues: - types: [opened] - -jobs: - close_tteck_issues: - if: github.repository == 'community-scripts/ProxmoxVED' - runs-on: ubuntu-latest - steps: - - name: Auto-close if tteck script detected - uses: actions/github-script@v7 - with: - script: | - const issue = context.payload.issue; - const content = `${issue.title}\n${issue.body}`; - const issueNumber = issue.number; - - // Check for tteck script mention - if (content.includes("tteck") || content.includes("tteck/Proxmox")) { - const message = `Hello, it looks like you are referencing the **old tteck repo**. - - This repository is no longer used for active scripts. - **Please update your bookmarks** and use: [https://helper-scripts.com](https://helper-scripts.com) - - Also make sure your Bash command starts with: - \`\`\`bash - bash <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/...) - \`\`\` - - This issue is being closed automatically.`; - - await github.rest.issues.createComment({ - ...context.repo, - issue_number: issueNumber, - body: message - }); - - // Optionally apply a label like "not planned" - await github.rest.issues.addLabels({ - ...context.repo, - issue_number: issueNumber, - labels: ["not planned"] - }); - - // Close the issue - await github.rest.issues.update({ - ...context.repo, - issue_number: issueNumber, - state: "closed" - }); - } diff --git a/.github/workflows/live/changelog-pr.yml b/.github/workflows/live/changelog-pr.yml deleted file mode 100644 index 87da55143..000000000 --- a/.github/workflows/live/changelog-pr.yml +++ /dev/null @@ -1,228 +0,0 @@ -name: Create Changelog Pull Request - -on: - push: - branches: ["main"] - workflow_dispatch: - -jobs: - update-changelog-pull-request: - runs-on: ubuntu-latest - env: - CONFIG_PATH: .github/changelog-pr-config.json - BRANCH_NAME: github-action-update-changelog - AUTOMATED_PR_LABEL: "automated pr" - permissions: - contents: write - pull-requests: write - steps: - - name: Generate a token - id: generate-token - uses: actions/create-github-app-token@v2 - with: - app-id: ${{ vars.APP_ID }} - private-key: ${{ secrets.APP_PRIVATE_KEY }} - owner: community-scripts - repositories: ProxmoxVED - - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Get latest dates in changelog - run: | - DATES=$(grep -E '^## [0-9]{4}-[0-9]{2}-[0-9]{2}' CHANGELOG.md | head -n 2 | awk '{print $2}') - - LATEST_DATE=$(echo "$DATES" | sed -n '1p') - SECOND_LATEST_DATE=$(echo "$DATES" | sed -n '2p') - TODAY=$(date -u +%Y-%m-%d) - - echo "TODAY=$TODAY" >> $GITHUB_ENV - if [[ "$LATEST_DATE" == "$TODAY" ]]; then - echo "LATEST_DATE=$SECOND_LATEST_DATE" >> $GITHUB_ENV - else - echo "LATEST_DATE=$LATEST_DATE" >> $GITHUB_ENV - fi - - - name: Get categorized pull requests - id: get-categorized-prs - uses: actions/github-script@v7 - with: - script: | - const fs = require('fs').promises; - const path = require('path'); - - const configPath = path.resolve(process.env.CONFIG_PATH); - const fileContent = await fs.readFile(configPath, 'utf-8'); - const changelogConfig = JSON.parse(fileContent); - - const categorizedPRs = changelogConfig.map(obj => ({ - ...obj, - notes: [], - subCategories: obj.subCategories ?? ( - obj.labels.includes("update script") ? [ - { title: "🐞 Bug Fixes", labels: ["bugfix"], notes: [] }, - { title: "✨ New Features", labels: ["feature"], notes: [] }, - { title: "💥 Breaking Changes", labels: ["breaking change"], notes: [] } - ] : - obj.labels.includes("maintenance") ? [ - { title: "🐞 Bug Fixes", labels: ["bugfix"], notes: [] }, - { title: "✨ New Features", labels: ["feature"], notes: [] }, - { title: "💥 Breaking Changes", labels: ["breaking change"], notes: [] }, - { title: "📡 API", labels: ["api"], notes: [] }, - { title: "Github", labels: ["github"], notes: [] } - ] : - obj.labels.includes("website") ? [ - { title: "🐞 Bug Fixes", labels: ["bugfix"], notes: [] }, - { title: "✨ New Features", labels: ["feature"], notes: [] }, - { title: "💥 Breaking Changes", labels: ["breaking change"], notes: [] }, - { title: "Script Information", labels: ["json"], notes: [] } - ] : [] - ) - })); - - const latestDateInChangelog = new Date(process.env.LATEST_DATE); - latestDateInChangelog.setUTCHours(23, 59, 59, 999); - - const { data: pulls } = await github.rest.pulls.list({ - owner: context.repo.owner, - repo: context.repo.repo, - base: "main", - state: "closed", - sort: "updated", - direction: "desc", - per_page: 100, - }); - - pulls.filter(pr => - pr.merged_at && - new Date(pr.merged_at) > latestDateInChangelog && - !pr.labels.some(label => - ["invalid", "wontdo", process.env.AUTOMATED_PR_LABEL].includes(label.name.toLowerCase()) - ) - ).forEach(pr => { - - const prLabels = pr.labels.map(label => label.name.toLowerCase()); - const prNote = `- ${pr.title} [@${pr.user.login}](https://github.com/${pr.user.login}) ([#${pr.number}](${pr.html_url}))`; - - const updateScriptsCategory = categorizedPRs.find(category => - category.labels.some(label => prLabels.includes(label)) - ); - - if (updateScriptsCategory) { - - const subCategory = updateScriptsCategory.subCategories.find(sub => - sub.labels.some(label => prLabels.includes(label)) - ); - - if (subCategory) { - subCategory.notes.push(prNote); - } else { - updateScriptsCategory.notes.push(prNote); - } - } - }); - - console.log(JSON.stringify(categorizedPRs, null, 2)); - - return categorizedPRs; - - - - name: Update CHANGELOG.md - uses: actions/github-script@v7 - with: - script: | - const fs = require('fs').promises; - const path = require('path'); - - const today = process.env.TODAY; - const latestDateInChangelog = process.env.LATEST_DATE; - const changelogPath = path.resolve('CHANGELOG.md'); - const categorizedPRs = ${{ steps.get-categorized-prs.outputs.result }}; - - console.log(JSON.stringify(categorizedPRs, null, 2)); - - - let newReleaseNotes = `## ${today}\n\n`; - for (const { title, notes, subCategories } of categorizedPRs) { - const hasSubcategories = subCategories && subCategories.length > 0; - const hasMainNotes = notes.length > 0; - const hasSubNotes = hasSubcategories && subCategories.some(sub => sub.notes && sub.notes.length > 0); - - - if (hasMainNotes || hasSubNotes) { - newReleaseNotes += `### ${title}\n\n`; - } - - if (hasMainNotes) { - newReleaseNotes += ` ${notes.join("\n")}\n\n`; - } - if (hasSubcategories) { - for (const { title: subTitle, notes: subNotes } of subCategories) { - if (subNotes && subNotes.length > 0) { - newReleaseNotes += ` - #### ${subTitle}\n\n`; - newReleaseNotes += ` ${subNotes.join("\n ")}\n\n`; - } - } - } - } - - const changelogContent = await fs.readFile(changelogPath, 'utf-8'); - const changelogIncludesTodaysReleaseNotes = changelogContent.includes(`\n## ${today}`); - - const regex = changelogIncludesTodaysReleaseNotes - ? new RegExp(`## ${today}.*(?=## ${latestDateInChangelog})`, "gs") - : new RegExp(`(?=## ${latestDateInChangelog})`, "gs"); - - const newChangelogContent = changelogContent.replace(regex, newReleaseNotes); - await fs.writeFile(changelogPath, newChangelogContent); - - - name: Check for changes - id: verify-diff - run: | - git diff --quiet . || echo "changed=true" >> $GITHUB_ENV - - - name: Commit and push changes - if: env.changed == 'true' - run: | - git config --global user.name "github-actions[bot]" - git config --global user.email "github-actions[bot]@users.noreply.github.com" - git add CHANGELOG.md - git commit -m "Update CHANGELOG.md" - git checkout -b $BRANCH_NAME || git checkout $BRANCH_NAME - git push origin $BRANCH_NAME --force - - - name: Create pull request if not exists - if: env.changed == 'true' - env: - GH_TOKEN: ${{ steps.generate-token.outputs.token }} - run: | - PR_EXISTS=$(gh pr list --head "${BRANCH_NAME}" --json number --jq '.[].number') - if [ -z "$PR_EXISTS" ]; then - gh pr create --title "[Github Action] Update CHANGELOG.md" \ - --body "This PR is auto-generated by a Github Action to update the CHANGELOG.md file." \ - --head $BRANCH_NAME \ - --base main \ - --label "$AUTOMATED_PR_LABEL" - fi - - - name: Approve pull request - if: env.changed == 'true' - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - PR_NUMBER=$(gh pr list --head "${BRANCH_NAME}" --json number --jq '.[].number') - if [ -n "$PR_NUMBER" ]; then - gh pr review $PR_NUMBER --approve - fi - - - name: Re-approve pull request after update - if: env.changed == 'true' - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - PR_NUMBER=$(gh pr list --head "${BRANCH_NAME}" --json number --jq '.[].number') - if [ -n "$PR_NUMBER" ]; then - gh pr review $PR_NUMBER --approve - fi diff --git a/.github/workflows/live/close-discussion.yml b/.github/workflows/live/close-discussion.yml deleted file mode 100644 index 4b39fbf96..000000000 --- a/.github/workflows/live/close-discussion.yml +++ /dev/null @@ -1,122 +0,0 @@ -name: Close Discussion on PR Merge - -on: - pull_request: - types: [closed] - -jobs: - close-discussion: - runs-on: ubuntu-latest - - steps: - - name: Checkout Repository - uses: actions/checkout@v4 - - - name: Set Up Node.js - uses: actions/setup-node@v4 - with: - node-version: "20" - - name: Install Dependencies - run: npm install zx @octokit/graphql - - - name: Close Discussion - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - PR_BODY: ${{ github.event.pull_request.body }} - PR_NUMBER: ${{ github.event.pull_request.number }} - REPO_OWNER: ${{ github.repository_owner }} - REPO_NAME: ${{ github.event.repository.name }} - run: | - npx zx << 'EOF' - import { graphql } from "@octokit/graphql"; - (async function() { - try { - const token = process.env.GITHUB_TOKEN; - const prBody = process.env.PR_BODY; - const prNumber = process.env.PR_NUMBER; - const owner = process.env.REPO_OWNER; - const repo = process.env.REPO_NAME; - - if (!token || !prBody || !prNumber || !owner || !repo) { - console.log("Missing required environment variables."); - process.exit(1); - } - - const match = prBody.match(/#(\d+)/); - if (!match) { - console.log("No discussion ID found in PR body."); - return; - } - const discussionNumber = match[1]; - - console.log(`Extracted Discussion Number: ${discussionNumber}`); - console.log(`PR Number: ${prNumber}`); - console.log(`Repository: ${owner}/${repo}`); - - const graphqlWithAuth = graphql.defaults({ - headers: { authorization: `Bearer ${token}` }, - }); - - const discussionQuery = ` - query($owner: String!, $repo: String!, $number: Int!) { - repository(owner: $owner, name: $repo) { - discussion(number: $number) { - id - } - } - } - `; - - const discussionResponse = await graphqlWithAuth(discussionQuery, { - owner, - repo, - number: parseInt(discussionNumber, 10), - }); - - const discussionQLId = discussionResponse.repository.discussion.id; - if (!discussionQLId) { - console.log("Failed to fetch discussion GraphQL ID."); - return; - } - - console.log(`GraphQL Discussion ID: ${discussionQLId}`); - - const commentMutation = ` - mutation($discussionId: ID!, $body: String!) { - addDiscussionComment(input: { discussionId: $discussionId, body: $body }) { - comment { id body } - } - } - `; - - const commentResponse = await graphqlWithAuth(commentMutation, { - discussionId: discussionQLId, - body: `Merged with PR #${prNumber}`, - }); - - const commentId = commentResponse.addDiscussionComment.comment.id; - if (!commentId) { - console.log("Failed to post the comment."); - return; - } - - console.log(`Comment Posted Successfully! Comment ID: ${commentId}`); - - const markAnswerMutation = ` - mutation($id: ID!) { - markDiscussionCommentAsAnswer(input: { id: $id }) { - discussion { id title } - } - } - `; - - await graphqlWithAuth(markAnswerMutation, { id: commentId }); - - console.log("Comment marked as answer successfully!"); - - } catch (error) { - console.error("Error:", error); - return; - } - })(); - EOF \ No newline at end of file diff --git a/.github/workflows/live/create-docker-for-runner.yml b/.github/workflows/live/create-docker-for-runner.yml deleted file mode 100644 index c9fef0a5c..000000000 --- a/.github/workflows/live/create-docker-for-runner.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Build and Publish Docker Image - -on: - push: - branches: - - main - paths: - - '.github/runner/docker/**' - schedule: - - cron: '0 0 * * *' - -jobs: - build: - runs-on: ubuntu-latest #To ensure it always builds we use the github runner with all the right tooling - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Log in to GHCR - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build Docker image - run: | - repo_name=${{ github.repository }} # Get repository name - repo_name_lower=$(echo $repo_name | tr '[:upper:]' '[:lower:]') # Convert to lowercase - docker build -t ghcr.io/$repo_name_lower/gh-runner-self:latest -f .github/runner/docker/gh-runner-self.dockerfile . - - - name: Push Docker image to GHCR - run: | - repo_name=${{ github.repository }} # Get repository name - repo_name_lower=$(echo $repo_name | tr '[:upper:]' '[:lower:]') # Convert to lowercase - docker push ghcr.io/$repo_name_lower/gh-runner-self:latest diff --git a/.github/workflows/live/delete-json-branch.yml b/.github/workflows/live/delete-json-branch.yml deleted file mode 100644 index e4cdcf24f..000000000 --- a/.github/workflows/live/delete-json-branch.yml +++ /dev/null @@ -1,28 +0,0 @@ - -name: Delete JSON date PR Branch - -on: - pull_request: - types: [closed] - branches: - - main - -jobs: - delete_branch: - runs-on: ubuntu-latest - steps: - - name: Checkout the code - uses: actions/checkout@v3 - - - name: Delete PR Update Branch - if: github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'pr-update-json-') - run: | - PR_BRANCH="${{ github.event.pull_request.head.ref }}" - echo "Deleting branch $PR_BRANCH..." - - # Avoid deleting the default branch (e.g., main) - if [[ "$PR_BRANCH" != "main" ]]; then - git push origin --delete "$PR_BRANCH" - else - echo "Skipping deletion of the main branch" - fi \ No newline at end of file diff --git a/.github/workflows/live/github-release.yml b/.github/workflows/live/github-release.yml deleted file mode 100644 index 482d88a09..000000000 --- a/.github/workflows/live/github-release.yml +++ /dev/null @@ -1,57 +0,0 @@ -name: Create Daily Release - -on: - schedule: - - cron: '1 0 * * *' # Runs daily at 00:01 UTC - workflow_dispatch: - -jobs: - create-daily-release: - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Extract first 5000 characters from CHANGELOG.md - run: head -c 5000 CHANGELOG.md > changelog_cropped.md - - - name: Debugging - Show extracted changelog - run: | - echo "=== CHANGELOG EXCERPT ===" - cat changelog_cropped.md - echo "=========================" - - - name: Parse CHANGELOG.md and create release - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - YESTERDAY=$(date -u --date="yesterday" +%Y-%m-%d) - echo "Checking for changes on: $YESTERDAY" - - # Ensure yesterday's date exists in the changelog - if ! grep -q "## $YESTERDAY" changelog_cropped.md; then - echo "No entry found for $YESTERDAY, skipping release." - exit 0 - fi - - # Extract section for yesterday's date - awk -v date="## $YESTERDAY" ' - $0 ~ date {found=1; next} - found && /^## [0-9]{4}-[0-9]{2}-[0-9]{2}/ {exit} - found - ' changelog_cropped.md > changelog_tmp.md - - echo "=== Extracted Changelog ===" - cat changelog_tmp.md - echo "===========================" - - # Skip if no content was found - if [ ! -s changelog_tmp.md ]; then - echo "No changes found for $YESTERDAY, skipping release." - exit 0 - fi - - # Create GitHub release - gh release create "$YESTERDAY" -t "$YESTERDAY" -F changelog_tmp.md diff --git a/.github/workflows/live/script-test.yml b/.github/workflows/live/script-test.yml deleted file mode 100644 index 272a12724..000000000 --- a/.github/workflows/live/script-test.yml +++ /dev/null @@ -1,177 +0,0 @@ -name: Run Scripts on PVE Node for testing -permissions: - pull-requests: write -on: - pull_request_target: - branches: - - main - paths: - - 'install/**.sh' - - 'ct/**.sh' - -jobs: - run-install-script: - runs-on: pvenode - steps: - - name: Checkout PR branch - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.ref }} - repository: ${{ github.event.pull_request.head.repo.full_name }} - fetch-depth: 0 - - - name: Add Git safe directory - run: | - git config --global --add safe.directory /__w/ProxmoxVED/ProxmoxVE - - - name: Set up GH_TOKEN - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - echo "GH_TOKEN=${GH_TOKEN}" >> $GITHUB_ENV - - - name: Get Changed Files - run: | - CHANGED_FILES=$(gh pr diff ${{ github.event.pull_request.number }} --repo ${{ github.repository }} --name-only) - CHANGED_FILES=$(echo "$CHANGED_FILES" | tr '\n' ' ') - echo "Changed files: $CHANGED_FILES" - echo "SCRIPT=$CHANGED_FILES" >> $GITHUB_ENV - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - - name: Get scripts - id: check-install-script - run: | - ALL_FILES=() - ADDED_FILES=() - for FILE in ${{ env.SCRIPT }}; do - if [[ $FILE =~ ^install/.*-install\.sh$ ]] || [[ $FILE =~ ^ct/.*\.sh$ ]]; then - STRIPPED_NAME=$(basename "$FILE" | sed 's/-install//' | sed 's/\.sh$//') - if [[ ! " ${ADDED_FILES[@]} " =~ " $STRIPPED_NAME " ]]; then - ALL_FILES+=("$FILE") - ADDED_FILES+=("$STRIPPED_NAME") # Mark this base file as added (without the path) - fi - fi - done - ALL_FILES=$(echo "${ALL_FILES[@]}" | xargs) - echo "$ALL_FILES" - echo "ALL_FILES=$ALL_FILES" >> $GITHUB_ENV - - - name: Run scripts - id: run-install - continue-on-error: true - run: | - set +e - #run for each files in /ct - for FILE in ${{ env.ALL_FILES }}; do - STRIPPED_NAME=$(basename "$FILE" | sed 's/-install//' | sed 's/\.sh$//') - echo "Running Test for: $STRIPPED_NAME" - if grep -E -q 'read\s+-r\s+-p\s+".*"\s+\w+' "$FILE"; then - echo "The script contains an interactive prompt. Skipping execution." - continue - fi - if [[ $FILE =~ ^install/.*-install\.sh$ ]]; then - CT_SCRIPT="ct/$STRIPPED_NAME.sh" - if [[ ! -f $CT_SCRIPT ]]; then - echo "No CT script found for $STRIPPED_NAME" - ERROR_MSG="No CT script found for $FILE" - echo "$ERROR_MSG" > result_$STRIPPED_NAME.log - continue - fi - if grep -E -q 'read\s+-r\s+-p\s+".*"\s+\w+' "install/$STRIPPED_NAME-install.sh"; then - echo "The script contains an interactive prompt. Skipping execution." - continue - fi - echo "Found CT script for $STRIPPED_NAME" - chmod +x "$CT_SCRIPT" - RUNNING_FILE=$CT_SCRIPT - elif [[ $FILE =~ ^ct/.*\.sh$ ]]; then - INSTALL_SCRIPT="install/$STRIPPED_NAME-install.sh" - if [[ ! -f $INSTALL_SCRIPT ]]; then - echo "No install script found for $STRIPPED_NAME" - ERROR_MSG="No install script found for $FILE" - echo "$ERROR_MSG" > result_$STRIPPED_NAME.log - continue - fi - echo "Found install script for $STRIPPED_NAME" - chmod +x "$INSTALL_SCRIPT" - RUNNING_FILE=$FILE - if grep -E -q 'read\s+-r\s+-p\s+".*"\s+\w+' "ct/$STRIPPED_NAME.sh"; then - echo "The script contains an interactive prompt. Skipping execution." - continue - fi - fi - git remote add community-scripts https://github.com/community-scripts/ProxmoxVE.git - git fetch community-scripts - rm -f .github/workflows/scripts/app-test/pr-build.func || true - rm -f .github/workflows/scripts/app-test/pr-install.func || true - rm -f .github/workflows/scripts/app-test/pr-alpine-install.func || true - rm -f .github/workflows/scripts/app-test/pr-create-lxc.sh || true - git checkout community-scripts/main -- .github/workflows/scripts/app-test/pr-build.func - git checkout community-scripts/main -- .github/workflows/scripts/app-test/pr-install.func - git checkout community-scripts/main -- .github/workflows/scripts/app-test/pr-alpine-install.func - git checkout community-scripts/main -- .github/workflows/scripts/app-test/pr-create-lxc.sh - chmod +x $RUNNING_FILE - chmod +x .github/workflows/scripts/app-test/pr-create-lxc.sh - chmod +x .github/workflows/scripts/app-test/pr-install.func - chmod +x .github/workflows/scripts/app-test/pr-alpine-install.func - chmod +x .github/workflows/scripts/app-test/pr-build.func - sed -i 's|source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func)|source .github/workflows/scripts/app-test/pr-build.func|g' "$RUNNING_FILE" - echo "Executing $RUNNING_FILE" - ERROR_MSG=$(./$RUNNING_FILE 2>&1 > /dev/null) - echo "Finished running $FILE" - if [ -n "$ERROR_MSG" ]; then - echo "ERROR in $STRIPPED_NAME: $ERROR_MSG" - echo "$ERROR_MSG" > result_$STRIPPED_NAME.log - fi - done - set -e # Restore exit-on-error - - - name: Cleanup PVE Node - run: | - containers=$(pct list | tail -n +2 | awk '{print $0 " " $4}' | awk '{print $1}') - - for container_id in $containers; do - status=$(pct status $container_id | awk '{print $2}') - if [[ $status == "running" ]]; then - pct stop $container_id - pct destroy $container_id - fi - done - - - name: Post error comments - run: | - ERROR="false" - SEARCH_LINE=".github/workflows/scripts/app-test/pr-build.func: line 255:" - - # Get all existing comments on the PR - EXISTING_COMMENTS=$(gh pr view ${{ github.event.pull_request.number }} --repo ${{ github.repository }} --json comments --jq '.comments[].body') - - for FILE in ${{ env.ALL_FILES }}; do - STRIPPED_NAME=$(basename "$FILE" | sed 's/-install//' | sed 's/\.sh$//') - if [[ ! -f result_$STRIPPED_NAME.log ]]; then - continue - fi - ERROR_MSG=$(cat result_$STRIPPED_NAME.log) - - if [ -n "$ERROR_MSG" ]; then - CLEANED_ERROR_MSG=$(echo "$ERROR_MSG" | sed "s|$SEARCH_LINE.*||") - COMMENT_BODY=":warning: The script _**$FILE**_ failed with the following message:
${CLEANED_ERROR_MSG}
" - - # Check if the comment already exists - if echo "$EXISTING_COMMENTS" | grep -qF "$COMMENT_BODY"; then - echo "Skipping duplicate comment for $FILE" - else - echo "Posting error message for $FILE" - gh pr comment ${{ github.event.pull_request.number }} \ - --repo ${{ github.repository }} \ - --body "$COMMENT_BODY" - ERROR="true" - fi - fi - done - - echo "ERROR=$ERROR" >> $GITHUB_ENV - - diff --git a/.github/workflows/live/script_format.yml b/.github/workflows/live/script_format.yml deleted file mode 100644 index c8ea7a4de..000000000 --- a/.github/workflows/live/script_format.yml +++ /dev/null @@ -1,243 +0,0 @@ -name: Script Format Check -permissions: - pull-requests: write -on: - pull_request_target: - branches: - - main - paths: - - 'install/*.sh' - - 'ct/*.sh' - -jobs: - run-install-script: - runs-on: pvenode - steps: - - name: Checkout PR branch (supports forks) - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.ref }} - repository: ${{ github.event.pull_request.head.repo.full_name }} - fetch-depth: 0 - - - name: Add Git safe directory - run: | - git config --global --add safe.directory /__w/ProxmoxVED/ProxmoxVE - - - name: Set up GH_TOKEN - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - echo "GH_TOKEN=${GH_TOKEN}" >> $GITHUB_ENV - - - name: Get Changed Files - run: | - CHANGED_FILES=$(gh pr diff ${{ github.event.pull_request.number }} --repo ${{ github.repository }} --name-only) - CHANGED_FILES=$(echo "$CHANGED_FILES" | tr '\n' ' ') - echo "Changed files: $CHANGED_FILES" - echo "SCRIPT=$CHANGED_FILES" >> $GITHUB_ENV - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Check scripts - id: run-install - continue-on-error: true - run: | - for FILE in ${{ env.SCRIPT }}; do - STRIPPED_NAME=$(basename "$FILE" | sed 's/-install//' | sed 's/\.sh$//') - echo "Running Test for: $STRIPPED_NAME" - FILE_STRIPPED="${FILE##*/}" - LOG_FILE="result_$FILE_STRIPPED.log" - - if [[ $FILE =~ ^ct/.*\.sh$ ]]; then - - FIRST_LINE=$(sed -n '1p' "$FILE") - [[ "$FIRST_LINE" != "#!/usr/bin/env bash" ]] && echo "Line 1 was $FIRST_LINE | Should be: #!/usr/bin/env bash" >> "$LOG_FILE" - SECOND_LINE=$(sed -n '2p' "$FILE") - [[ "$SECOND_LINE" != "source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func)" ]] && - echo "Line 2 was $SECOND_LINE | Should be: source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func)" >> "$LOG_FILE" - THIRD_LINE=$(sed -n '3p' "$FILE") - if ! [[ "$THIRD_LINE" =~ ^#\ Copyright\ \(c\)\ [0-9]{4}-[0-9]{4}\ community-scripts\ ORG$ || "$THIRD_LINE" =~ ^Copyright\ \(c\)\ [0-9]{4}-[0-9]{4}\ tteck$ ]]; then - echo "Line 3 was $THIRD_LINE | Should be: # Copyright (c) 2021-2025 community-scripts ORG" >> "$LOG_FILE" - fi - - EXPECTED_AUTHOR="# Author:" - EXPECTED_LICENSE="# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE" - EXPECTED_SOURCE="# Source:" - EXPECTED_EMPTY="" - - for i in {4..7}; do - LINE=$(sed -n "${i}p" "$FILE") - - case $i in - 4) - [[ $LINE == $EXPECTED_AUTHOR* ]] || printf "Line %d was: '%s' | Should start with: '%s'\n" "$i" "$LINE" "$EXPECTED_AUTHOR" >> $LOG_FILE - ;; - 5) - [[ "$LINE" == "$EXPECTED_LICENSE" ]] || printf "Line %d was: '%s' | Should be: '%s'\n" "$i" "$LINE" "$EXPECTED_LICENSE" >> $LOG_FILE - ;; - 6) - [[ $LINE == $EXPECTED_SOURCE* ]] || printf "Line %d was: '%s' | Should start with: '%s'\n" "$i" "$LINE" "$EXPECTED_SOURCE" >> $LOG_FILE - ;; - 7) - [[ -z $LINE ]] || printf "Line %d was: '%s' | Should be empty\n" "$i" "$LINE" >> $LOG_FILE - ;; - esac - done - - - EXPECTED_PREFIXES=( - "APP=" - "var_tags=" - "var_cpu=" # Must be a number - "var_ram=" # Must be a number - "var_disk=" # Must be a number - "var_os=" # Must be debian, alpine, or ubuntu - "var_version=" - "var_unprivileged=" # Must be 0 or 1 - ) - - - for i in {8..15}; do - LINE=$(sed -n "${i}p" "$FILE") - INDEX=$((i - 8)) - - case $INDEX in - 2|3|4) # var_cpu, var_ram, var_disk (must be numbers) - if [[ "$LINE" =~ ^${EXPECTED_PREFIXES[$INDEX]}([0-9]+)$ ]]; then - continue # Valid - else - echo "Line $i was '$LINE' | Should be: '${EXPECTED_PREFIXES[$INDEX]}'" >> "$LOG_FILE" - fi - ;; - 5) # var_os (must be debian, alpine, or ubuntu) - if [[ "$LINE" =~ ^var_os=(debian|alpine|ubuntu)$ ]]; then - continue # Valid - else - echo "Line $i was '$LINE' | Should be: 'var_os=[debian|alpine|ubuntu]'" >> "$LOG_FILE" - fi - ;; - 7) # var_unprivileged (must be 0 or 1) - if [[ "$LINE" =~ ^var_unprivileged=[01]$ ]]; then - continue # Valid - else - echo "Line $i was '$LINE' | Should be: 'var_unprivileged=[0|1]'" >> "$LOG_FILE" - fi - ;; - *) # Other lines (must start with expected prefix) - if [[ "$LINE" == ${EXPECTED_PREFIXES[$INDEX]}* ]]; then - continue # Valid - else - echo "Line $i was '$LINE' | Should start with '${EXPECTED_PREFIXES[$INDEX]}'" >> "$LOG_FILE" - fi - ;; - esac - done - - for i in {16..20}; do - LINE=$(sed -n "${i}p" "$FILE") - EXPECTED=( - "header_info \"$APP\"" - "variables" - "color" - "catch_errors" - "function update_script() {" - ) - [[ "$LINE" != "${EXPECTED[$((i-16))]}" ]] && echo "Line $i was $LINE | Should be: ${EXPECTED[$((i-16))]}" >> "$LOG_FILE" - done - cat "$LOG_FILE" - elif [[ $FILE =~ ^install/.*-install\.sh$ ]]; then - - FIRST_LINE=$(sed -n '1p' "$FILE") - [[ "$FIRST_LINE" != "#!/usr/bin/env bash" ]] && echo "Line 1 was $FIRST_LINE | Should be: #!/usr/bin/env bash" >> "$LOG_FILE" - - SECOND_LINE=$(sed -n '2p' "$FILE") - [[ -n "$SECOND_LINE" ]] && echo "Line 2 should be empty" >> "$LOG_FILE" - - THIRD_LINE=$(sed -n '3p' "$FILE") - if ! [[ "$THIRD_LINE" =~ ^#\ Copyright\ \(c\)\ [0-9]{4}-[0-9]{4}\ community-scripts\ ORG$ || "$THIRD_LINE" =~ ^Copyright\ \(c\)\ [0-9]{4}-[0-9]{4}\ tteck$ ]]; then - echo "Line 3 was $THIRD_LINE | Should be: # Copyright (c) 2021-2025 community-scripts ORG" >> "$LOG_FILE" - fi - - EXPECTED_AUTHOR="# Author:" - EXPECTED_LICENSE="# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE" - EXPECTED_SOURCE="# Source:" - EXPECTED_EMPTY="" - - for i in {4..7}; do - LINE=$(sed -n "${i}p" "$FILE") - - case $i in - 4) - [[ $LINE == $EXPECTED_AUTHOR* ]] || printf "Line %d was: '%s' | Should start with: '%s'\n" "$i" "$LINE" "$EXPECTED_AUTHOR" >> $LOG_FILE - ;; - 5) - [[ "$LINE" == "$EXPECTED_LICENSE" ]] || printf "Line %d was: '%s' | Should be: '%s'\n" "$i" "$LINE" "$EXPECTED_LICENSE" >> $LOG_FILE - ;; - 6) - [[ $LINE == $EXPECTED_SOURCE* ]] || printf "Line %d was: '%s' | Should start with: '%s'\n" "$i" "$LINE" "$EXPECTED_SOURCE" >> $LOG_FILE - ;; - 7) - [[ -z $LINE ]] || printf "Line %d was: '%s' | Should be empty\n" "$i" "$LINE" >> $LOG_FILE - ;; - esac - done - - [[ "$(sed -n '8p' "$FILE")" != 'source /dev/stdin <<< "$FUNCTIONS_FILE_PATH"' ]] && echo 'Line 8 should be: source /dev/stdin <<< "$FUNCTIONS_FILE_PATH"' >> "$LOG_FILE" - - for i in {9..14}; do - LINE=$(sed -n "${i}p" "$FILE") - EXPECTED=( - "color" - "verb_ip6" - "catch_errors" - "setting_up_container" - "network_check" - "update_os" - ) - [[ "$LINE" != "${EXPECTED[$((i-9))]}" ]] && echo "Line $i was $LINE | Should be: ${EXPECTED[$((i-9))]}" >> "$LOG_FILE" - done - - [[ -n "$(sed -n '15p' "$FILE")" ]] && echo "Line 15 should be empty" >> "$LOG_FILE" - [[ "$(sed -n '16p' "$FILE")" != 'msg_info "Installing Dependencies"' ]] && echo 'Line 16 should be: msg_info "Installing Dependencies"' >> "$LOG_FILE" - - LAST_3_LINES=$(tail -n 3 "$FILE") - [[ "$LAST_3_LINES" != *"$STD apt-get -y autoremove"* ]] && echo 'Third to last line should be: $STD apt-get -y autoremove' >> "$LOG_FILE" - [[ "$LAST_3_LINES" != *"$STD apt-get -y autoclean"* ]] && echo 'Second to last line should be: $STD apt-get -y clean' >> "$LOG_FILE" - [[ "$LAST_3_LINES" != *'msg_ok "Cleaned"'* ]] && echo 'Last line should be: msg_ok "Cleaned"' >> "$LOG_FILE" - cat "$LOG_FILE" - fi - - done - - - - name: Post error comments - run: | - ERROR="false" - for FILE in ${{ env.SCRIPT }}; do - FILE_STRIPPED="${FILE##*/}" - LOG_FILE="result_$FILE_STRIPPED.log" - echo $LOG_FILE - if [[ ! -f $LOG_FILE ]]; then - continue - fi - ERROR_MSG=$(cat $LOG_FILE) - - if [ -n "$ERROR_MSG" ]; then - echo "Posting error message for $FILE" - echo ${ERROR_MSG} - gh pr comment ${{ github.event.pull_request.number }} \ - --repo ${{ github.repository }} \ - --body ":warning: The script _**$FILE**_ has the following formatting errors:
${ERROR_MSG}
" - - - ERROR="true" - fi - done - echo "ERROR=$ERROR" >> $GITHUB_ENV - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Fail if error - if: ${{ env.ERROR == 'true' }} - run: exit 1 diff --git a/.github/workflows/live/update-json-date.yml b/.github/workflows/live/update-json-date.yml deleted file mode 100644 index 1bf965a44..000000000 --- a/.github/workflows/live/update-json-date.yml +++ /dev/null @@ -1,133 +0,0 @@ -name: Update JSON Date - -on: - push: - branches: - - main - paths: - - 'json/**.json' - workflow_dispatch: - -jobs: - update-app-files: - runs-on: ubuntu-latest - - permissions: - contents: write - pull-requests: write - - steps: - - name: Generate a token - id: generate-token - uses: actions/create-github-app-token@v2 - with: - app-id: ${{ vars.APP_ID }} - private-key: ${{ secrets.APP_PRIVATE_KEY }} - owner: community-scripts - repositories: ProxmoxVED - - - name: Generate dynamic branch name - id: timestamp - run: echo "BRANCH_NAME=pr-update-json-$(date +'%Y%m%d%H%M%S')" >> $GITHUB_ENV - - - name: Set up GH_TOKEN - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - echo "GH_TOKEN=${GH_TOKEN}" >> $GITHUB_ENV - - - name: Checkout Repository - uses: actions/checkout@v4 - with: - fetch-depth: 2 # Ensure we have the last two commits - - - name: Get Previous Commit - id: prev_commit - run: | - PREV_COMMIT=$(git rev-parse HEAD^) - echo "Previous commit: $PREV_COMMIT" - echo "prev_commit=$PREV_COMMIT" >> $GITHUB_ENV - - - name: Get Newly Added JSON Files - id: new_json_files - run: | - git diff --name-only --diff-filter=A ${{ env.prev_commit }} HEAD | grep '^json/.*\.json$' > new_files.txt || true - echo "New files detected:" - cat new_files.txt || echo "No new files." - - - name: Disable file mode changes - run: git config core.fileMode false - - - name: Set up Git - run: | - git config --global user.name "GitHub Actions" - git config --global user.email "github-actions[bot]@users.noreply.github.com" - - - name: Change JSON Date - id: change-json-date - run: | - current_date=$(date +"%Y-%m-%d") - while IFS= read -r file; do - # Skip empty lines - [[ -z "$file" ]] && continue - - if [[ -f "$file" ]]; then - echo "Processing $file..." - current_json_date=$(jq -r '.date_created // empty' "$file") - if [[ -z "$current_json_date" || "$current_json_date" != "$current_date" ]]; then - echo "Updating $file with date $current_date" - jq --arg date "$current_date" '.date_created = $date' "$file" > temp.json && mv temp.json "$file" - else - echo "Date in $file is already up to date." - fi - else - echo "Warning: File $file not found!" - fi - done < new_files.txt - rm new_files.txt - - - name: Check if there are any changes - run: | - echo "Checking for changes..." - git add -A # Untracked Dateien aufnehmen - git status - if git diff --cached --quiet; then - echo "No changes detected." - echo "changed=false" >> "$GITHUB_ENV" - else - echo "Changes detected:" - git diff --stat --cached - echo "changed=true" >> "$GITHUB_ENV" - fi - - # Step 7: Commit and create PR if changes exist - - name: Commit and create PR if changes exist - if: env.changed == 'true' - run: | - - - git commit -m "Update date in json" - git checkout -b ${{ env.BRANCH_NAME }} - git push origin ${{ env.BRANCH_NAME }} - - gh pr create --title "[core] update date in json" \ - --body "This PR is auto-generated by a GitHub Action to update the date in json." \ - --head ${{ env.BRANCH_NAME }} \ - --base main \ - --label "automated pr" - env: - GH_TOKEN: ${{ steps.generate-token.outputs.token }} - - - name: Approve pull request - if: env.changed == 'true' - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - PR_NUMBER=$(gh pr list --head "${{ env.BRANCH_NAME }}" --json number --jq '.[].number') - if [ -n "$PR_NUMBER" ]; then - gh pr review $PR_NUMBER --approve - fi - - - name: No changes detected - if: env.changed == 'false' - run: echo "No changes to commit. Workflow completed successfully." diff --git a/.github/workflows/live/validate-filenames.yml b/.github/workflows/live/validate-filenames.yml deleted file mode 100644 index 16f2f7106..000000000 --- a/.github/workflows/live/validate-filenames.yml +++ /dev/null @@ -1,161 +0,0 @@ -name: Validate filenames - -on: - pull_request_target: - paths: - - "ct/*.sh" - - "install/*.sh" - - "json/*.json" - -jobs: - check-files: - name: Check changed files - runs-on: ubuntu-latest - permissions: - pull-requests: write - - steps: - - name: Get pull request information - if: github.event_name == 'pull_request_target' - uses: actions/github-script@v7 - id: pr - with: - script: | - const { data: pullRequest } = await github.rest.pulls.get({ - ...context.repo, - pull_number: context.payload.pull_request.number, - }); - return pullRequest; - - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 # Ensure the full history is fetched for accurate diffing - ref: ${{ github.event_name == 'pull_request_target' && fromJSON(steps.pr.outputs.result).merge_commit_sha || '' }} - - - name: Get changed files - id: changed-files - run: | - if ${{ github.event_name == 'pull_request_target' }}; then - echo "files=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ steps.pr.outputs.result && fromJSON(steps.pr.outputs.result).merge_commit_sha }} | xargs)" >> $GITHUB_OUTPUT - else - echo "files=$(git diff --name-only ${{ github.event.before }} ${{ github.event.after }} | xargs)" >> $GITHUB_OUTPUT - fi - - - name: "Validate filenames in ct and install directory" - if: always() && steps.changed-files.outputs.files != '' - id: check-scripts - run: | - CHANGED_FILES=$(printf "%s\n" ${{ steps.changed-files.outputs.files }} | { grep -E '^(ct|install)/.*\.sh$' || true; }) - - NON_COMPLIANT_FILES="" - for FILE in $CHANGED_FILES; do - # Datei "misc/create_lxc.sh" explizit überspringen - if [[ "$FILE" == "misc/create_lxc.sh" ]]; then - continue - fi - BASENAME=$(echo "$(basename "${FILE%.*}")") - if [[ ! "$BASENAME" =~ ^[a-z0-9-]+$ ]]; then - NON_COMPLIANT_FILES="$NON_COMPLIANT_FILES $FILE" - fi - done - - if [ -n "$NON_COMPLIANT_FILES" ]; then - echo "files=$NON_COMPLIANT_FILES" >> $GITHUB_OUTPUT - echo "Non-compliant filenames found, change to lowercase:" - for FILE in $NON_COMPLIANT_FILES; do - echo "$FILE" - done - exit 1 - fi - - - name: "Validate filenames in json directory." - if: always() && steps.changed-files.outputs.files != '' - id: check-json - run: | - CHANGED_FILES=$(printf "%s\n" ${{ steps.changed-files.outputs.files }} | { grep -E '^json/.*\.json$' || true; }) - - NON_COMPLIANT_FILES="" - for FILE in $CHANGED_FILES; do - BASENAME=$(echo "$(basename "${FILE%.*}")") - if [[ ! "$BASENAME" =~ ^[a-z0-9-]+$ ]]; then - NON_COMPLIANT_FILES="$NON_COMPLIANT_FILES $FILE" - fi - done - - if [ -n "$NON_COMPLIANT_FILES" ]; then - echo "files=$NON_COMPLIANT_FILES" >> $GITHUB_OUTPUT - echo "Non-compliant filenames found, change to lowercase:" - for FILE in $NON_COMPLIANT_FILES; do - echo "$FILE" - done - exit 1 - fi - - - name: Post results and comment - if: always() && steps.check-scripts.outputs.files != '' && steps.check-json.outputs.files != '' && github.event_name == 'pull_request_target' - uses: actions/github-script@v7 - with: - script: | - const result = "${{ job.status }}" === "success" ? "success" : "failure"; - const nonCompliantFiles = { - script: "${{ steps.check-scripts.outputs.files }}", - JSON: "${{ steps.check-json.outputs.files }}", - }; - - const issueNumber = context.payload.pull_request - ? context.payload.pull_request.number - : null; - const commentIdentifier = "validate-filenames"; - let newCommentBody = `\n### Filename validation\n\n`; - - if (result === "failure") { - newCommentBody += ":x: We found issues in the following changed files:\n\n"; - for (const [check, files] of Object.entries(nonCompliantFiles)) { - if (files) { - newCommentBody += `**${check.charAt(0).toUpperCase() + check.slice(1)} filename invalid:**\n${files - .trim() - .split(" ") - .map((file) => `- ${file}`) - .join("\n")}\n\n`; - } - } - newCommentBody += - "Please change the filenames to lowercase and use only alphanumeric characters and dashes.\n"; - } else { - newCommentBody += `:rocket: All files passed filename validation!\n`; - } - - newCommentBody += `\n\n`; - - if (issueNumber) { - const { data: comments } = await github.rest.issues.listComments({ - ...context.repo, - issue_number: issueNumber, - }); - - const existingComment = comments.find( - (comment) => comment.user.login === "github-actions[bot]", - ); - - if (existingComment) { - if (existingComment.body.includes(commentIdentifier)) { - const re = new RegExp(String.raw`[\s\S]*?`, ""); - newCommentBody = existingComment.body.replace(re, newCommentBody); - } else { - newCommentBody = existingComment.body + '\n\n---\n\n' + newCommentBody; - } - - await github.rest.issues.updateComment({ - ...context.repo, - comment_id: existingComment.id, - body: newCommentBody, - }); - } else { - await github.rest.issues.createComment({ - ...context.repo, - issue_number: issueNumber, - body: newCommentBody, - }); - } - } From d51645e2ebce9da23ebcec92fb8875307723fe34 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 25 Aug 2025 14:25:59 +0200 Subject: [PATCH 0716/1733] cleanup --- ct/alpine-redlib.sh | 56 ---------- ct/healthchecks.sh | 70 ------------- frontend/public/json/healthchecks.json | 40 -------- frontend/public/json/redlib.json | 35 ------- install/alpine-redlib-install.sh | 98 ------------------ install/healthchecks-install.sh | 136 ------------------------- 6 files changed, 435 deletions(-) delete mode 100644 ct/alpine-redlib.sh delete mode 100644 ct/healthchecks.sh delete mode 100644 frontend/public/json/healthchecks.json delete mode 100644 frontend/public/json/redlib.json delete mode 100644 install/alpine-redlib-install.sh delete mode 100644 install/healthchecks-install.sh diff --git a/ct/alpine-redlib.sh b/ct/alpine-redlib.sh deleted file mode 100644 index 88f9fd3df..000000000 --- a/ct/alpine-redlib.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: andrej-kocijan (Andrej Kocijan) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/redlib-org/redlib - -APP="Alpine-Redlib" -var_tags="${var_tags:-alpine;frontend}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-512}" -var_disk="${var_disk:-1}" -var_os="${var_os:-alpine}" -var_version="${var_version:-3.22}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_resources - - if [[ ! -d /opt/redlib ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - msg_info "Updating Alpine Packages" - $STD apk -U upgrade - msg_ok "Updated Alpine Packages" - - msg_info "Stopping ${APP} Service" - $STD rc-service redlib stop - msg_ok "Stopped ${APP} Service" - - fetch_and_deploy_gh_release "redlib" "redlib-org/redlib" "prebuild" "latest" "/opt/redlib" "redlib-x86_64-unknown-linux-musl.tar.gz" - - msg_info "Starting ${APP} Service" - $STD rc-service redlib start - msg_ok "Started ${APP} Service" - - msg_ok "Update Successful" - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5252${CL}" diff --git a/ct/healthchecks.sh b/ct/healthchecks.sh deleted file mode 100644 index f8e5e45fc..000000000 --- a/ct/healthchecks.sh +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: MickLesk (CanbiZ) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: - -APP="healthchecks" -var_tags="${var_tags:-monitoring}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-5}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - - if [[ ! -d /opt/healthchecks ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - RELEASE=$(curl -fsSL https://api.github.com/repos/healthchecks/healthchecks/releases/latest | jq '.tag_name' | sed 's/^"v//;s/"$//') - if [[ "${RELEASE}" != "$(cat ~/.healthchecks 2>/dev/null)" ]] || [[ ! -f ~/.healthchecks ]]; then - msg_info "Stopping $APP" - systemctl stop healthchecks - msg_ok "Stopped $APP" - - setup_uv - fetch_and_deploy_gh_release "healthchecks" "healthchecks/healthchecks" - - msg_info "Updating $APP to v${RELEASE}" - cd /opt/healthchecks - mkdir -p /opt/healthchecks/static-collected/ - $STD uv pip install wheel gunicorn -r requirements.txt --system - $STD uv run -- python manage.py makemigrations - $STD uv run -- python manage.py migrate --noinput - $STD uv run -- python manage.py collectstatic --noinput - $STD uv run -- python manage.py compress - msg_ok "Updated $APP to v${RELEASE}" - - msg_info "Starting $APP" - systemctl start healthchecks - systemctl restart caddy - msg_ok "Started $APP" - - msg_ok "Update Successful" - else - msg_ok "No update required. ${APP} is already at v${RELEASE}" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}https://${IP}${CL}" diff --git a/frontend/public/json/healthchecks.json b/frontend/public/json/healthchecks.json deleted file mode 100644 index a0187d17d..000000000 --- a/frontend/public/json/healthchecks.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "Healthchecks", - "slug": "healthchecks", - "categories": [ - 9 - ], - "date_created": "2025-08-25", - "type": "ct", - "updateable": true, - "privileged": false, - "config_path": "/opt/healthchecks/hc/local_settings.py", - "interface_port": 3000, - "documentation": "https://healthchecks.io/", - "website": "https://healthchecks.io/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/healthchecks.webp", - "description": "Healthchecks is a cron job monitoring service. It listens for HTTP requests and email messages (\"pings\") from your cron jobs and scheduled tasks (\"checks\"). When a ping does not arrive on time, Healthchecks sends out alerts. Healthchecks comes with a web dashboard, API, 25+ integrations for delivering notifications, monthly email reports, WebAuthn 2FA support, team management features: projects, team members, read-only access.", - "install_methods": [ - { - "type": "default", - "script": "ct/healthchecks.sh", - "resources": { - "cpu": 2, - "ram": 2048, - "hdd": 5, - "os": "Debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "if you change your LXC-IP, you need to update /etc/caddy/Caddyfile & /opt/healthchecks/hc/local_settings.py", - "type": "info" - } - ] -} diff --git a/frontend/public/json/redlib.json b/frontend/public/json/redlib.json deleted file mode 100644 index 215b190f3..000000000 --- a/frontend/public/json/redlib.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "Redlib", - "slug": "redlib", - "categories": [ - 10 - ], - "date_created": "2025-08-22", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 5252, - "documentation": "https://github.com/redlib-org/redlib/blob/main/README.md", - "website": "https://github.com/redlib-org/redlib", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/redlib.webp", - "config_path": "/opt/redlib/redlib.conf", - "description": "An alternative private front-end to Reddit. Redlib hopes to provide an easier way to browse Reddit, without the ads, trackers, and bloat.", - "install_methods": [ - { - "type": "default", - "script": "ct/alpine-redlib.sh", - "resources": { - "cpu": 1, - "ram": 512, - "hdd": 1, - "os": "alpine", - "version": "3.22" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] -} diff --git a/install/alpine-redlib-install.sh b/install/alpine-redlib-install.sh deleted file mode 100644 index 0f7177210..000000000 --- a/install/alpine-redlib-install.sh +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: andrej-kocijan (Andrej Kocijan) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/redlib-org/redlib - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -fetch_and_deploy_gh_release "redlib" "redlib-org/redlib" "prebuild" "latest" "/opt/redlib" "redlib-x86_64-unknown-linux-musl.tar.gz" - -msg_info "Configuring Redlib" -cat </opt/redlib/redlib.conf -############################################ -# Redlib Instance Configuration File -# Uncomment and edit values as needed -############################################ - -## Instance settings -ADDRESS=0.0.0.0 -PORT=5252 # Integer (0-65535) - Internal port -#REDLIB_SFW_ONLY=off # ["on", "off"] - Filter all NSFW content -#REDLIB_BANNER= # String - Displayed on instance info page -#REDLIB_ROBOTS_DISABLE_INDEXING=off # ["on", "off"] - Disable search engine indexing -#REDLIB_PUSHSHIFT_FRONTEND=undelete.pullpush.io # Pushshift frontend for removed links -#REDLIB_ENABLE_RSS=off # ["on", "off"] - Enable RSS feed generation -#REDLIB_FULL_URL= # String - Needed for proper RSS URLs - -## Default user settings -#REDLIB_DEFAULT_THEME=system # Theme (system, light, dark, black, dracula, nord, laserwave, violet, gold, rosebox, gruvboxdark, gruvboxlight, tokyoNight, icebergDark, doomone, libredditBlack, libredditDark, libredditLight) -#REDLIB_DEFAULT_FRONT_PAGE=default # ["default", "popular", "all"] -#REDLIB_DEFAULT_LAYOUT=card # ["card", "clean", "compact"] -#REDLIB_DEFAULT_WIDE=off # ["on", "off"] -#REDLIB_DEFAULT_POST_SORT=hot # ["hot", "new", "top", "rising", "controversial"] -#REDLIB_DEFAULT_COMMENT_SORT=confidence # ["confidence", "top", "new", "controversial", "old"] -#REDLIB_DEFAULT_BLUR_SPOILER=off # ["on", "off"] -#REDLIB_DEFAULT_SHOW_NSFW=off # ["on", "off"] -#REDLIB_DEFAULT_BLUR_NSFW=off # ["on", "off"] -#REDLIB_DEFAULT_USE_HLS=off # ["on", "off"] -#REDLIB_DEFAULT_HIDE_HLS_NOTIFICATION=off # ["on", "off"] -#REDLIB_DEFAULT_AUTOPLAY_VIDEOS=off # ["on", "off"] -#REDLIB_DEFAULT_SUBSCRIPTIONS= # Example: sub1+sub2+sub3 -#REDLIB_DEFAULT_HIDE_AWARDS=off # ["on", "off"] -#REDLIB_DEFAULT_DISABLE_VISIT_REDDIT_CONFIRMATION=off # ["on", "off"] -#REDLIB_DEFAULT_HIDE_SCORE=off # ["on", "off"] -#REDLIB_DEFAULT_HIDE_SIDEBAR_AND_SUMMARY=off # ["on", "off"] -#REDLIB_DEFAULT_FIXED_NAVBAR=on # ["on", "off"] -#REDLIB_DEFAULT_REMOVE_DEFAULT_FEEDS=off # ["on", "off"] -EOF -msg_ok "Configured Redlib" - -msg_info "Creating Redlib Service" -cat </etc/init.d/redlib -#!/sbin/openrc-run - -name="Redlib" -description="Redlib Service" -command="/opt/redlib/redlib" -pidfile="/run/redlib.pid" -supervisor="supervise-daemon" -command_background="yes" - -depend() { - need net -} - -start_pre() { - - set -a - . /opt/redlib/redlib.conf - set +a - - : ${ADDRESS:=0.0.0.0} - : ${PORT:=5252} - - command_args="-a ${ADDRESS} -p ${PORT}" -} -EOF -$STD chmod +x /etc/init.d/redlib -$STD rc-update add redlib default -msg_ok "Created Redlib Service" - -msg_info "Starting Redlib Service" -$STD rc-service redlib start -msg_ok "Started Redlib Service" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apk cache clean -msg_ok "Cleaned" diff --git a/install/healthchecks-install.sh b/install/healthchecks-install.sh deleted file mode 100644 index 2507149da..000000000 --- a/install/healthchecks-install.sh +++ /dev/null @@ -1,136 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: MickLesk (Canbiz) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/getmaxun/maxun - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt-get install -y \ - gcc \ - libpq-dev \ - libcurl4-openssl-dev \ - libssl-dev \ - caddy -msg_ok "Installed Dependencies" - -setup_uv -PG_VERSION=16 setup_postgresql - -msg_info "Setup Database" -DB_NAME=healthchecks_db -DB_USER=hc_user -DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) -SECRET_KEY="$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | cut -c1-32)" -ADMIN_EMAIL="admin@helper-scripts.local" -ADMIN_PASSWORD="$DB_PASS" - -$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" -$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC'" -{ - echo "healthchecks-Credentials" - echo "healthchecks Database User: $DB_USER" - echo "healthchecks Database Password: $DB_PASS" - echo "healthchecks Database Name: $DB_NAME" - echo "healthchecks Admin Email: $ADMIN_EMAIL" - echo "healthchecks Admin Password: $ADMIN_PASSWORD" -} >>~/healthchecks.creds -msg_ok "Set up Database" - -fetch_and_deploy_gh_release "healthchecks" "healthchecks/healthchecks" - -msg_info "Setup healthchecks" -cd /opt/healthchecks -mkdir -p /opt/healthchecks/static-collected/ -$STD uv pip install wheel gunicorn -r requirements.txt --system - -LOCAL_IP=$(hostname -I | awk '{print $1}') -cat </opt/healthchecks/hc/local_settings.py -DEBUG = False - -ALLOWED_HOSTS = ["${LOCAL_IP}", "127.0.0.1", "localhost"] -CSRF_TRUSTED_ORIGINS = ["http://${LOCAL_IP}", "https://${LOCAL_IP}"] - -SECRET_KEY = "${SECRET_KEY}" - -SITE_ROOT = "http://${LOCAL_IP}:8000" -SITE_NAME = "MyChecks" -DEFAULT_FROM_EMAIL = "healthchecks@${LOCAL_IP}" - -STATIC_ROOT = "/opt/healthchecks/static-collected" -COMPRESS_OFFLINE = True - -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.postgresql', - 'NAME': '${DB_NAME}', - 'USER': '${DB_USER}', - 'PASSWORD': '${DB_PASS}', - 'HOST': '127.0.0.1', - 'PORT': '5432', - 'TEST': {'CHARSET': 'UTF8'} - } -} -EOF - -$STD uv run -- python manage.py makemigrations -$STD uv run -- python manage.py migrate --noinput -$STD uv run -- python manage.py collectstatic --noinput -$STD uv run -- python manage.py compress - -cat </etc/caddy/Caddyfile -{ - email admin@example.com -} - -${LOCAL_IP} { - reverse_proxy 127.0.0.1:8000 -} -EOF -msg_ok "Configured Caddy" - -msg_info "Creating Service" -cat </etc/systemd/system/healthchecks.service -[Unit] -Description=Healthchecks Service -After=network.target postgresql.service - -[Service] -WorkingDirectory=/opt/healthchecks/ -ExecStart=/usr/local/bin/uv run -- gunicorn hc.wsgi:application --bind 127.0.0.1:8000 -Restart=always - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now healthchecks caddy -systemctl reload caddy -msg_ok "Created Service" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" From 3c3aabd21d265af99a8d13a455a5f3d0490a384e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 25 Aug 2025 14:28:16 +0200 Subject: [PATCH 0717/1733] feat: expand logger with subshell-safe error handling --- misc/alpine-install.func | 1 + misc/api.func | 10 +++++ misc/build.func | 1 + misc/core.func | 19 ++++++++- misc/create_lxc.sh | 1 + misc/install.func | 1 + misc/logger.func | 88 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 misc/logger.func diff --git a/misc/alpine-install.func b/misc/alpine-install.func index 450e92091..660a3227d 100644 --- a/misc/alpine-install.func +++ b/misc/alpine-install.func @@ -32,6 +32,7 @@ error_handler() { local exit_code="$?" local line_number="$1" local command="$2" + log_error "line $line_number: exit code $exit_code while executing command $command" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" echo -e "\n$error_message\n" } diff --git a/misc/api.func b/misc/api.func index 08bdc914b..58ea56e02 100644 --- a/misc/api.func +++ b/misc/api.func @@ -2,6 +2,16 @@ # Author: michelroegl-brunner # License: MIT | https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/LICENSE +if [[ -f "$(dirname "${BASH_SOURCE[0]}")/logger.func" ]]; then + source "$(dirname "${BASH_SOURCE[0]}")/logger.func" +else + if command -v curl >/dev/null 2>&1; then + source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/logger.func) + elif command -v wget >/dev/null 2>&1; then + source <(wget -qO- https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/logger.func) + fi +fi + get_error_description() { local exit_code="$1" case "$exit_code" in diff --git a/misc/build.func b/misc/build.func index 8a58664fa..c2e78b58a 100644 --- a/misc/build.func +++ b/misc/build.func @@ -43,6 +43,7 @@ error_handler() { local line_number="$1" local command="$2" printf "\e[?25h" + log_error "line $line_number: exit code $exit_code while executing command $command" local error_message="[ERROR] in line $line_number: exit code $exit_code: while executing command $command" post_update_to_api "failed" "$command" echo -e "\n$error_message\n" diff --git a/misc/core.func b/misc/core.func index 5df50efd9..6ed1faf3a 100644 --- a/misc/core.func +++ b/misc/core.func @@ -9,6 +9,16 @@ [[ -n "${_CORE_FUNC_LOADED:-}" ]] && return _CORE_FUNC_LOADED=1 +if [[ -f "$(dirname "${BASH_SOURCE[0]}")/logger.func" ]]; then + source "$(dirname "${BASH_SOURCE[0]}")/logger.func" +else + if command -v curl >/dev/null 2>&1; then + source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/logger.func) + elif command -v wget >/dev/null 2>&1; then + source <(wget -qO- https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/logger.func) + fi +fi + load_functions() { [[ -n "${__FUNCTIONS_LOADED:-}" ]] && return __FUNCTIONS_LOADED=1 @@ -96,9 +106,9 @@ _tool_error_hint() { # exit 143 # } +# logger.func now sets strict modes and traps globally catch_errors() { - set -Eeuo pipefail - trap 'error_handler $LINENO "$BASH_COMMAND"' ERR + : } # ------------------------------------------------------------------------------ @@ -339,6 +349,7 @@ stop_spinner() { msg_info() { local msg="$1" [[ -z "$msg" ]] && return + log_info "$msg" if ! declare -p MSG_INFO_SHOWN &>/dev/null || ! declare -A MSG_INFO_SHOWN &>/dev/null; then declare -gA MSG_INFO_SHOWN=() @@ -365,6 +376,7 @@ msg_info() { msg_ok() { local msg="$1" [[ -z "$msg" ]] && return + log_info "$msg" stop_spinner clear_line printf "%s %b\n" "$CM" "${GN}${msg}${CL}" >&2 @@ -374,12 +386,14 @@ msg_ok() { msg_error() { stop_spinner local msg="$1" + log_error "$msg" echo -e "${BFR:-} ${CROSS:-✖️} ${RD}${msg}${CL}" } msg_warn() { stop_spinner local msg="$1" + log_warn "$msg" echo -e "${BFR:-} ${INFO:-ℹ️} ${YWB}${msg}${CL}" } @@ -395,6 +409,7 @@ msg_custom() { function msg_debug() { if [[ "${var_full_verbose:-0}" == "1" ]]; then [[ "${var_verbose:-0}" != "1" ]] && var_verbose=1 + log_debug "$*" echo -e "${YWB}[$(date '+%F %T')] [DEBUG]${CL} $*" fi } diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index 7a5d18cf9..3ada6c96f 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -36,6 +36,7 @@ function error_handler() { local line_number="$1" local command="$2" printf "\e[?25h" + log_error "line $line_number: exit code $exit_code while executing command $command" echo -e "\n${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}\n" exit "$exit_code" } diff --git a/misc/install.func b/misc/install.func index e3751c295..f7ee67458 100644 --- a/misc/install.func +++ b/misc/install.func @@ -36,6 +36,7 @@ error_handler() { local exit_code="$?" local line_number="$1" local command="$2" + log_error "line $line_number: exit code $exit_code while executing command $command" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" echo -e "\n$error_message" diff --git a/misc/logger.func b/misc/logger.func new file mode 100644 index 000000000..0b9290117 --- /dev/null +++ b/misc/logger.func @@ -0,0 +1,88 @@ +#!/usr/bin/env bash +# Centralized logging utilities for Proxmox helper scripts +# Provides log_* functions, stdout/stderr capture, and robust error handling. + +# Prevent multiple sourcing +[[ -n "${_LOGGER_FUNC_LOADED:-}" ]] && return +_LOGGER_FUNC_LOADED=1 + +# ------------------------------------------------------------------------------ +# Shell options – fail fast and propagate ERR through subshells +# ------------------------------------------------------------------------------ +set -o errexit -o nounset -o pipefail -o errtrace + +# ------------------------------------------------------------------------------ +# Logfile preparation +# ------------------------------------------------------------------------------ +LOGDIR=${LOGDIR:-/var/log/proxmoxve} +mkdir -p "$LOGDIR" 2>/dev/null || true + +SCRIPT_NAME="${SCRIPT_NAME:-$(basename "$0")}"; +RUN_ID="${RUN_ID:-$(date +%Y%m%d_%H%M%S)_$$}"; +LOGFILE="${LOGFILE:-$LOGDIR/${SCRIPT_NAME%.sh}_$RUN_ID.log}" + +LOG_LEVEL="${LOG_LEVEL:-INFO}" +declare -A LEVELS=([DEBUG]=0 [INFO]=1 [WARN]=2 [ERROR]=3) + +# Preserve original stdout/stderr for terminal output +exec 3>&1 4>&2 + +log_msg() { + local level="$1"; shift + local msg="$*" + local ts + ts="$(date '+%Y-%m-%d %H:%M:%S')" + echo "[$ts] [$SCRIPT_NAME] [$level] $msg" >>"$LOGFILE" + if (( ${LEVELS[$level]} >= ${LEVELS[$LOG_LEVEL]} )); then + case "$level" in + DEBUG) + [[ "${var_full_verbose:-0}" -eq 1 ]] && echo -e "\033[36m[DEBUG]\033[0m $msg" >&3 ;; + INFO) + echo -e "\033[34m[INFO]\033[0m $msg" >&3 ;; + WARN) + echo -e "\033[33m[WARN]\033[0m $msg" >&3 ;; + ERROR) + echo -e "\033[31m[ERROR]\033[0m $msg" >&4 ;; + esac + fi +} + +log_debug() { log_msg DEBUG "$*"; } +log_info() { log_msg INFO "$*"; } +log_warn() { log_msg WARN "$*"; } +log_error() { log_msg ERROR "$*"; } + +# Backward compatible wrappers +msg_info() { log_info "ℹ️ $*"; } +msg_ok() { log_info "✅ $*"; } +msg_warn() { log_warn "⚠️ $*"; } +msg_error() { log_error "❌ $*"; } +msg_debug() { log_debug "$*"; } + +# ------------------------------------------------------------------------------ +# Capture arbitrary stdout/stderr (including from subshells) +# ------------------------------------------------------------------------------ +log_stream() { + local level="$1" + while IFS= read -r line; do + log_msg "$level" "$line" + done +} + +# Redirect script output through logger +exec > >(log_stream INFO) 2> >(log_stream ERROR) + +# ------------------------------------------------------------------------------ +# Error handler – logs failing command, line, and exits +# ------------------------------------------------------------------------------ +error_handler() { + local code="$?" + local cmd="${BASH_COMMAND:-unknown}" + local line="${BASH_LINENO[0]:-unknown}" + local file="${BASH_SOURCE[1]:-unknown}" + log_error "command '$cmd' failed in $file:$line with exit code $code" + exit "$code" +} + +trap error_handler ERR + From af46e22bd2e526ee8c8139838a196780592a8dfc Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 25 Aug 2025 14:30:28 +0200 Subject: [PATCH 0718/1733] Revert "feat: add centralized logging utilities" --- misc/alpine-install.func | 1 - misc/api.func | 10 ----- misc/build.func | 1 - misc/core.func | 19 +-------- misc/create_lxc.sh | 1 - misc/install.func | 1 - misc/logger.func | 88 ---------------------------------------- 7 files changed, 2 insertions(+), 119 deletions(-) delete mode 100644 misc/logger.func diff --git a/misc/alpine-install.func b/misc/alpine-install.func index 660a3227d..450e92091 100644 --- a/misc/alpine-install.func +++ b/misc/alpine-install.func @@ -32,7 +32,6 @@ error_handler() { local exit_code="$?" local line_number="$1" local command="$2" - log_error "line $line_number: exit code $exit_code while executing command $command" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" echo -e "\n$error_message\n" } diff --git a/misc/api.func b/misc/api.func index 58ea56e02..08bdc914b 100644 --- a/misc/api.func +++ b/misc/api.func @@ -2,16 +2,6 @@ # Author: michelroegl-brunner # License: MIT | https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/LICENSE -if [[ -f "$(dirname "${BASH_SOURCE[0]}")/logger.func" ]]; then - source "$(dirname "${BASH_SOURCE[0]}")/logger.func" -else - if command -v curl >/dev/null 2>&1; then - source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/logger.func) - elif command -v wget >/dev/null 2>&1; then - source <(wget -qO- https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/logger.func) - fi -fi - get_error_description() { local exit_code="$1" case "$exit_code" in diff --git a/misc/build.func b/misc/build.func index c2e78b58a..8a58664fa 100644 --- a/misc/build.func +++ b/misc/build.func @@ -43,7 +43,6 @@ error_handler() { local line_number="$1" local command="$2" printf "\e[?25h" - log_error "line $line_number: exit code $exit_code while executing command $command" local error_message="[ERROR] in line $line_number: exit code $exit_code: while executing command $command" post_update_to_api "failed" "$command" echo -e "\n$error_message\n" diff --git a/misc/core.func b/misc/core.func index 6ed1faf3a..5df50efd9 100644 --- a/misc/core.func +++ b/misc/core.func @@ -9,16 +9,6 @@ [[ -n "${_CORE_FUNC_LOADED:-}" ]] && return _CORE_FUNC_LOADED=1 -if [[ -f "$(dirname "${BASH_SOURCE[0]}")/logger.func" ]]; then - source "$(dirname "${BASH_SOURCE[0]}")/logger.func" -else - if command -v curl >/dev/null 2>&1; then - source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/logger.func) - elif command -v wget >/dev/null 2>&1; then - source <(wget -qO- https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/logger.func) - fi -fi - load_functions() { [[ -n "${__FUNCTIONS_LOADED:-}" ]] && return __FUNCTIONS_LOADED=1 @@ -106,9 +96,9 @@ _tool_error_hint() { # exit 143 # } -# logger.func now sets strict modes and traps globally catch_errors() { - : + set -Eeuo pipefail + trap 'error_handler $LINENO "$BASH_COMMAND"' ERR } # ------------------------------------------------------------------------------ @@ -349,7 +339,6 @@ stop_spinner() { msg_info() { local msg="$1" [[ -z "$msg" ]] && return - log_info "$msg" if ! declare -p MSG_INFO_SHOWN &>/dev/null || ! declare -A MSG_INFO_SHOWN &>/dev/null; then declare -gA MSG_INFO_SHOWN=() @@ -376,7 +365,6 @@ msg_info() { msg_ok() { local msg="$1" [[ -z "$msg" ]] && return - log_info "$msg" stop_spinner clear_line printf "%s %b\n" "$CM" "${GN}${msg}${CL}" >&2 @@ -386,14 +374,12 @@ msg_ok() { msg_error() { stop_spinner local msg="$1" - log_error "$msg" echo -e "${BFR:-} ${CROSS:-✖️} ${RD}${msg}${CL}" } msg_warn() { stop_spinner local msg="$1" - log_warn "$msg" echo -e "${BFR:-} ${INFO:-ℹ️} ${YWB}${msg}${CL}" } @@ -409,7 +395,6 @@ msg_custom() { function msg_debug() { if [[ "${var_full_verbose:-0}" == "1" ]]; then [[ "${var_verbose:-0}" != "1" ]] && var_verbose=1 - log_debug "$*" echo -e "${YWB}[$(date '+%F %T')] [DEBUG]${CL} $*" fi } diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index 3ada6c96f..7a5d18cf9 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -36,7 +36,6 @@ function error_handler() { local line_number="$1" local command="$2" printf "\e[?25h" - log_error "line $line_number: exit code $exit_code while executing command $command" echo -e "\n${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}\n" exit "$exit_code" } diff --git a/misc/install.func b/misc/install.func index f7ee67458..e3751c295 100644 --- a/misc/install.func +++ b/misc/install.func @@ -36,7 +36,6 @@ error_handler() { local exit_code="$?" local line_number="$1" local command="$2" - log_error "line $line_number: exit code $exit_code while executing command $command" local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" echo -e "\n$error_message" diff --git a/misc/logger.func b/misc/logger.func deleted file mode 100644 index 0b9290117..000000000 --- a/misc/logger.func +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env bash -# Centralized logging utilities for Proxmox helper scripts -# Provides log_* functions, stdout/stderr capture, and robust error handling. - -# Prevent multiple sourcing -[[ -n "${_LOGGER_FUNC_LOADED:-}" ]] && return -_LOGGER_FUNC_LOADED=1 - -# ------------------------------------------------------------------------------ -# Shell options – fail fast and propagate ERR through subshells -# ------------------------------------------------------------------------------ -set -o errexit -o nounset -o pipefail -o errtrace - -# ------------------------------------------------------------------------------ -# Logfile preparation -# ------------------------------------------------------------------------------ -LOGDIR=${LOGDIR:-/var/log/proxmoxve} -mkdir -p "$LOGDIR" 2>/dev/null || true - -SCRIPT_NAME="${SCRIPT_NAME:-$(basename "$0")}"; -RUN_ID="${RUN_ID:-$(date +%Y%m%d_%H%M%S)_$$}"; -LOGFILE="${LOGFILE:-$LOGDIR/${SCRIPT_NAME%.sh}_$RUN_ID.log}" - -LOG_LEVEL="${LOG_LEVEL:-INFO}" -declare -A LEVELS=([DEBUG]=0 [INFO]=1 [WARN]=2 [ERROR]=3) - -# Preserve original stdout/stderr for terminal output -exec 3>&1 4>&2 - -log_msg() { - local level="$1"; shift - local msg="$*" - local ts - ts="$(date '+%Y-%m-%d %H:%M:%S')" - echo "[$ts] [$SCRIPT_NAME] [$level] $msg" >>"$LOGFILE" - if (( ${LEVELS[$level]} >= ${LEVELS[$LOG_LEVEL]} )); then - case "$level" in - DEBUG) - [[ "${var_full_verbose:-0}" -eq 1 ]] && echo -e "\033[36m[DEBUG]\033[0m $msg" >&3 ;; - INFO) - echo -e "\033[34m[INFO]\033[0m $msg" >&3 ;; - WARN) - echo -e "\033[33m[WARN]\033[0m $msg" >&3 ;; - ERROR) - echo -e "\033[31m[ERROR]\033[0m $msg" >&4 ;; - esac - fi -} - -log_debug() { log_msg DEBUG "$*"; } -log_info() { log_msg INFO "$*"; } -log_warn() { log_msg WARN "$*"; } -log_error() { log_msg ERROR "$*"; } - -# Backward compatible wrappers -msg_info() { log_info "ℹ️ $*"; } -msg_ok() { log_info "✅ $*"; } -msg_warn() { log_warn "⚠️ $*"; } -msg_error() { log_error "❌ $*"; } -msg_debug() { log_debug "$*"; } - -# ------------------------------------------------------------------------------ -# Capture arbitrary stdout/stderr (including from subshells) -# ------------------------------------------------------------------------------ -log_stream() { - local level="$1" - while IFS= read -r line; do - log_msg "$level" "$line" - done -} - -# Redirect script output through logger -exec > >(log_stream INFO) 2> >(log_stream ERROR) - -# ------------------------------------------------------------------------------ -# Error handler – logs failing command, line, and exits -# ------------------------------------------------------------------------------ -error_handler() { - local code="$?" - local cmd="${BASH_COMMAND:-unknown}" - local line="${BASH_LINENO[0]:-unknown}" - local file="${BASH_SOURCE[1]:-unknown}" - log_error "command '$cmd' failed in $file:$line with exit code $code" - exit "$code" -} - -trap error_handler ERR - From 0c903faa0b2c86fb32c648d679d8571ef3c4b391 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Mon, 25 Aug 2025 15:23:51 +0200 Subject: [PATCH 0719/1733] Enhance .env setup with reverse proxy guidance Updated .env configuration with comments for clarity. --- install/tracktor-install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/install/tracktor-install.sh b/install/tracktor-install.sh index 00c6c1d7e..6ec206ea3 100644 --- a/install/tracktor-install.sh +++ b/install/tracktor-install.sh @@ -26,7 +26,8 @@ cat </opt/tracktor/app/backend/.env NODE_ENV=production PUBLIC_DEMO_MODE=false DB_PATH=/opt/tracktor-data/tracktor.db -PUBLIC_API_BASE_URL=http://$HOST_IP:3000 +PUBLIC_API_BASE_URL=http://$HOST_IP:3000 # Replace this URL if using behind reverse proxy for https traffic. Though it is optional and should work without changing +CORS_ORIGINS=http://$HOST_IP:3000 # Here add the reverse proxy url as well to avoid cross errors from the app. PORT=3000 EOF msg_ok "Configured Tracktor" From b4446368645f76d92ba3b362fd1f3291a8e18afd Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 25 Aug 2025 15:29:43 +0200 Subject: [PATCH 0720/1733] Update create_lxc.sh --- misc/create_lxc.sh | 201 +++++++++++++++++++++++++++------------------ 1 file changed, 119 insertions(+), 82 deletions(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index 7a5d18cf9..c89e5d72b 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -205,7 +205,6 @@ if qm status "$CTID" &>/dev/null || pct status "$CTID" &>/dev/null; then fi # This checks for the presence of valid Container Storage and Template Storage locations -msg_info "Validating storage" if ! check_storage_support "rootdir"; then msg_error "No valid storage found for 'rootdir' [Container]" exit 1 @@ -280,158 +279,196 @@ fi # Update LXC template list TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" case "$PCT_OSTYPE" in -debian | ubuntu) - TEMPLATE_PATTERN="-standard_" - ;; -alpine | fedora | rocky | centos) - TEMPLATE_PATTERN="-default_" - ;; -*) - TEMPLATE_PATTERN="" - ;; +debian | ubuntu) TEMPLATE_PATTERN="-standard_" ;; +alpine | fedora | rocky | centos) TEMPLATE_PATTERN="-default_" ;; +*) TEMPLATE_PATTERN="" ;; esac -# 1. Check local templates first msg_info "Searching for template '$TEMPLATE_SEARCH'" -mapfile -t TEMPLATES < <( - pveam list "$TEMPLATE_STORAGE" | + +# 1. get / check local templates +mapfile -t LOCAL_TEMPLATES < <( + pveam list "$TEMPLATE_STORAGE" 2>/dev/null | awk -v s="$TEMPLATE_SEARCH" -v p="$TEMPLATE_PATTERN" '$1 ~ s && $1 ~ p {print $1}' | sed 's/.*\///' | sort -t - -k 2 -V ) -if [ ${#TEMPLATES[@]} -gt 0 ]; then +# 2. get online templates +pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)." +mapfile -t ONLINE_TEMPLATES < <( + pveam available -section system 2>/dev/null | + sed -n "s/.*\($TEMPLATE_SEARCH.*$TEMPLATE_PATTERN.*\)/\1/p" | + sort -t - -k 2 -V +) +ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]:-}" + +# 3. Local vs Online +if [ ${#LOCAL_TEMPLATES[@]} -gt 0 ]; then + TEMPLATE="${LOCAL_TEMPLATES[-1]}" TEMPLATE_SOURCE="local" else - msg_info "No local template found, checking online repository" - pveam update >/dev/null 2>&1 - mapfile -t TEMPLATES < <( - pveam update >/dev/null 2>&1 && - pveam available -section system | - sed -n "s/.*\($TEMPLATE_SEARCH.*$TEMPLATE_PATTERN.*\)/\1/p" | - sort -t - -k 2 -V - ) + TEMPLATE="$ONLINE_TEMPLATE" TEMPLATE_SOURCE="online" fi -TEMPLATE="${TEMPLATES[-1]}" -TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || - echo "/var/lib/vz/template/cache/$TEMPLATE")" -msg_ok "Template ${BL}$TEMPLATE${CL} [$TEMPLATE_SOURCE]" - -msg_debug "TEMPLATE_SEARCH=$TEMPLATE_SEARCH" -msg_debug "TEMPLATES=(${TEMPLATES[*]})" -msg_debug "Selected TEMPLATE=$TEMPLATE" -msg_debug "TEMPLATE_PATH=$TEMPLATE_PATH" - -# 4. Validate template (exists & not corrupted) -TEMPLATE_VALID=1 -if [ ! -s "$TEMPLATE_PATH" ]; then - TEMPLATE_VALID=0 -elif ! tar --use-compress-program=zstdcat -tf "$TEMPLATE_PATH" >/dev/null 2>&1; then - TEMPLATE_VALID=0 +# 4. Getting Path (universal, also for nfs/cifs) +TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" +if [[ -z "$TEMPLATE_PATH" ]]; then + TEMPLATE_BASE=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) + if [[ -n "$TEMPLATE_BASE" ]]; then + TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE" + fi fi -if [ "$TEMPLATE_VALID" -eq 0 ]; then - msg_warn "Template $TEMPLATE is missing or corrupted. Re-downloading." +if [[ -z "$TEMPLATE_PATH" ]]; then + msg_error "Unable to resolve template path for $TEMPLATE_STORAGE. Check storage type and permissions." + exit 220 +fi + +msg_ok "Template ${BL}$TEMPLATE${CL} [$TEMPLATE_SOURCE]" +msg_debug "Resolved TEMPLATE_PATH=$TEMPLATE_PATH" + +# 5. Validation +NEED_DOWNLOAD=0 +if [[ ! -f "$TEMPLATE_PATH" ]]; then + msg_info "Template not present locally – will download." + NEED_DOWNLOAD=1 +elif [[ ! -r "$TEMPLATE_PATH" ]]; then + msg_error "Template file exists but is not readable – check permissions." + exit 221 +elif [[ "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then + msg_warn "Template file too small (<1MB) – re-downloading." + NEED_DOWNLOAD=1 +elif ! zstdcat "$TEMPLATE_PATH" | tar -tf - &>/dev/null; then + msg_warn "Template appears corrupted – re-downloading." + NEED_DOWNLOAD=1 +else + msg_ok "Template $TEMPLATE is present and valid." +fi + +# 6. Update-Check (if local exist) +if [[ "$TEMPLATE_SOURCE" == "local" && -n "$ONLINE_TEMPLATE" && "$TEMPLATE" != "$ONLINE_TEMPLATE" ]]; then + msg_warn "Local template is outdated: $TEMPLATE (latest available: $ONLINE_TEMPLATE)" + if whiptail --yesno "A newer template is available:\n$ONLINE_TEMPLATE\n\nDo you want to download and use it instead?" 12 70; then + TEMPLATE="$ONLINE_TEMPLATE" + NEED_DOWNLOAD=1 + else + msg_info "Continuing with local template $TEMPLATE" + fi +fi + +# 7. Download if needed +if [[ "$NEED_DOWNLOAD" -eq 1 ]]; then [[ -f "$TEMPLATE_PATH" ]] && rm -f "$TEMPLATE_PATH" for attempt in {1..3}; do - msg_info "Attempt $attempt: Downloading LXC template..." + msg_info "Attempt $attempt: Downloading template $TEMPLATE to $TEMPLATE_STORAGE" if pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null 2>&1; then msg_ok "Template download successful." break fi if [ $attempt -eq 3 ]; then - msg_error "Failed after 3 attempts. Please check network access or manually run:\n pveam download $TEMPLATE_STORAGE $TEMPLATE" - exit 208 + msg_error "Failed after 3 attempts. Please check network access, permissions, or manually run:\n pveam download $TEMPLATE_STORAGE $TEMPLATE" + exit 222 fi sleep $((attempt * 5)) done fi -msg_info "Creating LXC Container" -# Check and fix subuid/subgid +# 8. Final Check – Template usability +if ! pveam list "$TEMPLATE_STORAGE" 2>/dev/null | grep -q "$TEMPLATE"; then + msg_error "Template $TEMPLATE not available in storage $TEMPLATE_STORAGE after download." + exit 223 +fi +msg_ok "Template $TEMPLATE is ready for container creation." + +# ------------------------------------------------------------------------------ +# Create LXC Container with validation, recovery and debug option +# ------------------------------------------------------------------------------ + +msg_info "Creating LXC container" + +# Ensure subuid/subgid entries exist grep -q "root:100000:65536" /etc/subuid || echo "root:100000:65536" >>/etc/subuid grep -q "root:100000:65536" /etc/subgid || echo "root:100000:65536" >>/etc/subgid -# Combine all options +# Assemble pct options PCT_OPTIONS=(${PCT_OPTIONS[@]:-${DEFAULT_PCT_OPTIONS[@]}}) [[ " ${PCT_OPTIONS[@]} " =~ " -rootfs " ]] || PCT_OPTIONS+=(-rootfs "$CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}") -# Secure creation of the LXC container with lock and template check +# Secure with lockfile lockfile="/tmp/template.${TEMPLATE}.lock" -msg_debug "Creating lockfile: $lockfile" exec 9>"$lockfile" || { msg_error "Failed to create lock file '$lockfile'." exit 200 } flock -w 60 9 || { - msg_error "Timeout while waiting for template lock" + msg_error "Timeout while waiting for template lock." exit 211 } +LOGFILE="/tmp/pct_create_${CTID}.log" msg_debug "pct create command: pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" +msg_debug "Logfile: $LOGFILE" -if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" &>/dev/null; then +# First attempt +if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >"$LOGFILE" 2>&1; then msg_error "Container creation failed on ${TEMPLATE_STORAGE}. Checking template..." + # Validate template file if [[ ! -s "$TEMPLATE_PATH" || "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then - msg_error "Template file too small or missing – re-downloading." + msg_warn "Template file too small or missing – re-downloading." rm -f "$TEMPLATE_PATH" pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" elif ! zstdcat "$TEMPLATE_PATH" | tar -tf - &>/dev/null; then - msg_error "Template appears to be corrupted – re-downloading." + msg_warn "Template appears corrupted – re-downloading." rm -f "$TEMPLATE_PATH" pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" - else - # --- NEW FALLBACK LOGIC --- + fi + + # Retry after repair + if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then + # Fallback to local storage if [[ "$TEMPLATE_STORAGE" != "local" ]]; then - msg_warn "Retrying container creation with fallback to local template storage..." + msg_warn "Retrying container creation with fallback to local storage..." LOCAL_TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" if [ ! -f "$LOCAL_TEMPLATE_PATH" ]; then msg_info "Downloading template to local..." - msg_debug "pveam download local $TEMPLATE" - pveam download local "$TEMPLATE" + pveam download local "$TEMPLATE" >/dev/null 2>&1 fi - msg_debug "pct create command (fallback): pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" - if pct create "$CTID" "local:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" &>/dev/null; then - msg_ok "Container successfully created using fallback to local template storage." + if pct create "$CTID" "local:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then + msg_ok "Container successfully created using local fallback." else - msg_error "Container creation failed even with fallback to local." + msg_error "Container creation failed even with local fallback. See $LOGFILE" + # Ask user if they want debug output + if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then + set -x + bash -x -c "pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" 2>&1 | tee -a "$LOGFILE" + set +x + fi exit 209 fi - # Skip remaining error handling since fallback worked - continue else - msg_error "Template is valid, but container creation still failed on local." + msg_error "Container creation failed on local storage. See $LOGFILE" + if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then + set -x + bash -x -c "pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" 2>&1 | tee -a "$LOGFILE" + set +x + fi exit 209 fi fi fi +# Verify container exists if ! pct list | awk '{print $1}' | grep -qx "$CTID"; then - msg_error "Container ID $CTID not listed in 'pct list' – unexpected failure." + msg_error "Container ID $CTID not listed in 'pct list'. See $LOGFILE" exit 215 fi +# Verify config rootfs if ! grep -q '^rootfs:' "/etc/pve/lxc/$CTID.conf"; then - msg_error "RootFS entry missing in container config – storage not correctly assigned." + msg_error "RootFS entry missing in container config. See $LOGFILE" exit 216 fi -if grep -q '^hostname:' "/etc/pve/lxc/$CTID.conf"; then - CT_HOSTNAME=$(grep '^hostname:' "/etc/pve/lxc/$CTID.conf" | awk '{print $2}') - if [[ ! "$CT_HOSTNAME" =~ ^[a-z0-9-]+$ ]]; then - msg_warn "Hostname '$CT_HOSTNAME' contains invalid characters – may cause issues with networking or DNS." - fi -fi - -if [[ "${PCT_RAM_SIZE:-2048}" -lt 1024 ]]; then - msg_warn "Configured RAM (${PCT_RAM_SIZE}MB) is below 1024MB – some apps may not work properly." -fi - -# comment out 19.08.2025 - PCT Options not correct, message every new lxc (without fuse too) -# if [[ "${PCT_UNPRIVILEGED:-1}" == "1" && " ${PCT_OPTIONS[*]} " == *"fuse=1"* ]]; then -# msg_warn "Unprivileged container with FUSE may fail unless extra device mappings are configured." -# fi - msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created." From 1e602015b9710ca72266b48157467652ae8079b8 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 25 Aug 2025 15:36:39 +0200 Subject: [PATCH 0721/1733] improve sleep --- misc/build.func | 1 + 1 file changed, 1 insertion(+) diff --git a/misc/build.func b/misc/build.func index 8a58664fa..fcf81c1f4 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1363,6 +1363,7 @@ EOF if [ "$var_os" != "alpine" ]; then msg_info "Waiting for network in LXC container" + sleep 2 for i in {1..10}; do # 1. Primary check: ICMP ping (fastest, but may be blocked by ISP/firewall) if pct exec "$CTID" -- ping -c1 -W1 deb.debian.org >/dev/null 2>&1; then From e9b7ded215e4d4463096e83f05c2984fa3c3d31e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 25 Aug 2025 15:37:19 +0200 Subject: [PATCH 0722/1733] silent sleep for 3 steps --- misc/build.func | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/misc/build.func b/misc/build.func index fcf81c1f4..011f43981 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1372,8 +1372,12 @@ EOF fi # Wait and retry if not reachable yet if [ "$i" -lt 10 ]; then - msg_warn "No network in LXC yet (try $i/10) – waiting..." - sleep 3 + if [ "$i" -le 3 ]; then + sleep 2 + else + msg_warn "No network in LXC yet (try $i/10) – waiting..." + sleep 3 + fi else # After 10 unsuccessful ping attempts, try HTTP connectivity via wget as fallback msg_warn "Ping failed 10 times. Trying HTTP connectivity check (wget) as fallback..." From d2cafebcd928403d1a5d07b9a44fda5c673aefc0 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 25 Aug 2025 15:39:50 +0200 Subject: [PATCH 0723/1733] mariadb fallback if repo down --- misc/tools.func | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/misc/tools.func b/misc/tools.func index 1413c26cd..96c27a30b 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -299,7 +299,19 @@ setup_mariadb() { echo "mariadb-server-$ver mariadb-server/feedback boolean false" | debconf-set-selections done fi - DEBIAN_FRONTEND=noninteractive $STD apt-get install -y mariadb-server mariadb-client + DEBIAN_FRONTEND=noninteractive $STD apt-get install -y mariadb-server mariadb-client || { + msg_warn "Failed to install MariaDB ${MARIADB_VERSION} from upstream repo – trying distro package as fallback..." + # Cleanup, remove upstream repo to avoid conflicts + rm -f /etc/apt/sources.list.d/mariadb.list /etc/apt/trusted.gpg.d/mariadb.gpg + $STD apt-get update + # Final fallback: distro package + DEBIAN_FRONTEND=noninteractive $STD apt-get install -y mariadb-server mariadb-client || { + msg_error "MariaDB installation failed even with distro fallback!" + return 1 + } + msg_ok "Setup MariaDB (distro fallback)" + return 0 + } msg_ok "Setup MariaDB $MARIADB_VERSION" } From 92e50a143850f9bcacf87d70e5cf3ec1a29f7d64 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 25 Aug 2025 15:42:37 +0200 Subject: [PATCH 0724/1733] Update leantime-install.sh --- install/leantime-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/leantime-install.sh b/install/leantime-install.sh index ff3580100..086157c8c 100644 --- a/install/leantime-install.sh +++ b/install/leantime-install.sh @@ -13,7 +13,7 @@ setting_up_container network_check update_os -PHP_VERSION=8.4 PHP_MODULE="mysql" PHP_APACHE="YES" PHP_FPM="YES" setup_php +PHP_VERSION="8.4" PHP_MODULE="mysql" PHP_APACHE="YES" PHP_FPM="YES" setup_php setup_mariadb msg_info "Setting up Database" From 36db41530af3033effa94253a630d75fc4971ed4 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Mon, 25 Aug 2025 16:39:42 +0200 Subject: [PATCH 0725/1733] Improve comments in tracktor-install.sh Updated comments for clarity in environment variable configuration. --- install/tracktor-install.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/install/tracktor-install.sh b/install/tracktor-install.sh index 6ec206ea3..d0d432d9c 100644 --- a/install/tracktor-install.sh +++ b/install/tracktor-install.sh @@ -26,8 +26,10 @@ cat </opt/tracktor/app/backend/.env NODE_ENV=production PUBLIC_DEMO_MODE=false DB_PATH=/opt/tracktor-data/tracktor.db -PUBLIC_API_BASE_URL=http://$HOST_IP:3000 # Replace this URL if using behind reverse proxy for https traffic. Though it is optional and should work without changing -CORS_ORIGINS=http://$HOST_IP:3000 # Here add the reverse proxy url as well to avoid cross errors from the app. +# Replace this URL if using behind reverse proxy for https traffic. Though it is optional and should work without changing +PUBLIC_API_BASE_URL=http://$HOST_IP:3000 +# Here add the reverse proxy url as well to avoid cross errors from the app. +CORS_ORIGINS=http://$HOST_IP:3000 PORT=3000 EOF msg_ok "Configured Tracktor" From f295102e72549b00fae53e98faf516cf01b9bcea Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Mon, 25 Aug 2025 16:42:21 +0200 Subject: [PATCH 0726/1733] Add note about updating .env file for reverse proxy Added informational note regarding reverse proxy configuration. --- frontend/public/json/tracktor.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/public/json/tracktor.json b/frontend/public/json/tracktor.json index 2d7d0a07b..0a8f7c3b8 100644 --- a/frontend/public/json/tracktor.json +++ b/frontend/public/json/tracktor.json @@ -31,5 +31,10 @@ "username": null, "password": null }, - "notes": [] + "notes": [ + { + "text": "Please check and update the '/opt/tracktor/app/backend/.env' file if using behind reverse proxy.", + "type": "info" + } + ] } From 78912e2c7a6e8b8dfa898c993941a62bb50ea93a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 25 Aug 2025 16:58:00 +0200 Subject: [PATCH 0727/1733] Update create_lxc.sh --- misc/create_lxc.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index c89e5d72b..f4d4d07ba 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -214,7 +214,6 @@ if ! check_storage_support "vztmpl"; then exit 1 fi -msg_info "Checking template storage" while true; do if select_storage template; then TEMPLATE_STORAGE="$STORAGE_RESULT" From b83dd90a7a6241054916346549366923e53c40a8 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 26 Aug 2025 08:47:04 +0200 Subject: [PATCH 0728/1733] romm testing --- ct/romm.sh | 73 +++++++++++ frontend/public/json/romm.json | 40 ++++++ install/romm-install.sh | 222 +++++++++++++++++++++++++++++++++ 3 files changed, 335 insertions(+) create mode 100644 ct/romm.sh create mode 100644 frontend/public/json/romm.json create mode 100644 install/romm-install.sh diff --git a/ct/romm.sh b/ct/romm.sh new file mode 100644 index 000000000..bcc0343e3 --- /dev/null +++ b/ct/romm.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash +source <(curl -s https://raw.githubusercontent.com/DevelopmentCats/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 DevelopmentCats +# Author: DevelopmentCats +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://romm.app +# Updated: 03/10/2025 + +APP="RomM" +var_tags="${var_tags:-emulation}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-4096}" +var_disk="${var_disk:-20}" +var_os="${var_os:-ubuntu}" +var_version="${var_version:-24.04}" +var_unprivileged="${var_unprivileged:-1}" +var_fuse="${var_fuse:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -d /opt/romm ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + msg_info "Stopping $APP" + systemctl stop romm + systemctl stop nginx + msg_ok "Stopped $APP" + + msg_info "Updating $APP" + cd /opt/romm/app + git pull + + # Update backend + cd /opt/romm/app + source /opt/romm/venv/bin/activate + pip install --upgrade pip + pip install poetry + poetry install + + # Update frontend + cd /opt/romm/app/frontend + npm install + npm run build + + echo "Updated on $(date)" >/opt/romm/version.txt + msg_ok "Updated $APP" + + msg_info "Starting $APP" + systemctl start romm + systemctl start nginx + msg_ok "Started $APP" + msg_ok "Update Successful" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" diff --git a/frontend/public/json/romm.json b/frontend/public/json/romm.json new file mode 100644 index 000000000..fc8ed35e4 --- /dev/null +++ b/frontend/public/json/romm.json @@ -0,0 +1,40 @@ +{ + "name": "RomM", + "slug": "romm", + "categories": [ + 2, + 3 + ], + "date_created": "2025-03-10", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 8080, + "documentation": "https://docs.romm.app/latest/", + "website": "https://romm.app/", + "config_path": "/opt", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/romm.webp", + "description": "RomM (ROM Manager) allows you to scan, enrich, browse and play your game collection with a clean and responsive interface. Support for multiple platforms, various naming schemes, and custom tags.", + "install_methods": [ + { + "type": "default", + "script": "ct/romm.sh", + "resources": { + "cpu": 2, + "ram": 4096, + "hdd": 20, + "os": "ubuntu", + "version": "24.04" + } + } + ], + "default_credentials": { + "username": "romm", + "password": "changeme" + }, + "notes": [ + "API Keys: For full functionality, you'll need API keys from IGDB, MobyGames, and/or SteamGridDB.", + "ROM Directory: Your ROM files should follow the RomM folder structure outlined in the documentation.", + "Authentication: The admin username and password will be set during the first-run setup process." + ] +} diff --git a/install/romm-install.sh b/install/romm-install.sh new file mode 100644 index 000000000..c43acb109 --- /dev/null +++ b/install/romm-install.sh @@ -0,0 +1,222 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: DevelopmentCats +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://romm.app +# Updated: 03/10/2025 + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing dependencies" +$STD apt-get install -y \ + acl \ + build-essential \ + libssl-dev \ + libffi-dev \ + python3-dev \ + python3-pip \ + python3-venv \ + libmariadb3 \ + libmariadb-dev \ + libpq-dev \ + redis-tools \ + p7zip \ + tzdata \ + jq +msg_ok "Installed core dependencies" + +PYTHON_VERSION="3.12" setup_uv +NODE_VERSION="22" NODE_MODULE="serve" setup_nodejs + +msg_info "Configuring Database" +DB_NAME=romm +DB_USER=romm +DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) +$STD mariadb -u root -e "CREATE DATABASE $DB_NAME CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" +$STD mariadb -u root -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';" +$STD mariadb -u root -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" +{ + echo "RomM-Credentials" + echo "RomM Database User: $DB_USER" + echo "RomM Database Password: $DB_PASS" + echo "RomM Database Name: $DB_NAME" +} >~/romm.creds +chmod 600 ~/romm.creds +msg_ok "Configured Database" + +msg_info "Creating romm user and directories" +id -u romm &>/dev/null || useradd -r -m -d /var/lib/romm -s /bin/bash romm +mkdir -p /opt/romm \ + /var/lib/romm/config \ + /var/lib/romm/resources \ + /var/lib/romm/assets/{saves,states,screenshots} \ + /var/lib/romm/library/roms/{gba,gbc,ps} \ + /var/lib/romm/library/bios/{gba,ps} +chown -R romm:romm /opt/romm /var/lib/romm +msg_ok "Created romm user and directories" + +msg_info "Configuring Database" +DB_NAME=romm +DB_USER=romm +DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) +$STD mariadb -u root -e "CREATE DATABASE $DB_NAME CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" +$STD mariadb -u root -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';" +$STD mariadb -u root -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" +{ + echo "RomM-Credentials" + echo "RomM Database User: $DB_USER" + echo "RomM Database Password: $DB_PASS" + echo "RomM Database Name: $DB_NAME" +} >~/romm.creds +msg_ok "Configured Database" + +fetch_and_deploy_gh_release "romm" "rommapp/romm" + +msg_info "Creating environment file" +sed -i 's/^supervised no/supervised systemd/' /etc/redis/redis.conf +systemctl restart redis-server +systemctl enable -q --now redis-server +AUTH_SECRET_KEY=$(openssl rand -hex 32) + +cat >/opt/romm/.env </etc/systemd/system/romm-backend.service </etc/systemd/system/romm-frontend.service </etc/systemd/system/romm-worker.service </etc/systemd/system/romm-scheduler.service < Date: Tue, 26 Aug 2025 08:47:48 +0200 Subject: [PATCH 0729/1733] Update romm.sh --- ct/romm.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ct/romm.sh b/ct/romm.sh index bcc0343e3..8dc95b587 100644 --- a/ct/romm.sh +++ b/ct/romm.sh @@ -1,10 +1,9 @@ #!/usr/bin/env bash -source <(curl -s https://raw.githubusercontent.com/DevelopmentCats/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 DevelopmentCats -# Author: DevelopmentCats +source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://romm.app -# Updated: 03/10/2025 APP="RomM" var_tags="${var_tags:-emulation}" From 3e7d3fa6180dd96d066c94b035c198c82ee0d269 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 26 Aug 2025 06:48:14 +0000 Subject: [PATCH 0730/1733] Update .app files --- ct/headers/alpine-redlib | 6 ------ ct/headers/healthchecks | 6 ------ ct/headers/romm | 6 ++++++ 3 files changed, 6 insertions(+), 12 deletions(-) delete mode 100644 ct/headers/alpine-redlib delete mode 100644 ct/headers/healthchecks create mode 100644 ct/headers/romm diff --git a/ct/headers/alpine-redlib b/ct/headers/alpine-redlib deleted file mode 100644 index 7d8f1130b..000000000 --- a/ct/headers/alpine-redlib +++ /dev/null @@ -1,6 +0,0 @@ - ___ __ _ ____ _____ __ - / | / /___ (_)___ ___ / __ \___ ____/ / (_) /_ - / /| | / / __ \/ / __ \/ _ \______/ /_/ / _ \/ __ / / / __ \ - / ___ |/ / /_/ / / / / / __/_____/ _, _/ __/ /_/ / / / /_/ / -/_/ |_/_/ .___/_/_/ /_/\___/ /_/ |_|\___/\__,_/_/_/_.___/ - /_/ diff --git a/ct/headers/healthchecks b/ct/headers/healthchecks deleted file mode 100644 index 8f61c8776..000000000 --- a/ct/headers/healthchecks +++ /dev/null @@ -1,6 +0,0 @@ - __ ____ __ __ __ - / /_ ___ ____ _/ / /_/ /_ _____/ /_ ___ _____/ /_______ - / __ \/ _ \/ __ `/ / __/ __ \/ ___/ __ \/ _ \/ ___/ //_/ ___/ - / / / / __/ /_/ / / /_/ / / / /__/ / / / __/ /__/ ,< (__ ) -/_/ /_/\___/\__,_/_/\__/_/ /_/\___/_/ /_/\___/\___/_/|_/____/ - diff --git a/ct/headers/romm b/ct/headers/romm new file mode 100644 index 000000000..7f214d48e --- /dev/null +++ b/ct/headers/romm @@ -0,0 +1,6 @@ + ____ __ ___ + / __ \____ ____ ___ / |/ / + / /_/ / __ \/ __ `__ \/ /|_/ / + / _, _/ /_/ / / / / / / / / / +/_/ |_|\____/_/ /_/ /_/_/ /_/ + From 19d48294dfeb1a017f57b06ac5331a08a6cb2ed1 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 26 Aug 2025 08:50:53 +0200 Subject: [PATCH 0731/1733] Delete rustdeskserver.json --- frontend/public/json/rustdeskserver.json | 55 ------------------------ 1 file changed, 55 deletions(-) delete mode 100644 frontend/public/json/rustdeskserver.json diff --git a/frontend/public/json/rustdeskserver.json b/frontend/public/json/rustdeskserver.json deleted file mode 100644 index 7dd43605c..000000000 --- a/frontend/public/json/rustdeskserver.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "name": "RustDesk Server", - "slug": "rustdeskserver", - "categories": [ - 21 - ], - "date_created": "2025-02-13", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 21114, - "documentation": "https://rustdesk.com/docs/en/", - "website": "https://rustdesk.com/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/rustdesk.webp", - "config_path": "", - "description": "RustDesk is a full-featured open source remote control alternative for self-hosting and security with minimal configuration.", - "install_methods": [ - { - "type": "default", - "script": "ct/rustdeskserver.sh", - "resources": { - "cpu": 1, - "ram": 512, - "hdd": 2, - "os": "debian", - "version": "12" - } - }, - { - "type": "alpine", - "script": "ct/alpine-rustdeskserver.sh", - "resources": { - "cpu": 1, - "ram": 512, - "hdd": 2, - "os": "alpine", - "version": "3.22" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "Check our configuration guide for help: `https://github.com/community-scripts/ProxmoxVE/discussions/2388`", - "type": "info" - }, - { - "text": "Login credentials: `cat ~/rustdesk.creds`", - "type": "info" - } - ] -} From ba92c7b642a14522869bab62a8e026f855ffa234 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 26 Aug 2025 08:51:16 +0200 Subject: [PATCH 0732/1733] - --- ct/alpine-rustdeskserver.sh | 74 -------------- install/alpine-rustdeskserver-install.sh | 122 ----------------------- 2 files changed, 196 deletions(-) delete mode 100644 ct/alpine-rustdeskserver.sh delete mode 100644 install/alpine-rustdeskserver-install.sh diff --git a/ct/alpine-rustdeskserver.sh b/ct/alpine-rustdeskserver.sh deleted file mode 100644 index 1494be484..000000000 --- a/ct/alpine-rustdeskserver.sh +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Slaviša Arežina (tremor021) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/rustdesk/rustdesk-server - -APP="Alpine-RustDeskServer" -var_tags="${var_tags:-alpine;monitoring}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-512}" -var_disk="${var_disk:-3}" -var_os="${var_os:-alpine}" -var_version="${var_version:-3.22}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - if [[ ! -d /opt/rustdesk-server ]]; then - msg_error "No ${APP} Installation Found!" - exit 1 - fi - - APIRELEASE=$(curl -s https://api.github.com/repos/lejianwen/rustdesk-api/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') - RELEASE=$(curl -s https://api.github.com/repos/rustdesk/rustdesk-server/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - if [ "${RELEASE}" != "$(cat ~/.rustdesk-server 2>/dev/null)" ] || [ ! -f ~/.rustdesk-server ]; then - msg_info "Updating RustDesk Server to v${RELEASE}" - $STD apk -U upgrade - $STD service rustdesk-server-hbbs stop - $STD service rustdesk-server-hbbr stop - temp_file1=$(mktemp) - curl -fsSL "https://github.com/rustdesk/rustdesk-server/releases/download/${RELEASE}/rustdesk-server-linux-amd64.zip" -o "$temp_file1" - $STD unzip "$temp_file1" - cp -r amd64/* /opt/rustdesk-server/ - echo "${RELEASE}" >~/.rustdesk-server - $STD service rustdesk-server-hbbs start - $STD service rustdesk-server-hbbr start - rm -rf amd64 - rm -f $temp_file1 - msg_ok "Updated RustDesk Server successfully" - else - msg_ok "No update required. ${APP} is already at v${RELEASE}" - fi - if [ "${APIRELEASE}" != "$(cat ~/.rustdesk-api)" ] || [ ! -f ~/.rustdesk-api ]; then - msg_info "Updating RustDesk API to v${APIRELEASE}" - $STD service rustdesk-api stop - temp_file2=$(mktemp) - curl -fsSL "https://github.com/lejianwen/rustdesk-api/releases/download/v${APIRELEASE}/linux-amd64.tar.gz" -o "$temp_file2" - $STD tar zxvf "$temp_file2" - cp -r release/* /opt/rustdesk-api - echo "${APIRELEASE}" >~/.rustdesk-api - $STD service rustdesk-api start - rm -rf release - rm -f $temp_file2 - msg_ok "Updated RustDesk API" - else - msg_ok "No update required. RustDesk API is already at v${APIRELEASE}" - fi - exit 0 -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following IP:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:21114${CL}" diff --git a/install/alpine-rustdeskserver-install.sh b/install/alpine-rustdeskserver-install.sh deleted file mode 100644 index b26f31347..000000000 --- a/install/alpine-rustdeskserver-install.sh +++ /dev/null @@ -1,122 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Slaviša Arežina (tremor021) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/rustdesk/rustdesk-server - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -RELEASE=$(curl -s https://api.github.com/repos/rustdesk/rustdesk-server/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') -msg_info "Installing RustDesk Server v${RELEASE}" -temp_file1=$(mktemp) -curl -fsSL "https://github.com/rustdesk/rustdesk-server/releases/download/${RELEASE}/rustdesk-server-linux-amd64.zip" -o "$temp_file1" -$STD unzip "$temp_file1" -mv amd64 /opt/rustdesk-server -mkdir -p /root/.config/rustdesk -cd /opt/rustdesk-server -./rustdesk-utils genkeypair > /tmp/rustdesk_keys.txt -grep "Public Key" /tmp/rustdesk_keys.txt | awk '{print $3}' > /root/.config/rustdesk/id_ed25519.pub -grep "Secret Key" /tmp/rustdesk_keys.txt | awk '{print $3}' > /root/.config/rustdesk/id_ed25519 -chmod 600 /root/.config/rustdesk/id_ed25519 -chmod 644 /root/.config/rustdesk/id_ed25519.pub -rm /tmp/rustdesk_keys.txt -echo "${RELEASE}" >~/.rustdesk-server -msg_ok "Installed RustDesk Server v${RELEASE}" - -APIRELEASE=$(curl -s https://api.github.com/repos/lejianwen/rustdesk-api/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') -msg_info "Installing RustDesk API v${APIRELEASE}" -temp_file2=$(mktemp) -curl -fsSL "https://github.com/lejianwen/rustdesk-api/releases/download/v${APIRELEASE}/linux-amd64.tar.gz" -o "$temp_file2" -$STD tar zxvf "$temp_file2" -mv release /opt/rustdesk-api -cd /opt/rustdesk-api -ADMINPASS=$(head -c 16 /dev/urandom | xxd -p -c 16) -$STD ./apimain reset-admin-pwd "$ADMINPASS" -{ - echo "RustDesk WebUI" - echo "" - echo "Username: admin" - echo "Password: $ADMINPASS" -} >>~/rustdesk.creds -echo "${APIRELEASE}" >~/.rustdesk-api -msg_ok "Installed RustDesk API v${APIRELEASE}" - -msg_info "Enabling RustDesk Server Services" -cat </etc/init.d/rustdesk-server-hbbs -#!/sbin/openrc-run -description="RustDesk HBBS Service" -directory="/opt/rustdesk-server" -command="/opt/rustdesk-server/hbbs" -command_args="" -command_background="true" -command_user="root" -pidfile="/var/run/rustdesk-server-hbbs.pid" -output_log="/var/log/rustdesk-hbbs.log" -error_log="/var/log/rustdesk-hbbs.err" - -depend() { - use net -} -EOF - -cat </etc/init.d/rustdesk-server-hbbr -#!/sbin/openrc-run -description="RustDesk HBBR Service" -directory="/opt/rustdesk-server" -command="/opt/rustdesk-server/hbbr" -command_args="" -command_background="true" -command_user="root" -pidfile="/var/run/rustdesk-server-hbbr.pid" -output_log="/var/log/rustdesk-hbbr.log" -error_log="/var/log/rustdesk-hbbr.err" - -depend() { - use net -} -EOF - -cat </etc/init.d/rustdesk-api -#!/sbin/openrc-run -description="RustDesk API Service" -directory="/opt/rustdesk-api" -command="/opt/rustdesk-api/apimain" -command_args="" -command_background="true" -command_user="root" -pidfile="/var/run/rustdesk-api.pid" -output_log="/var/log/rustdesk-api.log" -error_log="/var/log/rustdesk-api.err" - -depend() { - use net -} -EOF -chmod +x /etc/init.d/rustdesk-server-hbbs -chmod +x /etc/init.d/rustdesk-server-hbbr -chmod +x /etc/init.d/rustdesk-api -$STD rc-update add rustdesk-server-hbbs default -$STD rc-update add rustdesk-server-hbbr default -$STD rc-update add rustdesk-api default -msg_ok "Enabled RustDesk Server Services" - -msg_info "Starting RustDesk Server" -$STD service rustdesk-server-hbbs start -$STD service rustdesk-server-hbbr start -$STD service rustdesk-api start -msg_ok "Started RustDesk Server" - -motd_ssh -customize - -msg_info "Cleaning up" -rm -f "$temp_file1" "$temp_file2" -$STD apk cache clean -msg_ok "Cleaned" From 5ff0884a58a27354596f281f138123bf25d8123f Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 26 Aug 2025 08:52:59 +0200 Subject: [PATCH 0733/1733] Update romm.json --- frontend/public/json/romm.json | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/frontend/public/json/romm.json b/frontend/public/json/romm.json index fc8ed35e4..364a7ea2d 100644 --- a/frontend/public/json/romm.json +++ b/frontend/public/json/romm.json @@ -2,8 +2,7 @@ "name": "RomM", "slug": "romm", "categories": [ - 2, - 3 + 21 ], "date_created": "2025-03-10", "type": "ct", @@ -32,9 +31,5 @@ "username": "romm", "password": "changeme" }, - "notes": [ - "API Keys: For full functionality, you'll need API keys from IGDB, MobyGames, and/or SteamGridDB.", - "ROM Directory: Your ROM files should follow the RomM folder structure outlined in the documentation.", - "Authentication: The admin username and password will be set during the first-run setup process." - ] + "notes": [] } From 72eeff5e5ab1ac697e53271ec54cb30caa30dbc4 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 26 Aug 2025 10:11:02 +0200 Subject: [PATCH 0734/1733] Update romm-install.sh --- install/romm-install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install/romm-install.sh b/install/romm-install.sh index c43acb109..7a7336c80 100644 --- a/install/romm-install.sh +++ b/install/romm-install.sh @@ -34,6 +34,7 @@ msg_ok "Installed core dependencies" PYTHON_VERSION="3.12" setup_uv NODE_VERSION="22" NODE_MODULE="serve" setup_nodejs +setup_mariadb msg_info "Configuring Database" DB_NAME=romm From 244a7f8ae2c64ea81989ab51f7c5a21c8c78d0d5 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 26 Aug 2025 10:25:14 +0200 Subject: [PATCH 0735/1733] Update romm-install.sh --- install/romm-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/romm-install.sh b/install/romm-install.sh index 7a7336c80..36c791c46 100644 --- a/install/romm-install.sh +++ b/install/romm-install.sh @@ -40,8 +40,8 @@ msg_info "Configuring Database" DB_NAME=romm DB_USER=romm DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) -$STD mariadb -u root -e "CREATE DATABASE $DB_NAME CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" -$STD mariadb -u root -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';" +$STD mariadb -u root -e "CREATE DATABASE IF NOT EXISTS $DB_NAME CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" +$STD mariadb -u root -e "CREATE USER IF NOT EXISTS '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';" $STD mariadb -u root -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" { echo "RomM-Credentials" From af5c1b858b1245fecf77e56362c75b39ba58127e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 26 Aug 2025 11:15:52 +0200 Subject: [PATCH 0736/1733] debian 13 template --- ct/debian13.sh | 44 +++++++++++++++++++++++++++++++++++++ install/debian13-install.sh | 25 +++++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 ct/debian13.sh create mode 100644 install/debian13-install.sh diff --git a/ct/debian13.sh b/ct/debian13.sh new file mode 100644 index 000000000..4be94c1c8 --- /dev/null +++ b/ct/debian13.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://www.debian.org/ + +APP="Debian13" +var_tags="${var_tags:-os}" +var_cpu="${var_cpu:-4}" +var_ram="${var_ram:-4096}" +var_disk="${var_disk:-15}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" +#var_fuse="${var_fuse:-no}" +#var_tun="${var_tun:-no}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /var ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating $APP LXC" + $STD apt update + $STD apt -y upgrade + msg_ok "Updated $APP LXC" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!" +msg_custom "🚀" "${GN}" "${APP} setup has been successfully initialized!" diff --git a/install/debian13-install.sh b/install/debian13-install.sh new file mode 100644 index 000000000..466d6af6e --- /dev/null +++ b/install/debian13-install.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt install -y gpg +msg_ok "Installed Dependencies" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt -y autoremove +$STD apt -y autoclean +msg_ok "Cleaned" From b0c3978bbfd535daccbade3a620dd112acee6c6a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 26 Aug 2025 11:25:56 +0200 Subject: [PATCH 0737/1733] Update create_lxc.sh --- misc/create_lxc.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index f4d4d07ba..5b10404b1 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -299,7 +299,11 @@ mapfile -t ONLINE_TEMPLATES < <( sed -n "s/.*\($TEMPLATE_SEARCH.*$TEMPLATE_PATTERN.*\)/\1/p" | sort -t - -k 2 -V ) -ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]:-}" +if [ ${#ONLINE_TEMPLATES[@]} -gt 0 ]; then + ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" +else + ONLINE_TEMPLATE="" +fi # 3. Local vs Online if [ ${#LOCAL_TEMPLATES[@]} -gt 0 ]; then From 0b54c653f87f1ea2119f145ba0db100b30699ae6 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 26 Aug 2025 12:32:08 +0200 Subject: [PATCH 0738/1733] Update create_lxc.sh --- misc/create_lxc.sh | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index 5b10404b1..be4b81128 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -331,6 +331,7 @@ fi msg_ok "Template ${BL}$TEMPLATE${CL} [$TEMPLATE_SOURCE]" msg_debug "Resolved TEMPLATE_PATH=$TEMPLATE_PATH" +# 5. Validation # 5. Validation NEED_DOWNLOAD=0 if [[ ! -f "$TEMPLATE_PATH" ]]; then @@ -340,11 +341,19 @@ elif [[ ! -r "$TEMPLATE_PATH" ]]; then msg_error "Template file exists but is not readable – check permissions." exit 221 elif [[ "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then - msg_warn "Template file too small (<1MB) – re-downloading." - NEED_DOWNLOAD=1 -elif ! zstdcat "$TEMPLATE_PATH" | tar -tf - &>/dev/null; then - msg_warn "Template appears corrupted – re-downloading." - NEED_DOWNLOAD=1 + if [[ -n "$ONLINE_TEMPLATE" ]]; then + msg_warn "Template file too small (<1MB) – re-downloading." + NEED_DOWNLOAD=1 + else + msg_warn "Template looks too small, but no online version exists. Keeping local file." + fi +elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then + if [[ -n "$ONLINE_TEMPLATE" ]]; then + msg_warn "Template appears corrupted – re-downloading." + NEED_DOWNLOAD=1 + else + msg_warn "Template appears corrupted, but no online version exists. Keeping local file." + fi else msg_ok "Template $TEMPLATE is present and valid." fi @@ -422,10 +431,14 @@ if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[ msg_warn "Template file too small or missing – re-downloading." rm -f "$TEMPLATE_PATH" pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" - elif ! zstdcat "$TEMPLATE_PATH" | tar -tf - &>/dev/null; then - msg_warn "Template appears corrupted – re-downloading." - rm -f "$TEMPLATE_PATH" - pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" + elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then + if [[ -n "$ONLINE_TEMPLATE" ]]; then + msg_warn "Template appears corrupted – re-downloading." + rm -f "$TEMPLATE_PATH" + pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" + else + msg_warn "Template appears corrupted, but no online version exists. Skipping re-download." + fi fi # Retry after repair From 7eec40d117fd9d6fc07170bdae11571f46e41530 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 26 Aug 2025 12:40:19 +0200 Subject: [PATCH 0739/1733] Update create_lxc.sh --- misc/create_lxc.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index be4b81128..f2aa05de3 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -331,7 +331,6 @@ fi msg_ok "Template ${BL}$TEMPLATE${CL} [$TEMPLATE_SOURCE]" msg_debug "Resolved TEMPLATE_PATH=$TEMPLATE_PATH" -# 5. Validation # 5. Validation NEED_DOWNLOAD=0 if [[ ! -f "$TEMPLATE_PATH" ]]; then From ec1022dd3ab9557b09e763c231a67a71cc440688 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 26 Aug 2025 13:56:31 +0200 Subject: [PATCH 0740/1733] fix --- install/leantime-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/leantime-install.sh b/install/leantime-install.sh index 086157c8c..e1b114bf3 100644 --- a/install/leantime-install.sh +++ b/install/leantime-install.sh @@ -68,9 +68,9 @@ sed -i -e "s|^LEAN_DB_DATABASE.*|LEAN_DB_DATABASE = '$DB_NAME'|" \ "/opt/leantime/config/.env" a2enmod -q proxy_fcgi setenvif rewrite -a2enconf -q "php${PHP_VERSION}-fpm" +a2enconf -q "php8.4-fpm" -sed -i -e "s/^;extension.\(curl\|fileinfo\|gd\|intl\|ldap\|mbstring\|exif\|mysqli\|odbc\|openssl\|pdo_mysql\)/extension=\1/g" "/etc/php/${PHP_VERSION}/apache2/php.ini" +sed -i -e "s/^;extension.\(curl\|fileinfo\|gd\|intl\|ldap\|mbstring\|exif\|mysqli\|odbc\|openssl\|pdo_mysql\)/extension=\1/g" "/etc/php/8.4/apache2/php.ini" systemctl restart apache2 From f9f3517ed404707beeed926dc31485a4f5e713a2 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Tue, 26 Aug 2025 09:54:24 -0400 Subject: [PATCH 0741/1733] Autocaliweb: fix tar --- install/autocaliweb-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/autocaliweb-install.sh b/install/autocaliweb-install.sh index 4c6e64008..09dae8a50 100644 --- a/install/autocaliweb-install.sh +++ b/install/autocaliweb-install.sh @@ -51,7 +51,7 @@ msg_info "Installing Calibre" CALIBRE_RELEASE="$(curl -s https://api.github.com/repos/kovidgoyal/calibre/releases/latest | grep -o '"tag_name": "[^"]*' | cut -d'"' -f4)" CALIBRE_VERSION=${CALIBRE_RELEASE#v} curl -fsSL https://github.com/kovidgoyal/calibre/releases/download/${CALIBRE_RELEASE}/calibre-${CALIBRE_VERSION}-x86_64.txz -o /tmp/calibre.txz -$STD tar -xf /tmp/calibre.txz /opt/calibre +$STD tar -xf /tmp/calibre.txz -C /opt/calibre rm /tmp/calibre.txz $STD /opt/calibre/calibre_postinstall msg_ok "Calibre installed" From 230909ca3ea169d918783bcf80de42b55a65e0be Mon Sep 17 00:00:00 2001 From: vhsdream Date: Tue, 26 Aug 2025 10:06:20 -0400 Subject: [PATCH 0742/1733] Autocaliweb: create calibre dir --- install/autocaliweb-install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install/autocaliweb-install.sh b/install/autocaliweb-install.sh index 09dae8a50..faf315c1f 100644 --- a/install/autocaliweb-install.sh +++ b/install/autocaliweb-install.sh @@ -51,6 +51,7 @@ msg_info "Installing Calibre" CALIBRE_RELEASE="$(curl -s https://api.github.com/repos/kovidgoyal/calibre/releases/latest | grep -o '"tag_name": "[^"]*' | cut -d'"' -f4)" CALIBRE_VERSION=${CALIBRE_RELEASE#v} curl -fsSL https://github.com/kovidgoyal/calibre/releases/download/${CALIBRE_RELEASE}/calibre-${CALIBRE_VERSION}-x86_64.txz -o /tmp/calibre.txz +mkdir -p /opt/calibre $STD tar -xf /tmp/calibre.txz -C /opt/calibre rm /tmp/calibre.txz $STD /opt/calibre/calibre_postinstall From 11b52f7703c9f20baf1753cf70982cb7cdaea187 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Tue, 26 Aug 2025 14:34:02 -0400 Subject: [PATCH 0743/1733] Autocaliweb: add python3-dev and install via pyproject.toml --- install/autocaliweb-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/autocaliweb-install.sh b/install/autocaliweb-install.sh index faf315c1f..2a698b53e 100644 --- a/install/autocaliweb-install.sh +++ b/install/autocaliweb-install.sh @@ -15,7 +15,7 @@ update_os msg_info "Installing dependencies" $STD apt-get install -y --no-install-recommends \ - git \ + python3-dev \ sqlite3 \ build-essential \ libldap2-dev \ @@ -78,7 +78,7 @@ mkdir -p {"$CALIBRE_LIB_DIR","$INGEST_DIR"} cd "$INSTALL_DIR" $STD uv venv "$VIRTUAL_ENV" -$STD uv sync --all-extras --active +$STD uv pip install -r pyproject.toml --all-extras cat <./dirs.json { "ingest_folder": "$INGEST_DIR", From 3325c153cf4f3b060a20a06a449566f8592ac086 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Tue, 26 Aug 2025 14:44:17 -0400 Subject: [PATCH 0744/1733] Autocaliweb: source venv --- install/autocaliweb-install.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/install/autocaliweb-install.sh b/install/autocaliweb-install.sh index 2a698b53e..741bd9ffb 100644 --- a/install/autocaliweb-install.sh +++ b/install/autocaliweb-install.sh @@ -78,7 +78,9 @@ mkdir -p {"$CALIBRE_LIB_DIR","$INGEST_DIR"} cd "$INSTALL_DIR" $STD uv venv "$VIRTUAL_ENV" +$STD source "$VIRTUAL_ENV" $STD uv pip install -r pyproject.toml --all-extras +$STD deactivate cat <./dirs.json { "ingest_folder": "$INGEST_DIR", From c48041711d4ebe9f0d1c11b340eedee9c841a8ed Mon Sep 17 00:00:00 2001 From: vhsdream Date: Tue, 26 Aug 2025 14:49:59 -0400 Subject: [PATCH 0745/1733] fix --- install/autocaliweb-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/autocaliweb-install.sh b/install/autocaliweb-install.sh index 741bd9ffb..0260f7fff 100644 --- a/install/autocaliweb-install.sh +++ b/install/autocaliweb-install.sh @@ -78,7 +78,7 @@ mkdir -p {"$CALIBRE_LIB_DIR","$INGEST_DIR"} cd "$INSTALL_DIR" $STD uv venv "$VIRTUAL_ENV" -$STD source "$VIRTUAL_ENV" +$STD source "$VIRTUAL_ENV"/bin/activate $STD uv pip install -r pyproject.toml --all-extras $STD deactivate cat <./dirs.json From 8fa33480fe0b85f29113931865784b2da5b415a2 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Tue, 26 Aug 2025 15:00:34 -0400 Subject: [PATCH 0746/1733] Autocaliweb: fetch DB files since they aren't in release (why?) --- install/autocaliweb-install.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/install/autocaliweb-install.sh b/install/autocaliweb-install.sh index 0260f7fff..7f860ff38 100644 --- a/install/autocaliweb-install.sh +++ b/install/autocaliweb-install.sh @@ -106,9 +106,8 @@ msg_info "Initializing databases" KEPUBIFY_PATH=$(command -v kepubify 2>/dev/null || echo "/usr/bin/kepubify") EBOOK_CONVERT_PATH=$(command -v ebook-convert 2>/dev/null || echo "/usr/bin/ebook-convert") CALIBRE_BIN_DIR=$(dirname "$EBOOK_CONVERT_PATH") -cp "$INSTALL_DIR"/library/metadata.db "$CALIBRE_LIB_DIR"/metadata.db - -cp "$INSTALL_DIR"/library/app.db "$CONFIG_DIR"/app.db +curl -fsSL https://github.com/gelbphoenix/autocaliweb/raw/refs/heads/master/library/metadata.db -o "$CALIBRE_LIB_DIR"/metadata.db +curl -fsSL https://github.com/gelbphoenix/autocaliweb/raw/refs/heads/master/library/app.db -o "$CONFIG_DIR"/app.db sqlite3 "$CONFIG_DIR/app.db" < Date: Tue, 26 Aug 2025 15:14:05 -0400 Subject: [PATCH 0747/1733] Autocaliweb: export venv; fix issues with vars --- install/autocaliweb-install.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/install/autocaliweb-install.sh b/install/autocaliweb-install.sh index 7f860ff38..a5846d2eb 100644 --- a/install/autocaliweb-install.sh +++ b/install/autocaliweb-install.sh @@ -69,7 +69,7 @@ INGEST_DIR="/opt/acw-book-ingest" SERVICE_USER="acw" SERVICE_GROUP="acw" SCRIPTS_DIR="${INSTALL_DIR}/scripts" -VIRTUAL_ENV="${INSTALL_DIR}/venv" +export VIRTUAL_ENV="${INSTALL_DIR}/venv" mkdir -p "$CONFIG_DIR"/{.config/calibre/plugins,log_archive,.acw_conversion_tmp} mkdir -p "$CONFIG_DIR"/processed_books/{converted,imported,failed,fixed_originals} @@ -126,7 +126,8 @@ msg_info "Creating scripts and service files" cat <"$SCRIPTS_DIR"/ingest_watcher.sh #!/bin/bash -WATCH_FOLDER=\$(grep -o '"ingest_folder": "[^"]*' \${INSTALL_DIR}/dirs.json | grep -o '[^"]*\$') +INSTALL_PATH="$INSTALL_DIR" +WATCH_FOLDER=\$(grep -o '"ingest_folder": "[^"]*' \${INSTALL_PATH}/dirs.json | grep -o '[^"]*\$') echo "[acw-ingest-service] Watching folder: \$WATCH_FOLDER" # Monitor the folder for new files @@ -134,7 +135,7 @@ echo "[acw-ingest-service] Watching folder: \$WATCH_FOLDER" while read -r events filepath ; do echo "[acw-ingest-service] New files detected - \$filepath - Starting Ingest Processor..." # Use the Python interpreter from the virtual environment - \${VIRTUAL_ENV}/bin/python \${SCRIPTS_DIR}/ingest_processor.py "\$filepath" + ${VIRTUAL_ENV}/bin/python \${INSTALL_PATH}/ingest_processor.py "\$filepath" done EOF From f4b00c42fd79dce13d11da655f159dcc58140401 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Tue, 26 Aug 2025 15:20:01 -0400 Subject: [PATCH 0748/1733] Autocaliweb: revert --- install/autocaliweb-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/autocaliweb-install.sh b/install/autocaliweb-install.sh index a5846d2eb..1ece6b6b6 100644 --- a/install/autocaliweb-install.sh +++ b/install/autocaliweb-install.sh @@ -69,7 +69,7 @@ INGEST_DIR="/opt/acw-book-ingest" SERVICE_USER="acw" SERVICE_GROUP="acw" SCRIPTS_DIR="${INSTALL_DIR}/scripts" -export VIRTUAL_ENV="${INSTALL_DIR}/venv" +VIRTUAL_ENV="${INSTALL_DIR}/venv" mkdir -p "$CONFIG_DIR"/{.config/calibre/plugins,log_archive,.acw_conversion_tmp} mkdir -p "$CONFIG_DIR"/processed_books/{converted,imported,failed,fixed_originals} @@ -135,7 +135,7 @@ echo "[acw-ingest-service] Watching folder: \$WATCH_FOLDER" while read -r events filepath ; do echo "[acw-ingest-service] New files detected - \$filepath - Starting Ingest Processor..." # Use the Python interpreter from the virtual environment - ${VIRTUAL_ENV}/bin/python \${INSTALL_PATH}/ingest_processor.py "\$filepath" + \${INSTALL_PATH}/venv/bin/python \${INSTALL_PATH}/scripts/ingest_processor.py "\$filepath" done EOF From 98b4abb2d6eb401469f144c84200b123010cace0 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Tue, 26 Aug 2025 15:31:07 -0400 Subject: [PATCH 0749/1733] Autocaliweb: venv var gets unset after running deactivate --- install/autocaliweb-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/autocaliweb-install.sh b/install/autocaliweb-install.sh index 1ece6b6b6..7b51f7709 100644 --- a/install/autocaliweb-install.sh +++ b/install/autocaliweb-install.sh @@ -144,7 +144,7 @@ cat <"$SCRIPTS_DIR"/auto_zipper_wrapper.sh #!/bin/bash # Source virtual environment -source ${VIRTUAL_ENV}/bin/activate +source ${INSTALL_DIR}/venv/bin/activate WAKEUP="23:59" @@ -180,7 +180,7 @@ cat <"$SCRIPTS_DIR"/metadata_change_detector_wrapper.sh # metadata_change_detector_wrapper.sh - Wrapper for periodic metadata enforcement # Source virtual environment -source ${VIRTUAL_ENV}/bin/activate +source ${INSTALL_DIR}/venv/bin/activate # Configuration CHECK_INTERVAL=300 # Check every 5 minutes (300 seconds) From 8bd7d7cf7aae30d5b168b5aee54dd259cfa2a22f Mon Sep 17 00:00:00 2001 From: vhsdream Date: Tue, 26 Aug 2025 15:36:39 -0400 Subject: [PATCH 0750/1733] Autocaliweb: missing $ --- install/autocaliweb-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/autocaliweb-install.sh b/install/autocaliweb-install.sh index 7b51f7709..e09c06ca2 100644 --- a/install/autocaliweb-install.sh +++ b/install/autocaliweb-install.sh @@ -88,7 +88,7 @@ cat <./dirs.json "tmp_conversion_dir": "$CONFIG_DIR/.acw_conversion_tmp" } EOF -useradd -s /usr/sbin/nologin -d "$CONFIG_DIR" -M "SERVICE_USER" +useradd -s /usr/sbin/nologin -d "$CONFIG_DIR" -M "$SERVICE_USER" ln -sf "$CONFIG_DIR"/.config/calibre/plugins "$CONFIG_DIR"/calibre_plugins msg_ok "Configured Autocaliweb" From 04214bf968bdf9249b3ec0ba5f4c2c71fc239859 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Tue, 26 Aug 2025 18:01:05 -0400 Subject: [PATCH 0751/1733] Autocaliweb: add env, create version constraint for pyopenssl, use lock file for deps --- install/autocaliweb-install.sh | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/install/autocaliweb-install.sh b/install/autocaliweb-install.sh index e09c06ca2..50b4a5b4b 100644 --- a/install/autocaliweb-install.sh +++ b/install/autocaliweb-install.sh @@ -79,7 +79,9 @@ mkdir -p {"$CALIBRE_LIB_DIR","$INGEST_DIR"} cd "$INSTALL_DIR" $STD uv venv "$VIRTUAL_ENV" $STD source "$VIRTUAL_ENV"/bin/activate -$STD uv pip install -r pyproject.toml --all-extras +echo "pyopenssl>=24.2.1" >./constraint.txt +$STD uv pip compile requirements.txt optional-requirements.txt -c constraint.txt -o combined-requirements.lock +$STD uv pip sync combined-requirements.lock $STD deactivate cat <./dirs.json { @@ -90,6 +92,13 @@ cat <./dirs.json EOF useradd -s /usr/sbin/nologin -d "$CONFIG_DIR" -M "$SERVICE_USER" ln -sf "$CONFIG_DIR"/.config/calibre/plugins "$CONFIG_DIR"/calibre_plugins +cat <"$INSTALL_DIR"/.env +ACW_INSTALL_DIR=$INSTALL_DIR +ACW_CONFIG_DIR=$CONFIG_DIR +ACW_USER=$SERVICE_USER +ACW_GROUP=$SERVICE_GROUP +LIBRARY_DIR=$CALIBRE_LIB_DIR +EOF msg_ok "Configured Autocaliweb" msg_info "Creating ACWSync Plugin for KOReader" @@ -240,6 +249,7 @@ Environment=PYTHONPATH=$SCRIPTS_DIR:$INSTALL_DIR Environment=PYTHONDONTWRITEBYTECODE=1 Environment=PYTHONUNBUFFERED=1 Environment=CALIBRE_DBPATH=$CONFIG_DIR +EnvironmentFile=$INSTALL_DIR/.env ExecStart=$INSTALL_DIR/venv/bin/python $INSTALL_DIR/cps.py -p $CONFIG_DIR/app.db Restart=always @@ -251,7 +261,7 @@ StandardError=journal WantedBy=multi-user.target EOF -cat <"$SYS_PATH"/acw-ingestor.service +cat <"$SYS_PATH"/acw-ingest-service.service [Unit] Description=Autocaliweb Ingest Processor Service After=autocaliweb.service From 9710331bc1a84d5865d9be8578af16ce58259ab1 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Tue, 26 Aug 2025 18:06:32 -0400 Subject: [PATCH 0752/1733] Autocaliweb: quiesce zip output; fix service file enable --- install/autocaliweb-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/autocaliweb-install.sh b/install/autocaliweb-install.sh index 50b4a5b4b..e282f782e 100644 --- a/install/autocaliweb-install.sh +++ b/install/autocaliweb-install.sh @@ -107,7 +107,7 @@ PLUGIN_DIGEST="$(find acwsync.koplugin -type f -name "*.lua" -o -name "*.json" | echo "Plugin files digest: $PLUGIN_DIGEST" >acwsync.koplugin/${PLUGIN_DIGEST}.digest echo "Build date: $(date)" >>acwsync.koplugin/${PLUGIN_DIGEST}.digest echo "Files included:" >>acwsync.koplugin/${PLUGIN_DIGEST}.digest -zip -r koplugin.zip acwsync.koplugin/ +$STD zip -r koplugin.zip acwsync.koplugin/ cp -r koplugin.zip "$INSTALL_DIR"/cps/static msg_ok "Created ACWSync Plugin" @@ -320,7 +320,7 @@ Environment=HOME=${CONFIG_DIR} WantedBy=multi-user.target EOF -systemctl -q enable --now autocaliweb acw-ingestor acw-auto-zipper metadata-change-detector +systemctl -q enable --now autocaliweb acw-ingest-service acw-auto-zipper metadata-change-detector msg_ok "Created scripts and service files" motd_ssh From 3a583a906d17e17dc10463de0c7ea72a6a19c474 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 27 Aug 2025 10:10:08 +0200 Subject: [PATCH 0753/1733] garage --- install/alpine-garage-install.sh | 98 ++++++++++++++++++++++++++++++++ install/leantime-install.sh | 18 +++--- 2 files changed, 105 insertions(+), 11 deletions(-) create mode 100644 install/alpine-garage-install.sh diff --git a/install/alpine-garage-install.sh b/install/alpine-garage-install.sh new file mode 100644 index 000000000..126401d9a --- /dev/null +++ b/install/alpine-garage-install.sh @@ -0,0 +1,98 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://garagehq.deuxfleurs.fr/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Preparing directories" +mkdir -p /var/lib/garage/meta /var/lib/garage/data /var/lib/garage/snapshots +msg_ok "Prepared directories" + +msg_info "Setup Garage packages" +$STD apk add --no-cache garage garage-openrc openssl +msg_ok "Setup Garage packages" + +# msg_info "Generating RPC secret" +# if [[ ! -s /etc/garage.rpc_secret ]]; then +# openssl rand -hex 32 | tr -d '\n' >/etc/garage.rpc_secret +# chmod 600 /etc/garage.rpc_secret +# fi +# msg_ok "Generated RPC secret" + +# msg_info "Generating tokens" +# if [[ ! -s /etc/garage.tokens.env ]]; then +# ADMIN_TOKEN="$(openssl rand -base64 32)" +# METRICS_TOKEN="$(openssl rand -base64 32)" +# cat >/etc/garage.tokens.env </etc/garage.toml <>~/"$APPLICATION".creds +} >>~/leantime.creds msg_ok "Set up Database" fetch_and_deploy_gh_release "leantime" "Leantime/leantime" "prebuild" "latest" "/opt/leantime" Leantime*.tar.gz -msg_info "Setup ${APPLICATION}" -APACHE_LOG_DIR=/var/log/apache2 +msg_info "Setup Leantime" chown -R www-data:www-data "/opt/leantime" chmod -R 750 "/opt/leantime" @@ -55,8 +54,8 @@ cat </etc/apache2/sites-enabled/000-default.conf Require all granted - ErrorLog ${APACHE_LOG_DIR}/error.log - CustomLog ${APACHE_LOG_DIR}/access.log combined + ErrorLog /var/log/apache2/error.log + CustomLog /var/log/apache2/access.log combined
EOF @@ -67,13 +66,10 @@ sed -i -e "s|^LEAN_DB_DATABASE.*|LEAN_DB_DATABASE = '$DB_NAME'|" \ -e "s|^LEAN_SESSION_PASSWORD.*|LEAN_SESSION_PASSWORD = '$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13)'|" \ "/opt/leantime/config/.env" -a2enmod -q proxy_fcgi setenvif rewrite -a2enconf -q "php8.4-fpm" - +$STD a2enmod -q proxy_fcgi setenvif rewrite +$STD a2enconf -q "php8.4-fpm" sed -i -e "s/^;extension.\(curl\|fileinfo\|gd\|intl\|ldap\|mbstring\|exif\|mysqli\|odbc\|openssl\|pdo_mysql\)/extension=\1/g" "/etc/php/8.4/apache2/php.ini" - systemctl restart apache2 - msg_ok "Setup ${APPLICATION}" motd_ssh From d0424dccb90439158d19085e019c1f4a9c1b022e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 27 Aug 2025 10:26:39 +0200 Subject: [PATCH 0754/1733] Update haos-vm.sh --- vm/haos-vm.sh | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/vm/haos-vm.sh b/vm/haos-vm.sh index 8e9e61cc9..8bcb926ca 100644 --- a/vm/haos-vm.sh +++ b/vm/haos-vm.sh @@ -184,8 +184,8 @@ function exit-script() { function default_settings() { BRANCH="$stable" VMID=$(get_valid_nextid) - FORMAT=",efitype=4m" - MACHINE="" + FORMAT=",efitype=4m,pre-enrolled-keys=0" + MACHINE=" -machine q35" DISK_CACHE="cache=writethrough," HN="haos$stable" CPU_TYPE=" -cpu host" @@ -198,11 +198,11 @@ function default_settings() { START_VM="yes" METHOD="default" echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}q35${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" - echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" @@ -246,16 +246,16 @@ function advanced_settings() { done if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \ - "i440fx" "Machine i440fx" ON \ - "q35" "Machine q35" OFF \ + "i440fx" "Machine i440fx" OFF \ + "q35" "Machine q35" ON \ 3>&1 1>&2 2>&3); then if [ $MACH = q35 ]; then echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" - FORMAT="" + FORMAT=",efitype=4m,pre-enrolled-keys=0" MACHINE=" -machine q35" else echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" - FORMAT=",efitype=4m" + FORMAT=",efitype=4m,pre-enrolled-keys=0" MACHINE="" fi else @@ -486,7 +486,7 @@ btrfs | local-zfs) DISK_EXT=".raw" DISK_REF="$VMID/" DISK_IMPORT="-format raw" - FORMAT=",efitype=4m" + FORMAT=",efitype=4m,pre-enrolled-keys=0" THIN="" ;; esac @@ -507,6 +507,8 @@ qm set $VMID \ -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=32G \ -boot order=scsi0 >/dev/null +qm set $VMID -serial0 socket >/dev/null + DESCRIPTION=$( cat < From fa7cc26bf1a19aff90650ba6416172301126d165 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 27 Aug 2025 10:28:13 +0200 Subject: [PATCH 0755/1733] Update haos-vm.sh --- vm/haos-vm.sh | 9 --------- 1 file changed, 9 deletions(-) diff --git a/vm/haos-vm.sh b/vm/haos-vm.sh index 8bcb926ca..b83f1b30a 100644 --- a/vm/haos-vm.sh +++ b/vm/haos-vm.sh @@ -142,15 +142,6 @@ function check_root() { fi } -function pve_check() { - if ! pveversion | grep -Eq "pve-manager/8\.[1-4](\.[0-9]+)*"; then - msg_error "${CROSS}${RD}This version of Proxmox Virtual Environment is not supported" - echo -e "Requires Proxmox Virtual Environment Version 8.1 or later." - echo -e "Exiting..." - sleep 2 - exit - fi -} function arch_check() { if [ "$(dpkg --print-architecture)" != "amd64" ]; then From 95e1b1b44f891209710d5c780dd0ac75bf9e0f41 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 27 Aug 2025 10:29:21 +0200 Subject: [PATCH 0756/1733] Update haos-vm.sh --- vm/haos-vm.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/vm/haos-vm.sh b/vm/haos-vm.sh index b83f1b30a..bfdef0b60 100644 --- a/vm/haos-vm.sh +++ b/vm/haos-vm.sh @@ -142,7 +142,6 @@ function check_root() { fi } - function arch_check() { if [ "$(dpkg --print-architecture)" != "amd64" ]; then echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" @@ -412,7 +411,6 @@ function start_script() { check_root arch_check -pve_check ssh_check start_script From 089634caf401245775dc88843f4d787d1cf8d03a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 27 Aug 2025 11:38:18 +0200 Subject: [PATCH 0757/1733] Update haos-vm.sh --- vm/haos-vm.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/vm/haos-vm.sh b/vm/haos-vm.sh index bfdef0b60..ea7ff30d0 100644 --- a/vm/haos-vm.sh +++ b/vm/haos-vm.sh @@ -416,6 +416,9 @@ start_script post_to_api_vm +qm set socket 105 -serial0 +qm set serial0 -vga + msg_info "Validating Storage" while read -r line; do TAG=$(echo $line | awk '{print $1}') From cd7240c7e2101cbdaf97127e63fb5721459ec631 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Wed, 27 Aug 2025 08:00:06 -0400 Subject: [PATCH 0758/1733] Autocaliweb: add version tracking in the UI --- install/autocaliweb-install.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/install/autocaliweb-install.sh b/install/autocaliweb-install.sh index e282f782e..e9d04159e 100644 --- a/install/autocaliweb-install.sh +++ b/install/autocaliweb-install.sh @@ -46,6 +46,7 @@ $STD apt-get install -y --no-install-recommends \ msg_ok "Installed dependencies" fetch_and_deploy_gh_release "kepubify" "pgaskin/kepubify" "singlefile" "latest" "/usr/bin" "kepubify-linux-64bit" +KEPUB_VERSION="$(/usr/bin/kepubify --version)" msg_info "Installing Calibre" CALIBRE_RELEASE="$(curl -s https://api.github.com/repos/kovidgoyal/calibre/releases/latest | grep -o '"tag_name": "[^"]*' | cut -d'"' -f4)" @@ -75,6 +76,9 @@ mkdir -p "$CONFIG_DIR"/{.config/calibre/plugins,log_archive,.acw_conversion_tmp} mkdir -p "$CONFIG_DIR"/processed_books/{converted,imported,failed,fixed_originals} mkdir -p "$INSTALL_DIR"/{metadata_change_logs,metadata_temp} mkdir -p {"$CALIBRE_LIB_DIR","$INGEST_DIR"} +echo "$CALIBRE_VERSION" >"$INSTALL_DIR"/CALIBRE_RELEASE +echo "${KEPUB_VERSION#v}" >"$INSTALL_DIR"/KEPUBIFY_RELEASE +sed 's/^/v/' ~/.autocaliweb >"$INSTALL_DIR"/ACW_RELEASE cd "$INSTALL_DIR" $STD uv venv "$VIRTUAL_ENV" From b64dd8184e841a27eaa0ba311d7959711ca0cc03 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Wed, 27 Aug 2025 08:35:12 -0400 Subject: [PATCH 0759/1733] Autocaliweb: configure update function --- ct/autocaliweb.sh | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/ct/autocaliweb.sh b/ct/autocaliweb.sh index cc728dd6e..689865126 100644 --- a/ct/autocaliweb.sh +++ b/ct/autocaliweb.sh @@ -30,10 +30,10 @@ function update_script() { setup_uv - RELEASE=$(curl -fsSL https://api.github.com/repos/gelbphoenix/autocaliweb/releases/latest | jq '.tag_name' | sed 's/^v//') + RELEASE=$(curl -fsSL https://api.github.com/repos/gelbphoenix/autocaliweb/releases/latest | jq '.tag_name' | sed 's/^"v//;s/"$//') if [[ "${RELEASE}" != "$(cat ~/.autocaliweb 2>/dev/null)" ]] || [[ ! -f ~/.autocaliweb ]]; then msg_info "Stopping Services" - systemctl stop autocaliweb metadata-change-detector acw-ingestor acw-auto-zipper + systemctl stop autocaliweb metadata-change-detector acw-ingest-service acw-auto-zipper msg_ok "Stopped Services" INSTALL_DIR="/opt/autocaliweb" @@ -42,21 +42,30 @@ function update_script() { fetch_and_deploy_gh_release "autocaliweb" "gelbphoenix/autocaliweb" "tarball" "latest" "/opt/autocaliweb" msg_info "Updating ${APP}" cd "$INSTALL_DIR" - $STD uv sync --all-extras --active + $STD source "$VIRTUAL_ENV"/bin/activate + echo "pyopenssl>=24.2.1" >./constraint.txt + $STD uv pip compile requirements.txt optional-requirements.txt -c constraint.txt -o combined-requirements.lock + $STD uv pip sync combined-requirements.lock + $STD deactivate cd "$INSTALL_DIR"/koreader/plugins PLUGIN_DIGEST="$(find acwsync.koplugin -type f -name "*.lua" -o -name "*.json" | sort | xargs sha256sum | sha256sum | cut -d' ' -f1)" echo "Plugin files digest: $PLUGIN_DIGEST" >acwsync.koplugin/${PLUGIN_DIGEST}.digest echo "Build date: $(date)" >>acwsync.koplugin/${PLUGIN_DIGEST}.digest echo "Files included:" >>acwsync.koplugin/${PLUGIN_DIGEST}.digest - zip -r koplugin.zip acwsync.koplugin/ + $STD zip -r koplugin.zip acwsync.koplugin/ cp -r koplugin.zip "$INSTALL_DIR"/cps/static mkdir -p "$INSTALL_DIR"/metadata_temp $STD tar -xf ~/autocaliweb_bkp.tar --directory / + KEPUB_VERSION="$(/usr/bin/kepubify --version)" + CALIBRE_RELEASE="$(curl -s https://api.github.com/repos/kovidgoyal/calibre/releases/latest | grep -o '"tag_name": "[^"]*' | cut -d'"' -f4)" + echo "${KEPUB_VERSION#v}" >"$INSTALL_DIR"/KEPUBIFY_RELEASE + echo "${CALIBRE_RELEASE#v}" >/"$INSTALL_DIR"/CALIBRE_RELEASE + sed 's/^/v/' ~/.autocaliweb >"$INSTALL_DIR"/ACW_RELEASE chown -R acw:acw "$INSTALL_DIR" msg_ok "Updated $APP" msg_info "Starting Services" - systemctl start autocaliweb metadata-change-detector acw-ingestor acw-auto-zipper + systemctl start autocaliweb metadata-change-detector acw-ingest-service acw-auto-zipper msg_ok "Started Services" msg_ok "Updated Successfully" From ac361754cda58f25cd621de483b39ef7bd636524 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 27 Aug 2025 12:35:28 +0000 Subject: [PATCH 0760/1733] Update .app files --- ct/headers/alpine-rustdeskserver | 6 ------ ct/headers/debian13 | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) delete mode 100644 ct/headers/alpine-rustdeskserver create mode 100644 ct/headers/debian13 diff --git a/ct/headers/alpine-rustdeskserver b/ct/headers/alpine-rustdeskserver deleted file mode 100644 index 1c99e61fc..000000000 --- a/ct/headers/alpine-rustdeskserver +++ /dev/null @@ -1,6 +0,0 @@ - ___ __ _ ____ __ ____ __ _____ - / | / /___ (_)___ ___ / __ \__ _______/ /_/ __ \___ _____/ /__/ ___/___ ______ _____ _____ - / /| | / / __ \/ / __ \/ _ \______/ /_/ / / / / ___/ __/ / / / _ \/ ___/ //_/\__ \/ _ \/ ___/ | / / _ \/ ___/ - / ___ |/ / /_/ / / / / / __/_____/ _, _/ /_/ (__ ) /_/ /_/ / __(__ ) ,< ___/ / __/ / | |/ / __/ / -/_/ |_/_/ .___/_/_/ /_/\___/ /_/ |_|\__,_/____/\__/_____/\___/____/_/|_|/____/\___/_/ |___/\___/_/ - /_/ diff --git a/ct/headers/debian13 b/ct/headers/debian13 new file mode 100644 index 000000000..467acace8 --- /dev/null +++ b/ct/headers/debian13 @@ -0,0 +1,6 @@ + ____ __ _ ________ + / __ \___ / /_ (_)___ _____ < /__ / + / / / / _ \/ __ \/ / __ `/ __ \/ / /_ < + / /_/ / __/ /_/ / / /_/ / / / / /___/ / +/_____/\___/_.___/_/\__,_/_/ /_/_//____/ + From c97be769aae8b9c6bf740bcd456ab578184b586e Mon Sep 17 00:00:00 2001 From: vhsdream Date: Wed, 27 Aug 2025 09:10:17 -0400 Subject: [PATCH 0761/1733] Autocaliweb: add env file to temp backup --- ct/autocaliweb.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/autocaliweb.sh b/ct/autocaliweb.sh index 689865126..ceeccd3fd 100644 --- a/ct/autocaliweb.sh +++ b/ct/autocaliweb.sh @@ -38,7 +38,7 @@ function update_script() { INSTALL_DIR="/opt/autocaliweb" VIRTUAL_ENV="${INSTALL_DIR}/venv" - $STD tar -cf ~/autocaliweb_bkp.tar "$INSTALL_DIR"/{metadata_change_logs,dirs.json,scripts/ingest_watcher.sh,scripts/auto_zipper_wrapper.sh,scripts/metadata_change_detector_wrapper.sh} + $STD tar -cf ~/autocaliweb_bkp.tar "$INSTALL_DIR"/{metadata_change_logs,dirs.json,.env,scripts/ingest_watcher.sh,scripts/auto_zipper_wrapper.sh,scripts/metadata_change_detector_wrapper.sh} fetch_and_deploy_gh_release "autocaliweb" "gelbphoenix/autocaliweb" "tarball" "latest" "/opt/autocaliweb" msg_info "Updating ${APP}" cd "$INSTALL_DIR" From 73d2bab42a3b9fc24db1b815264314c1f9c5dabc Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 27 Aug 2025 16:02:20 +0200 Subject: [PATCH 0762/1733] Update mediamanager-install.sh --- install/mediamanager-install.sh | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/install/mediamanager-install.sh b/install/mediamanager-install.sh index ee68d8850..3aa0df7be 100644 --- a/install/mediamanager-install.sh +++ b/install/mediamanager-install.sh @@ -11,18 +11,14 @@ verb_ip6 catch_errors setting_up_container network_check +update_os read -r -p "${TAB3}Enter the email address of your first admin user: " admin_email if [[ "$admin_email" ]]; then EMAIL="$admin_email" fi -update_os - -msg_info "Installing dependencies" -$STD apt-get install -y yq -msg_ok "Installed dependencies" - +setup_yq NODE_VERSION="24" setup_nodejs setup_uv PG_VERSION="17" setup_postgresql @@ -43,6 +39,7 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8' msg_ok "Set up PostgreSQL" fetch_and_deploy_gh_release "MediaManager" "maxdorninger/MediaManager" "tarball" "latest" "/opt/mediamanager" + msg_info "Configuring MediaManager" MM_DIR="/opt/mm" MEDIA_DIR="${MM_DIR}/media" From 191d9655038d4b1aab9627b727773183877e5302 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Wed, 27 Aug 2025 14:01:16 -0400 Subject: [PATCH 0763/1733] Autocaliweb: use uv lock - Broken until ACW is updated (issue with pyopenssl/cryptography) --- ct/autocaliweb.sh | 9 ++++----- install/autocaliweb-install.sh | 6 +----- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/ct/autocaliweb.sh b/ct/autocaliweb.sh index ceeccd3fd..e7f33ba0c 100644 --- a/ct/autocaliweb.sh +++ b/ct/autocaliweb.sh @@ -42,11 +42,10 @@ function update_script() { fetch_and_deploy_gh_release "autocaliweb" "gelbphoenix/autocaliweb" "tarball" "latest" "/opt/autocaliweb" msg_info "Updating ${APP}" cd "$INSTALL_DIR" - $STD source "$VIRTUAL_ENV"/bin/activate - echo "pyopenssl>=24.2.1" >./constraint.txt - $STD uv pip compile requirements.txt optional-requirements.txt -c constraint.txt -o combined-requirements.lock - $STD uv pip sync combined-requirements.lock - $STD deactivate + if [[ ! -d "$VIRTUAL_ENV" ]]; then + $STD uv venv "$VIRTUAL_ENV" + fi + $STD uv sync --all-extras --active cd "$INSTALL_DIR"/koreader/plugins PLUGIN_DIGEST="$(find acwsync.koplugin -type f -name "*.lua" -o -name "*.json" | sort | xargs sha256sum | sha256sum | cut -d' ' -f1)" echo "Plugin files digest: $PLUGIN_DIGEST" >acwsync.koplugin/${PLUGIN_DIGEST}.digest diff --git a/install/autocaliweb-install.sh b/install/autocaliweb-install.sh index e9d04159e..9a97b37fe 100644 --- a/install/autocaliweb-install.sh +++ b/install/autocaliweb-install.sh @@ -82,11 +82,7 @@ sed 's/^/v/' ~/.autocaliweb >"$INSTALL_DIR"/ACW_RELEASE cd "$INSTALL_DIR" $STD uv venv "$VIRTUAL_ENV" -$STD source "$VIRTUAL_ENV"/bin/activate -echo "pyopenssl>=24.2.1" >./constraint.txt -$STD uv pip compile requirements.txt optional-requirements.txt -c constraint.txt -o combined-requirements.lock -$STD uv pip sync combined-requirements.lock -$STD deactivate +$STD uv sync --all-extras --active cat <./dirs.json { "ingest_folder": "$INGEST_DIR", From 28a2c12deeb0a629dfcfaf624cdb42f16de0d346 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 28 Aug 2025 09:43:39 +0200 Subject: [PATCH 0764/1733] test --- ct/flaresolverr.sh | 56 +++++++++++++++++++++++++ install/flaresolverr-install.sh | 72 +++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 ct/flaresolverr.sh create mode 100644 install/flaresolverr-install.sh diff --git a/ct/flaresolverr.sh b/ct/flaresolverr.sh new file mode 100644 index 000000000..95703f862 --- /dev/null +++ b/ct/flaresolverr.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) | Co-Author: remz1337 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/FlareSolverr/FlareSolverr + +APP="FlareSolverr" +var_tags="${var_tags:-proxy}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -f /etc/systemd/system/flaresolverr.service ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + RELEASE=$(curl -fsSL https://api.github.com/repos/FlareSolverr/FlareSolverr/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4)}') + if [[ "${RELEASE}" != "$(cat ~/.flaresolverr 2>/dev/null)" ]] || [[ ! -f ~/.flaresolverr ]]; then + msg_info "Stopping service" + systemctl stop flaresolverr + msg_ok "Stopped service" + + rm -rf /opt/flaresolverr + fetch_and_deploy_gh_release "flaresolverr" "FlareSolverr/FlareSolverr" "prebuild" "latest" "/opt/flaresolverr" "flaresolverr_linux_x64.tar.gz" + + msg_info "Starting service" + systemctl start flaresolverr + msg_ok "Started service" + else + msg_ok "No update required. ${APP} is already at ${RELEASE}" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8191${CL}" diff --git a/install/flaresolverr-install.sh b/install/flaresolverr-install.sh new file mode 100644 index 000000000..628d3205c --- /dev/null +++ b/install/flaresolverr-install.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# Co-Author: remz1337 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/FlareSolverr/FlareSolverr + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt-get install -y \ + apt-transport-https \ + xvfb +msg_ok "Installed Dependencies" + +msg_info "Installing Chrome" +curl -fsSL "https://dl.google.com/linux/linux_signing_key.pub" | gpg --dearmor -o /usr/share/keyrings/google-chrome.gpg +echo "deb [arch=amd64 signed-by=/usr/share/keyrings/google-chrome.gpg] http://dl.google.com/linux/chrome/deb/ stable main" >/etc/apt/sources.list.d/google-chrome.list +$STD apt update +$STD apt install -y google-chrome-stable +msg_ok "Installed Chrome" + +PYTHON_VERSION="3.13"setup_uv + +msg_info "prepare uv python 3.13" +UV_PY="$(uv python find 3.13)" +cat <<'EOF' >/usr/local/bin/python3 +#!/bin/bash +exec "$UV_PY/bin/python3.13" "$@" +EOF +chmod +x /usr/local/bin/python3 +ln -sf "$UV_PY/bin/python3.13" /usr/local/bin/python3.13 +msg_ok "prepared python 3.13" + + +fetch_and_deploy_gh_release "flaresolverr" "FlareSolverr/FlareSolverr" "prebuild" "latest" "/opt/flaresolverr" "flaresolverr_linux_x64.tar.gz" + +msg_info "Creating Service" +cat </etc/systemd/system/flaresolverr.service +[Unit] +Description=FlareSolverr +After=network.target +[Service] +SyslogIdentifier=flaresolverr +Restart=always +RestartSec=5 +Type=simple +Environment="LOG_LEVEL=info" +Environment="CAPTCHA_SOLVER=none" +WorkingDirectory=/opt/flaresolverr +ExecStart=/opt/flaresolverr/flaresolverr +TimeoutStopSec=30 +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now flaresolverr +msg_ok "Created Service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 69d66814815f0562eb8418df651ab6427860c63b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 28 Aug 2025 09:56:08 +0200 Subject: [PATCH 0765/1733] testing --- ct/flaresolverr.sh | 12 ++++++++++++ install/flaresolverr-install.sh | 1 - 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/ct/flaresolverr.sh b/ct/flaresolverr.sh index 95703f862..ea82e5fa4 100644 --- a/ct/flaresolverr.sh +++ b/ct/flaresolverr.sh @@ -34,6 +34,18 @@ function update_script() { systemctl stop flaresolverr msg_ok "Stopped service" + PYTHON_VERSION="3.13"setup_uv + + msg_info "prepare uv python 3.13" + UV_PY="$(uv python find 3.13)" + cat <<'EOF' >/usr/local/bin/python3 +#!/bin/bash +exec "$UV_PY/bin/python3.13" "$@" +EOF + chmod +x /usr/local/bin/python3 + ln -sf "$UV_PY/bin/python3.13" /usr/local/bin/python3.13 + msg_ok "prepared python 3.13" + rm -rf /opt/flaresolverr fetch_and_deploy_gh_release "flaresolverr" "FlareSolverr/FlareSolverr" "prebuild" "latest" "/opt/flaresolverr" "flaresolverr_linux_x64.tar.gz" diff --git a/install/flaresolverr-install.sh b/install/flaresolverr-install.sh index 628d3205c..b7ddbbcab 100644 --- a/install/flaresolverr-install.sh +++ b/install/flaresolverr-install.sh @@ -39,7 +39,6 @@ chmod +x /usr/local/bin/python3 ln -sf "$UV_PY/bin/python3.13" /usr/local/bin/python3.13 msg_ok "prepared python 3.13" - fetch_and_deploy_gh_release "flaresolverr" "FlareSolverr/FlareSolverr" "prebuild" "latest" "/opt/flaresolverr" "flaresolverr_linux_x64.tar.gz" msg_info "Creating Service" From 3bab2781e2520ed09e10b4d80f76e8435b7fd5c9 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 28 Aug 2025 10:02:13 +0200 Subject: [PATCH 0766/1733] fixes --- ct/flaresolverr.sh | 2 +- install/flaresolverr-install.sh | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ct/flaresolverr.sh b/ct/flaresolverr.sh index ea82e5fa4..e1cac141d 100644 --- a/ct/flaresolverr.sh +++ b/ct/flaresolverr.sh @@ -34,7 +34,7 @@ function update_script() { systemctl stop flaresolverr msg_ok "Stopped service" - PYTHON_VERSION="3.13"setup_uv + PYTHON_VERSION="3.13" setup_uv msg_info "prepare uv python 3.13" UV_PY="$(uv python find 3.13)" diff --git a/install/flaresolverr-install.sh b/install/flaresolverr-install.sh index b7ddbbcab..43dfe477e 100644 --- a/install/flaresolverr-install.sh +++ b/install/flaresolverr-install.sh @@ -20,14 +20,7 @@ $STD apt-get install -y \ xvfb msg_ok "Installed Dependencies" -msg_info "Installing Chrome" -curl -fsSL "https://dl.google.com/linux/linux_signing_key.pub" | gpg --dearmor -o /usr/share/keyrings/google-chrome.gpg -echo "deb [arch=amd64 signed-by=/usr/share/keyrings/google-chrome.gpg] http://dl.google.com/linux/chrome/deb/ stable main" >/etc/apt/sources.list.d/google-chrome.list -$STD apt update -$STD apt install -y google-chrome-stable -msg_ok "Installed Chrome" - -PYTHON_VERSION="3.13"setup_uv +PYTHON_VERSION="3.13" setup_uv msg_info "prepare uv python 3.13" UV_PY="$(uv python find 3.13)" @@ -39,6 +32,13 @@ chmod +x /usr/local/bin/python3 ln -sf "$UV_PY/bin/python3.13" /usr/local/bin/python3.13 msg_ok "prepared python 3.13" +msg_info "Installing Chrome" +curl -fsSL "https://dl.google.com/linux/linux_signing_key.pub" | gpg --dearmor -o /usr/share/keyrings/google-chrome.gpg +echo "deb [arch=amd64 signed-by=/usr/share/keyrings/google-chrome.gpg] http://dl.google.com/linux/chrome/deb/ stable main" >/etc/apt/sources.list.d/google-chrome.list +$STD apt update +$STD apt install -y google-chrome-stable +msg_ok "Installed Chrome" + fetch_and_deploy_gh_release "flaresolverr" "FlareSolverr/FlareSolverr" "prebuild" "latest" "/opt/flaresolverr" "flaresolverr_linux_x64.tar.gz" msg_info "Creating Service" From 431f2bd74168227ffd21c57b80728b2344c3876b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 28 Aug 2025 13:51:26 +0200 Subject: [PATCH 0767/1733] ddd --- ct/flaresolverr.sh | 9 ++++----- misc/tools.func | 22 +++++++++++++++++++++- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/ct/flaresolverr.sh b/ct/flaresolverr.sh index e1cac141d..cf2860f26 100644 --- a/ct/flaresolverr.sh +++ b/ct/flaresolverr.sh @@ -28,8 +28,9 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - RELEASE=$(curl -fsSL https://api.github.com/repos/FlareSolverr/FlareSolverr/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4)}') - if [[ "${RELEASE}" != "$(cat ~/.flaresolverr 2>/dev/null)" ]] || [[ ! -f ~/.flaresolverr ]]; then + + if check_for_gh_release "flaresolverr" "FlareSolverr/FlareSolverr"; then + msg_info "Stopping service" systemctl stop flaresolverr msg_ok "Stopped service" @@ -52,10 +53,8 @@ EOF msg_info "Starting service" systemctl start flaresolverr msg_ok "Started service" - else - msg_ok "No update required. ${APP} is already at ${RELEASE}" fi - exit + exit 0 } start diff --git a/misc/tools.func b/misc/tools.func index 96c27a30b..f5e7b5335 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -2001,7 +2001,27 @@ EOF fi } -check_for_update() { +# ------------------------------------------------------------------------------ +# Checks for new GitHub release (latest tag). +# +# Description: +# - Queries the GitHub API for the latest release tag +# - Compares it to a local cached version (~/.) +# - If newer, sets global CHECK_UPDATE_RELEASE and returns 0 +# +# Usage: +# check_for_gh_release "AppName" "user/repo" +# if [[ $? -eq 0 ]]; then +# echo "New version available: $CHECK_UPDATE_RELEASE" +# # trigger update... +# fi +# +# Notes: +# - Requires `jq` (auto-installed if missing) +# - Does not modify anything, only checks version state +# - Does not support pre-releases +# ------------------------------------------------------------------------------ +check_for_gh_release() { local app="$1" local source="$2" local current_file="$HOME/.${app,,}" From 694de8a7586c76a624a45fbac9afe6666f69a459 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 28 Aug 2025 16:11:05 +0200 Subject: [PATCH 0768/1733] Update tools.func --- misc/tools.func | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index f5e7b5335..6b619c449 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -2024,6 +2024,7 @@ EOF check_for_gh_release() { local app="$1" local source="$2" + local pinned_version="${3:-}" # optional local current_file="$HOME/.${app,,}" msg_info "Check for update: ${app}" @@ -2043,34 +2044,40 @@ check_for_gh_release() { fi # get latest release - local release +local release release=$(curl -fsSL "https://api.github.com/repos/${source}/releases/latest" | jq -r '.tag_name' | sed 's/^v//') - # DEBUG - #echo "[DEBUG] Latest release fetched: '${release}'" - if [[ -z "$release" ]]; then msg_error "Unable to determine latest release for ${app}" return 1 fi local current="" - if [[ -f "$current_file" ]]; then - current=$(<"$current_file") + [[ -f "$current_file" ]] && current=$(<"$current_file") + + # PINNED Releases + if [[ -n "$pinned_version" ]]; then + if [[ "$pinned_version" == "$release" ]]; then + msg_ok "${app} pinned to v${pinned_version} (no update needed)" + return 1 + else + if [[ "$current" == "$pinned_version" ]]; then + msg_ok "${app} pinned to v${pinned_version} (already installed, upstream v${release})" + return 1 + fi + msg_info "${app} pinned to v${pinned_version} (upstream v${release}) → update/downgrade required" + CHECK_UPDATE_RELEASE="$pinned_version" + return 0 + fi fi - # DEBUG - #echo "[DEBUG] Current file: '${current_file}'" - #echo "[DEBUG] Current version read: '${current}'" - if [[ "$release" != "$current" ]] || [[ ! -f "$current_file" ]]; then - #echo "[DEBUG] Decision: Update required (release='${release}' current='${current}')" CHECK_UPDATE_RELEASE="$release" + msg_info "New release available: v${release} (current: v${current:-none})" return 0 else - #echo "[DEBUG] Decision: No update (release='${release}' current='${current}')" msg_ok "${app} is up to date (v${release})" return 1 fi -} +} \ No newline at end of file From 34b0ccac5df3deec2314cfa38f788ada19d59e95 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Thu, 28 Aug 2025 13:15:49 -0400 Subject: [PATCH 0769/1733] Autocaliweb: update DB download paths --- install/autocaliweb-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/autocaliweb-install.sh b/install/autocaliweb-install.sh index 9a97b37fe..49e36772f 100644 --- a/install/autocaliweb-install.sh +++ b/install/autocaliweb-install.sh @@ -115,8 +115,8 @@ msg_info "Initializing databases" KEPUBIFY_PATH=$(command -v kepubify 2>/dev/null || echo "/usr/bin/kepubify") EBOOK_CONVERT_PATH=$(command -v ebook-convert 2>/dev/null || echo "/usr/bin/ebook-convert") CALIBRE_BIN_DIR=$(dirname "$EBOOK_CONVERT_PATH") -curl -fsSL https://github.com/gelbphoenix/autocaliweb/raw/refs/heads/master/library/metadata.db -o "$CALIBRE_LIB_DIR"/metadata.db -curl -fsSL https://github.com/gelbphoenix/autocaliweb/raw/refs/heads/master/library/app.db -o "$CONFIG_DIR"/app.db +curl -fsSL https://github.com/gelbphoenix/autocaliweb/raw/refs/heads/main/library/metadata.db -o "$CALIBRE_LIB_DIR"/metadata.db +curl -fsSL https://github.com/gelbphoenix/autocaliweb/raw/refs/heads/main/library/app.db -o "$CONFIG_DIR"/app.db sqlite3 "$CONFIG_DIR/app.db" < Date: Fri, 29 Aug 2025 11:41:08 +0200 Subject: [PATCH 0770/1733] tt --- ct/flaresolverr.sh | 67 ------------ install/flaresolverr-install.sh | 71 ------------ tools/pve/prx-add-ips.sh | 187 ++++++++++++++++++++++++++++++++ 3 files changed, 187 insertions(+), 138 deletions(-) delete mode 100644 ct/flaresolverr.sh delete mode 100644 install/flaresolverr-install.sh create mode 100644 tools/pve/prx-add-ips.sh diff --git a/ct/flaresolverr.sh b/ct/flaresolverr.sh deleted file mode 100644 index cf2860f26..000000000 --- a/ct/flaresolverr.sh +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) | Co-Author: remz1337 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/FlareSolverr/FlareSolverr - -APP="FlareSolverr" -var_tags="${var_tags:-proxy}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-4}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - - if [[ ! -f /etc/systemd/system/flaresolverr.service ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if check_for_gh_release "flaresolverr" "FlareSolverr/FlareSolverr"; then - - msg_info "Stopping service" - systemctl stop flaresolverr - msg_ok "Stopped service" - - PYTHON_VERSION="3.13" setup_uv - - msg_info "prepare uv python 3.13" - UV_PY="$(uv python find 3.13)" - cat <<'EOF' >/usr/local/bin/python3 -#!/bin/bash -exec "$UV_PY/bin/python3.13" "$@" -EOF - chmod +x /usr/local/bin/python3 - ln -sf "$UV_PY/bin/python3.13" /usr/local/bin/python3.13 - msg_ok "prepared python 3.13" - - rm -rf /opt/flaresolverr - fetch_and_deploy_gh_release "flaresolverr" "FlareSolverr/FlareSolverr" "prebuild" "latest" "/opt/flaresolverr" "flaresolverr_linux_x64.tar.gz" - - msg_info "Starting service" - systemctl start flaresolverr - msg_ok "Started service" - fi - exit 0 -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8191${CL}" diff --git a/install/flaresolverr-install.sh b/install/flaresolverr-install.sh deleted file mode 100644 index 43dfe477e..000000000 --- a/install/flaresolverr-install.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) -# Co-Author: remz1337 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/FlareSolverr/FlareSolverr - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt-get install -y \ - apt-transport-https \ - xvfb -msg_ok "Installed Dependencies" - -PYTHON_VERSION="3.13" setup_uv - -msg_info "prepare uv python 3.13" -UV_PY="$(uv python find 3.13)" -cat <<'EOF' >/usr/local/bin/python3 -#!/bin/bash -exec "$UV_PY/bin/python3.13" "$@" -EOF -chmod +x /usr/local/bin/python3 -ln -sf "$UV_PY/bin/python3.13" /usr/local/bin/python3.13 -msg_ok "prepared python 3.13" - -msg_info "Installing Chrome" -curl -fsSL "https://dl.google.com/linux/linux_signing_key.pub" | gpg --dearmor -o /usr/share/keyrings/google-chrome.gpg -echo "deb [arch=amd64 signed-by=/usr/share/keyrings/google-chrome.gpg] http://dl.google.com/linux/chrome/deb/ stable main" >/etc/apt/sources.list.d/google-chrome.list -$STD apt update -$STD apt install -y google-chrome-stable -msg_ok "Installed Chrome" - -fetch_and_deploy_gh_release "flaresolverr" "FlareSolverr/FlareSolverr" "prebuild" "latest" "/opt/flaresolverr" "flaresolverr_linux_x64.tar.gz" - -msg_info "Creating Service" -cat </etc/systemd/system/flaresolverr.service -[Unit] -Description=FlareSolverr -After=network.target -[Service] -SyslogIdentifier=flaresolverr -Restart=always -RestartSec=5 -Type=simple -Environment="LOG_LEVEL=info" -Environment="CAPTCHA_SOLVER=none" -WorkingDirectory=/opt/flaresolverr -ExecStart=/opt/flaresolverr/flaresolverr -TimeoutStopSec=30 -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now flaresolverr -msg_ok "Created Service" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" diff --git a/tools/pve/prx-add-ips.sh b/tools/pve/prx-add-ips.sh new file mode 100644 index 000000000..244cffd49 --- /dev/null +++ b/tools/pve/prx-add-ips.sh @@ -0,0 +1,187 @@ +#!/usr/bin/env bash +# ----------------------------------------------------------------- +# Proxmox Add-IPs (LXC + VMs → Tags) +# ----------------------------------------------------------------- +# © 2021-2025 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT +# ----------------------------------------------------------------- + +APP="Proxmox Add-IPs" +FILE_PATH="/usr/local/bin/prx-add-ips" +CONF_DIR="/opt/prx-add-ips" +CONF_FILE="$CONF_DIR/prx-add-ips.conf" + +set -Eeuo pipefail + +# --- Farben (optional) --- +YW="\033[33m" +GN="\033[1;92m" +RD="\033[01;31m" +CL="\033[m" +msg() { [[ "${USE_COLOR:-true}" == "true" ]] && echo -e "$@" || echo -e "$(echo "$@" | sed -E 's/\x1B\[[0-9;]*[JKmsu]//g')"; } +msg_info() { msg "${YW}➜ $1${CL}"; } +msg_ok() { msg "${GN}✔ $1${CL}"; } +msg_error() { msg "${RD}✖ $1${CL}"; } + +# ----------------------------------------------------------------- +# Installation +# ----------------------------------------------------------------- +if [[ -f "$FILE_PATH" ]]; then + msg_info "$APP already installed at $FILE_PATH" + exit 0 +fi + +msg_info "Installing dependencies" +apt-get update -qq +apt-get install -y jq ipcalc net-tools >/dev/null +msg_ok "Dependencies installed" + +mkdir -p "$CONF_DIR" + +# ----------------------------------------------------------------- +# Config +# ----------------------------------------------------------------- +if [[ ! -f "$CONF_FILE" ]]; then + cat <"$CONF_FILE" +# prx-add-ips.conf – configuration for Proxmox Add-IPs + +# Allowed CIDRs +CIDR_LIST=( + 192.168.0.0/16 + 10.0.0.0/8 + 172.16.0.0/12 +) + +# Main loop interval in seconds +LOOP_INTERVAL=60 + +# Use colored output? (true/false) +USE_COLOR=true +EOF + msg_ok "Default config written to $CONF_FILE" +else + msg_info "Config $CONF_FILE already exists" +fi + +# ----------------------------------------------------------------- +# Main Script +# ----------------------------------------------------------------- +cat <<"EOF" >"$FILE_PATH" +#!/usr/bin/env bash +set -Eeuo pipefail + +CONFIG_FILE="/opt/prx-add-ips/prx-add-ips.conf" +[[ -f "$CONFIG_FILE" ]] && source "$CONFIG_FILE" + +YW="\033[33m"; GN="\033[1;92m"; RD="\033[01;31m"; CL="\033[m" +msg() { [[ "${USE_COLOR:-true}" == "true" ]] && echo -e "$@" || echo -e "$(echo "$@" | sed -E 's/\x1B\[[0-9;]*[JKmsu]//g')"; } +msg_info() { msg "${YW}➜ $1${CL}"; } +msg_ok() { msg "${GN}✔ $1${CL}"; } +msg_error(){ msg "${RD}✖ $1${CL}"; } + +is_valid_ipv4() { + local ip=$1 + [[ $ip =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] || return 1 + for part in ${ip//./ }; do + ((part >= 0 && part <= 255)) || return 1 + done + return 0 +} + +ip_in_cidrs() { + local ip="$1" + for cidr in "${CIDR_LIST[@]}"; do + ipcalc -nb "$cidr" "$ip" &>/dev/null && return 0 + done + return 1 +} + +set_tags() { + local vmid="$1" kind="$2"; shift 2 + local ips=("$@") + + # aktuelle Tags holen + local existing_tags=() + mapfile -t existing_tags < <($kind config "$vmid" | awk '/tags:/{$1=""; print}' | tr ';' '\n') + + local existing_ips=() + local non_ip_tags=() + for t in "${existing_tags[@]}"; do + if is_valid_ipv4 "$t"; then + existing_ips+=("$t") + else + non_ip_tags+=("$t") + fi + done + + local new_tags=("${non_ip_tags[@]}" "${ips[@]}") + new_tags=($(printf "%s\n" "${new_tags[@]}" | sort -u)) + + if [[ "$(printf "%s\n" "${existing_ips[@]}" | sort -u)" != "$(printf "%s\n" "${ips[@]}" | sort -u)" ]]; then + msg_info "$kind $vmid → updating tags to ${new_tags[*]}" + $kind set "$vmid" -tags "$(IFS=';'; echo "${new_tags[*]}")" + else + msg_info "$kind $vmid → no IP change" + fi +} + +update_lxc_iptags() { + for vmid in $(pct list | awk 'NR>1 {print $1}'); do + local ips=() + for ip in $(lxc-info -n "$vmid" -iH 2>/dev/null); do + is_valid_ipv4 "$ip" && ip_in_cidrs "$ip" && ips+=("$ip") + done + [[ ${#ips[@]} -gt 0 ]] && set_tags "$vmid" pct "${ips[@]}" + done +} + +update_vm_iptags() { + for vmid in $(qm list | awk 'NR>1 {print $1}'); do + if qm agent "$vmid" ping &>/dev/null; then + local ips=() + mapfile -t ips < <(qm agent "$vmid" network-get-interfaces \ + | jq -r '.[]?."ip-addresses"[]?."ip-address" | select(test("^[0-9]+\\."))') + local filtered=() + for ip in "${ips[@]}"; do + is_valid_ipv4 "$ip" && ip_in_cidrs "$ip" && filtered+=("$ip") + done + [[ ${#filtered[@]} -gt 0 ]] && set_tags "$vmid" qm "${filtered[@]}" + fi + done +} + +while true; do + update_lxc_iptags + update_vm_iptags + sleep "${LOOP_INTERVAL:-60}" +done +EOF + +chmod +x "$FILE_PATH" +msg_ok "Main script installed to $FILE_PATH" + +# ----------------------------------------------------------------- +# Systemd Service +# ----------------------------------------------------------------- +SERVICE="/etc/systemd/system/prx-add-ips.service" +if [[ ! -f "$SERVICE" ]]; then + cat <"$SERVICE" +[Unit] +Description=Proxmox Add-IPs (LXC + VM) +After=network.target + +[Service] +Type=simple +ExecStart=$FILE_PATH +Restart=always + +[Install] +WantedBy=multi-user.target +EOF + msg_ok "Service created" +fi + +systemctl daemon-reload +systemctl enable -q --now prx-add-ips.service +msg_ok "$APP service started" From b6cd9d457c39fa65fbb79c900693d8a8d08e1885 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Fri, 29 Aug 2025 12:52:28 +0200 Subject: [PATCH 0771/1733] Zitadel testing --- ct/zitadel.sh | 62 +++++++++++++++ install/zitadel-install.sh | 149 +++++++++++++++++++++++++++++++++++++ 2 files changed, 211 insertions(+) create mode 100644 ct/zitadel.sh create mode 100644 install/zitadel-install.sh diff --git a/ct/zitadel.sh b/ct/zitadel.sh new file mode 100644 index 000000000..c35c7b397 --- /dev/null +++ b/ct/zitadel.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: dave-yap (dave-yap) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://zitadel.com/ + +APP="Zitadel" +var_tags="${var_tags:-identity-provider}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-1024}" +var_disk="${var_disk:-8}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -f /etc/systemd/system/zitadel.service ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + RELEASE=$(curl -fsSL https://api.github.com/repos/zitadel/zitadel/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') + if [[ ! -f ~/.zitadel ]] || [[ "${RELEASE}" != "$(cat ~/.zitadel)" ]]; then + msg_info "Stopping $APP" + systemctl stop zitadel + msg_ok "Stopped $APP" + + rm -f /usr/local/bin/zitadel + fetch_and_deploy_gh_release "zitadel" "zitadel/zitadel" "prebuild" "latest" "/usr/local/bin" "zitadel-linux-amd64.tar.gz" + + msg_info "Updating $APP to ${RELEASE}" + $STD zitadel setup --masterkeyFile /opt/zitadel/.masterkey --config /opt/zitadel/config.yaml --init-projections=true + msg_ok "Updated $APP to ${RELEASE}" + + msg_info "Starting $APP" + systemctl start zitadel + msg_ok "Started $APP" + + msg_ok "Update Successful" + else + msg_ok "No update required. ${APP} is already at ${RELEASE}" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080/ui/console${CL}" diff --git a/install/zitadel-install.sh b/install/zitadel-install.sh new file mode 100644 index 000000000..ba8cbdc49 --- /dev/null +++ b/install/zitadel-install.sh @@ -0,0 +1,149 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: dave-yap +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://zitadel.com/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies (Patience)" +$STD apt-get install -y ca-certificates +msg_ok "Installed Dependecies" + +PG_VERSION="17" setup_postgresql + +msg_info "Installing Postgresql" +DB_NAME="zitadel" +DB_USER="zitadel" +DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) +DB_ADMIN_USER="root" +DB_ADMIN_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) +systemctl start postgresql +$STD sudo -u postgres psql -c "CREATE USER $DB_USER WITH PASSWORD '$DB_PASS';" +$STD sudo -u postgres psql -c "CREATE USER $DB_ADMIN_USER WITH PASSWORD '$DB_ADMIN_PASS' SUPERUSER;" +$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME OWNER $DB_ADMIN_USER;" +{ + echo "Application Credentials" + echo "DB_NAME: $DB_NAME" + echo "DB_USER: $DB_USER" + echo "DB_PASS: $DB_PASS" + echo "DB_ADMIN_USER: $DB_ADMIN_USER" + echo "DB_ADMIN_PASS: $DB_ADMIN_PASS" +} >>~/zitadel.creds +msg_ok "Installed PostgreSQL" + +fetch_and_deploy_gh_release "zitadel" "zitadel/zitadel" "prebuild" "latest" "/usr/local/bin" "zitadel-linux-amd64.tar.gz" + +msg_info "Setting up Zitadel Environments" +mkdir -p /opt/zitadel +echo "/opt/zitadel/config.yaml" >"/opt/zitadel/.config" +head -c 32 < <(openssl rand -base64 48 | tr -dc 'a-zA-Z0-9') >"/opt/zitadel/.masterkey" +{ + echo "Config location: $(cat "/opt/zitadel/.config")" + echo "Masterkey: $(cat "/opt/zitadel/.masterkey")" +} >>~/zitadel.creds +cat </opt/zitadel/config.yaml +Port: 8080 +ExternalPort: 8080 +ExternalDomain: localhost +ExternalSecure: false +TLS: + Enabled: false + KeyPath: "" + Key: "" + CertPath: "" + Cert: "" + +Database: + postgres: + Host: localhost + Port: 5432 + Database: ${DB_NAME} + User: + Username: ${DB_USER} + Password: ${DB_PASS} + SSL: + Mode: disable + RootCert: "" + Cert: "" + Key: "" + Admin: + Username: ${DB_ADMIN_USER} + Password: ${DB_ADMIN_PASS} + SSL: + Mode: disable + RootCert: "" + Cert: "" + Key: "" +DefaultInstance: + Features: + LoginV2: + Required: false +EOF +msg_ok "Installed Zitadel Enviroments" + +msg_info "Creating Services" +cat </etc/systemd/system/zitadel.service +[Unit] +Description=ZITADEL Identiy Server +After=network.target postgresql.service +Wants=postgresql.service + +[Service] +Type=simple +User=zitadel +Group=zitadel +ExecStart=/usr/local/bin/zitadel start --masterkeyFile "/opt/zitadel/.masterkey" --config "/opt/zitadel/config.yaml" +Restart=always +RestartSec=5 +TimeoutStartSec=0 + +# Security Hardening options +ProtectSystem=full +ProtectHome=true +PrivateTmp=true +NoNewPrivileges=true + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q zitadel +msg_ok "Created Services" + +msg_info "Zitadel initial setup" +$STD zitadel start-from-init --masterkeyFile /opt/zitadel/.masterkey --config /opt/zitadel/config.yaml +sleep 60 +kill $(lsof -i | awk '/zitadel/ {print $2}' | head -n1) +useradd zitadel +msg_ok "Zitadel initialized" + +msg_info "Set ExternalDomain to current IP and restart Zitadel" +IP=$(ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) +sed -i "0,/localhost/s/localhost/${IP}/" /opt/zitadel/config.yaml +systemctl stop -q zitadel +$STD zitadel setup --masterkeyFile /opt/zitadel/.masterkey --config /opt/zitadel/config.yaml +systemctl restart -q zitadel +msg_ok "Zitadel restarted with ExternalDomain set to current IP" + +msg_info "Create zitadel-rerun.sh" +cat <~/zitadel-rerun.sh +systemctl stop zitadel +timeout --kill-after=5s 15s zitadel setup --masterkeyFile /opt/zitadel/.masterkey --config /opt/zitadel/config.yaml +systemctl restart zitadel +EOF +msg_ok "Bash script for rerunning Zitadel after changing Zitadel config.yaml" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 6c742a1d66259455dc53002cd8fe67ad166c4eb7 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Fri, 29 Aug 2025 12:53:44 +0200 Subject: [PATCH 0772/1733] test --- install/zitadel-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/zitadel-install.sh b/install/zitadel-install.sh index ba8cbdc49..5b5a84510 100644 --- a/install/zitadel-install.sh +++ b/install/zitadel-install.sh @@ -118,7 +118,7 @@ systemctl enable -q zitadel msg_ok "Created Services" msg_info "Zitadel initial setup" -$STD zitadel start-from-init --masterkeyFile /opt/zitadel/.masterkey --config /opt/zitadel/config.yaml +zitadel start-from-init --masterkeyFile /opt/zitadel/.masterkey --config /opt/zitadel/config.yaml &>/dev/null & sleep 60 kill $(lsof -i | awk '/zitadel/ {print $2}' | head -n1) useradd zitadel From e0442f88f8351a40f514956829aeb5dffa5722a5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 30 Aug 2025 06:52:26 +0000 Subject: [PATCH 0773/1733] Bump next in /frontend in the npm_and_yarn group across 1 directory Bumps the npm_and_yarn group with 1 update in the /frontend directory: [next](https://github.com/vercel/next.js). Updates `next` from 15.2.4 to 15.5.2 - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/compare/v15.2.4...v15.5.2) --- updated-dependencies: - dependency-name: next dependency-version: 15.5.2 dependency-type: direct:production dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 590 ++++++++++++++++++++++++++++++++----- frontend/package.json | 2 +- 2 files changed, 513 insertions(+), 79 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index fe7ecb632..20bb20922 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -33,7 +33,7 @@ "fuse.js": "^7.1.0", "lucide-react": "^0.453.0", "mini-svg-data-uri": "^1.4.4", - "next": "15.2.4", + "next": "15.5.2", "next-themes": "^0.3.0", "nuqs": "^2.4.1", "pocketbase": "^0.21.5", @@ -441,9 +441,9 @@ } }, "node_modules/@emnapi/runtime": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz", - "integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.5.0.tgz", + "integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==", "license": "MIT", "optional": true, "dependencies": { @@ -1267,6 +1267,22 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.0.tgz", + "integrity": "sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@img/sharp-libvips-linux-s390x": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", @@ -1375,6 +1391,28 @@ "@img/sharp-libvips-linux-arm64": "1.0.4" } }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.3.tgz", + "integrity": "sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.0" + } + }, "node_modules/@img/sharp-linux-s390x": { "version": "0.33.5", "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", @@ -1482,6 +1520,25 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.3.tgz", + "integrity": "sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@img/sharp-win32-ia32": { "version": "0.33.5", "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", @@ -1598,9 +1655,9 @@ "license": "MIT" }, "node_modules/@next/env": { - "version": "15.2.4", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.2.4.tgz", - "integrity": "sha512-+SFtMgoiYP3WoSswuNmxJOCwi06TdWE733D+WPjpXIe4LXGULwEaofiiAy6kbS0+XjM5xF5n3lKuBwN2SnqD9g==", + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.2.tgz", + "integrity": "sha512-Qe06ew4zt12LeO6N7j8/nULSOe3fMXE4dM6xgpBQNvdzyK1sv5y4oAP3bq4LamrvGCZtmRYnW8URFCeX5nFgGg==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { @@ -1644,9 +1701,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "15.2.4", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.2.4.tgz", - "integrity": "sha512-1AnMfs655ipJEDC/FHkSr0r3lXBgpqKo4K1kiwfUf3iE68rDFXZ1TtHdMvf7D0hMItgDZ7Vuq3JgNMbt/+3bYw==", + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.2.tgz", + "integrity": "sha512-8bGt577BXGSd4iqFygmzIfTYizHb0LGWqH+qgIF/2EDxS5JsSdERJKA8WgwDyNBZgTIIA4D8qUtoQHmxIIquoQ==", "cpu": [ "arm64" ], @@ -1660,9 +1717,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "15.2.4", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.2.4.tgz", - "integrity": "sha512-3qK2zb5EwCwxnO2HeO+TRqCubeI/NgCe+kL5dTJlPldV/uwCnUgC7VbEzgmxbfrkbjehL4H9BPztWOEtsoMwew==", + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.2.tgz", + "integrity": "sha512-2DjnmR6JHK4X+dgTXt5/sOCu/7yPtqpYt8s8hLkHFK3MGkka2snTv3yRMdHvuRtJVkPwCGsvBSwmoQCHatauFQ==", "cpu": [ "x64" ], @@ -1676,9 +1733,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.2.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.2.4.tgz", - "integrity": "sha512-HFN6GKUcrTWvem8AZN7tT95zPb0GUGv9v0d0iyuTb303vbXkkbHDp/DxufB04jNVD+IN9yHy7y/6Mqq0h0YVaQ==", + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.2.tgz", + "integrity": "sha512-3j7SWDBS2Wov/L9q0mFJtEvQ5miIqfO4l7d2m9Mo06ddsgUK8gWfHGgbjdFlCp2Ek7MmMQZSxpGFqcC8zGh2AA==", "cpu": [ "arm64" ], @@ -1692,9 +1749,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "15.2.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.2.4.tgz", - "integrity": "sha512-Oioa0SORWLwi35/kVB8aCk5Uq+5/ZIumMK1kJV+jSdazFm2NzPDztsefzdmzzpx5oGCJ6FkUC7vkaUseNTStNA==", + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.2.tgz", + "integrity": "sha512-s6N8k8dF9YGc5T01UPQ08yxsK6fUow5gG1/axWc1HVVBYQBgOjca4oUZF7s4p+kwhkB1bDSGR8QznWrFZ/Rt5g==", "cpu": [ "arm64" ], @@ -1708,9 +1765,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "15.2.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.2.4.tgz", - "integrity": "sha512-yb5WTRaHdkgOqFOZiu6rHV1fAEK0flVpaIN2HB6kxHVSy/dIajWbThS7qON3W9/SNOH2JWkVCyulgGYekMePuw==", + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.2.tgz", + "integrity": "sha512-o1RV/KOODQh6dM6ZRJGZbc+MOAHww33Vbs5JC9Mp1gDk8cpEO+cYC/l7rweiEalkSm5/1WGa4zY7xrNwObN4+Q==", "cpu": [ "x64" ], @@ -1724,9 +1781,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "15.2.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.2.4.tgz", - "integrity": "sha512-Dcdv/ix6srhkM25fgXiyOieFUkz+fOYkHlydWCtB0xMST6X9XYI3yPDKBZt1xuhOytONsIFJFB08xXYsxUwJLw==", + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.2.tgz", + "integrity": "sha512-/VUnh7w8RElYZ0IV83nUcP/J4KJ6LLYliiBIri3p3aW2giF+PAVgZb6mk8jbQSB3WlTai8gEmCAr7kptFa1H6g==", "cpu": [ "x64" ], @@ -1740,9 +1797,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.2.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.2.4.tgz", - "integrity": "sha512-dW0i7eukvDxtIhCYkMrZNQfNicPDExt2jPb9AZPpL7cfyUo7QSNl1DjsHjmmKp6qNAqUESyT8YFl/Aw91cNJJg==", + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.2.tgz", + "integrity": "sha512-sMPyTvRcNKXseNQ/7qRfVRLa0VhR0esmQ29DD6pqvG71+JdVnESJaHPA8t7bc67KD5spP3+DOCNLhqlEI2ZgQg==", "cpu": [ "arm64" ], @@ -1756,9 +1813,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "15.2.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.2.4.tgz", - "integrity": "sha512-SbnWkJmkS7Xl3kre8SdMF6F/XDh1DTFEhp0jRTj/uB8iPKoU2bb2NDfcu+iifv1+mxQEd1g2vvSxcZbXSKyWiQ==", + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.2.tgz", + "integrity": "sha512-W5VvyZHnxG/2ukhZF/9Ikdra5fdNftxI6ybeVKYvBPDtyx7x4jPPSNduUkfH5fo3zG0JQ0bPxgy41af2JX5D4Q==", "cpu": [ "x64" ], @@ -3043,12 +3100,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@swc/counter": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", - "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", - "license": "Apache-2.0" - }, "node_modules/@swc/helpers": { "version": "0.5.15", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", @@ -4041,17 +4092,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "dependencies": { - "streamsearch": "^1.1.0" - }, - "engines": { - "node": ">=10.16.0" - } - }, "node_modules/cac": { "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", @@ -4660,9 +4700,9 @@ } }, "node_modules/detect-libc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", - "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", "license": "Apache-2.0", "engines": { "node": ">=8" @@ -7252,15 +7292,13 @@ "license": "MIT" }, "node_modules/next": { - "version": "15.2.4", - "resolved": "https://registry.npmjs.org/next/-/next-15.2.4.tgz", - "integrity": "sha512-VwL+LAaPSxEkd3lU2xWbgEOtrM8oedmyhBqaVNmgKB+GvZlCy9rgaEc+y2on0wv+l0oSFqLtYD6dcC1eAedUaQ==", + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/next/-/next-15.5.2.tgz", + "integrity": "sha512-H8Otr7abj1glFhbGnvUt3gz++0AF1+QoCXEBmd/6aKbfdFwrn0LpA836Ed5+00va/7HQSDD+mOoVhn3tNy3e/Q==", "license": "MIT", "dependencies": { - "@next/env": "15.2.4", - "@swc/counter": "0.1.3", + "@next/env": "15.5.2", "@swc/helpers": "0.5.15", - "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" @@ -7272,19 +7310,19 @@ "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "15.2.4", - "@next/swc-darwin-x64": "15.2.4", - "@next/swc-linux-arm64-gnu": "15.2.4", - "@next/swc-linux-arm64-musl": "15.2.4", - "@next/swc-linux-x64-gnu": "15.2.4", - "@next/swc-linux-x64-musl": "15.2.4", - "@next/swc-win32-arm64-msvc": "15.2.4", - "@next/swc-win32-x64-msvc": "15.2.4", - "sharp": "^0.33.5" + "@next/swc-darwin-arm64": "15.5.2", + "@next/swc-darwin-x64": "15.5.2", + "@next/swc-linux-arm64-gnu": "15.5.2", + "@next/swc-linux-arm64-musl": "15.5.2", + "@next/swc-linux-x64-gnu": "15.5.2", + "@next/swc-linux-x64-musl": "15.5.2", + "@next/swc-win32-arm64-msvc": "15.5.2", + "@next/swc-win32-x64-msvc": "15.5.2", + "sharp": "^0.34.3" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", - "@playwright/test": "^1.41.2", + "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", @@ -7315,6 +7353,367 @@ "react-dom": "^16.8 || ^17 || ^18" } }, + "node_modules/next/node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.3.tgz", + "integrity": "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.0" + } + }, + "node_modules/next/node_modules/@img/sharp-darwin-x64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.3.tgz", + "integrity": "sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.0" + } + }, + "node_modules/next/node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.0.tgz", + "integrity": "sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.0.tgz", + "integrity": "sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.0.tgz", + "integrity": "sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.0.tgz", + "integrity": "sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.0.tgz", + "integrity": "sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.0.tgz", + "integrity": "sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.0.tgz", + "integrity": "sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.0.tgz", + "integrity": "sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-linux-arm": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.3.tgz", + "integrity": "sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.0" + } + }, + "node_modules/next/node_modules/@img/sharp-linux-arm64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.3.tgz", + "integrity": "sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.0" + } + }, + "node_modules/next/node_modules/@img/sharp-linux-s390x": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.3.tgz", + "integrity": "sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.0" + } + }, + "node_modules/next/node_modules/@img/sharp-linux-x64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.3.tgz", + "integrity": "sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.0" + } + }, + "node_modules/next/node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.3.tgz", + "integrity": "sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.0" + } + }, + "node_modules/next/node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.3.tgz", + "integrity": "sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.0" + } + }, + "node_modules/next/node_modules/@img/sharp-wasm32": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.3.tgz", + "integrity": "sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.4.4" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-win32-ia32": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.3.tgz", + "integrity": "sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/next/node_modules/@img/sharp-win32-x64": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.3.tgz", + "integrity": "sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/next/node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", @@ -7343,6 +7742,49 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/next/node_modules/sharp": { + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.3.tgz", + "integrity": "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.4", + "semver": "^7.7.2" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.3", + "@img/sharp-darwin-x64": "0.34.3", + "@img/sharp-libvips-darwin-arm64": "1.2.0", + "@img/sharp-libvips-darwin-x64": "1.2.0", + "@img/sharp-libvips-linux-arm": "1.2.0", + "@img/sharp-libvips-linux-arm64": "1.2.0", + "@img/sharp-libvips-linux-ppc64": "1.2.0", + "@img/sharp-libvips-linux-s390x": "1.2.0", + "@img/sharp-libvips-linux-x64": "1.2.0", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.0", + "@img/sharp-libvips-linuxmusl-x64": "1.2.0", + "@img/sharp-linux-arm": "0.34.3", + "@img/sharp-linux-arm64": "0.34.3", + "@img/sharp-linux-ppc64": "0.34.3", + "@img/sharp-linux-s390x": "0.34.3", + "@img/sharp-linux-x64": "0.34.3", + "@img/sharp-linuxmusl-arm64": "0.34.3", + "@img/sharp-linuxmusl-x64": "0.34.3", + "@img/sharp-wasm32": "0.34.3", + "@img/sharp-win32-arm64": "0.34.3", + "@img/sharp-win32-ia32": "0.34.3", + "@img/sharp-win32-x64": "0.34.3" + } + }, "node_modules/node-releases": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", @@ -8631,9 +9073,9 @@ "license": "MIT" }, "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -8848,14 +9290,6 @@ "dev": true, "license": "MIT" }, - "node_modules/streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index 50379a320..27421f713 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -44,7 +44,7 @@ "fuse.js": "^7.1.0", "lucide-react": "^0.453.0", "mini-svg-data-uri": "^1.4.4", - "next": "15.2.4", + "next": "15.5.2", "next-themes": "^0.3.0", "nuqs": "^2.4.1", "pocketbase": "^0.21.5", From 613569c035cb79978ad583efb4e0c33db482013c Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Sun, 31 Aug 2025 18:38:52 +0200 Subject: [PATCH 0774/1733] temp-change github path --- ct/livebook.sh | 3 ++- misc/build.func | 2 +- misc/install.func | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ct/livebook.sh b/ct/livebook.sh index d934ec25a..42e8937e7 100755 --- a/ct/livebook.sh +++ b/ct/livebook.sh @@ -1,5 +1,6 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/refs/heads/main/misc/build.func) +#source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/refs/heads/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: dkuku # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/misc/build.func b/misc/build.func index c40a57bd1..49b20b7fa 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1438,7 +1438,7 @@ EOF' fi msg_ok "Customized LXC Container" - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)" + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/install/${var_install}.sh)" } destroy_lxc() { diff --git a/misc/install.func b/misc/install.func index e3751c295..da0337a5b 100644 --- a/misc/install.func +++ b/misc/install.func @@ -196,7 +196,8 @@ EOF systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//') msg_ok "Customized Container" fi - echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update + #echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update + echo "bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/ct/${app}.sh)\"" >/usr/bin/update chmod +x /usr/bin/update if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then mkdir -p /root/.ssh From 929c6e8fb311ae15f988a7d344bd8b59245fdb23 Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Sun, 31 Aug 2025 19:01:21 +0200 Subject: [PATCH 0775/1733] update messages --- install/livebook-install.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/install/livebook-install.sh b/install/livebook-install.sh index aa2e1b031..06cb914f8 100644 --- a/install/livebook-install.sh +++ b/install/livebook-install.sh @@ -23,7 +23,7 @@ $STD apt-get install -y \ msg_ok "Installed Dependencies" msg_info "Creating livebook user" -adduser --system --group --home /opt/livebook --shell /bin/bash livebook +$STD adduser --system --group --home /opt/livebook --shell /bin/bash livebook msg_ok "Created livebook user" msg_info "Installing Erlang and Elixir" @@ -47,12 +47,14 @@ $STD mix local.hex --force $STD mix local.rebar --force $STD mix escript.install hex livebook --force +msg_info "Setting Livebook password" LIVEBOOK_PASSWORD=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c16) cat < /opt/livebook/livebook.creds Livebook-Credentials Livebook Password: $LIVEBOOK_PASSWORD EOF +msg_ok "Livebook password stored in /opt/livebook/livebook.creds" cat < /opt/livebook/.env export HOME=/opt/livebook @@ -98,7 +100,6 @@ msg_ok "Set ownership and permissions" systemctl enable -q --now livebook msg_ok "Installed Livebook" -msg_info "Livebook password stored in /opt/livebook.creds" msg_ok "Installation completed successfully" motd_ssh From 91bf3257518f0a1d6a49f563328fd1dd913f6456 Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Sun, 31 Aug 2025 19:08:10 +0200 Subject: [PATCH 0776/1733] mute logs --- ct/livebook.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/livebook.sh b/ct/livebook.sh index 42e8937e7..51323f89b 100755 --- a/ct/livebook.sh +++ b/ct/livebook.sh @@ -42,7 +42,7 @@ function update_script() { msg_info "Updating ${APP} to ${RELEASE}" source /opt/livebook/.env cd /opt/livebook || exit 1 - mix escript.install hex livebook --force + $STD mix escript.install hex livebook --force echo "$RELEASE" | $STD tee /opt/.livebook chown -R livebook:livebook /opt/livebook /data From fc871466faea3161d013401db3acbe517822d4ce Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Sun, 31 Aug 2025 19:28:37 +0200 Subject: [PATCH 0777/1733] restore github links --- ct/livebook.sh | 3 +-- misc/build.func | 2 +- misc/install.func | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/ct/livebook.sh b/ct/livebook.sh index 51323f89b..b0d0e9fe2 100755 --- a/ct/livebook.sh +++ b/ct/livebook.sh @@ -1,6 +1,5 @@ #!/usr/bin/env bash -#source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/refs/heads/main/misc/build.func) -source <(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/refs/heads/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: dkuku # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/misc/build.func b/misc/build.func index 49b20b7fa..c40a57bd1 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1438,7 +1438,7 @@ EOF' fi msg_ok "Customized LXC Container" - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/install/${var_install}.sh)" + lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)" } destroy_lxc() { diff --git a/misc/install.func b/misc/install.func index da0337a5b..e3751c295 100644 --- a/misc/install.func +++ b/misc/install.func @@ -196,8 +196,7 @@ EOF systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//') msg_ok "Customized Container" fi - #echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update - echo "bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/dkuku/ProxmoxVED/refs/heads/livebook/ct/${app}.sh)\"" >/usr/bin/update + echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update chmod +x /usr/bin/update if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then mkdir -p /root/.ssh From ec831b8c02961920c53105e20edf670a9eab4369 Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Sun, 31 Aug 2025 19:42:42 +0200 Subject: [PATCH 0778/1733] move version file to home folder --- ct/livebook.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/livebook.sh b/ct/livebook.sh index b0d0e9fe2..f6cb59d60 100755 --- a/ct/livebook.sh +++ b/ct/livebook.sh @@ -32,7 +32,7 @@ function update_script() { msg_info "Checking for updates..." RELEASE=$(curl -fsSL https://api.github.com/repos/livebook-dev/livebook/releases/latest | grep "tag_name" | awk -F'"' '{print $4}') - if [[ "${RELEASE}" != "$(cat /opt/.livebook 2>/dev/null)" ]]; then + if [[ "${RELEASE}" != "$(cat /opt/livebook/.livebook 2>/dev/null)" ]]; then msg_info "Updating ${APP} LXC" $STD apt-get update $STD apt-get -y upgrade @@ -43,7 +43,7 @@ function update_script() { cd /opt/livebook || exit 1 $STD mix escript.install hex livebook --force - echo "$RELEASE" | $STD tee /opt/.livebook + echo "$RELEASE" | $STD tee /opt/livebook/.livebook chown -R livebook:livebook /opt/livebook /data msg_ok "Successfully updated to ${RELEASE}" From c043da1b0ef5362199c71cbf6d129b0163e6dd08 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 1 Sep 2025 09:17:02 +0200 Subject: [PATCH 0779/1733] Create alpine-tools.func --- misc/alpine-tools.func | 535 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 535 insertions(+) create mode 100644 misc/alpine-tools.func diff --git a/misc/alpine-tools.func b/misc/alpine-tools.func new file mode 100644 index 000000000..b03f4f084 --- /dev/null +++ b/misc/alpine-tools.func @@ -0,0 +1,535 @@ +#!/bin/ash +# shellcheck shell=ash + +# Erwartet vorhandene msg_* und optional $STD aus deinem Framework. + +# ------------------------------ +# kleine Helfer +# ------------------------------ +lower() { printf '%s' "$1" | tr '[:upper:]' '[:lower:]'; } +has() { command -v "$1" >/dev/null 2>&1; } + +need_tool() { + # usage: need_tool curl jq unzip ... + # installiert fehlende Tools via apk --no-cache + local missing=0 t + for t in "$@"; do + if ! has "$t"; then missing=1; fi + done + if [ "$missing" -eq 1 ]; then + msg_info "Installing tools: $*" + # busybox 'apk' ist vorhanden auf Alpine + apk add --no-cache "$@" >/dev/null 2>&1 || { + msg_error "apk add failed for: $*" + return 1 + } + msg_ok "Tools ready: $*" + fi +} + +net_resolves() { + # robust gegen fehlendes getent auf busybox + # usage: net_resolves api.github.com + local host="$1" + ping -c1 -W1 "$host" >/dev/null 2>&1 || nslookup "$host" >/dev/null 2>&1 +} + +ensure_usr_local_bin_persist() { + local PROFILE_FILE="/etc/profile.d/10-localbin.sh" + if [ ! -f "$PROFILE_FILE" ]; then + echo 'case ":$PATH:" in *:/usr/local/bin:*) ;; *) export PATH="/usr/local/bin:$PATH";; esac' >"$PROFILE_FILE" + chmod +x "$PROFILE_FILE" + fi +} + +download_with_progress() { + # $1 url, $2 dest + local url="$1" out="$2" cl + need_tool curl pv || return 1 + cl=$(curl -fsSLI "$url" 2>/dev/null | awk 'tolower($0) ~ /^content-length:/ {print $2}' | tr -d '\r') + if [ -n "$cl" ]; then + curl -fsSL "$url" | pv -s "$cl" >"$out" || { + msg_error "Download failed: $url" + return 1 + } + else + curl -fL# -o "$out" "$url" || { + msg_error "Download failed: $url" + return 1 + } + fi +} + +# ------------------------------ +# GitHub: Release prüfen +# ------------------------------ +check_for_gh_release() { + # app, repo, [pinned] + local app="$1" source="$2" pinned="${3:-}" + local app_lc + app_lc="$(lower "$app" | tr -d ' ')" + local current_file="$HOME/.${app_lc}" + local current="" release tag + + msg_info "Check for update: $app" + + net_resolves api.github.com || { + msg_error "DNS/network error: api.github.com" + return 1 + } + need_tool curl jq || return 1 + + tag=$(curl -fsSL "https://api.github.com/repos/${source}/releases/latest" | jq -r '.tag_name // empty') + [ -z "$tag" ] && { + msg_error "Unable to fetch latest tag for $app" + return 1 + } + release="${tag#v}" + + [ -f "$current_file" ] && current="$(cat "$current_file")" + + if [ -n "$pinned" ]; then + if [ "$pinned" = "$release" ]; then + msg_ok "$app pinned to v$pinned (no update)" + return 1 + fi + if [ "$current" = "$pinned" ]; then + msg_ok "$app pinned v$pinned installed (upstream v$release)" + return 1 + fi + msg_info "$app pinned v$pinned (upstream v$release) → update/downgrade" + CHECK_UPDATE_RELEASE="$pinned" + return 0 + fi + + if [ "$release" != "$current" ] || [ ! -f "$current_file" ]; then + CHECK_UPDATE_RELEASE="$release" + msg_info "New release available: v$release (current: v${current:-none})" + return 0 + fi + + msg_ok "$app is up to date (v$release)" + return 1 +} + +# ------------------------------ +# GitHub: Release holen & deployen (Alpine) +# modes: tarball | prebuild | singlefile +# ------------------------------ +fetch_and_deploy_gh() { + # $1 app, $2 repo, [$3 mode], [$4 version], [$5 target], [$6 asset_pattern] + local app="$1" repo="$2" mode="${3:-tarball}" version="${4:-latest}" target="${5:-/opt/$1}" pattern="${6:-}" + local app_lc + app_lc="$(lower "$app" | tr -d ' ')" + local vfile="$HOME/.${app_lc}" + local json url filename tmpd unpack + + net_resolves api.github.com || { + msg_error "DNS/network error" + return 1 + } + need_tool curl jq tar || return 1 + [ "$mode" = "prebuild" ] || [ "$mode" = "singlefile" ] && need_tool unzip >/dev/null 2>&1 || true + + tmpd="$(mktemp -d)" || return 1 + mkdir -p "$target" + + # Release JSON + if [ "$version" = "latest" ]; then + json="$(curl -fsSL "https://api.github.com/repos/$repo/releases/latest")" || { + msg_error "GitHub API failed" + rm -rf "$tmpd" + return 1 + } + else + json="$(curl -fsSL "https://api.github.com/repos/$repo/releases/tags/$version")" || { + msg_error "GitHub API failed" + rm -rf "$tmpd" + return 1 + } + fi + + # Effektive Version + version="$(printf '%s' "$json" | jq -r '.tag_name // empty')" + version="${version#v}" + + [ -z "$version" ] && { + msg_error "No tag in release json" + rm -rf "$tmpd" + return 1 + } + + case "$mode" in + tarball | source) + url="$(printf '%s' "$json" | jq -r '.tarball_url // empty')" + [ -z "$url" ] && url="https://github.com/$repo/archive/refs/tags/v$version.tar.gz" + filename="${app_lc}-${version}.tar.gz" + download_with_progress "$url" "$tmpd/$filename" || { + rm -rf "$tmpd" + return 1 + } + tar -xzf "$tmpd/$filename" -C "$tmpd" || { + msg_error "tar extract failed" + rm -rf "$tmpd" + return 1 + } + unpack="$(find "$tmpd" -mindepth 1 -maxdepth 1 -type d | head -n1)" + # Inhalte nach target kopieren (inkl. dotfiles) + (cd "$unpack" && tar -cf - .) | (cd "$target" && tar -xf -) || { + msg_error "copy failed" + rm -rf "$tmpd" + return 1 + } + ;; + prebuild) + [ -n "$pattern" ] || { + msg_error "prebuild requires asset pattern" + rm -rf "$tmpd" + return 1 + } + url="$(printf '%s' "$json" | jq -r '.assets[].browser_download_url' | awk -v p="$pattern" ' + BEGIN{IGNORECASE=1} + $0 ~ p {print; exit} + ')" + [ -z "$url" ] && { + msg_error "asset not found for pattern: $pattern" + rm -rf "$tmpd" + return 1 + } + filename="${url##*/}" + download_with_progress "$url" "$tmpd/$filename" || { + rm -rf "$tmpd" + return 1 + } + # entpacken je nach Format + case "$filename" in + *.zip) + need_tool unzip || { + rm -rf "$tmpd" + return 1 + } + mkdir -p "$tmpd/unp" + unzip -q "$tmpd/$filename" -d "$tmpd/unp" + ;; + *.tar.gz | *.tgz | *.tar.xz | *.tar.zst | *.tar.bz2) + mkdir -p "$tmpd/unp" + tar -xf "$tmpd/$filename" -C "$tmpd/unp" + ;; + *) + msg_error "unsupported archive: $filename" + rm -rf "$tmpd" + return 1 + ;; + esac + # top-level folder ggf. strippen + if [ "$(find "$tmpd/unp" -mindepth 1 -maxdepth 1 -type d | wc -l)" -eq 1 ] && [ -z "$(find "$tmpd/unp" -mindepth 1 -maxdepth 1 -type f | head -n1)" ]; then + unpack="$(find "$tmpd/unp" -mindepth 1 -maxdepth 1 -type d)" + (cd "$unpack" && tar -cf - .) | (cd "$target" && tar -xf -) || { + msg_error "copy failed" + rm -rf "$tmpd" + return 1 + } + else + (cd "$tmpd/unp" && tar -cf - .) | (cd "$target" && tar -xf -) || { + msg_error "copy failed" + rm -rf "$tmpd" + return 1 + } + fi + ;; + singlefile) + [ -n "$pattern" ] || { + msg_error "singlefile requires asset pattern" + rm -rf "$tmpd" + return 1 + } + url="$(printf '%s' "$json" | jq -r '.assets[].browser_download_url' | awk -v p="$pattern" ' + BEGIN{IGNORECASE=1} + $0 ~ p {print; exit} + ')" + [ -z "$url" ] && { + msg_error "asset not found for pattern: $pattern" + rm -rf "$tmpd" + return 1 + } + filename="${url##*/}" + download_with_progress "$url" "$target/$app" || { + rm -rf "$tmpd" + return 1 + } + chmod +x "$target/$app" + ;; + *) + msg_error "Unknown mode: $mode" + rm -rf "$tmpd" + return 1 + ;; + esac + + echo "$version" >"$vfile" + ensure_usr_local_bin_persist + rm -rf "$tmpd" + msg_ok "Deployed $app ($version) → $target" +} + +# ------------------------------ +# yq (mikefarah) – Alpine +# ------------------------------ +setup_yq() { + # bevorzugt apk, optional FORCE_GH=1 → GitHub Binary + if [ "${FORCE_GH:-0}" != "1" ] && apk info -e yq >/dev/null 2>&1; then + msg_info "Updating yq via apk" + apk add --no-cache --upgrade yq >/dev/null 2>&1 || true + msg_ok "yq ready ($(yq --version 2>/dev/null))" + return 0 + fi + + need_tool curl || return 1 + local arch bin url tmp + case "$(uname -m)" in + x86_64) arch="amd64" ;; + aarch64) arch="arm64" ;; + *) + msg_error "Unsupported arch for yq: $(uname -m)" + return 1 + ;; + esac + url="https://github.com/mikefarah/yq/releases/latest/download/yq_linux_${arch}" + tmp="$(mktemp)" + download_with_progress "$url" "$tmp" || return 1 + install -m 0755 "$tmp" /usr/local/bin/yq + rm -f "$tmp" + msg_ok "Setup yq ($(yq --version 2>/dev/null))" +} + +# ------------------------------ +# Adminer – Alpine +# ------------------------------ +setup_adminer() { + need_tool curl || return 1 + msg_info "Setup Adminer (Alpine)" + mkdir -p /var/www/localhost/htdocs/adminer + curl -fsSL https://github.com/vrana/adminer/releases/latest/download/adminer.php \ + -o /var/www/localhost/htdocs/adminer/index.php || { + msg_error "Adminer download failed" + return 1 + } + msg_ok "Adminer at /adminer (served by your webserver)" +} + +# ------------------------------ +# uv – Alpine (musl tarball) +# Optional: PYTHON_VERSION="3.12" +# ------------------------------ +setup_uv() { + need_tool curl tar || return 1 + local UV_BIN="/usr/local/bin/uv" + local arch tarball url tmpd ver installed + + case "$(uname -m)" in + x86_64) arch="x86_64-unknown-linux-musl" ;; + aarch64) arch="aarch64-unknown-linux-musl" ;; + *) + msg_error "Unsupported arch for uv: $(uname -m)" + return 1 + ;; + esac + + ver="$(curl -fsSL https://api.github.com/repos/astral-sh/uv/releases/latest | jq -r '.tag_name' 2>/dev/null)" + ver="${ver#v}" + [ -z "$ver" ] && { + msg_error "uv: cannot determine latest version" + return 1 + } + + if has "$UV_BIN"; then + installed="$($UV_BIN -V 2>/dev/null | awk '{print $2}')" + [ "$installed" = "$ver" ] && { + msg_ok "uv $ver already installed" + return 0 + } + msg_info "Updating uv $installed → $ver" + else + msg_info "Setup uv $ver" + fi + + tmpd="$(mktemp -d)" || return 1 + tarball="uv-${arch}.tar.gz" + url="https://github.com/astral-sh/uv/releases/download/v${ver}/${tarball}" + + download_with_progress "$url" "$tmpd/uv.tar.gz" || { + rm -rf "$tmpd" + return 1 + } + tar -xzf "$tmpd/uv.tar.gz" -C "$tmpd" || { + msg_error "uv: extract failed" + rm -rf "$tmpd" + return 1 + } + + # tar enthält ./uv + if [ -x "$tmpd/uv" ]; then + install -m 0755 "$tmpd/uv" "$UV_BIN" + else + # fallback: in Unterordner + install -m 0755 "$tmpd"/*/uv "$UV_BIN" 2>/dev/null || { + msg_error "uv binary not found in tar" + rm -rf "$tmpd" + return 1 + } + fi + rm -rf "$tmpd" + ensure_usr_local_bin_persist + msg_ok "Setup uv $ver" + + if [ -n "${PYTHON_VERSION:-}" ]; then + # uv liefert cpython builds für musl; den neuesten Patchstand finden: + local match + match="$(uv python list --only-downloads 2>/dev/null | awk -v maj="$PYTHON_VERSION" ' + $0 ~ "^cpython-"maj"\\." { print $0 }' | awk -F- '{print $2}' | sort -V | tail -n1)" + [ -z "$match" ] && { + msg_error "No matching Python for $PYTHON_VERSION" + return 1 + } + if ! uv python list | grep -q "cpython-${match}-linux"; then + msg_info "Installing Python $match via uv" + uv python install "$match" || { + msg_error "uv python install failed" + return 1 + } + msg_ok "Python $match installed (uv)" + fi + fi +} + +# ------------------------------ +# Java – Alpine (OpenJDK) +# JAVA_VERSION: 17|21 (Default 21) +# ------------------------------ +setup_java() { + local JAVA_VERSION="${JAVA_VERSION:-21}" pkg + case "$JAVA_VERSION" in + 17) pkg="openjdk17-jdk" ;; + 21 | *) pkg="openjdk21-jdk" ;; + esac + msg_info "Setup Java (OpenJDK $JAVA_VERSION)" + apk add --no-cache "$pkg" >/dev/null 2>&1 || { + msg_error "apk add $pkg failed" + return 1 + } + # JAVA_HOME setzen + local prof="/etc/profile.d/20-java.sh" + if [ ! -f "$prof" ]; then + echo 'export JAVA_HOME=$(dirname $(dirname $(readlink -f $(command -v java))))' >"$prof" + echo 'case ":$PATH:" in *:$JAVA_HOME/bin:*) ;; *) export PATH="$JAVA_HOME/bin:$PATH";; esac' >>"$prof" + chmod +x "$prof" + fi + msg_ok "Java ready: $(java -version 2>&1 | head -n1)" +} + +# ------------------------------ +# Go – Alpine (apk bevorzugt; optional GO_VERSION tarball) +# ------------------------------ +setup_go() { + if [ -z "${GO_VERSION:-}" ]; then + msg_info "Setup Go (apk)" + apk add --no-cache go >/dev/null 2>&1 || { + msg_error "apk add go failed" + return 1 + } + msg_ok "Go ready: $(go version 2>/dev/null)" + return 0 + fi + + # explizite Version via offizielles tar.gz + need_tool curl tar || return 1 + local ARCH TARBALL URL TMP + case "$(uname -m)" in + x86_64) ARCH="amd64" ;; + aarch64) ARCH="arm64" ;; + *) + msg_error "Unsupported arch for Go: $(uname -m)" + return 1 + ;; + esac + TARBALL="go${GO_VERSION}.linux-${ARCH}.tar.gz" + URL="https://go.dev/dl/${TARBALL}" + msg_info "Setup Go $GO_VERSION (tarball)" + TMP="$(mktemp)" + download_with_progress "$URL" "$TMP" || return 1 + rm -rf /usr/local/go + tar -C /usr/local -xzf "$TMP" || { + msg_error "extract go failed" + rm -f "$TMP" + return 1 + } + rm -f "$TMP" + ln -sf /usr/local/go/bin/go /usr/local/bin/go + ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt + ensure_usr_local_bin_persist + msg_ok "Go ready: $(go version 2>/dev/null)" +} + +# ------------------------------ +# Composer – Alpine +# nutzt php83-cli + openssl + phar +# ------------------------------ +setup_composer() { + local COMPOSER_BIN="/usr/local/bin/composer" + if ! has php; then + # bevorzugt php83 auf Alpine 3.20/3.21+ + msg_info "Installing PHP CLI for Composer" + apk add --no-cache php83-cli php83-openssl php83-phar php83-iconv >/dev/null 2>&1 || { + # Fallback auf generisches php-cli + apk add --no-cache php-cli php-openssl php-phar php-iconv >/dev/null 2>&1 || { + msg_error "Failed to install php-cli for composer" + return 1 + } + } + fi + + if [ -x "$COMPOSER_BIN" ]; then + msg_info "Updating Composer" + else + msg_info "Setup Composer" + fi + + need_tool curl || return 1 + curl -fsSL https://getcomposer.org/installer -o /tmp/composer-setup.php || { + msg_error "composer installer download failed" + return 1 + } + php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer >/dev/null 2>&1 || { + msg_error "composer install failed" + return 1 + } + rm -f /tmp/composer-setup.php + ensure_usr_local_bin_persist + msg_ok "Composer ready: $(composer --version 2>/dev/null)" +} + +# ------------------------------ +# Adminer/uv/go/java/yq/composer stehen oben +# ------------------------------ + +# ------------------------------ +# (Optional) LOCAL_IP import – POSIX-safe +# ------------------------------ +import_local_ip() { + # lädt LOCAL_IP aus /run/local-ip.env oder ermittelt es best effort + local IP_FILE="/run/local-ip.env" + if [ -f "$IP_FILE" ]; then + # shellcheck disable=SC1090 + . "$IP_FILE" + fi + if [ -z "${LOCAL_IP:-}" ]; then + LOCAL_IP="$(ip route get 1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i=="src"){print $(i+1); exit}}')" + [ -z "$LOCAL_IP" ] && LOCAL_IP="$(hostname -i 2>/dev/null | awk '{print $1}')" + [ -z "$LOCAL_IP" ] && { + msg_error "Could not determine LOCAL_IP" + return 1 + } + echo "LOCAL_IP=$LOCAL_IP" >"$IP_FILE" + fi + export LOCAL_IP +} From 005f1204d81c6b12d7c8763612c2eb6ea762aa06 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 2 Sep 2025 10:32:11 +0200 Subject: [PATCH 0780/1733] Update alpine-install.func --- misc/alpine-install.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/alpine-install.func b/misc/alpine-install.func index 450e92091..906d5b14b 100644 --- a/misc/alpine-install.func +++ b/misc/alpine-install.func @@ -83,7 +83,7 @@ network_check() { update_os() { msg_info "Updating Container OS" $STD apk update && $STD apk upgrade - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-tools.func) msg_ok "Updated Container OS" } From a9de7571ec8d1be387d049d4014250351b190518 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 2 Sep 2025 10:52:25 +0200 Subject: [PATCH 0781/1733] Update build.func --- misc/build.func | 324 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 222 insertions(+), 102 deletions(-) diff --git a/misc/build.func b/misc/build.func index 011f43981..15151f6eb 100644 --- a/misc/build.func +++ b/misc/build.func @@ -13,7 +13,7 @@ variables() { DIAGNOSTICS="yes" # sets the DIAGNOSTICS variable to "yes", used for the API call. METHOD="default" # sets the METHOD variable to "default", used for the API call. RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" # generates a random UUID and sets it to the RANDOM_UUID variable. - CT_TYPE=${var_unprivileged:-$CT_TYPE} + #CT_TYPE=${var_unprivileged:-$CT_TYPE} } source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) @@ -214,93 +214,6 @@ ssh_check() { fi } -# select_storage() { -# local CLASS=$1 CONTENT CONTENT_LABEL -# case $CLASS in -# container) -# CONTENT='rootdir' -# CONTENT_LABEL='Container' -# ;; -# template) -# CONTENT='vztmpl' -# CONTENT_LABEL='Template' -# ;; -# iso) -# CONTENT='iso' -# CONTENT_LABEL='ISO image' -# ;; -# images) -# CONTENT='images' -# CONTENT_LABEL='VM Disk image' -# ;; -# backup) -# CONTENT='backup' -# CONTENT_LABEL='Backup' -# ;; -# snippets) -# CONTENT='snippets' -# CONTENT_LABEL='Snippets' -# ;; -# *) -# msg_error "Invalid storage class '$CLASS'." -# exit 201 -# ;; -# esac - -# command -v whiptail >/dev/null || { -# msg_error "whiptail missing." -# exit 220 -# } -# command -v numfmt >/dev/null || { -# msg_error "numfmt missing." -# exit 221 -# } - -# local -a MENU -# while read -r line; do -# local TAG=$(echo "$line" | awk '{print $1}') -# local TYPE=$(echo "$line" | awk '{printf "%-10s", $2}') -# local FREE=$(echo "$line" | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf "%9sB", $6}') -# MENU+=("$TAG" "Type: $TYPE Free: $FREE" "OFF") -# done < <(pvesm status -content "$CONTENT" | awk 'NR>1') - -# if [ ${#MENU[@]} -eq 0 ]; then -# msg_error "No storage found for content type '$CONTENT'." -# exit 203 -# fi - -# if [ $((${#MENU[@]} / 3)) -eq 1 ]; then -# echo "${MENU[0]}" -# return -# fi - -# local STORAGE -# STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ -# "Which storage pool for ${CONTENT_LABEL,,}?\n(Spacebar to select)" \ -# 16 70 6 "${MENU[@]}" 3>&1 1>&2 2>&3) || { -# msg_error "Storage selection cancelled by user." -# exit 202 -# } -# echo "$STORAGE" -# } - -# manage_default_storage() { -# local file="/usr/local/community-scripts/default_storage" -# mkdir -p /usr/local/community-scripts - -# local tmpl=$(select_storage template) -# local cont=$(select_storage container) - -# cat <"$file" -# TEMPLATE_STORAGE=$tmpl -# CONTAINER_STORAGE=$cont -# EOF - -# msg_ok "Default Storage set: Template=${BL}$tmpl${CL} ${GN}|${CL} Container=${BL}$cont${CL}" -# whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --msgbox "Default Storage set:\n\nTemplate: $tmpl\nContainer: $cont" 10 58 -# } - base_settings() { # Default Settings CT_TYPE=${var_unprivileged:-"1"} @@ -902,6 +815,205 @@ EOF } +# ------------------------------------------------------------------------------ +# default_var_settings +# +# - Ensures /usr/local/community-scripts/default.vars exists (creates if missing) +# - Loads var_* values from default.vars (safe parser, no source/eval) +# - Precedence: ENV var_* > default.vars > built-in defaults +# - Maps var_verbose → VERBOSE +# - Calls base_settings "$VERBOSE" and echo_default +# ------------------------------------------------------------------------------ +default_var_settings() { + # Allowed var_* keys (alphabetically sorted) + local VAR_WHITELIST=( + var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_ctid var_disk var_fuse + var_gateway var_hostname var_ipv6_method var_ipv6_static var_mac var_mtu + var_net var_ns var_os var_pw var_ram var_storage var_tags var_tun var_unprivileged + var_verbose var_version var_vlan var_ssh var_ssh_authorized_key + ) + + # Snapshot: environment variables (highest precedence) + declare -A _HARD_ENV=() + local _k + for _k in "${VAR_WHITELIST[@]}"; do + if printenv "$_k" >/dev/null 2>&1; then _HARD_ENV["$_k"]=1; fi + done + + # Find default.vars (first valid path wins) + local _find_default_vars + _find_default_vars() { + local f + for f in \ + /usr/local/community-scripts/default.vars \ + "$HOME/.config/community-scripts/default.vars" \ + "./default.vars"; do + [ -f "$f" ] && { + echo "$f" + return 0 + } + done + return 1 + } + + # Ensure default.vars exists, create with sane defaults if missing + local _ensure_default_vars + _ensure_default_vars() { + _find_default_vars >/dev/null 2>&1 && return 0 + local canonical="/usr/local/community-scripts/default.vars" + msg_info "No default.vars found. Creating ${canonical}" + mkdir -p /usr/local/community-scripts + cat >"$canonical" <<'EOF' +# Community-Scripts defaults (var_* only). Lines starting with # are comments. +# Precedence: ENV var_* > default.vars > built-ins. +# Keep keys alphabetically sorted. + +# CT/OS +var_os=debian +var_version=12 +var_unprivileged=1 + +# Resources +var_cpu=1 +var_disk=4 +var_ram=1024 + +# Network +var_brg=vmbr0 +var_net=dhcp +var_ipv6_method=auto +# var_gateway= +# var_ipv6_static= +# var_vlan= +# var_mtu= +# var_mac= +# var_ns= +# var_storage= + +# SSH +var_ssh=no +# var_ssh_authorized_key= + +# APT Cacher +# var_apt_cacher=yes +# var_apt_cacher_ip=192.168.1.10 + +# Features/Tags +var_fuse=no +var_tun=no +var_tags=community-script +var_verbose=no + +# Security (root PW) – empty => Autologin +# var_pw= + +# CTID/Hostname – empty => auto +# var_ctid= +# var_hostname= +EOF + chmod 0644 "$canonical" + msg_ok "Created ${canonical}" + } + + # Whitelist check + local _is_whitelisted_key + _is_whitelisted_key() { + local k="$1" + local w + for w in "${VAR_WHITELIST[@]}"; do [ "$k" = "$w" ] && return 0; done + return 1 + } + + # Safe parser for KEY=VALUE lines + local _load_vars_file + _load_vars_file() { + local file="$1" + [ -f "$file" ] || return 0 + msg_info "Loading defaults from ${file}" + local line key val + while IFS= read -r line || [ -n "$line" ]; do + line="${line#"${line%%[![:space:]]*}"}" + line="${line%"${line##*[![:space:]]}"}" + [[ -z "$line" || "$line" == \#* ]] && continue + if [[ "$line" =~ ^([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then + local var_key="${BASH_REMATCH[1]}" + local var_val="${BASH_REMATCH[2]}" + + [[ "$var_key" != var_* ]] && continue + _is_whitelisted_key "$var_key" || { + msg_debug "Ignore non-whitelisted ${var_key}" + continue + } + + # Strip quotes + if [[ "$var_val" =~ ^\"(.*)\"$ ]]; then + var_val="${BASH_REMATCH[1]}" + elif [[ "$var_val" =~ ^\'(.*)\'$ ]]; then + var_val="${BASH_REMATCH[1]}" + fi + + # Unsafe check without regex (formatter-friendly) + local _unsafe="" + case "$var_val" in + *'$('*) _unsafe=1 ;; + *'`'*) _unsafe=1 ;; + *';'*) _unsafe=1 ;; + *'&'*) _unsafe=1 ;; + *'<('*) _unsafe=1 ;; + esac + if [[ -n "$_unsafe" ]]; then + msg_warn "Ignoring ${var_key} from ${file}: unsafe characters" + continue + fi + + # Hard env wins + if [[ -n "${_HARD_ENV[$var_key]:-}" ]]; then + continue + fi + + # Set only if not already exported + if [[ -z "${!var_key+x}" ]]; then + export "${var_key}=${var_val}" + fi + + else + msg_warn "Malformed line in ${file}: ${line}" + fi + + done <"$file" + msg_ok "Loaded ${file}" + } + + # 1) Ensure file exists + _ensure_default_vars + + # 2) Load file + local dv + dv="$(_find_default_vars)" || { + msg_error "default.vars not found after ensure step" + return 1 + } + _load_vars_file "$dv" + + # 3) Map var_verbose → VERBOSE + if [[ -n "${var_verbose:-}" ]]; then + case "${var_verbose,,}" in + 1 | yes | true | on) VERBOSE="yes" ;; + 0 | no | false | off) VERBOSE="no" ;; + *) VERBOSE="${var_verbose}" ;; + esac + else + VERBOSE="no" + fi + + # 4) Apply base settings and show summary + METHOD="mydefaults-global" + base_settings "$VERBOSE" + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using My Defaults (default.vars) on node $PVEHOST_NAME${CL}" + echo_default +} + install_script() { pve_check shell_check @@ -931,23 +1043,27 @@ install_script() { ADVANCED | advanced | 3) CHOICE="3" ;; + DEFAULT_VARS | default_vars | 4) + CHOICE="4" + ;; *) echo -e "\n${CROSS}${RD}Invalid PRESET value: ${PRESET}${CL}\n" exit 1 ;; esac else + #"4" "Use Config File" \ + #"5" "Manage Default Storage" \ while true; do TMP_CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ --title "SETTINGS" \ - --menu "Choose an option:" 20 60 7 \ + --menu "Choose an option:" 20 60 6 \ "1" "Default Settings" \ "2" "Default Settings (with verbose)" \ "3" "Advanced Settings" \ - "4" "Use Config File" \ - "5" "Manage Default Storage" \ - "6" "Diagnostic Settings" \ - "7" "Exit" \ + "4" "My Default Vars" \ + "5" "Diagnostic Settings" \ + "6" "Exit" \ --default-item "1" 3>&1 1>&2 2>&3) || true if [ -z "$TMP_CHOICE" ]; then @@ -984,17 +1100,21 @@ install_script() { base_settings advanced_settings ;; + # 4) + # header_info + # echo -e "${INFO}${HOLD} ${GN}Using Config File on node $PVEHOST_NAME${CL}" + # METHOD="advanced" + # source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/config-file.func) + # config_file + # ;; 4) - header_info - echo -e "${INFO}${HOLD} ${GN}Using Config File on node $PVEHOST_NAME${CL}" - METHOD="advanced" - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/config-file.func) - config_file + # My Defaults (default.vars) + default_var_settings || { + msg_error "Failed to apply default.vars" + exit 1 + } ;; 5) - manage_default_storage - ;; - 6) if [[ $DIAGNOSTICS == "yes" ]]; then if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS SETTINGS" --yesno "Send Diagnostics of LXC Installation?\n\nCurrent setting: ${DIAGNOSTICS}" 10 58 \ --yes-button "No" --no-button "Back"; then @@ -1011,7 +1131,7 @@ install_script() { fi fi ;; - 7) + 6) echo -e "\n${CROSS}${RD}Script terminated. Have a great day!${CL}\n" exit 0 ;; From 9aeb67624d5907a0589163a7f676adc6685f9e34 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 2 Sep 2025 10:54:51 +0200 Subject: [PATCH 0782/1733] dev --- misc/build.func | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/misc/build.func b/misc/build.func index 15151f6eb..88c3395a0 100644 --- a/misc/build.func +++ b/misc/build.func @@ -829,8 +829,8 @@ default_var_settings() { local VAR_WHITELIST=( var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_ctid var_disk var_fuse var_gateway var_hostname var_ipv6_method var_ipv6_static var_mac var_mtu - var_net var_ns var_os var_pw var_ram var_storage var_tags var_tun var_unprivileged - var_verbose var_version var_vlan var_ssh var_ssh_authorized_key + var_net var_ns var_pw var_ram var_storage var_tags var_tun var_unprivileged + var_verbose var_vlan var_ssh var_ssh_authorized_key ) # Snapshot: environment variables (highest precedence) @@ -864,13 +864,12 @@ default_var_settings() { msg_info "No default.vars found. Creating ${canonical}" mkdir -p /usr/local/community-scripts cat >"$canonical" <<'EOF' + cat >"$canonical" <<'EOF' # Community-Scripts defaults (var_* only). Lines starting with # are comments. # Precedence: ENV var_* > default.vars > built-ins. # Keep keys alphabetically sorted. -# CT/OS -var_os=debian -var_version=12 +# Container type var_unprivileged=1 # Resources @@ -894,20 +893,20 @@ var_ipv6_method=auto var_ssh=no # var_ssh_authorized_key= -# APT Cacher +# APT cacher (optional) # var_apt_cacher=yes # var_apt_cacher_ip=192.168.1.10 -# Features/Tags +# Features/Tags/verbosity var_fuse=no var_tun=no var_tags=community-script var_verbose=no -# Security (root PW) – empty => Autologin +# Security (root PW) – empty => autologin # var_pw= -# CTID/Hostname – empty => auto +# Optional fixed CTID/hostname – empty => auto # var_ctid= # var_hostname= EOF From 6525f097b31102ef672877f3a3df6cbb82619941 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 2 Sep 2025 11:07:31 +0200 Subject: [PATCH 0783/1733] Update build.func --- misc/build.func | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/misc/build.func b/misc/build.func index 88c3395a0..ef345f802 100644 --- a/misc/build.func +++ b/misc/build.func @@ -829,8 +829,8 @@ default_var_settings() { local VAR_WHITELIST=( var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_ctid var_disk var_fuse var_gateway var_hostname var_ipv6_method var_ipv6_static var_mac var_mtu - var_net var_ns var_pw var_ram var_storage var_tags var_tun var_unprivileged - var_verbose var_vlan var_ssh var_ssh_authorized_key + var_net var_ns var_pw var_ram var_tags var_tun var_unprivileged + var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage ) # Snapshot: environment variables (highest precedence) @@ -864,7 +864,6 @@ default_var_settings() { msg_info "No default.vars found. Creating ${canonical}" mkdir -p /usr/local/community-scripts cat >"$canonical" <<'EOF' - cat >"$canonical" <<'EOF' # Community-Scripts defaults (var_* only). Lines starting with # are comments. # Precedence: ENV var_* > default.vars > built-ins. # Keep keys alphabetically sorted. @@ -872,6 +871,11 @@ default_var_settings() { # Container type var_unprivileged=1 +# Storage +# Example: "local", "docker", ... +# var_template_storage=local +# var_container_storage=docker + # Resources var_cpu=1 var_disk=4 @@ -880,14 +884,13 @@ var_ram=1024 # Network var_brg=vmbr0 var_net=dhcp -var_ipv6_method=auto +var_ipv6_method=none # var_gateway= # var_ipv6_static= # var_vlan= # var_mtu= # var_mac= # var_ns= -# var_storage= # SSH var_ssh=no From d044aaa785f8e945270e1f9d21efe0570eec1071 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 2 Sep 2025 11:10:00 +0200 Subject: [PATCH 0784/1733] storage preselection --- misc/create_lxc.sh | 95 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 79 insertions(+), 16 deletions(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index f2aa05de3..83b31d1a8 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -58,6 +58,55 @@ function exit_script() { exit 1 } +# Resolve and validate a preselected storage for a given class. +# class: "template" -> requires content=vztmpl +# "container" -> requires content=rootdir +resolve_storage_preselect() { + local class="$1" + local preselect="$2" + local required_content="" + case "$class" in + template) required_content="vztmpl" ;; + container) required_content="rootdir" ;; + *) return 1 ;; + esac + + # No preselect provided + [ -z "$preselect" ] && return 1 + + # Check storage exists and supports required content + if ! pvesm status -content "$required_content" | awk 'NR>1{print $1}' | grep -qx -- "$preselect"; then + msg_warn "Preselected storage '${preselect}' does not support content '${required_content}' (or not found)" + return 1 + fi + + # Build human-readable info string from pvesm status + # Expected columns: Name Type Status Total Used Free ... + local line total used free + line="$(pvesm status | awk -v s="$preselect" 'NR>1 && $1==s {print $0}')" + if [ -z "$line" ]; then + STORAGE_INFO="n/a" + else + total="$(echo "$line" | awk '{print $4}')" + used="$(echo "$line" | awk '{print $5}')" + free="$(echo "$line" | awk '{print $6}')" + # Format bytes to IEC + local total_h used_h free_h + if command -v numfmt >/dev/null 2>&1; then + total_h="$(numfmt --to=iec --suffix=B --format %.1f "$total" 2>/dev/null || echo "$total")" + used_h="$(numfmt --to=iec --suffix=B --format %.1f "$used" 2>/dev/null || echo "$used")" + free_h="$(numfmt --to=iec --suffix=B --format %.1f "$free" 2>/dev/null || echo "$free")" + STORAGE_INFO="Free: ${free_h} Used: ${used_h}" + else + STORAGE_INFO="Free: ${free} Used: ${used}" + fi + fi + + # Set outputs expected by your callers + STORAGE_RESULT="$preselect" + return 0 +} + function check_storage_support() { local CONTENT="$1" local -a VALID_STORAGES=() @@ -214,23 +263,37 @@ if ! check_storage_support "vztmpl"; then exit 1 fi -while true; do - if select_storage template; then - TEMPLATE_STORAGE="$STORAGE_RESULT" - TEMPLATE_STORAGE_INFO="$STORAGE_INFO" - msg_ok "Storage ${BL}$TEMPLATE_STORAGE${CL} ($TEMPLATE_STORAGE_INFO) [Template]" - break - fi -done +# Template storage selection +if resolve_storage_preselect template "${TEMPLATE_STORAGE}"; then + TEMPLATE_STORAGE="$STORAGE_RESULT" + TEMPLATE_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${TEMPLATE_STORAGE}${CL} (${TEMPLATE_STORAGE_INFO}) [Template]" +else + while true; do + if select_storage template; then + TEMPLATE_STORAGE="$STORAGE_RESULT" + TEMPLATE_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${TEMPLATE_STORAGE}${CL} (${TEMPLATE_STORAGE_INFO}) [Template]" + break + fi + done +fi -while true; do - if select_storage container; then - CONTAINER_STORAGE="$STORAGE_RESULT" - CONTAINER_STORAGE_INFO="$STORAGE_INFO" - msg_ok "Storage ${BL}$CONTAINER_STORAGE${CL} ($CONTAINER_STORAGE_INFO) [Container]" - break - fi -done +# Container storage selection +if resolve_storage_preselect container "${CONTAINER_STORAGE}"; then + CONTAINER_STORAGE="$STORAGE_RESULT" + CONTAINER_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${CONTAINER_STORAGE}${CL} (${CONTAINER_STORAGE_INFO}) [Container]" +else + while true; do + if select_storage container; then + CONTAINER_STORAGE="$STORAGE_RESULT" + CONTAINER_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${CONTAINER_STORAGE}${CL} (${CONTAINER_STORAGE_INFO}) [Container]" + break + fi + done +fi # Storage Content Validation msg_info "Validating content types of storage '$CONTAINER_STORAGE'" From 3720e1c6f053db93a7c8189e6bd6d43a3cc569c0 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 2 Sep 2025 11:11:36 +0200 Subject: [PATCH 0785/1733] Update build.func --- misc/build.func | 2 ++ 1 file changed, 2 insertions(+) diff --git a/misc/build.func b/misc/build.func index ef345f802..e203738c7 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1285,6 +1285,8 @@ build_container() { -unprivileged $CT_TYPE $PW " + export TEMPLATE_STORAGE="${var_template_storage:-}" + export CONTAINER_STORAGE="${var_container_storage:-}" bash -c "$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/create_lxc.sh)" || exit if [ $? -ne 0 ]; then exit 200 From 5ebb011f356d9381fb13ef48b7244e31d0405dc5 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 2 Sep 2025 11:21:32 +0200 Subject: [PATCH 0786/1733] Update build.func --- misc/build.func | 187 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) diff --git a/misc/build.func b/misc/build.func index e203738c7..5e84c2267 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1016,6 +1016,192 @@ EOF echo_default } +get_app_defaults_path() { + local n="${NSAPP:-${APP,,}}" + echo "/usr/local/community-scripts/defaults/${n}.vars" +} + +# ------------------------------------------------------------------------------ +# maybe_offer_save_app_defaults +# +# - Called after advanced_settings returned with fully chosen values. +# - If no .vars exists, offers to persist current advanced settings +# into /usr/local/community-scripts/defaults/.vars +# - Only writes whitelisted var_* keys. +# - Extracts raw values from flags like ",gw=..." ",mtu=..." etc. +# ------------------------------------------------------------------------------ +maybe_offer_save_app_defaults() { + local app_vars_path + app_vars_path="$(get_app_defaults_path)" + + # Only offer if file does not exist yet + if [ -f "$app_vars_path" ]; then + return 0 + fi + + # Ask user (English prompt as requested) + if ! whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --yesno "Save these advanced settings as defaults for ${APP}?\n\nThis will create:\n${app_vars_path}" 12 72; then + return 0 + fi + + # Ensure directory exists + mkdir -p "$(dirname "$app_vars_path")" + + # Normalizers (extract raw values from flags used during building) + local _val + + # NET/GATE: NET is either 'dhcp' or a CIDR; GATE holds ',gw=IP' or '' + local _net="${NET:-}" + local _gate="" + if [[ "${GATE:-}" =~ ^,gw= ]]; then + _gate="${GATE#,gw=}" + fi + + # IPv6: method + optional static + optional gateway + local _ipv6_method="${IPV6_METHOD:-auto}" + local _ipv6_static="" + local _ipv6_gateway="" + case "$_ipv6_method" in + static) + _ipv6_static="${IPV6_ADDR:-}" + _ipv6_gateway="${IPV6_GATE:-}" + ;; + esac + + # MTU: MTU looks like ',mtu=1500' or '' + local _mtu="" + if [[ "${MTU:-}" =~ ^,mtu= ]]; then + _mtu="${MTU#,mtu=}" + fi + + # VLAN: ',tag=NN' or '' + local _vlan="" + if [[ "${VLAN:-}" =~ ^,tag= ]]; then + _vlan="${VLAN#,tag=}" + fi + + # MAC: ',hwaddr=XX:XX:...' or '' + local _mac="" + if [[ "${MAC:-}" =~ ^,hwaddr= ]]; then + _mac="${MAC#,hwaddr=}" + fi + + # DNS nameserver: NS is like '-nameserver=IP' or '' + local _ns="" + if [[ "${NS:-}" =~ ^-nameserver= ]]; then + _ns="${NS#-nameserver=}" + fi + + # Search domain: SD is like '-searchdomain=foo' or '' + local _searchdomain="" + if [[ "${SD:-}" =~ ^-searchdomain= ]]; then + _searchdomain="${SD#-searchdomain=}" + fi + + # Authorized key: raw string already + local _ssh_auth="${SSH_AUTHORIZED_KEY:-}" + + # SSH enabled: "yes"/"no" + local _ssh="${SSH:-no}" + + # APT cacher + local _apt_cacher="${APT_CACHER:-}" + local _apt_cacher_ip="${APT_CACHER_IP:-}" + + # Features + local _fuse="${ENABLE_FUSE:-no}" + local _tun="${ENABLE_TUN:-no}" + + # Tags: TAGS may include 'community-script;' etc. Keep as-is unless empty + local _tags="${TAGS:-}" + + # Unprivileged container type: CT_TYPE is "1" (unpriv) or "0" (priv) + local _unpriv="${CT_TYPE:-1}" + + # Resources and names + local _cpu="${CORE_COUNT:-1}" + local _ram="${RAM_SIZE:-1024}" + local _disk="${DISK_SIZE:-4}" + local _hostname="${HN:-$NSAPP}" + + # Verbose + local _verbose="${VERBOSE:-no}" + + # Optional storages if already known in this phase + local _tpl_storage="${TEMPLATE_STORAGE:-}" + local _ct_storage="${CONTAINER_STORAGE:-}" + + # Sanitize function for values (basic safety for config file) + _sanitize_value() { + local s="$1" + # Disallow backticks, $(), <(), ;, & + case "$s" in + *'$('* | *'`'* | *';'* | *'&'* | *'<('*) + echo "" + ;; + *) + echo "$s" + ;; + esac + } + + # Build the file content + { + echo "# App-specific defaults for ${APP} (${NSAPP})" + echo "# Generated on $(date -u '+%Y-%m-%dT%H:%M:%SZ')" + echo "# Only var_* keys are read by the loader." + echo + + # Container type + echo "var_unprivileged=$(_sanitize_value "$_unpriv")" + + # Resources + echo "var_cpu=$(_sanitize_value "$_cpu")" + echo "var_ram=$(_sanitize_value "$_ram")" + echo "var_disk=$(_sanitize_value "$_disk")" + + # Network + [ -n "$BRG" ] && echo "var_brg=$(_sanitize_value "$BRG")" + [ -n "$_net" ] && echo "var_net=$(_sanitize_value "$_net")" + [ -n "$_gate" ] && echo "var_gateway=$(_sanitize_value "$_gate")" + [ -n "$_mtu" ] && echo "var_mtu=$(_sanitize_value "$_mtu")" + [ -n "$_vlan" ] && echo "var_vlan=$(_sanitize_value "$_vlan")" + [ -n "$_mac" ] && echo "var_mac=$(_sanitize_value "$_mac")" + [ -n "$_ns" ] && echo "var_ns=$(_sanitize_value "$_ns")" + + # IPv6 + [ -n "$_ipv6_method" ] && echo "var_ipv6_method=$(_sanitize_value "$_ipv6_method")" + [ -n "$_ipv6_static" ] && echo "var_ipv6_static=$(_sanitize_value "$_ipv6_static")" + # Note: we do not persist a dedicated var for IPv6 gateway; can be derived if needed + + # SSH + [ -n "$_ssh" ] && echo "var_ssh=$(_sanitize_value "$_ssh")" + [ -n "$_ssh_auth" ] && echo "var_ssh_authorized_key=$(_sanitize_value "$_ssh_auth")" + + # APT cacher + [ -n "$_apt_cacher" ] && echo "var_apt_cacher=$(_sanitize_value "$_apt_cacher")" + [ -n "$_apt_cacher_ip" ] && echo "var_apt_cacher_ip=$(_sanitize_value "$_apt_cacher_ip")" + + # Features / tags / verbosity + [ -n "$_fuse" ] && echo "var_fuse=$(_sanitize_value "$_fuse")" + [ -n "$_tun" ] && echo "var_tun=$(_sanitize_value "$_tun")" + [ -n "$_tags" ] && echo "var_tags=$(_sanitize_value "$_tags")" + [ -n "$_verbose" ] && echo "var_verbose=$(_sanitize_value "$_verbose")" + + # Identity (optional) + [ -n "$_hostname" ] && echo "var_hostname=$(_sanitize_value "$_hostname")" + [ -n "$_searchdomain" ] && echo "var_searchdomain=$(_sanitize_value "$_searchdomain")" + + # Storage (optional, if known at this stage) + [ -n "$_tpl_storage" ] && echo "var_template_storage=$(_sanitize_value "$_tpl_storage")" + [ -n "$_ct_storage" ] && echo "var_container_storage=$(_sanitize_value "$_ct_storage")" + } >"$app_vars_path" + + chmod 0644 "$app_vars_path" + msg_ok "Saved app defaults: ${app_vars_path}" +} + install_script() { pve_check shell_check @@ -1101,6 +1287,7 @@ install_script() { METHOD="advanced" base_settings advanced_settings + maybe_offer_save_app_defaults ;; # 4) # header_info From 36d2b0fb7f883c2969ae9d7d6a2c3bd5a6f9d9d8 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 2 Sep 2025 20:33:44 +0200 Subject: [PATCH 0787/1733] fix docker formatting --- vm/docker-vm.sh | 730 ++++++++++++++++++++++++------------------------ 1 file changed, 361 insertions(+), 369 deletions(-) diff --git a/vm/docker-vm.sh b/vm/docker-vm.sh index 67455134c..a396bac53 100644 --- a/vm/docker-vm.sh +++ b/vm/docker-vm.sh @@ -2,13 +2,15 @@ # Copyright (c) 2021-2025 community-scripts ORG # Author: thost96 (thost96) | Co-Author: michelroegl-brunner +# Refactor (q35 + PVE9 virt-customize network fix): MickLesk # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +set -e source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) function header_info() { - clear - cat <<"EOF" + clear + cat <<"EOF" ____ __ _ ____ ___ / __ \____ _____/ /_____ _____ | | / / |/ / / / / / __ \/ ___/ //_/ _ \/ ___/ | | / / /|_/ / @@ -19,6 +21,8 @@ EOF } header_info echo -e "\n Loading..." + +# ---------- Globals ---------- GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" METHOD="" @@ -29,13 +33,10 @@ DISK_SIZE="10G" YW=$(echo "\033[33m") BL=$(echo "\033[36m") -HA=$(echo "\033[1;34m") RD=$(echo "\033[01;31m") BGN=$(echo "\033[4;92m") GN=$(echo "\033[1;92m") DGN=$(echo "\033[32m") -CL=$(echo "\033[m") - CL=$(echo "\033[m") BOLD=$(echo "\033[1m") BFR="\\r\\033[K" @@ -59,349 +60,276 @@ MACADDRESS="${TAB}🔗${TAB}${CL}" VLANTAG="${TAB}🏷️${TAB}${CL}" CREATING="${TAB}🚀${TAB}${CL}" ADVANCED="${TAB}🧩${TAB}${CL}" +CLOUD="${TAB}☁️${TAB}${CL}" + THIN="discard=on,ssd=1," -set -e + trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap cleanup EXIT trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM + function error_handler() { - local exit_code="$?" - local line_number="$1" - local command="$2" - post_update_to_api "failed" "${command}" - local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" - echo -e "\n$error_message\n" - cleanup_vmid + local exit_code="$?" + local line_number="$1" + local command="$2" + local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" + post_update_to_api "failed" "${command}" + echo -e "\n$error_message\n" + cleanup_vmid } function get_valid_nextid() { - local try_id - try_id=$(pvesh get /cluster/nextid) - while true; do - if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then - try_id=$((try_id + 1)) - continue - fi - if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then - try_id=$((try_id + 1)) - continue - fi - break - done - echo "$try_id" + local try_id + try_id=$(pvesh get /cluster/nextid) + while true; do + if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then + try_id=$((try_id + 1)) + continue + fi + if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then + try_id=$((try_id + 1)) + continue + fi + break + done + echo "$try_id" } function cleanup_vmid() { - if qm status $VMID &>/dev/null; then - qm stop $VMID &>/dev/null - qm destroy $VMID &>/dev/null - fi + if qm status $VMID &>/dev/null; then + qm stop $VMID &>/dev/null || true + qm destroy $VMID &>/dev/null || true + fi } function cleanup() { - popd >/dev/null - post_update_to_api "done" "none" - rm -rf $TEMP_DIR + popd >/dev/null || true + post_update_to_api "done" "none" + rm -rf "$TEMP_DIR" } TEMP_DIR=$(mktemp -d) -pushd $TEMP_DIR >/dev/null -if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Docker VM" --yesno "This will create a New Docker VM. Proceed?" 10 58; then - : -else - header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit +pushd "$TEMP_DIR" >/dev/null + +if ! whiptail --backtitle "Proxmox VE Helper Scripts" --title "Docker VM" --yesno "This will create a New Docker VM. Proceed?" 10 58; then + header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit fi -function msg_info() { - local msg="$1" - echo -ne "${TAB}${YW}${HOLD}${msg}${HOLD}" -} - -function msg_ok() { - local msg="$1" - echo -e "${BFR}${CM}${GN}${msg}${CL}" -} - -function msg_error() { - local msg="$1" - echo -e "${BFR}${CROSS}${RD}${msg}${CL}" -} +function msg_info() { echo -ne "${TAB}${YW}${HOLD}$1${HOLD}"; } +function msg_ok() { echo -e "${BFR}${CM}${GN}$1${CL}"; } +function msg_error() { echo -e "${BFR}${CROSS}${RD}$1${CL}"; } function check_root() { - if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then - clear - msg_error "Please run this script as root." - echo -e "\nExiting..." - sleep 2 - exit - fi + if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then + clear + msg_error "Please run this script as root." + echo -e "\nExiting..." + sleep 2 + exit + fi } -function pve_check() { - if ! pveversion | grep -Eq "pve-manager/8\.[1-4](\.[0-9]+)*"; then - msg_error "${CROSS}${RD}This version of Proxmox Virtual Environment is not supported" - echo -e "Requires Proxmox Virtual Environment Version 8.1 or later." - echo -e "Exiting..." - sleep 2 - exit - fi +# Supported: Proxmox VE 8.0.x – 8.9.x and 9.0 (NOT 9.1+) +pve_check() { + local PVE_VER + PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" + if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then + local MINOR="${BASH_REMATCH[1]}" + ((MINOR >= 0 && MINOR <= 9)) && return 0 + msg_error "This version of Proxmox VE is not supported." + exit 1 + fi + if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then + local MINOR="${BASH_REMATCH[1]}" + ((MINOR == 0)) && return 0 + msg_error "This version of Proxmox VE is not yet supported (9.1+)." + exit 1 + fi + msg_error "This version of Proxmox VE is not supported (need 8.x or 9.0)." + exit 1 } function arch_check() { - if [ "$(dpkg --print-architecture)" != "amd64" ]; then - echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" - echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" - echo -e "Exiting..." - sleep 2 - exit - fi + if [ "$(dpkg --print-architecture)" != "amd64" ]; then + echo -e "\n ${INFO}This script will not work with PiMox! \n" + echo -e "\n Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" + echo -e "Exiting..." + sleep 2 + exit + fi } function ssh_check() { - if command -v pveversion >/dev/null 2>&1; then - if [ -n "${SSH_CLIENT:+x}" ]; then - if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Would you like to proceed with using SSH?" 10 62; then - echo "you've been warned" - else - clear - exit - fi + if command -v pveversion >/dev/null 2>&1 && [ -n "${SSH_CLIENT:+x}" ]; then + if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Proceed anyway?" 10 62; then + : + else + clear + exit + fi fi - fi } function exit-script() { - clear - echo -e "\n${CROSS}${RD}User exited script${CL}\n" - exit + clear + echo -e "\n${CROSS}${RD}User exited script${CL}\n" + exit } function default_settings() { - VMID=$(get_valid_nextid) - FORMAT=",efitype=4m" - MACHINE="" - DISK_CACHE="" - DISK_SIZE="8G" - HN="docker" - CPU_TYPE="" - CORE_COUNT="2" - RAM_SIZE="4096" - BRG="vmbr0" - MAC="$GEN_MAC" - VLAN="" - MTU="" - START_VM="yes" - METHOD="default" - echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" - echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" - echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" - echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" - echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" - echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" - echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" - echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" - echo -e "${CREATING}${BOLD}${DGN}Creating a Docker VM using the above default settings${CL}" + VMID=$(get_valid_nextid) + FORMAT=",efitype=4m" + DISK_CACHE="" + DISK_SIZE="10G" + HN="docker" + CPU_TYPE="" + CORE_COUNT="2" + RAM_SIZE="4096" + BRG="vmbr0" + MAC="$GEN_MAC" + VLAN="" + MTU="" + START_VM="yes" + METHOD="default" + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}q35${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" + echo -e "${CREATING}${BOLD}${DGN}Creating a Docker VM using the above default settings${CL}" } function advanced_settings() { - METHOD="advanced" - [ -z "${VMID:-}" ] && VMID=$(get_valid_nextid) - while true; do - if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - if [ -z "$VMID" ]; then - VMID=$(get_valid_nextid) - fi - if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then - echo -e "${CROSS}${RD} ID $VMID is already in use${CL}" - sleep 2 - continue - fi - echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" - break + METHOD="advanced" + [ -z "${VMID:-}" ] && VMID=$(get_valid_nextid) + while true; do + if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + [ -z "$VMID" ] && VMID=$(get_valid_nextid) + if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then + echo -e "${CROSS}${RD} ID $VMID is already in use${CL}" + sleep 2 + continue + fi + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" + break + else exit-script; fi + done + + # Force q35 like our other scripts + FORMAT=",efitype=4m" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}q35${CL}" + + if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') + if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then DISK_SIZE="${DISK_SIZE}G"; fi + [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]] || { + echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size.${CL}" + exit-script + } + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" + else exit-script; fi + + if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ + "0" "None (Default)" ON "1" "Write Through" OFF 3>&1 1>&2 2>&3); then + if [ "$DISK_CACHE" = "1" ]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" + DISK_CACHE="cache=writethrough," + else + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" + DISK_CACHE="" + fi + else exit-script; fi + + if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 docker --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z "$VM_NAME" ]; then HN="docker"; else HN=$(echo ${VM_NAME,,} | tr -d ' '); fi + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" + else exit-script; fi + + if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ + "0" "KVM64 (Default)" ON "1" "Host" OFF 3>&1 1>&2 2>&3); then + if [ "$CPU_TYPE1" = "1" ]; then + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" + CPU_TYPE=" -cpu host" + else + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" + CPU_TYPE="" + fi + else exit-script; fi + + if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + [ -z "$CORE_COUNT" ] && CORE_COUNT="2" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + else exit-script; fi + + if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + [ -z "$RAM_SIZE" ] && RAM_SIZE="2048" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" + else exit-script; fi + + if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + [ -z "$BRG" ] && BRG="vmbr0" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + else exit-script; fi + + if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z "$MAC1" ]; then MAC="$GEN_MAC"; else MAC="$MAC1"; fi + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" + else exit-script; fi + + if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z "$VLAN1" ]; then + VLAN1="Default" + VLAN="" + else VLAN=",tag=$VLAN1"; fi + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" + else exit-script; fi + + if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z "$MTU1" ]; then + MTU1="Default" + MTU="" + else MTU=",mtu=$MTU1"; fi + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" + else exit-script; fi + + if whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58; then + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" + START_VM="yes" else - exit-script + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}" + START_VM="no" fi - done - if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \ - "i440fx" "Machine i440fx" ON \ - "q35" "Machine q35" OFF \ - 3>&1 1>&2 2>&3); then - if [ $MACH = q35 ]; then - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" - FORMAT="" - MACHINE=" -machine q35" + if whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Docker VM?" --no-button Do-Over 10 58; then + echo -e "${CREATING}${BOLD}${DGN}Creating a Docker VM using the above advanced settings${CL}" else - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" - FORMAT=",efitype=4m" - MACHINE="" + header_info + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" + advanced_settings fi - else - exit-script - fi - - if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') - if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then - DISK_SIZE="${DISK_SIZE}G" - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" - elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" - else - echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}" - exit-script - fi - else - exit-script - fi - - if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ - "0" "None (Default)" ON \ - "1" "Write Through" OFF \ - 3>&1 1>&2 2>&3); then - if [ $DISK_CACHE = "1" ]; then - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" - DISK_CACHE="cache=writethrough," - else - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" - DISK_CACHE="" - fi - else - exit-script - fi - - if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 docker --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - if [ -z $VM_NAME ]; then - HN="docker" - echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" - else - HN=$(echo ${VM_NAME,,} | tr -d ' ') - echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" - fi - else - exit-script - fi - - if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ - "0" "KVM64 (Default)" ON \ - "1" "Host" OFF \ - 3>&1 1>&2 2>&3); then - if [ $CPU_TYPE1 = "1" ]; then - echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" - CPU_TYPE=" -cpu host" - else - echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" - CPU_TYPE="" - fi - else - exit-script - fi - - if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - if [ -z $CORE_COUNT ]; then - CORE_COUNT="2" - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" - else - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" - fi - else - exit-script - fi - - if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - if [ -z $RAM_SIZE ]; then - RAM_SIZE="2048" - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" - else - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" - fi - else - exit-script - fi - - if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - if [ -z $BRG ]; then - BRG="vmbr0" - echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" - else - echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" - fi - else - exit-script - fi - - if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - if [ -z $MAC1 ]; then - MAC="$GEN_MAC" - echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" - else - MAC="$MAC1" - echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" - fi - else - exit-script - fi - - if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - if [ -z $VLAN1 ]; then - VLAN1="Default" - VLAN="" - echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" - else - VLAN=",tag=$VLAN1" - echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" - fi - else - exit-script - fi - - if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - if [ -z $MTU1 ]; then - MTU1="Default" - MTU="" - echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" - else - MTU=",mtu=$MTU1" - echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" - fi - else - exit-script - fi - - if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then - echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" - START_VM="yes" - else - echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}" - START_VM="no" - fi - - if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Docker VM?" --no-button Do-Over 10 58); then - echo -e "${CREATING}${BOLD}${DGN}Creating a Docker VM using the above advanced settings${CL}" - else - header_info - echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" - advanced_settings - fi } function start_script() { - if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then - header_info - echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}" - default_settings - else - header_info - echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" - advanced_settings - fi + if whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58; then + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}" + default_settings + else + header_info + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" + advanced_settings + fi } + check_root arch_check pve_check @@ -409,103 +337,165 @@ ssh_check start_script post_to_api_vm +# ---------- Storage selection ---------- msg_info "Validating Storage" while read -r line; do - TAG=$(echo $line | awk '{print $1}') - TYPE=$(echo $line | awk '{printf "%-10s", $2}') - FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') - ITEM=" Type: $TYPE Free: $FREE " - OFFSET=2 - if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then - MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) - fi - STORAGE_MENU+=("$TAG" "$ITEM" "OFF") + TAG=$(echo $line | awk '{print $1}') + TYPE=$(echo $line | awk '{printf "%-10s", $2}') + FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') + ITEM=" Type: $TYPE Free: $FREE " + OFFSET=2 + if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then + MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) + fi + STORAGE_MENU+=("$TAG" "$ITEM" "OFF") done < <(pvesm status -content images | awk 'NR>1') VALID=$(pvesm status -content images | awk 'NR>1') if [ -z "$VALID" ]; then - msg_error "Unable to detect a valid storage location." - exit + msg_error "Unable to detect a valid storage location." + exit elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then - STORAGE=${STORAGE_MENU[0]} + STORAGE=${STORAGE_MENU[0]} else - while [ -z "${STORAGE:+x}" ]; do - STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ - "Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \ - 16 $(($MSG_MAX_LENGTH + 23)) 6 \ - "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) - done + while [ -z "${STORAGE:+x}" ]; do + STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ + "Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \ + 16 $(($MSG_MAX_LENGTH + 23)) 6 \ + "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) + done fi msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." + +# ---------- Download Debian Cloud Image ---------- msg_info "Retrieving the URL for the Debian 12 Qcow2 Disk Image" URL="https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-nocloud-$(dpkg --print-architecture).qcow2" -sleep 2 +sleep 1 msg_ok "${CL}${BL}${URL}${CL}" curl -f#SL -o "$(basename "$URL")" "$URL" echo -en "\e[1A\e[0K" -FILE=$(basename $URL) +FILE=$(basename "$URL") msg_ok "Downloaded ${CL}${BL}${FILE}${CL}" -STORAGE_TYPE=$(pvesm status -storage "$STORAGE" | awk 'NR>1 {print $2}') -case $STORAGE_TYPE in -nfs | dir) - DISK_EXT=".qcow2" - DISK_REF="$VMID/" - DISK_IMPORT="-format qcow2" - THIN="" - ;; -btrfs) - DISK_EXT=".raw" - DISK_REF="$VMID/" - DISK_IMPORT="-format raw" - FORMAT=",efitype=4m" - THIN="" - ;; -esac -for i in {0,1}; do - disk="DISK$i" - eval DISK${i}=vm-${VMID}-disk-${i}${DISK_EXT:-} - eval DISK${i}_REF=${STORAGE}:${DISK_REF:-}${!disk} -done - +# ---------- Ensure libguestfs-tools ---------- if ! command -v virt-customize &>/dev/null; then - msg_info "Installing Pre-Requisite libguestfs-tools onto Host" - apt-get -qq update >/dev/null - apt-get -qq install libguestfs-tools lsb-release -y >/dev/null - msg_ok "Installed libguestfs-tools successfully" + msg_info "Installing libguestfs-tools on host" + apt-get -qq update >/dev/null + apt-get -qq install -y libguestfs-tools lsb-release >/dev/null + msg_ok "Installed libguestfs-tools" fi -msg_info "Adding UniFi OS Server Installer to Debian 12 Qcow2 Disk Image" -UOS_VERSION="4.2.23" -UOS_URL="https://fw-download.ubnt.com/data/unifi-os-server/8b93-linux-x64-4.2.23-158fa00b-6b2c-4cd8-94ea-e92bc4a81369.23-x64" -UOS_INSTALLER="unifi-os-server-${UOS_VERSION}.bin" +# ---------- First-boot Docker installer (avoids network in virt-customize on PVE9) ---------- +msg_info "Preparing first-boot Docker install (inside guest)" +mkdir -p firstboot +cat >firstboot/firstboot-docker.sh <<'EOSH' +#!/usr/bin/env bash +set -e +LOG=/var/log/firstboot-docker.log +exec >>"$LOG" 2>&1 + +touch /var/lib/firstboot/.running +export DEBIAN_FRONTEND=noninteractive + +apt-get update -qq +apt-get install -y ca-certificates curl gnupg qemu-guest-agent apt-transport-https software-properties-common lsb-release + +install -m 0755 -d /etc/apt/keyrings +curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg +chmod a+r /etc/apt/keyrings/docker.gpg +echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian bookworm stable" >/etc/apt/sources.list.d/docker.list + +apt-get update -qq +apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin + +systemctl enable --now qemu-guest-agent || true +systemctl enable --now docker + +mkdir -p /var/lib/firstboot +date > /var/lib/firstboot/docker.done +rm -f /var/lib/firstboot/.running +EOSH +chmod +x firstboot/firstboot-docker.sh + +cat >firstboot/firstboot-docker.service <<'EOUNIT' +[Unit] +Description=First boot: install Docker & QGA +After=network-online.target +Wants=network-online.target +ConditionPathExists=!/var/lib/firstboot/docker.done + +[Service] +Type=oneshot +ExecStart=/usr/local/sbin/firstboot-docker.sh +RemainAfterExit=no + +[Install] +WantedBy=multi-user.target +EOUNIT + +# hostname + machine-id reset for cloud-init style behaviour +echo "$HN" >firstboot/hostname + +# Inject files without requiring guest network virt-customize -q -a "${FILE}" \ - --install qemu-guest-agent,ca-certificates,curl,lsb-release,podman \ - --run-command "curl -fsSL '${UOS_URL}' -o /root/${UOS_INSTALLER} && chmod +x /root/${UOS_INSTALLER}" >/dev/null - -msg_ok "Added UniFi OS Server Installer to Debian 12 Qcow2 Disk Image successfully" + --copy-in firstboot/firstboot-docker.sh:/usr/local/sbin \ + --copy-in firstboot/firstboot-docker.service:/etc/systemd/system \ + --copy-in firstboot/hostname:/etc \ + --run-command "chmod +x /usr/local/sbin/firstboot-docker.sh" \ + --run-command "systemctl enable firstboot-docker.service" \ + --run-command "echo -n > /etc/machine-id" \ + --run-command "truncate -s 0 /etc/hostname && mv /etc/hostname /etc/hostname.orig && echo '${HN}' >/etc/hostname" >/dev/null +msg_ok "Prepared first-boot installer & hostname" +# ---------- Expand partition offline ---------- msg_info "Expanding root partition to use full disk space" qemu-img create -f qcow2 expanded.qcow2 ${DISK_SIZE} >/dev/null 2>&1 virt-resize --expand /dev/sda1 ${FILE} expanded.qcow2 >/dev/null 2>&1 mv expanded.qcow2 ${FILE} >/dev/null 2>&1 msg_ok "Expanded image to full size" -msg_info "Creating a Docker VM" -qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \ - -name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci -pvesm alloc $STORAGE $VMID $DISK0 4M 1>&/dev/null -qm importdisk $VMID ${FILE} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null -qm set $VMID \ - -efidisk0 ${DISK0_REF}${FORMAT} \ - -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ - -boot order=scsi0 \ - -serial0 socket >/dev/null -qm resize $VMID scsi0 8G >/dev/null -qm set $VMID --agent enabled=1 >/dev/null +# ---------- Create VM shell (q35) ---------- +msg_info "Creating a Docker VM shell" +qm create "$VMID" -machine q35 -bios ovmf -agent 1 -tablet 0 -localtime 1 ${CPU_TYPE} \ + -cores "$CORE_COUNT" -memory "$RAM_SIZE" -name "$HN" -tags community-script \ + -net0 "virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU" -onboot 1 -ostype l26 -scsihw virtio-scsi-pci >/dev/null +msg_ok "Created VM shell" +# ---------- Import disk ---------- +msg_info "Importing disk into storage ($STORAGE)" +if qm disk import --help >/dev/null 2>&1; then + IMPORT_CMD=(qm disk import) +else + IMPORT_CMD=(qm importdisk) +fi +IMPORT_OUT="$("${IMPORT_CMD[@]}" "$VMID" "${FILE}" "$STORAGE" --format qcow2 2>&1 || true)" +DISK_REF="$(printf '%s\n' "$IMPORT_OUT" | sed -n "s/.*successfully imported disk '\([^']\+\)'.*/\1/p" | tr -d "\r\"'")" +[[ -z "$DISK_REF" ]] && DISK_REF="$(pvesm list "$STORAGE" | awk -v id="$VMID" '$5 ~ ("vm-"id"-disk-") {print $1":"$5}' | sort | tail -n1)" +[[ -z "$DISK_REF" ]] && { + msg_error "Unable to determine imported disk reference." + echo "$IMPORT_OUT" + exit 1 +} +msg_ok "Imported disk (${CL}${BL}${DISK_REF}${CL})" + +# ---------- Attach EFI + root disk ---------- +msg_info "Attaching EFI and root disk" +qm set "$VMID" \ + --efidisk0 "${STORAGE}:0${FORMAT}" \ + --scsi0 "${DISK_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE}" \ + --boot order=scsi0 \ + --serial0 socket >/dev/null +qm set "$VMID" --agent enabled=1 >/dev/null +msg_ok "Attached EFI and root disk" + +# ---------- Ensure final size (PVE layer) ---------- +msg_info "Resizing disk to $DISK_SIZE (PVE layer)" +qm resize "$VMID" scsi0 "${DISK_SIZE}" >/dev/null || true +msg_ok "Resized disk" + +# ---------- Description ---------- DESCRIPTION=$( - cat < Logo @@ -537,10 +527,12 @@ EOF qm set "$VMID" -description "$DESCRIPTION" >/dev/null msg_ok "Created a Docker VM ${CL}${BL}(${HN})" + if [ "$START_VM" == "yes" ]; then - msg_info "Starting Docker VM" - qm start $VMID - msg_ok "Started Docker VM" + msg_info "Starting Docker VM" + qm start $VMID + msg_ok "Started Docker VM" fi + post_update_to_api "done" "none" msg_ok "Completed Successfully!\n" From ed6dad511e97b4ca6b46b45e67b732b6443aa7ca Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 2 Sep 2025 20:47:08 +0200 Subject: [PATCH 0788/1733] Update docker-vm.sh --- vm/docker-vm.sh | 143 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 114 insertions(+), 29 deletions(-) diff --git a/vm/docker-vm.sh b/vm/docker-vm.sh index a396bac53..6655c7e64 100644 --- a/vm/docker-vm.sh +++ b/vm/docker-vm.sh @@ -337,6 +337,48 @@ ssh_check start_script post_to_api_vm +function choose_os() { + if OS_CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "Choose Base OS" \ + --radiolist "Select the OS for the Docker VM:" 12 60 3 \ + "debian12" "Debian 12 (Bookworm, stable & best for scripts)" ON \ + "debian13" "Debian 13 (Trixie, newer, but repos lag)" OFF \ + "ubuntu24" "Ubuntu 24.04 LTS (modern kernel, GPU/AI friendly)" OFF \ + 3>&1 1>&2 2>&3); then + case "$OS_CHOICE" in + debian12) + var_os="debian" + var_version="12" + URL="https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-nocloud-$(dpkg --print-architecture).qcow2" + ;; + debian13) + var_os="debian" + var_version="13" + URL="https://cloud.debian.org/images/cloud/trixie/latest/debian-13-nocloud-$(dpkg --print-architecture).qcow2" + ;; + ubuntu24) + var_os="ubuntu" + var_version="24.04" + URL="https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-$(dpkg --print-architecture).img" + ;; + esac + echo -e "${OS}${BOLD}${DGN}Selected OS: ${BGN}${OS_CHOICE}${CL}" + else + exit-script + fi +} + +PVE_VER=$(pveversion | awk -F'/' '{print $2}' | cut -d'-' -f1 | cut -d'.' -f1) + +if [ "$PVE_VER" -eq 8 ]; then + INSTALL_MODE="direct" +elif [ "$PVE_VER" -eq 9 ]; then + INSTALL_MODE="firstboot" +else + msg_error "Unsupported Proxmox VE version: $PVE_VER" + exit 1 +fi + # ---------- Storage selection ---------- msg_info "Validating Storage" while read -r line; do @@ -368,12 +410,9 @@ msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." # ---------- Download Debian Cloud Image ---------- -msg_info "Retrieving the URL for the Debian 12 Qcow2 Disk Image" -URL="https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-nocloud-$(dpkg --print-architecture).qcow2" -sleep 1 -msg_ok "${CL}${BL}${URL}${CL}" +choose_os +msg_info "Retrieving Cloud Image for $var_os $var_version" curl -f#SL -o "$(basename "$URL")" "$URL" -echo -en "\e[1A\e[0K" FILE=$(basename "$URL") msg_ok "Downloaded ${CL}${BL}${FILE}${CL}" @@ -385,25 +424,72 @@ if ! command -v virt-customize &>/dev/null; then msg_ok "Installed libguestfs-tools" fi -# ---------- First-boot Docker installer (avoids network in virt-customize on PVE9) ---------- -msg_info "Preparing first-boot Docker install (inside guest)" -mkdir -p firstboot -cat >firstboot/firstboot-docker.sh <<'EOSH' +# ---------- Decide distro codename & Docker repo base from chosen URL ---------- +# (choose_os must have set $URL and we've downloaded $FILE above; we only need URL to derive codename) +if [[ "$URL" == *"/bookworm/"* || "$FILE" == *"debian-12-"* ]]; then + CODENAME="bookworm" + DOCKER_BASE="https://download.docker.com/linux/debian" +elif [[ "$URL" == *"/trixie/"* || "$FILE" == *"debian-13-"* ]]; then + CODENAME="trixie" + DOCKER_BASE="https://download.docker.com/linux/debian" +elif [[ "$URL" == *"/noble/"* || "$FILE" == *"noble-"* ]]; then + CODENAME="noble" + DOCKER_BASE="https://download.docker.com/linux/ubuntu" +else + CODENAME="bookworm" + DOCKER_BASE="https://download.docker.com/linux/debian" +fi + +# ---------- Detect PVE major version and select install mode ---------- +PVE_MAJ=$(pveversion | awk -F'/' '{print $2}' | cut -d'-' -f1 | cut -d'.' -f1) +if [ "$PVE_MAJ" -eq 8 ]; then + INSTALL_MODE="direct" # fast path: install Docker directly into image +else + INSTALL_MODE="firstboot" # robust path for PVE9: install Docker at first boot inside guest +fi + +# ---------- Optional: allow manual override ---------- +if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Docker Installation Mode" \ + --yesno "Detected PVE ${PVE_MAJ}. Use ${INSTALL_MODE^^} mode?\n\nYes = ${INSTALL_MODE^^}\nNo = Switch to the other mode" 11 70; then + : # keep detected mode +else + if [ "$INSTALL_MODE" = "direct" ]; then INSTALL_MODE="firstboot"; else INSTALL_MODE="direct"; fi +fi + +# ---------- PVE8: Direct install into image via virt-customize ---------- +if [ "$INSTALL_MODE" = "direct" ]; then + msg_info "Injecting Docker directly into image (${CODENAME}, $(basename "$DOCKER_BASE"))" + virt-customize -q -a "${FILE}" \ + --install qemu-guest-agent,apt-transport-https,ca-certificates,curl,gnupg,lsb-release \ + --run-command "install -m 0755 -d /etc/apt/keyrings" \ + --run-command "curl -fsSL ${DOCKER_BASE}/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg" \ + --run-command "chmod a+r /etc/apt/keyrings/docker.gpg" \ + --run-command "echo 'deb [arch=\$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] ${DOCKER_BASE} ${CODENAME} stable' > /etc/apt/sources.list.d/docker.list" \ + --run-command "apt-get update -qq" \ + --run-command "apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin" \ + --run-command "systemctl enable docker" \ + --run-command "systemctl enable qemu-guest-agent" >/dev/null + msg_ok "Docker injected into image" +fi + +# ---------- PVE9: First-boot installer inside guest ---------- +if [ "$INSTALL_MODE" = "firstboot" ]; then + msg_info "Preparing first-boot Docker installer (${CODENAME}, $(basename "$DOCKER_BASE"))" + mkdir -p firstboot + cat >firstboot/firstboot-docker.sh <>"$LOG" 2>&1 +exec >>"\$LOG" 2>&1 -touch /var/lib/firstboot/.running export DEBIAN_FRONTEND=noninteractive - apt-get update -qq apt-get install -y ca-certificates curl gnupg qemu-guest-agent apt-transport-https software-properties-common lsb-release install -m 0755 -d /etc/apt/keyrings -curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg +curl -fsSL ${DOCKER_BASE}/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg chmod a+r /etc/apt/keyrings/docker.gpg -echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian bookworm stable" >/etc/apt/sources.list.d/docker.list +echo "deb [arch=\$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] ${DOCKER_BASE} ${CODENAME} stable" >/etc/apt/sources.list.d/docker.list apt-get update -qq apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin @@ -413,11 +499,10 @@ systemctl enable --now docker mkdir -p /var/lib/firstboot date > /var/lib/firstboot/docker.done -rm -f /var/lib/firstboot/.running EOSH -chmod +x firstboot/firstboot-docker.sh + chmod +x firstboot/firstboot-docker.sh -cat >firstboot/firstboot-docker.service <<'EOUNIT' + cat >firstboot/firstboot-docker.service <<'EOUNIT' [Unit] Description=First boot: install Docker & QGA After=network-online.target @@ -433,19 +518,19 @@ RemainAfterExit=no WantedBy=multi-user.target EOUNIT -# hostname + machine-id reset for cloud-init style behaviour -echo "$HN" >firstboot/hostname + # hostname + machine-id reset for cloud-init style behaviour + echo "$HN" >firstboot/hostname -# Inject files without requiring guest network -virt-customize -q -a "${FILE}" \ - --copy-in firstboot/firstboot-docker.sh:/usr/local/sbin \ - --copy-in firstboot/firstboot-docker.service:/etc/systemd/system \ - --copy-in firstboot/hostname:/etc \ - --run-command "chmod +x /usr/local/sbin/firstboot-docker.sh" \ - --run-command "systemctl enable firstboot-docker.service" \ - --run-command "echo -n > /etc/machine-id" \ - --run-command "truncate -s 0 /etc/hostname && mv /etc/hostname /etc/hostname.orig && echo '${HN}' >/etc/hostname" >/dev/null -msg_ok "Prepared first-boot installer & hostname" + virt-customize -q -a "${FILE}" \ + --copy-in firstboot/firstboot-docker.sh:/usr/local/sbin \ + --copy-in firstboot/firstboot-docker.service:/etc/systemd/system \ + --copy-in firstboot/hostname:/etc \ + --run-command "chmod +x /usr/local/sbin/firstboot-docker.sh" \ + --run-command "systemctl enable firstboot-docker.service" \ + --run-command "echo -n > /etc/machine-id" \ + --run-command "truncate -s 0 /etc/hostname && mv /etc/hostname /etc/hostname.orig && echo '${HN}' >/etc/hostname" >/dev/null + msg_ok "First-boot Docker installer injected" +fi # ---------- Expand partition offline ---------- msg_info "Expanding root partition to use full disk space" From 2203b24bdd258696fdf533922816094cd50b2877 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 2 Sep 2025 20:59:05 +0200 Subject: [PATCH 0789/1733] path fix --- vm/docker-vm.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/vm/docker-vm.sh b/vm/docker-vm.sh index 6655c7e64..2dc856f5a 100644 --- a/vm/docker-vm.sh +++ b/vm/docker-vm.sh @@ -493,6 +493,10 @@ echo "deb [arch=\$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker apt-get update -qq apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin +sed -i 's#^ENV_SUPATH.*#ENV_SUPATH PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin#' /etc/login.defs || true +sed -i 's#^ENV_PATH.*#ENV_PATH PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin#' /etc/login.defs || true +printf 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\n' >/etc/environment +grep -q 'export PATH=' /root/.bashrc || echo 'export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' >> /root/.bashrc systemctl enable --now qemu-guest-agent || true systemctl enable --now docker From 5aedc07b3cf7a4498615263ece9ff0c7704388a7 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 2 Sep 2025 21:10:20 +0200 Subject: [PATCH 0790/1733] fix pve9 --- vm/docker-vm.sh | 103 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 81 insertions(+), 22 deletions(-) diff --git a/vm/docker-vm.sh b/vm/docker-vm.sh index 2dc856f5a..22558e037 100644 --- a/vm/docker-vm.sh +++ b/vm/docker-vm.sh @@ -456,6 +456,7 @@ else if [ "$INSTALL_MODE" = "direct" ]; then INSTALL_MODE="firstboot"; else INSTALL_MODE="direct"; fi fi +# ---------- PVE8: Direct install into image via virt-customize ---------- # ---------- PVE8: Direct install into image via virt-customize ---------- if [ "$INSTALL_MODE" = "direct" ]; then msg_info "Injecting Docker directly into image (${CODENAME}, $(basename "$DOCKER_BASE"))" @@ -468,7 +469,14 @@ if [ "$INSTALL_MODE" = "direct" ]; then --run-command "apt-get update -qq" \ --run-command "apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin" \ --run-command "systemctl enable docker" \ - --run-command "systemctl enable qemu-guest-agent" >/dev/null + --run-command "systemctl enable qemu-guest-agent" + + # ensure PATH in the guest for root (non-login shells, qm terminal, etc.) + --run-command "sed -i 's#^ENV_SUPATH.*#ENV_SUPATH PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin#' /etc/login.defs || true" \ + --run-command "sed -i 's#^ENV_PATH.*#ENV_PATH PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin#' /etc/login.defs || true" \ + --run-command "printf 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\n' >/etc/environment" \ + --run-command "grep -q 'export PATH=' /root/.bashrc || echo 'export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' >> /root/.bashrc" \ + >/dev/null msg_ok "Docker injected into image" fi @@ -476,46 +484,97 @@ fi if [ "$INSTALL_MODE" = "firstboot" ]; then msg_info "Preparing first-boot Docker installer (${CODENAME}, $(basename "$DOCKER_BASE"))" mkdir -p firstboot - cat >firstboot/firstboot-docker.sh <firstboot/firstboot-docker.sh <<'EOSH' #!/usr/bin/env bash -set -e +set -euxo pipefail + LOG=/var/log/firstboot-docker.log -exec >>"\$LOG" 2>&1 +exec >>"$LOG" 2>&1 -export DEBIAN_FRONTEND=noninteractive -apt-get update -qq -apt-get install -y ca-certificates curl gnupg qemu-guest-agent apt-transport-https software-properties-common lsb-release +mark_done() { + mkdir -p /var/lib/firstboot + date > /var/lib/firstboot/docker.done +} -install -m 0755 -d /etc/apt/keyrings -curl -fsSL ${DOCKER_BASE}/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg -chmod a+r /etc/apt/keyrings/docker.gpg -echo "deb [arch=\$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] ${DOCKER_BASE} ${CODENAME} stable" >/etc/apt/sources.list.d/docker.list +retry() { + local tries=$1; shift + local n=0 + until "$@"; do + n=$((n+1)) + if [ "$n" -ge "$tries" ]; then return 1; fi + sleep 5 + done +} -apt-get update -qq -apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin -sed -i 's#^ENV_SUPATH.*#ENV_SUPATH PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin#' /etc/login.defs || true -sed -i 's#^ENV_PATH.*#ENV_PATH PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin#' /etc/login.defs || true -printf 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\n' >/etc/environment -grep -q 'export PATH=' /root/.bashrc || echo 'export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' >> /root/.bashrc +wait_network() { + # DNS + HTTPS reachability + retry 30 getent hosts deb.debian.org || retry 30 getent hosts archive.ubuntu.com + retry 30 bash -c 'curl -fsS https://download.docker.com/ >/dev/null' +} -systemctl enable --now qemu-guest-agent || true -systemctl enable --now docker +fix_path() { + sed -i 's#^ENV_SUPATH.*#ENV_SUPATH PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin#' /etc/login.defs || true + sed -i 's#^ENV_PATH.*#ENV_PATH PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin#' /etc/login.defs || true + printf 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\n' >/etc/environment + grep -q 'export PATH=' /root/.bashrc || echo 'export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' >> /root/.bashrc + export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +} -mkdir -p /var/lib/firstboot -date > /var/lib/firstboot/docker.done +main() { + export DEBIAN_FRONTEND=noninteractive + + wait_network + + # Distro erkennen -> Codename + Docker-Repo + . /etc/os-release + CODENAME="${VERSION_CODENAME:-bookworm}" + case "$ID" in + ubuntu) DOCKER_BASE="https://download.docker.com/linux/ubuntu" ;; + debian|*) DOCKER_BASE="https://download.docker.com/linux/debian" ;; + esac + + # Basispakete mit Retries + retry 10 apt-get update -qq + retry 5 apt-get install -y ca-certificates curl gnupg qemu-guest-agent apt-transport-https lsb-release software-properties-common + + # Docker GPG + Repo + install -m 0755 -d /etc/apt/keyrings + curl -fsSL "${DOCKER_BASE}/gpg" | gpg --dearmor -o /etc/apt/keyrings/docker.gpg + chmod a+r /etc/apt/keyrings/docker.gpg + echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] ${DOCKER_BASE} ${CODENAME} stable" > /etc/apt/sources.list.d/docker.list + + retry 10 apt-get update -qq + retry 5 apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin + + systemctl enable --now qemu-guest-agent || true + systemctl enable --now docker + + # PATH sicherstellen + fix_path + + # Erfolg validieren + command -v docker >/dev/null + systemctl is-active --quiet docker + + mark_done +} +main EOSH chmod +x firstboot/firstboot-docker.sh cat >firstboot/firstboot-docker.service <<'EOUNIT' [Unit] Description=First boot: install Docker & QGA -After=network-online.target +After=network-online.target cloud-init.service Wants=network-online.target ConditionPathExists=!/var/lib/firstboot/docker.done +StartLimitIntervalSec=0 [Service] Type=oneshot ExecStart=/usr/local/sbin/firstboot-docker.sh +Restart=on-failure +RestartSec=10s RemainAfterExit=no [Install] From 8086160fbd41ac42362a0b1a1e5cd0e65c02c6be Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 2 Sep 2025 21:46:53 +0200 Subject: [PATCH 0791/1733] dev --- vm/docker-vm.sh | 114 +++++++++++++++++++++--------------------------- 1 file changed, 50 insertions(+), 64 deletions(-) diff --git a/vm/docker-vm.sh b/vm/docker-vm.sh index 22558e037..137baf92f 100644 --- a/vm/docker-vm.sh +++ b/vm/docker-vm.sh @@ -2,7 +2,7 @@ # Copyright (c) 2021-2025 community-scripts ORG # Author: thost96 (thost96) | Co-Author: michelroegl-brunner -# Refactor (q35 + PVE9 virt-customize network fix): MickLesk +# Refactor (q35 + PVE9 virt-customize network fix + robustness): MickLesk # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE set -e @@ -162,9 +162,7 @@ function arch_check() { function ssh_check() { if command -v pveversion >/dev/null 2>&1 && [ -n "${SSH_CLIENT:+x}" ]; then - if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Proceed anyway?" 10 62; then - : - else + if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Proceed anyway?" 10 62; then :; else clear exit fi @@ -224,7 +222,6 @@ function advanced_settings() { else exit-script; fi done - # Force q35 like our other scripts FORMAT=",efitype=4m" echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}q35${CL}" @@ -241,11 +238,11 @@ function advanced_settings() { if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ "0" "None (Default)" ON "1" "Write Through" OFF 3>&1 1>&2 2>&3); then if [ "$DISK_CACHE" = "1" ]; then - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" DISK_CACHE="cache=writethrough," + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" else - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" DISK_CACHE="" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" fi else exit-script; fi @@ -257,11 +254,11 @@ function advanced_settings() { if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ "0" "KVM64 (Default)" ON "1" "Host" OFF 3>&1 1>&2 2>&3); then if [ "$CPU_TYPE1" = "1" ]; then - echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" CPU_TYPE=" -cpu host" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" else - echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" CPU_TYPE="" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" fi else exit-script; fi @@ -369,7 +366,6 @@ function choose_os() { } PVE_VER=$(pveversion | awk -F'/' '{print $2}' | cut -d'-' -f1 | cut -d'.' -f1) - if [ "$PVE_VER" -eq 8 ]; then INSTALL_MODE="direct" elif [ "$PVE_VER" -eq 9 ]; then @@ -409,13 +405,22 @@ fi msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." -# ---------- Download Debian Cloud Image ---------- +# ---------- Download Cloud Image ---------- choose_os msg_info "Retrieving Cloud Image for $var_os $var_version" -curl -f#SL -o "$(basename "$URL")" "$URL" +curl --retry 30 --retry-delay 3 --retry-connrefused -fSL -o "$(basename "$URL")" "$URL" FILE=$(basename "$URL") msg_ok "Downloaded ${CL}${BL}${FILE}${CL}" +# Ubuntu RAW → qcow2 +if [[ "$FILE" == *.img ]]; then + msg_info "Converting RAW image to qcow2" + qemu-img convert -O qcow2 "$FILE" "${FILE%.img}.qcow2" + rm -f "$FILE" + FILE="${FILE%.img}.qcow2" + msg_ok "Converted to ${CL}${BL}${FILE}${CL}" +fi + # ---------- Ensure libguestfs-tools ---------- if ! command -v virt-customize &>/dev/null; then msg_info "Installing libguestfs-tools on host" @@ -424,8 +429,7 @@ if ! command -v virt-customize &>/dev/null; then msg_ok "Installed libguestfs-tools" fi -# ---------- Decide distro codename & Docker repo base from chosen URL ---------- -# (choose_os must have set $URL and we've downloaded $FILE above; we only need URL to derive codename) +# ---------- Decide distro codename & Docker repo base ---------- if [[ "$URL" == *"/bookworm/"* || "$FILE" == *"debian-12-"* ]]; then CODENAME="bookworm" DOCKER_BASE="https://download.docker.com/linux/debian" @@ -439,24 +443,22 @@ else CODENAME="bookworm" DOCKER_BASE="https://download.docker.com/linux/debian" fi - -# ---------- Detect PVE major version and select install mode ---------- -PVE_MAJ=$(pveversion | awk -F'/' '{print $2}' | cut -d'-' -f1 | cut -d'.' -f1) -if [ "$PVE_MAJ" -eq 8 ]; then - INSTALL_MODE="direct" # fast path: install Docker directly into image -else - INSTALL_MODE="firstboot" # robust path for PVE9: install Docker at first boot inside guest +# Map Debian trixie → bookworm (Docker-Repo oft später) +REPO_CODENAME="$CODENAME" +if [[ "$DOCKER_BASE" == *"linux/debian"* && "$CODENAME" == "trixie" ]]; then + REPO_CODENAME="bookworm" fi +# ---------- Detect PVE major version (again; independent var) ---------- +PVE_MAJ=$(pveversion | awk -F'/' '{print $2}' | cut -d'-' -f1 | cut -d'.' -f1) +if [ "$PVE_MAJ" -eq 8 ]; then INSTALL_MODE="direct"; else INSTALL_MODE="firstboot"; fi + # ---------- Optional: allow manual override ---------- if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Docker Installation Mode" \ - --yesno "Detected PVE ${PVE_MAJ}. Use ${INSTALL_MODE^^} mode?\n\nYes = ${INSTALL_MODE^^}\nNo = Switch to the other mode" 11 70; then - : # keep detected mode -else + --yesno "Detected PVE ${PVE_MAJ}. Use ${INSTALL_MODE^^} mode?\n\nYes = ${INSTALL_MODE^^}\nNo = Switch to the other mode" 11 70; then :; else if [ "$INSTALL_MODE" = "direct" ]; then INSTALL_MODE="firstboot"; else INSTALL_MODE="direct"; fi fi -# ---------- PVE8: Direct install into image via virt-customize ---------- # ---------- PVE8: Direct install into image via virt-customize ---------- if [ "$INSTALL_MODE" = "direct" ]; then msg_info "Injecting Docker directly into image (${CODENAME}, $(basename "$DOCKER_BASE"))" @@ -465,18 +467,19 @@ if [ "$INSTALL_MODE" = "direct" ]; then --run-command "install -m 0755 -d /etc/apt/keyrings" \ --run-command "curl -fsSL ${DOCKER_BASE}/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg" \ --run-command "chmod a+r /etc/apt/keyrings/docker.gpg" \ - --run-command "echo 'deb [arch=\$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] ${DOCKER_BASE} ${CODENAME} stable' > /etc/apt/sources.list.d/docker.list" \ + --run-command "echo 'deb [arch=\$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] ${DOCKER_BASE} ${REPO_CODENAME} stable' > /etc/apt/sources.list.d/docker.list" \ --run-command "apt-get update -qq" \ --run-command "apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin" \ --run-command "systemctl enable docker" \ - --run-command "systemctl enable qemu-guest-agent" + --run-command "systemctl enable qemu-guest-agent" >/dev/null - # ensure PATH in the guest for root (non-login shells, qm terminal, etc.) - --run-command "sed -i 's#^ENV_SUPATH.*#ENV_SUPATH PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin#' /etc/login.defs || true" \ + # PATH-Fix separat + virt-customize -q -a "${FILE}" \ + --run-command "sed -i 's#^ENV_SUPATH.*#ENV_SUPATH PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin#' /etc/login.defs || true" \ --run-command "sed -i 's#^ENV_PATH.*#ENV_PATH PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin#' /etc/login.defs || true" \ --run-command "printf 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\n' >/etc/environment" \ - --run-command "grep -q 'export PATH=' /root/.bashrc || echo 'export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' >> /root/.bashrc" \ - >/dev/null + --run-command "grep -q 'export PATH=' /root/.bashrc || echo 'export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' >> /root/.bashrc" >/dev/null + msg_ok "Docker injected into image" fi @@ -491,25 +494,12 @@ set -euxo pipefail LOG=/var/log/firstboot-docker.log exec >>"$LOG" 2>&1 -mark_done() { - mkdir -p /var/lib/firstboot - date > /var/lib/firstboot/docker.done -} - -retry() { - local tries=$1; shift - local n=0 - until "$@"; do - n=$((n+1)) - if [ "$n" -ge "$tries" ]; then return 1; fi - sleep 5 - done -} +mark_done() { mkdir -p /var/lib/firstboot; date > /var/lib/firstboot/docker.done; } +retry() { local t=$1; shift; local n=0; until "$@"; do n=$((n+1)); [ "$n" -ge "$t" ] && return 1; sleep 5; done; } wait_network() { - # DNS + HTTPS reachability - retry 30 getent hosts deb.debian.org || retry 30 getent hosts archive.ubuntu.com - retry 30 bash -c 'curl -fsS https://download.docker.com/ >/dev/null' + retry 60 getent hosts deb.debian.org || retry 60 getent hosts archive.ubuntu.com + retry 60 bash -lc 'curl -fsS https://download.docker.com/ >/dev/null' } fix_path() { @@ -522,37 +512,36 @@ fix_path() { main() { export DEBIAN_FRONTEND=noninteractive + mkdir -p /etc/apt/apt.conf.d + printf 'Acquire::Retries "10";\nAcquire::http::Timeout "60";\nAcquire::https::Timeout "60";\n' >/etc/apt/apt.conf.d/80-retries-timeouts wait_network - # Distro erkennen -> Codename + Docker-Repo . /etc/os-release CODENAME="${VERSION_CODENAME:-bookworm}" case "$ID" in ubuntu) DOCKER_BASE="https://download.docker.com/linux/ubuntu" ;; debian|*) DOCKER_BASE="https://download.docker.com/linux/debian" ;; esac + REPO_CODENAME="$CODENAME" + if [ "$ID" = "debian" ] && [ "$CODENAME" = "trixie" ]; then REPO_CODENAME="bookworm"; fi - # Basispakete mit Retries - retry 10 apt-get update -qq - retry 5 apt-get install -y ca-certificates curl gnupg qemu-guest-agent apt-transport-https lsb-release software-properties-common + retry 20 apt-get update -qq + retry 10 apt-get install -y ca-certificates curl gnupg qemu-guest-agent apt-transport-https lsb-release software-properties-common - # Docker GPG + Repo install -m 0755 -d /etc/apt/keyrings curl -fsSL "${DOCKER_BASE}/gpg" | gpg --dearmor -o /etc/apt/keyrings/docker.gpg chmod a+r /etc/apt/keyrings/docker.gpg - echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] ${DOCKER_BASE} ${CODENAME} stable" > /etc/apt/sources.list.d/docker.list + echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] ${DOCKER_BASE} ${REPO_CODENAME} stable" > /etc/apt/sources.list.d/docker.list - retry 10 apt-get update -qq - retry 5 apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin + retry 20 apt-get update -qq + retry 10 apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin systemctl enable --now qemu-guest-agent || true systemctl enable --now docker - # PATH sicherstellen fix_path - # Erfolg validieren command -v docker >/dev/null systemctl is-active --quiet docker @@ -575,13 +564,13 @@ Type=oneshot ExecStart=/usr/local/sbin/firstboot-docker.sh Restart=on-failure RestartSec=10s +TimeoutStartSec=0 RemainAfterExit=no [Install] WantedBy=multi-user.target EOUNIT - # hostname + machine-id reset for cloud-init style behaviour echo "$HN" >firstboot/hostname virt-customize -q -a "${FILE}" \ @@ -592,6 +581,7 @@ EOUNIT --run-command "systemctl enable firstboot-docker.service" \ --run-command "echo -n > /etc/machine-id" \ --run-command "truncate -s 0 /etc/hostname && mv /etc/hostname /etc/hostname.orig && echo '${HN}' >/etc/hostname" >/dev/null + msg_ok "First-boot Docker installer injected" fi @@ -611,11 +601,7 @@ msg_ok "Created VM shell" # ---------- Import disk ---------- msg_info "Importing disk into storage ($STORAGE)" -if qm disk import --help >/dev/null 2>&1; then - IMPORT_CMD=(qm disk import) -else - IMPORT_CMD=(qm importdisk) -fi +if qm disk import --help >/dev/null 2>&1; then IMPORT_CMD=(qm disk import); else IMPORT_CMD=(qm importdisk); fi IMPORT_OUT="$("${IMPORT_CMD[@]}" "$VMID" "${FILE}" "$STORAGE" --format qcow2 2>&1 || true)" DISK_REF="$(printf '%s\n' "$IMPORT_OUT" | sed -n "s/.*successfully imported disk '\([^']\+\)'.*/\1/p" | tr -d "\r\"'")" [[ -z "$DISK_REF" ]] && DISK_REF="$(pvesm list "$STORAGE" | awk -v id="$VMID" '$5 ~ ("vm-"id"-disk-") {print $1":"$5}' | sort | tail -n1)" From 24b594faeaeb9a4da94825b3b951bdbd0a409339 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Tue, 2 Sep 2025 16:16:27 -0400 Subject: [PATCH 0792/1733] trying to fix broken container creation --- misc/create_lxc.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index 83b31d1a8..23b148be8 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -466,8 +466,8 @@ grep -q "root:100000:65536" /etc/subuid || echo "root:100000:65536" >>/etc/subui grep -q "root:100000:65536" /etc/subgid || echo "root:100000:65536" >>/etc/subgid # Assemble pct options -PCT_OPTIONS=(${PCT_OPTIONS[@]:-${DEFAULT_PCT_OPTIONS[@]}}) -[[ " ${PCT_OPTIONS[@]} " =~ " -rootfs " ]] || PCT_OPTIONS+=(-rootfs "$CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}") +PCT_OPTIONS=("${PCT_OPTIONS[@]:-${DEFAULT_PCT_OPTIONS[@]}}") +[[ " ${PCT_OPTIONS[*]} " =~ " -rootfs " ]] || PCT_OPTIONS+=(-rootfs "$CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}") # Secure with lockfile lockfile="/tmp/template.${TEMPLATE}.lock" From 8fd2b0c20d62e4cc2b3a56bebead967dcc654fb4 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Tue, 2 Sep 2025 16:19:15 -0400 Subject: [PATCH 0793/1733] revert one test --- misc/create_lxc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index 23b148be8..dc92cac9d 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -467,7 +467,7 @@ grep -q "root:100000:65536" /etc/subgid || echo "root:100000:65536" >>/etc/subgi # Assemble pct options PCT_OPTIONS=("${PCT_OPTIONS[@]:-${DEFAULT_PCT_OPTIONS[@]}}") -[[ " ${PCT_OPTIONS[*]} " =~ " -rootfs " ]] || PCT_OPTIONS+=(-rootfs "$CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}") +[[ " ${PCT_OPTIONS[@]} " =~ " -rootfs " ]] || PCT_OPTIONS+=(-rootfs "$CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}") # Secure with lockfile lockfile="/tmp/template.${TEMPLATE}.lock" From d95e5d7c9399e27cbfd833918030950c4ffa8d64 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Tue, 2 Sep 2025 16:22:38 -0400 Subject: [PATCH 0794/1733] next test --- misc/create_lxc.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index dc92cac9d..ea53362e9 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -466,8 +466,8 @@ grep -q "root:100000:65536" /etc/subuid || echo "root:100000:65536" >>/etc/subui grep -q "root:100000:65536" /etc/subgid || echo "root:100000:65536" >>/etc/subgid # Assemble pct options -PCT_OPTIONS=("${PCT_OPTIONS[@]:-${DEFAULT_PCT_OPTIONS[@]}}") -[[ " ${PCT_OPTIONS[@]} " =~ " -rootfs " ]] || PCT_OPTIONS+=(-rootfs "$CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}") +PCT_OPTIONS=(${PCT_OPTIONS[@]:-${DEFAULT_PCT_OPTIONS[@]}}) +[[ " ${PCT_OPTIONS[*]} " =~ " -rootfs " ]] || PCT_OPTIONS+=(-rootfs "$CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}") # Secure with lockfile lockfile="/tmp/template.${TEMPLATE}.lock" From 08743166bdb585ebca13c918a1bd116bd53a313c Mon Sep 17 00:00:00 2001 From: vhsdream Date: Tue, 2 Sep 2025 16:55:54 -0400 Subject: [PATCH 0795/1733] Autocaliweb: export the venv --- install/autocaliweb-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/autocaliweb-install.sh b/install/autocaliweb-install.sh index 49e36772f..0686473c8 100644 --- a/install/autocaliweb-install.sh +++ b/install/autocaliweb-install.sh @@ -70,7 +70,7 @@ INGEST_DIR="/opt/acw-book-ingest" SERVICE_USER="acw" SERVICE_GROUP="acw" SCRIPTS_DIR="${INSTALL_DIR}/scripts" -VIRTUAL_ENV="${INSTALL_DIR}/venv" +export VIRTUAL_ENV="${INSTALL_DIR}/venv" mkdir -p "$CONFIG_DIR"/{.config/calibre/plugins,log_archive,.acw_conversion_tmp} mkdir -p "$CONFIG_DIR"/processed_books/{converted,imported,failed,fixed_originals} From d5b550493b7b2e5e91f244abe7fb7b30a33a7730 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 3 Sep 2025 09:45:11 +0200 Subject: [PATCH 0796/1733] Cleanup --- ct/debian13.sh | 44 ---- ct/{ => deferred}/librespeed.sh | 0 ct/{ => deferred}/vikunja.sh | 0 ct/litellm.sh | 60 ----- ct/mediamanager.sh | 80 ------- ct/nginxproxymanager.sh | 158 ------------- ct/proxmox-backup-server.sh | 44 ---- ct/tracktor.sh | 70 ------ ct/traefik.sh | 58 ----- ct/zitadel.sh | 62 ----- frontend/public/json/librespeed.json | 35 --- frontend/public/json/litellm.json | 40 ---- frontend/public/json/mediamanager.json | 45 ---- frontend/public/json/tracktor.json | 40 ---- frontend/public/json/vikunja.json | 35 --- install/debian13-install.sh | 25 -- install/{ => deferred}/vikunja-install.sh | 0 install/jeedom-install.sh | 94 -------- install/librespeed-install.sh | 54 ----- install/litellm-install.sh | 80 ------- install/mediamanager-install.sh | 120 ---------- install/nginxproxymanager-install.sh | 171 -------------- install/proxmox-backup-server-install.sh | 39 ---- install/tracktor-install.sh | 61 ----- install/traefik-install.sh | 266 ---------------------- install/zitadel-install.sh | 149 ------------ 26 files changed, 1830 deletions(-) delete mode 100644 ct/debian13.sh rename ct/{ => deferred}/librespeed.sh (100%) rename ct/{ => deferred}/vikunja.sh (100%) delete mode 100644 ct/litellm.sh delete mode 100644 ct/mediamanager.sh delete mode 100644 ct/nginxproxymanager.sh delete mode 100644 ct/proxmox-backup-server.sh delete mode 100644 ct/tracktor.sh delete mode 100644 ct/traefik.sh delete mode 100644 ct/zitadel.sh delete mode 100644 frontend/public/json/librespeed.json delete mode 100644 frontend/public/json/litellm.json delete mode 100644 frontend/public/json/mediamanager.json delete mode 100644 frontend/public/json/tracktor.json delete mode 100644 frontend/public/json/vikunja.json delete mode 100644 install/debian13-install.sh rename install/{ => deferred}/vikunja-install.sh (100%) delete mode 100644 install/jeedom-install.sh delete mode 100644 install/librespeed-install.sh delete mode 100644 install/litellm-install.sh delete mode 100644 install/mediamanager-install.sh delete mode 100644 install/nginxproxymanager-install.sh delete mode 100644 install/proxmox-backup-server-install.sh delete mode 100644 install/tracktor-install.sh delete mode 100644 install/traefik-install.sh delete mode 100644 install/zitadel-install.sh diff --git a/ct/debian13.sh b/ct/debian13.sh deleted file mode 100644 index 4be94c1c8..000000000 --- a/ct/debian13.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://www.debian.org/ - -APP="Debian13" -var_tags="${var_tags:-os}" -var_cpu="${var_cpu:-4}" -var_ram="${var_ram:-4096}" -var_disk="${var_disk:-15}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" -var_unprivileged="${var_unprivileged:-1}" -#var_fuse="${var_fuse:-no}" -#var_tun="${var_tun:-no}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /var ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - msg_info "Updating $APP LXC" - $STD apt update - $STD apt -y upgrade - msg_ok "Updated $APP LXC" - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!" -msg_custom "🚀" "${GN}" "${APP} setup has been successfully initialized!" diff --git a/ct/librespeed.sh b/ct/deferred/librespeed.sh similarity index 100% rename from ct/librespeed.sh rename to ct/deferred/librespeed.sh diff --git a/ct/vikunja.sh b/ct/deferred/vikunja.sh similarity index 100% rename from ct/vikunja.sh rename to ct/deferred/vikunja.sh diff --git a/ct/litellm.sh b/ct/litellm.sh deleted file mode 100644 index 8d2b7f733..000000000 --- a/ct/litellm.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: stout01 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/BerriAI/litellm - -APP="LiteLLM" -var_tags="${var_tags:-ai;interface}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-4}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - - if [[ ! -f /etc/systemd/system/litellm.service ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - msg_info "Stopping ${APP}" - systemctl stop litellm - msg_ok "Stopped ${APP}" - - VENV_PATH="/opt/litellm/.venv" - PYTHON_VERSION="3.13" setup_uv - - msg_info "Updating $APP" - $STD "$VENV_PATH/bin/python" -m pip install --upgrade litellm[proxy] prisma - - msg_info "Updating DB Schema" - $STD uv --directory=/opt/litellm run litellm --config /opt/litellm/litellm.yaml --use_prisma_db_push --skip_server_startup - msg_ok "DB Schema Updated" - - msg_info "Starting ${APP}" - systemctl start litellm - msg_ok "Started ${APP}" - msg_ok "Updated Successfully" - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:4000${CL}" diff --git a/ct/mediamanager.sh b/ct/mediamanager.sh deleted file mode 100644 index d633f657a..000000000 --- a/ct/mediamanager.sh +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: vhsdream -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/maxdorninger/MediaManager - -APP="MediaManager" -var_tags="${var_tags:-arr}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-3072}" -var_disk="${var_disk:-4}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/mediamanager ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - setup_uv - - RELEASE=$(curl -fsSL https://api.github.com/repos/maxdorninger/MediaManager/releases/latest | jq '.tag_name' | sed 's/^v//') - if [[ "${RELEASE}" != "$(cat ~/.mediamanager 2>/dev/null)" ]] || [[ ! -f ~/.mediamanager ]]; then - msg_info "Stopping Service" - systemctl stop mediamanager - msg_ok "Stopped Service" - - fetch_and_deploy_gh_release "MediaManager" "maxdorninger/MediaManager" "tarball" "latest" "/opt/mediamanager" - msg_info "Updating ${APP}" - MM_DIR="/opt/mm" - export CONFIG_DIR="${MM_DIR}/config" - export FRONTEND_FILES_DIR="${MM_DIR}/web/build" - export BASE_PATH="" - export PUBLIC_VERSION="" - export PUBLIC_API_URL="${BASE_PATH}/api/v1" - export BASE_PATH="${BASE_PATH}/web" - cd /opt/mediamanager/web - $STD npm ci - $STD npm run build - rm -rf "$FRONTEND_FILES_DIR"/build - cp -r build "$FRONTEND_FILES_DIR" - - export BASE_PATH="" - export VIRTUAL_ENV="/opt/${MM_DIR}/venv" - cd /opt/mediamanager - rm -rf "$MM_DIR"/{media_manager,alembic*} - cp -r {media_manager,alembic*} "$MM_DIR" - $STD /usr/local/bin/uv sync --locked --active - msg_ok "Updated $APP" - - msg_info "Starting Service" - systemctl start mediamanager - msg_ok "Started Service" - - msg_ok "Updated Successfully" - else - msg_ok "Already up to date" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}" diff --git a/ct/nginxproxymanager.sh b/ct/nginxproxymanager.sh deleted file mode 100644 index eb7259dee..000000000 --- a/ct/nginxproxymanager.sh +++ /dev/null @@ -1,158 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://nginxproxymanager.com/ - -APP="Nginx Proxy Manager" -var_tags="${var_tags:-proxy}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-1024}" -var_disk="${var_disk:-4}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -f /lib/systemd/system/npm.service ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - if ! command -v pnpm &>/dev/null; then - msg_info "Installing pnpm" - #export NODE_OPTIONS=--openssl-legacy-provider - $STD npm install -g pnpm@8.15 - msg_ok "Installed pnpm" - fi - RELEASE=$(curl -fsSL https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/releases/latest | - grep "tag_name" | - awk '{print substr($2, 3, length($2)-4) }') - msg_info "Stopping Services" - systemctl stop openresty - systemctl stop npm - msg_ok "Stopped Services" - - msg_info "Cleaning Old Files" - rm -rf /app \ - /var/www/html \ - /etc/nginx \ - /var/log/nginx \ - /var/lib/nginx \ - "$STD" /var/cache/nginx - msg_ok "Cleaned Old Files" - - msg_info "Downloading NPM v${RELEASE}" - curl -fsSL "https://codeload.github.com/NginxProxyManager/nginx-proxy-manager/tar.gz/v${RELEASE}" | tar -xz - cd nginx-proxy-manager-"${RELEASE}" - msg_ok "Downloaded NPM v${RELEASE}" - - msg_info "Setting up Enviroment" - ln -sf /usr/bin/python3 /usr/bin/python - ln -sf /usr/bin/certbot /opt/certbot/bin/certbot - ln -sf /usr/local/openresty/nginx/sbin/nginx /usr/sbin/nginx - ln -sf /usr/local/openresty/nginx/ /etc/nginx - sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" backend/package.json - sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" frontend/package.json - sed -i 's+^daemon+#daemon+g' docker/rootfs/etc/nginx/nginx.conf - NGINX_CONFS=$(find "$(pwd)" -type f -name "*.conf") - for NGINX_CONF in $NGINX_CONFS; do - sed -i 's+include conf.d+include /etc/nginx/conf.d+g' "$NGINX_CONF" - done - mkdir -p /var/www/html /etc/nginx/logs - cp -r docker/rootfs/var/www/html/* /var/www/html/ - cp -r docker/rootfs/etc/nginx/* /etc/nginx/ - cp docker/rootfs/etc/letsencrypt.ini /etc/letsencrypt.ini - cp docker/rootfs/etc/logrotate.d/nginx-proxy-manager /etc/logrotate.d/nginx-proxy-manager - ln -sf /etc/nginx/nginx.conf /etc/nginx/conf/nginx.conf - rm -f /etc/nginx/conf.d/dev.conf - mkdir -p /tmp/nginx/body \ - /run/nginx \ - /data/nginx \ - /data/custom_ssl \ - /data/logs \ - /data/access \ - /data/nginx/default_host \ - /data/nginx/default_www \ - /data/nginx/proxy_host \ - /data/nginx/redirection_host \ - /data/nginx/stream \ - /data/nginx/dead_host \ - /data/nginx/temp \ - /var/lib/nginx/cache/public \ - /var/lib/nginx/cache/private \ - /var/cache/nginx/proxy_temp - chmod -R 777 /var/cache/nginx - chown root /tmp/nginx - echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" {print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf);" >/etc/nginx/conf.d/include/resolvers.conf - if [ ! -f /data/nginx/dummycert.pem ] || [ ! -f /data/nginx/dummykey.pem ]; then - $STD openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 -subj "/O=Nginx Proxy Manager/OU=Dummy Certificate/CN=localhost" -keyout /data/nginx/dummykey.pem -out /data/nginx/dummycert.pem - fi - mkdir -p /app/global /app/frontend/images - cp -r backend/* /app - cp -r global/* /app/global - $STD python3 -m pip install --no-cache-dir --break-system-packages certbot-dns-cloudflare - msg_ok "Setup Enviroment" - - msg_info "Building Frontend" - cd ./frontend - $STD pnpm install - $STD pnpm upgrade - $STD pnpm run build - cp -r dist/* /app/frontend - cp -r app-images/* /app/frontend/images - msg_ok "Built Frontend" - - msg_info "Initializing Backend" - $STD rm -rf /app/config/default.json - if [ ! -f /app/config/production.json ]; then - cat <<'EOF' >/app/config/production.json -{ - "database": { - "engine": "knex-native", - "knex": { - "client": "sqlite3", - "connection": { - "filename": "/data/database.sqlite" - } - } - } -} -EOF - fi - cd /app - $STD pnpm install - msg_ok "Initialized Backend" - - msg_info "Starting Services" - sed -i 's/user npm/user root/g; s/^pid/#pid/g' /usr/local/openresty/nginx/conf/nginx.conf - sed -i 's/su npm npm/su root root/g' /etc/logrotate.d/nginx-proxy-manager - sed -i 's/include-system-site-packages = false/include-system-site-packages = true/g' /opt/certbot/pyvenv.cfg - systemctl enable -q --now openresty - systemctl enable -q --now npm - msg_ok "Started Services" - - msg_info "Cleaning up" - rm -rf ~/nginx-proxy-manager-* - msg_ok "Cleaned" - - msg_ok "Updated Successfully" - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:81${CL}" diff --git a/ct/proxmox-backup-server.sh b/ct/proxmox-backup-server.sh deleted file mode 100644 index 5463b1eba..000000000 --- a/ct/proxmox-backup-server.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://www.proxmox.com/en/proxmox-backup-server - -APP="Proxmox-Backup-Server" -var_tags="${var_tags:-backup}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-10}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -e /usr/sbin/proxmox-backup-manager ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - msg_info "Updating $APP LXC" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "Updated $APP LXC" - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:8007${CL}" diff --git a/ct/tracktor.sh b/ct/tracktor.sh deleted file mode 100644 index c568f28d9..000000000 --- a/ct/tracktor.sh +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: CrazyWolf13 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://tracktor.bytedge.in/ - -APP="tracktor" -var_tags="${var_tags:-car;monitoring}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-4096}" -var_disk="${var_disk:-6}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/tracktor ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - RELEASE=$(curl -fsSL https://api.github.com/repos/javedh-dev/tracktor/releases/latest | jq -r '.tag_name' | sed 's/^v//') - if [[ "${RELEASE}" != "$(cat ~/.tracktor 2>/dev/null)" ]] || [[ ! -f ~/.tracktor ]]; then - msg_info "Stopping Service" - systemctl stop tracktor - msg_ok "Stopped Service" - - msg_info "Creating Backup" - cp /opt/tracktor/app/backend/.env /opt/tracktor.env - msg_ok "Created Backup" - - msg_info "Updating ${APP}" - setup_nodejs - fetch_and_deploy_gh_release "tracktor" "javedh-dev/tracktor" "tarball" "latest" "/opt/tracktor" - cd /opt/tracktor - $STD npm install - $STD npm run build - msg_ok "Updated $APP" - - msg_info "Restoring Backup" - cp /opt/tracktor.env /opt/tracktor/app/backend/.env - msg_ok "Restored Backup" - - msg_info "Starting Service" - systemctl start tracktor - msg_ok "Started Service" - msg_ok "Updated Successfully" - else - msg_ok "Already up to date" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" diff --git a/ct/traefik.sh b/ct/traefik.sh deleted file mode 100644 index fe4d80f92..000000000 --- a/ct/traefik.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://traefik.io/ - -APP="Traefik" -var_tags="${var_tags:-proxy}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-512}" -var_disk="${var_disk:-2}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -f /etc/systemd/system/traefik.service ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - RELEASE=$(curl -fsSL https://api.github.com/repos/traefik/traefik/releases | grep -oP '"tag_name":\s*"v\K[\d.]+?(?=")' | sort -V | tail -n 1) - msg_info "Updating $APP LXC" - if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then - curl -fsSL "https://github.com/traefik/traefik/releases/download/v${RELEASE}/traefik_v${RELEASE}_linux_amd64.tar.gz" -o $(basename "https://github.com/traefik/traefik/releases/download/v${RELEASE}/traefik_v${RELEASE}_linux_amd64.tar.gz") - tar -C /tmp -xzf traefik*.tar.gz - mv /tmp/traefik /usr/bin/ - rm -rf traefik*.tar.gz - systemctl restart traefik.service - echo "${RELEASE}" >/opt/${APP}_version.txt - msg_ok "Updated $APP LXC" - else - msg_ok "No update required. ${APP} is already at ${RELEASE}" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" -echo -e "Commands available are as below:" -echo -e "addsite - creating a config" -echo -e "ensite - enables a config" -echo -e "dissite - disables a config" -echo -e "editsite - edits a config" diff --git a/ct/zitadel.sh b/ct/zitadel.sh deleted file mode 100644 index c35c7b397..000000000 --- a/ct/zitadel.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: dave-yap (dave-yap) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://zitadel.com/ - -APP="Zitadel" -var_tags="${var_tags:-identity-provider}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-1024}" -var_disk="${var_disk:-8}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -f /etc/systemd/system/zitadel.service ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - RELEASE=$(curl -fsSL https://api.github.com/repos/zitadel/zitadel/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') - if [[ ! -f ~/.zitadel ]] || [[ "${RELEASE}" != "$(cat ~/.zitadel)" ]]; then - msg_info "Stopping $APP" - systemctl stop zitadel - msg_ok "Stopped $APP" - - rm -f /usr/local/bin/zitadel - fetch_and_deploy_gh_release "zitadel" "zitadel/zitadel" "prebuild" "latest" "/usr/local/bin" "zitadel-linux-amd64.tar.gz" - - msg_info "Updating $APP to ${RELEASE}" - $STD zitadel setup --masterkeyFile /opt/zitadel/.masterkey --config /opt/zitadel/config.yaml --init-projections=true - msg_ok "Updated $APP to ${RELEASE}" - - msg_info "Starting $APP" - systemctl start zitadel - msg_ok "Started $APP" - - msg_ok "Update Successful" - else - msg_ok "No update required. ${APP} is already at ${RELEASE}" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080/ui/console${CL}" diff --git a/frontend/public/json/librespeed.json b/frontend/public/json/librespeed.json deleted file mode 100644 index 713b2fe81..000000000 --- a/frontend/public/json/librespeed.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "Librespeed", - "slug": "librespeed", - "categories": [ - 4 - ], - "date_created": "2025-04-26", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 80, - "documentation": "https://github.com/librespeed/speedtest/blob/master/doc.md", - "config_path": "", - "website": "https://librespeed.org", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/librespeed.webp", - "description": "No Flash, No Java, No Websocket, No Bullshit. This is a very lightweight speed test implemented in Javascript, using XMLHttpRequest and Web Workers.", - "install_methods": [ - { - "type": "default", - "script": "ct/librespeed.sh", - "resources": { - "cpu": 1, - "ram": 512, - "hdd": 4, - "os": "Debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": "root", - "password": null - }, - "notes": [] -} diff --git a/frontend/public/json/litellm.json b/frontend/public/json/litellm.json deleted file mode 100644 index b2e7b7d79..000000000 --- a/frontend/public/json/litellm.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "LiteLLM", - "slug": "litellm", - "categories": [ - 20 - ], - "date_created": "2025-08-07", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 4000, - "documentation": "https://docs.litellm.ai/", - "config_path": "/opt/litellm/litellm.yaml", - "website": "https://www.litellm.ai/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/litellm-light.webp", - "description": "LLM proxy to call 100+ LLMs in a unified interface & track spend, set budgets per virtual key/user", - "install_methods": [ - { - "type": "default", - "script": "ct/litellm.sh", - "resources": { - "cpu": 2, - "ram": 2048, - "hdd": 4, - "os": "Debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": "admin", - "password": "sk-1234" - }, - "notes": [ - { - "text": "Update master key in the config file", - "type": "info" - } - ] -} diff --git a/frontend/public/json/mediamanager.json b/frontend/public/json/mediamanager.json deleted file mode 100644 index c8590b012..000000000 --- a/frontend/public/json/mediamanager.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "name": "MediaManager", - "slug": "mediamanager", - "categories": [ - 14, - 13 - ], - "date_created": "2025-07-22", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 8000, - "documentation": "https://maxdorninger.github.io/MediaManager/introduction.html", - "config_path": "/opt/mm_data/config.toml", - "website": "https://github.com/maxdorninger/MediaManager", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/mediamanager.webp", - "description": "A modern selfhosted media management system for your media library", - "install_methods": [ - { - "type": "default", - "script": "ct/mediamanager.sh", - "resources": { - "cpu": 2, - "ram": 3072, - "hdd": 4, - "os": "Debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": "", - "password": "admin" - }, - "notes": [ - { - "text": "During the installation, provide the email address of the first admin user", - "type": "info" - }, - { - "text": "You're probably going to want to use a bind mount for the media directories", - "type": "info" - } - ] -} diff --git a/frontend/public/json/tracktor.json b/frontend/public/json/tracktor.json deleted file mode 100644 index 0a8f7c3b8..000000000 --- a/frontend/public/json/tracktor.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "Tracktor", - "slug": "tracktor", - "categories": [ - 9 - ], - "date_created": "2025-08-06", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 3000, - "documentation": "https://tracktor.bytedge.in/introduction.html", - "config_path": "/opt/tracktor/app/server/.env", - "website": "https://tracktor.bytedge.in/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/tracktor.svg", - "description": "Tracktor is an open-source web application for comprehensive vehicle management.\nEasily track ⛽ fuel consumption, 🛠️ maintenance, 🛡️ insurance, and 📄 regulatory documents for all your vehicles in one place. ", - "install_methods": [ - { - "type": "default", - "script": "ct/tracktor.sh", - "resources": { - "cpu": 1, - "ram": 1024, - "hdd": 6, - "os": "Debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "Please check and update the '/opt/tracktor/app/backend/.env' file if using behind reverse proxy.", - "type": "info" - } - ] -} diff --git a/frontend/public/json/vikunja.json b/frontend/public/json/vikunja.json deleted file mode 100644 index ea1711406..000000000 --- a/frontend/public/json/vikunja.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "Vikunja", - "slug": "vikunja", - "categories": [ - 12 - ], - "date_created": "2024-11-05", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 3456, - "documentation": null, - "website": "https://vikunja.io/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/vikunja.webp", - "config_path": "/etc/vikunja/config.yml", - "description": "Vikunja is a powerful self-hosted todo app. It allows you to create and manage to-do lists. You can plan tasks, set priorities and collaborate with others. The best part is that your data is safe with you and you can customize the app to your liking. It's like a personal assistant that helps you stay organized.", - "install_methods": [ - { - "type": "default", - "script": "ct/vikunja.sh", - "resources": { - "cpu": 1, - "ram": 1024, - "hdd": 4, - "os": "debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] -} diff --git a/install/debian13-install.sh b/install/debian13-install.sh deleted file mode 100644 index 466d6af6e..000000000 --- a/install/debian13-install.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: MickLesk (CanbiZ) -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt install -y gpg -msg_ok "Installed Dependencies" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt -y autoremove -$STD apt -y autoclean -msg_ok "Cleaned" diff --git a/install/vikunja-install.sh b/install/deferred/vikunja-install.sh similarity index 100% rename from install/vikunja-install.sh rename to install/deferred/vikunja-install.sh diff --git a/install/jeedom-install.sh b/install/jeedom-install.sh deleted file mode 100644 index d868e84b1..000000000 --- a/install/jeedom-install.sh +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Mips2648 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://jeedom.com/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing dependencies" -$STD apt-get install -y \ - lsb-release \ - git -msg_ok "Dependencies installed" - -DEFAULT_BRANCH="master" -REPO_URL="https://github.com/jeedom/core.git" - -echo -while true; do - read -rp "${TAB3}Enter branch to use (master, beta, alpha...) (Default: ${DEFAULT_BRANCH}): " BRANCH - BRANCH="${BRANCH:-$DEFAULT_BRANCH}" - - if git ls-remote --heads "$REPO_URL" "refs/heads/$BRANCH" | grep -q .; then - break - else - msg_error "Branch '$BRANCH' does not exist on remote. Please try again." - fi -done - -msg_info "Downloading Jeedom installation script" -cd /tmp -wget -q https://raw.githubusercontent.com/jeedom/core/"${BRANCH}"/install/install.sh -chmod +x install.sh -msg_ok "Installation script downloaded" - -msg_info "Install Jeedom main dependencies, please wait" -$STD ./install.sh -v "$BRANCH" -s 2 -msg_ok "Installed Jeedom main dependencies" - -msg_info "Install Database" -$STD ./install.sh -v "$BRANCH" -s 3 -msg_ok "Database installed" - -msg_info "Install Apache" -$STD ./install.sh -v "$BRANCH" -s 4 -msg_ok "Apache installed" - -msg_info "Install PHP and dependencies" -$STD ./install.sh -v "$BRANCH" -s 5 -msg_ok "PHP installed" - -msg_info "Download Jeedom core" -$STD ./install.sh -v "$BRANCH" -s 6 -msg_ok "Download done" - -msg_info "Database customisation" -$STD ./install.sh -v "$BRANCH" -s 7 -msg_ok "Database customisation done" - -msg_info "Jeedom customisation" -$STD ./install.sh -v "$BRANCH" -s 8 -msg_ok "Jeedom customisation done" - -msg_info "Configuring Jeedom" -$STD ./install.sh -v "$BRANCH" -s 9 -msg_ok "Jeedom configured" - -msg_info "Installing Jeedom" -$STD ./install.sh -v "$BRANCH" -s 10 -msg_ok "Jeedom installed" - -msg_info "Post installation" -$STD ./install.sh -v "$BRANCH" -s 11 -msg_ok "Post installation done" - -msg_info "Check installation" -$STD ./install.sh -v "$BRANCH" -s 12 -msg_ok "Installation checked, everything is successfuly installed. A reboot is recommended." - -motd_ssh -customize - -msg_info "Cleaning up" -rm -rf /tmp/install.sh -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" diff --git a/install/librespeed-install.sh b/install/librespeed-install.sh deleted file mode 100644 index 8b2bc48a8..000000000 --- a/install/librespeed-install.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: elvito -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/librespeed/speedtest - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt-get update -$STD apt-get install -y \ - caddy \ - php-fpm -msg_ok "Installed Dependencies" - -msg_info "Installing librespeed" -temp_file=$(mktemp) -RELEASE=$(curl -fsSL https://api.github.com/repos/librespeed/speedtest/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}') -curl -fsSL "https://github.com/librespeed/speedtest/archive/refs/tags/${RELEASE}.zip" -o "$temp_file" -mkdir -p /opt/librespeed -mkdir -p /temp -unzip -q "$temp_file" -d /temp -cd /temp/speedtest-"${RELEASE}" -cp -u favicon.ico index.html speedtest.js speedtest_worker.js /opt/librespeed/ -cp -ru backend results /opt/librespeed/ - -cat </etc/caddy/Caddyfile -:80 { - root * /opt/librespeed - file_server - php_fastcgi unix//run/php/php-fpm.sock -} -EOF - -systemctl restart caddy -echo "${RELEASE}" >/opt/"${APP}_version.txt" -msg_ok "Installation completed" - -motd_ssh -customize - -msg_info "Cleaning up" -rm -rf /temp -rm -f "$temp_file" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" diff --git a/install/litellm-install.sh b/install/litellm-install.sh deleted file mode 100644 index b79341f99..000000000 --- a/install/litellm-install.sh +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: stout01 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/BerriAI/litellm - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -PG_VERSION="17" setup_postgresql -PYTHON_VERSION="3.13" setup_uv - -msg_info "Setting up PostgreSQL" -DB_NAME="litellm_db" -DB_USER="litellm" -DB_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" -$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" -$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" -{ - echo "${APPLICATION} Credentials" - echo "Database Name: $DB_NAME" - echo "Database User: $DB_USER" - echo "Database Password: $DB_PASS" -} >>~/litellm.creds -msg_ok "Set up PostgreSQL" - -msg_info "Setting up Virtual Environment" -mkdir -p /opt/litellm -cd /opt/litellm -$STD uv venv /opt/litellm/.venv -$STD /opt/litellm/.venv/bin/python -m ensurepip --upgrade -$STD /opt/litellm/.venv/bin/python -m pip install --upgrade pip -$STD /opt/litellm/.venv/bin/python -m pip install litellm[proxy] prisma -msg_ok "Installed LiteLLM" - -msg_info "Configuring LiteLLM" -mkdir -p /opt -cat </opt/litellm/litellm.yaml -general_settings: - master_key: sk-1234 - database_url: postgresql://$DB_USER:$DB_PASS@127.0.0.1:5432/$DB_NAME - store_model_in_db: true -EOF - -uv --directory=/opt/litellm run litellm --config /opt/litellm/litellm.yaml --use_prisma_db_push --skip_server_startup -msg_ok "Configured LiteLLM" - -msg_info "Creating Service" -cat </etc/systemd/system/litellm.service -[Unit] -Description=LiteLLM - -[Service] -Type=simple -ExecStart=uv --directory=/opt/litellm run litellm --config /opt/litellm/litellm.yaml -Restart=always - -[Install] -WantedBy=multi-user.target -EOF - -systemctl enable -q --now litellm -msg_ok "Created Service" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" diff --git a/install/mediamanager-install.sh b/install/mediamanager-install.sh deleted file mode 100644 index 3aa0df7be..000000000 --- a/install/mediamanager-install.sh +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2025 Community Scripts ORG -# Author: vhsdream -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/maxdorninger/MediaManager - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -read -r -p "${TAB3}Enter the email address of your first admin user: " admin_email -if [[ "$admin_email" ]]; then - EMAIL="$admin_email" -fi - -setup_yq -NODE_VERSION="24" setup_nodejs -setup_uv -PG_VERSION="17" setup_postgresql - -msg_info "Setting up PostgreSQL" -DB_NAME="mm_db" -DB_USER="mm_user" -DB_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" -$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" -$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER TEMPLATE template0;" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" -{ - echo "MediaManager Credentials" - echo "MediaManager Database User: $DB_USER" - echo "MediaManager Database Password: $DB_PASS" - echo "MediaManager Database Name: $DB_NAME" -} >>~/mediamanager.creds -msg_ok "Set up PostgreSQL" - -fetch_and_deploy_gh_release "MediaManager" "maxdorninger/MediaManager" "tarball" "latest" "/opt/mediamanager" - -msg_info "Configuring MediaManager" -MM_DIR="/opt/mm" -MEDIA_DIR="${MM_DIR}/media" -export CONFIG_DIR="${MM_DIR}/config" -export FRONTEND_FILES_DIR="${MM_DIR}/web/build" -export BASE_PATH="" -export PUBLIC_VERSION="" -export PUBLIC_API_URL="${BASE_PATH}/api/v1" -export BASE_PATH="${BASE_PATH}/web" -cd /opt/mediamanager/web -$STD npm ci -$STD npm run build -mkdir -p {"$MM_DIR"/web,"$MEDIA_DIR","$CONFIG_DIR"} -cp -r build "$FRONTEND_FILES_DIR" - -export BASE_PATH="" -export VIRTUAL_ENV="${MM_DIR}/venv" -cd /opt/mediamanager -cp -r {media_manager,alembic*} "$MM_DIR" -$STD /usr/local/bin/uv venv "$VIRTUAL_ENV" -$STD /usr/local/bin/uv sync --locked --active -msg_ok "Configured MediaManager" - -msg_info "Creating config and start script" -LOCAL_IP="$(hostname -I | awk '{print $1}')" -SECRET="$(openssl rand -hex 32)" -sed -e "s/localhost:8/$LOCAL_IP:8/g" \ - -e "s|/data/|$MEDIA_DIR/|g" \ - -e 's/"db"/"localhost"/' \ - -e "s/user = \"MediaManager\"/user = \"$DB_USER\"/" \ - -e "s/password = \"MediaManager\"/password = \"$DB_PASS\"/" \ - -e "s/dbname = \"MediaManager\"/dbname = \"$DB_NAME\"/" \ - -e "/^token_secret/s/=.*/= \"$SECRET\"/" \ - -e "s/admin@example.com/$EMAIL/" \ - -e '/^admin_emails/s/, .*/]/' \ - /opt/mediamanager/config.example.toml >"$CONFIG_DIR"/config.toml - -mkdir -p "$MEDIA_DIR"/{images,tv,movies,torrents} - -cat <"$MM_DIR"/start.sh -#!/usr/bin/env bash - -export CONFIG_DIR="$CONFIG_DIR" -export FRONTEND_FILES_DIR="$FRONTEND_FILES_DIR" -export BASE_PATH="" - -cd "$MM_DIR" -source ./venv/bin/activate -/usr/local/bin/uv run alembic upgrade head -/usr/local/bin/uv run fastapi run ./media_manager/main.py --port 8000 -EOF -chmod +x "$MM_DIR"/start.sh -msg_ok "Created config and start script" - -msg_info "Creating service" -cat </etc/systemd/system/mediamanager.service -[Unit] -Description=MediaManager Backend Service -After=network.target - -[Service] -Type=simple -WorkingDirectory=${MM_DIR} -ExecStart=/usr/bin/bash start.sh - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now mediamanager -msg_ok "Created service" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" diff --git a/install/nginxproxymanager-install.sh b/install/nginxproxymanager-install.sh deleted file mode 100644 index 9aaf4cc40..000000000 --- a/install/nginxproxymanager-install.sh +++ /dev/null @@ -1,171 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://nginxproxymanager.com/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt-get update -$STD apt-get -y install \ - ca-certificates \ - apache2-utils \ - logrotate \ - build-essential \ - jq \ - git -msg_ok "Installed Dependencies" - -NODE_VERSION="16" NODE_MODULE="yarn" setup_nodejs -PYTHON_VERSION="3.12" setup_uv -fetch_and_deploy_gh_release "nginxproxymanager" "NginxProxyManager/nginx-proxy-manager" "tarball" "latest" "/tmp/nginxproxymanager" - -msg_info "Installing Python Dependencies" -$STD apt-get install -y \ - python3 \ - python3-dev \ - python3-venv -msg_ok "Installed Python Dependencies" - -msg_info "Setting up Certbot Environment" -$STD uv venv /opt/certbot -$STD uv pip install --python \ - certbot \ - certbot-dns-cloudflare \ - certbot-dns-multi -msg_ok "Certbot Environment Ready" - -msg_info "Installing Openresty" -VERSION="$(awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release)" -curl -fsSL "https://openresty.org/package/pubkey.gpg" | gpg --dearmor -o /etc/apt/trusted.gpg.d/openresty-archive-keyring.gpg -echo -e "deb http://openresty.org/package/debian $VERSION openresty" >/etc/apt/sources.list.d/openresty.list -$STD apt-get update -$STD apt-get -y install openresty -msg_ok "Installed Openresty" - -msg_info "Setting up Environment" -ln -sf /usr/bin/python3 /usr/bin/python -ln -sf /opt/certbot/bin/certbot /usr/bin/certbot -ln -sf /usr/local/openresty/nginx/sbin/nginx /usr/sbin/nginx -ln -sf /usr/local/openresty/nginx/ /etc/nginx -sed -i 's+^daemon+#daemon+g' /tmp/nginxproxymanager/docker/rootfs/etc/nginx/nginx.conf -NGINX_CONFS=$(find "$(pwd)" -type f -name "*.conf") -for NGINX_CONF in $NGINX_CONFS; do - sed -i 's+include conf.d+include /etc/nginx/conf.d+g' "$NGINX_CONF" -done - -mkdir -p /var/www/html /etc/nginx/logs -cd /tmp/nginxproxymanager -cp -r docker/rootfs/var/www/html/* /var/www/html/ -cp -r docker/rootfs/etc/nginx/* /etc/nginx/ -cp docker/rootfs/etc/letsencrypt.ini /etc/letsencrypt.ini -cp docker/rootfs/etc/logrotate.d/nginx-proxy-manager /etc/logrotate.d/nginx-proxy-manager -ln -sf /etc/nginx/nginx.conf /etc/nginx/conf/nginx.conf -rm -f /etc/nginx/conf.d/dev.conf - -mkdir -p /tmp/nginx/body \ - /run/nginx \ - /data/nginx \ - /data/custom_ssl \ - /data/logs \ - /data/access \ - /data/nginx/default_host \ - /data/nginx/default_www \ - /data/nginx/proxy_host \ - /data/nginx/redirection_host \ - /data/nginx/stream \ - /data/nginx/dead_host \ - /data/nginx/temp \ - /var/lib/nginx/cache/public \ - /var/lib/nginx/cache/private \ - /var/cache/nginx/proxy_temp - -chmod -R 777 /var/cache/nginx -chown root /tmp/nginx - -echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" {print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf);" >/etc/nginx/conf.d/include/resolvers.conf - -if [ ! -f /data/nginx/dummycert.pem ] || [ ! -f /data/nginx/dummykey.pem ]; then - openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 -subj "/O=Nginx Proxy Manager/OU=Dummy Certificate/CN=localhost" -keyout /data/nginx/dummykey.pem -out /data/nginx/dummycert.pem &>/dev/null -fi - -mkdir -p /app/global /app/frontend/images -cd /tmp/nginxproxymanager -cp -r backend/* /app -cp -r global/* /app/global -msg_ok "Set up Environment" - -msg_info "Building Frontend" -cd /tmp/nginxproxymanager/frontend -$STD yarn install --frozen-lockfile -$STD yarn build -cp -r dist/* /app/frontend -cp -r app-images/* /app/frontend/images -msg_ok "Built Frontend" - -msg_info "Initializing Backend" -rm -rf /app/config/default.json -if [ ! -f /app/config/production.json ]; then - cat <<'EOF' >/app/config/production.json -{ - "database": { - "engine": "knex-native", - "knex": { - "client": "sqlite3", - "connection": { - "filename": "/data/database.sqlite" - } - } - } -} -EOF -fi -cd /app -$STD yarn install --production -msg_ok "Initialized Backend" - -msg_info "Creating Service" -cat <<'EOF' >/lib/systemd/system/npm.service -[Unit] -Description=Nginx Proxy Manager -After=network.target -Wants=openresty.service - -[Service] -Type=simple -Environment=NODE_ENV=production -Environment=NODE_OPTIONS=--openssl-legacy-provider -ExecStartPre=-mkdir -p /tmp/nginx/body /data/letsencrypt-acme-challenge -ExecStart=/usr/bin/node index.js --abort_on_uncaught_exception --max_old_space_size=1024 -WorkingDirectory=/app -Restart=on-failure - -[Install] -WantedBy=multi-user.target -EOF -msg_ok "Created Service" - -motd_ssh -customize - -msg_info "Starting Services" -sed -i 's/user npm/user root/g; s/^pid/#pid/g' /usr/local/openresty/nginx/conf/nginx.conf -sed -r -i 's/^([[:space:]]*)su npm npm/\1#su npm npm/g;' /etc/logrotate.d/nginx-proxy-manager -systemctl enable -q --now openresty -systemctl enable -q --now npm -msg_ok "Started Services" - -msg_info "Cleaning up" -rm -rf /tmp/* -systemctl restart openresty -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" diff --git a/install/proxmox-backup-server-install.sh b/install/proxmox-backup-server-install.sh deleted file mode 100644 index cac095b99..000000000 --- a/install/proxmox-backup-server-install.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://www.proxmox.com/en/proxmox-backup-server - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -read -rp "${TAB3}Do you want to use the Enterprise repository (requires valid subscription key)? [y/N]: " USE_ENTERPRISE_REPO - -msg_info "Installing Proxmox Backup Server" -curl -fsSL https://enterprise.proxmox.com/debian/proxmox-release-bookworm.gpg | - gpg --dearmor -o /etc/apt/trusted.gpg.d/proxmox-release-bookworm.gpg -if [[ "$USE_ENTERPRISE_REPO" =~ ^([yY].*)$ ]]; then - echo "deb https://enterprise.proxmox.com/debian/pbs bookworm pbs-enterprise" >/etc/apt/sources.list.d/pbs-enterprise.list - msg_ok "Enterprise repository enabled. Make sure your subscription key is installed." -else - echo "deb http://download.proxmox.com/debian/pbs bookworm pbs-no-subscription" >>/etc/apt/sources.list - msg_ok "No-subscription repository enabled." -fi - -$STD apt-get update -$STD apt-get install -y proxmox-backup-server -msg_ok "Installed Proxmox Backup Server" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" diff --git a/install/tracktor-install.sh b/install/tracktor-install.sh deleted file mode 100644 index d0d432d9c..000000000 --- a/install/tracktor-install.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2025 Community Scripts ORG -# Author: CrazyWolf13 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://tracktor.bytedge.in - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -setup_nodejs -fetch_and_deploy_gh_release "tracktor" "javedh-dev/tracktor" "tarball" "latest" "/opt/tracktor" - -msg_info "Configuring Tracktor" -cd /opt/tracktor -$STD npm install -$STD npm run build -mkdir /opt/tracktor-data -HOST_IP=$(hostname -I | awk '{print $1}') -cat </opt/tracktor/app/backend/.env -NODE_ENV=production -PUBLIC_DEMO_MODE=false -DB_PATH=/opt/tracktor-data/tracktor.db -# Replace this URL if using behind reverse proxy for https traffic. Though it is optional and should work without changing -PUBLIC_API_BASE_URL=http://$HOST_IP:3000 -# Here add the reverse proxy url as well to avoid cross errors from the app. -CORS_ORIGINS=http://$HOST_IP:3000 -PORT=3000 -EOF -msg_ok "Configured Tracktor" - -msg_info "Creating service" -cat </etc/systemd/system/tracktor.service -[Unit] -Description=Tracktor Service -After=network.target - -[Service] -Type=simple -WorkingDirectory=/opt/tracktor -EnvironmentFile=/opt/tracktor/app/backend/.env -ExecStart=/usr/bin/npm start - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now tracktor -msg_ok "Created service" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" diff --git a/install/traefik-install.sh b/install/traefik-install.sh deleted file mode 100644 index 3f3dc2fbf..000000000 --- a/install/traefik-install.sh +++ /dev/null @@ -1,266 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://traefik.io/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt-get install -y apt-transport-https -msg_ok "Installed Dependencies" - -RELEASE=$(curl -fsSL https://api.github.com/repos/traefik/traefik/releases | grep -oP '"tag_name":\s*"v\K[\d.]+?(?=")' | sort -V | tail -n 1) -msg_info "Installing Traefik v${RELEASE}" -mkdir -p /etc/traefik/{conf.d,ssl,sites-available} -curl -fsSL "https://github.com/traefik/traefik/releases/download/v${RELEASE}/traefik_v${RELEASE}_linux_amd64.tar.gz" -o "traefik_v${RELEASE}_linux_amd64.tar.gz" -tar -C /tmp -xzf traefik*.tar.gz -mv /tmp/traefik /usr/bin/ -rm -rf traefik*.tar.gz -echo "${RELEASE}" >/opt/${APPLICATION}_version.txt -msg_ok "Installed Traefik v${RELEASE}" - -msg_info "Creating Traefik configuration" -cat <<'EOF' >/etc/traefik/traefik.yaml -providers: - file: - directory: /etc/traefik/conf.d/ - watch: true - -entryPoints: - web: - address: ':80' - http: - redirections: - entryPoint: - to: websecure - scheme: https - websecure: - address: ':443' - http: - tls: - certResolver: letsencrypt - # Uncomment below if using cloudflare - /* - forwardedHeaders: - trustedIPs: - - 173.245.48.0/20 - - 103.21.244.0/22 - - 103.22.200.0/22 - - 103.31.101.64/22 - - 141.101.64.0/18 - - 108.162.192.0/18 - - 190.93.240.0/20 - - 188.114.96.0/20 - - 197.234.240.0/22 - - 198.41.128.0/17 - - 162.158.0.0/15 - - 104.16.0.0/13 - - 104.16.0.0/13 - - 172.64.0.0/13 - - 131.0.72.0/22 - */ - asDefault: true - traefik: - address: ':8080' - -certificatesResolvers: - letsencrypt: - acme: - email: "foo@bar.com" - storage: /etc/traefik/ssl/acme.json - tlsChallenge: {} - -# Uncomment below if you are using self signed or no certificate -#serversTransport: -# insecureSkipVerify: true - -api: - dashboard: true - insecure: true - -log: - filePath: /var/log/traefik/traefik.log - format: json - level: INFO - -accessLog: - filePath: /var/log/traefik/traefik-access.log - format: json - filters: - statusCodes: - - "200" - - "400-599" - retryAttempts: true - minDuration: "10ms" - bufferingSize: 0 - fields: - headers: - defaultMode: drop - names: - User-Agent: keep -EOF -msg_ok "Created Traefik configuration" - -msg_info "Creating Service" -cat <<'EOF' >/etc/systemd/system/traefik.service -[Unit] -Description=Traefik is an open-source Edge Router that makes publishing your services a fun and easy experience - -[Service] -Type=notify -ExecStart=/usr/bin/traefik --configFile=/etc/traefik/traefik.yaml -Restart=on-failure -ExecReload=/bin/kill -USR1 \$MAINPID - -[Install] -WantedBy=multi-user.target -EOF - -systemctl enable -q --now traefik -msg_ok "Created Service" - -msg_info "Creating site templates" -cat <<'EOF' >/etc/traefik/template.yaml.tpl -http: - routers: - ${hostname}: - rule: Host(`${FQDN}`) - service: ${hostname} - tls: - certResolver: letsencrypt - services: - ${hostname}: - loadbalancer: - servers: - - url: "${URL}" -EOF -msg_ok "Template Created" - -msg_info "Creating Helper Scripts" -cat <<'EOF' >/usr/bin/addsite -#!/bin/bash - -function setup_site() { - hostname="$(whiptail --inputbox "Enter the hostname of the Site" 8 78 --title "Hostname" 3>&1 1>&2 2>&3)" - exitstatus=$? - [[ "$exitstatus" = 1 ]] && return; - FQDN="$(whiptail --inputbox "Enter the FQDN of the Site" 8 78 --title "FQDN" 3>&1 1>&2 2>&3)" - exitstatus=$? - [[ "$exitstatus" = 1 ]] && return; - URL="$(whiptail --inputbox "Enter the URL of the Site (For example http://192.168.x.x:8080)" 8 78 --title "URL" 3>&1 1>&2 2>&3)" - exitstatus=$? - [[ "$exitstatus" = 1 ]] && return; - filename="/etc/traefik/sites-available/${hostname}.yaml" - export hostname FQDN URL - envsubst '${hostname} ${FQDN} ${URL}' < /etc/traefik/template.yaml.tpl > ${filename} -} - -setup_site -EOF - -cat <<'EOF' >/usr/bin/ensite -#!/bin/bash - -function ensite() { - DIR="/etc/traefik/sites-available" - files=( "$DIR"/* ) - - opts=() - for f in "${files[@]}"; do - name="${f##*/}" - opts+=( "$name" "" ) - done - - choice=$(whiptail \ - --title "Select an entry" \ - --menu "Choose a site" \ - 20 60 12 \ - "${opts[@]}" \ - 3>&1 1>&2 2>&3) - - if [ $? -eq 0 ]; then - ln -s $DIR/$choice /etc/traefik/conf.d - else - return - fi -} - -ensite -EOF - -cat <<'EOF' >/usr/bin/dissite -#!/bin/bash - -function dissite() { - DIR="/etc/traefik/conf.d" - files=( "$DIR"/* ) - - opts=() - for f in "${files[@]}"; do - name="${f##*/}" - opts+=( "$name" "" ) - done - - choice=$(whiptail \ - --title "Select an entry" \ - --menu "Choose a site" \ - 20 60 12 \ - "${opts[@]}" \ - 3>&1 1>&2 2>&3) - - if [ $? -eq 0 ]; then - rm $DIR/$choice - else - return - fi -} - -dissite -EOF - -cat <<'EOF' >/usr/bin/editsite -#!/bin/bash - -function edit_site() { - DIR="/etc/traefik/sites-available" - files=( "$DIR"/* ) - - opts=() - for f in "${files[@]}"; do - name="${f##*/}" - opts+=( "$name" "" ) - done - - choice=$(whiptail \ - --title "Select an entry" \ - --menu "Choose a site" \ - 20 60 12 \ - "${opts[@]}" \ - 3>&1 1>&2 2>&3) - - if [ $? -eq 0 ]; then - nano $DIR/$choice - else - return - fi -} - -edit_site -EOF -msg_ok "Helper Scripts Created" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" diff --git a/install/zitadel-install.sh b/install/zitadel-install.sh deleted file mode 100644 index 5b5a84510..000000000 --- a/install/zitadel-install.sh +++ /dev/null @@ -1,149 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: dave-yap -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://zitadel.com/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies (Patience)" -$STD apt-get install -y ca-certificates -msg_ok "Installed Dependecies" - -PG_VERSION="17" setup_postgresql - -msg_info "Installing Postgresql" -DB_NAME="zitadel" -DB_USER="zitadel" -DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) -DB_ADMIN_USER="root" -DB_ADMIN_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) -systemctl start postgresql -$STD sudo -u postgres psql -c "CREATE USER $DB_USER WITH PASSWORD '$DB_PASS';" -$STD sudo -u postgres psql -c "CREATE USER $DB_ADMIN_USER WITH PASSWORD '$DB_ADMIN_PASS' SUPERUSER;" -$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME OWNER $DB_ADMIN_USER;" -{ - echo "Application Credentials" - echo "DB_NAME: $DB_NAME" - echo "DB_USER: $DB_USER" - echo "DB_PASS: $DB_PASS" - echo "DB_ADMIN_USER: $DB_ADMIN_USER" - echo "DB_ADMIN_PASS: $DB_ADMIN_PASS" -} >>~/zitadel.creds -msg_ok "Installed PostgreSQL" - -fetch_and_deploy_gh_release "zitadel" "zitadel/zitadel" "prebuild" "latest" "/usr/local/bin" "zitadel-linux-amd64.tar.gz" - -msg_info "Setting up Zitadel Environments" -mkdir -p /opt/zitadel -echo "/opt/zitadel/config.yaml" >"/opt/zitadel/.config" -head -c 32 < <(openssl rand -base64 48 | tr -dc 'a-zA-Z0-9') >"/opt/zitadel/.masterkey" -{ - echo "Config location: $(cat "/opt/zitadel/.config")" - echo "Masterkey: $(cat "/opt/zitadel/.masterkey")" -} >>~/zitadel.creds -cat </opt/zitadel/config.yaml -Port: 8080 -ExternalPort: 8080 -ExternalDomain: localhost -ExternalSecure: false -TLS: - Enabled: false - KeyPath: "" - Key: "" - CertPath: "" - Cert: "" - -Database: - postgres: - Host: localhost - Port: 5432 - Database: ${DB_NAME} - User: - Username: ${DB_USER} - Password: ${DB_PASS} - SSL: - Mode: disable - RootCert: "" - Cert: "" - Key: "" - Admin: - Username: ${DB_ADMIN_USER} - Password: ${DB_ADMIN_PASS} - SSL: - Mode: disable - RootCert: "" - Cert: "" - Key: "" -DefaultInstance: - Features: - LoginV2: - Required: false -EOF -msg_ok "Installed Zitadel Enviroments" - -msg_info "Creating Services" -cat </etc/systemd/system/zitadel.service -[Unit] -Description=ZITADEL Identiy Server -After=network.target postgresql.service -Wants=postgresql.service - -[Service] -Type=simple -User=zitadel -Group=zitadel -ExecStart=/usr/local/bin/zitadel start --masterkeyFile "/opt/zitadel/.masterkey" --config "/opt/zitadel/config.yaml" -Restart=always -RestartSec=5 -TimeoutStartSec=0 - -# Security Hardening options -ProtectSystem=full -ProtectHome=true -PrivateTmp=true -NoNewPrivileges=true - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q zitadel -msg_ok "Created Services" - -msg_info "Zitadel initial setup" -zitadel start-from-init --masterkeyFile /opt/zitadel/.masterkey --config /opt/zitadel/config.yaml &>/dev/null & -sleep 60 -kill $(lsof -i | awk '/zitadel/ {print $2}' | head -n1) -useradd zitadel -msg_ok "Zitadel initialized" - -msg_info "Set ExternalDomain to current IP and restart Zitadel" -IP=$(ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) -sed -i "0,/localhost/s/localhost/${IP}/" /opt/zitadel/config.yaml -systemctl stop -q zitadel -$STD zitadel setup --masterkeyFile /opt/zitadel/.masterkey --config /opt/zitadel/config.yaml -systemctl restart -q zitadel -msg_ok "Zitadel restarted with ExternalDomain set to current IP" - -msg_info "Create zitadel-rerun.sh" -cat <~/zitadel-rerun.sh -systemctl stop zitadel -timeout --kill-after=5s 15s zitadel setup --masterkeyFile /opt/zitadel/.masterkey --config /opt/zitadel/config.yaml -systemctl restart zitadel -EOF -msg_ok "Bash script for rerunning Zitadel after changing Zitadel config.yaml" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" From 7d679aaef7be549ea348f89bc2cc76a60ed480ec Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 3 Sep 2025 10:12:56 +0200 Subject: [PATCH 0797/1733] Update dispatcharr-install.sh --- install/dispatcharr-install.sh | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/install/dispatcharr-install.sh b/install/dispatcharr-install.sh index 29e9457c7..f5af7f5f6 100644 --- a/install/dispatcharr-install.sh +++ b/install/dispatcharr-install.sh @@ -31,9 +31,10 @@ $STD apt-get install -y \ streamlink msg_ok "Installed Dependencies" -setup_uv +PYTHON_VERSION="3.13" setup_uv NODE_VERSION="22" setup_nodejs PG_VERSION="16" setup_postgresql +fetch_and_deploy_gh_release "dispatcharr" "Dispatcharr/Dispatcharr" msg_info "Set up PostgreSQL Database" DB_NAME=dispatcharr_db @@ -53,21 +54,16 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" } >>~/dispatcharr.creds msg_ok "Set up PostgreSQL Database" -fetch_and_deploy_gh_release "dispatcharr" "Dispatcharr/Dispatcharr" - -#chown -R "$APP_USER:$APP_GROUP" {/etc/dispatcharr,/opt/dispatcharr,/data} - -msg_info "Install Python Requirements" +msg_info "Setup Python Requirements" mkdir -p /data/{db,epgs,logos,m3us,recordings,uploads} mkdir -p /etc/dispatcharr sed -i 's/program\[\x27channel_id\x27\]/program["channel_id"]/g' "/opt/dispatcharr/apps/output/views.py" cd /opt/dispatcharr -python3 -m venv env -source env/bin/activate - -$STD pip install --upgrade pip -$STD pip install -r requirements.txt -$STD pip install gunicorn +UV_PY="${PYTHON_VERSION:-3.13}" +$STD uv python install "$UV_PY" +$STD uv venv --python "$UV_PY" .venv +$STD uv pip install --python ./.venv/bin/python -r requirements.txt +$STD uv pip install --python ./.venv/bin/python gunicorn gevent celery daphne ln -sf /usr/bin/ffmpeg /opt/dispatcharr/env/bin/ffmpeg msg_ok "Python Requirements Installed" @@ -79,13 +75,12 @@ msg_ok "Built Frontend" msg_info "Running Django Migrations" cd /opt/dispatcharr -source env/bin/activate set -o allexport source /etc/dispatcharr/dispatcharr.env set +o allexport -$STD python manage.py migrate --noinput -$STD python manage.py collectstatic --noinput +$STD ./.venv/bin/python manage.py migrate --noinput +$STD ./.venv/bin/python manage.py collectstatic --noinput msg_ok "Migrations Complete" msg_info "Configuring Nginx" From fbb093844e9c85f7644838741dff2c6cb568e4d4 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 3 Sep 2025 10:23:46 +0200 Subject: [PATCH 0798/1733] fixes --- ct/uhf.sh | 37 +++++++++++++++++++------------------ install/uhf-install.sh | 5 ++--- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/ct/uhf.sh b/ct/uhf.sh index 308c62964..09a30202d 100644 --- a/ct/uhf.sh +++ b/ct/uhf.sh @@ -27,28 +27,29 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - msg_info "Stopping ${APP}" - systemctl stop uhf-server - msg_ok "Stopped ${APP}" + if check_for_gh_release "uhf-server" "swapplications/uhf-server-dist"; then + msg_info "Stopping Service" + systemctl stop uhf-server + msg_ok "Stopped Service" - msg_info "Updating APT packages" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "Package list updated" + msg_info "Updating APT packages" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Package list updated" - fetch_and_deploy_gh_release "comskip" "swapplications/comskip" "prebuild" "latest" "/opt/comskip" "comskip-x64-*.zip" - fetch_and_deploy_gh_release "uhf-server" "swapplications/uhf-server-dist" "prebuild" "latest" "/opt/uhf-server" "UHF.Server-linux-x64-*.zip" + fetch_and_deploy_gh_release "comskip" "swapplications/comskip" "prebuild" "latest" "/opt/comskip" "comskip-x64-*.zip" + fetch_and_deploy_gh_release "uhf-server" "swapplications/uhf-server-dist" "prebuild" "latest" "/opt/uhf-server" "UHF.Server-linux-x64-*.zip" - msg_info "Starting ${APP}" - systemctl start uhf-server - msg_ok "Started ${APP}" + msg_info "Starting Service" + systemctl start uhf-server + msg_ok "Started Service" - msg_info "Cleaning up" - $STD apt-get -y autoremove - $STD apt-get -y autoclean - msg_ok "Cleaned" - - msg_ok "Updated Successfully" + msg_info "Cleaning up" + $STD apt-get -y autoremove + $STD apt-get -y autoclean + msg_ok "Cleaned" + msg_ok "Updated Successfully" + fi exit } diff --git a/install/uhf-install.sh b/install/uhf-install.sh index 8b9ddf84d..c0447cf35 100644 --- a/install/uhf-install.sh +++ b/install/uhf-install.sh @@ -34,9 +34,8 @@ fetch_and_deploy_gh_release "comskip" "swapplications/comskip" "prebuild" "lates fetch_and_deploy_gh_release "uhf-server" "swapplications/uhf-server-dist" "prebuild" "latest" "/opt/uhf-server" "UHF.Server-linux-x64-*.zip" msg_info "Creating Service" -service_path="" cat </etc/systemd/system/uhf-server.service -echo "[Unit] +[Unit] Description=UHF Server service After=syslog.target network-online.target [Service] @@ -47,7 +46,7 @@ ExecStart=/opt/uhf-server/uhf-server [Install] WantedBy=multi-user.target EOF -systemctl enable --now -q uhf-server.service +systemctl enable -q --now uhf-server msg_ok "Created Service" motd_ssh From 64f90572f9f5d2cb53fad52a425d99abad42aacf Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 3 Sep 2025 10:27:09 +0200 Subject: [PATCH 0799/1733] Update uhf-install.sh --- install/uhf-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/uhf-install.sh b/install/uhf-install.sh index c0447cf35..f6bb6ad3e 100644 --- a/install/uhf-install.sh +++ b/install/uhf-install.sh @@ -14,7 +14,7 @@ network_check update_os msg_info "Installing Dependencies" -setup_ffmpeg +apt install -y ffmpeg msg_ok "Installed Dependencies" msg_info "Setting Up UHF Server Environment" From 1fc593ef3849295a2fa206b598bd3aae688f4ffc Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 3 Sep 2025 10:27:19 +0200 Subject: [PATCH 0800/1733] Update uhf-install.sh --- install/uhf-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/uhf-install.sh b/install/uhf-install.sh index f6bb6ad3e..e414878c1 100644 --- a/install/uhf-install.sh +++ b/install/uhf-install.sh @@ -14,7 +14,7 @@ network_check update_os msg_info "Installing Dependencies" -apt install -y ffmpeg +$STD apt install -y ffmpeg msg_ok "Installed Dependencies" msg_info "Setting Up UHF Server Environment" From 8739822db602a72cf3320c17b3c5749956db8137 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 3 Sep 2025 10:38:49 +0200 Subject: [PATCH 0801/1733] Update dispatcharr-install.sh --- install/dispatcharr-install.sh | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/install/dispatcharr-install.sh b/install/dispatcharr-install.sh index f5af7f5f6..98d9435a9 100644 --- a/install/dispatcharr-install.sh +++ b/install/dispatcharr-install.sh @@ -54,16 +54,21 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" } >>~/dispatcharr.creds msg_ok "Set up PostgreSQL Database" -msg_info "Setup Python Requirements" -mkdir -p /data/{db,epgs,logos,m3us,recordings,uploads} -mkdir -p /etc/dispatcharr -sed -i 's/program\[\x27channel_id\x27\]/program["channel_id"]/g' "/opt/dispatcharr/apps/output/views.py" -cd /opt/dispatcharr +msg_info "Setup Python (uv) requirements (system)" UV_PY="${PYTHON_VERSION:-3.13}" $STD uv python install "$UV_PY" -$STD uv venv --python "$UV_PY" .venv -$STD uv pip install --python ./.venv/bin/python -r requirements.txt -$STD uv pip install --python ./.venv/bin/python gunicorn gevent celery daphne +cd /opt/dispatcharr +PYPI_URL="https://pypi.org/simple" +mapfile -t EXTRA_INDEX_URLS < <(grep -E '^(--(extra-)?index-url|-i)\s' requirements.txt 2>/dev/null | awk '{print $2}' | sed 's#/*$##') + +UV_INDEX_ARGS=(--index-url "$PYPI_URL" --index-strategy unsafe-best-match) +for u in "${EXTRA_INDEX_URLS[@]}"; do + [[ -n "$u" && "$u" != "$PYPI_URL" ]] && UV_INDEX_ARGS+=(--extra-index-url "$u") +done +if [[ -f requirements.txt ]]; then + $STD uv pip install --system "${UV_INDEX_ARGS[@]}" -r requirements.txt +fi +$STD uv pip install --system "${UV_INDEX_ARGS[@]}" gunicorn gevent celery daphne ln -sf /usr/bin/ffmpeg /opt/dispatcharr/env/bin/ffmpeg msg_ok "Python Requirements Installed" From aa4c843b1e105dd7ca8c8bdc2f53a732d9de169f Mon Sep 17 00:00:00 2001 From: vhsdream Date: Wed, 3 Sep 2025 11:13:35 -0400 Subject: [PATCH 0802/1733] Autocaliweb: export VIRTUAL_ENV in update --- ct/autocaliweb.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/autocaliweb.sh b/ct/autocaliweb.sh index e7f33ba0c..e2aa97351 100644 --- a/ct/autocaliweb.sh +++ b/ct/autocaliweb.sh @@ -37,7 +37,7 @@ function update_script() { msg_ok "Stopped Services" INSTALL_DIR="/opt/autocaliweb" - VIRTUAL_ENV="${INSTALL_DIR}/venv" + export VIRTUAL_ENV="${INSTALL_DIR}/venv" $STD tar -cf ~/autocaliweb_bkp.tar "$INSTALL_DIR"/{metadata_change_logs,dirs.json,.env,scripts/ingest_watcher.sh,scripts/auto_zipper_wrapper.sh,scripts/metadata_change_detector_wrapper.sh} fetch_and_deploy_gh_release "autocaliweb" "gelbphoenix/autocaliweb" "tarball" "latest" "/opt/autocaliweb" msg_info "Updating ${APP}" From bf951a09ed47e5de8f9858c738faeca96138a50e Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 3 Sep 2025 15:14:05 +0000 Subject: [PATCH 0803/1733] Update .app files --- ct/headers/debian13 | 6 ------ ct/headers/librespeed | 6 ------ ct/headers/litellm | 6 ------ ct/headers/mediamanager | 6 ------ ct/headers/nginxproxymanager | 6 ------ ct/headers/proxmox-backup-server | 6 ------ ct/headers/tracktor | 6 ------ ct/headers/traefik | 6 ------ ct/headers/vikunja | 6 ------ tools/headers/prx-add-ips | 6 ++++++ 10 files changed, 6 insertions(+), 54 deletions(-) delete mode 100644 ct/headers/debian13 delete mode 100644 ct/headers/librespeed delete mode 100644 ct/headers/litellm delete mode 100644 ct/headers/mediamanager delete mode 100644 ct/headers/nginxproxymanager delete mode 100644 ct/headers/proxmox-backup-server delete mode 100644 ct/headers/tracktor delete mode 100644 ct/headers/traefik delete mode 100644 ct/headers/vikunja create mode 100644 tools/headers/prx-add-ips diff --git a/ct/headers/debian13 b/ct/headers/debian13 deleted file mode 100644 index 467acace8..000000000 --- a/ct/headers/debian13 +++ /dev/null @@ -1,6 +0,0 @@ - ____ __ _ ________ - / __ \___ / /_ (_)___ _____ < /__ / - / / / / _ \/ __ \/ / __ `/ __ \/ / /_ < - / /_/ / __/ /_/ / / /_/ / / / / /___/ / -/_____/\___/_.___/_/\__,_/_/ /_/_//____/ - diff --git a/ct/headers/librespeed b/ct/headers/librespeed deleted file mode 100644 index b75b5cec5..000000000 --- a/ct/headers/librespeed +++ /dev/null @@ -1,6 +0,0 @@ - ___ __ __ - / (_) /_ ________ _________ ___ ___ ____/ / - / / / __ \/ ___/ _ \/ ___/ __ \/ _ \/ _ \/ __ / - / / / /_/ / / / __(__ ) /_/ / __/ __/ /_/ / -/_/_/_.___/_/ \___/____/ .___/\___/\___/\__,_/ - /_/ diff --git a/ct/headers/litellm b/ct/headers/litellm deleted file mode 100644 index 1360a8ff9..000000000 --- a/ct/headers/litellm +++ /dev/null @@ -1,6 +0,0 @@ - __ _ __ __ __ __ ___ - / / (_) /____ / / / / / |/ / - / / / / __/ _ \/ / / / / /|_/ / - / /___/ / /_/ __/ /___/ /___/ / / / -/_____/_/\__/\___/_____/_____/_/ /_/ - diff --git a/ct/headers/mediamanager b/ct/headers/mediamanager deleted file mode 100644 index b05f4db74..000000000 --- a/ct/headers/mediamanager +++ /dev/null @@ -1,6 +0,0 @@ - __ ___ ___ __ ___ - / |/ /__ ____/ (_)___ _/ |/ /___ _____ ____ _____ ____ _____ - / /|_/ / _ \/ __ / / __ `/ /|_/ / __ `/ __ \/ __ `/ __ `/ _ \/ ___/ - / / / / __/ /_/ / / /_/ / / / / /_/ / / / / /_/ / /_/ / __/ / -/_/ /_/\___/\__,_/_/\__,_/_/ /_/\__,_/_/ /_/\__,_/\__, /\___/_/ - /____/ diff --git a/ct/headers/nginxproxymanager b/ct/headers/nginxproxymanager deleted file mode 100644 index d68d0c9d8..000000000 --- a/ct/headers/nginxproxymanager +++ /dev/null @@ -1,6 +0,0 @@ - _ __ _ ____ __ ___ - / | / /___ _(_)___ _ __ / __ \_________ _ ____ __ / |/ /___ _____ ____ _____ ____ _____ - / |/ / __ `/ / __ \| |/_/ / /_/ / ___/ __ \| |/_/ / / / / /|_/ / __ `/ __ \/ __ `/ __ `/ _ \/ ___/ - / /| / /_/ / / / / /> < / ____/ / / /_/ /> < / ___ / /_/ / /_/ /_____// // ____(__ ) +/_/ /_/ \____/_/|_/_/ /_/ /_/\____/_/|_| /_/ |_\__,_/\__,_/ /___/_/ /____/ + From ae770c38ea242e2546081a82f394c4e48d92be8d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 4 Sep 2025 14:05:38 +0200 Subject: [PATCH 0804/1733] ssh key feature --- misc/build.func | 117 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 108 insertions(+), 9 deletions(-) diff --git a/misc/build.func b/misc/build.func index 5e84c2267..c14babc27 100644 --- a/misc/build.func +++ b/misc/build.func @@ -281,6 +281,34 @@ exit_script() { exit } +find_host_ssh_keys() { + local glob="${var_ssh_import_glob:-/root/.ssh/*}" + local files=() + local f + local total=0 + local keyre='^(ssh-(rsa|ed25519|ecdsa)|sk-(ssh-ed25519|ecdsa-sha2-nistp256))\s+' + + shopt -s nullglob + for f in $glob; do + # skip directories / unreadable + [[ -f "$f" && -r "$f" ]] || continue + # count lines that look like authorized_keys entries + local c + c=$(awk -v RS='\r?\n' '$0 ~ /^#/ {next} $0 ~ /'"$keyre"'/ {cnt++} END{print cnt+0}' "$f") + if [[ "${c:-0}" -gt 0 ]]; then + files+=("$f") + total=$((total + c)) + fi + done + shopt -u nullglob + + FOUND_HOST_KEY_COUNT="$total" + ( + IFS=: + echo "${files[*]}" + ) +} + # This function allows the user to configure advanced settings for the script. advanced_settings() { whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox --title "Here is an instructional tip:" "To make a selection, use the Spacebar." 8 58 @@ -705,23 +733,48 @@ advanced_settings() { exit_script fi - SSH_AUTHORIZED_KEY="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "SSH Authorized key for root (leave empty for none)" 8 58 --title "SSH Key" 3>&1 1>&2 2>&3)" - - if [[ -z "${SSH_AUTHORIZED_KEY}" ]]; then - SSH_AUTHORIZED_KEY="" + SSH_AUTHORIZED_KEY="" + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno \ + --title "SSH KEY (Manual)" --yesno "Enter a manual SSH public key now?\n(Choose 'No' to skip manual entry)" 10 70); then + SSH_AUTHORIZED_KEY="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Paste a single SSH public key line (ssh-ed25519 ...)" 10 70 --title "SSH Public Key" 3>&1 1>&2 2>&3)" fi - if [[ "$PW" == -password* || -n "$SSH_AUTHORIZED_KEY" ]]; then - if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable Root SSH Access?" 10 58); then + SSH_IMPORT_FILES="" + HOST_KEYS_AVAILABLE="no" + SSH_IMPORT_FILES="$(find_host_ssh_keys)" + if [[ -n "$SSH_IMPORT_FILES" && "${FOUND_HOST_KEY_COUNT:-0}" -gt 0 ]]; then + HOST_KEYS_AVAILABLE="yes" + fi + + SSH_SOURCE="none" + if [[ "$HOST_KEYS_AVAILABLE" == "yes" ]]; then + SSH_SOURCE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ + "Choose how to provision SSH keys for root:" 14 70 4 \ + "none" "No keys" \ + "host" "Import host store (${FOUND_HOST_KEY_COUNT} keys total)" \ + "manual" "Use the entered single key" \ + "both" "Host store + manual key (dedupe)" 3>&1 1>&2 2>&3) || exit_script + else + SSH_SOURCE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ + "No host keys detected; choose manual/none:" 12 70 2 \ + "none" "No keys" \ + "manual" "Use the entered single key" 3>&1 1>&2 2>&3) || exit_script + fi + + # 4) enable SSH? + if [[ "$SSH_SOURCE" != "none" || "$PW" == -password* ]]; then + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno \ + --title "SSH ACCESS" --yesno "Enable Root SSH Access?" 10 58); then SSH="yes" else SSH="no" fi - echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}" else SSH="no" - echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}" fi + echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}" + export SSH_SOURCE SSH_AUTHORIZED_KEY SSH_IMPORT_FILES if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "FUSE Support" --yesno "Enable FUSE support?\nRequired for tools like rclone, mergerfs, AppImage, etc." 10 58); then ENABLE_FUSE="yes" @@ -1369,6 +1422,52 @@ check_container_storage() { fi } +install_ssh_keys_into_ct() { + [[ "$SSH" != "yes" ]] && return 0 + + local tmp="$(mktemp)" any=0 + local keyre='^(ssh-(rsa|ed25519|ecdsa)|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+' + + case "$SSH_SOURCE" in + host | both) + if [[ -n "${SSH_IMPORT_FILES:-}" ]]; then + IFS=: read -r -a _files <<<"$SSH_IMPORT_FILES" + for f in "${_files[@]}"; do + [[ -r "$f" ]] || continue + awk '$0 !~ /^#/ && $0 ~ /'"$keyre"'/ {print $0}' "$f" >>"$tmp" + any=1 + done + fi + ;; + esac + + case "$SSH_SOURCE" in + manual | both) + if [[ -n "${SSH_AUTHORIZED_KEY:-}" ]]; then + echo "${SSH_AUTHORIZED_KEY}" | awk '$0 !~ /^#/ && $0 ~ /'"$keyre"'/ {print $0}' >>"$tmp" + any=1 + fi + ;; + esac + + if [[ "$any" -eq 0 ]]; then + rm -f "$tmp" + msg_warn "No SSH keys to install (skipping)." + return 0 + fi + + sort -u -o "$tmp" "$tmp" + + msg_info "Installing SSH keys into CT ${CTID}" + pct exec "$CTID" -- sh -c 'mkdir -p /root/.ssh && chmod 700 /root/.ssh' + pct push "$CTID" "$tmp" /root/.ssh/authorized_keys >/dev/null 2>&1 || + pct exec "$CTID" -- sh -c "cat > /root/.ssh/authorized_keys" <"$tmp" + pct exec "$CTID" -- sh -c 'chmod 600 /root/.ssh/authorized_keys' + + rm -f "$tmp" + msg_ok "Installed SSH keys into CT ${CTID}" +} + start() { source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) if command -v pveversion >/dev/null 2>&1; then @@ -1753,7 +1852,7 @@ EOF' } fi msg_ok "Customized LXC Container" - + install_ssh_keys_into_ct lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)" } From 399b5d9705c6e9c54453169166c22779bf0adfa0 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 4 Sep 2025 14:19:40 +0200 Subject: [PATCH 0805/1733] Update build.func --- misc/build.func | 114 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 82 insertions(+), 32 deletions(-) diff --git a/misc/build.func b/misc/build.func index c14babc27..4cc9a9fed 100644 --- a/misc/build.func +++ b/misc/build.func @@ -282,25 +282,53 @@ exit_script() { } find_host_ssh_keys() { - local glob="${var_ssh_import_glob:-/root/.ssh/*}" - local files=() - local f - local total=0 - local keyre='^(ssh-(rsa|ed25519|ecdsa)|sk-(ssh-ed25519|ecdsa-sha2-nistp256))\s+' + local glob_override="${var_ssh_import_glob:-}" + local -a candidates=() - shopt -s nullglob - for f in $glob; do - # skip directories / unreadable + if [[ -n "$glob_override" ]]; then + shopt -s nullglob + candidates+=($glob_override) + shopt -u nullglob + else + shopt -s nullglob + candidates+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2) + candidates+=(/root/.ssh/*.pub) + candidates+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*) + shopt -u nullglob + fi + + local -A seen=() + local files=() + local total=0 + local f + + for f in "${candidates[@]}"; do [[ -f "$f" && -r "$f" ]] || continue - # count lines that look like authorized_keys entries + + case "$(basename "$f")" in + known_hosts | known_hosts.* | config) continue ;; + id_*) [[ "$f" != *.pub ]] && continue ;; + esac + local c - c=$(awk -v RS='\r?\n' '$0 ~ /^#/ {next} $0 ~ /'"$keyre"'/ {cnt++} END{print cnt+0}' "$f") - if [[ "${c:-0}" -gt 0 ]]; then - files+=("$f") - total=$((total + c)) + c=$(tr -d '\r' <"$f" | awk ' + /^[[:space:]]*#/ {next} + /^[[:space:]]*$/ {next} + # Startet mit Key-Typ + /^(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/ {cnt++; next} + # Oder startet mit authorized_keys-Optionen und enthält später einen Key + /^(command=|environment=|from=|no-agent-forwarding|no-port-forwarding|no-pty|no-user-rc|no-X11-forwarding|permitopen=|principals=|tunnel=)/ \ + && /(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))/ {cnt++} + END {print cnt+0} + ') + if ((c > 0)); then + [[ -n "${seen[$f]:-}" ]] || { + files+=("$f") + seen[$f]=1 + total=$((total + c)) + } fi done - shopt -u nullglob FOUND_HOST_KEY_COUNT="$total" ( @@ -1425,30 +1453,40 @@ check_container_storage() { install_ssh_keys_into_ct() { [[ "$SSH" != "yes" ]] && return 0 - local tmp="$(mktemp)" any=0 - local keyre='^(ssh-(rsa|ed25519|ecdsa)|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+' - - case "$SSH_SOURCE" in - host | both) + local tmp + tmp="$(mktemp)" || return 1 + local any=0 + if [[ "$SSH_SOURCE" == "host" || "$SSH_SOURCE" == "both" ]]; then if [[ -n "${SSH_IMPORT_FILES:-}" ]]; then IFS=: read -r -a _files <<<"$SSH_IMPORT_FILES" for f in "${_files[@]}"; do [[ -r "$f" ]] || continue - awk '$0 !~ /^#/ && $0 ~ /'"$keyre"'/ {print $0}' "$f" >>"$tmp" + tr -d '\r' <"$f" | awk ' + /^[[:space:]]*#/ {next} + /^[[:space:]]*$/ {next} + # reine Keyzeile + /^(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/ {print; next} + # authorized_keys mit Optionen + /^(command=|environment=|from=|no-agent-forwarding|no-port-forwarding|no-pty|no-user-rc|no-X11-forwarding|permitopen=|principals=|tunnel=)/ \ + && /(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))/ {print} + ' >>"$tmp" any=1 done fi - ;; - esac + fi - case "$SSH_SOURCE" in - manual | both) + if [[ "$SSH_SOURCE" == "manual" || "$SSH_SOURCE" == "both" ]]; then if [[ -n "${SSH_AUTHORIZED_KEY:-}" ]]; then - echo "${SSH_AUTHORIZED_KEY}" | awk '$0 !~ /^#/ && $0 ~ /'"$keyre"'/ {print $0}' >>"$tmp" + printf '%s\n' "$SSH_AUTHORIZED_KEY" | tr -d '\r' | awk ' + /^[[:space:]]*#/ {next} + /^[[:space:]]*$/ {next} + /^(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/ {print; next} + /^(command=|environment=|from=|no-agent-forwarding|no-port-forwarding|no-pty|no-user-rc|no-X11-forwarding|permitopen=|principals=|tunnel=)/ \ + && /(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))/ {print} + ' >>"$tmp" any=1 fi - ;; - esac + fi if [[ "$any" -eq 0 ]]; then rm -f "$tmp" @@ -1456,14 +1494,26 @@ install_ssh_keys_into_ct() { return 0 fi - sort -u -o "$tmp" "$tmp" + # Dedupe + clean EOF + sort -u "$tmp" -o "$tmp" + printf '\n' >>"$tmp" msg_info "Installing SSH keys into CT ${CTID}" - pct exec "$CTID" -- sh -c 'mkdir -p /root/.ssh && chmod 700 /root/.ssh' - pct push "$CTID" "$tmp" /root/.ssh/authorized_keys >/dev/null 2>&1 || - pct exec "$CTID" -- sh -c "cat > /root/.ssh/authorized_keys" <"$tmp" - pct exec "$CTID" -- sh -c 'chmod 600 /root/.ssh/authorized_keys' + pct exec "$CTID" -- sh -c 'mkdir -p /root/.ssh && chmod 700 /root/.ssh' || { + msg_error "prepare /root/.ssh failed" + rm -f "$tmp" + return 1 + } + if ! pct push "$CTID" "$tmp" /root/.ssh/authorized_keys >/dev/null 2>&1; then + pct exec "$CTID" -- sh -c "cat > /root/.ssh/authorized_keys" <"$tmp" || { + msg_error "write authorized_keys failed" + rm -f "$tmp" + return 1 + } + fi + + pct exec "$CTID" -- sh -c 'chmod 600 /root/.ssh/authorized_keys' || true rm -f "$tmp" msg_ok "Installed SSH keys into CT ${CTID}" } From 919eb89681dab5fd5f93b6c474634b958d4de4ad Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 4 Sep 2025 14:41:48 +0200 Subject: [PATCH 0806/1733] Update build.func --- misc/build.func | 106 +++++++++++++++++++++++------------------------- 1 file changed, 50 insertions(+), 56 deletions(-) diff --git a/misc/build.func b/misc/build.func index 4cc9a9fed..9d6015034 100644 --- a/misc/build.func +++ b/misc/build.func @@ -282,54 +282,50 @@ exit_script() { } find_host_ssh_keys() { - local glob_override="${var_ssh_import_glob:-}" - local -a candidates=() + local re='(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))' + local -a files=() cand=() + local g="${var_ssh_import_glob:-}" + local total=0 f base c - if [[ -n "$glob_override" ]]; then - shopt -s nullglob - candidates+=($glob_override) - shopt -u nullglob + shopt -s nullglob + if [[ -n "$g" ]]; then + for pat in $g; do cand+=($pat); done else - shopt -s nullglob - candidates+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2) - candidates+=(/root/.ssh/*.pub) - candidates+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*) - shopt -u nullglob + cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2) + cand+=(/root/.ssh/*.pub) + cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*) fi + shopt -u nullglob - local -A seen=() - local files=() - local total=0 - local f - - for f in "${candidates[@]}"; do + for f in "${cand[@]}"; do [[ -f "$f" && -r "$f" ]] || continue - - case "$(basename "$f")" in + base="$(basename -- "$f")" + case "$base" in known_hosts | known_hosts.* | config) continue ;; id_*) [[ "$f" != *.pub ]] && continue ;; esac - local c + # CRLF safe check for host keys c=$(tr -d '\r' <"$f" | awk ' /^[[:space:]]*#/ {next} /^[[:space:]]*$/ {next} - # Startet mit Key-Typ - /^(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/ {cnt++; next} - # Oder startet mit authorized_keys-Optionen und enthält später einen Key - /^(command=|environment=|from=|no-agent-forwarding|no-port-forwarding|no-pty|no-user-rc|no-X11-forwarding|permitopen=|principals=|tunnel=)/ \ - && /(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))/ {cnt++} - END {print cnt+0} - ') + {print} + ' | grep -E -c '"$re"' || true) + if ((c > 0)); then - [[ -n "${seen[$f]:-}" ]] || { - files+=("$f") - seen[$f]=1 - total=$((total + c)) - } + files+=("$f") + total=$((total + c)) fi done + # Fallback to /root/.ssh/authorized_keys + if ((${#files[@]} == 0)) && [[ -r /root/.ssh/authorized_keys ]]; then + if grep -E -q "$re" /root/.ssh/authorized_keys; then + files+=(/root/.ssh/authorized_keys) + total=$((total + $(grep -E -c "$re" /root/.ssh/authorized_keys || echo 0))) + fi + fi + FOUND_HOST_KEY_COUNT="$total" ( IFS=: @@ -761,39 +757,36 @@ advanced_settings() { exit_script fi - SSH_AUTHORIZED_KEY="" - if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno \ - --title "SSH KEY (Manual)" --yesno "Enter a manual SSH public key now?\n(Choose 'No' to skip manual entry)" 10 70); then - SSH_AUTHORIZED_KEY="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --inputbox "Paste a single SSH public key line (ssh-ed25519 ...)" 10 70 --title "SSH Public Key" 3>&1 1>&2 2>&3)" - fi - - SSH_IMPORT_FILES="" - HOST_KEYS_AVAILABLE="no" + # --- SSH key provisioning (one dialog) --- SSH_IMPORT_FILES="$(find_host_ssh_keys)" - if [[ -n "$SSH_IMPORT_FILES" && "${FOUND_HOST_KEY_COUNT:-0}" -gt 0 ]]; then - HOST_KEYS_AVAILABLE="yes" - fi + HOST_KEYS_AVAILABLE="no" + [[ -n "$SSH_IMPORT_FILES" && "${FOUND_HOST_KEY_COUNT:-0}" -gt 0 ]] && HOST_KEYS_AVAILABLE="yes" + msg_debug "SSH host files: $SSH_IMPORT_FILES (keys=${FOUND_HOST_KEY_COUNT:-0})" - SSH_SOURCE="none" if [[ "$HOST_KEYS_AVAILABLE" == "yes" ]]; then SSH_SOURCE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ - "Choose how to provision SSH keys for root:" 14 70 4 \ - "none" "No keys" \ - "host" "Import host store (${FOUND_HOST_KEY_COUNT} keys total)" \ - "manual" "Use the entered single key" \ - "both" "Host store + manual key (dedupe)" 3>&1 1>&2 2>&3) || exit_script + "Provision SSH keys for root:" 14 72 4 \ + "host" "Use keys from host (${FOUND_HOST_KEY_COUNT} found)" \ + "manual" "Paste a single public key" \ + "both" "Host + Manual (dedupe)" \ + "none" "No keys" 3>&1 1>&2 2>&3) || exit_script else SSH_SOURCE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ - "No host keys detected; choose manual/none:" 12 70 2 \ - "none" "No keys" \ - "manual" "Use the entered single key" 3>&1 1>&2 2>&3) || exit_script + "No host keys detected; choose manual/none:" 12 72 2 \ + "manual" "Paste a single public key" \ + "none" "No keys" 3>&1 1>&2 2>&3) || exit_script fi - # 4) enable SSH? + # Manual-Eingabe nur wenn nötig + SSH_AUTHORIZED_KEY="" + if [[ "$SSH_SOURCE" == "manual" || "$SSH_SOURCE" == "both" ]]; then + SSH_AUTHORIZED_KEY="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Paste one SSH public key line (ssh-ed25519/ssh-rsa/...)" 10 72 --title "SSH Public Key" 3>&1 1>&2 2>&3)" + fi + + # SSH aktivieren, wenn Quelle != none oder PW gesetzt if [[ "$SSH_SOURCE" != "none" || "$PW" == -password* ]]; then - if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno \ - --title "SSH ACCESS" --yesno "Enable Root SSH Access?" 10 58); then + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable root SSH access?" 10 58); then SSH="yes" else SSH="no" @@ -802,6 +795,7 @@ advanced_settings() { SSH="no" fi echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}" + export SSH_SOURCE SSH_AUTHORIZED_KEY SSH_IMPORT_FILES if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "FUSE Support" --yesno "Enable FUSE support?\nRequired for tools like rclone, mergerfs, AppImage, etc." 10 58); then From 584ea1f11c322376c7a37294228d57520fb422dd Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 4 Sep 2025 14:54:42 +0200 Subject: [PATCH 0807/1733] Update build.func --- misc/build.func | 234 +++++++++++++++++++++++++++++++----------------- 1 file changed, 154 insertions(+), 80 deletions(-) diff --git a/misc/build.func b/misc/build.func index 9d6015034..08ed7cc21 100644 --- a/misc/build.func +++ b/misc/build.func @@ -214,6 +214,30 @@ ssh_check() { fi } +install_ssh_keys_into_ct() { + [[ "$SSH" != "yes" ]] && return 0 + + if [[ -n "$SSH_KEYS_FILE" && -s "$SSH_KEYS_FILE" ]]; then + msg_info "Installing selected SSH keys into CT ${CTID}" + pct exec "$CTID" -- sh -c 'mkdir -p /root/.ssh && chmod 700 /root/.ssh' || { + msg_error "prepare /root/.ssh failed" + return 1 + } + pct push "$CTID" "$SSH_KEYS_FILE" /root/.ssh/authorized_keys >/dev/null 2>&1 || + pct exec "$CTID" -- sh -c "cat > /root/.ssh/authorized_keys" <"$SSH_KEYS_FILE" || { + msg_error "write authorized_keys failed" + return 1 + } + pct exec "$CTID" -- sh -c 'chmod 600 /root/.ssh/authorized_keys' || true + msg_ok "Installed SSH keys into CT ${CTID}" + return 0 + fi + + # Fallback: nichts ausgewählt + msg_warn "No SSH keys to install (skipping)." + return 0 +} + base_settings() { # Default Settings CT_TYPE=${var_unprivileged:-"1"} @@ -758,34 +782,83 @@ advanced_settings() { fi # --- SSH key provisioning (one dialog) --- - SSH_IMPORT_FILES="$(find_host_ssh_keys)" - HOST_KEYS_AVAILABLE="no" - [[ -n "$SSH_IMPORT_FILES" && "${FOUND_HOST_KEY_COUNT:-0}" -gt 0 ]] && HOST_KEYS_AVAILABLE="yes" - msg_debug "SSH host files: $SSH_IMPORT_FILES (keys=${FOUND_HOST_KEY_COUNT:-0})" + SSH_KEYS_FILE="$(mktemp)" # ausgewählte Keys landen hier (eine Datei, mehrere Zeilen) + : >"$SSH_KEYS_FILE" - if [[ "$HOST_KEYS_AVAILABLE" == "yes" ]]; then - SSH_SOURCE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ + # 2.1 Default-Files finden und ggf. Auswahl anbieten + IFS=$'\0' read -r -d '' -a _def_files < <(ssh_discover_default_files && printf '\0') + ssh_build_choices_from_files "${_def_files[@]}" + DEF_KEYS_COUNT="$COUNT" + + if [[ "$DEF_KEYS_COUNT" -gt 0 ]]; then + SSH_KEY_MODE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ "Provision SSH keys for root:" 14 72 4 \ - "host" "Use keys from host (${FOUND_HOST_KEY_COUNT} found)" \ + "found" "Select from detected keys (${DEF_KEYS_COUNT})" \ "manual" "Paste a single public key" \ - "both" "Host + Manual (dedupe)" \ + "folder" "Scan another folder (path or glob)" \ "none" "No keys" 3>&1 1>&2 2>&3) || exit_script else - SSH_SOURCE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ + SSH_KEY_MODE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ "No host keys detected; choose manual/none:" 12 72 2 \ "manual" "Paste a single public key" \ "none" "No keys" 3>&1 1>&2 2>&3) || exit_script fi - # Manual-Eingabe nur wenn nötig - SSH_AUTHORIZED_KEY="" - if [[ "$SSH_SOURCE" == "manual" || "$SSH_SOURCE" == "both" ]]; then + case "$SSH_KEY_MODE" in + found) + # Checkliste einzelner Keys + SEL=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT HOST KEYS" \ + --checklist "Select one or more keys to import:" 20 78 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script + for tag in $SEL; do + tag="${tag%\"}" + tag="${tag#\"}" + line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) + [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" + done + ;; + manual) SSH_AUTHORIZED_KEY="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ --inputbox "Paste one SSH public key line (ssh-ed25519/ssh-rsa/...)" 10 72 --title "SSH Public Key" 3>&1 1>&2 2>&3)" + [[ -n "$SSH_AUTHORIZED_KEY" ]] && printf '%s\n' "$SSH_AUTHORIZED_KEY" >>"$SSH_KEYS_FILE" + ;; + folder) + GLOB_PATH="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Enter a folder or glob to scan (e.g. /root/.ssh/*.pub)" 10 72 --title "Scan Folder/Glob" 3>&1 1>&2 2>&3)" + if [[ -n "$GLOB_PATH" ]]; then + # expandiere Globs + shopt -s nullglob + read -r -a _scan_files <<<"$GLOB_PATH" + shopt -u nullglob + if [[ "${#_scan_files[@]}" -gt 0 ]]; then + ssh_build_choices_from_files "${_scan_files[@]}" + if [[ "$COUNT" -gt 0 ]]; then + SEL=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT FOLDER KEYS" \ + --checklist "Select key(s) to import:" 20 78 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script + for tag in $SEL; do + tag="${tag%\"}" + tag="${tag#\"}" + line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) + [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" + done + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "No keys found in: $GLOB_PATH" 8 60 + fi + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "Path/glob returned no files." 8 60 + fi + fi + ;; + none) : ;; + esac + + # Dedupe + sauberer EOF + if [[ -s "$SSH_KEYS_FILE" ]]; then + sort -u -o "$SSH_KEYS_FILE" "$SSH_KEYS_FILE" + printf '\n' >>"$SSH_KEYS_FILE" fi - # SSH aktivieren, wenn Quelle != none oder PW gesetzt - if [[ "$SSH_SOURCE" != "none" || "$PW" == -password* ]]; then + # SSH aktivieren, wenn Keys oder PW + if [[ -s "$SSH_KEYS_FILE" || "$PW" == -password* ]]; then if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable root SSH access?" 10 58); then SSH="yes" else @@ -796,8 +869,7 @@ advanced_settings() { fi echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}" - export SSH_SOURCE SSH_AUTHORIZED_KEY SSH_IMPORT_FILES - + export SSH_KEYS_FILE if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "FUSE Support" --yesno "Enable FUSE support?\nRequired for tools like rclone, mergerfs, AppImage, etc." 10 58); then ENABLE_FUSE="yes" else @@ -1444,72 +1516,74 @@ check_container_storage() { fi } -install_ssh_keys_into_ct() { - [[ "$SSH" != "yes" ]] && return 0 - - local tmp - tmp="$(mktemp)" || return 1 - local any=0 - if [[ "$SSH_SOURCE" == "host" || "$SSH_SOURCE" == "both" ]]; then - if [[ -n "${SSH_IMPORT_FILES:-}" ]]; then - IFS=: read -r -a _files <<<"$SSH_IMPORT_FILES" - for f in "${_files[@]}"; do - [[ -r "$f" ]] || continue - tr -d '\r' <"$f" | awk ' - /^[[:space:]]*#/ {next} - /^[[:space:]]*$/ {next} - # reine Keyzeile - /^(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/ {print; next} - # authorized_keys mit Optionen - /^(command=|environment=|from=|no-agent-forwarding|no-port-forwarding|no-pty|no-user-rc|no-X11-forwarding|permitopen=|principals=|tunnel=)/ \ - && /(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))/ {print} - ' >>"$tmp" - any=1 - done - fi - fi - - if [[ "$SSH_SOURCE" == "manual" || "$SSH_SOURCE" == "both" ]]; then - if [[ -n "${SSH_AUTHORIZED_KEY:-}" ]]; then - printf '%s\n' "$SSH_AUTHORIZED_KEY" | tr -d '\r' | awk ' - /^[[:space:]]*#/ {next} - /^[[:space:]]*$/ {next} - /^(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/ {print; next} - /^(command=|environment=|from=|no-agent-forwarding|no-port-forwarding|no-pty|no-user-rc|no-X11-forwarding|permitopen=|principals=|tunnel=)/ \ - && /(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))/ {print} - ' >>"$tmp" - any=1 - fi - fi - - if [[ "$any" -eq 0 ]]; then - rm -f "$tmp" - msg_warn "No SSH keys to install (skipping)." - return 0 - fi - - # Dedupe + clean EOF - sort -u "$tmp" -o "$tmp" - printf '\n' >>"$tmp" - - msg_info "Installing SSH keys into CT ${CTID}" - pct exec "$CTID" -- sh -c 'mkdir -p /root/.ssh && chmod 700 /root/.ssh' || { - msg_error "prepare /root/.ssh failed" - rm -f "$tmp" - return 1 - } - - if ! pct push "$CTID" "$tmp" /root/.ssh/authorized_keys >/dev/null 2>&1; then - pct exec "$CTID" -- sh -c "cat > /root/.ssh/authorized_keys" <"$tmp" || { - msg_error "write authorized_keys failed" - rm -f "$tmp" - return 1 +ssh_extract_keys_from_file() { + local f="$1" + [[ -r "$f" ]] || return 0 + tr -d '\r' <"$f" | awk ' + /^[[:space:]]*#/ {next} + /^[[:space:]]*$/ {next} + # nackt: typ base64 [comment] + /^(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/ {print; next} + # mit Optionen: finde ab erstem Key-Typ + { + match($0, /(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/) + if (RSTART>0) { print substr($0, RSTART) } } - fi + ' +} - pct exec "$CTID" -- sh -c 'chmod 600 /root/.ssh/authorized_keys' || true - rm -f "$tmp" - msg_ok "Installed SSH keys into CT ${CTID}" +ssh_build_choices_from_files() { + local -a files=("$@") + CHOICES=() + COUNT=0 + MAPFILE="$(mktemp)" + local id key typ fp cmt base ln=0 + + for f in "${files[@]}"; do + [[ -f "$f" && -r "$f" ]] || continue + base="$(basename -- "$f")" + case "$base" in + known_hosts | known_hosts.* | config) continue ;; + id_*) [[ "$f" != *.pub ]] && continue ;; + esac + + # jede Key-Zeile mappen -> K| + while IFS= read -r key; do + [[ -n "$key" ]] || continue + + # Fingerprint/Type/Comment hübsch machen (best effort) + typ="" + fp="" + cmt="" + # Nur der pure Key-Teil (ohne Optionen) ist schon in 'key' enthalten + read -r _typ _b64 _cmt <<<"$key" + typ="${_typ:-key}" + cmt="${_cmt:-}" + # Fingerprint via ssh-keygen (falls verfügbar) + if command -v ssh-keygen >/dev/null 2>&1; then + fp="$(printf '%s\n' "$key" | ssh-keygen -lf - 2>/dev/null | awk '{print $2}')" + fi + # Label kürzen + [[ ${#cmt} -gt 40 ]] && cmt="${cmt:0:37}..." + + ln=$((ln + 1)) + COUNT=$((COUNT + 1)) + id="K${COUNT}" + echo "${id}|${key}" >>"$MAPFILE" + CHOICES+=("$id" "[$typ] ${fp:+$fp }${cmt:+$cmt }— ${base}" "OFF") + done < <(ssh_extract_keys_from_file "$f") + done +} + +# Sucht Standard-Quellen (authorized_keys, *.pub, /etc/ssh/authorized_keys.d/*) +ssh_discover_default_files() { + local -a cand=() + shopt -s nullglob + cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2) + cand+=(/root/.ssh/*.pub) + cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*) + shopt -u nullglob + printf '%s\0' "${cand[@]}" } start() { From e5ef51e69dd75bed48667b70c6926f63d9b5d54d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 5 Sep 2025 09:03:10 +0200 Subject: [PATCH 0808/1733] - --- ct/leantime.sh | 28 +++++++++++++++++++++------- install/leantime-install.sh | 5 +---- misc/build.func | 11 ++++------- 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/ct/leantime.sh b/ct/leantime.sh index f49c503a2..6a0295715 100644 --- a/ct/leantime.sh +++ b/ct/leantime.sh @@ -1,6 +1,5 @@ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) - # Copyright (c) 2021-2025 community-scripts ORG # Author: Stroopwafe1 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -25,16 +24,31 @@ function update_script() { check_container_storage check_container_resources - if [[ ! -d /opt/${APP} ]]; then + if [[ ! -d /opt/leantime ]]; then msg_error "No ${APP} Installation Found!" exit fi - msg_info "Creating Backup" - mariadb-dump leantime >"/opt/${APP}_db_backup_$(date +%F).sql" - tar -czf "/opt/${APP}_backup_$(date +%F).tar.gz" "/opt/${APP}" - msg_ok "Backup Created" - fetch_and_deploy_gh_release "$APP" "Leantime/leantime" "prebuild" "latest" "/opt/${APP}" Leantime-v[0-9].[0-9].[0-9].tar.gz + if check_for_gh_release "leantime" "Leantime/leantime"; then + msg_info "Creating Backup" + mariadb-dump leantime >"/opt/${APP}_db_backup_$(date +%F).sql" + tar -czf "/opt/${APP}_backup_$(date +%F).tar.gz" "/opt/${APP}" + mv /opt/leantime /opt/leantime_bak + msg_ok "Backup Created" + + fetch_and_deploy_gh_release "leantime" "Leantime/leantime" "prebuild" "latest" "/opt/leantime" Leantime*.tar.gz + + msg_info "Restoring Config & Permissions" + mv /opt/leantime_bak/config/.env /opt/leantime/config/.env + chown -R www-data:www-data "/opt/leantime" + chmod -R 750 "/opt/leantime" + msg_ok "Restored Config & Permissions" + + msg_info "Removing Backup" + rm -rf /opt/leantime_bak + msg_ok "Removed Backup" + msg_ok "Updated Successfully" + fi exit } diff --git a/install/leantime-install.sh b/install/leantime-install.sh index 8c72b30af..db67e925e 100644 --- a/install/leantime-install.sh +++ b/install/leantime-install.sh @@ -36,7 +36,6 @@ fetch_and_deploy_gh_release "leantime" "Leantime/leantime" "prebuild" "latest" " msg_info "Setup Leantime" chown -R www-data:www-data "/opt/leantime" chmod -R 750 "/opt/leantime" - cat </etc/apache2/sites-enabled/000-default.conf ServerAdmin webmaster@localhost @@ -58,19 +57,17 @@ cat </etc/apache2/sites-enabled/000-default.conf CustomLog /var/log/apache2/access.log combined EOF - mv "/opt/leantime/config/sample.env" "/opt/leantime/config/.env" sed -i -e "s|^LEAN_DB_DATABASE.*|LEAN_DB_DATABASE = '$DB_NAME'|" \ -e "s|^LEAN_DB_USER.*|LEAN_DB_USER = '$DB_USER'|" \ -e "s|^LEAN_DB_PASSWORD.*|LEAN_DB_PASSWORD = '$DB_PASS'|" \ -e "s|^LEAN_SESSION_PASSWORD.*|LEAN_SESSION_PASSWORD = '$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13)'|" \ "/opt/leantime/config/.env" - $STD a2enmod -q proxy_fcgi setenvif rewrite $STD a2enconf -q "php8.4-fpm" sed -i -e "s/^;extension.\(curl\|fileinfo\|gd\|intl\|ldap\|mbstring\|exif\|mysqli\|odbc\|openssl\|pdo_mysql\)/extension=\1/g" "/etc/php/8.4/apache2/php.ini" systemctl restart apache2 -msg_ok "Setup ${APPLICATION}" +msg_ok "Setup leantime" motd_ssh customize diff --git a/misc/build.func b/misc/build.func index 08ed7cc21..766aab331 100644 --- a/misc/build.func +++ b/misc/build.func @@ -782,10 +782,9 @@ advanced_settings() { fi # --- SSH key provisioning (one dialog) --- - SSH_KEYS_FILE="$(mktemp)" # ausgewählte Keys landen hier (eine Datei, mehrere Zeilen) + SSH_KEYS_FILE="$(mktemp)" : >"$SSH_KEYS_FILE" - # 2.1 Default-Files finden und ggf. Auswahl anbieten IFS=$'\0' read -r -d '' -a _def_files < <(ssh_discover_default_files && printf '\0') ssh_build_choices_from_files "${_def_files[@]}" DEF_KEYS_COUNT="$COUNT" @@ -806,9 +805,8 @@ advanced_settings() { case "$SSH_KEY_MODE" in found) - # Checkliste einzelner Keys SEL=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT HOST KEYS" \ - --checklist "Select one or more keys to import:" 20 78 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script + --checklist "Select one or more keys to import:" 20 20 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script for tag in $SEL; do tag="${tag%\"}" tag="${tag#\"}" @@ -825,7 +823,6 @@ advanced_settings() { GLOB_PATH="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ --inputbox "Enter a folder or glob to scan (e.g. /root/.ssh/*.pub)" 10 72 --title "Scan Folder/Glob" 3>&1 1>&2 2>&3)" if [[ -n "$GLOB_PATH" ]]; then - # expandiere Globs shopt -s nullglob read -r -a _scan_files <<<"$GLOB_PATH" shopt -u nullglob @@ -851,13 +848,13 @@ advanced_settings() { none) : ;; esac - # Dedupe + sauberer EOF + # Dedupe + clean EOF if [[ -s "$SSH_KEYS_FILE" ]]; then sort -u -o "$SSH_KEYS_FILE" "$SSH_KEYS_FILE" printf '\n' >>"$SSH_KEYS_FILE" fi - # SSH aktivieren, wenn Keys oder PW + # SSH activate, if keys found or password set if [[ -s "$SSH_KEYS_FILE" || "$PW" == -password* ]]; then if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable root SSH access?" 10 58); then SSH="yes" From 61f98ba7b2bca08e8bf4ade7a9f223e955e98bc2 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 5 Sep 2025 09:40:57 +0200 Subject: [PATCH 0809/1733] Update librenms-install.sh --- install/librenms-install.sh | 46 ++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/install/librenms-install.sh b/install/librenms-install.sh index bea0d4a8e..46f3c5bfd 100644 --- a/install/librenms-install.sh +++ b/install/librenms-install.sh @@ -15,28 +15,28 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - lsb-release \ - ca-certificates \ - acl \ - fping \ - graphviz \ - imagemagick \ - mtr-tiny \ - nginx \ - nmap \ - rrdtool \ - snmp \ - snmpd + lsb-release \ + ca-certificates \ + acl \ + fping \ + graphviz \ + imagemagick \ + mtr-tiny \ + nginx \ + nmap \ + rrdtool \ + snmp \ + snmpd msg_ok "Installed Dependencies" -PHP_VERSION=8.2 PHP_FPM=YES PHP_APACHE=NO PHP_MODULE="gmp,mysql,snmp" setup_php +PHP_VERSION="8.3" PHP_FPM="YES" PHP_MODULE="gmp,mysql,snmp" setup_php setup_mariadb setup_composer -setup_uv +PYTHON_VERSION="3.13" setup_uv msg_info "Installing Python" $STD apt-get install -y \ - python3-{dotenv,pymysql,redis,setuptools,systemd,pip} + python3-{dotenv,pymysql,redis,setuptools,systemd,pip} msg_ok "Installed Python" msg_info "Configuring Database" @@ -47,16 +47,17 @@ $STD mariadb -u root -e "CREATE DATABASE $DB_NAME CHARACTER SET utf8mb4 COLLATE $STD mariadb -u root -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';" $STD mariadb -u root -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" { - echo "LibreNMS-Credentials" - echo "LibreNMS Database User: $DB_USER" - echo "LibreNMS Database Password: $DB_PASS" - echo "LibreNMS Database Name: $DB_NAME" + echo "LibreNMS-Credentials" + echo "LibreNMS Database User: $DB_USER" + echo "LibreNMS Database Password: $DB_PASS" + echo "LibreNMS Database Name: $DB_NAME" } >>~/librenms.creds msg_ok "Configured Database" -msg_info "Setup Librenms" +fetch_and_deploy_gh_release "LibreNMS" "librenms/librenms" + +msg_info "Configuring LibreNMS" $STD useradd librenms -d /opt/librenms -M -r -s "$(which bash)" -fetch_and_deploy_gh_release "librenms/librenms" setfacl -d -m g::rwx /opt/librenms/rrd /opt/librenms/logs /opt/librenms/bootstrap/cache/ /opt/librenms/storage/ setfacl -R -m g::rwx /opt/librenms/rrd /opt/librenms/logs /opt/librenms/bootstrap/cache/ /opt/librenms/storage/ cd /opt/librenms @@ -72,7 +73,7 @@ chown -R librenms:librenms /opt/librenms chmod 771 /opt/librenms setfacl -d -m g::rwx /opt/librenms/bootstrap/cache /opt/librenms/storage /opt/librenms/logs /opt/librenms/rrd chmod -R ug=rwX /opt/librenms/bootstrap/cache /opt/librenms/storage /opt/librenms/logs /opt/librenms/rrd -msg_ok "Setup LibreNMS" +msg_ok "Configured LibreNMS" msg_info "Configure MariaDB" sed -i "/\[mysqld\]/a innodb_file_per_table=1\nlower_case_table_names=0" /etc/mysql/mariadb.conf.d/50-server.cnf @@ -147,7 +148,6 @@ motd_ssh customize msg_info "Cleaning up" -rm -f $tmp_file $STD apt-get -y autoremove $STD apt-get -y autoclean msg_ok "Cleaned" From 2dcc7736dc68fda5596b5b7ef40a644b8d12390d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Oliveira?= Date: Fri, 5 Sep 2025 11:43:19 +0100 Subject: [PATCH 0810/1733] Added ct, install and json files --- ct/stylus.sh | 62 ++++++++++++++++++++++++++++++++ frontend/public/json/stylus.json | 40 +++++++++++++++++++++ install/stylus-install.sh | 62 ++++++++++++++++++++++++++++++++ 3 files changed, 164 insertions(+) create mode 100644 ct/stylus.sh create mode 100644 frontend/public/json/stylus.json create mode 100644 install/stylus-install.sh diff --git a/ct/stylus.sh b/ct/stylus.sh new file mode 100644 index 000000000..8c3c5568b --- /dev/null +++ b/ct/stylus.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: luismco +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/mmastrac/stylus + +APP="Stylus" +var_tags="${var_tags:-network}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-8}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" +var_fuse="${var_fuse:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -d /opt/stylus ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + RELEASE=$(curl -fsSL https://api.github.com/repos/mmastrac/stylus/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') + if [[ ! -f /opt/stylus/stylus_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/stylus/stylus_version.txt)" ]]; then + msg_info "Stopping $APP" + systemctl stop stylus + msg_ok "Stopped $APP" + + msg_info "Updating $APP" + $STD rustup update + $STD cargo install-update -a + $STD su -c "cargo install --list | grep 'stylus' | cut -d' ' -f2 | sed 's/^v//;s/:$//' > /opt/stylus/stylus_version.txt" + msg_ok "Updated $APP" + + msg_info "Starting $APP" + systemctl start stylus + msg_ok "Started $APP" + msg_ok "Update Successful" + else + msg_ok "No update required. ${APP} is already at v${RELEASE}." + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}" diff --git a/frontend/public/json/stylus.json b/frontend/public/json/stylus.json new file mode 100644 index 000000000..4a8efe039 --- /dev/null +++ b/frontend/public/json/stylus.json @@ -0,0 +1,40 @@ +{ + "name": "Stylus", + "slug": "stylus", + "categories": [ + 4 + ], + "date_created": "2025-09-05", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 8000, + "documentation": "https://mmastrac.github.io/stylus/", + "website": "https://github.com/mmastrac/stylus", + "logo": null, + "config_path": "/opt/stylus/", + "description": "Stylus (style + status) is a lightweight status page for infrastructure and networks. Configure a set of bash scripts that test the various parts of your infrastructure, set up visualizations with minimal configuration, and Stylus will generate you a dashboard for your system.", + "install_methods": [ + { + "type": "default", + "script": "ct/stylus.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 8, + "os": "debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Configuration Path: `/opt/stylus/`", + "type": "info" + } + ] +} diff --git a/install/stylus-install.sh b/install/stylus-install.sh new file mode 100644 index 000000000..84b9af32f --- /dev/null +++ b/install/stylus-install.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: luismco +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/mmastrac/stylus + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing dependencies" +$STD apt-get install -y \ + build-essential \ + openssl \ + libssl-dev \ + pkg-config +msg_ok "Installed dependencies" + +msg_info "Installing Rust" +$STD su -c "curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh -s -- -y" +$STD . "$HOME/.cargo/env" +$STD cargo install cargo-update +msg_ok "Installed Rust" + +msg_info "Installing Stylus" +$STD cargo install stylus +$STD stylus init /opt/stylus/ +$STD su -c "cargo install --list | grep 'stylus' | cut -d' ' -f2 | sed 's/^v//;s/:$//' > /opt/stylus/stylus_version.txt" +msg_ok "Installed Stylus" + +msg_info "Creating service" + +cat >/etc/systemd/system/stylus.service < Date: Fri, 5 Sep 2025 12:53:50 +0100 Subject: [PATCH 0811/1733] Added new check_for_gh_release function and changed version file to new home. Config path now points to a file. Revisioned cat command on install script. --- ct/stylus.sh | 6 ++---- frontend/public/json/stylus.json | 4 ++-- install/stylus-install.sh | 7 ++++--- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/ct/stylus.sh b/ct/stylus.sh index 8c3c5568b..69ae4a2d5 100644 --- a/ct/stylus.sh +++ b/ct/stylus.sh @@ -29,9 +29,7 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - - RELEASE=$(curl -fsSL https://api.github.com/repos/mmastrac/stylus/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') - if [[ ! -f /opt/stylus/stylus_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/stylus/stylus_version.txt)" ]]; then + if check_for_gh_release "stylus" "mmastrac/stylus"; then msg_info "Stopping $APP" systemctl stop stylus msg_ok "Stopped $APP" @@ -39,7 +37,7 @@ function update_script() { msg_info "Updating $APP" $STD rustup update $STD cargo install-update -a - $STD su -c "cargo install --list | grep 'stylus' | cut -d' ' -f2 | sed 's/^v//;s/:$//' > /opt/stylus/stylus_version.txt" + $STD su -c "cargo install --list | grep 'stylus' | cut -d' ' -f2 | sed 's/^v//;s/:$//' > ~/.stylus" msg_ok "Updated $APP" msg_info "Starting $APP" diff --git a/frontend/public/json/stylus.json b/frontend/public/json/stylus.json index 4a8efe039..94b2244d2 100644 --- a/frontend/public/json/stylus.json +++ b/frontend/public/json/stylus.json @@ -12,7 +12,7 @@ "documentation": "https://mmastrac.github.io/stylus/", "website": "https://github.com/mmastrac/stylus", "logo": null, - "config_path": "/opt/stylus/", + "config_path": "/opt/stylus/config.yaml", "description": "Stylus (style + status) is a lightweight status page for infrastructure and networks. Configure a set of bash scripts that test the various parts of your infrastructure, set up visualizations with minimal configuration, and Stylus will generate you a dashboard for your system.", "install_methods": [ { @@ -33,7 +33,7 @@ }, "notes": [ { - "text": "Configuration Path: `/opt/stylus/`", + "text": "Configuration Path: `/opt/stylus/config.yaml`", "type": "info" } ] diff --git a/install/stylus-install.sh b/install/stylus-install.sh index 84b9af32f..c515ce28f 100644 --- a/install/stylus-install.sh +++ b/install/stylus-install.sh @@ -30,20 +30,21 @@ msg_ok "Installed Rust" msg_info "Installing Stylus" $STD cargo install stylus $STD stylus init /opt/stylus/ -$STD su -c "cargo install --list | grep 'stylus' | cut -d' ' -f2 | sed 's/^v//;s/:$//' > /opt/stylus/stylus_version.txt" +$STD su -c "cargo install --list | grep 'stylus' | cut -d' ' -f2 | sed 's/^v//;s/:$//' > ~/.stylus" msg_ok "Installed Stylus" msg_info "Creating service" -cat >/etc/systemd/system/stylus.service </etc/systemd/system/stylus.service [Unit] -Description=Stylus +Description=Stylus Service After=network.target [Service] Type=simple ExecStart=$HOME/.cargo/bin/stylus run /opt/stylus Restart=on-failure +RestartSec=5 [Install] WantedBy=multi-user.target From 91fcd53a53f34c83658e1f46495751ed7e686b76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Oliveira?= Date: Fri, 5 Sep 2025 14:16:30 +0100 Subject: [PATCH 0812/1733] Small text change in the update msg_ok to remove duplicate version number info --- ct/stylus.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/stylus.sh b/ct/stylus.sh index 69ae4a2d5..fb1addb21 100644 --- a/ct/stylus.sh +++ b/ct/stylus.sh @@ -45,7 +45,7 @@ function update_script() { msg_ok "Started $APP" msg_ok "Update Successful" else - msg_ok "No update required. ${APP} is already at v${RELEASE}." + msg_ok "No update required. Latest version already installed." fi exit } From 3dbe49b6c21d4bdfd41b40f1ee6b6e571483dbcf Mon Sep 17 00:00:00 2001 From: vhsdream Date: Fri, 5 Sep 2025 11:19:49 -0400 Subject: [PATCH 0813/1733] Autocaliweb: use new version helper --- ct/autocaliweb.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ct/autocaliweb.sh b/ct/autocaliweb.sh index e2aa97351..53cf7fc65 100644 --- a/ct/autocaliweb.sh +++ b/ct/autocaliweb.sh @@ -31,7 +31,7 @@ function update_script() { setup_uv RELEASE=$(curl -fsSL https://api.github.com/repos/gelbphoenix/autocaliweb/releases/latest | jq '.tag_name' | sed 's/^"v//;s/"$//') - if [[ "${RELEASE}" != "$(cat ~/.autocaliweb 2>/dev/null)" ]] || [[ ! -f ~/.autocaliweb ]]; then + if check_for_gh_release "autocaliweb" "gelbphoenix/autocaliweb"; then msg_info "Stopping Services" systemctl stop autocaliweb metadata-change-detector acw-ingest-service acw-auto-zipper msg_ok "Stopped Services" @@ -61,6 +61,7 @@ function update_script() { echo "${CALIBRE_RELEASE#v}" >/"$INSTALL_DIR"/CALIBRE_RELEASE sed 's/^/v/' ~/.autocaliweb >"$INSTALL_DIR"/ACW_RELEASE chown -R acw:acw "$INSTALL_DIR" + rm ~/autocaliweb_bkp.tar msg_ok "Updated $APP" msg_info "Starting Services" @@ -68,8 +69,6 @@ function update_script() { msg_ok "Started Services" msg_ok "Updated Successfully" - else - msg_ok "Already up to date" fi exit } From 923e721889a2d2695b9f2ad70c806596149ee7e4 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Fri, 5 Sep 2025 19:44:33 +0200 Subject: [PATCH 0814/1733] Update resilio-sync --- install/resiliosync-install.sh | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/install/resiliosync-install.sh b/install/resiliosync-install.sh index 8b03ff9e2..d03b58d0a 100644 --- a/install/resiliosync-install.sh +++ b/install/resiliosync-install.sh @@ -13,14 +13,17 @@ setting_up_container network_check update_os -msg_info "Installing Resilio Sync" +msg_info "Setting up Resilio Sync Repository" curl -fsSL "https://linux-packages.resilio.com/resilio-sync/key.asc" >/etc/apt/trusted.gpg.d/resilio-sync.asc echo "deb [signed-by=/etc/apt/trusted.gpg.d/resilio-sync.asc] http://linux-packages.resilio.com/resilio-sync/deb resilio-sync non-free" >/etc/apt/sources.list.d/resilio-sync.list $STD apt-get update +msg_ok "Resilio Sync Repository Setup" + +msg_info "Installing Resilio Sync" $STD apt-get install -y resilio-sync -sed -i 's/127.0.0.1:8888/0.0.0.0:8888/g' /etc/resilio-sync/config.json -$STD systemctl enable resilio-sync -$STD systemctl restart resilio-sync +sed -i "s/127.0.0.1:8888/0.0.0.0:8888/g" /etc/resilio-sync/config.json +systemctl enable -q resilio-sync +systemctl restart resilio-sync msg_ok "Installed Resilio Sync" motd_ssh From 99cac0486ca18db0dbcd5e90552d0c37ce0fc589 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Fri, 5 Sep 2025 20:00:49 +0200 Subject: [PATCH 0815/1733] Fix resiliosync icon --- frontend/public/json/resiliosync.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/resiliosync.json b/frontend/public/json/resiliosync.json index 222786e12..3dcc81e06 100644 --- a/frontend/public/json/resiliosync.json +++ b/frontend/public/json/resiliosync.json @@ -12,7 +12,7 @@ "interface_port": 8888, "documentation": "https://help.resilio.com/", "website": "https://www.resilio.com/sync", - "logo": "https://www.resilio.com/images/sync-appicon-blue-bg.svg", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/resilio-sync.webp", "description": "Fast, reliable, and simple file sync and share solution, powered by P2P technology. Sync files across all your devices without storing them in the cloud.", "install_methods": [ { From 480fe61ee39327b10887f3f9a7df52466a0ae26d Mon Sep 17 00:00:00 2001 From: tremor021 Date: Fri, 5 Sep 2025 20:07:39 +0200 Subject: [PATCH 0816/1733] Update resilio json --- frontend/public/json/resiliosync.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/public/json/resiliosync.json b/frontend/public/json/resiliosync.json index 3dcc81e06..a75ae70cb 100644 --- a/frontend/public/json/resiliosync.json +++ b/frontend/public/json/resiliosync.json @@ -31,5 +31,10 @@ "username": null, "password": null }, - "notes": [] + "notes": [ + { + "text": "After free registration, you will receive a license keyfile to your email address. Upload it into any LXC directory and select on first run.", + "type": "info" + } + ] } From a761c097a3cd36766a49627685c956e68b0eceb6 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Fri, 5 Sep 2025 20:10:46 +0200 Subject: [PATCH 0817/1733] Update resiliosync --- ct/{resiliosync.sh => resilio-sync.sh} | 0 frontend/public/json/{resiliosync.json => resilio-sync.json} | 2 +- install/{resiliosync-install.sh => resilio-sync-install.sh} | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename ct/{resiliosync.sh => resilio-sync.sh} (100%) rename frontend/public/json/{resiliosync.json => resilio-sync.json} (96%) rename install/{resiliosync-install.sh => resilio-sync-install.sh} (100%) diff --git a/ct/resiliosync.sh b/ct/resilio-sync.sh similarity index 100% rename from ct/resiliosync.sh rename to ct/resilio-sync.sh diff --git a/frontend/public/json/resiliosync.json b/frontend/public/json/resilio-sync.json similarity index 96% rename from frontend/public/json/resiliosync.json rename to frontend/public/json/resilio-sync.json index a75ae70cb..0b61fe3d6 100644 --- a/frontend/public/json/resiliosync.json +++ b/frontend/public/json/resilio-sync.json @@ -17,7 +17,7 @@ "install_methods": [ { "type": "default", - "script": "ct/resiliosync.sh", + "script": "ct/resilio-sync.sh", "resources": { "cpu": 2, "ram": 2048, diff --git a/install/resiliosync-install.sh b/install/resilio-sync-install.sh similarity index 100% rename from install/resiliosync-install.sh rename to install/resilio-sync-install.sh From 791db5589a81a96a5a47351846f1f0993ef8c880 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Oliveira?= Date: Sat, 6 Sep 2025 09:56:35 +0100 Subject: [PATCH 0818/1733] Simplified install and update scripts with binaries instead of building it from source. Added app icon. Updated app resources. --- ct/stylus.sh | 11 +++++------ frontend/public/json/stylus.json | 10 +++++----- install/stylus-install.sh | 24 +++++------------------- 3 files changed, 15 insertions(+), 30 deletions(-) diff --git a/ct/stylus.sh b/ct/stylus.sh index fb1addb21..9e8bd99b5 100644 --- a/ct/stylus.sh +++ b/ct/stylus.sh @@ -7,9 +7,9 @@ source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/ APP="Stylus" var_tags="${var_tags:-network}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-8}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-1024}" +var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" @@ -35,9 +35,8 @@ function update_script() { msg_ok "Stopped $APP" msg_info "Updating $APP" - $STD rustup update - $STD cargo install-update -a - $STD su -c "cargo install --list | grep 'stylus' | cut -d' ' -f2 | sed 's/^v//;s/:$//' > ~/.stylus" + fetch_and_deploy_gh_release "stylus" "mmastrac/stylus" "singlefile" "latest" "/usr/bin/" "*_linux_amd64" + msg_ok "Updated $APP" msg_info "Starting $APP" diff --git a/frontend/public/json/stylus.json b/frontend/public/json/stylus.json index 94b2244d2..9ef412442 100644 --- a/frontend/public/json/stylus.json +++ b/frontend/public/json/stylus.json @@ -4,14 +4,14 @@ "categories": [ 4 ], - "date_created": "2025-09-05", + "date_created": "2025-09-06", "type": "ct", "updateable": true, "privileged": false, "interface_port": 8000, "documentation": "https://mmastrac.github.io/stylus/", "website": "https://github.com/mmastrac/stylus", - "logo": null, + "logo": "https: //cdn.jsdelivr.net/gh/selfhst/icons/webp/stylus.webp", "config_path": "/opt/stylus/config.yaml", "description": "Stylus (style + status) is a lightweight status page for infrastructure and networks. Configure a set of bash scripts that test the various parts of your infrastructure, set up visualizations with minimal configuration, and Stylus will generate you a dashboard for your system.", "install_methods": [ @@ -19,9 +19,9 @@ "type": "default", "script": "ct/stylus.sh", "resources": { - "cpu": 2, - "ram": 2048, - "hdd": 8, + "cpu": 1, + "ram": 1024, + "hdd": 2, "os": "debian", "version": "12" } diff --git a/install/stylus-install.sh b/install/stylus-install.sh index c515ce28f..d740fd3b4 100644 --- a/install/stylus-install.sh +++ b/install/stylus-install.sh @@ -13,28 +13,14 @@ setting_up_container network_check update_os -msg_info "Installing dependencies" -$STD apt-get install -y \ - build-essential \ - openssl \ - libssl-dev \ - pkg-config -msg_ok "Installed dependencies" - -msg_info "Installing Rust" -$STD su -c "curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh -s -- -y" -$STD . "$HOME/.cargo/env" -$STD cargo install cargo-update -msg_ok "Installed Rust" - msg_info "Installing Stylus" -$STD cargo install stylus +fetch_and_deploy_gh_release "stylus" "mmastrac/stylus" "singlefile" "latest" "/usr/bin/" "*_linux_amd64" + +msg_info "Configuring Stylus" $STD stylus init /opt/stylus/ -$STD su -c "cargo install --list | grep 'stylus' | cut -d' ' -f2 | sed 's/^v//;s/:$//' > ~/.stylus" -msg_ok "Installed Stylus" +msg_ok "Configured Stylus" msg_info "Creating service" - cat </etc/systemd/system/stylus.service [Unit] Description=Stylus Service @@ -42,7 +28,7 @@ After=network.target [Service] Type=simple -ExecStart=$HOME/.cargo/bin/stylus run /opt/stylus +ExecStart=stylus run /opt/stylus/ Restart=on-failure RestartSec=5 From 42588937730bd310a84a2834004f9339524492c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Oliveira?= Date: Sat, 6 Sep 2025 10:04:49 +0100 Subject: [PATCH 0819/1733] Small correction to the install script --- install/stylus-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/install/stylus-install.sh b/install/stylus-install.sh index d740fd3b4..483b055f0 100644 --- a/install/stylus-install.sh +++ b/install/stylus-install.sh @@ -13,7 +13,6 @@ setting_up_container network_check update_os -msg_info "Installing Stylus" fetch_and_deploy_gh_release "stylus" "mmastrac/stylus" "singlefile" "latest" "/usr/bin/" "*_linux_amd64" msg_info "Configuring Stylus" From 50e90457ea381793fcdf853e31243631584b9b7f Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Sat, 6 Sep 2025 16:00:05 +0200 Subject: [PATCH 0820/1733] Rename resilio-sync.json to resiliosync.json --- frontend/public/json/{resilio-sync.json => resiliosync.json} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename frontend/public/json/{resilio-sync.json => resiliosync.json} (100%) diff --git a/frontend/public/json/resilio-sync.json b/frontend/public/json/resiliosync.json similarity index 100% rename from frontend/public/json/resilio-sync.json rename to frontend/public/json/resiliosync.json From f5bebc286d7dee1b6fb51b7b300b3b60d472cac5 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Sat, 6 Sep 2025 16:11:08 +0200 Subject: [PATCH 0821/1733] Rename resilio-sync-install.sh to resiliosync-install.sh --- install/{resilio-sync-install.sh => resiliosync-install.sh} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename install/{resilio-sync-install.sh => resiliosync-install.sh} (100%) diff --git a/install/resilio-sync-install.sh b/install/resiliosync-install.sh similarity index 100% rename from install/resilio-sync-install.sh rename to install/resiliosync-install.sh From d4d7ae6febc83cebb26e10505db1b5a589316406 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Sat, 6 Sep 2025 16:11:32 +0200 Subject: [PATCH 0822/1733] Rename resilio-sync.sh to resiliosync.sh --- ct/{resilio-sync.sh => resiliosync.sh} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ct/{resilio-sync.sh => resiliosync.sh} (100%) diff --git a/ct/resilio-sync.sh b/ct/resiliosync.sh similarity index 100% rename from ct/resilio-sync.sh rename to ct/resiliosync.sh From d2f1f4643a14e432a00dd4c42ffb8d4d6b71b5c0 Mon Sep 17 00:00:00 2001 From: Christian Benincasa Date: Sat, 6 Sep 2025 11:20:04 -0400 Subject: [PATCH 0823/1733] Add Tunarr script (#871) --- ct/headers/tunarr | 5 ++ ct/tunarr.sh | 76 ++++++++++++++++++++++++++++ frontend/public/json/tunarr.json | 35 +++++++++++++ install/tunarr-install.sh | 85 ++++++++++++++++++++++++++++++++ misc/build.func | 2 +- 5 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 ct/headers/tunarr create mode 100644 ct/tunarr.sh create mode 100644 frontend/public/json/tunarr.json create mode 100644 install/tunarr-install.sh diff --git a/ct/headers/tunarr b/ct/headers/tunarr new file mode 100644 index 000000000..c802ed8e7 --- /dev/null +++ b/ct/headers/tunarr @@ -0,0 +1,5 @@ +______ +/ _ __ / _ ______ ____ ___________ + / / / / / / __ \/ __ `/ ___/ ___/ + / / / /_/ / / / / /_/ / / / / +/_/ \__, _/_/ /_/\__, _/_/ /_/ diff --git a/ct/tunarr.sh b/ct/tunarr.sh new file mode 100644 index 000000000..a3941b1d6 --- /dev/null +++ b/ct/tunarr.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 tteck +# Author: chrisbenincasa +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://tunarr.com/ + +APP="Tunarr" +var_tags="${var_tags:-iptv}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-1024}" +var_disk="${var_disk:-5}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/tunarr ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + if check_for_gh_release "tunarr" "chrisbenincasa/tunarr"; then + msg_info "Stopping ${APP}" + systemctl stop tunarr + msg_ok "Stopped ${APP}" + + msg_info "Creating Backup" + tar -czf "/opt/${APP}_backup_$(date +%F).tar.gz" /usr/.local/share/tunarr + msg_ok "Backup Created" + + fetch_and_deploy_gh_release "tunarr" "chrisbenincasa/tunarr" "singlefile" "latest" "/opt/tunarr" "*linux-x64" + + msg_info "Starting ${APP}" + systemctl start tunarr + msg_ok "Started ${APP}" + + msg_ok "Updated Successfully" + fi + + if check_for_gh_release "ersatztv-ffmpeg" "ErsatzTV/ErsatzTV-ffmpeg"; then + msg_info "Stopping ${APP}" + systemctl stop tunarr + msg_ok "Stopped ${APP}" + + fetch_and_deploy_gh_release "ersatztv-ffmpeg" "ErsatzTV/ErsatzTV-ffmpeg" "prebuild" "latest" "/opt/ErsatzTV-ffmpeg" "*-linux64-gpl-7.1.tar.xz" + + msg_info "Set ErsatzTV-ffmpeg links" + chmod +x /opt/ErsatzTV-ffmpeg/bin/* + ln -sf /opt/ErsatzTV-ffmpeg/bin/ffmpeg /usr/local/bin/ffmpeg + ln -sf /opt/ErsatzTV-ffmpeg/bin/ffplay /usr/local/bin/ffplay + ln -sf /opt/ErsatzTV-ffmpeg/bin/ffprobe /usr/local/bin/ffprobe + msg_ok "ffmpeg links set" + + msg_info "Starting ${APP}" + systemctl start tunarr + msg_ok "Started ${APP}" + msg_ok "Updated Successfully" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}" diff --git a/frontend/public/json/tunarr.json b/frontend/public/json/tunarr.json new file mode 100644 index 000000000..d77f28d19 --- /dev/null +++ b/frontend/public/json/tunarr.json @@ -0,0 +1,35 @@ +{ + "name": "Tunarr", + "slug": "tunarr", + "categories": [ + 13 + ], + "date_created": "2025-09-06", + "type": "ct", + "updateable": true, + "privileged": false, + "config_path": "/opt/tunarr/.env", + "interface_port": 8000, + "documentation": "https://tunarr.com/", + "website": "https://tunarr.com/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/png/tunarr.png", + "description": "Create a classic TV experience using your own media - IPTV backed by Plex/Jellyfin/Emby.", + "install_methods": [ + { + "type": "default", + "script": "ct/tunarr.sh", + "resources": { + "cpu": 2, + "ram": 1024, + "hdd": 5, + "os": "Debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} diff --git a/install/tunarr-install.sh b/install/tunarr-install.sh new file mode 100644 index 000000000..854db7137 --- /dev/null +++ b/install/tunarr-install.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 tteck +# Author: chrisbenincasa +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://tunarr.com/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Setting Up Hardware Acceleration" +$STD apt-get -y install {va-driver-all,ocl-icd-libopencl1,intel-opencl-icd,vainfo,intel-gpu-tools} +if [[ "$CTTYPE" == "0" ]]; then + chgrp video /dev/dri + chmod 755 /dev/dri + chmod 660 /dev/dri/* + $STD adduser $(id -u -n) video + $STD adduser $(id -u -n) render +fi +msg_ok "Set Up Hardware Acceleration" + +read -r -p "${TAB3}Do you need the intel-media-va-driver-non-free driver for HW encoding (Debian 12 only)? " prompt +if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then + msg_info "Installing Intel Hardware Acceleration (non-free)" + cat </etc/apt/sources.list.d/non-free.list + +deb http://deb.debian.org/debian bookworm non-free non-free-firmware +deb-src http://deb.debian.org/debian bookworm non-free non-free-firmware + +deb http://deb.debian.org/debian-security bookworm-security non-free non-free-firmware +deb-src http://deb.debian.org/debian-security bookworm-security non-free non-free-firmware + +deb http://deb.debian.org/debian bookworm-updates non-free non-free-firmware +deb-src http://deb.debian.org/debian bookworm-updates non-free non-free-firmware +EOF + $STD apt-get update + $STD apt-get -y install {intel-media-va-driver-non-free,ocl-icd-libopencl1,intel-opencl-icd,vainfo,intel-gpu-tools} +else + msg_info "Installing Intel Hardware Acceleration" + $STD apt-get -y install {va-driver-all,ocl-icd-libopencl1,intel-opencl-icd,vainfo,intel-gpu-tools} +fi +msg_ok "Installed and Set Up Intel Hardware Acceleration" + +fetch_and_deploy_gh_release "tunarr" "chrisbenincasa/tunarr" "singlefile" "latest" "/opt/tunarr" "*linux-x64" +fetch_and_deploy_gh_release "ersatztv-ffmpeg" "ErsatzTV/ErsatzTV-ffmpeg" "prebuild" "latest" "/opt/ErsatzTV-ffmpeg" "*-linux64-gpl-7.1.tar.xz" + +msg_info "Set ErsatzTV-ffmpeg links" +chmod +x /opt/ErsatzTV-ffmpeg/bin/* +ln -sf /opt/ErsatzTV-ffmpeg/bin/ffmpeg /usr/bin/ffmpeg +ln -sf /opt/ErsatzTV-ffmpeg/bin/ffplay /usr/bin/ffplay +ln -sf /opt/ErsatzTV-ffmpeg/bin/ffprobe /usr/bin/ffprobe +msg_ok "ffmpeg links set" + +msg_info "Creating Service" +cat </etc/systemd/system/tunarr.service +[Unit] +Description=Tunarr Service +After=multi-user.target + +[Service] +Type=simple +User=root +WorkingDirectory=/opt/tunarr +ExecStart=/opt/tunarr/tunarr +Restart=always +RestartSec=30 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now tunarr +msg_ok "Created Service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" diff --git a/misc/build.func b/misc/build.func index 766aab331..a98899137 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1715,7 +1715,7 @@ EOF VAAPI_APPS=( "immich" "Channels" "Emby" "ErsatzTV" "Frigate" "Jellyfin" "Plex" "Scrypted" "Tdarr" "Unmanic" "Ollama" "FileFlows" - "Open WebUI" "Debian" + "Open WebUI" "Debian" "Tunarr" ) is_vaapi_app=false From 9636683fa95a0a2a3c0fcfe1b51d61c433e5eb5c Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sat, 6 Sep 2025 15:20:23 +0000 Subject: [PATCH 0824/1733] Update .app files --- ct/headers/stylus | 6 ++++++ ct/headers/tunarr | 9 +++++---- 2 files changed, 11 insertions(+), 4 deletions(-) create mode 100644 ct/headers/stylus diff --git a/ct/headers/stylus b/ct/headers/stylus new file mode 100644 index 000000000..bb8c5765b --- /dev/null +++ b/ct/headers/stylus @@ -0,0 +1,6 @@ + _____ __ __ + / ___// /___ __/ /_ _______ + \__ \/ __/ / / / / / / / ___/ + ___/ / /_/ /_/ / / /_/ (__ ) +/____/\__/\__, /_/\__,_/____/ + /____/ diff --git a/ct/headers/tunarr b/ct/headers/tunarr index c802ed8e7..27f2a281a 100644 --- a/ct/headers/tunarr +++ b/ct/headers/tunarr @@ -1,5 +1,6 @@ -______ -/ _ __ / _ ______ ____ ___________ + ______ + /_ __/_ ______ ____ ___________ / / / / / / __ \/ __ `/ ___/ ___/ - / / / /_/ / / / / /_/ / / / / -/_/ \__, _/_/ /_/\__, _/_/ /_/ + / / / /_/ / / / / /_/ / / / / +/_/ \__,_/_/ /_/\__,_/_/ /_/ + From 3f5166ea106f2bb9550aa98d8981372322a8a90c Mon Sep 17 00:00:00 2001 From: Daniel Kukula Date: Sun, 7 Sep 2025 14:29:10 +0200 Subject: [PATCH 0825/1733] use new gh_release check, update logging --- ct/livebook.sh | 26 +++++++++++--------------- install/livebook-install.sh | 23 ++++++++--------------- 2 files changed, 19 insertions(+), 30 deletions(-) diff --git a/ct/livebook.sh b/ct/livebook.sh index f6cb59d60..438c14f46 100755 --- a/ct/livebook.sh +++ b/ct/livebook.sh @@ -26,31 +26,28 @@ function update_script() { if [[ ! -f /opt/livebook/.mix/escripts/livebook ]]; then msg_error "No ${APP} Installation Found!" - exit 1 + exit fi - msg_info "Checking for updates..." - RELEASE=$(curl -fsSL https://api.github.com/repos/livebook-dev/livebook/releases/latest | grep "tag_name" | awk -F'"' '{print $4}') + if check_for_gh_release "livebook" "livebook-dev/livebook"; then + msg_info "Stopping ${APP}" + systemctl stop livebook + msg_info "Service stopped" - if [[ "${RELEASE}" != "$(cat /opt/livebook/.livebook 2>/dev/null)" ]]; then - msg_info "Updating ${APP} LXC" + msg_info "Updating container" $STD apt-get update $STD apt-get -y upgrade - msg_ok "Updated ${APP} LXC" + msg_ok "Updated container" - msg_info "Updating ${APP} to ${RELEASE}" + msg_info "Updating ${APP}" source /opt/livebook/.env - cd /opt/livebook || exit 1 + cd /opt/livebook $STD mix escript.install hex livebook --force - echo "$RELEASE" | $STD tee /opt/livebook/.livebook chown -R livebook:livebook /opt/livebook /data - - msg_ok "Successfully updated to ${RELEASE}" - else - msg_ok "No update required. ${APP} is already at ${RELEASE}." + systemctl start livebook + msg_ok "Updated ${APP}" fi - exit } @@ -58,7 +55,6 @@ start build_container description -msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" diff --git a/install/livebook-install.sh b/install/livebook-install.sh index 06cb914f8..1818616e4 100644 --- a/install/livebook-install.sh +++ b/install/livebook-install.sh @@ -30,14 +30,14 @@ msg_info "Installing Erlang and Elixir" mkdir -p /opt/livebook /data export HOME=/opt/livebook -cd /opt/livebook || exit 1 +cd /opt/livebook curl -fsSO https://elixir-lang.org/install.sh $STD sh install.sh elixir@latest otp@latest -RELEASE=$(curl -fsSL https://api.github.com/repos/livebook-dev/livebook/releases/latest | grep "tag_name" | awk -F'"' '{print $4}') ERLANG_VERSION=$(ls /opt/livebook/.elixir-install/installs/otp/ | head -n1) ELIXIR_VERSION=$(ls /opt/livebook/.elixir-install/installs/elixir/ | head -n1) +LIVEBOOK_PASSWORD=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c16) export ERLANG_BIN="/opt/livebook/.elixir-install/installs/otp/$ERLANG_VERSION/bin" export ELIXIR_BIN="/opt/livebook/.elixir-install/installs/elixir/$ELIXIR_VERSION/bin" @@ -47,18 +47,8 @@ $STD mix local.hex --force $STD mix local.rebar --force $STD mix escript.install hex livebook --force -msg_info "Setting Livebook password" -LIVEBOOK_PASSWORD=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c16) - -cat < /opt/livebook/livebook.creds -Livebook-Credentials -Livebook Password: $LIVEBOOK_PASSWORD -EOF -msg_ok "Livebook password stored in /opt/livebook/livebook.creds" - cat < /opt/livebook/.env export HOME=/opt/livebook -export LIVEBOOK_VERSION=$RELEASE export ERLANG_VERSION=$ERLANG_VERSION export ELIXIR_VERSION=$ELIXIR_VERSION export LIVEBOOK_PORT=8080 @@ -93,14 +83,17 @@ RestartSec=5 WantedBy=multi-user.target EOF -msg_info "Setting ownership and permissions" chown -R livebook:livebook /opt/livebook /data -msg_ok "Set ownership and permissions" systemctl enable -q --now livebook msg_ok "Installed Livebook" -msg_ok "Installation completed successfully" +msg_info "Saving Livebook credentials" +cat < /opt/livebook/livebook.creds +Livebook-Credentials +Livebook Password: $LIVEBOOK_PASSWORD +EOF +msg_ok "Livebook password stored in /opt/livebook/livebook.creds" motd_ssh customize From 1bc195864af915cda3d0f8020e32dae224f39714 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 8 Sep 2025 10:46:48 +0200 Subject: [PATCH 0826/1733] test --- ct/npmplus.sh | 58 +++++++++++++++++ install/npmplus-install.sh | 124 +++++++++++++++++++++++++++++++++++++ 2 files changed, 182 insertions(+) create mode 100644 ct/npmplus.sh create mode 100644 install/npmplus-install.sh diff --git a/ct/npmplus.sh b/ct/npmplus.sh new file mode 100644 index 000000000..10a2fe4e6 --- /dev/null +++ b/ct/npmplus.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/ZoeyVid/NPMplus + +APP="NPMplus" +var_tags="${var_tags:-proxy;nginx}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-512}" +var_disk="${var_disk:-3}" +var_os="${var_os:-alpine}" +var_version="${var_version:-3.22}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + UPD=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "UPDATE MODE" --radiolist --cancel-button Exit-Script "Spacebar = Select" 14 60 2 \ + "1" "Check for Alpine Updates" OFF \ + "2" "Update NPMplus Docker Container" ON \ + 3>&1 1>&2 2>&3) + + header_info "$APP" + + case "$UPD" in + "1") + msg_info "Updating Alpine OS" + $STD apk -U upgrade + msg_ok "System updated" + exit + ;; + "2") + msg_info "Updating NPMplus Container" + cd /opt + msg_info "Pulling latest container image" + $STD docker compose pull + msg_info "Recreating container" + $STD docker compose up -d + msg_ok "NPMplus container updated" + exit + ;; + esac + exit 0 +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:81${CL}" diff --git a/install/npmplus-install.sh b/install/npmplus-install.sh new file mode 100644 index 000000000..58688240f --- /dev/null +++ b/install/npmplus-install.sh @@ -0,0 +1,124 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/ZoeyVid/NPMplus + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apk add \ + tzdata \ + gawk \ + yq +msg_ok "Installed Dependencies" + +msg_info "Installing Docker & Compose" +$STD apk add docker +$STD rc-service docker start +$STD rc-update add docker default + +get_latest_release() { + curl -fsSL https://api.github.com/repos/$1/releases/latest | grep '"tag_name":' | cut -d'"' -f4 +} +DOCKER_COMPOSE_LATEST_VERSION=$(get_latest_release "docker/compose") +DOCKER_CONFIG=${DOCKER_CONFIG:-$HOME/.docker} +mkdir -p $DOCKER_CONFIG/cli-plugins +curl -fsSL https://github.com/docker/compose/releases/download/$DOCKER_COMPOSE_LATEST_VERSION/docker-compose-linux-x86_64 -o ~/.docker/cli-plugins/docker-compose +chmod +x $DOCKER_CONFIG/cli-plugins/docker-compose +msg_ok "Installed Docker & Compose" + +msg_info "Fetching NPMplus" +cd /opt +curl -fsSL "https://raw.githubusercontent.com/ZoeyVid/NPMplus/refs/heads/develop/compose.yaml" -o compose.yaml +msg_ok "Fetched NPMplus" + +attempts=0 +while true; do + read -r -p "${TAB3}Enter your TZ Identifier (e.g., Europe/Berlin): " TZ_INPUT + if validate_tz "$TZ_INPUT"; then + break + fi + msg_error "Invalid timezone! Please enter a valid TZ identifier." + + attempts=$((attempts + 1)) + if [[ "$attempts" -ge 3 ]]; then + msg_error "Maximum attempts reached. Exiting." + exit 1 + fi +done + +read -r -p "${TAB3}Enter your ACME Email: " ACME_EMAIL_INPUT + +yq -i " + .services.npmplus.environment |= + (map(select(. != \"TZ=*\" and . != \"ACME_EMAIL=*\")) + + [\"TZ=$TZ_INPUT\", \"ACME_EMAIL=$ACME_EMAIL_INPUT\"]) +" /opt/compose.yaml + +msg_info "Building and Starting NPMplus (Patience)" +$STD docker compose up -d +CONTAINER_ID="" +for i in {1..60}; do + CONTAINER_ID=$(docker ps --filter "name=npmplus" --format "{{.ID}}") + if [[ -n "$CONTAINER_ID" ]]; then + STATUS=$(docker inspect --format '{{.State.Health.Status}}' "$CONTAINER_ID" 2>/dev/null || echo "starting") + if [[ "$STATUS" == "healthy" ]]; then + msg_ok "NPMplus is running and healthy" + break + elif [[ "$STATUS" == "unhealthy" ]]; then + msg_error "NPMplus container is unhealthy! Check logs." + docker logs "$CONTAINER_ID" + exit 1 + fi + fi + sleep 2 + [[ $i -eq 60 ]] && msg_error "NPMplus container did not become healthy within 120s." && docker logs "$CONTAINER_ID" && exit 1 +done +msg_ok "Builded and started NPMplus" + +motd_ssh +customize + +msg_info "Retrieving Default Login (Patience)" +PASSWORD_FOUND=0 + +for i in {1..60}; do + PASSWORD_LINE=$( + { awk '/Creating a new user:/{print; exit}' < <(docker logs "$CONTAINER_ID" 2>&1); } || true + ) + + if [[ -n "${PASSWORD_LINE:-}" ]]; then + PASSWORD="${PASSWORD_LINE#*password: }" + printf 'username: admin@example.org\npassword: %s\n' "$PASSWORD" >/opt/.npm_pwd + msg_ok "Saved default login to /opt/.npm_pwd" + PASSWORD_FOUND=1 + break + fi + sleep 2 +done +if [[ $PASSWORD_FOUND -eq 0 ]]; then + PASSWORD_LINE=$( + timeout 30s bash -c ' + docker logs -f --since=0s --tail=0 "$1" 2>&1 | awk "/Creating a new user:/{print; exit}" + ' _ "$CONTAINER_ID" || true + ) + if [[ -n "${PASSWORD_LINE:-}" ]]; then + PASSWORD="${PASSWORD_LINE#*password: }" + printf 'username: admin@example.org\npassword: %s\n' "$PASSWORD" >/opt/.npm_pwd + msg_ok "Saved default login to /opt/.npm_pwd (live)" + PASSWORD_FOUND=1 + fi +fi + +if [[ $PASSWORD_FOUND -eq 0 ]]; then + msg_error "Could not retrieve default login after 120s." + echo -e "\nYou can manually check the container logs with:\n docker logs $CONTAINER_ID | grep 'Creating a new user:'\n" +fi From f8510656bd8906153ec6cfa5cf53f647123da501 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 8 Sep 2025 14:34:57 +0200 Subject: [PATCH 0827/1733] ttt --- ct/debian.sh | 2 +- misc/build.func | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ct/debian.sh b/ct/debian.sh index d3bbda9b8..c41a2a3db 100644 --- a/ct/debian.sh +++ b/ct/debian.sh @@ -6,7 +6,7 @@ source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxV # Source: https://www.debian.org/ APP="Debian" -var_tags="${var_tags:-os}" +var_tags="${var_tags:-}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-15}" diff --git a/misc/build.func b/misc/build.func index a98899137..5d5b6b825 100644 --- a/misc/build.func +++ b/misc/build.func @@ -263,7 +263,7 @@ base_settings() { SSH=${var_ssh:-"no"} SSH_AUTHORIZED_KEY=${var_ssh_authorized_key:-""} UDHCPC_FIX=${var_udhcpc_fix:-""} - TAGS="community-script;${var_tags:-}" + TAGS="community-script,${var_tags:-}" ENABLE_FUSE=${var_fuse:-"${1:-no}"} ENABLE_TUN=${var_tun:-"${1:-no}"} @@ -291,7 +291,7 @@ echo_default() { echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" - if [ "$VERB" == "yes" ]; then + if [ "$VERBOSE" == "yes" ]; then echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}Enabled${CL}" fi echo -e "${CREATING}${BOLD}${BL}Creating a ${APP} LXC using the above default settings${CL}" From ae17d53a370ec5a961a3d4b2945921b1ee1d40c9 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 8 Sep 2025 14:38:17 +0200 Subject: [PATCH 0828/1733] Update build.func --- misc/build.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index 5d5b6b825..621a2d378 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1671,7 +1671,7 @@ build_container() { export ENABLE_FUSE="$ENABLE_FUSE" export ENABLE_TUN="$ENABLE_TUN" export PCT_OSTYPE="$var_os" - export PCT_OSVERSION="$var_version" + export PCT_OSVERSION="${var_version%%.*}" export PCT_DISK_SIZE="$DISK_SIZE" export PCT_OPTIONS=" -features $FEATURES From 25d838e27b4284e6fd5ad3982fb8f0799393f63b Mon Sep 17 00:00:00 2001 From: vhsdream Date: Mon, 8 Sep 2025 14:31:19 -0400 Subject: [PATCH 0829/1733] Autocaliweb: fix kepubify version parsing --- install/autocaliweb-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/autocaliweb-install.sh b/install/autocaliweb-install.sh index 0686473c8..f8b89f818 100644 --- a/install/autocaliweb-install.sh +++ b/install/autocaliweb-install.sh @@ -46,7 +46,7 @@ $STD apt-get install -y --no-install-recommends \ msg_ok "Installed dependencies" fetch_and_deploy_gh_release "kepubify" "pgaskin/kepubify" "singlefile" "latest" "/usr/bin" "kepubify-linux-64bit" -KEPUB_VERSION="$(/usr/bin/kepubify --version)" +KEPUB_VERSION="$(/usr/bin/kepubify --version | awk '{print $2}')" msg_info "Installing Calibre" CALIBRE_RELEASE="$(curl -s https://api.github.com/repos/kovidgoyal/calibre/releases/latest | grep -o '"tag_name": "[^"]*' | cut -d'"' -f4)" From 7a8d180c075fa0329649249cde595dce8f369884 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 9 Sep 2025 16:45:37 +0200 Subject: [PATCH 0830/1733] Cleanup --- ct/resiliosync.sh | 44 --------------------------- frontend/public/json/resiliosync.json | 40 ------------------------ install/resiliosync-install.sh | 35 --------------------- 3 files changed, 119 deletions(-) delete mode 100644 ct/resiliosync.sh delete mode 100644 frontend/public/json/resiliosync.json delete mode 100644 install/resiliosync-install.sh diff --git a/ct/resiliosync.sh b/ct/resiliosync.sh deleted file mode 100644 index eff0f8d51..000000000 --- a/ct/resiliosync.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env bash -source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: David Bennett (dbinit) -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://www.resilio.com/sync - -APP="Resilio Sync" -var_tags="${var_tags:-sync}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-8}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /var/lib/resilio-sync ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - msg_info "Updating ${APP} LXC" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "Updated Successfully" - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:8888${CL}" diff --git a/frontend/public/json/resiliosync.json b/frontend/public/json/resiliosync.json deleted file mode 100644 index 0b61fe3d6..000000000 --- a/frontend/public/json/resiliosync.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "Resilio Sync", - "slug": "resiliosync", - "categories": [ - 11 - ], - "date_created": "2025-08-14", - "type": "ct", - "updateable": true, - "privileged": false, - "config_path": "/etc/resilio-sync/config.json", - "interface_port": 8888, - "documentation": "https://help.resilio.com/", - "website": "https://www.resilio.com/sync", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/resilio-sync.webp", - "description": "Fast, reliable, and simple file sync and share solution, powered by P2P technology. Sync files across all your devices without storing them in the cloud.", - "install_methods": [ - { - "type": "default", - "script": "ct/resilio-sync.sh", - "resources": { - "cpu": 2, - "ram": 2048, - "hdd": 8, - "os": "debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "After free registration, you will receive a license keyfile to your email address. Upload it into any LXC directory and select on first run.", - "type": "info" - } - ] -} diff --git a/install/resiliosync-install.sh b/install/resiliosync-install.sh deleted file mode 100644 index d03b58d0a..000000000 --- a/install/resiliosync-install.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: David Bennett (dbinit) -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://www.resilio.com/sync - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Setting up Resilio Sync Repository" -curl -fsSL "https://linux-packages.resilio.com/resilio-sync/key.asc" >/etc/apt/trusted.gpg.d/resilio-sync.asc -echo "deb [signed-by=/etc/apt/trusted.gpg.d/resilio-sync.asc] http://linux-packages.resilio.com/resilio-sync/deb resilio-sync non-free" >/etc/apt/sources.list.d/resilio-sync.list -$STD apt-get update -msg_ok "Resilio Sync Repository Setup" - -msg_info "Installing Resilio Sync" -$STD apt-get install -y resilio-sync -sed -i "s/127.0.0.1:8888/0.0.0.0:8888/g" /etc/resilio-sync/config.json -systemctl enable -q resilio-sync -systemctl restart resilio-sync -msg_ok "Installed Resilio Sync" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" From 341adadaf7dd6afebeadf5818761fdff24d25683 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 9 Sep 2025 14:45:53 +0000 Subject: [PATCH 0831/1733] Update .app files --- ct/headers/ghostfolio | 6 ++++++ ct/headers/npmplus | 6 ++++++ ct/headers/resiliosync | 6 ------ 3 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 ct/headers/ghostfolio create mode 100644 ct/headers/npmplus delete mode 100644 ct/headers/resiliosync diff --git a/ct/headers/ghostfolio b/ct/headers/ghostfolio new file mode 100644 index 000000000..19e1f29f8 --- /dev/null +++ b/ct/headers/ghostfolio @@ -0,0 +1,6 @@ + ________ __ ____ ___ + / ____/ /_ ____ _____/ /_/ __/___ / (_)___ + / / __/ __ \/ __ \/ ___/ __/ /_/ __ \/ / / __ \ +/ /_/ / / / / /_/ (__ ) /_/ __/ /_/ / / / /_/ / +\____/_/ /_/\____/____/\__/_/ \____/_/_/\____/ + diff --git a/ct/headers/npmplus b/ct/headers/npmplus new file mode 100644 index 000000000..9920f2976 --- /dev/null +++ b/ct/headers/npmplus @@ -0,0 +1,6 @@ + _ ______ __ ___ __ + / | / / __ \/ |/ /___ / /_ _______ + / |/ / /_/ / /|_/ / __ \/ / / / / ___/ + / /| / ____/ / / / /_/ / / /_/ (__ ) +/_/ |_/_/ /_/ /_/ .___/_/\__,_/____/ + /_/ diff --git a/ct/headers/resiliosync b/ct/headers/resiliosync deleted file mode 100644 index 500ed27e1..000000000 --- a/ct/headers/resiliosync +++ /dev/null @@ -1,6 +0,0 @@ - ____ _ ___ _____ - / __ \___ _____(_) (_)___ / ___/__ ______ _____ - / /_/ / _ \/ ___/ / / / __ \ \__ \/ / / / __ \/ ___/ - / _, _/ __(__ ) / / / /_/ / ___/ / /_/ / / / / /__ -/_/ |_|\___/____/_/_/_/\____/ /____/\__, /_/ /_/\___/ - /____/ From 4117df34a49d83c01c99221c2e9ffbde0d8a5f32 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 9 Sep 2025 16:50:37 +0200 Subject: [PATCH 0832/1733] Update stylus.json --- frontend/public/json/stylus.json | 76 ++++++++++++++++---------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/frontend/public/json/stylus.json b/frontend/public/json/stylus.json index 9ef412442..6a0261ab2 100644 --- a/frontend/public/json/stylus.json +++ b/frontend/public/json/stylus.json @@ -1,40 +1,40 @@ { - "name": "Stylus", - "slug": "stylus", - "categories": [ - 4 - ], - "date_created": "2025-09-06", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 8000, - "documentation": "https://mmastrac.github.io/stylus/", - "website": "https://github.com/mmastrac/stylus", - "logo": "https: //cdn.jsdelivr.net/gh/selfhst/icons/webp/stylus.webp", - "config_path": "/opt/stylus/config.yaml", - "description": "Stylus (style + status) is a lightweight status page for infrastructure and networks. Configure a set of bash scripts that test the various parts of your infrastructure, set up visualizations with minimal configuration, and Stylus will generate you a dashboard for your system.", - "install_methods": [ - { - "type": "default", - "script": "ct/stylus.sh", - "resources": { - "cpu": 1, - "ram": 1024, - "hdd": 2, - "os": "debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "Configuration Path: `/opt/stylus/config.yaml`", - "type": "info" - } - ] + "name": "Stylus", + "slug": "stylus", + "categories": [ + 4 + ], + "date_created": "2025-09-06", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 8000, + "documentation": "https://mmastrac.github.io/stylus/", + "website": "https://github.com/mmastrac/stylus", + "logo": "https: //cdn.jsdelivr.net/gh/selfhst/icons/webp/stylus.webp", + "config_path": "/opt/stylus/config.yaml", + "description": "Stylus (style + status) is a lightweight status page for infrastructure and networks. Configure a set of bash scripts that test the various parts of your infrastructure, set up visualizations with minimal configuration, and Stylus will generate you a dashboard for your system.", + "install_methods": [ + { + "type": "default", + "script": "ct/stylus.sh", + "resources": { + "cpu": 1, + "ram": 1024, + "hdd": 2, + "os": "debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Configuration Path: `/opt/stylus/config.yaml`", + "type": "info" + } + ] } From cbc10dc7ccbad05836ae77db1b4bf2c4bb4db9b3 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 9 Sep 2025 16:51:33 +0200 Subject: [PATCH 0833/1733] url fix --- frontend/public/json/stylus.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/stylus.json b/frontend/public/json/stylus.json index 6a0261ab2..5f8a0275a 100644 --- a/frontend/public/json/stylus.json +++ b/frontend/public/json/stylus.json @@ -11,7 +11,7 @@ "interface_port": 8000, "documentation": "https://mmastrac.github.io/stylus/", "website": "https://github.com/mmastrac/stylus", - "logo": "https: //cdn.jsdelivr.net/gh/selfhst/icons/webp/stylus.webp", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/stylus.webp", "config_path": "/opt/stylus/config.yaml", "description": "Stylus (style + status) is a lightweight status page for infrastructure and networks. Configure a set of bash scripts that test the various parts of your infrastructure, set up visualizations with minimal configuration, and Stylus will generate you a dashboard for your system.", "install_methods": [ From 1b1aa786046fbb74108ab3035e4287c5ad44db32 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Sep 2025 01:39:38 +0000 Subject: [PATCH 0834/1733] Bump vite in /frontend in the npm_and_yarn group across 1 directory Bumps the npm_and_yarn group with 1 update in the /frontend directory: [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite). Updates `vite` from 6.3.4 to 6.3.6 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/v6.3.6/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v6.3.6/packages/vite) --- updated-dependencies: - dependency-name: vite dependency-version: 6.3.6 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 20bb20922..04b1ca67d 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -10202,9 +10202,9 @@ "license": "MIT" }, "node_modules/vite": { - "version": "6.3.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.4.tgz", - "integrity": "sha512-BiReIiMS2fyFqbqNT/Qqt4CVITDU9M9vE+DKcVAsB+ZV0wvTKd+3hMbkpxz1b+NmEDMegpVbisKiAZOnvO92Sw==", + "version": "6.3.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.6.tgz", + "integrity": "sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA==", "dev": true, "license": "MIT", "dependencies": { From df6d5c3e02c72998477a98a2a5cd6cce73035e17 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 10 Sep 2025 12:07:02 +0200 Subject: [PATCH 0835/1733] Update create_lxc.sh --- misc/create_lxc.sh | 327 ++++++++++++++++++++++++++++----------------- 1 file changed, 201 insertions(+), 126 deletions(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index ea53362e9..4426ff07e 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -1,37 +1,42 @@ #!/usr/bin/env bash - # Copyright (c) 2021-2025 tteck # Author: tteck (tteckster) # Co-Author: MickLesk # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# This sets verbose mode if the global variable is set to "yes" -if [ "$CREATE_LXC_VERBOSE" == "yes" ]; then set -x; fi +# ------------------------------------------------------------------------------ +# Optional verbose mode (debug tracing) +# ------------------------------------------------------------------------------ +if [[ "${CREATE_LXC_VERBOSE:-no}" == "yes" ]]; then set -x; fi +# ------------------------------------------------------------------------------ +# Load core functions (msg_info/msg_ok/msg_error/…) +# ------------------------------------------------------------------------------ if command -v curl >/dev/null 2>&1; then + # Achtung: bewusst exakt diese URL-Struktur source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) load_functions - #echo "(create-lxc.sh) Loaded core.func via curl" elif command -v wget >/dev/null 2>&1; then source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) load_functions - #echo "(create-lxc.sh) Loaded core.func via wget" fi -# This sets error handling options and defines the error_handler function to handle errors +# ------------------------------------------------------------------------------ +# Strict error handling +# ------------------------------------------------------------------------------ set -Eeuo pipefail trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap on_exit EXIT trap on_interrupt INT trap on_terminate TERM -function on_exit() { +on_exit() { local exit_code="$?" [[ -n "${lockfile:-}" && -e "$lockfile" ]] && rm -f "$lockfile" exit "$exit_code" } -function error_handler() { +error_handler() { local exit_code="$?" local line_number="$1" local command="$2" @@ -40,17 +45,15 @@ function error_handler() { exit "$exit_code" } -function on_interrupt() { +on_interrupt() { echo -e "\n${RD}Interrupted by user (SIGINT)${CL}" exit 130 } - -function on_terminate() { +on_terminate() { echo -e "\n${RD}Terminated by signal (SIGTERM)${CL}" exit 143 } - -function exit_script() { +exit_script() { clear printf "\e[?25h" echo -e "\n${CROSS}${RD}User exited script${CL}\n" @@ -58,39 +61,100 @@ function exit_script() { exit 1 } -# Resolve and validate a preselected storage for a given class. -# class: "template" -> requires content=vztmpl -# "container" -> requires content=rootdir +# ------------------------------------------------------------------------------ +# Helpers (dynamic versioning / template parsing) +# ------------------------------------------------------------------------------ +pkg_ver() { dpkg-query -W -f='${Version}\n' "$1" 2>/dev/null || echo ""; } +pkg_cand() { apt-cache policy "$1" 2>/dev/null | awk '/Candidate:/ {print $2}'; } + +ver_ge() { dpkg --compare-versions "$1" ge "$2"; } +ver_gt() { dpkg --compare-versions "$1" gt "$2"; } +ver_lt() { dpkg --compare-versions "$1" lt "$2"; } + +# Extract Debian OS minor from template name: debian-13-standard_13.1-1_amd64.tar.zst => "13.1" +parse_template_osver() { sed -n 's/.*_\([0-9][0-9]*\(\.[0-9]\+\)\?\)-.*/\1/p' <<<"$1"; } + +# Offer upgrade for pve-container/lxc-pve if candidate > installed; optional auto-retry pct create +# Returns: +# 0 = no upgrade needed +# 1 = upgraded (and if do_retry=yes and retry succeeded, creation done) +# 2 = user declined +# 3 = upgrade attempted but failed OR retry failed +offer_lxc_stack_upgrade_and_maybe_retry() { + local do_retry="${1:-no}" # yes|no + local _pvec_i _pvec_c _lxcp_i _lxcp_c need=0 + + _pvec_i="$(pkg_ver pve-container)" + _lxcp_i="$(pkg_ver lxc-pve)" + _pvec_c="$(pkg_cand pve-container)" + _lxcp_c="$(pkg_cand lxc-pve)" + + if [[ -n "$_pvec_c" && "$_pvec_c" != "none" ]]; then + ver_gt "$_pvec_c" "${_pvec_i:-0}" && need=1 + fi + if [[ -n "$_lxcp_c" && "$_lxcp_c" != "none" ]]; then + ver_gt "$_lxcp_c" "${_lxcp_i:-0}" && need=1 + fi + if [[ $need -eq 0 ]]; then + msg_debug "No newer candidate for pve-container/lxc-pve (installed=$_pvec_i/$_lxcp_i, cand=$_pvec_c/$_lxcp_c)" + return 0 + fi + + echo + echo "An update for the Proxmox LXC stack is available:" + echo " pve-container: installed=${_pvec_i:-n/a} candidate=${_pvec_c:-n/a}" + echo " lxc-pve : installed=${_lxcp_i:-n/a} candidate=${_lxcp_c:-n/a}" + echo + read -rp "Do you want to upgrade now? [y/N] " _ans + case "${_ans,,}" in + y | yes) + msg_info "Upgrading Proxmox LXC stack (pve-container, lxc-pve)" + if apt-get update -qq && apt-get install -y --only-upgrade pve-container lxc-pve; then + msg_ok "LXC stack upgraded." + if [[ "$do_retry" == "yes" ]]; then + msg_info "Retrying container creation after upgrade" + if pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then + msg_ok "Container created successfully after upgrade." + return 1 + else + msg_error "pct create still failed after upgrade. See $LOGFILE" + return 3 + fi + fi + return 1 + else + msg_error "Upgrade failed. Please check APT output." + return 3 + fi + ;; + *) return 2 ;; + esac +} + +# ------------------------------------------------------------------------------ +# Storage discovery / selection helpers +# ------------------------------------------------------------------------------ resolve_storage_preselect() { - local class="$1" - local preselect="$2" - local required_content="" + local class="$1" preselect="$2" required_content="" case "$class" in template) required_content="vztmpl" ;; container) required_content="rootdir" ;; *) return 1 ;; esac - - # No preselect provided - [ -z "$preselect" ] && return 1 - - # Check storage exists and supports required content + [[ -z "$preselect" ]] && return 1 if ! pvesm status -content "$required_content" | awk 'NR>1{print $1}' | grep -qx -- "$preselect"; then msg_warn "Preselected storage '${preselect}' does not support content '${required_content}' (or not found)" return 1 fi - # Build human-readable info string from pvesm status - # Expected columns: Name Type Status Total Used Free ... local line total used free line="$(pvesm status | awk -v s="$preselect" 'NR>1 && $1==s {print $0}')" - if [ -z "$line" ]; then + if [[ -z "$line" ]]; then STORAGE_INFO="n/a" else - total="$(echo "$line" | awk '{print $4}')" - used="$(echo "$line" | awk '{print $5}')" - free="$(echo "$line" | awk '{print $6}')" - # Format bytes to IEC + total="$(awk '{print $4}' <<<"$line")" + used="$(awk '{print $5}' <<<"$line")" + free="$(awk '{print $6}' <<<"$line")" local total_h used_h free_h if command -v numfmt >/dev/null 2>&1; then total_h="$(numfmt --to=iec --suffix=B --format %.1f "$total" 2>/dev/null || echo "$total")" @@ -101,30 +165,22 @@ resolve_storage_preselect() { STORAGE_INFO="Free: ${free} Used: ${used}" fi fi - - # Set outputs expected by your callers STORAGE_RESULT="$preselect" return 0 } -function check_storage_support() { - local CONTENT="$1" - local -a VALID_STORAGES=() - +check_storage_support() { + local CONTENT="$1" VALID=0 while IFS= read -r line; do local STORAGE_NAME STORAGE_NAME=$(awk '{print $1}' <<<"$line") - [[ -z "$STORAGE_NAME" ]] && continue - VALID_STORAGES+=("$STORAGE_NAME") + [[ -n "$STORAGE_NAME" ]] && VALID=1 done < <(pvesm status -content "$CONTENT" 2>/dev/null | awk 'NR>1') - - [[ ${#VALID_STORAGES[@]} -gt 0 ]] + [[ $VALID -eq 1 ]] } -# This function selects a storage pool for a given content type (e.g., rootdir, vztmpl). -function select_storage() { +select_storage() { local CLASS=$1 CONTENT CONTENT_LABEL - case $CLASS in container) CONTENT='rootdir' @@ -156,8 +212,7 @@ function select_storage() { ;; esac - # Check for preset STORAGE variable - if [ "$CONTENT" = "rootdir" ] && [ -n "${STORAGE:-}" ]; then + if [[ "$CONTENT" == "rootdir" && -n "${STORAGE:-}" ]]; then if pvesm status -content "$CONTENT" | awk 'NR>1 {print $1}' | grep -qx "$STORAGE"; then STORAGE_RESULT="$STORAGE" msg_info "Using preset storage: $STORAGE_RESULT for $CONTENT_LABEL" @@ -168,28 +223,27 @@ function select_storage() { fi fi - local -A STORAGE_MAP - local -a MENU + declare -A STORAGE_MAP + local -a MENU=() local COL_WIDTH=0 while read -r TAG TYPE _ TOTAL USED FREE _; do [[ -n "$TAG" && -n "$TYPE" ]] || continue - local STORAGE_NAME="$TAG" - local DISPLAY="${STORAGE_NAME} (${TYPE})" + local DISPLAY="${TAG} (${TYPE})" local USED_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$USED") local FREE_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$FREE") local INFO="Free: ${FREE_FMT}B Used: ${USED_FMT}B" - STORAGE_MAP["$DISPLAY"]="$STORAGE_NAME" + STORAGE_MAP["$DISPLAY"]="$TAG" MENU+=("$DISPLAY" "$INFO" "OFF") ((${#DISPLAY} > COL_WIDTH)) && COL_WIDTH=${#DISPLAY} done < <(pvesm status -content "$CONTENT" | awk 'NR>1') - if [ ${#MENU[@]} -eq 0 ]; then + if [[ ${#MENU[@]} -eq 0 ]]; then msg_error "No storage found for content type '$CONTENT'." return 2 fi - if [ $((${#MENU[@]} / 3)) -eq 1 ]; then + if [[ $((${#MENU[@]} / 3)) -eq 1 ]]; then STORAGE_RESULT="${STORAGE_MAP[${MENU[0]}]}" STORAGE_INFO="${MENU[1]}" return 0 @@ -201,19 +255,13 @@ function select_storage() { DISPLAY_SELECTED=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "Storage Pools" \ --radiolist "Which storage pool for ${CONTENT_LABEL,,}?\n(Spacebar to select)" \ - 16 "$WIDTH" 6 "${MENU[@]}" 3>&1 1>&2 2>&3) + 16 "$WIDTH" 6 "${MENU[@]}" 3>&1 1>&2 2>&3) || exit_script - # Cancel or ESC - [[ $? -ne 0 ]] && exit_script - - # Strip trailing whitespace or newline (important for storages like "storage (dir)") DISPLAY_SELECTED=$(sed 's/[[:space:]]*$//' <<<"$DISPLAY_SELECTED") - if [[ -z "$DISPLAY_SELECTED" || -z "${STORAGE_MAP[$DISPLAY_SELECTED]+_}" ]]; then whiptail --msgbox "No valid storage selected. Please try again." 8 58 continue fi - STORAGE_RESULT="${STORAGE_MAP[$DISPLAY_SELECTED]}" for ((i = 0; i < ${#MENU[@]}; i += 3)); do if [[ "${MENU[$i]}" == "$DISPLAY_SELECTED" ]]; then @@ -225,7 +273,9 @@ function select_storage() { done } -# Test if required variables are set +# ------------------------------------------------------------------------------ +# Required input variables +# ------------------------------------------------------------------------------ [[ "${CTID:-}" ]] || { msg_error "You need to set 'CTID' variable." exit 203 @@ -239,13 +289,11 @@ msg_debug "CTID=$CTID" msg_debug "PCT_OSTYPE=$PCT_OSTYPE" msg_debug "PCT_OSVERSION=${PCT_OSVERSION:-default}" -# Test if ID is valid -[ "$CTID" -ge "100" ] || { +# ID checks +[[ "$CTID" -ge 100 ]] || { msg_error "ID cannot be less than 100." exit 205 } - -# Test if ID is in use if qm status "$CTID" &>/dev/null || pct status "$CTID" &>/dev/null; then echo -e "ID '$CTID' is already in use." unset CTID @@ -253,18 +301,18 @@ if qm status "$CTID" &>/dev/null || pct status "$CTID" &>/dev/null; then exit 206 fi -# This checks for the presence of valid Container Storage and Template Storage locations -if ! check_storage_support "rootdir"; then +# Storage capability check +check_storage_support "rootdir" || { msg_error "No valid storage found for 'rootdir' [Container]" exit 1 -fi -if ! check_storage_support "vztmpl"; then +} +check_storage_support "vztmpl" || { msg_error "No valid storage found for 'vztmpl' [Template]" exit 1 -fi +} # Template storage selection -if resolve_storage_preselect template "${TEMPLATE_STORAGE}"; then +if resolve_storage_preselect template "${TEMPLATE_STORAGE:-}"; then TEMPLATE_STORAGE="$STORAGE_RESULT" TEMPLATE_STORAGE_INFO="$STORAGE_INFO" msg_ok "Storage ${BL}${TEMPLATE_STORAGE}${CL} (${TEMPLATE_STORAGE_INFO}) [Template]" @@ -280,7 +328,7 @@ else fi # Container storage selection -if resolve_storage_preselect container "${CONTAINER_STORAGE}"; then +if resolve_storage_preselect container "${CONTAINER_STORAGE:-}"; then CONTAINER_STORAGE="$STORAGE_RESULT" CONTAINER_STORAGE_INFO="$STORAGE_INFO" msg_ok "Storage ${BL}${CONTAINER_STORAGE}${CL} (${CONTAINER_STORAGE_INFO}) [Container]" @@ -295,41 +343,35 @@ else done fi -# Storage Content Validation +# Validate content types msg_info "Validating content types of storage '$CONTAINER_STORAGE'" STORAGE_CONTENT=$(grep -A4 -E "^(zfspool|dir|lvmthin|lvm): $CONTAINER_STORAGE" /etc/pve/storage.cfg | grep content | awk '{$1=""; print $0}' | xargs) - msg_debug "Storage '$CONTAINER_STORAGE' has content types: $STORAGE_CONTENT" - -# check if rootdir supported -if ! grep -qw "rootdir" <<<"$STORAGE_CONTENT"; then +grep -qw "rootdir" <<<"$STORAGE_CONTENT" || { msg_error "Storage '$CONTAINER_STORAGE' does not support 'rootdir'. Cannot create LXC." exit 217 -fi +} msg_ok "Storage '$CONTAINER_STORAGE' supports 'rootdir'" -# check if template storage is compatible msg_info "Validating content types of template storage '$TEMPLATE_STORAGE'" TEMPLATE_CONTENT=$(grep -A4 -E "^[^:]+: $TEMPLATE_STORAGE" /etc/pve/storage.cfg | grep content | awk '{$1=""; print $0}' | xargs) - msg_debug "Template storage '$TEMPLATE_STORAGE' has content types: $TEMPLATE_CONTENT" - if ! grep -qw "vztmpl" <<<"$TEMPLATE_CONTENT"; then msg_warn "Template storage '$TEMPLATE_STORAGE' does not declare 'vztmpl'. This may cause pct create to fail." else msg_ok "Template storage '$TEMPLATE_STORAGE' supports 'vztmpl'" fi -# Check free space on selected container storage +# Free space check STORAGE_FREE=$(pvesm status | awk -v s="$CONTAINER_STORAGE" '$1 == s { print $6 }') REQUIRED_KB=$((${PCT_DISK_SIZE:-8} * 1024 * 1024)) -if [ "$STORAGE_FREE" -lt "$REQUIRED_KB" ]; then +[[ "$STORAGE_FREE" -ge "$REQUIRED_KB" ]] || { msg_error "Not enough space on '$CONTAINER_STORAGE'. Needed: ${PCT_DISK_SIZE:-8}G." exit 214 -fi +} -# Check Cluster Quorum if in Cluster -if [ -f /etc/pve/corosync.conf ]; then +# Cluster quorum (if cluster) +if [[ -f /etc/pve/corosync.conf ]]; then msg_info "Checking cluster quorum" if ! pvecm status | awk -F':' '/^Quorate/ { exit ($2 ~ /Yes/) ? 0 : 1 }'; then msg_error "Cluster is not quorate. Start all nodes or configure quorum device (QDevice)." @@ -338,7 +380,9 @@ if [ -f /etc/pve/corosync.conf ]; then msg_ok "Cluster is quorate" fi -# Update LXC template list +# ------------------------------------------------------------------------------ +# Template discovery & validation +# ------------------------------------------------------------------------------ TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" case "$PCT_OSTYPE" in debian | ubuntu) TEMPLATE_PATTERN="-standard_" ;; @@ -348,28 +392,22 @@ esac msg_info "Searching for template '$TEMPLATE_SEARCH'" -# 1. get / check local templates mapfile -t LOCAL_TEMPLATES < <( pveam list "$TEMPLATE_STORAGE" 2>/dev/null | awk -v s="$TEMPLATE_SEARCH" -v p="$TEMPLATE_PATTERN" '$1 ~ s && $1 ~ p {print $1}' | - sed 's/.*\///' | sort -t - -k 2 -V + sed 's|.*/||' | sort -t - -k 2 -V ) -# 2. get online templates pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)." mapfile -t ONLINE_TEMPLATES < <( pveam available -section system 2>/dev/null | sed -n "s/.*\($TEMPLATE_SEARCH.*$TEMPLATE_PATTERN.*\)/\1/p" | sort -t - -k 2 -V ) -if [ ${#ONLINE_TEMPLATES[@]} -gt 0 ]; then - ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" -else - ONLINE_TEMPLATE="" -fi +ONLINE_TEMPLATE="" +[[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" -# 3. Local vs Online -if [ ${#LOCAL_TEMPLATES[@]} -gt 0 ]; then +if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then TEMPLATE="${LOCAL_TEMPLATES[-1]}" TEMPLATE_SOURCE="local" else @@ -377,24 +415,19 @@ else TEMPLATE_SOURCE="online" fi -# 4. Getting Path (universal, also for nfs/cifs) TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" if [[ -z "$TEMPLATE_PATH" ]]; then TEMPLATE_BASE=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) - if [[ -n "$TEMPLATE_BASE" ]]; then - TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE" - fi + [[ -n "$TEMPLATE_BASE" ]] && TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE" fi - -if [[ -z "$TEMPLATE_PATH" ]]; then +[[ -n "$TEMPLATE_PATH" ]] || { msg_error "Unable to resolve template path for $TEMPLATE_STORAGE. Check storage type and permissions." exit 220 -fi +} msg_ok "Template ${BL}$TEMPLATE${CL} [$TEMPLATE_SOURCE]" msg_debug "Resolved TEMPLATE_PATH=$TEMPLATE_PATH" -# 5. Validation NEED_DOWNLOAD=0 if [[ ! -f "$TEMPLATE_PATH" ]]; then msg_info "Template not present locally – will download." @@ -420,7 +453,6 @@ else msg_ok "Template $TEMPLATE is present and valid." fi -# 6. Update-Check (if local exist) if [[ "$TEMPLATE_SOURCE" == "local" && -n "$ONLINE_TEMPLATE" && "$TEMPLATE" != "$ONLINE_TEMPLATE" ]]; then msg_warn "Local template is outdated: $TEMPLATE (latest available: $ONLINE_TEMPLATE)" if whiptail --yesno "A newer template is available:\n$ONLINE_TEMPLATE\n\nDo you want to download and use it instead?" 12 70; then @@ -431,7 +463,6 @@ if [[ "$TEMPLATE_SOURCE" == "local" && -n "$ONLINE_TEMPLATE" && "$TEMPLATE" != " fi fi -# 7. Download if needed if [[ "$NEED_DOWNLOAD" -eq 1 ]]; then [[ -f "$TEMPLATE_PATH" ]] && rm -f "$TEMPLATE_PATH" for attempt in {1..3}; do @@ -440,7 +471,7 @@ if [[ "$NEED_DOWNLOAD" -eq 1 ]]; then msg_ok "Template download successful." break fi - if [ $attempt -eq 3 ]; then + if [[ $attempt -eq 3 ]]; then msg_error "Failed after 3 attempts. Please check network access, permissions, or manually run:\n pveam download $TEMPLATE_STORAGE $TEMPLATE" exit 222 fi @@ -448,7 +479,6 @@ if [[ "$NEED_DOWNLOAD" -eq 1 ]]; then done fi -# 8. Final Check – Template usability if ! pveam list "$TEMPLATE_STORAGE" 2>/dev/null | grep -q "$TEMPLATE"; then msg_error "Template $TEMPLATE not available in storage $TEMPLATE_STORAGE after download." exit 223 @@ -456,9 +486,19 @@ fi msg_ok "Template $TEMPLATE is ready for container creation." # ------------------------------------------------------------------------------ -# Create LXC Container with validation, recovery and debug option +# Dynamic preflight for Debian 13.x: offer upgrade if available (no hard mins) # ------------------------------------------------------------------------------ +if [[ "$PCT_OSTYPE" == "debian" ]]; then + OSVER="$(parse_template_osver "$TEMPLATE")" + if [[ -n "$OSVER" ]]; then + # Proactive, aber ohne Abbruch – nur Angebot + offer_lxc_stack_upgrade_and_maybe_retry "no" || true + fi +fi +# ------------------------------------------------------------------------------ +# Create LXC Container +# ------------------------------------------------------------------------------ msg_info "Creating LXC container" # Ensure subuid/subgid entries exist @@ -469,7 +509,7 @@ grep -q "root:100000:65536" /etc/subgid || echo "root:100000:65536" >>/etc/subgi PCT_OPTIONS=(${PCT_OPTIONS[@]:-${DEFAULT_PCT_OPTIONS[@]}}) [[ " ${PCT_OPTIONS[*]} " =~ " -rootfs " ]] || PCT_OPTIONS+=(-rootfs "$CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}") -# Secure with lockfile +# Lock by template file (avoid concurrent downloads/creates) lockfile="/tmp/template.${TEMPLATE}.lock" exec 9>"$lockfile" || { msg_error "Failed to create lock file '$lockfile'." @@ -509,15 +549,58 @@ if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[ if [[ "$TEMPLATE_STORAGE" != "local" ]]; then msg_warn "Retrying container creation with fallback to local storage..." LOCAL_TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" - if [ ! -f "$LOCAL_TEMPLATE_PATH" ]; then + if [[ ! -f "$LOCAL_TEMPLATE_PATH" ]]; then msg_info "Downloading template to local..." pveam download local "$TEMPLATE" >/dev/null 2>&1 fi if pct create "$CTID" "local:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then msg_ok "Container successfully created using local fallback." else - msg_error "Container creation failed even with local fallback. See $LOGFILE" - # Ask user if they want debug output + # --- Dynamic stack upgrade + auto-retry on the well-known error pattern --- + if grep -qiE 'unsupported .* version' "$LOGFILE"; then + echo + echo "pct reported 'unsupported ... version' – your LXC stack might be too old for this template." + echo "We can try to upgrade 'pve-container' and 'lxc-pve' now and retry automatically." + if offer_lxc_stack_upgrade_and_maybe_retry "yes"; then + : # success after retry + else + rc=$? + case $rc in + 2) echo "Upgrade was declined. Please update and re-run: + apt update && apt install --only-upgrade pve-container lxc-pve" ;; + 3) echo "Upgrade and/or retry failed. Please inspect: $LOGFILE" ;; + esac + exit 231 + fi + else + msg_error "Container creation failed even with local fallback. See $LOGFILE" + if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then + set -x + bash -x -c "pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" 2>&1 | tee -a "$LOGFILE" + set +x + fi + exit 209 + fi + fi + else + msg_error "Container creation failed on local storage. See $LOGFILE" + # --- Dynamic stack upgrade + auto-retry on the well-known error pattern --- + if grep -qiE 'unsupported .* version' "$LOGFILE"; then + echo + echo "pct reported 'unsupported ... version' – your LXC stack might be too old for this template." + echo "We can try to upgrade 'pve-container' and 'lxc-pve' now and retry automatically." + if offer_lxc_stack_upgrade_and_maybe_retry "yes"; then + : # success after retry + else + rc=$? + case $rc in + 2) echo "Upgrade was declined. Please update and re-run: + apt update && apt install --only-upgrade pve-container lxc-pve" ;; + 3) echo "Upgrade and/or retry failed. Please inspect: $LOGFILE" ;; + esac + exit 231 + fi + else if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then set -x bash -x -c "pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" 2>&1 | tee -a "$LOGFILE" @@ -525,28 +608,20 @@ if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[ fi exit 209 fi - else - msg_error "Container creation failed on local storage. See $LOGFILE" - if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then - set -x - bash -x -c "pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" 2>&1 | tee -a "$LOGFILE" - set +x - fi - exit 209 fi fi fi # Verify container exists -if ! pct list | awk '{print $1}' | grep -qx "$CTID"; then +pct list | awk '{print $1}' | grep -qx "$CTID" || { msg_error "Container ID $CTID not listed in 'pct list'. See $LOGFILE" exit 215 -fi +} # Verify config rootfs -if ! grep -q '^rootfs:' "/etc/pve/lxc/$CTID.conf"; then +grep -q '^rootfs:' "/etc/pve/lxc/$CTID.conf" || { msg_error "RootFS entry missing in container config. See $LOGFILE" exit 216 -fi +} msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created." From 59dbd349e338594d5f7078e4efe5ef40d5a285ff Mon Sep 17 00:00:00 2001 From: Giovanni Pellerano Date: Wed, 10 Sep 2025 23:33:04 +0200 Subject: [PATCH 0836/1733] Add GlobaLeaks This commit adds: - GlobaLeaks script as CT - GlobaLeaks script as install - The frontend descriptor --- ct/globaleaks.sh | 44 ++++++++++++++++++++++++++++ frontend/public/json/globaleaks.json | 35 ++++++++++++++++++++++ install/globaleaks-install.sh | 25 ++++++++++++++++ 3 files changed, 104 insertions(+) create mode 100644 ct/globaleaks.sh create mode 100644 frontend/public/json/globaleaks.json create mode 100644 install/globaleaks-install.sh diff --git a/ct/globaleaks.sh b/ct/globaleaks.sh new file mode 100644 index 000000000..9bfa59ef0 --- /dev/null +++ b/ct/globaleaks.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 communtiy-scripts ORG +# Author: Giovanni `evilaliv3` Pellerano +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/globaleaks/globaleaks-whistleblowing-software +## App Default Values +APP="GlobaLeaks" +var_tags="${var_tags:-whistleblowing-software}" +var_disk="${var_disk:-4}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -f /usr/sbin/globaleaks ]]; then + msg_error "No ${APP} installation found!" + exit + fi + + msg_info "Updating $APP LXC" + $STD apt-get update + $STD apt-get -y upgrade + $STD bash <(curl -fsSL https://deb.globaleaks.org//install.sh) -y + msg_ok "Updated $APP LXC" +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN} ${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}https://${IP}${CL}" diff --git a/frontend/public/json/globaleaks.json b/frontend/public/json/globaleaks.json new file mode 100644 index 000000000..18c066c55 --- /dev/null +++ b/frontend/public/json/globaleaks.json @@ -0,0 +1,35 @@ +{ + "name": "GlobaLeaks", + "slug": "globaleaks", + "categories": [ + 0 + ], + "date_created": "2025-09-10", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 80, + "documentation": "https://docs.globaleaks.org", + "website": "https://www.globaleaks.org/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/globaleaks.webp", + "config_path": "", + "description": "GlobaLeaks is a free and open-source whistleblowing software enabling anyone to easily set up and maintain a secure reporting platform.", + "install_methods": [ + { + "type": "default", + "script": "ct/globaleaks.sh", + "resources": { + "cpu": 2, + "ram": 1024, + "hdd": 4, + "os": "debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} diff --git a/install/globaleaks-install.sh b/install/globaleaks-install.sh new file mode 100644 index 000000000..6a7c8bdda --- /dev/null +++ b/install/globaleaks-install.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Giovanni `evilaliv3` Pellerano +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/globaleaks/globaleaks-whistleblowing-software + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Setup GlobaLeaks" +$STD bash <(curl -fsSL https://deb.globaleaks.org//install.sh) -y +msg_ok "Setup GlobaLeaks" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From b48e89d4954e2fa56bf3c7c840ea14ed8a5bd66b Mon Sep 17 00:00:00 2001 From: Giovanni Pellerano Date: Thu, 11 Sep 2025 05:44:23 +0200 Subject: [PATCH 0837/1733] Remove usage of external script --- ct/globaleaks.sh | 5 +++++ install/globaleaks-install.sh | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/ct/globaleaks.sh b/ct/globaleaks.sh index 9bfa59ef0..926b2cbe4 100644 --- a/ct/globaleaks.sh +++ b/ct/globaleaks.sh @@ -31,6 +31,11 @@ function update_script() { $STD apt-get update $STD apt-get -y upgrade $STD bash <(curl -fsSL https://deb.globaleaks.org//install.sh) -y + + $STD sh -c 'echo "deb [signed-by=/etc/apt/trusted.gpg.d/globaleaks.gpg] http://deb.globaleaks.org $(lsb_release -sc) main" > /etc/apt/sources.list.d/globaleaks.list' + $STD apt-get update + $STD apt-get -y install globaleaks + msg_ok "Updated $APP LXC" } diff --git a/install/globaleaks-install.sh b/install/globaleaks-install.sh index 6a7c8bdda..02bbfde30 100644 --- a/install/globaleaks-install.sh +++ b/install/globaleaks-install.sh @@ -13,7 +13,9 @@ network_check update_os msg_info "Setup GlobaLeaks" -$STD bash <(curl -fsSL https://deb.globaleaks.org//install.sh) -y +$STD sh -c 'echo "deb [signed-by=/etc/apt/trusted.gpg.d/globaleaks.gpg] http://deb.globaleaks.org $(lsb_release -sc) main" > /etc/apt/sources.list.d/globaleaks.list' +$STD apt-get update +$STD apt-get -y install globaleaks msg_ok "Setup GlobaLeaks" motd_ssh From 52b991a4f23446dca94697e98e482c704a9bef8a Mon Sep 17 00:00:00 2001 From: Giovanni Pellerano Date: Thu, 11 Sep 2025 09:57:29 +0200 Subject: [PATCH 0838/1733] Simplify loading of GPG key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Slaviša Arežina <58952836+tremor021@users.noreply.github.com> --- install/globaleaks-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/globaleaks-install.sh b/install/globaleaks-install.sh index 02bbfde30..1b08305da 100644 --- a/install/globaleaks-install.sh +++ b/install/globaleaks-install.sh @@ -13,7 +13,7 @@ network_check update_os msg_info "Setup GlobaLeaks" -$STD sh -c 'echo "deb [signed-by=/etc/apt/trusted.gpg.d/globaleaks.gpg] http://deb.globaleaks.org $(lsb_release -sc) main" > /etc/apt/sources.list.d/globaleaks.list' +echo "deb [signed-by=/etc/apt/trusted.gpg.d/globaleaks.gpg] http://deb.globaleaks.org bookworm main" >/etc/apt/sources.list.d/globaleaks.list $STD apt-get update $STD apt-get -y install globaleaks msg_ok "Setup GlobaLeaks" From ee69543cda852f9173d2689efb32e9f6302f881e Mon Sep 17 00:00:00 2001 From: Giovanni Pellerano Date: Thu, 11 Sep 2025 09:59:49 +0200 Subject: [PATCH 0839/1733] Update ct/globaleaks.sh using apt-get upgrade in place of apt-get install globaleaks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Slaviša Arežina <58952836+tremor021@users.noreply.github.com> --- ct/globaleaks.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/globaleaks.sh b/ct/globaleaks.sh index 926b2cbe4..890c0f8d1 100644 --- a/ct/globaleaks.sh +++ b/ct/globaleaks.sh @@ -34,7 +34,7 @@ function update_script() { $STD sh -c 'echo "deb [signed-by=/etc/apt/trusted.gpg.d/globaleaks.gpg] http://deb.globaleaks.org $(lsb_release -sc) main" > /etc/apt/sources.list.d/globaleaks.list' $STD apt-get update - $STD apt-get -y install globaleaks + $STD apt-get -y upgrade msg_ok "Updated $APP LXC" } From 00e6b9ceb11f60046959b36b1ed36d59b2f0b274 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 11 Sep 2025 10:22:49 +0200 Subject: [PATCH 0840/1733] Update ct/globaleaks.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Slaviša Arežina <58952836+tremor021@users.noreply.github.com> --- ct/globaleaks.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/globaleaks.sh b/ct/globaleaks.sh index 890c0f8d1..39db682b6 100644 --- a/ct/globaleaks.sh +++ b/ct/globaleaks.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 communtiy-scripts ORG # Author: Giovanni `evilaliv3` Pellerano # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE From dc6cd153600098e61fec057a9c92d3e24dc9886a Mon Sep 17 00:00:00 2001 From: Giovanni Pellerano Date: Thu, 11 Sep 2025 11:44:29 +0200 Subject: [PATCH 0841/1733] Update ct/globaleaks.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Slaviša Arežina <58952836+tremor021@users.noreply.github.com> --- ct/globaleaks.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/ct/globaleaks.sh b/ct/globaleaks.sh index 39db682b6..d8438bc19 100644 --- a/ct/globaleaks.sh +++ b/ct/globaleaks.sh @@ -28,8 +28,6 @@ function update_script() { fi msg_info "Updating $APP LXC" - $STD apt-get update - $STD apt-get -y upgrade $STD bash <(curl -fsSL https://deb.globaleaks.org//install.sh) -y $STD sh -c 'echo "deb [signed-by=/etc/apt/trusted.gpg.d/globaleaks.gpg] http://deb.globaleaks.org $(lsb_release -sc) main" > /etc/apt/sources.list.d/globaleaks.list' From 75c15879ec91124817cf9d78e0e2e35318608648 Mon Sep 17 00:00:00 2001 From: Giovanni Pellerano Date: Thu, 11 Sep 2025 11:44:50 +0200 Subject: [PATCH 0842/1733] Update ct/globaleaks.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Slaviša Arežina <58952836+tremor021@users.noreply.github.com> --- ct/globaleaks.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/ct/globaleaks.sh b/ct/globaleaks.sh index d8438bc19..8558830a8 100644 --- a/ct/globaleaks.sh +++ b/ct/globaleaks.sh @@ -28,9 +28,6 @@ function update_script() { fi msg_info "Updating $APP LXC" - $STD bash <(curl -fsSL https://deb.globaleaks.org//install.sh) -y - - $STD sh -c 'echo "deb [signed-by=/etc/apt/trusted.gpg.d/globaleaks.gpg] http://deb.globaleaks.org $(lsb_release -sc) main" > /etc/apt/sources.list.d/globaleaks.list' $STD apt-get update $STD apt-get -y upgrade From 95a6ddcca1a11d647cea99cb9d122840e22977da Mon Sep 17 00:00:00 2001 From: Giovanni Pellerano Date: Thu, 11 Sep 2025 11:50:22 +0200 Subject: [PATCH 0843/1733] Load GlobaLeaks signing key during install --- ct/globaleaks.sh | 2 +- install/globaleaks-install.sh | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ct/globaleaks.sh b/ct/globaleaks.sh index 8558830a8..d530b37e8 100644 --- a/ct/globaleaks.sh +++ b/ct/globaleaks.sh @@ -4,7 +4,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV # Author: Giovanni `evilaliv3` Pellerano # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/globaleaks/globaleaks-whistleblowing-software -## App Default Values + APP="GlobaLeaks" var_tags="${var_tags:-whistleblowing-software}" var_disk="${var_disk:-4}" diff --git a/install/globaleaks-install.sh b/install/globaleaks-install.sh index 1b08305da..3e9b91c3d 100644 --- a/install/globaleaks-install.sh +++ b/install/globaleaks-install.sh @@ -13,6 +13,7 @@ network_check update_os msg_info "Setup GlobaLeaks" +curl -sS https://deb.globaleaks.org/globaleaks.asc | gpg --dearmor -o /etc/apt/trusted.gpg.d/globaleaks.gpg echo "deb [signed-by=/etc/apt/trusted.gpg.d/globaleaks.gpg] http://deb.globaleaks.org bookworm main" >/etc/apt/sources.list.d/globaleaks.list $STD apt-get update $STD apt-get -y install globaleaks From 3b9acb977a72bb24717199ba3ec56a50e229d9f1 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 11 Sep 2025 15:15:51 +0200 Subject: [PATCH 0844/1733] Add telegraf.sh for managing Telegraf installation --- ct/telegraf.sh | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 ct/telegraf.sh diff --git a/ct/telegraf.sh b/ct/telegraf.sh new file mode 100644 index 000000000..8012bcf1a --- /dev/null +++ b/ct/telegraf.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: CrazyWolf13 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/influxdata/telegraf + +APP="telegraf" +var_tags="${var_tags:-collector;metrics}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-1024}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -f /etc/telegraf/telegraf.conf ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "telegraf" "influxdb/telegraf"; then + msg_info "Stopping $APP" + systemctl stop telegraf + msg_ok "Stopped $APP" + + msg_info "Updating ${APP}" + apt-get update + apt-get upgrade telegraf -y + msg_ok "Updated ${APP}" + + msg_info "Starting $APP" + systemctl start telegraf + msg_ok "Started $APP" + msg_ok "Updated Successfully" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Check out /etc/telegraf/telegraf.conf to configure telegraf${CL}" From 241d3a26a744168b87774553ccc2f4979d3a35ff Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 11 Sep 2025 15:23:29 +0200 Subject: [PATCH 0845/1733] Update source URL in telegraf-install.sh --- install/telegraf-install.sh | 44 +++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 install/telegraf-install.sh diff --git a/install/telegraf-install.sh b/install/telegraf-install.sh new file mode 100644 index 000000000..3b1ddda93 --- /dev/null +++ b/install/telegraf-install.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: CrazyWolf13 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/influxdata/telegraf + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Adding Telegraf key and repository" +curl --silent --location -O https://repos.influxdata.com/influxdata-archive.key +gpg --show-keys --with-fingerprint --with-colons ./influxdata-archive.key 2>&1 \ +| grep -q '^fpr:\+24C975CBA61A024EE1B631787C3D57159FC2F927:$' \ +&& cat influxdata-archive.key \ +| gpg --dearmor \ +| tee /etc/apt/keyrings/influxdata-archive.gpg > /dev/null \ +&& echo 'deb [signed-by=/etc/apt/keyrings/influxdata-archive.gpg] https://repos.influxdata.com/debian stable main' \ +| tee /etc/apt/sources.list.d/influxdata.list +msg_ok "Added Telegraf Repository" + +msg_info "Installing Telegraf" +apt-get update +apt-get install telegraf +msg_ok "Installed Telegraf" + +msg_info "Creating default config" +telegraf config > telegraf.conf +msg_ok "Created default config" + +systemctl enable -q --now telegraf + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From c67fcec6160a6af3965f81b812f18ec8910170bf Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 11 Sep 2025 15:28:25 +0200 Subject: [PATCH 0846/1733] Add telegraf.json configuration file --- frontend/public/json/telegraf.json | 40 ++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 frontend/public/json/telegraf.json diff --git a/frontend/public/json/telegraf.json b/frontend/public/json/telegraf.json new file mode 100644 index 000000000..695126d0c --- /dev/null +++ b/frontend/public/json/telegraf.json @@ -0,0 +1,40 @@ +{ + "name": "Telegraf", + "slug": "telegraf", + "categories": [ + 9 + ], + "date_created": "2025-09-11", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": null, + "documentation": "https://docs.influxdata.com/telegraf/v1/", + "config_path": "/etc/telegraf/telegraf.conf", + "website": "https://github.com/influxdata/telegraf", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/influxdb.webp", + "description": "Telegraf collects and sends time series data from databases, systems, and IoT sensors. It has no external dependencies, is easy to install, and requires minimal memory.", + "install_methods": [ + { + "type": "default", + "script": "ct/telegraf.sh", + "resources": { + "cpu": 1, + "ram": 1024, + "hdd": 4, + "os": "Debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Check out the config afterwards and adapt to your needs.", + "type": "" + } + ] +} From 3e0ec08c74e7829408f831a725e337342110cd6a Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 11 Sep 2025 15:29:57 +0200 Subject: [PATCH 0847/1733] Update telegraf.json --- frontend/public/json/telegraf.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/telegraf.json b/frontend/public/json/telegraf.json index 695126d0c..775371da4 100644 --- a/frontend/public/json/telegraf.json +++ b/frontend/public/json/telegraf.json @@ -34,7 +34,7 @@ "notes": [ { "text": "Check out the config afterwards and adapt to your needs.", - "type": "" + "type": "info" } ] } From 38f23db68712cba13cdcce4443fd08c76f155c70 Mon Sep 17 00:00:00 2001 From: Giovanni Pellerano Date: Thu, 11 Sep 2025 15:35:11 +0200 Subject: [PATCH 0848/1733] Update globaleaks.json changing port from 80 to 443 --- frontend/public/json/globaleaks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/globaleaks.json b/frontend/public/json/globaleaks.json index 18c066c55..f21437376 100644 --- a/frontend/public/json/globaleaks.json +++ b/frontend/public/json/globaleaks.json @@ -8,7 +8,7 @@ "type": "ct", "updateable": true, "privileged": false, - "interface_port": 80, + "interface_port": 443, "documentation": "https://docs.globaleaks.org", "website": "https://www.globaleaks.org/", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/globaleaks.webp", From 1eabafb7075e092a0708af98071db179dba71036 Mon Sep 17 00:00:00 2001 From: Giovanni Pellerano Date: Thu, 11 Sep 2025 15:35:34 +0200 Subject: [PATCH 0849/1733] Update globaleaks.json changing url for image Url changed waiting for integration of icons on https://github.com/selfhst/icons/pull/530 --- frontend/public/json/globaleaks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/globaleaks.json b/frontend/public/json/globaleaks.json index f21437376..3420ebab0 100644 --- a/frontend/public/json/globaleaks.json +++ b/frontend/public/json/globaleaks.json @@ -11,7 +11,7 @@ "interface_port": 443, "documentation": "https://docs.globaleaks.org", "website": "https://www.globaleaks.org/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/globaleaks.webp", + "logo": "https://raw.githubusercontent.com/globaleaks/globaleaks-whistleblowing-software/stable/brand/assets/webp/globaleaks-icon-color.webp", "config_path": "", "description": "GlobaLeaks is a free and open-source whistleblowing software enabling anyone to easily set up and maintain a secure reporting platform.", "install_methods": [ From c14aa244e1c811ca49dd391b59a0b16c7d2afa3a Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 11 Sep 2025 15:40:00 +0200 Subject: [PATCH 0850/1733] Simplify Telegraf installation script --- install/telegraf-install.sh | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/install/telegraf-install.sh b/install/telegraf-install.sh index 3b1ddda93..41c1633cb 100644 --- a/install/telegraf-install.sh +++ b/install/telegraf-install.sh @@ -29,16 +29,11 @@ apt-get update apt-get install telegraf msg_ok "Installed Telegraf" -msg_info "Creating default config" -telegraf config > telegraf.conf -msg_ok "Created default config" - -systemctl enable -q --now telegraf - motd_ssh customize msg_info "Cleaning up" $STD apt-get -y autoremove $STD apt-get -y autoclean +rm /influxdata-archive.key msg_ok "Cleaned" From eb0cafae15c54dbeae63f063b8bfb18ac870e0ec Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 11 Sep 2025 15:42:23 +0200 Subject: [PATCH 0851/1733] Update telegraf.sh --- ct/telegraf.sh | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/ct/telegraf.sh b/ct/telegraf.sh index 8012bcf1a..66214f868 100644 --- a/ct/telegraf.sh +++ b/ct/telegraf.sh @@ -28,20 +28,19 @@ function update_script() { exit fi - if check_for_gh_release "telegraf" "influxdb/telegraf"; then - msg_info "Stopping $APP" - systemctl stop telegraf - msg_ok "Stopped $APP" + msg_info "Stopping $APP" + systemctl stop telegraf + msg_ok "Stopped $APP" - msg_info "Updating ${APP}" - apt-get update - apt-get upgrade telegraf -y - msg_ok "Updated ${APP}" + msg_info "Updating $APP" + apt-get update + apt-get upgrade telegraf -y + msg_ok "Updated $APP" - msg_info "Starting $APP" - systemctl start telegraf - msg_ok "Started $APP" - msg_ok "Updated Successfully" + msg_info "Starting $APP" + systemctl start telegraf + msg_ok "Started $APP" + msg_ok "Updated Successfully" fi exit } From 9a41be241d2ea66ed0e72d09972c0fe38837f9fd Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 11 Sep 2025 15:42:54 +0200 Subject: [PATCH 0852/1733] Remove unnecessary 'fi' statement in telegraf.sh --- ct/telegraf.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/ct/telegraf.sh b/ct/telegraf.sh index 66214f868..8cad99f80 100644 --- a/ct/telegraf.sh +++ b/ct/telegraf.sh @@ -41,7 +41,6 @@ function update_script() { systemctl start telegraf msg_ok "Started $APP" msg_ok "Updated Successfully" - fi exit } From 08b02f5962bc5b704701400a49b332d20b9aae54 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 11 Sep 2025 13:43:14 +0000 Subject: [PATCH 0853/1733] Update .app files --- ct/headers/telegraf | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/telegraf diff --git a/ct/headers/telegraf b/ct/headers/telegraf new file mode 100644 index 000000000..14c860970 --- /dev/null +++ b/ct/headers/telegraf @@ -0,0 +1,6 @@ + __ __ ____ + / /____ / /__ ____ __________ _/ __/ + / __/ _ \/ / _ \/ __ `/ ___/ __ `/ /_ +/ /_/ __/ / __/ /_/ / / / /_/ / __/ +\__/\___/_/\___/\__, /_/ \__,_/_/ + /____/ From e38236b008595ce37e105b3bc7e1dee904dd477b Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 11 Sep 2025 15:56:53 +0200 Subject: [PATCH 0854/1733] Refactor update commands to use STD variable --- ct/telegraf.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/telegraf.sh b/ct/telegraf.sh index 8cad99f80..f48f08707 100644 --- a/ct/telegraf.sh +++ b/ct/telegraf.sh @@ -33,8 +33,8 @@ function update_script() { msg_ok "Stopped $APP" msg_info "Updating $APP" - apt-get update - apt-get upgrade telegraf -y + $STD apt-get update + $STD apt-get upgrade telegraf -y msg_ok "Updated $APP" msg_info "Starting $APP" From f5fbf7dad72942d68d6911e852d3430e4aae6756 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 11 Sep 2025 16:06:05 +0200 Subject: [PATCH 0855/1733] Redirect output of repository addition to /dev/null --- install/telegraf-install.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/install/telegraf-install.sh b/install/telegraf-install.sh index 41c1633cb..a92ffe9b0 100644 --- a/install/telegraf-install.sh +++ b/install/telegraf-install.sh @@ -21,12 +21,12 @@ gpg --show-keys --with-fingerprint --with-colons ./influxdata-archive.key 2>&1 \ | gpg --dearmor \ | tee /etc/apt/keyrings/influxdata-archive.gpg > /dev/null \ && echo 'deb [signed-by=/etc/apt/keyrings/influxdata-archive.gpg] https://repos.influxdata.com/debian stable main' \ -| tee /etc/apt/sources.list.d/influxdata.list +| tee /etc/apt/sources.list.d/influxdata.list > /dev/null msg_ok "Added Telegraf Repository" msg_info "Installing Telegraf" -apt-get update -apt-get install telegraf +$STD apt-get update +$STD apt-get install telegraf msg_ok "Installed Telegraf" motd_ssh From 33a7cc06bffc7c9c45b22a37a8c85bf37986470e Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 11 Sep 2025 20:28:32 +0200 Subject: [PATCH 0856/1733] Add Joplin Server script --- ct/joplin-server.sh | 81 +++++++++++++++++++++++++++ install/joplin-server-install.sh | 95 ++++++++++++++++++++++++++++++++ 2 files changed, 176 insertions(+) create mode 100644 ct/joplin-server.sh create mode 100644 install/joplin-server-install.sh diff --git a/ct/joplin-server.sh b/ct/joplin-server.sh new file mode 100644 index 000000000..922b0e04a --- /dev/null +++ b/ct/joplin-server.sh @@ -0,0 +1,81 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://joplinapp.org/ + +APP="Joplin Server" +var_tags="${var_tags:-notes}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-4096}" +var_disk="${var_disk:-15}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/joplin-server ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + RELEASE=$(curl -fsSL https://api.github.com/repos/laurent22/joplin/releases/latest | jq '.tag_name' | sed 's/^"v//;s/"$//') + if check_for_gh_release "joplin-server" "laurent22/joplin"; then + msg_info "Stopping Services" + systemctl stop joplin-server + msg_ok "Stopped Services" + + INSTALL_DIR="/opt/autocaliweb" + export VIRTUAL_ENV="${INSTALL_DIR}/venv" + $STD tar -cf ~/autocaliweb_bkp.tar "$INSTALL_DIR"/{metadata_change_logs,dirs.json,.env,scripts/ingest_watcher.sh,scripts/auto_zipper_wrapper.sh,scripts/metadata_change_detector_wrapper.sh} + fetch_and_deploy_gh_release "autocaliweb" "gelbphoenix/autocaliweb" "tarball" "latest" "/opt/autocaliweb" + msg_info "Updating ${APP}" + cd "$INSTALL_DIR" + if [[ ! -d "$VIRTUAL_ENV" ]]; then + $STD uv venv "$VIRTUAL_ENV" + fi + $STD uv sync --all-extras --active + cd "$INSTALL_DIR"/koreader/plugins + PLUGIN_DIGEST="$(find acwsync.koplugin -type f -name "*.lua" -o -name "*.json" | sort | xargs sha256sum | sha256sum | cut -d' ' -f1)" + echo "Plugin files digest: $PLUGIN_DIGEST" >acwsync.koplugin/${PLUGIN_DIGEST}.digest + echo "Build date: $(date)" >>acwsync.koplugin/${PLUGIN_DIGEST}.digest + echo "Files included:" >>acwsync.koplugin/${PLUGIN_DIGEST}.digest + $STD zip -r koplugin.zip acwsync.koplugin/ + cp -r koplugin.zip "$INSTALL_DIR"/cps/static + mkdir -p "$INSTALL_DIR"/metadata_temp + $STD tar -xf ~/autocaliweb_bkp.tar --directory / + KEPUB_VERSION="$(/usr/bin/kepubify --version)" + CALIBRE_RELEASE="$(curl -s https://api.github.com/repos/kovidgoyal/calibre/releases/latest | grep -o '"tag_name": "[^"]*' | cut -d'"' -f4)" + echo "${KEPUB_VERSION#v}" >"$INSTALL_DIR"/KEPUBIFY_RELEASE + echo "${CALIBRE_RELEASE#v}" >/"$INSTALL_DIR"/CALIBRE_RELEASE + sed 's/^/v/' ~/.autocaliweb >"$INSTALL_DIR"/ACW_RELEASE + chown -R acw:acw "$INSTALL_DIR" + rm ~/autocaliweb_bkp.tar + msg_ok "Updated $APP" + + msg_info "Starting Services" + systemctl start autocaliweb metadata-change-detector acw-ingest-service acw-auto-zipper + msg_ok "Started Services" + + msg_ok "Updated Successfully" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8083${CL}" diff --git a/install/joplin-server-install.sh b/install/joplin-server-install.sh new file mode 100644 index 000000000..77d2c297c --- /dev/null +++ b/install/joplin-server-install.sh @@ -0,0 +1,95 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://joplinapp.org/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt-get install -y \ + git \ + rsync +msg_ok "Installed Dependencies" + +PG_VERSION="16" setup_postgresql +NODE_VERSION=22 NODE_MODULE="yarn@latest,npm@latest,pm2@latest" setup_nodejs +mkdir -p /opt/pm2 +export PM2_HOME=/opt/pm2 +pm2 install pm2-logrotate +pm2 set pm2-logrotate:max_size 100MB +pm2 set pm2-logrotate:retain 5 +pm2 set pm2-logrotate:compress tr + +msg_info "Setting up PostgreSQL Database" +DB_NAME=joplin +DB_USER=joplin +DB_PASS="$(openssl rand -base64 18 | cut -c1-13)" +$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" +$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC'" +{ + echo "Joplin-Credentials" + echo "Joplin Database User: $DB_USER" + echo "Joplin Database Password: $DB_PASS" + echo "Joplin Database Name: $DB_NAME" +} >>~/joplin.creds +msg_ok "Set up PostgreSQL Database" + +fetch_and_deploy_gh_release "joplin-server" "laurent22/joplin" "tarball" "latest" + +msg_info "Setup Joplin Server (Patience)" +LOCAL_IP=$(hostname -I | awk '{print $1}') +cd /opt/joplin-server +$STD yarn config set --home enableTelemetry 0 +export BUILD_SEQUENCIAL=1 +$STD yarn install --inline-builds + +cat </opt/joplin-server/.env +APP_BASE_URL=http://$LOCAL_IP:22300 +APP_PORT=22300 +DB_CLIENT=pg +POSTGRES_PASSWORD=$DB_PASS +POSTGRES_DATABASE=joplin +POSTGRES_USER=joplin +POSTGRES_PORT=5432 +POSTGRES_HOST=localhost +EOF +msg_ok "Setup Joplin Server" + +msg_info "Setup Service" +cat </etc/systemd/system/joplin-server.service +[Unit] +Description=Joplin Server Service +After=network.target + +[Service] +Type=simple +Directory=/opt/joplin-server/packages/server +EnvironmentFile=/opt/joplin-server/.env +ExecStart=/usr/bin/yarn start-prod +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now joplin-server +msg_ok "Service Setup" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 66fcee87e4c9c721b466408a20da44a86cec1fb5 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 11 Sep 2025 20:29:26 +0200 Subject: [PATCH 0857/1733] Update --- ct/joplin-server.sh | 31 +++---------------------------- 1 file changed, 3 insertions(+), 28 deletions(-) diff --git a/ct/joplin-server.sh b/ct/joplin-server.sh index 922b0e04a..96e00bd88 100644 --- a/ct/joplin-server.sh +++ b/ct/joplin-server.sh @@ -28,42 +28,17 @@ function update_script() { exit fi - RELEASE=$(curl -fsSL https://api.github.com/repos/laurent22/joplin/releases/latest | jq '.tag_name' | sed 's/^"v//;s/"$//') if check_for_gh_release "joplin-server" "laurent22/joplin"; then msg_info "Stopping Services" systemctl stop joplin-server msg_ok "Stopped Services" - INSTALL_DIR="/opt/autocaliweb" - export VIRTUAL_ENV="${INSTALL_DIR}/venv" - $STD tar -cf ~/autocaliweb_bkp.tar "$INSTALL_DIR"/{metadata_change_logs,dirs.json,.env,scripts/ingest_watcher.sh,scripts/auto_zipper_wrapper.sh,scripts/metadata_change_detector_wrapper.sh} - fetch_and_deploy_gh_release "autocaliweb" "gelbphoenix/autocaliweb" "tarball" "latest" "/opt/autocaliweb" msg_info "Updating ${APP}" - cd "$INSTALL_DIR" - if [[ ! -d "$VIRTUAL_ENV" ]]; then - $STD uv venv "$VIRTUAL_ENV" - fi - $STD uv sync --all-extras --active - cd "$INSTALL_DIR"/koreader/plugins - PLUGIN_DIGEST="$(find acwsync.koplugin -type f -name "*.lua" -o -name "*.json" | sort | xargs sha256sum | sha256sum | cut -d' ' -f1)" - echo "Plugin files digest: $PLUGIN_DIGEST" >acwsync.koplugin/${PLUGIN_DIGEST}.digest - echo "Build date: $(date)" >>acwsync.koplugin/${PLUGIN_DIGEST}.digest - echo "Files included:" >>acwsync.koplugin/${PLUGIN_DIGEST}.digest - $STD zip -r koplugin.zip acwsync.koplugin/ - cp -r koplugin.zip "$INSTALL_DIR"/cps/static - mkdir -p "$INSTALL_DIR"/metadata_temp - $STD tar -xf ~/autocaliweb_bkp.tar --directory / - KEPUB_VERSION="$(/usr/bin/kepubify --version)" - CALIBRE_RELEASE="$(curl -s https://api.github.com/repos/kovidgoyal/calibre/releases/latest | grep -o '"tag_name": "[^"]*' | cut -d'"' -f4)" - echo "${KEPUB_VERSION#v}" >"$INSTALL_DIR"/KEPUBIFY_RELEASE - echo "${CALIBRE_RELEASE#v}" >/"$INSTALL_DIR"/CALIBRE_RELEASE - sed 's/^/v/' ~/.autocaliweb >"$INSTALL_DIR"/ACW_RELEASE - chown -R acw:acw "$INSTALL_DIR" - rm ~/autocaliweb_bkp.tar + msg_ok "Updated $APP" msg_info "Starting Services" - systemctl start autocaliweb metadata-change-detector acw-ingest-service acw-auto-zipper + systemctl start joplin-server msg_ok "Started Services" msg_ok "Updated Successfully" @@ -78,4 +53,4 @@ description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8083${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:22300${CL}" From 186f33c72a296c6de59b6de6034477bcf2616d1f Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 11 Sep 2025 20:32:57 +0200 Subject: [PATCH 0858/1733] Update Joplin Server --- ct/joplin-server.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/joplin-server.sh b/ct/joplin-server.sh index 96e00bd88..0408b5f8e 100644 --- a/ct/joplin-server.sh +++ b/ct/joplin-server.sh @@ -5,7 +5,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://joplinapp.org/ -APP="Joplin Server" +APP="Joplin-Server" var_tags="${var_tags:-notes}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" From babd0d1b4f9d738f9647c3beeea1a07511f616d0 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 11 Sep 2025 20:45:04 +0200 Subject: [PATCH 0859/1733] Update Joplin Server --- ct/joplin-server.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/joplin-server.sh b/ct/joplin-server.sh index 0408b5f8e..8a5ad377b 100644 --- a/ct/joplin-server.sh +++ b/ct/joplin-server.sh @@ -5,7 +5,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://joplinapp.org/ -APP="Joplin-Server" +APP="joplin-server" var_tags="${var_tags:-notes}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" From 577aad1b35ab77f3d183ab64d77959265913bfb8 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 11 Sep 2025 20:50:10 +0200 Subject: [PATCH 0860/1733] Update Joplin Server --- install/joplin-server-install.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/install/joplin-server-install.sh b/install/joplin-server-install.sh index 77d2c297c..7d5257bb5 100644 --- a/install/joplin-server-install.sh +++ b/install/joplin-server-install.sh @@ -23,10 +23,10 @@ PG_VERSION="16" setup_postgresql NODE_VERSION=22 NODE_MODULE="yarn@latest,npm@latest,pm2@latest" setup_nodejs mkdir -p /opt/pm2 export PM2_HOME=/opt/pm2 -pm2 install pm2-logrotate -pm2 set pm2-logrotate:max_size 100MB -pm2 set pm2-logrotate:retain 5 -pm2 set pm2-logrotate:compress tr +$STD pm2 install pm2-logrotate +$STD pm2 set pm2-logrotate:max_size 100MB +$STD pm2 set pm2-logrotate:retain 5 +$STD pm2 set pm2-logrotate:compress tr msg_info "Setting up PostgreSQL Database" DB_NAME=joplin From 8b1c677e98a3fee7200066f8a5e0d2561b36be59 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 11 Sep 2025 21:02:00 +0200 Subject: [PATCH 0861/1733] Update Joplin Server --- install/joplin-server-install.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/install/joplin-server-install.sh b/install/joplin-server-install.sh index 7d5257bb5..383d0c59c 100644 --- a/install/joplin-server-install.sh +++ b/install/joplin-server-install.sh @@ -47,9 +47,10 @@ msg_ok "Set up PostgreSQL Database" fetch_and_deploy_gh_release "joplin-server" "laurent22/joplin" "tarball" "latest" -msg_info "Setup Joplin Server (Patience)" +msg_info "Setting up Joplin Server (Patience)" LOCAL_IP=$(hostname -I | awk '{print $1}') cd /opt/joplin-server +sed -i "/onenote-converter/d" packages/lib/package.json $STD yarn config set --home enableTelemetry 0 export BUILD_SEQUENCIAL=1 $STD yarn install --inline-builds @@ -59,8 +60,8 @@ APP_BASE_URL=http://$LOCAL_IP:22300 APP_PORT=22300 DB_CLIENT=pg POSTGRES_PASSWORD=$DB_PASS -POSTGRES_DATABASE=joplin -POSTGRES_USER=joplin +POSTGRES_DATABASE=$DB_NAME +POSTGRES_USER=$DB_USER POSTGRES_PORT=5432 POSTGRES_HOST=localhost EOF From 57b06b204342a3199844486265abee7daa681a96 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 11 Sep 2025 21:21:00 +0200 Subject: [PATCH 0862/1733] Update Joplin Server --- install/joplin-server-install.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/install/joplin-server-install.sh b/install/joplin-server-install.sh index 383d0c59c..061787c26 100644 --- a/install/joplin-server-install.sh +++ b/install/joplin-server-install.sh @@ -56,6 +56,8 @@ export BUILD_SEQUENCIAL=1 $STD yarn install --inline-builds cat </opt/joplin-server/.env +PM2_HOME=/opt/pm2 +NODE_ENV=production APP_BASE_URL=http://$LOCAL_IP:22300 APP_PORT=22300 DB_CLIENT=pg @@ -75,7 +77,7 @@ After=network.target [Service] Type=simple -Directory=/opt/joplin-server/packages/server +WorkingDirectory=/opt/joplin-server/packages/server EnvironmentFile=/opt/joplin-server/.env ExecStart=/usr/bin/yarn start-prod Restart=on-failure From 188b566de452262573c6f637f5ed8d8b782c3b42 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 11 Sep 2025 21:54:26 +0200 Subject: [PATCH 0863/1733] Update Joplin Server --- ct/joplin-server.sh | 2 +- install/joplin-server-install.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/joplin-server.sh b/ct/joplin-server.sh index 8a5ad377b..cde793086 100644 --- a/ct/joplin-server.sh +++ b/ct/joplin-server.sh @@ -9,7 +9,7 @@ APP="joplin-server" var_tags="${var_tags:-notes}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" -var_disk="${var_disk:-15}" +var_disk="${var_disk:-20}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" diff --git a/install/joplin-server-install.sh b/install/joplin-server-install.sh index 061787c26..5cc2af32b 100644 --- a/install/joplin-server-install.sh +++ b/install/joplin-server-install.sh @@ -69,7 +69,7 @@ POSTGRES_HOST=localhost EOF msg_ok "Setup Joplin Server" -msg_info "Setup Service" +msg_info "Setting up Service" cat </etc/systemd/system/joplin-server.service [Unit] Description=Joplin Server Service From 6885c07805b772368bd9142b6e48f07f629d47bc Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 11 Sep 2025 22:01:54 +0200 Subject: [PATCH 0864/1733] Update Joplin Server --- ct/joplin-server.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ct/joplin-server.sh b/ct/joplin-server.sh index cde793086..fb6969cf7 100644 --- a/ct/joplin-server.sh +++ b/ct/joplin-server.sh @@ -33,8 +33,14 @@ function update_script() { systemctl stop joplin-server msg_ok "Stopped Services" - msg_info "Updating ${APP}" + fetch_and_deploy_gh_release "joplin-server" "laurent22/joplin" "tarball" "latest" + msg_info "Updating ${APP}" + cd /opt/joplin-server + sed -i "/onenote-converter/d" packages/lib/package.json + $STD yarn config set --home enableTelemetry 0 + export BUILD_SEQUENCIAL=1 + $STD yarn install --inline-builds msg_ok "Updated $APP" msg_info "Starting Services" From 49393af07c9284d1d0f0761a846457d3a75bdfcb Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 11 Sep 2025 20:02:13 +0000 Subject: [PATCH 0865/1733] Update .app files --- ct/headers/joplin-server | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/joplin-server diff --git a/ct/headers/joplin-server b/ct/headers/joplin-server new file mode 100644 index 000000000..3d72492c3 --- /dev/null +++ b/ct/headers/joplin-server @@ -0,0 +1,6 @@ + _ ___ + (_)___ ____ / (_)___ ________ ______ _____ _____ + / / __ \/ __ \/ / / __ \______/ ___/ _ \/ ___/ | / / _ \/ ___/ + / / /_/ / /_/ / / / / / /_____(__ ) __/ / | |/ / __/ / + __/ /\____/ .___/_/_/_/ /_/ /____/\___/_/ |___/\___/_/ +/___/ /_/ From 82240eda70404d3313b6b9e977212f4bf434e857 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Fri, 12 Sep 2025 11:48:19 +0200 Subject: [PATCH 0866/1733] Add Joplin Server json --- frontend/public/json/joplin-server.json | 40 +++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 frontend/public/json/joplin-server.json diff --git a/frontend/public/json/joplin-server.json b/frontend/public/json/joplin-server.json new file mode 100644 index 000000000..49db64487 --- /dev/null +++ b/frontend/public/json/joplin-server.json @@ -0,0 +1,40 @@ +{ + "name": "Joplin Server", + "slug": "joplin-server", + "categories": [ + 12 + ], + "date_created": "2025-09-12", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 22300, + "documentation": "https://joplinapp.org/help/", + "config_path": "/opt/joplin-server/.env", + "website": "https://joplinapp.org/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/joplin.webp", + "description": "Joplin - the privacy-focused note taking app with sync capabilities for Windows, macOS, Linux, Android and iOS.", + "install_methods": [ + { + "type": "default", + "script": "ct/joplin-server.sh", + "resources": { + "cpu": 2, + "ram": 4096, + "hdd": 20, + "os": "Debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": "admin@localhost", + "password": "admin" + }, + "notes": [ + { + "text": "Application can take some time to build, depending on your host speed. Please be patient.", + "type": "info" + } + ] +} From 286262d7c889326790a9ac4489bd71eee0ee1330 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Fri, 12 Sep 2025 08:02:56 -0400 Subject: [PATCH 0867/1733] Remove Autocaliweb --- ct/autocaliweb.sh | 83 ------- frontend/public/json/autocaliweb.json | 35 --- install/autocaliweb-install.sh | 332 -------------------------- 3 files changed, 450 deletions(-) delete mode 100644 ct/autocaliweb.sh delete mode 100644 frontend/public/json/autocaliweb.json delete mode 100644 install/autocaliweb-install.sh diff --git a/ct/autocaliweb.sh b/ct/autocaliweb.sh deleted file mode 100644 index 53cf7fc65..000000000 --- a/ct/autocaliweb.sh +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: vhsdream -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/gelbphoenix/autocaliweb - -APP="Autocaliweb" -var_tags="${var_tags:-ebooks}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-6}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/autocaliweb ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - setup_uv - - RELEASE=$(curl -fsSL https://api.github.com/repos/gelbphoenix/autocaliweb/releases/latest | jq '.tag_name' | sed 's/^"v//;s/"$//') - if check_for_gh_release "autocaliweb" "gelbphoenix/autocaliweb"; then - msg_info "Stopping Services" - systemctl stop autocaliweb metadata-change-detector acw-ingest-service acw-auto-zipper - msg_ok "Stopped Services" - - INSTALL_DIR="/opt/autocaliweb" - export VIRTUAL_ENV="${INSTALL_DIR}/venv" - $STD tar -cf ~/autocaliweb_bkp.tar "$INSTALL_DIR"/{metadata_change_logs,dirs.json,.env,scripts/ingest_watcher.sh,scripts/auto_zipper_wrapper.sh,scripts/metadata_change_detector_wrapper.sh} - fetch_and_deploy_gh_release "autocaliweb" "gelbphoenix/autocaliweb" "tarball" "latest" "/opt/autocaliweb" - msg_info "Updating ${APP}" - cd "$INSTALL_DIR" - if [[ ! -d "$VIRTUAL_ENV" ]]; then - $STD uv venv "$VIRTUAL_ENV" - fi - $STD uv sync --all-extras --active - cd "$INSTALL_DIR"/koreader/plugins - PLUGIN_DIGEST="$(find acwsync.koplugin -type f -name "*.lua" -o -name "*.json" | sort | xargs sha256sum | sha256sum | cut -d' ' -f1)" - echo "Plugin files digest: $PLUGIN_DIGEST" >acwsync.koplugin/${PLUGIN_DIGEST}.digest - echo "Build date: $(date)" >>acwsync.koplugin/${PLUGIN_DIGEST}.digest - echo "Files included:" >>acwsync.koplugin/${PLUGIN_DIGEST}.digest - $STD zip -r koplugin.zip acwsync.koplugin/ - cp -r koplugin.zip "$INSTALL_DIR"/cps/static - mkdir -p "$INSTALL_DIR"/metadata_temp - $STD tar -xf ~/autocaliweb_bkp.tar --directory / - KEPUB_VERSION="$(/usr/bin/kepubify --version)" - CALIBRE_RELEASE="$(curl -s https://api.github.com/repos/kovidgoyal/calibre/releases/latest | grep -o '"tag_name": "[^"]*' | cut -d'"' -f4)" - echo "${KEPUB_VERSION#v}" >"$INSTALL_DIR"/KEPUBIFY_RELEASE - echo "${CALIBRE_RELEASE#v}" >/"$INSTALL_DIR"/CALIBRE_RELEASE - sed 's/^/v/' ~/.autocaliweb >"$INSTALL_DIR"/ACW_RELEASE - chown -R acw:acw "$INSTALL_DIR" - rm ~/autocaliweb_bkp.tar - msg_ok "Updated $APP" - - msg_info "Starting Services" - systemctl start autocaliweb metadata-change-detector acw-ingest-service acw-auto-zipper - msg_ok "Started Services" - - msg_ok "Updated Successfully" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8083${CL}" diff --git a/frontend/public/json/autocaliweb.json b/frontend/public/json/autocaliweb.json deleted file mode 100644 index 792645a2f..000000000 --- a/frontend/public/json/autocaliweb.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "Autocaliweb", - "slug": "autocaliweb", - "categories": [ - 13 - ], - "date_created": "2025-08-30", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 8083, - "documentation": "https://github.com/gelbphoenix/autocaliweb/wiki", - "config_path": "/etc/autocaliweb", - "website": "https://github.com/gelbphoenix/autocaliweb", - "logo": "https://github.com/gelbphoenix/autocaliweb/raw/refs/heads/master/cps/static/icon-dark.svg", - "description": "A modern web management system for eBooks, eComics and PDFs", - "install_methods": [ - { - "type": "default", - "script": "ct/autocaliweb.sh", - "resources": { - "cpu": 2, - "ram": 2048, - "hdd": 6, - "os": "Debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": "admin", - "password": "admin123" - }, - "notes": [] -} diff --git a/install/autocaliweb-install.sh b/install/autocaliweb-install.sh deleted file mode 100644 index f8b89f818..000000000 --- a/install/autocaliweb-install.sh +++ /dev/null @@ -1,332 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2025 Community Scripts ORG -# Author: vhsdream -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/gelbphoenix/autocaliweb - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing dependencies" -$STD apt-get install -y --no-install-recommends \ - python3-dev \ - sqlite3 \ - build-essential \ - libldap2-dev \ - libssl-dev \ - libsasl2-dev \ - imagemagick \ - ghostscript \ - libmagic1 \ - libxi6 \ - libxslt1.1 \ - libxtst6 \ - libxrandr2 \ - libxkbfile1 \ - libxcomposite1 \ - libopengl0 \ - libnss3 \ - libxkbcommon0 \ - libegl1 \ - libxdamage1 \ - libgl1 \ - libglx-mesa0 \ - xz-utils \ - xdg-utils \ - inotify-tools \ - binutils \ - unrar-free \ - zip -msg_ok "Installed dependencies" - -fetch_and_deploy_gh_release "kepubify" "pgaskin/kepubify" "singlefile" "latest" "/usr/bin" "kepubify-linux-64bit" -KEPUB_VERSION="$(/usr/bin/kepubify --version | awk '{print $2}')" - -msg_info "Installing Calibre" -CALIBRE_RELEASE="$(curl -s https://api.github.com/repos/kovidgoyal/calibre/releases/latest | grep -o '"tag_name": "[^"]*' | cut -d'"' -f4)" -CALIBRE_VERSION=${CALIBRE_RELEASE#v} -curl -fsSL https://github.com/kovidgoyal/calibre/releases/download/${CALIBRE_RELEASE}/calibre-${CALIBRE_VERSION}-x86_64.txz -o /tmp/calibre.txz -mkdir -p /opt/calibre -$STD tar -xf /tmp/calibre.txz -C /opt/calibre -rm /tmp/calibre.txz -$STD /opt/calibre/calibre_postinstall -msg_ok "Calibre installed" - -setup_uv - -fetch_and_deploy_gh_release "autocaliweb" "gelbphoenix/autocaliweb" "tarball" "latest" "/opt/autocaliweb" - -msg_info "Configuring Autocaliweb" -INSTALL_DIR="/opt/autocaliweb" -CONFIG_DIR="/etc/autocaliweb" -CALIBRE_LIB_DIR="/opt/calibre-library" -INGEST_DIR="/opt/acw-book-ingest" -SERVICE_USER="acw" -SERVICE_GROUP="acw" -SCRIPTS_DIR="${INSTALL_DIR}/scripts" -export VIRTUAL_ENV="${INSTALL_DIR}/venv" - -mkdir -p "$CONFIG_DIR"/{.config/calibre/plugins,log_archive,.acw_conversion_tmp} -mkdir -p "$CONFIG_DIR"/processed_books/{converted,imported,failed,fixed_originals} -mkdir -p "$INSTALL_DIR"/{metadata_change_logs,metadata_temp} -mkdir -p {"$CALIBRE_LIB_DIR","$INGEST_DIR"} -echo "$CALIBRE_VERSION" >"$INSTALL_DIR"/CALIBRE_RELEASE -echo "${KEPUB_VERSION#v}" >"$INSTALL_DIR"/KEPUBIFY_RELEASE -sed 's/^/v/' ~/.autocaliweb >"$INSTALL_DIR"/ACW_RELEASE - -cd "$INSTALL_DIR" -$STD uv venv "$VIRTUAL_ENV" -$STD uv sync --all-extras --active -cat <./dirs.json -{ - "ingest_folder": "$INGEST_DIR", - "calibre_library_dir": "$CALIBRE_LIB_DIR", - "tmp_conversion_dir": "$CONFIG_DIR/.acw_conversion_tmp" -} -EOF -useradd -s /usr/sbin/nologin -d "$CONFIG_DIR" -M "$SERVICE_USER" -ln -sf "$CONFIG_DIR"/.config/calibre/plugins "$CONFIG_DIR"/calibre_plugins -cat <"$INSTALL_DIR"/.env -ACW_INSTALL_DIR=$INSTALL_DIR -ACW_CONFIG_DIR=$CONFIG_DIR -ACW_USER=$SERVICE_USER -ACW_GROUP=$SERVICE_GROUP -LIBRARY_DIR=$CALIBRE_LIB_DIR -EOF -msg_ok "Configured Autocaliweb" - -msg_info "Creating ACWSync Plugin for KOReader" -cd "$INSTALL_DIR"/koreader/plugins -PLUGIN_DIGEST="$(find acwsync.koplugin -type f -name "*.lua" -o -name "*.json" | sort | xargs sha256sum | sha256sum | cut -d' ' -f1)" -echo "Plugin files digest: $PLUGIN_DIGEST" >acwsync.koplugin/${PLUGIN_DIGEST}.digest -echo "Build date: $(date)" >>acwsync.koplugin/${PLUGIN_DIGEST}.digest -echo "Files included:" >>acwsync.koplugin/${PLUGIN_DIGEST}.digest -$STD zip -r koplugin.zip acwsync.koplugin/ -cp -r koplugin.zip "$INSTALL_DIR"/cps/static -msg_ok "Created ACWSync Plugin" - -msg_info "Initializing databases" -KEPUBIFY_PATH=$(command -v kepubify 2>/dev/null || echo "/usr/bin/kepubify") -EBOOK_CONVERT_PATH=$(command -v ebook-convert 2>/dev/null || echo "/usr/bin/ebook-convert") -CALIBRE_BIN_DIR=$(dirname "$EBOOK_CONVERT_PATH") -curl -fsSL https://github.com/gelbphoenix/autocaliweb/raw/refs/heads/main/library/metadata.db -o "$CALIBRE_LIB_DIR"/metadata.db -curl -fsSL https://github.com/gelbphoenix/autocaliweb/raw/refs/heads/main/library/app.db -o "$CONFIG_DIR"/app.db -sqlite3 "$CONFIG_DIR/app.db" <"$SCRIPTS_DIR"/ingest_watcher.sh -#!/bin/bash - -INSTALL_PATH="$INSTALL_DIR" -WATCH_FOLDER=\$(grep -o '"ingest_folder": "[^"]*' \${INSTALL_PATH}/dirs.json | grep -o '[^"]*\$') -echo "[acw-ingest-service] Watching folder: \$WATCH_FOLDER" - -# Monitor the folder for new files -/usr/bin/inotifywait -m -r --format="%e %w%f" -e close_write -e moved_to "\$WATCH_FOLDER" | -while read -r events filepath ; do - echo "[acw-ingest-service] New files detected - \$filepath - Starting Ingest Processor..." - # Use the Python interpreter from the virtual environment - \${INSTALL_PATH}/venv/bin/python \${INSTALL_PATH}/scripts/ingest_processor.py "\$filepath" -done -EOF - -# auto-zipper -cat <"$SCRIPTS_DIR"/auto_zipper_wrapper.sh -#!/bin/bash - -# Source virtual environment -source ${INSTALL_DIR}/venv/bin/activate - -WAKEUP="23:59" - -while true; do - # Replace expr with modern Bash arithmetic (safer and less prone to parsing issues) - # fix: expr: non-integer argument and sleep: missing operand - SECS=\$(( \$(date -d "\$WAKEUP" +%s) - \$(date -d "now" +%s) )) - if [[ \$SECS -lt 0 ]]; then - SECS=\$(( \$(date -d "tomorrow \$WAKEUP" +%s) - \$(date -d "now" +%s) )) - fi - echo "[acw-auto-zipper] Next run in \$SECS seconds." - sleep \$SECS & - wait \$! - - # Use virtual environment python - python ${SCRIPTS_DIR}/auto_zip.py - - if [[ \$? == 1 ]]; then - echo "[acw-auto-zipper] Error occurred during script initialisation." - elif [[ \$? == 2 ]]; then - echo "[acw-auto-zipper] Error occurred while zipping today's files." - elif [[ \$? == 3 ]]; then - echo "[acw-auto-zipper] Error occurred while trying to remove zipped files." - fi - - sleep 60 -done -EOF - -# metadata change detector -cat <"$SCRIPTS_DIR"/metadata_change_detector_wrapper.sh -#!/bin/bash -# metadata_change_detector_wrapper.sh - Wrapper for periodic metadata enforcement - -# Source virtual environment -source ${INSTALL_DIR}/venv/bin/activate - -# Configuration -CHECK_INTERVAL=300 # Check every 5 minutes (300 seconds) -METADATA_LOGS_DIR="${INSTALL_DIR}/metadata_change_logs" - -echo "[metadata-change-detector] Starting metadata change detector service..." -echo "[metadata-change-detector] Checking for changes every \$CHECK_INTERVAL seconds" - -while true; do - # Check if there are any log files to process - if [ -d "\$METADATA_LOGS_DIR" ] && [ "\$(ls -A \$METADATA_LOGS_DIR 2>/dev/null)" ]; then - echo "[metadata-change-detector] Found metadata change logs, processing..." - - # Process each log file - for log_file in "\$METADATA_LOGS_DIR"/*.json; do - if [ -f "\$log_file" ]; then - log_name=\$(basename "\$log_file") - echo "[metadata-change-detector] Processing log: \$log_name" - - # Call cover_enforcer.py with the log file - ${INSTALL_DIR}/venv/bin/python ${SCRIPTS_DIR}/cover_enforcer.py --log "\$log_name" - - if [ \$? -eq 0 ]; then - echo "[metadata-change-detector] Successfully processed \$log_name" - else - echo "[metadata-change-detector] Error processing \$log_name" - fi - fi - done - else - echo "[metadata-change-detector] No metadata changes detected" - fi - - echo "[metadata-change-detector] Sleeping for \$CHECK_INTERVAL seconds..." - sleep \$CHECK_INTERVAL -done -EOF -chmod +x "$SCRIPTS_DIR"/{ingest_watcher.sh,auto_zipper_wrapper.sh,metadata_change_detector_wrapper.sh} -chown -R "$SERVICE_USER":"$SERVICE_GROUP" {"$INSTALL_DIR","$CONFIG_DIR","$INGEST_DIR","$CALIBRE_LIB_DIR"} - -# systemd service files -SYS_PATH="/etc/systemd/system" -cat <"$SYS_PATH"/autocaliweb.service -[Unit] -Description=Autocaliweb -After=network.target -Wants=network-online.target -After=network-online.target - -[Service] -Type=simple -User=$SERVICE_USER -Group=$SERVICE_GROUP -WorkingDirectory=$INSTALL_DIR -Environment=PATH=$INSTALL_DIR/venv/bin:/usr/bin:/bin -Environment=PYTHONPATH=$SCRIPTS_DIR:$INSTALL_DIR -Environment=PYTHONDONTWRITEBYTECODE=1 -Environment=PYTHONUNBUFFERED=1 -Environment=CALIBRE_DBPATH=$CONFIG_DIR -EnvironmentFile=$INSTALL_DIR/.env -ExecStart=$INSTALL_DIR/venv/bin/python $INSTALL_DIR/cps.py -p $CONFIG_DIR/app.db - -Restart=always -RestartSec=10 -StandardOutput=journal -StandardError=journal - -[Install] -WantedBy=multi-user.target -EOF - -cat <"$SYS_PATH"/acw-ingest-service.service -[Unit] -Description=Autocaliweb Ingest Processor Service -After=autocaliweb.service -Requires=autocaliweb.service - -[Service] -User=${SERVICE_USER} -Group=${SERVICE_GROUP} -WorkingDirectory=${INSTALL_DIR} -Environment=CALIBRE_DBPATH=${CONFIG_DIR} -Environment=HOME=${CONFIG_DIR} -ExecStart=/bin/bash ${SCRIPTS_DIR}/ingest_watcher.sh -Restart=always -StandardOutput=journal -StandardError=journal - -[Install] -WantedBy=multi-user.target -EOF - -cat <"$SYS_PATH"/acw-auto-zipper.service -[Unit] -Description=Autocaliweb Auto Zipper Service -After=network.target - -[Service] -User=${SERVICE_USER} -Group=${SERVICE_GROUP} -WorkingDirectory=${INSTALL_DIR} -Environment=CALIBRE_DBPATH=${CONFIG_DIR} -ExecStart=${SCRIPTS_DIR}/auto_zipper_wrapper.sh -Restart=always -StandardOutput=journal -StandardError=journal - -[Install] -WantedBy=multi-user.target -EOF - -cat <"$SYS_PATH"/metadata-change-detector.service -[Unit] -Description=Autocaliweb Metadata Change Detector -After=network.target - -[Service] -User=${SERVICE_USER} -Group=${SERVICE_GROUP} -WorkingDirectory=${INSTALL_DIR} -ExecStart=/bin/bash ${SCRIPTS_DIR}/metadata_change_detector_wrapper.sh -Restart=always -StandardOutput=journal -StandardError=journal -Environment=CALIBRE_DBPATH=${CONFIG_DIR} -Environment=HOME=${CONFIG_DIR} -[Install] -WantedBy=multi-user.target -EOF - -systemctl -q enable --now autocaliweb acw-ingest-service acw-auto-zipper metadata-change-detector -msg_ok "Created scripts and service files" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" From 3e1bc0e66b30716461bf99df0f4d949f2b4a9e56 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 12 Sep 2025 12:03:19 +0000 Subject: [PATCH 0868/1733] Update .app files --- ct/headers/autocaliweb | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 ct/headers/autocaliweb diff --git a/ct/headers/autocaliweb b/ct/headers/autocaliweb deleted file mode 100644 index a5aa7d63b..000000000 --- a/ct/headers/autocaliweb +++ /dev/null @@ -1,6 +0,0 @@ - ___ __ ___ __ - / | __ __/ /_____ _________ _/ (_) _____ / /_ - / /| |/ / / / __/ __ \/ ___/ __ `/ / / | /| / / _ \/ __ \ - / ___ / /_/ / /_/ /_/ / /__/ /_/ / / /| |/ |/ / __/ /_/ / -/_/ |_\__,_/\__/\____/\___/\__,_/_/_/ |__/|__/\___/_.___/ - From 3c476e0fbbdc97ba7508af8376bd65ff47c14328 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Fri, 12 Sep 2025 19:59:47 +0200 Subject: [PATCH 0869/1733] Joplin Server Update PSQL to v17 --- install/joplin-server-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/joplin-server-install.sh b/install/joplin-server-install.sh index 5cc2af32b..45f201660 100644 --- a/install/joplin-server-install.sh +++ b/install/joplin-server-install.sh @@ -19,7 +19,7 @@ $STD apt-get install -y \ rsync msg_ok "Installed Dependencies" -PG_VERSION="16" setup_postgresql +PG_VERSION="17" setup_postgresql NODE_VERSION=22 NODE_MODULE="yarn@latest,npm@latest,pm2@latest" setup_nodejs mkdir -p /opt/pm2 export PM2_HOME=/opt/pm2 From 3e1c6b9f5f6418807c9ab35bb44dca20155040c1 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Fri, 12 Sep 2025 22:41:42 +0200 Subject: [PATCH 0870/1733] Add SigNoz script --- ct/signoz.sh | 58 +++++++++ install/signoz-install.sh | 263 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 321 insertions(+) create mode 100644 ct/signoz.sh create mode 100644 install/signoz-install.sh diff --git a/ct/signoz.sh b/ct/signoz.sh new file mode 100644 index 000000000..160ab60b8 --- /dev/null +++ b/ct/signoz.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://signoz.io/ + +APP="signoz" +var_tags="${var_tags:-notes}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-4096}" +var_disk="${var_disk:-20}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/signoz ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "signoz" "SigNoz/signoz"; then + msg_info "Stopping Services" + systemctl stop signoz + msg_ok "Stopped Services" + + fetch_and_deploy_gh_release "signoz" "SigNoz/signoz" "prebuild" "latest" "/opt/signoz" "signoz-community_linux_amd64.tar.gz" + + msg_info "Updating ${APP}" + + msg_ok "Updated $APP" + + msg_info "Starting Services" + systemctl start signoz + msg_ok "Started Services" + + msg_ok "Updated Successfully" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:22300${CL}" diff --git a/install/signoz-install.sh b/install/signoz-install.sh new file mode 100644 index 000000000..92bc0b5dd --- /dev/null +++ b/install/signoz-install.sh @@ -0,0 +1,263 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://signoz.io/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt-get install -y \ + apt-transport-https \ + ca-certificates +msg_ok "Installed Dependencies" + +JAVA_VERSION="21" setup_java + +msg_info "Setting up ClickHouse" +curl -fsSL "https://packages.clickhouse.com/rpm/lts/repodata/repomd.xml.key" | gpg --dearmor -o /usr/share/keyrings/clickhouse-keyring.gpg +echo "deb [signed-by=/usr/share/keyrings/clickhouse-keyring.gpg arch=amd64] https://packages.clickhouse.com/deb stable main" >/etc/apt/sources.list.d/clickhouse.list +$STD apt-get update +export DEBIAN_FRONTEND=noninteractive +$STD apt-get install -y clickhouse-server clickhouse-client +msg_ok "Setup ClickHouse" + +msg_info "Setting up Zookeeper" +curl -fsSL https://dlcdn.apache.org/zookeeper/zookeeper-3.8.4/apache-zookeeper-3.8.4-bin.tar.gz -o zookeeper.tar.gz +tar -xzf zookeeper.tar.gz +mkdir -p /opt/zookeeper +mkdir -p /var/lib/zookeeper +mkdir -p /var/log/zookeeper +cp -r apache-zookeeper-3.8.4-bin/* /opt/zookeeper + +cat </opt/zookeeper/conf/zoo.cfg +tickTime=2000 +dataDir=/var/lib/zookeeper +clientPort=2181 +admin.serverPort=3181 +EOF + +cat </opt/zookeeper/conf/zoo.env +ZOO_LOG_DIR=/var/log/zookeeper +EOF + +cat </etc/systemd/system/zookeeper.service +[Unit] +Description=Zookeeper +Documentation=http://zookeeper.apache.org + +[Service] +EnvironmentFile=/opt/zookeeper/conf/zoo.env +Type=forking +WorkingDirectory=/opt/zookeeper +ExecStart=/opt/zookeeper/bin/zkServer.sh start /opt/zookeeper/conf/zoo.cfg +ExecStop=/opt/zookeeper/bin/zkServer.sh stop /opt/zookeeper/conf/zoo.cfg +ExecReload=/opt/zookeeper/bin/zkServer.sh restart /opt/zookeeper/conf/zoo.cfg +TimeoutSec=30 +Restart=on-failure + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now zookeeper +msg_ok "Setup Zookeeper" + +msg_info "Configuring ClickHouse" +cat </etc/clickhouse-server/config.d/cluster.xml + + + /clickhouse/task_queue/ddl + + + + + + 127.0.0.1 + 9000 + + + + + + + 127.0.0.1 + 2181 + + + + 01 + 01 + + +EOF +systemctl enable -q --now clickhouse-server +msg_ok "Configured ClickHouse" + +fetch_and_deploy_gh_release "signoz-schema-migrator" "SigNoz/signoz-otel-collector" "prebuild" "latest" "/opt/signoz-schema-migrator" "signoz-schema-migrator_linux_amd64.tar.gz" + +msg_info "Running ClickHouse migrations" +cd /opt/signoz-schema-migrator/bin +$STD ./signoz-schema-migrator sync --dsn="tcp://localhost:9000?password=" --replication=true --up= +$STD ./signoz-schema-migrator async --dsn="tcp://localhost:9000?password=" --replication=true --up= +msg_ok "ClickHouse Migrations Completed" + +fetch_and_deploy_gh_release "signoz" "SigNoz/signoz" "prebuild" "latest" "/opt/signoz" "signoz-community_linux_amd64.tar.gz" + +msg_info "Setting up Signoz" +mkdir -p /var/lib/signoz + +cat </opt/signoz/conf/systemd.env +SIGNOZ_INSTRUMENTATION_LOGS_LEVEL=info +INVITE_EMAIL_TEMPLATE=/opt/signoz/templates/invitation_email_template.html +SIGNOZ_SQLSTORE_SQLITE_PATH=/var/lib/signoz/signoz.db +SIGNOZ_WEB_ENABLED=true +SIGNOZ_WEB_DIRECTORY=/opt/signoz/web +SIGNOZ_JWT_SECRET=secret +SIGNOZ_ALERTMANAGER_PROVIDER=signoz +SIGNOZ_TELEMETRYSTORE_PROVIDER=clickhouse +SIGNOZ_TELEMETRYSTORE_CLICKHOUSE_DSN=tcp://localhost:9000?password= +DOT_METRICS_ENABLED=true +EOF + +cat </etc/systemd/system/signoz.service +[Unit] +Description=SigNoz +Documentation=https://signoz.io/docs +After=clickhouse-server.service + +[Service] +Type=simple +KillMode=mixed +Restart=on-failure +WorkingDirectory=/opt/signoz +EnvironmentFile=/opt/signoz/conf/systemd.env +ExecStart=/opt/signoz/bin/signoz server + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now signoz +msg_ok "Setup Signoz" + +fetch_and_deploy_gh_release "signoz-otel-collector" "SigNoz/signoz-otel-collector" "prebuild" "latest" "/opt/signoz-otel-collector" "signoz-otel-collector_linux_amd64.tar.gz" + +msg_info "Setting up Signoz OTel Collector" +mkdir -p /var/lib/signoz-otel-collector + +cat </opt/signoz-otel-collector/conf/config.yaml +receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + max_recv_msg_size_mib: 16 + http: + endpoint: 0.0.0.0:4318 + jaeger: + protocols: + grpc: + endpoint: 0.0.0.0:14250 + thrift_http: + endpoint: 0.0.0.0:14268 + httplogreceiver/heroku: + endpoint: 0.0.0.0:8081 + source: heroku + httplogreceiver/json: + endpoint: 0.0.0.0:8082 + source: json +processors: + batch: + send_batch_size: 50000 + timeout: 1s + signozspanmetrics/delta: + metrics_exporter: signozclickhousemetrics + latency_histogram_buckets: [100us, 1ms, 2ms, 6ms, 10ms, 50ms, 100ms, 250ms, 500ms, 1000ms, 1400ms, 2000ms, 5s, 10s, 20s, 40s, 60s] + dimensions_cache_size: 100000 + dimensions: + - name: service.namespace + default: default + - name: deployment.environment + default: default + - name: signoz.collector.id + aggregation_temporality: AGGREGATION_TEMPORALITY_DELTA +extensions: + health_check: + endpoint: 0.0.0.0:13133 + zpages: + endpoint: localhost:55679 + pprof: + endpoint: localhost:1777 +exporters: + clickhousetraces: + datasource: tcp://localhost:9000/signoz_traces?password= + use_new_schema: true + signozclickhousemetrics: + dsn: tcp://localhost:9000/signoz_metrics?password= + timeout: 45s + clickhouselogsexporter: + dsn: tcp://localhost:9000/signoz_logs?password= + timeout: 10s + use_new_schema: true + metadataexporter: + dsn: tcp://localhost:9000/signoz_metadata?password= + timeout: 10s + tenant_id: default + cache: + provider: in_memory +service: + telemetry: + logs: + encoding: json + extensions: [health_check, zpages, pprof] + pipelines: + traces: + receivers: [otlp, jaeger] + processors: [signozspanmetrics/delta, batch] + exporters: [clickhousetraces, metadataexporter] + metrics: + receivers: [otlp] + processors: [batch] + exporters: [metadataexporter, signozclickhousemetrics] + logs: + receivers: [otlp, httplogreceiver/heroku, httplogreceiver/json] + processors: [batch] + exporters: [clickhouselogsexporter, metadataexporter] +EOF + +cat </opt/signoz-otel-collector/conf/opamp.yaml +server_endpoint: ws://127.0.0.1:4320/v1/opamp +EOF + +cat </etc/systemd/system/signoz-otel-collector.service +[Unit] +Description=SigNoz OTel Collector +Documentation=https://signoz.io/docs +After=clickhouse-server.service + +[Service] +Type=simple +KillMode=mixed +Restart=on-failure +WorkingDirectory=/opt/signoz-otel-collector +ExecStart=/opt/signoz-otel-collector/bin/signoz-otel-collector --config=/opt/signoz-otel-collector/conf/config.yaml --manager-config=/opt/signoz-otel-collector/conf/opamp.yaml --copy-path=/var/lib/signoz-otel-collector/config.yaml + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now signoz-otel-collector + + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 6f88aaf4f864e880d8837830e63eaed2dd3287fd Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Sat, 13 Sep 2025 11:14:30 +0200 Subject: [PATCH 0871/1733] Create proxmox-datacenter-manager.sh --- ct/proxmox-datacenter-manager.sh | 44 ++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 ct/proxmox-datacenter-manager.sh diff --git a/ct/proxmox-datacenter-manager.sh b/ct/proxmox-datacenter-manager.sh new file mode 100644 index 000000000..9239e8171 --- /dev/null +++ b/ct/proxmox-datacenter-manager.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: CrazyWolf13 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: Proxmox Server Solution GmbH + +APP="Proxmox-Datacenter-Manager" +var_tags="${var_tags:-datacenter}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-10}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -e /usr/sbin/proxmox-datacenter-manager-admin ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating $APP LXC" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Updated $APP LXC" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:8443${CL}" From e7208fd41d1df45c4afcb17594f895e347fba967 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Sat, 13 Sep 2025 11:15:18 +0200 Subject: [PATCH 0872/1733] Add installation script for Proxmox Datacenter Manager --- install/proxmox-datacenter-manager-install.sh | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 install/proxmox-datacenter-manager-install.sh diff --git a/install/proxmox-datacenter-manager-install.sh b/install/proxmox-datacenter-manager-install.sh new file mode 100644 index 000000000..5e6b2e3e5 --- /dev/null +++ b/install/proxmox-datacenter-manager-install.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: CrazyWolf13 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: Proxmox Server Solution GmbH + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Proxmox Datacenter Manager" +curl -fsSL https://enterprise.proxmox.com/debian/proxmox-release-bookworm.gpg | gpg --dearmor -o /etc/apt/keyrings/proxmox-release-bookworm.gpg +echo "deb [signed-by=/etc/apt/keyrings/proxmox-release-bookworm.gpg] http://download.proxmox.com/debian/pdm bookworm pdm-test " >/etc/apt/sources.list.d/proxmox-release-bookworm.list +$STD apt-get update +$STD apt-get install -y \ + proxmox-datacenter-manager \ + proxmox-datacenter-manager-ui +msg_ok "Installed Proxmox Datacenter Manager" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From abed3ab4f534917ee1386db314a40c25bd2ec732 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Sat, 13 Sep 2025 11:24:20 +0200 Subject: [PATCH 0873/1733] Update Proxmox installation script for keyring changes --- install/proxmox-datacenter-manager-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/proxmox-datacenter-manager-install.sh b/install/proxmox-datacenter-manager-install.sh index 5e6b2e3e5..406b9aec6 100644 --- a/install/proxmox-datacenter-manager-install.sh +++ b/install/proxmox-datacenter-manager-install.sh @@ -14,8 +14,8 @@ network_check update_os msg_info "Installing Proxmox Datacenter Manager" -curl -fsSL https://enterprise.proxmox.com/debian/proxmox-release-bookworm.gpg | gpg --dearmor -o /etc/apt/keyrings/proxmox-release-bookworm.gpg -echo "deb [signed-by=/etc/apt/keyrings/proxmox-release-bookworm.gpg] http://download.proxmox.com/debian/pdm bookworm pdm-test " >/etc/apt/sources.list.d/proxmox-release-bookworm.list +curl -fsSL https://enterprise.proxmox.com/debian/proxmox-archive-keyring-trixie.gpg -o /usr/share/keyrings/proxmox-archive-keyring.gpg +echo "deb [signed-by=/usr/share/keyrings/proxmox-archive-keyring.gpg] http://download.proxmox.com/debian/pdm bookworm pdm-test " >/etc/apt/sources.list.d/pdm-test.list $STD apt-get update $STD apt-get install -y \ proxmox-datacenter-manager \ From 62fd9bf3b243e0952d6b38744a82a572b67145d8 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Sat, 13 Sep 2025 12:43:11 +0200 Subject: [PATCH 0874/1733] Refactor installation command for Proxmox Datacenter Manager --- install/proxmox-datacenter-manager-install.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/install/proxmox-datacenter-manager-install.sh b/install/proxmox-datacenter-manager-install.sh index 406b9aec6..d8b21e186 100644 --- a/install/proxmox-datacenter-manager-install.sh +++ b/install/proxmox-datacenter-manager-install.sh @@ -17,9 +17,11 @@ msg_info "Installing Proxmox Datacenter Manager" curl -fsSL https://enterprise.proxmox.com/debian/proxmox-archive-keyring-trixie.gpg -o /usr/share/keyrings/proxmox-archive-keyring.gpg echo "deb [signed-by=/usr/share/keyrings/proxmox-archive-keyring.gpg] http://download.proxmox.com/debian/pdm bookworm pdm-test " >/etc/apt/sources.list.d/pdm-test.list $STD apt-get update -$STD apt-get install -y \ - proxmox-datacenter-manager \ - proxmox-datacenter-manager-ui +$STD DEBIAN_FRONTEND=noninteractive \ +$STD apt-get -o Dpkg::Options::="--force-confdef" \ + -o Dpkg::Options::="--force-confold" \ + install -y proxmox-datacenter-manager \ + proxmox-datacenter-manager-ui msg_ok "Installed Proxmox Datacenter Manager" motd_ssh From e44708be95709ef7e381028687c69cb69d273fca Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Sat, 13 Sep 2025 12:46:12 +0200 Subject: [PATCH 0875/1733] Fix installation command for Proxmox Datacenter Manager --- install/proxmox-datacenter-manager-install.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/install/proxmox-datacenter-manager-install.sh b/install/proxmox-datacenter-manager-install.sh index d8b21e186..e8246bd16 100644 --- a/install/proxmox-datacenter-manager-install.sh +++ b/install/proxmox-datacenter-manager-install.sh @@ -17,8 +17,7 @@ msg_info "Installing Proxmox Datacenter Manager" curl -fsSL https://enterprise.proxmox.com/debian/proxmox-archive-keyring-trixie.gpg -o /usr/share/keyrings/proxmox-archive-keyring.gpg echo "deb [signed-by=/usr/share/keyrings/proxmox-archive-keyring.gpg] http://download.proxmox.com/debian/pdm bookworm pdm-test " >/etc/apt/sources.list.d/pdm-test.list $STD apt-get update -$STD DEBIAN_FRONTEND=noninteractive \ -$STD apt-get -o Dpkg::Options::="--force-confdef" \ +$STD DEBIAN_FRONTEND=noninteractive apt-get -o Dpkg::Options::="--force-confdef" \ -o Dpkg::Options::="--force-confold" \ install -y proxmox-datacenter-manager \ proxmox-datacenter-manager-ui From b1797e079389ad5368f341bcca94637d053ac950 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Sat, 13 Sep 2025 12:54:18 +0200 Subject: [PATCH 0876/1733] Fix installation command for Proxmox Datacenter Manager --- install/proxmox-datacenter-manager-install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/install/proxmox-datacenter-manager-install.sh b/install/proxmox-datacenter-manager-install.sh index e8246bd16..6c62976e9 100644 --- a/install/proxmox-datacenter-manager-install.sh +++ b/install/proxmox-datacenter-manager-install.sh @@ -17,7 +17,8 @@ msg_info "Installing Proxmox Datacenter Manager" curl -fsSL https://enterprise.proxmox.com/debian/proxmox-archive-keyring-trixie.gpg -o /usr/share/keyrings/proxmox-archive-keyring.gpg echo "deb [signed-by=/usr/share/keyrings/proxmox-archive-keyring.gpg] http://download.proxmox.com/debian/pdm bookworm pdm-test " >/etc/apt/sources.list.d/pdm-test.list $STD apt-get update -$STD DEBIAN_FRONTEND=noninteractive apt-get -o Dpkg::Options::="--force-confdef" \ +DEBIAN_FRONTEND=noninteractive +$STD apt-get -o Dpkg::Options::="--force-confdef" \ -o Dpkg::Options::="--force-confold" \ install -y proxmox-datacenter-manager \ proxmox-datacenter-manager-ui From ae7fdb486275fc2c6a0bb2244ce9a87bcaed1349 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Sat, 13 Sep 2025 13:20:50 +0200 Subject: [PATCH 0877/1733] Configure Proxmox repository for Debian 12 Add Proxmox repository for Debian 12 and update keyring. --- ct/proxmox-datacenter-manager.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ct/proxmox-datacenter-manager.sh b/ct/proxmox-datacenter-manager.sh index 9239e8171..b3f125742 100644 --- a/ct/proxmox-datacenter-manager.sh +++ b/ct/proxmox-datacenter-manager.sh @@ -27,6 +27,14 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi + + if grep -q 'Debian GNU/Linux 12' /etc/os-release && [ -f /etc/apt/sources.list.d/proxmox-release-bookworm.list ] && [ -f /etc/apt/sources.list.d/pdm-test.list ]; then + echo "deb [signed-by=/usr/share/keyrings/proxmox-archive-keyring.gpg] http://download.proxmox.com/debian/pdm bookworm pdm-test" > /etc/apt/sources.list.d/pdm-test.list + curl -fsSL https://enterprise.proxmox.com/debian/proxmox-archive-keyring-trixie.gpg -o /usr/share/keyrings/proxmox-archive-keyring.gpg + rm -f /etc/apt/keyrings/proxmox-release-bookworm.gpg /etc/apt/sources.list.d/proxmox-release-bookworm.list + $STD apt update + fi + msg_info "Updating $APP LXC" $STD apt-get update $STD apt-get -y upgrade From 3311221d5e20059b6dcb47b3bd6773984ad4866e Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Sat, 13 Sep 2025 13:23:01 +0200 Subject: [PATCH 0878/1733] Fix source formats and update Proxmox keyrings Updated outdated source formats for Proxmox and removed old keyrings. --- ct/proxmox-datacenter-manager.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ct/proxmox-datacenter-manager.sh b/ct/proxmox-datacenter-manager.sh index b3f125742..da490239b 100644 --- a/ct/proxmox-datacenter-manager.sh +++ b/ct/proxmox-datacenter-manager.sh @@ -29,10 +29,12 @@ function update_script() { fi if grep -q 'Debian GNU/Linux 12' /etc/os-release && [ -f /etc/apt/sources.list.d/proxmox-release-bookworm.list ] && [ -f /etc/apt/sources.list.d/pdm-test.list ]; then + msg_info "Updating outdated outdated source formats" echo "deb [signed-by=/usr/share/keyrings/proxmox-archive-keyring.gpg] http://download.proxmox.com/debian/pdm bookworm pdm-test" > /etc/apt/sources.list.d/pdm-test.list curl -fsSL https://enterprise.proxmox.com/debian/proxmox-archive-keyring-trixie.gpg -o /usr/share/keyrings/proxmox-archive-keyring.gpg rm -f /etc/apt/keyrings/proxmox-release-bookworm.gpg /etc/apt/sources.list.d/proxmox-release-bookworm.list $STD apt update + msg_ok "Updated old sources" fi msg_info "Updating $APP LXC" From f734886321b7ad123def58a5c5e124448705e5c3 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Sat, 13 Sep 2025 13:25:01 +0200 Subject: [PATCH 0879/1733] Change apt to apt-get for updating sources --- ct/proxmox-datacenter-manager.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/proxmox-datacenter-manager.sh b/ct/proxmox-datacenter-manager.sh index da490239b..19cf1328e 100644 --- a/ct/proxmox-datacenter-manager.sh +++ b/ct/proxmox-datacenter-manager.sh @@ -33,7 +33,7 @@ function update_script() { echo "deb [signed-by=/usr/share/keyrings/proxmox-archive-keyring.gpg] http://download.proxmox.com/debian/pdm bookworm pdm-test" > /etc/apt/sources.list.d/pdm-test.list curl -fsSL https://enterprise.proxmox.com/debian/proxmox-archive-keyring-trixie.gpg -o /usr/share/keyrings/proxmox-archive-keyring.gpg rm -f /etc/apt/keyrings/proxmox-release-bookworm.gpg /etc/apt/sources.list.d/proxmox-release-bookworm.list - $STD apt update + $STD apt-get update msg_ok "Updated old sources" fi From f4aaece8f38a511d2929d9bfc0d7ccb43c4a8d28 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sat, 13 Sep 2025 11:25:16 +0000 Subject: [PATCH 0880/1733] Update .app files --- ct/headers/proxmox-datacenter-manager | 6 ++++++ ct/headers/signoz | 6 ++++++ 2 files changed, 12 insertions(+) create mode 100644 ct/headers/proxmox-datacenter-manager create mode 100644 ct/headers/signoz diff --git a/ct/headers/proxmox-datacenter-manager b/ct/headers/proxmox-datacenter-manager new file mode 100644 index 000000000..041028c7b --- /dev/null +++ b/ct/headers/proxmox-datacenter-manager @@ -0,0 +1,6 @@ + ____ ____ __ __ __ ___ + / __ \_________ _ ______ ___ ____ _ __ / __ \____ _/ /_____ _________ ____ / /____ _____ / |/ /___ _____ ____ _____ ____ _____ + / /_/ / ___/ __ \| |/_/ __ `__ \/ __ \| |/_/_____/ / / / __ `/ __/ __ `/ ___/ _ \/ __ \/ __/ _ \/ ___/_____/ /|_/ / __ `/ __ \/ __ `/ __ `/ _ \/ ___/ + / ____/ / / /_/ /> Date: Sun, 14 Sep 2025 09:44:34 +0200 Subject: [PATCH 0881/1733] Update SigNoz --- install/signoz-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/install/signoz-install.sh b/install/signoz-install.sh index 92bc0b5dd..ad4b0b862 100644 --- a/install/signoz-install.sh +++ b/install/signoz-install.sh @@ -253,7 +253,6 @@ WantedBy=multi-user.target EOF systemctl enable -q --now signoz-otel-collector - motd_ssh customize From 9de7cd563c1e164b8838da92af29d34107474499 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sun, 14 Sep 2025 14:29:40 +0200 Subject: [PATCH 0882/1733] Update SigNoz --- install/signoz-install.sh | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/install/signoz-install.sh b/install/signoz-install.sh index ad4b0b862..d49db2033 100644 --- a/install/signoz-install.sh +++ b/install/signoz-install.sh @@ -30,12 +30,12 @@ $STD apt-get install -y clickhouse-server clickhouse-client msg_ok "Setup ClickHouse" msg_info "Setting up Zookeeper" -curl -fsSL https://dlcdn.apache.org/zookeeper/zookeeper-3.8.4/apache-zookeeper-3.8.4-bin.tar.gz -o zookeeper.tar.gz -tar -xzf zookeeper.tar.gz +curl -fsSL https://dlcdn.apache.org/zookeeper/zookeeper-3.8.4/apache-zookeeper-3.8.4-bin.tar.gz -o "$HOME/zookeeper.tar.gz" +tar -xzf "$HOME/zookeeper.tar.gz" mkdir -p /opt/zookeeper mkdir -p /var/lib/zookeeper mkdir -p /var/log/zookeeper -cp -r apache-zookeeper-3.8.4-bin/* /opt/zookeeper +cp -r ~/apache-zookeeper-3.8.4-bin/* /opt/zookeeper cat </opt/zookeeper/conf/zoo.cfg tickTime=2000 @@ -110,7 +110,7 @@ msg_ok "ClickHouse Migrations Completed" fetch_and_deploy_gh_release "signoz" "SigNoz/signoz" "prebuild" "latest" "/opt/signoz" "signoz-community_linux_amd64.tar.gz" -msg_info "Setting up Signoz" +msg_info "Setting up SigNoz" mkdir -p /var/lib/signoz cat </opt/signoz/conf/systemd.env @@ -148,7 +148,7 @@ msg_ok "Setup Signoz" fetch_and_deploy_gh_release "signoz-otel-collector" "SigNoz/signoz-otel-collector" "prebuild" "latest" "/opt/signoz-otel-collector" "signoz-otel-collector_linux_amd64.tar.gz" -msg_info "Setting up Signoz OTel Collector" +msg_info "Setting up SigNoz OTel Collector" mkdir -p /var/lib/signoz-otel-collector cat </opt/signoz-otel-collector/conf/config.yaml @@ -257,6 +257,8 @@ motd_ssh customize msg_info "Cleaning up" +rm -rf ~/zookeeper.tar.gz +rm -rf ~/apache-zookeeper-3.8.4-bin $STD apt-get -y autoremove $STD apt-get -y autoclean msg_ok "Cleaned" From 084fe63b8b43f9984b412650a505035e75162801 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sun, 14 Sep 2025 14:33:29 +0200 Subject: [PATCH 0883/1733] Update SigNoz --- ct/signoz.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/signoz.sh b/ct/signoz.sh index 160ab60b8..1e25d1c43 100644 --- a/ct/signoz.sh +++ b/ct/signoz.sh @@ -55,4 +55,4 @@ description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:22300${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" From 64c2c4b175a2daae381aedc037e345378d83e744 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sun, 14 Sep 2025 14:40:21 +0200 Subject: [PATCH 0884/1733] Update SigNoz --- install/signoz-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/signoz-install.sh b/install/signoz-install.sh index d49db2033..c7baa750e 100644 --- a/install/signoz-install.sh +++ b/install/signoz-install.sh @@ -30,8 +30,8 @@ $STD apt-get install -y clickhouse-server clickhouse-client msg_ok "Setup ClickHouse" msg_info "Setting up Zookeeper" -curl -fsSL https://dlcdn.apache.org/zookeeper/zookeeper-3.8.4/apache-zookeeper-3.8.4-bin.tar.gz -o "$HOME/zookeeper.tar.gz" -tar -xzf "$HOME/zookeeper.tar.gz" +curl -fsSL "https://dlcdn.apache.org/zookeeper/zookeeper-3.8.4/apache-zookeeper-3.8.4-bin.tar.gz" -o "$HOME/zookeeper.tar.gz" +tar -xzf "$HOME/zookeeper.tar.gz" -C "$HOME" mkdir -p /opt/zookeeper mkdir -p /var/lib/zookeeper mkdir -p /var/log/zookeeper From fbb3cf46c1dc74f7e9a05fbe7627446ca3968381 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sun, 14 Sep 2025 14:51:59 +0200 Subject: [PATCH 0885/1733] Update SigNoz --- ct/signoz.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ct/signoz.sh b/ct/signoz.sh index 1e25d1c43..936ac4ccf 100644 --- a/ct/signoz.sh +++ b/ct/signoz.sh @@ -31,15 +31,21 @@ function update_script() { if check_for_gh_release "signoz" "SigNoz/signoz"; then msg_info "Stopping Services" systemctl stop signoz + systemctl stop signoz-otel-collector msg_ok "Stopped Services" fetch_and_deploy_gh_release "signoz" "SigNoz/signoz" "prebuild" "latest" "/opt/signoz" "signoz-community_linux_amd64.tar.gz" + fetch_and_deploy_gh_release "signoz-otel-collector" "SigNoz/signoz-otel-collector" "prebuild" "latest" "/opt/signoz-otel-collector" "signoz-otel-collector_linux_amd64.tar.gz" + fetch_and_deploy_gh_release "signoz-schema-migrator" "SigNoz/signoz-otel-collector" "prebuild" "latest" "/opt/signoz-schema-migrator" "signoz-schema-migrator_linux_amd64.tar.gz" msg_info "Updating ${APP}" - + cd /opt/signoz-schema-migrator/bin + $STD ./signoz-schema-migrator sync --dsn="tcp://localhost:9000?password=" --replication=true --up= + $STD ./signoz-schema-migrator async --dsn="tcp://localhost:9000?password=" --replication=true --up= msg_ok "Updated $APP" msg_info "Starting Services" + systemctl start signoz-otel-collector systemctl start signoz msg_ok "Started Services" From 15b4d1beb24a5b1f1c36d10f6443caeea1a40659 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sun, 14 Sep 2025 15:11:51 +0200 Subject: [PATCH 0886/1733] Add SigNoz json --- frontend/public/json/signoz.json | 40 ++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 frontend/public/json/signoz.json diff --git a/frontend/public/json/signoz.json b/frontend/public/json/signoz.json new file mode 100644 index 000000000..07b532b0c --- /dev/null +++ b/frontend/public/json/signoz.json @@ -0,0 +1,40 @@ +{ + "name": "SigNoz", + "slug": "signoz", + "categories": [ + 9 + ], + "date_created": "2025-09-12", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 8080, + "documentation": "https://signoz.io/docs/introduction/", + "config_path": "/opt/signoz/conf/systemd.env", + "website": "https://signoz.io/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/signoz.webp", + "description": "SigNoz is an open-source Datadog or New Relic alternative. Get APM, logs, traces, metrics, exceptions, & alerts in a single tool.", + "install_methods": [ + { + "type": "default", + "script": "ct/signoz.sh", + "resources": { + "cpu": 2, + "ram": 4096, + "hdd": 20, + "os": "Debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "The first user you register will be the admin user.", + "type": "info" + } + ] +} From 6f764f3f52f629fba43b4cc8ac9b7a23672cb89d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 15 Sep 2025 08:15:12 +0200 Subject: [PATCH 0887/1733] Cleanup --- ct/stylus.sh | 59 ---------------------------- ct/telegraf.sh | 53 ------------------------- ct/uhf.sh | 63 ------------------------------ frontend/public/json/stylus.json | 40 ------------------- frontend/public/json/telegraf.json | 40 ------------------- frontend/public/json/uhf.json | 35 ----------------- install/stylus-install.sh | 48 ----------------------- install/telegraf-install.sh | 39 ------------------ install/uhf-install.sh | 58 --------------------------- 9 files changed, 435 deletions(-) delete mode 100644 ct/stylus.sh delete mode 100644 ct/telegraf.sh delete mode 100644 ct/uhf.sh delete mode 100644 frontend/public/json/stylus.json delete mode 100644 frontend/public/json/telegraf.json delete mode 100644 frontend/public/json/uhf.json delete mode 100644 install/stylus-install.sh delete mode 100644 install/telegraf-install.sh delete mode 100644 install/uhf-install.sh diff --git a/ct/stylus.sh b/ct/stylus.sh deleted file mode 100644 index 9e8bd99b5..000000000 --- a/ct/stylus.sh +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env bash -source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: luismco -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/mmastrac/stylus - -APP="Stylus" -var_tags="${var_tags:-network}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-1024}" -var_disk="${var_disk:-2}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" -var_fuse="${var_fuse:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - - if [[ ! -d /opt/stylus ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - if check_for_gh_release "stylus" "mmastrac/stylus"; then - msg_info "Stopping $APP" - systemctl stop stylus - msg_ok "Stopped $APP" - - msg_info "Updating $APP" - fetch_and_deploy_gh_release "stylus" "mmastrac/stylus" "singlefile" "latest" "/usr/bin/" "*_linux_amd64" - - msg_ok "Updated $APP" - - msg_info "Starting $APP" - systemctl start stylus - msg_ok "Started $APP" - msg_ok "Update Successful" - else - msg_ok "No update required. Latest version already installed." - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}" diff --git a/ct/telegraf.sh b/ct/telegraf.sh deleted file mode 100644 index f48f08707..000000000 --- a/ct/telegraf.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: CrazyWolf13 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/influxdata/telegraf - -APP="telegraf" -var_tags="${var_tags:-collector;metrics}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-1024}" -var_disk="${var_disk:-4}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -f /etc/telegraf/telegraf.conf ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - msg_info "Stopping $APP" - systemctl stop telegraf - msg_ok "Stopped $APP" - - msg_info "Updating $APP" - $STD apt-get update - $STD apt-get upgrade telegraf -y - msg_ok "Updated $APP" - - msg_info "Starting $APP" - systemctl start telegraf - msg_ok "Started $APP" - msg_ok "Updated Successfully" - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Check out /etc/telegraf/telegraf.conf to configure telegraf${CL}" diff --git a/ct/uhf.sh b/ct/uhf.sh deleted file mode 100644 index 09a30202d..000000000 --- a/ct/uhf.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: zackwithak13 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://www.uhfapp.com/server - -APP="UHF" -var_tags="${var_tags:-media}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-8}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/uhf-server ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - if check_for_gh_release "uhf-server" "swapplications/uhf-server-dist"; then - msg_info "Stopping Service" - systemctl stop uhf-server - msg_ok "Stopped Service" - - msg_info "Updating APT packages" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "Package list updated" - - fetch_and_deploy_gh_release "comskip" "swapplications/comskip" "prebuild" "latest" "/opt/comskip" "comskip-x64-*.zip" - fetch_and_deploy_gh_release "uhf-server" "swapplications/uhf-server-dist" "prebuild" "latest" "/opt/uhf-server" "UHF.Server-linux-x64-*.zip" - - msg_info "Starting Service" - systemctl start uhf-server - msg_ok "Started Service" - - msg_info "Cleaning up" - $STD apt-get -y autoremove - $STD apt-get -y autoclean - msg_ok "Cleaned" - msg_ok "Updated Successfully" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:7568${CL}" diff --git a/frontend/public/json/stylus.json b/frontend/public/json/stylus.json deleted file mode 100644 index 5f8a0275a..000000000 --- a/frontend/public/json/stylus.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "Stylus", - "slug": "stylus", - "categories": [ - 4 - ], - "date_created": "2025-09-06", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 8000, - "documentation": "https://mmastrac.github.io/stylus/", - "website": "https://github.com/mmastrac/stylus", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/stylus.webp", - "config_path": "/opt/stylus/config.yaml", - "description": "Stylus (style + status) is a lightweight status page for infrastructure and networks. Configure a set of bash scripts that test the various parts of your infrastructure, set up visualizations with minimal configuration, and Stylus will generate you a dashboard for your system.", - "install_methods": [ - { - "type": "default", - "script": "ct/stylus.sh", - "resources": { - "cpu": 1, - "ram": 1024, - "hdd": 2, - "os": "debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "Configuration Path: `/opt/stylus/config.yaml`", - "type": "info" - } - ] -} diff --git a/frontend/public/json/telegraf.json b/frontend/public/json/telegraf.json deleted file mode 100644 index 775371da4..000000000 --- a/frontend/public/json/telegraf.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "Telegraf", - "slug": "telegraf", - "categories": [ - 9 - ], - "date_created": "2025-09-11", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": null, - "documentation": "https://docs.influxdata.com/telegraf/v1/", - "config_path": "/etc/telegraf/telegraf.conf", - "website": "https://github.com/influxdata/telegraf", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/influxdb.webp", - "description": "Telegraf collects and sends time series data from databases, systems, and IoT sensors. It has no external dependencies, is easy to install, and requires minimal memory.", - "install_methods": [ - { - "type": "default", - "script": "ct/telegraf.sh", - "resources": { - "cpu": 1, - "ram": 1024, - "hdd": 4, - "os": "Debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "Check out the config afterwards and adapt to your needs.", - "type": "info" - } - ] -} diff --git a/frontend/public/json/uhf.json b/frontend/public/json/uhf.json deleted file mode 100644 index d23d001a0..000000000 --- a/frontend/public/json/uhf.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "UHF Server", - "slug": "uhf", - "categories": [ - 13 - ], - "date_created": "2025-08-07", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 7568, - "documentation": "https://www.uhfapp.com/server", - "website": "https://www.uhfapp.com/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/uhf.webp", - "config_path": "/etc/uhf-server/", - "description": "UHF Server is a powerful companion app that lets you seamlessly schedule and record your favorite shows from the UHF app.", - "install_methods": [ - { - "type": "default", - "script": "ct/uhf.sh", - "resources": { - "cpu": 2, - "ram": 2048, - "hdd": 8, - "os": "Debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] -} diff --git a/install/stylus-install.sh b/install/stylus-install.sh deleted file mode 100644 index 483b055f0..000000000 --- a/install/stylus-install.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: luismco -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/mmastrac/stylus - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -fetch_and_deploy_gh_release "stylus" "mmastrac/stylus" "singlefile" "latest" "/usr/bin/" "*_linux_amd64" - -msg_info "Configuring Stylus" -$STD stylus init /opt/stylus/ -msg_ok "Configured Stylus" - -msg_info "Creating service" -cat </etc/systemd/system/stylus.service -[Unit] -Description=Stylus Service -After=network.target - -[Service] -Type=simple -ExecStart=stylus run /opt/stylus/ -Restart=on-failure -RestartSec=5 - -[Install] -WantedBy=multi-user.target -EOF - -systemctl enable -q --now stylus -msg_ok "Created service" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -$STD apt-get -y clean -msg_ok "Cleaned up" diff --git a/install/telegraf-install.sh b/install/telegraf-install.sh deleted file mode 100644 index a92ffe9b0..000000000 --- a/install/telegraf-install.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: CrazyWolf13 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/influxdata/telegraf - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Adding Telegraf key and repository" -curl --silent --location -O https://repos.influxdata.com/influxdata-archive.key -gpg --show-keys --with-fingerprint --with-colons ./influxdata-archive.key 2>&1 \ -| grep -q '^fpr:\+24C975CBA61A024EE1B631787C3D57159FC2F927:$' \ -&& cat influxdata-archive.key \ -| gpg --dearmor \ -| tee /etc/apt/keyrings/influxdata-archive.gpg > /dev/null \ -&& echo 'deb [signed-by=/etc/apt/keyrings/influxdata-archive.gpg] https://repos.influxdata.com/debian stable main' \ -| tee /etc/apt/sources.list.d/influxdata.list > /dev/null -msg_ok "Added Telegraf Repository" - -msg_info "Installing Telegraf" -$STD apt-get update -$STD apt-get install telegraf -msg_ok "Installed Telegraf" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -rm /influxdata-archive.key -msg_ok "Cleaned" diff --git a/install/uhf-install.sh b/install/uhf-install.sh deleted file mode 100644 index e414878c1..000000000 --- a/install/uhf-install.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: zackwithak13 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://www.uhfapp.com/server - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt install -y ffmpeg -msg_ok "Installed Dependencies" - -msg_info "Setting Up UHF Server Environment" -mkdir -p /etc/uhf-server -mkdir -p /var/lib/uhf-server/data -mkdir -p /var/lib/uhf-server/recordings -cat </etc/uhf-server/.env -API_HOST=0.0.0.0 -API_PORT=7568 -RECORDINGS_DIR=/var/lib/uhf-server/recordings -DB_PATH=/var/lib/uhf-server/data/db.json -LOG_LEVEL=INFO -EOF -msg_ok "Set Up UHF Server Environment" - -fetch_and_deploy_gh_release "comskip" "swapplications/comskip" "prebuild" "latest" "/opt/comskip" "comskip-x64-*.zip" -fetch_and_deploy_gh_release "uhf-server" "swapplications/uhf-server-dist" "prebuild" "latest" "/opt/uhf-server" "UHF.Server-linux-x64-*.zip" - -msg_info "Creating Service" -cat </etc/systemd/system/uhf-server.service -[Unit] -Description=UHF Server service -After=syslog.target network-online.target -[Service] -Type=simple -WorkingDirectory=/opt/uhf-server -EnvironmentFile=/etc/uhf-server/.env -ExecStart=/opt/uhf-server/uhf-server -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now uhf-server -msg_ok "Created Service" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" From 2800fe35e5285d4a99d521fdd04ec2994ec7949c Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 15 Sep 2025 06:15:46 +0000 Subject: [PATCH 0888/1733] Update .app files --- ct/headers/stylus | 6 ------ ct/headers/telegraf | 6 ------ ct/headers/uhf | 6 ------ 3 files changed, 18 deletions(-) delete mode 100644 ct/headers/stylus delete mode 100644 ct/headers/telegraf delete mode 100644 ct/headers/uhf diff --git a/ct/headers/stylus b/ct/headers/stylus deleted file mode 100644 index bb8c5765b..000000000 --- a/ct/headers/stylus +++ /dev/null @@ -1,6 +0,0 @@ - _____ __ __ - / ___// /___ __/ /_ _______ - \__ \/ __/ / / / / / / / ___/ - ___/ / /_/ /_/ / / /_/ (__ ) -/____/\__/\__, /_/\__,_/____/ - /____/ diff --git a/ct/headers/telegraf b/ct/headers/telegraf deleted file mode 100644 index 14c860970..000000000 --- a/ct/headers/telegraf +++ /dev/null @@ -1,6 +0,0 @@ - __ __ ____ - / /____ / /__ ____ __________ _/ __/ - / __/ _ \/ / _ \/ __ `/ ___/ __ `/ /_ -/ /_/ __/ / __/ /_/ / / / /_/ / __/ -\__/\___/_/\___/\__, /_/ \__,_/_/ - /____/ diff --git a/ct/headers/uhf b/ct/headers/uhf deleted file mode 100644 index ba248924a..000000000 --- a/ct/headers/uhf +++ /dev/null @@ -1,6 +0,0 @@ - __ ____ ________ - / / / / / / / ____/ - / / / / /_/ / /_ -/ /_/ / __ / __/ -\____/_/ /_/_/ - From 5a68fb7ce2945b8f2f12102d24b55a4d70552c11 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 15 Sep 2025 09:41:03 +0200 Subject: [PATCH 0889/1733] finalize --- ct/scraparr.sh | 61 ++++++++++++++++++------------------- install/scraparr-install.sh | 2 +- 2 files changed, 30 insertions(+), 33 deletions(-) diff --git a/ct/scraparr.sh b/ct/scraparr.sh index b7eeee7e5..e5ba622a3 100644 --- a/ct/scraparr.sh +++ b/ct/scraparr.sh @@ -20,40 +20,37 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources + header_info + check_container_storage + check_container_resources - if [[ ! -d /opt/scraparr/ ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - RELEASE=$(curl -fsSL https://api.github.com/repos/thecfu/scraparr/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') - if [[ ! -f "${HOME}/.scrappar" ]] || [[ "${RELEASE}" != "$(cat "${HOME}"/.scrappar)" ]]; then - msg_info "Stopping Services" - systemctl stop scraparr - msg_ok "Services Stopped" - - PYTHON_VERSION="3.12" setup_uv - fetch_and_deploy_gh_release "scrappar" "thecfu/scraparr" "tarball" "latest" "/opt/scraparr" - - msg_info "Updating ${APP} to ${RELEASE}" - cd /opt/scraparr || exit - $STD uv venv /opt/scraparr/.venv - $STD /opt/scraparr/.venv/bin/python -m ensurepip --upgrade - $STD /opt/scraparr/.venv/bin/python -m pip install --upgrade pip - $STD /opt/scraparr/.venv/bin/python -m pip install -r /opt/scraparr/src/scraparr/requirements.txt - chmod -R 755 /opt/scraparr - msg_ok "Updated ${APP} to v${RELEASE}" - - msg_info "Starting Services" - systemctl start scraparr - msg_ok "Services Started" - else - msg_ok "No update required. ${APP} is already at v${RELEASE}" - fi + if [[ ! -d /opt/scraparr/ ]]; then + msg_error "No ${APP} Installation Found!" exit + fi + if check_for_gh_release "scraparr" "thecfu/scraparr"; then + msg_info "Stopping Services" + systemctl stop scraparr + msg_ok "Services Stopped" + + PYTHON_VERSION="3.12" setup_uv + fetch_and_deploy_gh_release "scrappar" "thecfu/scraparr" "tarball" "latest" "/opt/scraparr" + + msg_info "Updating Scraparr" + cd /opt/scraparr + $STD uv venv /opt/scraparr/.venv + $STD /opt/scraparr/.venv/bin/python -m ensurepip --upgrade + $STD /opt/scraparr/.venv/bin/python -m pip install --upgrade pip + $STD /opt/scraparr/.venv/bin/python -m pip install -r /opt/scraparr/src/scraparr/requirements.txt + chmod -R 755 /opt/scraparr + msg_ok "Updated Scraparr" + + msg_info "Starting Services" + systemctl start scraparr + msg_ok "Services Started" + msg_ok "Updated Successfully" + fi + exit } start diff --git a/install/scraparr-install.sh b/install/scraparr-install.sh index 510f2248e..8c0de782c 100644 --- a/install/scraparr-install.sh +++ b/install/scraparr-install.sh @@ -13,7 +13,7 @@ setting_up_container network_check update_os -PYTHON_VERSION="3.12" setup_uv +PYTHON_VERSION="3.13" setup_uv fetch_and_deploy_gh_release "scrappar" "thecfu/scraparr" "tarball" "latest" "/opt/scraparr" msg_info "Installing Scraparr" From 047dcbe375c4a3a6f2c9ce5c0219a33be25c0363 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 15 Sep 2025 09:50:31 +0200 Subject: [PATCH 0890/1733] add json --- frontend/public/json/execute.json | 48 +++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 frontend/public/json/execute.json diff --git a/frontend/public/json/execute.json b/frontend/public/json/execute.json new file mode 100644 index 000000000..a7ceb141b --- /dev/null +++ b/frontend/public/json/execute.json @@ -0,0 +1,48 @@ +{ + "name": "Proxmox VE LXC Execute Command", + "slug": "lxc-execute", + "categories": [ + 1 + ], + "date_created": "2025-09-15", + "type": "pve", + "updateable": false, + "privileged": false, + "interface_port": null, + "documentation": null, + "website": null, + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/proxmox.webp", + "config_path": null, + "description": "This script allows administrators to execute a custom command inside one or multiple LXC containers on a Proxmox VE node. Containers can be selectively excluded via an interactive checklist. If a container is stopped, the script will automatically start it, run the command, and then shut it down again. Only Debian and Ubuntu based containers are supported.", + "install_methods": [ + { + "type": "default", + "script": "tools/pve/execute.sh", + "resources": { + "cpu": null, + "ram": null, + "hdd": null, + "os": null, + "version": null + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Execute within the Proxmox shell.", + "type": "info" + }, + { + "text": "Non-Debian/Ubuntu containers will be skipped automatically.", + "type": "info" + }, + { + "text": "Stopped containers will be started temporarily to run the command, then shut down again.", + "type": "warning" + } + ] +} From 521395e19aac9fbf4be5e0bface00b94061f6997 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 15 Sep 2025 09:52:02 +0200 Subject: [PATCH 0891/1733] Update update-apps.json --- frontend/public/json/update-apps.json | 32 ++++++++++++++++++++------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/frontend/public/json/update-apps.json b/frontend/public/json/update-apps.json index 553863671..10c37913c 100644 --- a/frontend/public/json/update-apps.json +++ b/frontend/public/json/update-apps.json @@ -1,23 +1,23 @@ { - "name": "Proxmox VE LXC-Updater", + "name": "Proxmox VE LXC Apps Update", "slug": "update-apps", "categories": [ 1 ], - "date_created": "2025-07-29", + "date_created": "2025-09-15", "type": "pve", - "updateable": false, + "updateable": true, "privileged": false, "interface_port": null, "documentation": null, "website": null, - "logo": "https://raw.githubusercontent.com/selfhst/icons/refs/heads/main/svg/proxmox.svg", - "config_path": "", - "description": "This script automatically updates LXC containers using a Systemd service. The service also updates the tags if a LXC IP address is changed.", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/proxmox.webp", + "config_path": null, + "description": "This script updates community-scripts managed LXC containers on a Proxmox VE node. It detects the installed service, verifies available update scripts, and applies updates interactively or unattended. Optionally, containers can be backed up before the update process. If additional build resources (CPU/RAM) are required, the script adjusts container resources temporarily and restores them after the update. Containers requiring a reboot will be listed at the end of the process.", "install_methods": [ { "type": "default", - "script": "tools/pve/update-apps.sh", + "script": "pve/update-apps.sh", "resources": { "cpu": null, "ram": null, @@ -33,7 +33,23 @@ }, "notes": [ { - "text": "Execute only within the Proxmox shell", + "text": "Execute within the Proxmox shell.", + "type": "info" + }, + { + "text": "Only containers with `community-script` or `proxmox-helper-scripts` tags are listed for update.", + "type": "info" + }, + { + "text": "Optionally performs a vzdump backup before updating containers.", + "type": "warning" + }, + { + "text": "If required, the script will temporarily increase container CPU/RAM resources for the build process and restore them after completion.", + "type": "info" + }, + { + "text": "At the end of the update, containers requiring a reboot will be listed, and you may choose to reboot them directly.", "type": "info" } ] From 0431ce8b1a846dc1d155c48d99b0bb681bb9888e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 15 Sep 2025 09:52:53 +0200 Subject: [PATCH 0892/1733] fix path --- frontend/public/json/execute.json | 2 +- frontend/public/json/update-apps.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/public/json/execute.json b/frontend/public/json/execute.json index a7ceb141b..7fed45ed8 100644 --- a/frontend/public/json/execute.json +++ b/frontend/public/json/execute.json @@ -12,7 +12,7 @@ "documentation": null, "website": null, "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/proxmox.webp", - "config_path": null, + "config_path": "", "description": "This script allows administrators to execute a custom command inside one or multiple LXC containers on a Proxmox VE node. Containers can be selectively excluded via an interactive checklist. If a container is stopped, the script will automatically start it, run the command, and then shut it down again. Only Debian and Ubuntu based containers are supported.", "install_methods": [ { diff --git a/frontend/public/json/update-apps.json b/frontend/public/json/update-apps.json index 10c37913c..e9504b79f 100644 --- a/frontend/public/json/update-apps.json +++ b/frontend/public/json/update-apps.json @@ -12,7 +12,7 @@ "documentation": null, "website": null, "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/proxmox.webp", - "config_path": null, + "config_path": "", "description": "This script updates community-scripts managed LXC containers on a Proxmox VE node. It detects the installed service, verifies available update scripts, and applies updates interactively or unattended. Optionally, containers can be backed up before the update process. If additional build resources (CPU/RAM) are required, the script adjusts container resources temporarily and restores them after the update. Containers requiring a reboot will be listed at the end of the process.", "install_methods": [ { From e52118539214a2f9238ae42b4968a27364bee020 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 15 Sep 2025 09:53:25 +0200 Subject: [PATCH 0893/1733] Update update-apps.json --- frontend/public/json/update-apps.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/update-apps.json b/frontend/public/json/update-apps.json index e9504b79f..4a34cc676 100644 --- a/frontend/public/json/update-apps.json +++ b/frontend/public/json/update-apps.json @@ -17,7 +17,7 @@ "install_methods": [ { "type": "default", - "script": "pve/update-apps.sh", + "script": "tools/pve/update-apps.sh", "resources": { "cpu": null, "ram": null, From 89af41b323060dd44511cee5069ddf253d7fb0ed Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 15 Sep 2025 09:56:58 +0200 Subject: [PATCH 0894/1733] ProxmoxVE > PVE --- frontend/public/json/dependency-check.json | 90 +++++++++++----------- frontend/public/json/execute.json | 2 +- frontend/public/json/update-apps.json | 2 +- 3 files changed, 47 insertions(+), 47 deletions(-) diff --git a/frontend/public/json/dependency-check.json b/frontend/public/json/dependency-check.json index 13b31fe15..cb0612bd8 100644 --- a/frontend/public/json/dependency-check.json +++ b/frontend/public/json/dependency-check.json @@ -1,48 +1,48 @@ { - "name": "Proxmox VE VM Startup Dependency Check", - "slug": "dependency-check", - "categories": [ - 1 - ], - "date_created": "2025-08-12", - "type": "pve", - "updateable": false, - "privileged": false, - "interface_port": null, - "documentation": null, - "website": null, - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/proxmox.webp", - "config_path": "/etc/default/pve-auto-hook", - "description": "This script checks for the presence of required dependencies before starting a VM or LXC container in Proxmox. It ensures that all referenced storages are available and, additionally, supports the usage of tags to check for specific dependencies. If any required dependency is missing, the VM or container will not start until the issue is resolved. This script is designed to be used as a Proxmox hookscript, which can be applied to both QEMU VMs and LXC containers.", - "install_methods": [ - { - "type": "default", - "script": "tools/pve/dependency-check.sh", - "resources": { - "cpu": null, - "ram": null, - "hdd": null, - "os": null, - "version": null - } - } - ], - "default_credentials": { - "username": null, - "password": null + "name": "PVE Startup Dependency Check", + "slug": "dependency-check", + "categories": [ + 1 + ], + "date_created": "2025-08-12", + "type": "pve", + "updateable": false, + "privileged": false, + "interface_port": null, + "documentation": null, + "website": null, + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/proxmox.webp", + "config_path": "/etc/default/pve-auto-hook", + "description": "This script checks for the presence of required dependencies before starting a VM or LXC container in Proxmox. It ensures that all referenced storages are available and, additionally, supports the usage of tags to check for specific dependencies. If any required dependency is missing, the VM or container will not start until the issue is resolved. This script is designed to be used as a Proxmox hookscript, which can be applied to both QEMU VMs and LXC containers.", + "install_methods": [ + { + "type": "default", + "script": "tools/pve/dependency-check.sh", + "resources": { + "cpu": null, + "ram": null, + "hdd": null, + "os": null, + "version": null + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Execute within the Proxmox shell", + "type": "info" }, - "notes": [ - { - "text": "Execute within the Proxmox shell", - "type": "info" - }, - { - "text": "To wait until a certain host is available, tag the VM or container with `dep_ping_` where `` is the name or IP of the host to ping. The script will wait until the host is reachable before proceeding with the startup.", - "type": "info" - }, - { - "text": "To wait until a certain TCP port is open, tag the VM or container with `dep_tcp__` where `` is the name or IP of the host and `` is the TCP port number. The script will wait until the port is open before proceeding with the startup.", - "type": "info" - } - ] + { + "text": "To wait until a certain host is available, tag the VM or container with `dep_ping_` where `` is the name or IP of the host to ping. The script will wait until the host is reachable before proceeding with the startup.", + "type": "info" + }, + { + "text": "To wait until a certain TCP port is open, tag the VM or container with `dep_tcp__` where `` is the name or IP of the host and `` is the TCP port number. The script will wait until the port is open before proceeding with the startup.", + "type": "info" + } + ] } diff --git a/frontend/public/json/execute.json b/frontend/public/json/execute.json index 7fed45ed8..9014f9c3a 100644 --- a/frontend/public/json/execute.json +++ b/frontend/public/json/execute.json @@ -1,5 +1,5 @@ { - "name": "Proxmox VE LXC Execute Command", + "name": "PVE LXC Execute Command", "slug": "lxc-execute", "categories": [ 1 diff --git a/frontend/public/json/update-apps.json b/frontend/public/json/update-apps.json index 4a34cc676..62104d038 100644 --- a/frontend/public/json/update-apps.json +++ b/frontend/public/json/update-apps.json @@ -1,5 +1,5 @@ { - "name": "Proxmox VE LXC Apps Update", + "name": "PVE LXC Apps Update", "slug": "update-apps", "categories": [ 1 From cfa3b6476475679d10bca09a5008a193d3ba211b Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 15 Sep 2025 10:28:57 +0200 Subject: [PATCH 0895/1733] Update SigNoz --- install/signoz-install.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/install/signoz-install.sh b/install/signoz-install.sh index c7baa750e..212fec386 100644 --- a/install/signoz-install.sh +++ b/install/signoz-install.sh @@ -30,12 +30,13 @@ $STD apt-get install -y clickhouse-server clickhouse-client msg_ok "Setup ClickHouse" msg_info "Setting up Zookeeper" -curl -fsSL "https://dlcdn.apache.org/zookeeper/zookeeper-3.8.4/apache-zookeeper-3.8.4-bin.tar.gz" -o "$HOME/zookeeper.tar.gz" +curl -fsSL "https://dlcdn.apache.org/zookeeper/current/$(curl -fsSL https://dlcdn.apache.org/zookeeper/current/ | grep -o 'apache-zookeeper-[0-9.]\+-bin\.tar\.gz' | head -n1)" -o ~/zookeeper.tar.gz + tar -xzf "$HOME/zookeeper.tar.gz" -C "$HOME" mkdir -p /opt/zookeeper mkdir -p /var/lib/zookeeper mkdir -p /var/log/zookeeper -cp -r ~/apache-zookeeper-3.8.4-bin/* /opt/zookeeper +cp -r ~/apache-zookeeper-*-bin/* /opt/zookeeper cat </opt/zookeeper/conf/zoo.cfg tickTime=2000 @@ -258,7 +259,7 @@ customize msg_info "Cleaning up" rm -rf ~/zookeeper.tar.gz -rm -rf ~/apache-zookeeper-3.8.4-bin +rm -rf ~/apache-zookeeper-*-bin $STD apt-get -y autoremove $STD apt-get -y autoclean msg_ok "Cleaned" From 8f7895495ceb511237c0211617c28b724b0246d5 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 15 Sep 2025 10:29:36 +0200 Subject: [PATCH 0896/1733] Update SigNoz --- install/signoz-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/install/signoz-install.sh b/install/signoz-install.sh index 212fec386..8a35766b0 100644 --- a/install/signoz-install.sh +++ b/install/signoz-install.sh @@ -31,7 +31,6 @@ msg_ok "Setup ClickHouse" msg_info "Setting up Zookeeper" curl -fsSL "https://dlcdn.apache.org/zookeeper/current/$(curl -fsSL https://dlcdn.apache.org/zookeeper/current/ | grep -o 'apache-zookeeper-[0-9.]\+-bin\.tar\.gz' | head -n1)" -o ~/zookeeper.tar.gz - tar -xzf "$HOME/zookeeper.tar.gz" -C "$HOME" mkdir -p /opt/zookeeper mkdir -p /var/lib/zookeeper From c3ab9ae8305ec8ac41ab40539c9a34133bf08c50 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 15 Sep 2025 12:08:44 +0200 Subject: [PATCH 0897/1733] Update unifi-os-vm.sh --- vm/unifi-os-vm.sh | 144 ++++++++++++++++++---------------------------- 1 file changed, 55 insertions(+), 89 deletions(-) diff --git a/vm/unifi-os-vm.sh b/vm/unifi-os-vm.sh index 41239a6b1..39dc54d73 100644 --- a/vm/unifi-os-vm.sh +++ b/vm/unifi-os-vm.sh @@ -9,11 +9,11 @@ source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-sc function header_info() { clear cat <<"EOF" - ____ __ _ ____ ___ - / __ \____ _____/ /_____ _____ | | / / |/ / - / / / / __ \/ ___/ //_/ _ \/ ___/ | | / / /|_/ / - / /_/ / /_/ / /__/ ,< / __/ / | |/ / / / / -/_____/\____/\___/_/|_|\___/_/ |___/_/ /_/ + __ __ _ _____ ____ _____ _____ + / / / /___ (_) __(_) / __ \/ ___/ / ___/___ ______ _____ _____ + / / / / __ \/ / /_/ / / / / /\__ \ \__ \/ _ \/ ___/ | / / _ \/ ___/ +/ /_/ / / / / / __/ / / /_/ /___/ / ___/ / __/ / | |/ / __/ / +\____/_/ /_/_/_/ /_/ \____//____/ /____/\___/_/ |___/\___/_/ EOF } @@ -25,8 +25,8 @@ METHOD="" NSAPP="UniFi OS Server" var_os="linux" var_version="x64" -UOS_VERSION="4.2.23" -UOS_URL="https://fw-download.ubnt.com/data/unifi-os-server/8b93-linux-x64-4.2.23-158fa00b-6b2c-4cd8-94ea-e92bc4a81369.23-x64" +UOS_VERSION="4.3.5" +UOS_URL="https://fw-download.ubnt.com/data/unifi-os-server/da70-linux-x64-4.3.5-5306ffbb-fc6d-4414-912b-29cbfb0ce85a.5-x64" UOS_INSTALLER="unifi-os-server-${UOS_VERSION}.bin" YW=$(echo "\033[33m") @@ -62,19 +62,20 @@ VLANTAG="${TAB}🏷️${TAB}${CL}" CREATING="${TAB}🚀${TAB}${CL}" ADVANCED="${TAB}🧩${TAB}${CL}" THIN="discard=on,ssd=1," -set -e + +set -Eeuo pipefail trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap cleanup EXIT trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM + function error_handler() { local exit_code="$?" local line_number="$1" local command="$2" post_update_to_api "failed" "${command}" - local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" - echo -e "\n$error_message\n" - cleanup_vmid + echo -e "\n${RD}[ERROR]${CL} line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing ${YW}$command${CL}\n" + if qm status $VMID &>/dev/null; then qm stop $VMID &>/dev/null || true; fi } function get_valid_nextid() { @@ -205,11 +206,11 @@ function exit-script() { function default_settings() { VMID=$(get_valid_nextid) FORMAT=",efitype=4m" - MACHINE="" + MACHINE="-machine q35" DISK_CACHE="" DISK_SIZE="30G" HN="unifi-server-os" - CPU_TYPE="" + CPU_TYPE="-cpu host" CORE_COUNT="2" RAM_SIZE="4096" BRG="vmbr0" @@ -435,102 +436,67 @@ start_script post_to_api_vm msg_info "Validating Storage" +STORAGE_MENU=() while read -r line; do TAG=$(echo $line | awk '{print $1}') TYPE=$(echo $line | awk '{printf "%-10s", $2}') FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') - ITEM=" Type: $TYPE Free: $FREE " - OFFSET=2 - if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then - MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) - fi - STORAGE_MENU+=("$TAG" "$ITEM" "OFF") + STORAGE_MENU+=("$TAG" "Type: $TYPE Free: $FREE" OFF) done < <(pvesm status -content images | awk 'NR>1') -VALID=$(pvesm status -content images | awk 'NR>1') -if [ -z "$VALID" ]; then - msg_error "Unable to detect a valid storage location." - exit -elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then + +if [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then STORAGE=${STORAGE_MENU[0]} else - while [ -z "${STORAGE:+x}" ]; do - STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ - "Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \ - 16 $(($MSG_MAX_LENGTH + 23)) 6 \ - "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) - done + STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" \ + --radiolist "Choose storage pool for UniFi OS VM" 16 70 6 "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) fi -msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." -msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." -msg_info "Retrieving the URL for the Debian 12 Qcow2 Disk Image" -URL="https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-nocloud-$(dpkg --print-architecture).qcow2" -sleep 2 -msg_ok "${CL}${BL}${URL}${CL}" -curl -f#SL -o "$(basename "$URL")" "$URL" -echo -en "\e[1A\e[0K" -FILE=$(basename $URL) -msg_ok "Downloaded ${CL}${BL}${FILE}${CL}" +msg_ok "Using $STORAGE" -STORAGE_TYPE=$(pvesm status -storage "$STORAGE" | awk 'NR>1 {print $2}') -case $STORAGE_TYPE in -nfs | dir | cifs) - DISK_EXT=".qcow2" - DISK_REF="$VMID/" - DISK_IMPORT="-format qcow2" - THIN="" - ;; -btrfs) - DISK_EXT=".raw" - DISK_REF="$VMID/" - DISK_IMPORT="-format raw" - FORMAT=",efitype=4m" - THIN="" - ;; -esac -for i in {0,1}; do - disk="DISK$i" - eval DISK${i}=vm-${VMID}-disk-${i}${DISK_EXT:-} - eval DISK${i}_REF=${STORAGE}:${DISK_REF:-}${!disk} -done +# --- Download Debian Cloud Image --- +msg_info "Downloading Debian 13 Cloud Image" +URL="https://cloud.debian.org/images/cloud/trixie/latest/debian-13-nocloud-amd64.qcow2" +curl -fsSL -o debian-13-nocloud-amd64.qcow2 "$URL" +FILE="debian-13-nocloud-amd64.qcow2" +msg_ok "Downloaded Debian Cloud Image" +# --- Inject UniFi Installer --- if ! command -v virt-customize &>/dev/null; then - msg_info "Installing Pre-Requisite libguestfs-tools onto Host" + msg_info "Installing libguestfs-tools on host" apt-get -qq update >/dev/null - apt-get -qq install libguestfs-tools lsb-release -y >/dev/null - msg_ok "Installed libguestfs-tools successfully" + apt-get -qq install libguestfs-tools -y >/dev/null + msg_ok "Installed libguestfs-tools" fi -msg_info "Adding UniFi OS Server Installer & root Autologin zu Debian 12 Qcow2 Disk Image" -UOS_VERSION="4.2.23" -UOS_URL="https://fw-download.ubnt.com/data/unifi-os-server/8b93-linux-x64-4.2.23-158fa00b-6b2c-4cd8-94ea-e92bc4a81369.23-x64" -UOS_INSTALLER="unifi-os-server-${UOS_VERSION}.bin" -virt-customize -q -a "${FILE}" \ +msg_info "Injecting UniFi OS Installer into Cloud Image" +virt-customize -q -a "$FILE" \ --install qemu-guest-agent,ca-certificates,curl,lsb-release,podman \ --run-command "curl -fsSL '${UOS_URL}' -o /root/${UOS_INSTALLER} && chmod +x /root/${UOS_INSTALLER}" \ --run-command 'mkdir -p /etc/systemd/system/getty@tty1.service.d' \ --run-command "bash -c 'echo -e \"[Service]\nExecStart=\nExecStart=-/sbin/agetty --autologin root --noclear %I \$TERM\" > /etc/systemd/system/getty@tty1.service.d/override.conf'" \ - --run-command 'systemctl daemon-reload' \ - --run-command 'systemctl restart getty@tty1' >/dev/null -msg_ok "Installer & root Autologin integrated in image" + >/dev/null +msg_ok "UniFi OS Installer integrated" -msg_info "Expanding root partition to use full disk space" -qemu-img create -f qcow2 expanded.qcow2 ${DISK_SIZE} >/dev/null 2>&1 -virt-resize --expand /dev/sda1 ${FILE} expanded.qcow2 >/dev/null 2>&1 -mv expanded.qcow2 ${FILE} >/dev/null 2>&1 -msg_ok "Expanded image to full size" +msg_info "Creating UniFi OS VM" +qm create "$VMID" -agent 1 $MACHINE -tablet 0 -localtime 1 -bios ovmf \ + $CPU_TYPE -cores "$CORE_COUNT" -memory "$RAM_SIZE" \ + -name "$HN" -tags community-script \ + -net0 virtio,bridge="$BRG",macaddr="$MAC""$VLAN""$MTU" \ + -onboot 1 -ostype l26 -scsihw virtio-scsi-pci -msg_info "Creating a Unifi OS VM" -qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \ - -name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci -pvesm alloc $STORAGE $VMID $DISK0 4M 1>&/dev/null -qm importdisk $VMID ${FILE} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null -qm set $VMID \ - -efidisk0 ${DISK0_REF}${FORMAT} \ - -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ - -boot order=scsi0 \ - -serial0 socket >/dev/null -qm resize $VMID scsi0 8G >/dev/null -qm set $VMID --agent enabled=1 >/dev/null +pvesm alloc "$STORAGE" "$VMID" "vm-$VMID-disk-0" 4M >/dev/null +IMPORT_OUT="$(qm importdisk "$VMID" "$FILE" "$STORAGE" --format qcow2 2>&1 || true)" +DISK_REF="$(printf '%s\n' "$IMPORT_OUT" | sed -n "s/.*successfully imported disk '\([^']\+\)'.*/\1/p")" + +if [[ -z "$DISK_REF" ]]; then + DISK_REF="$(pvesm list "$STORAGE" | awk -v id="$VMID" '$1 ~ ("vm-"id"-disk-") {print $1}' | sort | tail -n1)" +fi + +qm set "$VMID" \ + -efidisk0 "${STORAGE}:0${FORMAT},size=4M" \ + -scsi0 "${DISK_REF},size=${DISK_SIZE}" \ + -boot order=scsi0 -serial0 socket >/dev/null +qm resize "$VMID" scsi0 "$DISK_SIZE" >/dev/null +qm set "$VMID" --agent enabled=1 >/dev/null DESCRIPTION=$( cat < Date: Mon, 15 Sep 2025 12:12:36 +0200 Subject: [PATCH 0898/1733] Update unifi-os-vm.sh --- vm/unifi-os-vm.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/vm/unifi-os-vm.sh b/vm/unifi-os-vm.sh index 39dc54d73..36a82cd30 100644 --- a/vm/unifi-os-vm.sh +++ b/vm/unifi-os-vm.sh @@ -302,9 +302,9 @@ function advanced_settings() { exit-script fi - if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 docker --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 unifi-os-server --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $VM_NAME ]; then - HN="docker" + HN="unifi-os-server" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" else HN=$(echo ${VM_NAME,,} | tr -d ' ') @@ -469,6 +469,7 @@ fi msg_info "Injecting UniFi OS Installer into Cloud Image" virt-customize -q -a "$FILE" \ + --run-command "echo 'nameserver 1.1.1.1' > /etc/resolv.conf" \ --install qemu-guest-agent,ca-certificates,curl,lsb-release,podman \ --run-command "curl -fsSL '${UOS_URL}' -o /root/${UOS_INSTALLER} && chmod +x /root/${UOS_INSTALLER}" \ --run-command 'mkdir -p /etc/systemd/system/getty@tty1.service.d' \ From 820ed46e3c676eb88857f52bbcdeac705ef774cf Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 15 Sep 2025 12:18:59 +0200 Subject: [PATCH 0899/1733] Update unifi-os-vm.sh --- vm/unifi-os-vm.sh | 102 ++++++++++++++++++++++++++++------------------ 1 file changed, 63 insertions(+), 39 deletions(-) diff --git a/vm/unifi-os-vm.sh b/vm/unifi-os-vm.sh index 36a82cd30..61bef8a40 100644 --- a/vm/unifi-os-vm.sh +++ b/vm/unifi-os-vm.sh @@ -141,36 +141,37 @@ function check_root() { fi } +# This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported. +# Supported: Proxmox VE 8.0.x – 8.9.x and 9.0 (NOT 9.1+) pve_check() { local PVE_VER PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" - # Check for Proxmox VE 8.x + # Check for Proxmox VE 8.x: allow 8.0–8.9 if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then local MINOR="${BASH_REMATCH[1]}" - if ((MINOR < 1 || MINOR > 4)); then + if ((MINOR < 0 || MINOR > 9)); then msg_error "This version of Proxmox VE is not supported." - echo -e "Required: Proxmox VE version 8.1 – 8.4" + msg_error "Supported: Proxmox VE version 8.0 – 8.9" exit 1 fi return 0 fi - # Check for Proxmox VE 9.x (Beta) — require confirmation + # Check for Proxmox VE 9.x: allow ONLY 9.0 if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then - if whiptail --title "Proxmox 9.x Detected (Beta)" \ - --yesno "You are using Proxmox VE $PVE_VER, which is currently in Beta state.\n\nThis version is experimentally supported.\n\nDo you want to proceed anyway?" 12 70; then - msg_ok "Confirmed: Continuing with Proxmox VE $PVE_VER" - return 0 - else - msg_error "Aborted by user: Proxmox VE 9.x was not confirmed." + local MINOR="${BASH_REMATCH[1]}" + if ((MINOR != 0)); then + msg_error "This version of Proxmox VE is not yet supported." + msg_error "Supported: Proxmox VE version 9.0" exit 1 fi + return 0 fi # All other unsupported versions msg_error "This version of Proxmox VE is not supported." - echo -e "Supported versions: Proxmox VE 8.1 – 8.4 or 9.x (Beta, with confirmation)" + msg_error "Supported versions: Proxmox VE 8.0 – 8.x or 9.0" exit 1 } @@ -205,12 +206,11 @@ function exit-script() { function default_settings() { VMID=$(get_valid_nextid) - FORMAT=",efitype=4m" - MACHINE="-machine q35" - DISK_CACHE="" + FORMAT="" + MACHINE="q35" DISK_SIZE="30G" HN="unifi-server-os" - CPU_TYPE="-cpu host" + CPU_TYPE="" CORE_COUNT="2" RAM_SIZE="4096" BRG="vmbr0" @@ -220,9 +220,8 @@ function default_settings() { START_VM="yes" METHOD="default" echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}q35${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" @@ -255,16 +254,16 @@ function advanced_settings() { fi done - if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \ - "i440fx" "Machine i440fx" ON \ - "q35" "Machine q35" OFF \ + if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Machine Type" 10 58 2 \ + "q35" "Modern (PCIe, UEFI, default)" ON \ + "i440fx" "Legacy (older compatibility)" OFF \ 3>&1 1>&2 2>&3); then - if [ $MACH = q35 ]; then - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" + if [ "$MACH" = "q35" ]; then + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}q35${CL}" FORMAT="" MACHINE=" -machine q35" else - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" FORMAT=",efitype=4m" MACHINE="" fi @@ -288,8 +287,8 @@ function advanced_settings() { fi if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ - "0" "None (Default)" ON \ - "1" "Write Through" OFF \ + "0" "None" OFF \ + "1" "Write Through (Default)" ON \ 3>&1 1>&2 2>&3); then if [ $DISK_CACHE = "1" ]; then echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" @@ -314,17 +313,20 @@ function advanced_settings() { exit-script fi - if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ - "0" "KVM64 (Default)" ON \ - "1" "Host" OFF \ + if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose CPU Model" --cancel-button Exit-Script 10 58 2 \ + "KVM64" "Default – safe for migration/compatibility" ON \ + "Host" "Use host CPU features (faster, no migration)" OFF \ 3>&1 1>&2 2>&3); then - if [ $CPU_TYPE1 = "1" ]; then + case "$CPU_TYPE1" in + Host) echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" CPU_TYPE=" -cpu host" - else + ;; + *) echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" CPU_TYPE="" - fi + ;; + esac else exit-script fi @@ -436,26 +438,48 @@ start_script post_to_api_vm msg_info "Validating Storage" -STORAGE_MENU=() while read -r line; do TAG=$(echo $line | awk '{print $1}') TYPE=$(echo $line | awk '{printf "%-10s", $2}') FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') - STORAGE_MENU+=("$TAG" "Type: $TYPE Free: $FREE" OFF) + ITEM=" Type: $TYPE Free: $FREE " + OFFSET=2 + if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then + MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) + fi + STORAGE_MENU+=("$TAG" "$ITEM" "OFF") done < <(pvesm status -content images | awk 'NR>1') - -if [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then +VALID=$(pvesm status -content images | awk 'NR>1') +if [ -z "$VALID" ]; then + msg_error "Unable to detect a valid storage location." + exit +elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then STORAGE=${STORAGE_MENU[0]} else - STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" \ - --radiolist "Choose storage pool for UniFi OS VM" 16 70 6 "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) + while [ -z "${STORAGE:+x}" ]; do + if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi + printf "\e[?25h" + STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ + "Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \ + 16 $(($MSG_MAX_LENGTH + 23)) 6 \ + "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) + done fi -msg_ok "Using $STORAGE" +msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." +msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." # --- Download Debian Cloud Image --- msg_info "Downloading Debian 13 Cloud Image" URL="https://cloud.debian.org/images/cloud/trixie/latest/debian-13-nocloud-amd64.qcow2" -curl -fsSL -o debian-13-nocloud-amd64.qcow2 "$URL" +CACHE_DIR="/var/lib/vz/template/cache" +CACHE_FILE="$CACHE_DIR/$(basename "$URL")" +FILE_IMG="/var/lib/vz/template/tmp/${CACHE_FILE##*/%.xz}" # .qcow2 +if [[ ! -s "$CACHE_FILE" ]]; then + curl -f#SL -o "$CACHE_FILE" "$URL" + msg_ok "Downloaded ${CL}${BL}$(basename "$CACHE_FILE")${CL}" +else + msg_ok "Using cached image ${CL}${BL}$(basename "$CACHE_FILE")${CL}" +fi FILE="debian-13-nocloud-amd64.qcow2" msg_ok "Downloaded Debian Cloud Image" From cbce010fcf0de5aef984eb3a5c9b56af25016bd6 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 15 Sep 2025 10:23:00 +0000 Subject: [PATCH 0900/1733] Update .app files --- ct/headers/globaleaks | 6 ++++++ ct/headers/livebook | 6 ++++++ 2 files changed, 12 insertions(+) create mode 100644 ct/headers/globaleaks create mode 100644 ct/headers/livebook diff --git a/ct/headers/globaleaks b/ct/headers/globaleaks new file mode 100644 index 000000000..b54d1871c --- /dev/null +++ b/ct/headers/globaleaks @@ -0,0 +1,6 @@ + ________ __ __ __ + / ____/ /___ / /_ ____ _/ / ___ ____ _/ /_______ + / / __/ / __ \/ __ \/ __ `/ / / _ \/ __ `/ //_/ ___/ +/ /_/ / / /_/ / /_/ / /_/ / /___/ __/ /_/ / ,< (__ ) +\____/_/\____/_.___/\__,_/_____/\___/\__,_/_/|_/____/ + diff --git a/ct/headers/livebook b/ct/headers/livebook new file mode 100644 index 000000000..6ff6b47ef --- /dev/null +++ b/ct/headers/livebook @@ -0,0 +1,6 @@ + __ _ __ __ + / / (_) _____ / /_ ____ ____ / /__ + / / / / | / / _ \/ __ \/ __ \/ __ \/ //_/ + / /___/ /| |/ / __/ /_/ / /_/ / /_/ / ,< +/_____/_/ |___/\___/_.___/\____/\____/_/|_| + From e22948fe0370a3986317d2185f1c41ec126cd88d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 15 Sep 2025 12:30:49 +0200 Subject: [PATCH 0901/1733] fixes --- ct/globaleaks.sh | 23 +++++++++++------------ install/globaleaks-install.sh | 4 ++-- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/ct/globaleaks.sh b/ct/globaleaks.sh index d530b37e8..f6c7a43be 100644 --- a/ct/globaleaks.sh +++ b/ct/globaleaks.sh @@ -19,19 +19,18 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -f /usr/sbin/globaleaks ]]; then - msg_error "No ${APP} installation found!" - exit - fi + header_info + check_container_storage + check_container_resources + if [[ ! -f /usr/sbin/globaleaks ]]; then + msg_error "No ${APP} installation found!" + exit + fi - msg_info "Updating $APP LXC" - $STD apt-get update - $STD apt-get -y upgrade - - msg_ok "Updated $APP LXC" + msg_info "Updating $APP LXC" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Updated $APP LXC" } start diff --git a/install/globaleaks-install.sh b/install/globaleaks-install.sh index 3e9b91c3d..dd72c3282 100644 --- a/install/globaleaks-install.sh +++ b/install/globaleaks-install.sh @@ -13,8 +13,8 @@ network_check update_os msg_info "Setup GlobaLeaks" -curl -sS https://deb.globaleaks.org/globaleaks.asc | gpg --dearmor -o /etc/apt/trusted.gpg.d/globaleaks.gpg -echo "deb [signed-by=/etc/apt/trusted.gpg.d/globaleaks.gpg] http://deb.globaleaks.org bookworm main" >/etc/apt/sources.list.d/globaleaks.list +curl -fsSL https://deb.globaleaks.org/globaleaks.asc | gpg --dearmor >/etc/apt/trusted.gpg.d/globaleaks.asc +echo "deb [signed-by=/etc/apt/trusted.gpg.d/globaleaks.asc] http://deb.globaleaks.org bookworm main" >/etc/apt/sources.list.d/globaleaks.list $STD apt-get update $STD apt-get -y install globaleaks msg_ok "Setup GlobaLeaks" From 87ac17b0437ca00c55664dc43a34631306b1d950 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 15 Sep 2025 12:32:05 +0200 Subject: [PATCH 0902/1733] Update unifi-os-vm.sh --- vm/unifi-os-vm.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/unifi-os-vm.sh b/vm/unifi-os-vm.sh index 61bef8a40..576cb2376 100644 --- a/vm/unifi-os-vm.sh +++ b/vm/unifi-os-vm.sh @@ -457,7 +457,7 @@ elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then STORAGE=${STORAGE_MENU[0]} else while [ -z "${STORAGE:+x}" ]; do - if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi + #if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi printf "\e[?25h" STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ "Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \ From 5e3dcbea52081873293dc3fbb985f5e073adce6b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 15 Sep 2025 12:52:18 +0200 Subject: [PATCH 0903/1733] Update debian-13-vm.sh --- vm/debian-13-vm.sh | 159 +++++++++++++++++++++++---------------------- 1 file changed, 81 insertions(+), 78 deletions(-) diff --git a/vm/debian-13-vm.sh b/vm/debian-13-vm.sh index 05aa50bcd..6169ba28e 100644 --- a/vm/debian-13-vm.sh +++ b/vm/debian-13-vm.sh @@ -203,25 +203,22 @@ function exit-script() { function default_settings() { VMID=$(get_valid_nextid) - FORMAT=",efitype=4m" - MACHINE="" - DISK_SIZE="8G" - DISK_CACHE="" + FORMAT="" + MACHINE="q35" + DISK_SIZE="30G" HN="debian" CPU_TYPE="" CORE_COUNT="2" - RAM_SIZE="2048" + RAM_SIZE="4096" BRG="vmbr0" MAC="$GEN_MAC" VLAN="" MTU="" START_VM="yes" - CLOUD_INIT="no" METHOD="default" echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}q35${CL}" echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" @@ -230,7 +227,6 @@ function default_settings() { echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" - echo -e "${CLOUD}${BOLD}${DGN}Configure Cloud-init: ${BGN}no${CL}" echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" echo -e "${CREATING}${BOLD}${DGN}Creating a Debian 13 VM using the above default settings${CL}" } @@ -255,16 +251,16 @@ function advanced_settings() { fi done - if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \ - "i440fx" "Machine i440fx" ON \ - "q35" "Machine q35" OFF \ + if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Machine Type" 10 58 2 \ + "q35" "Modern (PCIe, UEFI, default)" ON \ + "i440fx" "Legacy (older compatibility)" OFF \ 3>&1 1>&2 2>&3); then - if [ $MACH = q35 ]; then - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" + if [ "$MACH" = "q35" ]; then + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}q35${CL}" FORMAT="" MACHINE=" -machine q35" else - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}$MACH${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx${CL}" FORMAT=",efitype=4m" MACHINE="" fi @@ -288,8 +284,8 @@ function advanced_settings() { fi if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ - "0" "None (Default)" ON \ - "1" "Write Through" OFF \ + "0" "None" OFF \ + "1" "Write Through (Default)" ON \ 3>&1 1>&2 2>&3); then if [ $DISK_CACHE = "1" ]; then echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" @@ -302,9 +298,9 @@ function advanced_settings() { exit-script fi - if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 debian --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 unifi-os-server --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $VM_NAME ]; then - HN="debian" + HN="unifi-os-server" echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" else HN=$(echo ${VM_NAME,,} | tr -d ' ') @@ -314,17 +310,20 @@ function advanced_settings() { exit-script fi - if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ - "0" "KVM64 (Default)" ON \ - "1" "Host" OFF \ + if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose CPU Model" --cancel-button Exit-Script 10 58 2 \ + "KVM64" "Default – safe for migration/compatibility" ON \ + "Host" "Use host CPU features (faster, no migration)" OFF \ 3>&1 1>&2 2>&3); then - if [ $CPU_TYPE1 = "1" ]; then + case "$CPU_TYPE1" in + Host) echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" CPU_TYPE=" -cpu host" - else + ;; + *) echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" CPU_TYPE="" - fi + ;; + esac else exit-script fi @@ -436,13 +435,11 @@ function start_script() { advanced_settings fi } - check_root arch_check pve_check ssh_check start_script - post_to_api_vm msg_info "Validating Storage" @@ -465,6 +462,8 @@ elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then STORAGE=${STORAGE_MENU[0]} else while [ -z "${STORAGE:+x}" ]; do + if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi + printf "\e[?25h" STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ "Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \ 16 $(($MSG_MAX_LENGTH + 23)) 6 \ @@ -473,60 +472,71 @@ else fi msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." + msg_info "Retrieving the URL for the Debian 13 Qcow2 Disk Image" if [ "$CLOUD_INIT" == "yes" ]; then URL=https://cloud.debian.org/images/cloud/trixie/latest/debian-13-genericcloud-amd64.qcow2 else URL=https://cloud.debian.org/images/cloud/trixie/latest/debian-13-nocloud-amd64.qcow2 fi -sleep 2 +CACHE_DIR="/var/lib/vz/template/cache" +CACHE_FILE="$CACHE_DIR/$(basename "$URL")" +FILE_IMG="/var/lib/vz/template/tmp/${CACHE_FILE##*/%.xz}" # .qcow2 +mkdir -p "$CACHE_DIR" "$(dirname "$FILE_IMG")" msg_ok "${CL}${BL}${URL}${CL}" -curl -f#SL -o "$(basename "$URL")" "$URL" -echo -en "\e[1A\e[0K" -FILE=$(basename $URL) -msg_ok "Downloaded ${CL}${BL}${FILE}${CL}" -STORAGE_TYPE=$(pvesm status -storage $STORAGE | awk 'NR>1 {print $2}') -case $STORAGE_TYPE in -nfs | dir) - DISK_EXT=".qcow2" - DISK_REF="$VMID/" - DISK_IMPORT="-format qcow2" - THIN="" - ;; -btrfs) - DISK_EXT=".raw" - DISK_REF="$VMID/" - DISK_IMPORT="-format raw" - FORMAT=",efitype=4m" - THIN="" - ;; -esac -for i in {0,1}; do - disk="DISK$i" - eval DISK${i}=vm-${VMID}-disk-${i}${DISK_EXT:-} - eval DISK${i}_REF=${STORAGE}:${DISK_REF:-}${!disk} -done - -msg_info "Creating a Debian 13 VM" -qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \ - -name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci -pvesm alloc $STORAGE $VMID $DISK0 4M 1>&/dev/null -qm importdisk $VMID ${FILE} $STORAGE ${DISK_IMPORT:-} 1>&/dev/null -if [ "$CLOUD_INIT" == "yes" ]; then - qm set $VMID \ - -efidisk0 ${DISK0_REF}${FORMAT} \ - -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ - -scsi1 ${STORAGE}:cloudinit \ - -boot order=scsi0 \ - -serial0 socket >/dev/null +if [[ ! -s "$CACHE_FILE" ]]; then + curl -f#SL -o "$CACHE_FILE" "$URL" + msg_ok "Downloaded ${CL}${BL}$(basename "$CACHE_FILE")${CL}" else - qm set $VMID \ - -efidisk0 ${DISK0_REF}${FORMAT} \ - -scsi0 ${DISK1_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE} \ - -boot order=scsi0 \ - -serial0 socket >/dev/null + msg_ok "Using cached image ${CL}${BL}$(basename "$CACHE_FILE")${CL}" fi + +set -o pipefail +msg_info "Creating Debian 13 VM shell" +qm create "$VMID" -machine q35 -bios ovmf -agent 1 -tablet 0 -localtime 1 ${CPU_TYPE} \ + -cores "$CORE_COUNT" -memory "$RAM_SIZE" -name "$HN" -tags community-script \ + -net0 "virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU" -onboot 1 -ostype l26 -scsihw virtio-scsi-pci >/dev/null +msg_ok "Created VM shell" + +msg_info "Importing disk into storage ($STORAGE)" +if qm disk import --help >/dev/null 2>&1; then + IMPORT_CMD=(qm disk import) +else + IMPORT_CMD=(qm importdisk) +fi +IMPORT_OUT="$("${IMPORT_CMD[@]}" "$VMID" "$CACHE_FILE" "$STORAGE" --format raw 2>&1 || true)" +DISK_REF="$(printf '%s\n' "$IMPORT_OUT" | sed -n "s/.*successfully imported disk '\([^']\+\)'.*/\1/p" | tr -d "\r\"'")" +[[ -z "$DISK_REF" ]] && DISK_REF="$(pvesm list "$STORAGE" | awk -v id="$VMID" '$5 ~ ("vm-"id"-disk-") {print $1":"$5}' | sort | tail -n1)" +[[ -z "$DISK_REF" ]] && { + msg_error "Unable to determine imported disk reference." + echo "$IMPORT_OUT" + exit 1 +} +msg_ok "Imported disk (${CL}${BL}${DISK_REF}${CL})" + +msg_info "Attaching EFI and root disk" +if [ "$CLOUD_INIT" == "yes" ]; then + qm set "$VMID" \ + --efidisk0 "${STORAGE}:0,efitype=4m" \ + --scsi0 "${DISK_REF},ssd=1,discard=on" \ + --scsi1 "${STORAGE}:cloudinit" \ + --boot order=scsi0 \ + --serial0 socket >/dev/null +else + qm set "$VMID" \ + --efidisk0 "${STORAGE}:0,efitype=4m" \ + --scsi0 "${DISK_REF},ssd=1,discard=on" \ + --boot order=scsi0 \ + --serial0 socket >/dev/null +fi +qm set "$VMID" --agent enabled=1 >/dev/null +msg_ok "Attached EFI and root disk" + +msg_info "Resizing disk to ${DISK_SIZE}" +qm resize "$VMID" scsi0 "${DISK_SIZE}" >/dev/null +msg_ok "Resized disk" + DESCRIPTION=$( cat < @@ -558,13 +568,6 @@ DESCRIPTION=$( EOF ) qm set "$VMID" -description "$DESCRIPTION" >/dev/null -if [ -n "$DISK_SIZE" ]; then - msg_info "Resizing disk to $DISK_SIZE GB" - qm resize $VMID scsi0 ${DISK_SIZE} >/dev/null -else - msg_info "Using default disk size of $DEFAULT_DISK_SIZE GB" - qm resize $VMID scsi0 ${DEFAULT_DISK_SIZE} >/dev/null -fi msg_ok "Created a Debian 13 VM ${CL}${BL}(${HN})" if [ "$START_VM" == "yes" ]; then From 569384b5d01a5f2aafe60e1300fcfbfa0f7131e4 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Mon, 15 Sep 2025 13:15:37 +0200 Subject: [PATCH 0904/1733] Update logo URL in globaleaks.json --- frontend/public/json/globaleaks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/globaleaks.json b/frontend/public/json/globaleaks.json index 3420ebab0..f21437376 100644 --- a/frontend/public/json/globaleaks.json +++ b/frontend/public/json/globaleaks.json @@ -11,7 +11,7 @@ "interface_port": 443, "documentation": "https://docs.globaleaks.org", "website": "https://www.globaleaks.org/", - "logo": "https://raw.githubusercontent.com/globaleaks/globaleaks-whistleblowing-software/stable/brand/assets/webp/globaleaks-icon-color.webp", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/globaleaks.webp", "config_path": "", "description": "GlobaLeaks is a free and open-source whistleblowing software enabling anyone to easily set up and maintain a secure reporting platform.", "install_methods": [ From a7814274514e5621d3280c937d8976768504f12c Mon Sep 17 00:00:00 2001 From: cobaltgit Date: Mon, 15 Sep 2025 13:05:14 +0100 Subject: [PATCH 0905/1733] ct: add Alpine Linux Caddy --- ct/alpine-caddy.sh | 47 ++++++++++++++++++++ ct/headers/alpine-caddy | 6 +++ frontend/public/json/alpine-caddy.json | 59 ++++++++++++++++++++++++++ install/alpine-caddy-install.sh | 43 +++++++++++++++++++ 4 files changed, 155 insertions(+) create mode 100644 ct/alpine-caddy.sh create mode 100644 ct/headers/alpine-caddy create mode 100644 frontend/public/json/alpine-caddy.json create mode 100644 install/alpine-caddy-install.sh diff --git a/ct/alpine-caddy.sh b/ct/alpine-caddy.sh new file mode 100644 index 000000000..55c73eab6 --- /dev/null +++ b/ct/alpine-caddy.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 cobalt +# Author: cobalt (cobaltgit) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://caddyserver.com/ + +APP="Alpine-Caddy" +var_tags="${var_tags:-webserver}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-256}" +var_disk="${var_disk:-3}" +var_os="${var_os:-alpine}" +var_version="${var_version:-3.22}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /etc/caddy ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating $APP LXC" + $STD apk -U upgrade + msg_ok "Updated $APP LXC" + + msg_info "Restarting Caddy" + rc-service caddy restart + msg_ok "Restarted Caddy" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:80${CL}" diff --git a/ct/headers/alpine-caddy b/ct/headers/alpine-caddy new file mode 100644 index 000000000..75208a457 --- /dev/null +++ b/ct/headers/alpine-caddy @@ -0,0 +1,6 @@ + ___ __ _ ______ __ __ + / | / /___ (_)___ ___ / ____/___ _____/ /___/ /_ __ + / /| | / / __ \/ / __ \/ _ \______/ / / __ `/ __ / __ / / / / + / ___ |/ / /_/ / / / / / __/_____/ /___/ /_/ / /_/ / /_/ / /_/ / +/_/ |_/_/ .___/_/_/ /_/\___/ \____/\__,_/\__,_/\__,_/\__, / + /_/ /____/ diff --git a/frontend/public/json/alpine-caddy.json b/frontend/public/json/alpine-caddy.json new file mode 100644 index 000000000..1ce595ade --- /dev/null +++ b/frontend/public/json/alpine-caddy.json @@ -0,0 +1,59 @@ +{ + "name": "Caddy", + "slug": "caddy", + "categories": [ + 21 + ], + "date_created": "2024-05-11", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 80, + "documentation": "https://caddyserver.com/docs/", + "website": "https://caddyserver.com/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/caddy.webp", + "config_path": "/etc/caddy/Caddyfile", + "description": "Caddy is a powerful, extensible platform to serve your sites, services, and apps, written in Go.", + "install_methods": [ + { + "type": "default", + "script": "ct/caddy.sh", + "resources": { + "cpu": 1, + "ram": 512, + "hdd": 4, + "os": "debian", + "version": "12" + }, + { + "type": "alpine", + "script": "ct/alpine-caddy.sh", + "resources": { + "cpu": 1, + "ram": 256, + "hdd": 3, + "os": "alpine", + "version": "3.22" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "xcaddy needs to be updated manually after a caddy update!", + "type": "warning" + }, + { + "text": "if you need an internal module run: `caddy add-package PACKAGENAME`", + "type": "info" + }, + { + "text": "if you need an external module run: `xcaddy build --with github.com/caddy-dns/cloudflare`", + "type": "info" + } + ] +} + diff --git a/install/alpine-caddy-install.sh b/install/alpine-caddy-install.sh new file mode 100644 index 000000000..8f2541505 --- /dev/null +++ b/install/alpine-caddy-install.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 cobalt +# Author: cobalt (cobaltgit) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://caddyserver.com/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Caddy" +$STD apk add --no-cache caddy caddy-openrc +msg_ok "Installed Caddy" + +read -r -p "${TAB3}Would you like to install xCaddy Addon? " prompt +if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then + setup_go + msg_info "Setup xCaddy" + $STD apk add --no-cache git + cd /opt + RELEASE=$(curl -fsSL https://api.github.com/repos/caddyserver/xcaddy/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + curl -fsSL "https://github.com/caddyserver/xcaddy/releases/download/${RELEASE}/xcaddy_${RELEASE:1}_linux_amd64.tar.gz" -o "xcaddy_${RELEASE:1}_linux_amd64.tar.gz" + $STD tar xzf xcaddy_"${RELEASE:1}"_linux_amd64.tar.gz -C /usr/local/bin + rm -rf /opt/xcaddy* + $STD xcaddy build + msg_ok "Setup xCaddy" +fi + +msg_info "Enabling Caddy Service" +$STD rc-update add caddy default +msg_ok "Enabled Caddy Service" + +msg_info "Starting Caddy" +$STD service caddy start +msg_ok "Started Caddy" + +motd_ssh +customize From e5819be4ec2807c27c266bb59c926cf19e7c7474 Mon Sep 17 00:00:00 2001 From: cobaltgit Date: Mon, 15 Sep 2025 13:07:14 +0100 Subject: [PATCH 0906/1733] frontend: fix alpine caddy json --- frontend/public/json/alpine-caddy.json | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/frontend/public/json/alpine-caddy.json b/frontend/public/json/alpine-caddy.json index 1ce595ade..e8d8c5feb 100644 --- a/frontend/public/json/alpine-caddy.json +++ b/frontend/public/json/alpine-caddy.json @@ -15,15 +15,16 @@ "config_path": "/etc/caddy/Caddyfile", "description": "Caddy is a powerful, extensible platform to serve your sites, services, and apps, written in Go.", "install_methods": [ - { - "type": "default", - "script": "ct/caddy.sh", - "resources": { - "cpu": 1, - "ram": 512, - "hdd": 4, - "os": "debian", - "version": "12" + { + "type": "default", + "script": "ct/caddy.sh", + "resources": { + "cpu": 1, + "ram": 512, + "hdd": 4, + "os": "debian", + "version": "12" + } }, { "type": "alpine", From cfe24835d90ce557e1b8f72f6a376054f3840dfd Mon Sep 17 00:00:00 2001 From: cobaltgit Date: Mon, 15 Sep 2025 13:11:55 +0100 Subject: [PATCH 0907/1733] ct: alpine caddy: source development build.func --- ct/alpine-caddy.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/alpine-caddy.sh b/ct/alpine-caddy.sh index 55c73eab6..1b629fa90 100644 --- a/ct/alpine-caddy.sh +++ b/ct/alpine-caddy.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 cobalt # Author: cobalt (cobaltgit) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE From 383502f5e296f0d4b1032f95c655ad58c56a40c5 Mon Sep 17 00:00:00 2001 From: cobaltgit Date: Mon, 15 Sep 2025 13:24:48 +0100 Subject: [PATCH 0908/1733] alpine-caddy: change copyright to community-scripts ORG --- ct/alpine-caddy.sh | 2 +- install/alpine-caddy-install.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/alpine-caddy.sh b/ct/alpine-caddy.sh index 1b629fa90..29e61b2ef 100644 --- a/ct/alpine-caddy.sh +++ b/ct/alpine-caddy.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 cobalt +# Copyright (c) 2021-2025 community-scripts ORG # Author: cobalt (cobaltgit) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://caddyserver.com/ diff --git a/install/alpine-caddy-install.sh b/install/alpine-caddy-install.sh index 8f2541505..929a7d592 100644 --- a/install/alpine-caddy-install.sh +++ b/install/alpine-caddy-install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 cobalt +# Copyright (c) 2021-2025 community-scripts ORG # Author: cobalt (cobaltgit) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://caddyserver.com/ From bd1dbc64b1de552a145879825fb5b257533202a5 Mon Sep 17 00:00:00 2001 From: cobaltgit Date: Mon, 15 Sep 2025 13:34:12 +0100 Subject: [PATCH 0909/1733] alpine-caddy: use apk for xcaddy --- install/alpine-caddy-install.sh | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/install/alpine-caddy-install.sh b/install/alpine-caddy-install.sh index 929a7d592..5774087c8 100644 --- a/install/alpine-caddy-install.sh +++ b/install/alpine-caddy-install.sh @@ -19,14 +19,8 @@ msg_ok "Installed Caddy" read -r -p "${TAB3}Would you like to install xCaddy Addon? " prompt if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then - setup_go msg_info "Setup xCaddy" - $STD apk add --no-cache git - cd /opt - RELEASE=$(curl -fsSL https://api.github.com/repos/caddyserver/xcaddy/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - curl -fsSL "https://github.com/caddyserver/xcaddy/releases/download/${RELEASE}/xcaddy_${RELEASE:1}_linux_amd64.tar.gz" -o "xcaddy_${RELEASE:1}_linux_amd64.tar.gz" - $STD tar xzf xcaddy_"${RELEASE:1}"_linux_amd64.tar.gz -C /usr/local/bin - rm -rf /opt/xcaddy* + $STD apk add --no-cache xcaddy $STD xcaddy build msg_ok "Setup xCaddy" fi From 837108265a668a73536dd6f891cf6477f2257e61 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 15 Sep 2025 15:10:00 +0200 Subject: [PATCH 0910/1733] error_handler --- misc/alpine-install.func | 42 +++++++++++++++++++++++-------- misc/build.func | 53 +++++++++++++++++++++++----------------- misc/create_lxc.sh | 28 +++++++++++++-------- vm/debian-13-vm.sh | 24 ++++++++++++++++++ 4 files changed, 104 insertions(+), 43 deletions(-) diff --git a/misc/alpine-install.func b/misc/alpine-install.func index 906d5b14b..6feacd324 100644 --- a/misc/alpine-install.func +++ b/misc/alpine-install.func @@ -21,19 +21,41 @@ verb_ip6() { fi } -# This function catches errors and handles them with the error handler function -catch_errors() { - set -Eeuo pipefail - trap 'error_handler $LINENO "$BASH_COMMAND"' ERR +set -Eeuo pipefail +trap 'error_handler $? $LINENO "$BASH_COMMAND"' ERR +trap on_exit EXIT +trap on_interrupt INT +trap on_terminate TERM + +error_handler() { + local exit_code="$1" + local line_number="$2" + local command="$3" + + # Exitcode 0 = kein Fehler → ignorieren + if [[ "$exit_code" -eq 0 ]]; then + return 0 + fi + + printf "\e[?25h" + echo -e "\n${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}\n" + exit "$exit_code" } -# This function handles errors -error_handler() { +on_exit() { local exit_code="$?" - local line_number="$1" - local command="$2" - local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" - echo -e "\n$error_message\n" + [[ -n "${lockfile:-}" && -e "$lockfile" ]] && rm -f "$lockfile" + exit "$exit_code" +} + +on_interrupt() { + echo -e "\n${RD}Interrupted by user (SIGINT)${CL}" + exit 130 +} + +on_terminate() { + echo -e "\n${RD}Terminated by signal (SIGTERM)${CL}" + exit 143 } # This function sets up the Container OS by generating the locale, setting the timezone, and checking the network connection diff --git a/misc/build.func b/misc/build.func index 621a2d378..f401d566a 100644 --- a/misc/build.func +++ b/misc/build.func @@ -28,34 +28,41 @@ elif command -v wget >/dev/null 2>&1; then #echo "(build.func) Loaded core.func via wget" fi -# This function enables error handling in the script by setting options and defining a trap for the ERR signal. -catch_errors() { - set -Eeo pipefail - # if [ -n "$BASH_VERSION" ] && command -v shopt >/dev/null 2>&1; then - # shopt -s errtrace - # fi - trap 'error_handler $LINENO "$BASH_COMMAND"' ERR +set -Eeuo pipefail +trap 'error_handler $? $LINENO "$BASH_COMMAND"' ERR +trap on_exit EXIT +trap on_interrupt INT +trap on_terminate TERM + +error_handler() { + local exit_code="$1" + local line_number="$2" + local command="$3" + + # Exitcode 0 = kein Fehler → ignorieren + if [[ "$exit_code" -eq 0 ]]; then + return 0 + fi + + printf "\e[?25h" + echo -e "\n${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}\n" + exit "$exit_code" } -# This function is called when an error occurs. It receives the exit code, line number, and command that caused the error, and displays an error message. -error_handler() { +on_exit() { local exit_code="$?" - local line_number="$1" - local command="$2" - printf "\e[?25h" - local error_message="[ERROR] in line $line_number: exit code $exit_code: while executing command $command" - post_update_to_api "failed" "$command" - echo -e "\n$error_message\n" + [[ -n "${lockfile:-}" && -e "$lockfile" ]] && rm -f "$lockfile" exit "$exit_code" +} - if [[ -n "$CT_ID" ]]; then - read -p "Remove this Container? " prompt - if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then - pct stop "$CT_ID" &>/dev/null - pct destroy "$CT_ID" &>/dev/null - msg_ok "Removed this Container" - fi - fi +on_interrupt() { + echo -e "\n${RD}Interrupted by user (SIGINT)${CL}" + exit 130 +} + +on_terminate() { + echo -e "\n${RD}Terminated by signal (SIGTERM)${CL}" + exit 143 } # Check if the shell is using bash diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index 4426ff07e..1c4fb07f4 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -25,34 +25,42 @@ fi # Strict error handling # ------------------------------------------------------------------------------ set -Eeuo pipefail -trap 'error_handler $LINENO "$BASH_COMMAND"' ERR +trap 'error_handler $? $LINENO "$BASH_COMMAND"' ERR trap on_exit EXIT trap on_interrupt INT trap on_terminate TERM +error_handler() { + local exit_code="$1" + local line_number="$2" + local command="$3" + + # Exitcode 0 = kein Fehler → ignorieren + if [[ "$exit_code" -eq 0 ]]; then + return 0 + fi + + printf "\e[?25h" + echo -e "\n${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}\n" + exit "$exit_code" +} + on_exit() { local exit_code="$?" [[ -n "${lockfile:-}" && -e "$lockfile" ]] && rm -f "$lockfile" exit "$exit_code" } -error_handler() { - local exit_code="$?" - local line_number="$1" - local command="$2" - printf "\e[?25h" - echo -e "\n${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}\n" - exit "$exit_code" -} - on_interrupt() { echo -e "\n${RD}Interrupted by user (SIGINT)${CL}" exit 130 } + on_terminate() { echo -e "\n${RD}Terminated by signal (SIGTERM)${CL}" exit 143 } + exit_script() { clear printf "\e[?25h" diff --git a/vm/debian-13-vm.sh b/vm/debian-13-vm.sh index 6169ba28e..e1efe022a 100644 --- a/vm/debian-13-vm.sh +++ b/vm/debian-13-vm.sh @@ -576,5 +576,29 @@ if [ "$START_VM" == "yes" ]; then msg_ok "Started Debian 13 VM" fi +msg_info "Installing resize tools in VM" +qm set "$VMID" --sshkeys ~/.ssh/id_rsa.pub >/dev/null 2>&1 || true +qm exec "$VMID" -- bash -c "apt-get update && apt-get install -y cloud-guest-utils e2fsprogs xfsprogs" +msg_ok "Installed resize tools in VM" + +msg_info "Injecting auto-resize script into VM" +cat <<'EOF' >/var/lib/vz/snippets/resize-root.sh +#!/bin/bash +set -e +DEVICE=$(lsblk -no pkname $(findmnt -n -o SOURCE /)) +PART=$(findmnt -n -o SOURCE / | sed 's|/dev/||') +growpart /dev/${DEVICE} ${PART##*[a-z]} +FSTYPE=$(findmnt -n -o FSTYPE /) +if [ "$FSTYPE" = "ext4" ]; then + resize2fs /dev/$PART +elif [ "$FSTYPE" = "xfs" ]; then + xfs_growfs / +fi +EOF +chmod +x /var/lib/vz/snippets/resize-root.sh + +qm set "$VMID" --hookscript local:snippets/resize-root.sh >/dev/null +msg_ok "Injected auto-resize script" + msg_ok "Completed Successfully!\n" echo "More Info at https://github.com/community-scripts/ProxmoxVE/discussions/836" From 0f7a8351ddc8bc136dc6a994db52758e1203bf63 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 15 Sep 2025 15:12:43 +0200 Subject: [PATCH 0911/1733] Update create_lxc.sh --- misc/create_lxc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index 1c4fb07f4..b679ce133 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -117,7 +117,7 @@ offer_lxc_stack_upgrade_and_maybe_retry() { case "${_ans,,}" in y | yes) msg_info "Upgrading Proxmox LXC stack (pve-container, lxc-pve)" - if apt-get update -qq && apt-get install -y --only-upgrade pve-container lxc-pve; then + if apt-get update -qq >/dev/null && apt-get install -y --only-upgrade pve-container lxc-pve >/dev/null; then msg_ok "LXC stack upgraded." if [[ "$do_retry" == "yes" ]]; then msg_info "Retrying container creation after upgrade" From 861c5070a7107a7a9654cad6c23b3d00047b5ce2 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 15 Sep 2025 15:17:17 +0200 Subject: [PATCH 0912/1733] fixes --- misc/build.func | 5 ++--- misc/create_lxc.sh | 5 ++--- misc/install.func | 20 +++++++++----------- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/misc/build.func b/misc/build.func index f401d566a..1142ef9e2 100644 --- a/misc/build.func +++ b/misc/build.func @@ -37,15 +37,14 @@ trap on_terminate TERM error_handler() { local exit_code="$1" local line_number="$2" - local command="$3" + local command="${3:-}" - # Exitcode 0 = kein Fehler → ignorieren if [[ "$exit_code" -eq 0 ]]; then return 0 fi printf "\e[?25h" - echo -e "\n${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}\n" + echo -e "\n${RD}[ERROR]${CL} in line ${RD}${line_number}${CL}: exit code ${RD}${exit_code}${CL}: while executing command ${YW}${command}${CL}\n" exit "$exit_code" } diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index b679ce133..36e4fcb91 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -33,15 +33,14 @@ trap on_terminate TERM error_handler() { local exit_code="$1" local line_number="$2" - local command="$3" + local command="${3:-}" - # Exitcode 0 = kein Fehler → ignorieren if [[ "$exit_code" -eq 0 ]]; then return 0 fi printf "\e[?25h" - echo -e "\n${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}\n" + echo -e "\n${RD}[ERROR]${CL} in line ${RD}${line_number}${CL}: exit code ${RD}${exit_code}${CL}: while executing command ${YW}${command}${CL}\n" exit "$exit_code" } diff --git a/misc/install.func b/misc/install.func index e3751c295..dd365527b 100644 --- a/misc/install.func +++ b/misc/install.func @@ -32,19 +32,17 @@ catch_errors() { # This function handles errors error_handler() { source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/api.func) - printf "\e[?25h" - local exit_code="$?" - local line_number="$1" - local command="$2" - local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" - echo -e "\n$error_message" + local exit_code="$1" + local line_number="$2" + local command="${3:-}" - if [[ "$line_number" -eq 50 ]]; then - echo -e "The silent function has suppressed the error, run the script with verbose mode enabled, which will provide more detailed output.\n" - post_update_to_api "failed" "No error message, script ran in silent mode" - else - post_update_to_api "failed" "${command}" + if [[ "$exit_code" -eq 0 ]]; then + return 0 fi + + printf "\e[?25h" + echo -e "\n${RD}[ERROR]${CL} in line ${RD}${line_number}${CL}: exit code ${RD}${exit_code}${CL}: while executing command ${YW}${command}${CL}\n" + exit "$exit_code" } # This function sets up the Container OS by generating the locale, setting the timezone, and checking the network connection From 343af10d8b12b24ebb6e97b286fe48fea47320e9 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 15 Sep 2025 15:26:57 +0200 Subject: [PATCH 0913/1733] error_handler --- misc/build.func | 4 ++ misc/error-handler.func | 95 +++++++++++++++++++++++++++++++++++++++++ misc/install.func | 2 + 3 files changed, 101 insertions(+) create mode 100644 misc/error-handler.func diff --git a/misc/build.func b/misc/build.func index 1142ef9e2..41bfb2734 100644 --- a/misc/build.func +++ b/misc/build.func @@ -20,11 +20,15 @@ source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxV if command -v curl >/dev/null 2>&1; then source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) load_functions + init_error_traps #echo "(build.func) Loaded core.func via curl" elif command -v wget >/dev/null 2>&1; then source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) load_functions + init_error_traps #echo "(build.func) Loaded core.func via wget" fi diff --git a/misc/error-handler.func b/misc/error-handler.func new file mode 100644 index 000000000..6bf08dc1e --- /dev/null +++ b/misc/error-handler.func @@ -0,0 +1,95 @@ +#!/usr/bin/env bash +# ------------------------------------------------------------------------------ +# Error & Signal Handling for ProxmoxVED Scripts +# ------------------------------------------------------------------------------ +# Copyright (c) 2021-2025 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# ------------------------------------------------------------------------------ + +explain_exit_code() { + local code="$1" + case "$code" in + 1) echo "General error / Operation not permitted" ;; + 2) echo "Misuse of shell builtins (e.g. syntax error)" ;; + 126) echo "Command invoked cannot execute (permission problem?)" ;; + 127) echo "Command not found" ;; + 128) echo "Invalid argument to exit" ;; + 130) echo "Terminated by Ctrl+C (SIGINT)" ;; + 137) echo "Killed (SIGKILL / out of memory?)" ;; + 143) echo "Terminated (SIGTERM)" ;; + 200) echo "Custom: Failed to create lock file" ;; + 203) echo "Custom: Missing CTID variable" ;; + 204) echo "Custom: Missing PCT_OSTYPE variable" ;; + 205) echo "Custom: Invalid CTID (<100)" ;; + 209) echo "Custom: Container creation failed" ;; + 210) echo "Custom: Cluster not quorate" ;; + 214) echo "Custom: Not enough storage space" ;; + 215) echo "Custom: Container ID not listed" ;; + 216) echo "Custom: RootFS entry missing in config" ;; + 217) echo "Custom: Storage does not support rootdir" ;; + 220) echo "Custom: Unable to resolve template path" ;; + 222) echo "Custom: Template download failed after 3 attempts" ;; + 223) echo "Custom: Template not available after download" ;; + 231) echo "Custom: LXC stack upgrade/retry failed" ;; + *) echo "Unknown error" ;; + esac +} + +# === Error handler ============================================================ +error_handler() { + local exit_code=$? + local line_number=${BASH_LINENO[0]:-unknown} + local command=${BASH_COMMAND:-unknown} + + # Exitcode 0 = kein Fehler → ignorieren + if [[ "$exit_code" -eq 0 ]]; then + return 0 + fi + + local explanation + explanation="$(explain_exit_code "$exit_code")" + + printf "\e[?25h" + echo -e "\n${RD}[ERROR]${CL} in line ${RD}${line_number}${CL}: exit code ${RD}${exit_code}${CL} (${explanation}): while executing command ${YW}${command}${CL}\n" + + if [[ -n "${DEBUG_LOGFILE:-}" ]]; then + { + echo "------ ERROR ------" + echo "Timestamp : $(date '+%Y-%m-%d %H:%M:%S')" + echo "Exit Code : $exit_code ($explanation)" + echo "Line : $line_number" + echo "Command : $command" + echo "-------------------" + } >>"$DEBUG_LOGFILE" + fi + + exit "$exit_code" +} + +# === Exit handler ============================================================= +on_exit() { + local exit_code=$? + [[ -n "${lockfile:-}" && -e "$lockfile" ]] && rm -f "$lockfile" + exit "$exit_code" +} + +# === Signal handlers ========================================================== +on_interrupt() { + echo -e "\n${RD}Interrupted by user (SIGINT)${CL}" + exit 130 +} + +on_terminate() { + echo -e "\n${RD}Terminated by signal (SIGTERM)${CL}" + exit 143 +} + +# === Init traps =============================================================== +init_error_traps() { + set -Eeuo pipefail + trap 'error_handler' ERR + trap on_exit EXIT + trap on_interrupt INT + trap on_terminate TERM +} diff --git a/misc/install.func b/misc/install.func index dd365527b..94e5c5a8f 100644 --- a/misc/install.func +++ b/misc/install.func @@ -11,7 +11,9 @@ if ! command -v curl >/dev/null 2>&1; then apt-get install -y curl >/dev/null 2>&1 fi source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) load_functions +init_error_traps # This function enables IPv6 if it's not disabled and sets verbose mode verb_ip6() { From c3fd7ea4e4642753d8a9f1e43ce0a4badca29b27 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 15 Sep 2025 15:27:49 +0200 Subject: [PATCH 0914/1733] rename --- misc/{error-handler.func => error_handler.func} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename misc/{error-handler.func => error_handler.func} (100%) diff --git a/misc/error-handler.func b/misc/error_handler.func similarity index 100% rename from misc/error-handler.func rename to misc/error_handler.func From 8d687ba9abb8ddda82b3bf1b5a4e9b188d4cddf7 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 15 Sep 2025 15:34:41 +0200 Subject: [PATCH 0915/1733] test --- misc/build.func | 58 ++++++++++++++++++++++----------------------- misc/core.func | 13 ++++++---- misc/create_lxc.sh | 59 +++++++++++++++++++++++----------------------- misc/install.func | 36 ++++++++++++++-------------- 4 files changed, 84 insertions(+), 82 deletions(-) diff --git a/misc/build.func b/misc/build.func index 41bfb2734..e6b49e4e3 100644 --- a/misc/build.func +++ b/misc/build.func @@ -32,41 +32,41 @@ elif command -v wget >/dev/null 2>&1; then #echo "(build.func) Loaded core.func via wget" fi -set -Eeuo pipefail -trap 'error_handler $? $LINENO "$BASH_COMMAND"' ERR -trap on_exit EXIT -trap on_interrupt INT -trap on_terminate TERM +# set -Eeuo pipefail +# trap 'error_handler $? $LINENO "$BASH_COMMAND"' ERR +# trap on_exit EXIT +# trap on_interrupt INT +# trap on_terminate TERM -error_handler() { - local exit_code="$1" - local line_number="$2" - local command="${3:-}" +# error_handler() { +# local exit_code="$1" +# local line_number="$2" +# local command="${3:-}" - if [[ "$exit_code" -eq 0 ]]; then - return 0 - fi +# if [[ "$exit_code" -eq 0 ]]; then +# return 0 +# fi - printf "\e[?25h" - echo -e "\n${RD}[ERROR]${CL} in line ${RD}${line_number}${CL}: exit code ${RD}${exit_code}${CL}: while executing command ${YW}${command}${CL}\n" - exit "$exit_code" -} +# printf "\e[?25h" +# echo -e "\n${RD}[ERROR]${CL} in line ${RD}${line_number}${CL}: exit code ${RD}${exit_code}${CL}: while executing command ${YW}${command}${CL}\n" +# exit "$exit_code" +# } -on_exit() { - local exit_code="$?" - [[ -n "${lockfile:-}" && -e "$lockfile" ]] && rm -f "$lockfile" - exit "$exit_code" -} +# on_exit() { +# local exit_code="$?" +# [[ -n "${lockfile:-}" && -e "$lockfile" ]] && rm -f "$lockfile" +# exit "$exit_code" +# } -on_interrupt() { - echo -e "\n${RD}Interrupted by user (SIGINT)${CL}" - exit 130 -} +# on_interrupt() { +# echo -e "\n${RD}Interrupted by user (SIGINT)${CL}" +# exit 130 +# } -on_terminate() { - echo -e "\n${RD}Terminated by signal (SIGTERM)${CL}" - exit 143 -} +# on_terminate() { +# echo -e "\n${RD}Terminated by signal (SIGTERM)${CL}" +# exit 143 +# } # Check if the shell is using bash shell_check() { diff --git a/misc/core.func b/misc/core.func index 5df50efd9..5937b3dd1 100644 --- a/misc/core.func +++ b/misc/core.func @@ -96,10 +96,10 @@ _tool_error_hint() { # exit 143 # } -catch_errors() { - set -Eeuo pipefail - trap 'error_handler $LINENO "$BASH_COMMAND"' ERR -} +# catch_errors() { +# set -Eeuo pipefail +# trap 'error_handler $LINENO "$BASH_COMMAND"' ERR +# } # ------------------------------------------------------------------------------ # Sets ANSI color codes used for styled terminal output. @@ -190,8 +190,11 @@ set_std_mode() { } # Silent execution function +SILENT_LOGFILE="/tmp/silent.$$.log" + silent() { - "$@" >/dev/null 2>&1 + "$@" >>"$SILENT_LOGFILE" 2>&1 + return $? } # Function to download & save header files diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index 36e4fcb91..ace589d4d 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -24,41 +24,41 @@ fi # ------------------------------------------------------------------------------ # Strict error handling # ------------------------------------------------------------------------------ -set -Eeuo pipefail -trap 'error_handler $? $LINENO "$BASH_COMMAND"' ERR -trap on_exit EXIT -trap on_interrupt INT -trap on_terminate TERM +# set -Eeuo pipefail +# trap 'error_handler $? $LINENO "$BASH_COMMAND"' ERR +# trap on_exit EXIT +# trap on_interrupt INT +# trap on_terminate TERM -error_handler() { - local exit_code="$1" - local line_number="$2" - local command="${3:-}" +# error_handler() { +# local exit_code="$1" +# local line_number="$2" +# local command="${3:-}" - if [[ "$exit_code" -eq 0 ]]; then - return 0 - fi +# if [[ "$exit_code" -eq 0 ]]; then +# return 0 +# fi - printf "\e[?25h" - echo -e "\n${RD}[ERROR]${CL} in line ${RD}${line_number}${CL}: exit code ${RD}${exit_code}${CL}: while executing command ${YW}${command}${CL}\n" - exit "$exit_code" -} +# printf "\e[?25h" +# echo -e "\n${RD}[ERROR]${CL} in line ${RD}${line_number}${CL}: exit code ${RD}${exit_code}${CL}: while executing command ${YW}${command}${CL}\n" +# exit "$exit_code" +# } -on_exit() { - local exit_code="$?" - [[ -n "${lockfile:-}" && -e "$lockfile" ]] && rm -f "$lockfile" - exit "$exit_code" -} +# on_exit() { +# local exit_code="$?" +# [[ -n "${lockfile:-}" && -e "$lockfile" ]] && rm -f "$lockfile" +# exit "$exit_code" +# } -on_interrupt() { - echo -e "\n${RD}Interrupted by user (SIGINT)${CL}" - exit 130 -} +# on_interrupt() { +# echo -e "\n${RD}Interrupted by user (SIGINT)${CL}" +# exit 130 +# } -on_terminate() { - echo -e "\n${RD}Terminated by signal (SIGTERM)${CL}" - exit 143 -} +# on_terminate() { +# echo -e "\n${RD}Terminated by signal (SIGTERM)${CL}" +# exit 143 +# } exit_script() { clear @@ -490,7 +490,6 @@ if ! pveam list "$TEMPLATE_STORAGE" 2>/dev/null | grep -q "$TEMPLATE"; then msg_error "Template $TEMPLATE not available in storage $TEMPLATE_STORAGE after download." exit 223 fi -msg_ok "Template $TEMPLATE is ready for container creation." # ------------------------------------------------------------------------------ # Dynamic preflight for Debian 13.x: offer upgrade if available (no hard mins) diff --git a/misc/install.func b/misc/install.func index 94e5c5a8f..1248c2733 100644 --- a/misc/install.func +++ b/misc/install.func @@ -25,27 +25,27 @@ verb_ip6() { fi } -# This function sets error handling options and defines the error_handler function to handle errors -catch_errors() { - set -Eeuo pipefail - trap 'error_handler $LINENO "$BASH_COMMAND"' ERR -} +# # This function sets error handling options and defines the error_handler function to handle errors +# catch_errors() { +# set -Eeuo pipefail +# trap 'error_handler $LINENO "$BASH_COMMAND"' ERR +# } -# This function handles errors -error_handler() { - source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/api.func) - local exit_code="$1" - local line_number="$2" - local command="${3:-}" +# # This function handles errors +# error_handler() { +# source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/api.func) +# local exit_code="$1" +# local line_number="$2" +# local command="${3:-}" - if [[ "$exit_code" -eq 0 ]]; then - return 0 - fi +# if [[ "$exit_code" -eq 0 ]]; then +# return 0 +# fi - printf "\e[?25h" - echo -e "\n${RD}[ERROR]${CL} in line ${RD}${line_number}${CL}: exit code ${RD}${exit_code}${CL}: while executing command ${YW}${command}${CL}\n" - exit "$exit_code" -} +# printf "\e[?25h" +# echo -e "\n${RD}[ERROR]${CL} in line ${RD}${line_number}${CL}: exit code ${RD}${exit_code}${CL}: while executing command ${YW}${command}${CL}\n" +# exit "$exit_code" +#} # This function sets up the Container OS by generating the locale, setting the timezone, and checking the network connection setting_up_container() { From 3f69160fb9fbbf5a70120e9ae7f5f6f0ebc99677 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 15 Sep 2025 15:37:56 +0200 Subject: [PATCH 0916/1733] test --- ct/alpine.sh | 2 +- ct/debian.sh | 24 ++-- ct/deferred/alpine-homarr.sh | 2 +- ct/deferred/ampache.sh | 2 +- ct/deferred/ghostfolio.sh | 2 +- ct/deferred/hoodik.sh | 2 +- ct/deferred/jumpserver.sh | 64 +++++----- ct/deferred/kasm.sh | 62 +++++----- ct/deferred/koel.sh | 2 +- ct/deferred/librespeed.sh | 48 ++++---- ct/deferred/netbootxyz.sh | 2 +- ct/deferred/nginxproxymanager.sh | 2 +- ct/deferred/ocis.sh | 2 +- ct/deferred/openwebui.sh | 2 +- ct/deferred/pixelfed.sh | 2 +- ct/deferred/polaris.sh | 2 +- ct/deferred/roundcubemail.sh | 2 +- ct/deferred/squirrelserversmanager.sh | 2 +- ct/deferred/vikunja.sh | 91 +++++++------- ct/dispatcharr.sh | 170 +++++++++++++------------- ct/docspell.sh | 2 +- ct/ente.sh | 24 ++-- ct/freepbx.sh | 54 ++++---- ct/frigate.sh | 2 +- ct/garmin-grafana.sh | 134 ++++++++++---------- ct/ghostfolio.sh | 88 ++++++------- ct/globaleaks.sh | 24 ++-- ct/hanko.sh | 2 +- ct/joplin-server.sh | 60 ++++----- ct/kanba.sh | 2 +- ct/leantime.sh | 58 ++++----- ct/librenms.sh | 2 +- ct/livebook.sh | 56 ++++----- ct/manyfold.sh | 2 +- ct/maxun.sh | 2 +- ct/notesnook.sh | 2 +- ct/npmplus.sh | 50 ++++---- ct/opencloud.sh | 2 +- ct/postiz.sh | 2 +- ct/proxmox-datacenter-manager.sh | 4 +- ct/romm.sh | 76 ++++++------ ct/rybbit.sh | 2 +- ct/scraparr.sh | 60 ++++----- ct/signoz.sh | 64 +++++----- ct/tunarr.sh | 90 +++++++------- ct/ubuntu.sh | 2 +- ct/viseron.sh | 18 +-- ct/wallabag.sh | 2 +- 48 files changed, 685 insertions(+), 688 deletions(-) diff --git a/ct/alpine.sh b/ct/alpine.sh index ea946a60e..4940d6fc5 100644 --- a/ct/alpine.sh +++ b/ct/alpine.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { header_info diff --git a/ct/debian.sh b/ct/debian.sh index c41a2a3db..dad596833 100644 --- a/ct/debian.sh +++ b/ct/debian.sh @@ -19,21 +19,21 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /var ]]; then - msg_error "No ${APP} Installation Found!" + header_info + check_container_storage + check_container_resources + if [[ ! -d /var ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating $APP LXC" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Updated $APP LXC" exit - fi - msg_info "Updating $APP LXC" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "Updated $APP LXC" - exit } start diff --git a/ct/deferred/alpine-homarr.sh b/ct/deferred/alpine-homarr.sh index c78398d65..def7cca2e 100644 --- a/ct/deferred/alpine-homarr.sh +++ b/ct/deferred/alpine-homarr.sh @@ -18,7 +18,7 @@ header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { header_info diff --git a/ct/deferred/ampache.sh b/ct/deferred/ampache.sh index ada18f098..ccf300e22 100644 --- a/ct/deferred/ampache.sh +++ b/ct/deferred/ampache.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { header_info diff --git a/ct/deferred/ghostfolio.sh b/ct/deferred/ghostfolio.sh index 56002d405..360390927 100644 --- a/ct/deferred/ghostfolio.sh +++ b/ct/deferred/ghostfolio.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { header_info diff --git a/ct/deferred/hoodik.sh b/ct/deferred/hoodik.sh index 0e7597b14..1fee14b13 100644 --- a/ct/deferred/hoodik.sh +++ b/ct/deferred/hoodik.sh @@ -18,7 +18,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { header_info diff --git a/ct/deferred/jumpserver.sh b/ct/deferred/jumpserver.sh index a87847336..1658a3683 100644 --- a/ct/deferred/jumpserver.sh +++ b/ct/deferred/jumpserver.sh @@ -17,42 +17,42 @@ var_unprivileged="1" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { - header_info - check_container_storage - check_container_resources + header_info + check_container_storage + check_container_resources - if [[ ! -d /opt/jumpserver ]]; then - msg_error "No ${APP} Installation Found!" + if [[ ! -d /opt/jumpserver ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + RELEASE=$(curl -fsSL https://api.github.com/repos/jumpserver/installer/releases/latest | grep '"tag_name"' | sed -E 's/.*"tag_name": "([^"]+)".*/\1/') + if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then + msg_info "Updating ${APP} to ${RELEASE}" + if [[ -d /opt/jumpserver/config ]]; then + cp -r /opt/jumpserver/config /opt/jumpserver_config_backup + fi + echo "${RELEASE}" >/opt/${APP}_version.txt + rm -rf /opt/jumpserver + cd /opt + curl -fsSL "https://github.com/jumpserver/installer/releases/download/${RELEASE}/jumpserver-installer-${RELEASE}.tar.gz" -o jumpserver-installer-${RELEASE}.tar.gz + mkdir -p /opt/jumpserver + $STD tar -xzvf jumpserver-installer-${RELEASE}.tar.gz -C /opt/jumpserver --strip-components=1 + if [[ -d /opt/jumpserver_config_backup ]]; then + cp -r /opt/jumpserver_config_backup /opt/jumpserver/config + rm -rf /opt/jumpserver_config_backup + fi + cd /opt/jumpserver + yes y | head -n 3 | $STD ./jmsctl.sh upgrade + $STD ./jmsctl.sh start + rm -rf /opt/jumpserver-installer-${RELEASE}.tar.gz + msg_ok "Updated Successfully" + else + msg_ok "No update required. ${APP} is already at ${RELEASE}." + fi exit - fi - RELEASE=$(curl -fsSL https://api.github.com/repos/jumpserver/installer/releases/latest | grep '"tag_name"' | sed -E 's/.*"tag_name": "([^"]+)".*/\1/') - if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then - msg_info "Updating ${APP} to ${RELEASE}" - if [[ -d /opt/jumpserver/config ]]; then - cp -r /opt/jumpserver/config /opt/jumpserver_config_backup - fi - echo "${RELEASE}" >/opt/${APP}_version.txt - rm -rf /opt/jumpserver - cd /opt - curl -fsSL "https://github.com/jumpserver/installer/releases/download/${RELEASE}/jumpserver-installer-${RELEASE}.tar.gz" -o jumpserver-installer-${RELEASE}.tar.gz - mkdir -p /opt/jumpserver - $STD tar -xzvf jumpserver-installer-${RELEASE}.tar.gz -C /opt/jumpserver --strip-components=1 - if [[ -d /opt/jumpserver_config_backup ]]; then - cp -r /opt/jumpserver_config_backup /opt/jumpserver/config - rm -rf /opt/jumpserver_config_backup - fi - cd /opt/jumpserver - yes y | head -n 3 | $STD ./jmsctl.sh upgrade - $STD ./jmsctl.sh start - rm -rf /opt/jumpserver-installer-${RELEASE}.tar.gz - msg_ok "Updated Successfully" - else - msg_ok "No update required. ${APP} is already at ${RELEASE}." - fi - exit } start diff --git a/ct/deferred/kasm.sh b/ct/deferred/kasm.sh index 6ee3bb931..ba78e8d57 100644 --- a/ct/deferred/kasm.sh +++ b/ct/deferred/kasm.sh @@ -19,41 +19,41 @@ var_tun="${var_tun:-yes}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { - header_info - check_container_storage - check_container_resources + header_info + check_container_storage + check_container_resources - if [[ ! -d /opt/kasm ]]; then - msg_error "No ${APP} Installation Found!" + if [[ ! -d /opt/kasm ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + RELEASE=$(curl -fsSL 'https://www.kasmweb.com/downloads' | grep -o 'https://kasm-static-content.s3.amazonaws.com/kasm_release_[^"]*\.tar\.gz' | head -n 1 | sed -E 's/.*release_(.*)\.tar\.gz/\1/') + if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then + msg_info "Updating ${APP} to v${RELEASE}" + temp_file=$(mktemp) + curl -fsSL "https://kasm-static-content.s3.amazonaws.com/kasm_release_${RELEASE}.tar.gz" -o "$temp_file" + tar zxf "$temp_file" + mkdir -p /opt/kasm/backups/ + chmod 777 /opt/kasm/backups/ + mv /opt/kasm/1.*/certs/kasm_nginx.crt /opt/kasm/kasm_nginx.crt_bak + printf 'y\n' | $STD sudo bash /tmp/kasm_release/upgrade.sh + $STD sudo bash /tmp/kasm_release/upgrade.sh + echo "${RELEASE}" >/opt/${APP}_version.txt + msg_ok "Updated ${APP} to v${RELEASE}" + + msg_info "Cleaning up" + rm -f "$temp_file" + rm -rf /tmp/kasm_release + $STD apt-get -y autoremove + $STD apt-get -y autoclean + msg_ok "Cleaned" + else + msg_ok "No update required. ${APP} is already at v${RELEASE}" + fi exit - fi - RELEASE=$(curl -fsSL 'https://www.kasmweb.com/downloads' | grep -o 'https://kasm-static-content.s3.amazonaws.com/kasm_release_[^"]*\.tar\.gz' | head -n 1 | sed -E 's/.*release_(.*)\.tar\.gz/\1/') - if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then - msg_info "Updating ${APP} to v${RELEASE}" - temp_file=$(mktemp) - curl -fsSL "https://kasm-static-content.s3.amazonaws.com/kasm_release_${RELEASE}.tar.gz" -o "$temp_file" - tar zxf "$temp_file" - mkdir -p /opt/kasm/backups/ - chmod 777 /opt/kasm/backups/ - mv /opt/kasm/1.*/certs/kasm_nginx.crt /opt/kasm/kasm_nginx.crt_bak - printf 'y\n' | $STD sudo bash /tmp/kasm_release/upgrade.sh - $STD sudo bash /tmp/kasm_release/upgrade.sh - echo "${RELEASE}" >/opt/${APP}_version.txt - msg_ok "Updated ${APP} to v${RELEASE}" - - msg_info "Cleaning up" - rm -f "$temp_file" - rm -rf /tmp/kasm_release - $STD apt-get -y autoremove - $STD apt-get -y autoclean - msg_ok "Cleaned" - else - msg_ok "No update required. ${APP} is already at v${RELEASE}" - fi - exit } start diff --git a/ct/deferred/koel.sh b/ct/deferred/koel.sh index a4b6ea7e8..b0c12d864 100644 --- a/ct/deferred/koel.sh +++ b/ct/deferred/koel.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { header_info diff --git a/ct/deferred/librespeed.sh b/ct/deferred/librespeed.sh index 931248cd3..4861a5688 100644 --- a/ct/deferred/librespeed.sh +++ b/ct/deferred/librespeed.sh @@ -17,34 +17,34 @@ var_unprivileged="1" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { - header_info - check_container_storage - check_container_resources + header_info + check_container_storage + check_container_resources - if [[ ! -f /opt/librespeed/index.html ]]; then - msg_error "No ${APP} Installation Found!" + if [[ ! -f /opt/librespeed/index.html ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + RELEASE=$(curl -fsSL https://api.github.com/repos/librespeed/speedtest/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}') + if [[ ! -f /opt/librespeed/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt//librespeed/${APP}_version.txt)" ]]; then + msg_info "Updating $APP..." + temp_file=$(mktemp) + curl -fsSL "https://github.com/librespeed/speedtest/archive/refs/tags/${RELEASE}.zip" -o "$temp_file" + mkdir -p /temp + unzip -qu "$temp_file" -d /temp + cd /temp/speedtest-"${RELEASE}" + cp -u favicon.ico index.html speedtest.js speedtest_worker.js /opt/librespeed/ + cp -ru backend /opt/librespeed/ + echo "${RELEASE}" >/opt/"${APP}"_version.txt + systemctl restart caddy + msg_ok "$APP has been updated." + else + msg_ok "No update required. ${APP} is already at ${RELEASE}" + fi exit - fi - RELEASE=$(curl -fsSL https://api.github.com/repos/librespeed/speedtest/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}') - if [[ ! -f /opt/librespeed/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt//librespeed/${APP}_version.txt)" ]]; then - msg_info "Updating $APP..." - temp_file=$(mktemp) - curl -fsSL "https://github.com/librespeed/speedtest/archive/refs/tags/${RELEASE}.zip" -o "$temp_file" - mkdir -p /temp - unzip -qu "$temp_file" -d /temp - cd /temp/speedtest-"${RELEASE}" - cp -u favicon.ico index.html speedtest.js speedtest_worker.js /opt/librespeed/ - cp -ru backend /opt/librespeed/ - echo "${RELEASE}" >/opt/"${APP}"_version.txt - systemctl restart caddy - msg_ok "$APP has been updated." - else - msg_ok "No update required. ${APP} is already at ${RELEASE}" - fi - exit } start build_container diff --git a/ct/deferred/netbootxyz.sh b/ct/deferred/netbootxyz.sh index 201073af9..60832ff44 100644 --- a/ct/deferred/netbootxyz.sh +++ b/ct/deferred/netbootxyz.sh @@ -26,7 +26,7 @@ var_os="${var_os:-debian}" var_version="${var_version:-12}" variables color -catch_errors +init_error_traps function default_settings() { CT_TYPE="1" diff --git a/ct/deferred/nginxproxymanager.sh b/ct/deferred/nginxproxymanager.sh index b1ff024d7..41f1aecdf 100644 --- a/ct/deferred/nginxproxymanager.sh +++ b/ct/deferred/nginxproxymanager.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { header_info diff --git a/ct/deferred/ocis.sh b/ct/deferred/ocis.sh index 167b4593d..44f5f1aec 100644 --- a/ct/deferred/ocis.sh +++ b/ct/deferred/ocis.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { header_info diff --git a/ct/deferred/openwebui.sh b/ct/deferred/openwebui.sh index 85341bd9b..47a9e74a1 100644 --- a/ct/deferred/openwebui.sh +++ b/ct/deferred/openwebui.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { header_info diff --git a/ct/deferred/pixelfed.sh b/ct/deferred/pixelfed.sh index 8ddab519c..44ac53b3b 100644 --- a/ct/deferred/pixelfed.sh +++ b/ct/deferred/pixelfed.sh @@ -16,7 +16,7 @@ var_version="${var_version:-12}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { header_info diff --git a/ct/deferred/polaris.sh b/ct/deferred/polaris.sh index c290d7706..c43b57dec 100644 --- a/ct/deferred/polaris.sh +++ b/ct/deferred/polaris.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { header_info diff --git a/ct/deferred/roundcubemail.sh b/ct/deferred/roundcubemail.sh index 75d0e3bcd..6f65e5277 100644 --- a/ct/deferred/roundcubemail.sh +++ b/ct/deferred/roundcubemail.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { header_info diff --git a/ct/deferred/squirrelserversmanager.sh b/ct/deferred/squirrelserversmanager.sh index 4403ab5f0..751a6026b 100644 --- a/ct/deferred/squirrelserversmanager.sh +++ b/ct/deferred/squirrelserversmanager.sh @@ -16,7 +16,7 @@ var_unprivileged="${var_unprivileged:-1}" variables color -catch_errors +init_error_traps function update_script() { header_info diff --git a/ct/deferred/vikunja.sh b/ct/deferred/vikunja.sh index 792e3b1e5..687d47002 100644 --- a/ct/deferred/vikunja.sh +++ b/ct/deferred/vikunja.sh @@ -17,55 +17,54 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/vikunja ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/vikunja ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi - if whiptail --backtitle "Vikunja Update" --title "🔄 VERSION SELECTION" --yesno \ - "Choose the version type to update to:\n\n• STABLE: Recommended for production use\n• UNSTABLE: Latest development version\n\n⚠️ WARNING: Unstable versions may contain bugs,\nbe incomplete, or cause system instability.\nOnly use for testing purposes.\n\nDo you want to use the UNSTABLE version?\n(No = Stable, Yes = Unstable)" 16 70 --defaultno - then - msg_info "Selecting version" - RELEASE="unstable" - FILENAME="vikunja-${RELEASE}-x86_64.deb" - msg_ok "Selected UNSTABLE version" - else - msg_info "Selecting version" - RELEASE=$(curl -fsSL https://dl.vikunja.io/vikunja/ | grep -oP 'href="/vikunja/\K[0-9]+\.[0-9]+\.[0-9]+' | sort -V | tail -n 1) - FILENAME="vikunja-${RELEASE}-amd64.deb" - msg_ok "Selected STABLE version: ${RELEASE}" - fi - - if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then - msg_info "Stopping ${APP}" - systemctl stop vikunja - msg_ok "Stopped ${APP}" - msg_info "Updating ${APP} to ${RELEASE}" - cd /opt - rm -rf /opt/vikunja/vikunja - rm -rf "/opt/$FILENAME" - curl -fsSL "https://dl.vikunja.io/vikunja/$RELEASE/$FILENAME" -o $(basename "https://dl.vikunja.io/vikunja/$RELEASE/$FILENAME") - export DEBIAN_FRONTEND=noninteractive - $STD dpkg -i $FILENAME - echo "${RELEASE}" >/opt/${APP}_version.txt - msg_ok "Updated ${APP}" - msg_info "Starting ${APP}" - systemctl start vikunja - msg_ok "Started ${APP}" - msg_info "Cleaning Up" - rm -rf /opt/$FILENAME - msg_ok "Cleaned" - msg_ok "Updated Successfully" - else - msg_ok "No update required. ${APP} is already at ${RELEASE}" - fi - exit + if whiptail --backtitle "Vikunja Update" --title "🔄 VERSION SELECTION" --yesno \ + "Choose the version type to update to:\n\n• STABLE: Recommended for production use\n• UNSTABLE: Latest development version\n\n⚠️ WARNING: Unstable versions may contain bugs,\nbe incomplete, or cause system instability.\nOnly use for testing purposes.\n\nDo you want to use the UNSTABLE version?\n(No = Stable, Yes = Unstable)" 16 70 --defaultno; then + msg_info "Selecting version" + RELEASE="unstable" + FILENAME="vikunja-${RELEASE}-x86_64.deb" + msg_ok "Selected UNSTABLE version" + else + msg_info "Selecting version" + RELEASE=$(curl -fsSL https://dl.vikunja.io/vikunja/ | grep -oP 'href="/vikunja/\K[0-9]+\.[0-9]+\.[0-9]+' | sort -V | tail -n 1) + FILENAME="vikunja-${RELEASE}-amd64.deb" + msg_ok "Selected STABLE version: ${RELEASE}" + fi + + if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then + msg_info "Stopping ${APP}" + systemctl stop vikunja + msg_ok "Stopped ${APP}" + msg_info "Updating ${APP} to ${RELEASE}" + cd /opt + rm -rf /opt/vikunja/vikunja + rm -rf "/opt/$FILENAME" + curl -fsSL "https://dl.vikunja.io/vikunja/$RELEASE/$FILENAME" -o $(basename "https://dl.vikunja.io/vikunja/$RELEASE/$FILENAME") + export DEBIAN_FRONTEND=noninteractive + $STD dpkg -i $FILENAME + echo "${RELEASE}" >/opt/${APP}_version.txt + msg_ok "Updated ${APP}" + msg_info "Starting ${APP}" + systemctl start vikunja + msg_ok "Started ${APP}" + msg_info "Cleaning Up" + rm -rf /opt/$FILENAME + msg_ok "Cleaned" + msg_ok "Updated Successfully" + else + msg_ok "No update required. ${APP} is already at ${RELEASE}" + fi + exit } start diff --git a/ct/dispatcharr.sh b/ct/dispatcharr.sh index 492858590..979cd3d07 100644 --- a/ct/dispatcharr.sh +++ b/ct/dispatcharr.sh @@ -18,96 +18,94 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { - header_info - check_container_storage - check_container_resources + header_info + check_container_storage + check_container_resources - if [[ ! -d "/opt/dispatcharr" ]]; then - msg_error "No ${APP} Installation Found!" + if [[ ! -d "/opt/dispatcharr" ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + RELEASE=$(curl -fsSL https://api.github.com/repos/Dispatcharr/Dispatcharr/releases/latest | jq -r '.tag_name' | sed 's/^v//') + if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then + msg_ok "Starting update" + APP_DIR="/opt/dispatcharr" + APP_USER="dispatcharr" + APP_GROUP="dispatcharr" + + msg_info "Stopping $APP" + systemctl stop dispatcharr-celery + systemctl stop dispatcharr-celerybeat + systemctl stop dispatcharr-daphne + systemctl stop dispatcharr + msg_ok "Stopped $APP" + + msg_info "Creating Backup" + BACKUP_FILE="/opt/dispatcharr_$(date +%F).tar.gz" + msg_info "Source and Database backup" + set -o allexport + source /etc/$APP_NAME/$APP_NAME.env + set +o allexport + PGPASSWORD=$POSTGRES_PASSWORD pg_dump -U $POSTGRES_USER -h $POSTGRES_HOST $POSTGRES_DB >/opt/$POSTGRES_DB-$(date +%F).sql + $STD tar -czf "$BACKUP_FILE" /opt/dispatcharr /opt/Dispatcharr_version.txt /opt/$POSTGRES_DB-$(date +%F).sql &>/dev/null + msg_ok "Backup Created" + + msg_info "Updating $APP to v${RELEASE}" + rm -rf /opt/dispatcharr + fetch_and_deploy_gh_release "dispatcharr" "Dispatcharr/Dispatcharr" + chown -R "$APP_USER:$APP_GROUP" "$APP_DIR" + sed -i 's/program\[\x27channel_id\x27\]/program["channel_id"]/g' "${APP_DIR}/apps/output/views.py" + + msg_ok "Dispatcharr Updated to $RELEASE" + + msg_info "Creating Python Virtual Environment" + cd $APP_DIR + python3 -m venv env + source env/bin/activate + $STD pip install --upgrade pip + $STD pip install -r requirements.txt + $STD pip install gunicorn + ln -sf /usr/bin/ffmpeg $APP_DIR/env/bin/ffmpeg + msg_ok "Python Environment Setup" + + msg_info "Building Frontend" + cd $APP_DIR/frontend + $STD npm install --legacy-peer-deps + $STD npm run build + msg_ok "Built Frontend" + + msg_info "Running Django Migrations" + cd $APP_DIR + source env/bin/activate + set -o allexport + source /etc/$APP_NAME/$APP_NAME.env + set +o allexport + $STD python manage.py migrate --noinput + $STD python manage.py collectstatic --noinput + msg_ok "Migrations Complete" + + msg_info "Starting $APP" + systemctl start dispatcharr-celery + systemctl start dispatcharr-celerybeat + systemctl start dispatcharr-daphne + systemctl start dispatcharr + msg_ok "Started $APP" + echo "${RELEASE}" >"/opt/${APP}_version.txt" + + msg_info "Cleaning Up" + rm -rf /opt/$POSTGRES_DB-$(date +%F).sql + msg_ok "Cleanup Completed" + + msg_ok "Update Successful, Backup saved to $BACKUP_FILE" + + else + msg_ok "No update required. ${APP} is already at v${RELEASE}" + fi exit - fi - - RELEASE=$(curl -fsSL https://api.github.com/repos/Dispatcharr/Dispatcharr/releases/latest | jq -r '.tag_name' | sed 's/^v//') - if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then - msg_ok "Starting update" - APP_DIR="/opt/dispatcharr" - APP_USER="dispatcharr" - APP_GROUP="dispatcharr" - - - - msg_info "Stopping $APP" - systemctl stop dispatcharr-celery - systemctl stop dispatcharr-celerybeat - systemctl stop dispatcharr-daphne - systemctl stop dispatcharr - msg_ok "Stopped $APP" - - msg_info "Creating Backup" - BACKUP_FILE="/opt/dispatcharr_$(date +%F).tar.gz" - msg_info "Source and Database backup" - set -o allexport - source /etc/$APP_NAME/$APP_NAME.env - set +o allexport - PGPASSWORD=$POSTGRES_PASSWORD pg_dump -U $POSTGRES_USER -h $POSTGRES_HOST $POSTGRES_DB > /opt/$POSTGRES_DB-`date +%F`.sql - $STD tar -czf "$BACKUP_FILE" /opt/dispatcharr /opt/Dispatcharr_version.txt /opt/$POSTGRES_DB-`date +%F`.sql &>/dev/null - msg_ok "Backup Created" - - msg_info "Updating $APP to v${RELEASE}" - rm -rf /opt/dispatcharr - fetch_and_deploy_gh_release "dispatcharr" "Dispatcharr/Dispatcharr" - chown -R "$APP_USER:$APP_GROUP" "$APP_DIR" - sed -i 's/program\[\x27channel_id\x27\]/program["channel_id"]/g' "${APP_DIR}/apps/output/views.py" - - msg_ok "Dispatcharr Updated to $RELEASE" - - msg_info "Creating Python Virtual Environment" - cd $APP_DIR - python3 -m venv env - source env/bin/activate - $STD pip install --upgrade pip - $STD pip install -r requirements.txt - $STD pip install gunicorn - ln -sf /usr/bin/ffmpeg $APP_DIR/env/bin/ffmpeg - msg_ok "Python Environment Setup" - - msg_info "Building Frontend" - cd $APP_DIR/frontend - $STD npm install --legacy-peer-deps - $STD npm run build - msg_ok "Built Frontend" - - msg_info "Running Django Migrations" - cd $APP_DIR - source env/bin/activate - set -o allexport - source /etc/$APP_NAME/$APP_NAME.env - set +o allexport - $STD python manage.py migrate --noinput - $STD python manage.py collectstatic --noinput - msg_ok "Migrations Complete" - - msg_info "Starting $APP" - systemctl start dispatcharr-celery - systemctl start dispatcharr-celerybeat - systemctl start dispatcharr-daphne - systemctl start dispatcharr - msg_ok "Started $APP" - echo "${RELEASE}" > "/opt/${APP}_version.txt" - - msg_info "Cleaning Up" - rm -rf /opt/$POSTGRES_DB-`date +%F`.sql - msg_ok "Cleanup Completed" - - msg_ok "Update Successful, Backup saved to $BACKUP_FILE" - - else - msg_ok "No update required. ${APP} is already at v${RELEASE}" - fi - exit } start diff --git a/ct/docspell.sh b/ct/docspell.sh index 93eb6806f..a9667ceaa 100644 --- a/ct/docspell.sh +++ b/ct/docspell.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { header_info diff --git a/ct/ente.sh b/ct/ente.sh index 3074d79bd..7f8b3132b 100644 --- a/ct/ente.sh +++ b/ct/ente.sh @@ -17,21 +17,21 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /var ]]; then - msg_error "No ${APP} Installation Found!" + header_info + check_container_storage + check_container_resources + if [[ ! -d /var ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating $APP LXC" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Updated $APP LXC" exit - fi - msg_info "Updating $APP LXC" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "Updated $APP LXC" - exit } start diff --git a/ct/freepbx.sh b/ct/freepbx.sh index d7526bcda..96f8dd029 100644 --- a/ct/freepbx.sh +++ b/ct/freepbx.sh @@ -18,44 +18,44 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { - header_info - check_container_storage - check_container_resources + header_info + check_container_storage + check_container_resources + + if [[ ! -f /lib/systemd/system/freepbx.service ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + msg_info "Updating $APP LXC" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Updated $APP LXC" + + msg_info "Updating $APP Modules" + $STD fwconsole ma updateall + $STD fwconsole reload + msg_ok "Updated $APP Modules" - if [[ ! -f /lib/systemd/system/freepbx.service ]]; then - msg_error "No ${APP} Installation Found!" exit - fi - - msg_info "Updating $APP LXC" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "Updated $APP LXC" - - msg_info "Updating $APP Modules" - $STD fwconsole ma updateall - $STD fwconsole reload - msg_ok "Updated $APP Modules" - - exit } start if whiptail --title "Commercial Modules" --yesno "Remove Commercial modules?" --defaultno 10 50; then - export ONLY_OPENSOURCE="yes" + export ONLY_OPENSOURCE="yes" - if whiptail --title "Firewall Module" --yesno "Do you want to KEEP the Firewall module (and sysadmin)?" 10 50; then - export REMOVE_FIREWALL="no" - else - export REMOVE_FIREWALL="yes" - fi + if whiptail --title "Firewall Module" --yesno "Do you want to KEEP the Firewall module (and sysadmin)?" 10 50; then + export REMOVE_FIREWALL="no" + else + export REMOVE_FIREWALL="yes" + fi else - export ONLY_OPENSOURCE="no" - export REMOVE_FIREWALL="no" + export ONLY_OPENSOURCE="no" + export REMOVE_FIREWALL="no" fi build_container diff --git a/ct/frigate.sh b/ct/frigate.sh index 479c6b98d..2cc5d1139 100644 --- a/ct/frigate.sh +++ b/ct/frigate.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-0}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { header_info diff --git a/ct/garmin-grafana.sh b/ct/garmin-grafana.sh index 7351ad005..f34fec36f 100644 --- a/ct/garmin-grafana.sh +++ b/ct/garmin-grafana.sh @@ -17,78 +17,78 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps # this only updates garmin-grafana, not influxdb or grafana, which are upgraded with apt function update_script() { - header_info - check_container_storage - check_container_resources + header_info + check_container_storage + check_container_resources - if [[ ! -d /opt/garmin-grafana/ ]]; then - msg_error "No ${APP} Installation Found!" + if [[ ! -d /opt/garmin-grafana/ ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + RELEASE=$(curl -fsSL https://api.github.com/repos/arpanghosh8453/garmin-grafana/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + if [[ ! -d /opt/garmin-grafana/ ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then + msg_info "Stopping $APP" + systemctl stop garmin-grafana + systemctl stop grafana-server + systemctl stop influxdb + msg_ok "Stopped $APP" + + if [[ ! -f /opt/garmin-grafana/.env ]]; then + msg_error "No .env file found in /opt/garmin-grafana/.env" + exit + fi + source /opt/garmin-grafana/.env + if [[ -z "${INFLUXDB_USER}" || -z "${INFLUXDB_PASSWORD}" || -z "${INFLUXDB_NAME}" ]]; then + msg_error "INFLUXDB_USER, INFLUXDB_PASSWORD, or INFLUXDB_NAME not set in .env file" + exit + fi + + msg_info "Creating Backup" + tar -czf "/opt/${APP}_backup_$(date +%F).tar.gz" /opt/garmin-grafana/.garminconnect /opt/garmin-grafana/.env + mv /opt/garmin-grafana/ /opt/garmin-grafana-backup/ + msg_ok "Backup Created" + + msg_info "Updating $APP to v${RELEASE}" + curl -fsSL -o "${RELEASE}.zip" "https://github.com/arpanghosh8453/garmin-grafana/archive/refs/tags/${RELEASE}.zip" + unzip -q "${RELEASE}.zip" + mv "garmin-grafana-${RELEASE}/" "/opt/garmin-grafana" + rm -f "${RELEASE}.zip" + $STD uv sync --locked --project /opt/garmin-grafana/ + # shellcheck disable=SC2016 + sed -i 's/\${DS_GARMIN_STATS}/garmin_influxdb/g' /opt/garmin-grafana/Grafana_Dashboard/Garmin-Grafana-Dashboard.json + sed -i 's/influxdb:8086/localhost:8086/' /opt/garmin-grafana/Grafana_Datasource/influxdb.yaml + sed -i "s/influxdb_user/${INFLUXDB_USER}/" /opt/garmin-grafana/Grafana_Datasource/influxdb.yaml + sed -i "s/influxdb_secret_password/${INFLUXDB_PASSWORD}/" /opt/garmin-grafana/Grafana_Datasource/influxdb.yaml + sed -i "s/GarminStats/${INFLUXDB_NAME}/" /opt/garmin-grafana/Grafana_Datasource/influxdb.yaml + # Copy across grafana data + cp -r /opt/garmin-grafana/Grafana_Datasource/* /etc/grafana/provisioning/datasources + cp -r /opt/garmin-grafana/Grafana_Dashboard/* /etc/grafana/provisioning/dashboards + # Copy back the env and token files + cp /opt/garmin-grafana-backup/.env /opt/garmin-grafana/.env + cp -r /opt/garmin-grafana-backup/.garminconnect /opt/garmin-grafana/.garminconnect + msg_ok "Updated $APP to v${RELEASE}" + + msg_info "Starting $APP" + systemctl start garmin-grafana + systemctl start grafana-server + systemctl start influxdb + msg_ok "Started $APP" + + msg_info "Cleaning Up" + rm -rf /opt/garmin-grafana-backup + msg_ok "Cleanup Completed" + + echo "${RELEASE}" >/opt/${APP}_version.txt + msg_ok "Update Successful" + else + msg_ok "No update required. ${APP} is already at v${RELEASE}" + fi exit - fi - - RELEASE=$(curl -fsSL https://api.github.com/repos/arpanghosh8453/garmin-grafana/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - if [[ ! -d /opt/garmin-grafana/ ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then - msg_info "Stopping $APP" - systemctl stop garmin-grafana - systemctl stop grafana-server - systemctl stop influxdb - msg_ok "Stopped $APP" - - if [[ ! -f /opt/garmin-grafana/.env ]]; then - msg_error "No .env file found in /opt/garmin-grafana/.env" - exit - fi - source /opt/garmin-grafana/.env - if [[ -z "${INFLUXDB_USER}" || -z "${INFLUXDB_PASSWORD}" || -z "${INFLUXDB_NAME}" ]]; then - msg_error "INFLUXDB_USER, INFLUXDB_PASSWORD, or INFLUXDB_NAME not set in .env file" - exit - fi - - msg_info "Creating Backup" - tar -czf "/opt/${APP}_backup_$(date +%F).tar.gz" /opt/garmin-grafana/.garminconnect /opt/garmin-grafana/.env - mv /opt/garmin-grafana/ /opt/garmin-grafana-backup/ - msg_ok "Backup Created" - - msg_info "Updating $APP to v${RELEASE}" - curl -fsSL -o "${RELEASE}.zip" "https://github.com/arpanghosh8453/garmin-grafana/archive/refs/tags/${RELEASE}.zip" - unzip -q "${RELEASE}.zip" - mv "garmin-grafana-${RELEASE}/" "/opt/garmin-grafana" - rm -f "${RELEASE}.zip" - $STD uv sync --locked --project /opt/garmin-grafana/ - # shellcheck disable=SC2016 - sed -i 's/\${DS_GARMIN_STATS}/garmin_influxdb/g' /opt/garmin-grafana/Grafana_Dashboard/Garmin-Grafana-Dashboard.json - sed -i 's/influxdb:8086/localhost:8086/' /opt/garmin-grafana/Grafana_Datasource/influxdb.yaml - sed -i "s/influxdb_user/${INFLUXDB_USER}/" /opt/garmin-grafana/Grafana_Datasource/influxdb.yaml - sed -i "s/influxdb_secret_password/${INFLUXDB_PASSWORD}/" /opt/garmin-grafana/Grafana_Datasource/influxdb.yaml - sed -i "s/GarminStats/${INFLUXDB_NAME}/" /opt/garmin-grafana/Grafana_Datasource/influxdb.yaml - # Copy across grafana data - cp -r /opt/garmin-grafana/Grafana_Datasource/* /etc/grafana/provisioning/datasources - cp -r /opt/garmin-grafana/Grafana_Dashboard/* /etc/grafana/provisioning/dashboards - # Copy back the env and token files - cp /opt/garmin-grafana-backup/.env /opt/garmin-grafana/.env - cp -r /opt/garmin-grafana-backup/.garminconnect /opt/garmin-grafana/.garminconnect - msg_ok "Updated $APP to v${RELEASE}" - - msg_info "Starting $APP" - systemctl start garmin-grafana - systemctl start grafana-server - systemctl start influxdb - msg_ok "Started $APP" - - msg_info "Cleaning Up" - rm -rf /opt/garmin-grafana-backup - msg_ok "Cleanup Completed" - - echo "${RELEASE}" >/opt/${APP}_version.txt - msg_ok "Update Successful" - else - msg_ok "No update required. ${APP} is already at v${RELEASE}" - fi - exit } start diff --git a/ct/ghostfolio.sh b/ct/ghostfolio.sh index 6595d5f76..d74aead6b 100644 --- a/ct/ghostfolio.sh +++ b/ct/ghostfolio.sh @@ -17,56 +17,56 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { - header_info - check_container_storage - check_container_resources + header_info + check_container_storage + check_container_resources - if [[ ! -f /opt/ghostfolio/dist/apps/api/main.js ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - msg_info "Stopping $APP" - systemctl stop ghostfolio - msg_ok "Stopped $APP" - - msg_info "Creating Backup" - tar -czf "/opt/ghostfolio_backup_$(date +%F).tar.gz" /opt/ghostfolio - msg_ok "Backup Created" - - msg_info "Updating $APP" - systemctl stop ghostfolio - - if [[ -d /opt/ghostfolio ]]; then - rm -rf /opt/ghostfolio_backup - mv /opt/ghostfolio /opt/ghostfolio_backup - fi - - if fetch_and_deploy_gh_release "ghostfolio" "ghostfolio/ghostfolio" "tarball" "latest" "/opt/ghostfolio"; then - cd /opt/ghostfolio - npm ci - npm run build:production - npx prisma migrate deploy - msg_ok "Updated $APP" - else - if [[ -d /opt/ghostfolio_backup ]]; then - rm -rf /opt/ghostfolio - mv /opt/ghostfolio_backup /opt/ghostfolio + if [[ ! -f /opt/ghostfolio/dist/apps/api/main.js ]]; then + msg_error "No ${APP} Installation Found!" + exit fi - msg_ok "No update required or update failed. ${APP} is up to date" - fi - msg_info "Starting $APP" - systemctl start ghostfolio - msg_ok "Started $APP" + msg_info "Stopping $APP" + systemctl stop ghostfolio + msg_ok "Stopped $APP" - msg_info "Cleaning Up" - npm cache clean --force - msg_ok "Cleanup Completed" - exit + msg_info "Creating Backup" + tar -czf "/opt/ghostfolio_backup_$(date +%F).tar.gz" /opt/ghostfolio + msg_ok "Backup Created" + + msg_info "Updating $APP" + systemctl stop ghostfolio + + if [[ -d /opt/ghostfolio ]]; then + rm -rf /opt/ghostfolio_backup + mv /opt/ghostfolio /opt/ghostfolio_backup + fi + + if fetch_and_deploy_gh_release "ghostfolio" "ghostfolio/ghostfolio" "tarball" "latest" "/opt/ghostfolio"; then + cd /opt/ghostfolio + npm ci + npm run build:production + npx prisma migrate deploy + msg_ok "Updated $APP" + else + if [[ -d /opt/ghostfolio_backup ]]; then + rm -rf /opt/ghostfolio + mv /opt/ghostfolio_backup /opt/ghostfolio + fi + msg_ok "No update required or update failed. ${APP} is up to date" + fi + + msg_info "Starting $APP" + systemctl start ghostfolio + msg_ok "Started $APP" + + msg_info "Cleaning Up" + npm cache clean --force + msg_ok "Cleanup Completed" + exit } start diff --git a/ct/globaleaks.sh b/ct/globaleaks.sh index f6c7a43be..16e60b1ac 100644 --- a/ct/globaleaks.sh +++ b/ct/globaleaks.sh @@ -16,21 +16,21 @@ var_version="${var_version:-12}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -f /usr/sbin/globaleaks ]]; then - msg_error "No ${APP} installation found!" - exit - fi + header_info + check_container_storage + check_container_resources + if [[ ! -f /usr/sbin/globaleaks ]]; then + msg_error "No ${APP} installation found!" + exit + fi - msg_info "Updating $APP LXC" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "Updated $APP LXC" + msg_info "Updating $APP LXC" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Updated $APP LXC" } start diff --git a/ct/hanko.sh b/ct/hanko.sh index de3079c26..53e295d27 100644 --- a/ct/hanko.sh +++ b/ct/hanko.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { header_info diff --git a/ct/joplin-server.sh b/ct/joplin-server.sh index fb6969cf7..02983e108 100644 --- a/ct/joplin-server.sh +++ b/ct/joplin-server.sh @@ -17,39 +17,39 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/joplin-server ]]; then - msg_error "No ${APP} Installation Found!" + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/joplin-server ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "joplin-server" "laurent22/joplin"; then + msg_info "Stopping Services" + systemctl stop joplin-server + msg_ok "Stopped Services" + + fetch_and_deploy_gh_release "joplin-server" "laurent22/joplin" "tarball" "latest" + + msg_info "Updating ${APP}" + cd /opt/joplin-server + sed -i "/onenote-converter/d" packages/lib/package.json + $STD yarn config set --home enableTelemetry 0 + export BUILD_SEQUENCIAL=1 + $STD yarn install --inline-builds + msg_ok "Updated $APP" + + msg_info "Starting Services" + systemctl start joplin-server + msg_ok "Started Services" + + msg_ok "Updated Successfully" + fi exit - fi - - if check_for_gh_release "joplin-server" "laurent22/joplin"; then - msg_info "Stopping Services" - systemctl stop joplin-server - msg_ok "Stopped Services" - - fetch_and_deploy_gh_release "joplin-server" "laurent22/joplin" "tarball" "latest" - - msg_info "Updating ${APP}" - cd /opt/joplin-server - sed -i "/onenote-converter/d" packages/lib/package.json - $STD yarn config set --home enableTelemetry 0 - export BUILD_SEQUENCIAL=1 - $STD yarn install --inline-builds - msg_ok "Updated $APP" - - msg_info "Starting Services" - systemctl start joplin-server - msg_ok "Started Services" - - msg_ok "Updated Successfully" - fi - exit } start diff --git a/ct/kanba.sh b/ct/kanba.sh index 270f5e00f..04f85acef 100644 --- a/ct/kanba.sh +++ b/ct/kanba.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { header_info diff --git a/ct/leantime.sh b/ct/leantime.sh index 6a0295715..e6fe9864d 100644 --- a/ct/leantime.sh +++ b/ct/leantime.sh @@ -17,39 +17,39 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { - header_info - check_container_storage - check_container_resources + header_info + check_container_storage + check_container_resources - if [[ ! -d /opt/leantime ]]; then - msg_error "No ${APP} Installation Found!" + if [[ ! -d /opt/leantime ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "leantime" "Leantime/leantime"; then + msg_info "Creating Backup" + mariadb-dump leantime >"/opt/${APP}_db_backup_$(date +%F).sql" + tar -czf "/opt/${APP}_backup_$(date +%F).tar.gz" "/opt/${APP}" + mv /opt/leantime /opt/leantime_bak + msg_ok "Backup Created" + + fetch_and_deploy_gh_release "leantime" "Leantime/leantime" "prebuild" "latest" "/opt/leantime" Leantime*.tar.gz + + msg_info "Restoring Config & Permissions" + mv /opt/leantime_bak/config/.env /opt/leantime/config/.env + chown -R www-data:www-data "/opt/leantime" + chmod -R 750 "/opt/leantime" + msg_ok "Restored Config & Permissions" + + msg_info "Removing Backup" + rm -rf /opt/leantime_bak + msg_ok "Removed Backup" + msg_ok "Updated Successfully" + fi exit - fi - - if check_for_gh_release "leantime" "Leantime/leantime"; then - msg_info "Creating Backup" - mariadb-dump leantime >"/opt/${APP}_db_backup_$(date +%F).sql" - tar -czf "/opt/${APP}_backup_$(date +%F).tar.gz" "/opt/${APP}" - mv /opt/leantime /opt/leantime_bak - msg_ok "Backup Created" - - fetch_and_deploy_gh_release "leantime" "Leantime/leantime" "prebuild" "latest" "/opt/leantime" Leantime*.tar.gz - - msg_info "Restoring Config & Permissions" - mv /opt/leantime_bak/config/.env /opt/leantime/config/.env - chown -R www-data:www-data "/opt/leantime" - chmod -R 750 "/opt/leantime" - msg_ok "Restored Config & Permissions" - - msg_info "Removing Backup" - rm -rf /opt/leantime_bak - msg_ok "Removed Backup" - msg_ok "Updated Successfully" - fi - exit } start diff --git a/ct/librenms.sh b/ct/librenms.sh index aaa71a1af..3acd3812f 100644 --- a/ct/librenms.sh +++ b/ct/librenms.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { header_info diff --git a/ct/livebook.sh b/ct/livebook.sh index 438c14f46..7c8f32762 100755 --- a/ct/livebook.sh +++ b/ct/livebook.sh @@ -17,38 +17,38 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { - header_info - check_container_storage - check_container_resources + header_info + check_container_storage + check_container_resources - if [[ ! -f /opt/livebook/.mix/escripts/livebook ]]; then - msg_error "No ${APP} Installation Found!" + if [[ ! -f /opt/livebook/.mix/escripts/livebook ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "livebook" "livebook-dev/livebook"; then + msg_info "Stopping ${APP}" + systemctl stop livebook + msg_info "Service stopped" + + msg_info "Updating container" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Updated container" + + msg_info "Updating ${APP}" + source /opt/livebook/.env + cd /opt/livebook + $STD mix escript.install hex livebook --force + + chown -R livebook:livebook /opt/livebook /data + systemctl start livebook + msg_ok "Updated ${APP}" + fi exit - fi - - if check_for_gh_release "livebook" "livebook-dev/livebook"; then - msg_info "Stopping ${APP}" - systemctl stop livebook - msg_info "Service stopped" - - msg_info "Updating container" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "Updated container" - - msg_info "Updating ${APP}" - source /opt/livebook/.env - cd /opt/livebook - $STD mix escript.install hex livebook --force - - chown -R livebook:livebook /opt/livebook /data - systemctl start livebook - msg_ok "Updated ${APP}" - fi - exit } start diff --git a/ct/manyfold.sh b/ct/manyfold.sh index 2d782d551..d5b464843 100644 --- a/ct/manyfold.sh +++ b/ct/manyfold.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { header_info diff --git a/ct/maxun.sh b/ct/maxun.sh index a5561dc56..78a865fab 100644 --- a/ct/maxun.sh +++ b/ct/maxun.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { header_info diff --git a/ct/notesnook.sh b/ct/notesnook.sh index 3d1fbb3cf..ddb87f824 100644 --- a/ct/notesnook.sh +++ b/ct/notesnook.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { header_info diff --git a/ct/npmplus.sh b/ct/npmplus.sh index 10a2fe4e6..e6bb0b985 100644 --- a/ct/npmplus.sh +++ b/ct/npmplus.sh @@ -17,35 +17,35 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { - UPD=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "UPDATE MODE" --radiolist --cancel-button Exit-Script "Spacebar = Select" 14 60 2 \ - "1" "Check for Alpine Updates" OFF \ - "2" "Update NPMplus Docker Container" ON \ - 3>&1 1>&2 2>&3) + UPD=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "UPDATE MODE" --radiolist --cancel-button Exit-Script "Spacebar = Select" 14 60 2 \ + "1" "Check for Alpine Updates" OFF \ + "2" "Update NPMplus Docker Container" ON \ + 3>&1 1>&2 2>&3) - header_info "$APP" + header_info "$APP" - case "$UPD" in - "1") - msg_info "Updating Alpine OS" - $STD apk -U upgrade - msg_ok "System updated" - exit - ;; - "2") - msg_info "Updating NPMplus Container" - cd /opt - msg_info "Pulling latest container image" - $STD docker compose pull - msg_info "Recreating container" - $STD docker compose up -d - msg_ok "NPMplus container updated" - exit - ;; - esac - exit 0 + case "$UPD" in + "1") + msg_info "Updating Alpine OS" + $STD apk -U upgrade + msg_ok "System updated" + exit + ;; + "2") + msg_info "Updating NPMplus Container" + cd /opt + msg_info "Pulling latest container image" + $STD docker compose pull + msg_info "Recreating container" + $STD docker compose up -d + msg_ok "NPMplus container updated" + exit + ;; + esac + exit 0 } start diff --git a/ct/opencloud.sh b/ct/opencloud.sh index 2c428f93c..e97c3af38 100644 --- a/ct/opencloud.sh +++ b/ct/opencloud.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { header_info diff --git a/ct/postiz.sh b/ct/postiz.sh index e53553677..32ec0ac34 100644 --- a/ct/postiz.sh +++ b/ct/postiz.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { header_info diff --git a/ct/proxmox-datacenter-manager.sh b/ct/proxmox-datacenter-manager.sh index 19cf1328e..577963832 100644 --- a/ct/proxmox-datacenter-manager.sh +++ b/ct/proxmox-datacenter-manager.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { header_info @@ -30,7 +30,7 @@ function update_script() { if grep -q 'Debian GNU/Linux 12' /etc/os-release && [ -f /etc/apt/sources.list.d/proxmox-release-bookworm.list ] && [ -f /etc/apt/sources.list.d/pdm-test.list ]; then msg_info "Updating outdated outdated source formats" - echo "deb [signed-by=/usr/share/keyrings/proxmox-archive-keyring.gpg] http://download.proxmox.com/debian/pdm bookworm pdm-test" > /etc/apt/sources.list.d/pdm-test.list + echo "deb [signed-by=/usr/share/keyrings/proxmox-archive-keyring.gpg] http://download.proxmox.com/debian/pdm bookworm pdm-test" >/etc/apt/sources.list.d/pdm-test.list curl -fsSL https://enterprise.proxmox.com/debian/proxmox-archive-keyring-trixie.gpg -o /usr/share/keyrings/proxmox-archive-keyring.gpg rm -f /etc/apt/keyrings/proxmox-release-bookworm.gpg /etc/apt/sources.list.d/proxmox-release-bookworm.list $STD apt-get update diff --git a/ct/romm.sh b/ct/romm.sh index 8dc95b587..771aed1d3 100644 --- a/ct/romm.sh +++ b/ct/romm.sh @@ -18,48 +18,48 @@ var_fuse="${var_fuse:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { - header_info - check_container_storage - check_container_resources + header_info + check_container_storage + check_container_resources - if [[ ! -d /opt/romm ]]; then - msg_error "No ${APP} Installation Found!" + if [[ ! -d /opt/romm ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + msg_info "Stopping $APP" + systemctl stop romm + systemctl stop nginx + msg_ok "Stopped $APP" + + msg_info "Updating $APP" + cd /opt/romm/app + git pull + + # Update backend + cd /opt/romm/app + source /opt/romm/venv/bin/activate + pip install --upgrade pip + pip install poetry + poetry install + + # Update frontend + cd /opt/romm/app/frontend + npm install + npm run build + + echo "Updated on $(date)" >/opt/romm/version.txt + msg_ok "Updated $APP" + + msg_info "Starting $APP" + systemctl start romm + systemctl start nginx + msg_ok "Started $APP" + msg_ok "Update Successful" exit - fi - - msg_info "Stopping $APP" - systemctl stop romm - systemctl stop nginx - msg_ok "Stopped $APP" - - msg_info "Updating $APP" - cd /opt/romm/app - git pull - - # Update backend - cd /opt/romm/app - source /opt/romm/venv/bin/activate - pip install --upgrade pip - pip install poetry - poetry install - - # Update frontend - cd /opt/romm/app/frontend - npm install - npm run build - - echo "Updated on $(date)" >/opt/romm/version.txt - msg_ok "Updated $APP" - - msg_info "Starting $APP" - systemctl start romm - systemctl start nginx - msg_ok "Started $APP" - msg_ok "Update Successful" - exit } start diff --git a/ct/rybbit.sh b/ct/rybbit.sh index e523bde2b..6e3caddd6 100644 --- a/ct/rybbit.sh +++ b/ct/rybbit.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { header_info diff --git a/ct/scraparr.sh b/ct/scraparr.sh index e5ba622a3..bca478dfc 100644 --- a/ct/scraparr.sh +++ b/ct/scraparr.sh @@ -17,40 +17,40 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { - header_info - check_container_storage - check_container_resources + header_info + check_container_storage + check_container_resources - if [[ ! -d /opt/scraparr/ ]]; then - msg_error "No ${APP} Installation Found!" + if [[ ! -d /opt/scraparr/ ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + if check_for_gh_release "scraparr" "thecfu/scraparr"; then + msg_info "Stopping Services" + systemctl stop scraparr + msg_ok "Services Stopped" + + PYTHON_VERSION="3.12" setup_uv + fetch_and_deploy_gh_release "scrappar" "thecfu/scraparr" "tarball" "latest" "/opt/scraparr" + + msg_info "Updating Scraparr" + cd /opt/scraparr + $STD uv venv /opt/scraparr/.venv + $STD /opt/scraparr/.venv/bin/python -m ensurepip --upgrade + $STD /opt/scraparr/.venv/bin/python -m pip install --upgrade pip + $STD /opt/scraparr/.venv/bin/python -m pip install -r /opt/scraparr/src/scraparr/requirements.txt + chmod -R 755 /opt/scraparr + msg_ok "Updated Scraparr" + + msg_info "Starting Services" + systemctl start scraparr + msg_ok "Services Started" + msg_ok "Updated Successfully" + fi exit - fi - if check_for_gh_release "scraparr" "thecfu/scraparr"; then - msg_info "Stopping Services" - systemctl stop scraparr - msg_ok "Services Stopped" - - PYTHON_VERSION="3.12" setup_uv - fetch_and_deploy_gh_release "scrappar" "thecfu/scraparr" "tarball" "latest" "/opt/scraparr" - - msg_info "Updating Scraparr" - cd /opt/scraparr - $STD uv venv /opt/scraparr/.venv - $STD /opt/scraparr/.venv/bin/python -m ensurepip --upgrade - $STD /opt/scraparr/.venv/bin/python -m pip install --upgrade pip - $STD /opt/scraparr/.venv/bin/python -m pip install -r /opt/scraparr/src/scraparr/requirements.txt - chmod -R 755 /opt/scraparr - msg_ok "Updated Scraparr" - - msg_info "Starting Services" - systemctl start scraparr - msg_ok "Services Started" - msg_ok "Updated Successfully" - fi - exit } start diff --git a/ct/signoz.sh b/ct/signoz.sh index 936ac4ccf..3e7d5bcc4 100644 --- a/ct/signoz.sh +++ b/ct/signoz.sh @@ -17,41 +17,41 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/signoz ]]; then - msg_error "No ${APP} Installation Found!" + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/signoz ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "signoz" "SigNoz/signoz"; then + msg_info "Stopping Services" + systemctl stop signoz + systemctl stop signoz-otel-collector + msg_ok "Stopped Services" + + fetch_and_deploy_gh_release "signoz" "SigNoz/signoz" "prebuild" "latest" "/opt/signoz" "signoz-community_linux_amd64.tar.gz" + fetch_and_deploy_gh_release "signoz-otel-collector" "SigNoz/signoz-otel-collector" "prebuild" "latest" "/opt/signoz-otel-collector" "signoz-otel-collector_linux_amd64.tar.gz" + fetch_and_deploy_gh_release "signoz-schema-migrator" "SigNoz/signoz-otel-collector" "prebuild" "latest" "/opt/signoz-schema-migrator" "signoz-schema-migrator_linux_amd64.tar.gz" + + msg_info "Updating ${APP}" + cd /opt/signoz-schema-migrator/bin + $STD ./signoz-schema-migrator sync --dsn="tcp://localhost:9000?password=" --replication=true --up= + $STD ./signoz-schema-migrator async --dsn="tcp://localhost:9000?password=" --replication=true --up= + msg_ok "Updated $APP" + + msg_info "Starting Services" + systemctl start signoz-otel-collector + systemctl start signoz + msg_ok "Started Services" + + msg_ok "Updated Successfully" + fi exit - fi - - if check_for_gh_release "signoz" "SigNoz/signoz"; then - msg_info "Stopping Services" - systemctl stop signoz - systemctl stop signoz-otel-collector - msg_ok "Stopped Services" - - fetch_and_deploy_gh_release "signoz" "SigNoz/signoz" "prebuild" "latest" "/opt/signoz" "signoz-community_linux_amd64.tar.gz" - fetch_and_deploy_gh_release "signoz-otel-collector" "SigNoz/signoz-otel-collector" "prebuild" "latest" "/opt/signoz-otel-collector" "signoz-otel-collector_linux_amd64.tar.gz" - fetch_and_deploy_gh_release "signoz-schema-migrator" "SigNoz/signoz-otel-collector" "prebuild" "latest" "/opt/signoz-schema-migrator" "signoz-schema-migrator_linux_amd64.tar.gz" - - msg_info "Updating ${APP}" - cd /opt/signoz-schema-migrator/bin - $STD ./signoz-schema-migrator sync --dsn="tcp://localhost:9000?password=" --replication=true --up= - $STD ./signoz-schema-migrator async --dsn="tcp://localhost:9000?password=" --replication=true --up= - msg_ok "Updated $APP" - - msg_info "Starting Services" - systemctl start signoz-otel-collector - systemctl start signoz - msg_ok "Started Services" - - msg_ok "Updated Successfully" - fi - exit } start diff --git a/ct/tunarr.sh b/ct/tunarr.sh index a3941b1d6..9af3bebcf 100644 --- a/ct/tunarr.sh +++ b/ct/tunarr.sh @@ -17,53 +17,53 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/tunarr ]]; then - msg_error "No ${APP} Installation Found!" + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/tunarr ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + if check_for_gh_release "tunarr" "chrisbenincasa/tunarr"; then + msg_info "Stopping ${APP}" + systemctl stop tunarr + msg_ok "Stopped ${APP}" + + msg_info "Creating Backup" + tar -czf "/opt/${APP}_backup_$(date +%F).tar.gz" /usr/.local/share/tunarr + msg_ok "Backup Created" + + fetch_and_deploy_gh_release "tunarr" "chrisbenincasa/tunarr" "singlefile" "latest" "/opt/tunarr" "*linux-x64" + + msg_info "Starting ${APP}" + systemctl start tunarr + msg_ok "Started ${APP}" + + msg_ok "Updated Successfully" + fi + + if check_for_gh_release "ersatztv-ffmpeg" "ErsatzTV/ErsatzTV-ffmpeg"; then + msg_info "Stopping ${APP}" + systemctl stop tunarr + msg_ok "Stopped ${APP}" + + fetch_and_deploy_gh_release "ersatztv-ffmpeg" "ErsatzTV/ErsatzTV-ffmpeg" "prebuild" "latest" "/opt/ErsatzTV-ffmpeg" "*-linux64-gpl-7.1.tar.xz" + + msg_info "Set ErsatzTV-ffmpeg links" + chmod +x /opt/ErsatzTV-ffmpeg/bin/* + ln -sf /opt/ErsatzTV-ffmpeg/bin/ffmpeg /usr/local/bin/ffmpeg + ln -sf /opt/ErsatzTV-ffmpeg/bin/ffplay /usr/local/bin/ffplay + ln -sf /opt/ErsatzTV-ffmpeg/bin/ffprobe /usr/local/bin/ffprobe + msg_ok "ffmpeg links set" + + msg_info "Starting ${APP}" + systemctl start tunarr + msg_ok "Started ${APP}" + msg_ok "Updated Successfully" + fi exit - fi - if check_for_gh_release "tunarr" "chrisbenincasa/tunarr"; then - msg_info "Stopping ${APP}" - systemctl stop tunarr - msg_ok "Stopped ${APP}" - - msg_info "Creating Backup" - tar -czf "/opt/${APP}_backup_$(date +%F).tar.gz" /usr/.local/share/tunarr - msg_ok "Backup Created" - - fetch_and_deploy_gh_release "tunarr" "chrisbenincasa/tunarr" "singlefile" "latest" "/opt/tunarr" "*linux-x64" - - msg_info "Starting ${APP}" - systemctl start tunarr - msg_ok "Started ${APP}" - - msg_ok "Updated Successfully" - fi - - if check_for_gh_release "ersatztv-ffmpeg" "ErsatzTV/ErsatzTV-ffmpeg"; then - msg_info "Stopping ${APP}" - systemctl stop tunarr - msg_ok "Stopped ${APP}" - - fetch_and_deploy_gh_release "ersatztv-ffmpeg" "ErsatzTV/ErsatzTV-ffmpeg" "prebuild" "latest" "/opt/ErsatzTV-ffmpeg" "*-linux64-gpl-7.1.tar.xz" - - msg_info "Set ErsatzTV-ffmpeg links" - chmod +x /opt/ErsatzTV-ffmpeg/bin/* - ln -sf /opt/ErsatzTV-ffmpeg/bin/ffmpeg /usr/local/bin/ffmpeg - ln -sf /opt/ErsatzTV-ffmpeg/bin/ffplay /usr/local/bin/ffplay - ln -sf /opt/ErsatzTV-ffmpeg/bin/ffprobe /usr/local/bin/ffprobe - msg_ok "ffmpeg links set" - - msg_info "Starting ${APP}" - systemctl start tunarr - msg_ok "Started ${APP}" - msg_ok "Updated Successfully" - fi - exit } start diff --git a/ct/ubuntu.sh b/ct/ubuntu.sh index f208d7d09..5bae645b9 100644 --- a/ct/ubuntu.sh +++ b/ct/ubuntu.sh @@ -19,7 +19,7 @@ var_unprivileged="${var_unprivileged:-0}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { header_info diff --git a/ct/viseron.sh b/ct/viseron.sh index ef0f4a98b..6534ff2eb 100644 --- a/ct/viseron.sh +++ b/ct/viseron.sh @@ -14,18 +14,18 @@ header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -f /etc/systemd/system/viseron.service ]]; then - msg_error "No ${APP} Installation Found!" + header_info + check_container_storage + check_container_resources + if [[ ! -f /etc/systemd/system/viseron.service ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_error "To update Viseron, create a new container and transfer your configuration." exit - fi - msg_error "To update Viseron, create a new container and transfer your configuration." - exit } start diff --git a/ct/wallabag.sh b/ct/wallabag.sh index 9325189a7..910948ff6 100644 --- a/ct/wallabag.sh +++ b/ct/wallabag.sh @@ -22,7 +22,7 @@ base_settings # Core variables color -catch_errors +init_error_traps function update_script() { header_info From eed916b6cc4a516bc617e015314edb85c3149732 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 15 Sep 2025 15:38:03 +0200 Subject: [PATCH 0917/1733] test --- install/alpine-garage-install.sh | 4 +- install/alpine-install.sh | 2 +- install/debian-install.sh | 2 +- install/deferred/ampache-install.sh | 2 +- install/deferred/freepbx-install_backup.sh | 114 +++++++++--------- install/deferred/funkwhale-install.sh | 92 +++++++------- install/deferred/ghostfolio-install.sh | 8 +- install/deferred/hoodik-install.sh | 26 ++-- install/deferred/jumpserver-install.sh | 4 +- install/deferred/kasm-install.sh | 2 +- install/deferred/koel-install.sh | 2 +- install/deferred/netbootxyz-install.sh | 8 +- install/deferred/nginxproxymanager-install.sh | 76 ++++++------ install/deferred/nimbus-install.sh | 16 +-- install/deferred/ocis-install.sh | 2 +- install/deferred/openwebui-install.sh | 2 +- install/deferred/pixelfed-install.sh | 2 +- install/deferred/polaris-install.sh | 16 +-- install/deferred/roundcubemail-install.sh | 24 ++-- .../squirrelserversmanager-install.sh | 14 +-- install/deferred/timescaledb-install.sh | 61 +++++----- install/deferred/vikunja-install.sh | 2 +- install/dispatcharr-install.sh | 32 ++--- install/docspell-install.sh | 17 ++- install/ente-install.sh | 24 ++-- install/freepbx-install.sh | 112 ++++++++--------- install/frigate-install.sh | 34 +++--- install/garmin-grafana-install.sh | 42 +++---- install/ghostfolio-install.sh | 46 +++---- install/globaleaks-install.sh | 2 +- install/hanko-install.sh | 12 +- install/joplin-server-install.sh | 14 +-- install/kanba-install.sh | 10 +- install/leantime-install.sh | 18 +-- install/librenms-install.sh | 36 +++--- install/livebook-install.sh | 6 +- install/manyfold-install.sh | 30 ++--- install/maxun-install.sh | 70 +++++------ install/notesnook-install.sh | 8 +- install/npmplus-install.sh | 100 +++++++-------- install/opencloud-install.sh | 8 +- install/postiz-install.sh | 24 ++-- install/proxmox-datacenter-manager-install.sh | 8 +- install/romm-install.sh | 56 ++++----- install/rybbit-install.sh | 17 ++- install/scraparr-install.sh | 2 +- install/signoz-install.sh | 12 +- install/tunarr-install.sh | 24 ++-- install/ubuntu-install.sh | 2 +- install/viseron-install.sh | 40 +++--- install/wallabag-install.sh | 30 ++--- 51 files changed, 656 insertions(+), 661 deletions(-) diff --git a/install/alpine-garage-install.sh b/install/alpine-garage-install.sh index 126401d9a..c9ca62e5c 100644 --- a/install/alpine-garage-install.sh +++ b/install/alpine-garage-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os @@ -46,7 +46,7 @@ msg_ok "Setup Garage packages" msg_info "Writing config" if [[ ! -f /etc/garage.toml ]]; then - cat >/etc/garage.toml </etc/garage.toml <>~/funkwhale.creds echo -e "Funkwhale Database User: \e[32m$DB_USER\e[0m" >>~/funkwhale.creds echo -e "Funkwhale Database Password: \e[32m$DB_PASS\e[0m" >>~/funkwhale.creds @@ -130,26 +130,26 @@ msg_ok "Funkwhale successfully set up" read -r -p "Would you like to Setup Reverse Proxy (Nginx)? " prompt if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then - msg_info "Installing NGINX" - $STD apt install -y nginx - sudo su - $STD curl -L -o /etc/nginx/funkwhale_proxy.conf "https://dev.funkwhale.audio/funkwhale/funkwhale/raw/$FUNKWHALE_VERSION/deploy/funkwhale_proxy.conf" - $STD curl -L -o /etc/nginx/sites-available/funkwhale.template "https://dev.funkwhale.audio/funkwhale/funkwhale/raw/$FUNKWHALE_VERSION/deploy/nginx.template" - $STD set -a && source /opt/funkwhale/config/.env && set +a envsubst "`env | awk -F = '{printf \" $%s\", $$1}'`" \ - < /etc/nginx/sites-available/funkwhale.template \ - > /etc/nginx/sites-available/funkwhale.conf - $STD grep '${' /etc/nginx/sites-available/funkwhale.conf - $STD ln -s /etc/nginx/sites-available/funkwhale.conf /etc/nginx/sites-enabled/ - $STD systemctl reload nginx - msg_ok "Installed Nginx" + msg_info "Installing NGINX" + $STD apt install -y nginx + sudo su + $STD curl -L -o /etc/nginx/funkwhale_proxy.conf "https://dev.funkwhale.audio/funkwhale/funkwhale/raw/$FUNKWHALE_VERSION/deploy/funkwhale_proxy.conf" + $STD curl -L -o /etc/nginx/sites-available/funkwhale.template "https://dev.funkwhale.audio/funkwhale/funkwhale/raw/$FUNKWHALE_VERSION/deploy/nginx.template" + $STD set -a && source /opt/funkwhale/config/.env && set +a envsubst "$(env | awk -F = '{printf \" $%s\", $$1}')" \ + /etc/nginx/sites-available/funkwhale.conf + $STD grep '${' /etc/nginx/sites-available/funkwhale.conf + $STD ln -s /etc/nginx/sites-available/funkwhale.conf /etc/nginx/sites-enabled/ + $STD systemctl reload nginx + msg_ok "Installed Nginx" fi read -r -p "Would you like to Setup TLS (Certbot)? " prompt if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then - msg_info "Installing Certbot" - $STD apt install -y certbot python3-certbot-nginx - $STD sudo certbot --nginx -d $FUNKWHALE_HOSTNAME - msg_ok "Installed Certbot" + msg_info "Installing Certbot" + $STD apt install -y certbot python3-certbot-nginx + $STD sudo certbot --nginx -d $FUNKWHALE_HOSTNAME + msg_ok "Installed Certbot" fi motd_ssh diff --git a/install/deferred/ghostfolio-install.sh b/install/deferred/ghostfolio-install.sh index e834d734f..91e27db27 100644 --- a/install/deferred/ghostfolio-install.sh +++ b/install/deferred/ghostfolio-install.sh @@ -6,10 +6,10 @@ # License: MIT # https://github.com/tteck/Proxmox/raw/main/LICENSE -source /dev/stdin <<< "$FUNCTIONS_FILE_PATH" +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os @@ -71,7 +71,7 @@ REDIS_PORT=6379 REDIS_PASSWORD="$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 32)" $STD redis-cli CONFIG SET requirepass "$REDIS_PASSWORD" -$STD redis-cli -a "$REDIS_PASSWORD" CONFIG REWRITE +$STD redis-cli -a "$REDIS_PASSWORD" CONFIG REWRITE $STD systemctl restart redis echo "" >>~/ghostfolio.creds echo "Ghostfolio Redis Credentials" >>~/ghostfolio.creds @@ -176,4 +176,4 @@ customize msg_info "Cleaning up" $STD apt-get -y autoremove $STD apt-get -y autoclean -msg_ok "Cleaned" \ No newline at end of file +msg_ok "Cleaned" diff --git a/install/deferred/hoodik-install.sh b/install/deferred/hoodik-install.sh index bf40b47f0..51ed398f1 100644 --- a/install/deferred/hoodik-install.sh +++ b/install/deferred/hoodik-install.sh @@ -9,25 +9,25 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os msg_info "Installing Dependencies (Patience)" $STD apt-get install -y \ - pkg-config \ - libssl-dev \ - libc6-dev \ - libpq-dev \ - clang \ - llvm \ - nettle-dev \ - build-essential \ - curl \ - sudo \ - make \ - mc + pkg-config \ + libssl-dev \ + libc6-dev \ + libpq-dev \ + clang \ + llvm \ + nettle-dev \ + build-essential \ + curl \ + sudo \ + make \ + mc msg_ok "Installed Dependencies" msg_info "Installing Rust (Patience)" diff --git a/install/deferred/jumpserver-install.sh b/install/deferred/jumpserver-install.sh index 119bad3b8..23203327c 100644 --- a/install/deferred/jumpserver-install.sh +++ b/install/deferred/jumpserver-install.sh @@ -8,14 +8,14 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - iptables + iptables msg_ok "Installed Dependencies" msg_info "Installing JumpServer" diff --git a/install/deferred/kasm-install.sh b/install/deferred/kasm-install.sh index 236d15ba8..e55cd17c4 100644 --- a/install/deferred/kasm-install.sh +++ b/install/deferred/kasm-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os diff --git a/install/deferred/koel-install.sh b/install/deferred/koel-install.sh index 38c14d5fa..4f0336516 100644 --- a/install/deferred/koel-install.sh +++ b/install/deferred/koel-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os diff --git a/install/deferred/netbootxyz-install.sh b/install/deferred/netbootxyz-install.sh index 933167c35..e38772932 100644 --- a/install/deferred/netbootxyz-install.sh +++ b/install/deferred/netbootxyz-install.sh @@ -5,10 +5,10 @@ # License: MIT # https://github.com/tteck/Proxmox/raw/main/LICENSE -source /dev/stdin <<< "$FUNCTIONS_FILE_PATH" +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os @@ -22,8 +22,8 @@ msg_info "Installing netboot.xyz ${RELEASE}" $STD curl --silent -o ${RELEASE}.tar.gz -L "https://github.com/netbootxyz/netboot.xyz/archive/${RELEASE}.tar.gz" $STD tar xvzf ${RELEASE}.tar.gz VER=$(curl -s https://api.github.com/repos/netbootxyz/netboot.xyz/releases/latest | - grep "tag_name" | - awk '{print substr($2, 2, length($2)-3) }') + grep "tag_name" | + awk '{print substr($2, 2, length($2)-3) }') rm -rf ${RELEASE}.tar.gz mv netboot.xyz-${VER} /opt/netboot.xyz msg_ok "Installed netboot.xyz ${RELEASE}" diff --git a/install/deferred/nginxproxymanager-install.sh b/install/deferred/nginxproxymanager-install.sh index 0169c5d04..3fd212796 100644 --- a/install/deferred/nginxproxymanager-install.sh +++ b/install/deferred/nginxproxymanager-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os @@ -16,29 +16,29 @@ update_os msg_info "Installing Dependencies" $STD apt-get update $STD apt-get -y install \ - sudo \ - mc \ - curl \ - gnupg \ - make \ - gcc \ - g++ \ - ca-certificates \ - apache2-utils \ - logrotate \ - build-essential \ - git + sudo \ + mc \ + curl \ + gnupg \ + make \ + gcc \ + g++ \ + ca-certificates \ + apache2-utils \ + logrotate \ + build-essential \ + git msg_ok "Installed Dependencies" msg_info "Installing Python3" $STD apt-get install -y \ - python3 \ - python3-dev \ - python3-pip \ - python3-venv \ - python3-cffi \ - python3-certbot \ - python3-certbot-dns-cloudflare + python3 \ + python3-dev \ + python3-pip \ + python3-venv \ + python3-cffi \ + python3-certbot \ + python3-certbot-dns-cloudflare $STD pip3 install certbot-dns-multi $STD python3 -m venv /opt/certbot/ rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED @@ -76,7 +76,7 @@ sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" frontend/package.js sed -i 's+^daemon+#daemon+g' docker/rootfs/etc/nginx/nginx.conf NGINX_CONFS=$(find "$(pwd)" -type f -name "*.conf") for NGINX_CONF in $NGINX_CONFS; do - sed -i 's+include conf.d+include /etc/nginx/conf.d+g' "$NGINX_CONF" + sed -i 's+include conf.d+include /etc/nginx/conf.d+g' "$NGINX_CONF" done mkdir -p /var/www/html /etc/nginx/logs @@ -88,21 +88,21 @@ ln -sf /etc/nginx/nginx.conf /etc/nginx/conf/nginx.conf rm -f /etc/nginx/conf.d/dev.conf mkdir -p /tmp/nginx/body \ - /run/nginx \ - /data/nginx \ - /data/custom_ssl \ - /data/logs \ - /data/access \ - /data/nginx/default_host \ - /data/nginx/default_www \ - /data/nginx/proxy_host \ - /data/nginx/redirection_host \ - /data/nginx/stream \ - /data/nginx/dead_host \ - /data/nginx/temp \ - /var/lib/nginx/cache/public \ - /var/lib/nginx/cache/private \ - /var/cache/nginx/proxy_temp + /run/nginx \ + /data/nginx \ + /data/custom_ssl \ + /data/logs \ + /data/access \ + /data/nginx/default_host \ + /data/nginx/default_www \ + /data/nginx/proxy_host \ + /data/nginx/redirection_host \ + /data/nginx/stream \ + /data/nginx/dead_host \ + /data/nginx/temp \ + /var/lib/nginx/cache/public \ + /var/lib/nginx/cache/private \ + /var/cache/nginx/proxy_temp chmod -R 777 /var/cache/nginx chown root /tmp/nginx @@ -110,7 +110,7 @@ chown root /tmp/nginx echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" {print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf);" >/etc/nginx/conf.d/include/resolvers.conf if [ ! -f /data/nginx/dummycert.pem ] || [ ! -f /data/nginx/dummykey.pem ]; then - openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 -subj "/O=Nginx Proxy Manager/OU=Dummy Certificate/CN=localhost" -keyout /data/nginx/dummykey.pem -out /data/nginx/dummycert.pem &>/dev/null + openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 -subj "/O=Nginx Proxy Manager/OU=Dummy Certificate/CN=localhost" -keyout /data/nginx/dummykey.pem -out /data/nginx/dummycert.pem &>/dev/null fi mkdir -p /app/global /app/frontend/images @@ -130,7 +130,7 @@ msg_ok "Built Frontend" msg_info "Initializing Backend" rm -rf /app/config/default.json if [ ! -f /app/config/production.json ]; then - cat <<'EOF' >/app/config/production.json + cat <<'EOF' >/app/config/production.json { "database": { "engine": "knex-native", diff --git a/install/deferred/nimbus-install.sh b/install/deferred/nimbus-install.sh index 9cbeead47..5940020c4 100644 --- a/install/deferred/nimbus-install.sh +++ b/install/deferred/nimbus-install.sh @@ -8,16 +8,16 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os msg_info "Installing dependencies" $STD apt-get install -y \ - build-essential \ - openssl \ - git + build-essential \ + openssl \ + git msg_ok "Installed Dependencies" msg_info "Installing Bun" @@ -38,10 +38,10 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8' $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC'" { - echo "Nimbus-Credentials" - echo "Nimbus Database User: $DB_USER" - echo "Nimbus Database Password: $DB_PASS" - echo "Nimbus Database Name: $DB_NAME" + echo "Nimbus-Credentials" + echo "Nimbus Database User: $DB_USER" + echo "Nimbus Database Password: $DB_PASS" + echo "Nimbus Database Name: $DB_NAME" } >>~/nimbus.creds msg_ok "Set up PostgreSQL Database" diff --git a/install/deferred/ocis-install.sh b/install/deferred/ocis-install.sh index 987b0397e..98be246e1 100644 --- a/install/deferred/ocis-install.sh +++ b/install/deferred/ocis-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os diff --git a/install/deferred/openwebui-install.sh b/install/deferred/openwebui-install.sh index 0e9384d8f..9eb6c3c08 100644 --- a/install/deferred/openwebui-install.sh +++ b/install/deferred/openwebui-install.sh @@ -9,7 +9,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os diff --git a/install/deferred/pixelfed-install.sh b/install/deferred/pixelfed-install.sh index 0996ec330..526922a45 100644 --- a/install/deferred/pixelfed-install.sh +++ b/install/deferred/pixelfed-install.sh @@ -9,7 +9,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os diff --git a/install/deferred/polaris-install.sh b/install/deferred/polaris-install.sh index 76577e367..f9f70c6ed 100644 --- a/install/deferred/polaris-install.sh +++ b/install/deferred/polaris-install.sh @@ -8,20 +8,20 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - make \ - git \ - build-essential \ - binutils \ - pkg-config \ - libsqlite3-dev \ - libssl-dev + make \ + git \ + build-essential \ + binutils \ + pkg-config \ + libsqlite3-dev \ + libssl-dev msg_ok "Installed Dependencies" msg_info "Installing Rust" diff --git a/install/deferred/roundcubemail-install.sh b/install/deferred/roundcubemail-install.sh index 16c4ce65f..40982d9ca 100644 --- a/install/deferred/roundcubemail-install.sh +++ b/install/deferred/roundcubemail-install.sh @@ -7,24 +7,24 @@ # https://github.com/tteck/Proxmox/raw/main/LICENSE # Source: https://github.com/roundcube/roundcubemail -source /dev/stdin <<< "$FUNCTIONS_FILE_PATH" +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - curl \ - sudo \ - mc \ - postgresql \ - apache2 \ - libapache2-mod-php \ - composer \ - php8.2-{mbstring,gd,imap,mysql,ldap,curl,intl,imagick,bz2,sqlite3,zip,xml} + curl \ + sudo \ + mc \ + postgresql \ + apache2 \ + libapache2-mod-php \ + composer \ + php8.2-{mbstring,gd,imap,mysql,ldap,curl,intl,imagick,bz2,sqlite3,zip,xml} msg_ok "Installed Dependencies" msg_info "Setting up PostgreSQL" @@ -74,7 +74,7 @@ $STD sudo a2enmod deflate $STD sudo a2enmod expires $STD sudo a2enmod headers $STD a2ensite roundcubemail.conf -$STD a2dissite 000-default.conf +$STD a2dissite 000-default.conf $STD systemctl reload apache2 msg_ok "Installed Wallos" @@ -85,4 +85,4 @@ msg_info "Cleaning up" rm -rf /opt/roundcubemail-${RELEASE}-complete.tar.gz $STD apt-get -y autoremove $STD apt-get -y autoclean -msg_ok "Cleaned" \ No newline at end of file +msg_ok "Cleaned" diff --git a/install/deferred/squirrelserversmanager-install.sh b/install/deferred/squirrelserversmanager-install.sh index 2bbda264a..86eab5fb1 100644 --- a/install/deferred/squirrelserversmanager-install.sh +++ b/install/deferred/squirrelserversmanager-install.sh @@ -4,11 +4,11 @@ # Author: tteck (tteckster) # License: MIT # https://github.com/tteck/Proxmox/raw/main/LICENSE -source /dev/stdin <<< "$FUNCTIONS_FILE_PATH" +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os @@ -16,7 +16,7 @@ update_os # Generate a random string generate_random_string() { local LENGTH=$1 - tr -dc A-Za-z0-9 /dev/null || true + tr -dc A-Za-z0-9 /dev/null || true } msg_info "Installing Dependencies" @@ -46,7 +46,7 @@ msg_ok "Installed Redis" msg_info "Installing Nginx" $STD apk add nginx rm -rf /etc/nginx/http.d/default.conf -cat <<'EOF'> /etc/nginx/http.d/default.conf +cat <<'EOF' >/etc/nginx/http.d/default.conf server { listen 80; server_name localhost; @@ -90,8 +90,8 @@ msg_ok "Installed Nginx" msg_info "Installing MongoDB Database" DB_NAME=ssm DB_PORT=27017 -echo 'http://dl-cdn.alpinelinux.org/alpine/v3.9/main' >> /etc/apk/repositories -echo 'http://dl-cdn.alpinelinux.org/alpine/v3.9/community' >> /etc/apk/repositories +echo 'http://dl-cdn.alpinelinux.org/alpine/v3.9/main' >>/etc/apk/repositories +echo 'http://dl-cdn.alpinelinux.org/alpine/v3.9/community' >>/etc/apk/repositories $STD apk update $STD apk add mongodb mongodb-tools msg_ok "Installed MongoDB Database" @@ -108,7 +108,7 @@ $STD git clone https://github.com/SquirrelCorporation/SquirrelServersManager.git SECRET=$(generate_random_string 32) SALT=$(generate_random_string 16) VAULT_PWD=$(generate_random_string 32) -cat < /opt/squirrelserversmanager/.env +cat </opt/squirrelserversmanager/.env # SECRETS SECRET=$SECRET SALT=$SALT diff --git a/install/deferred/timescaledb-install.sh b/install/deferred/timescaledb-install.sh index 7e01a7586..e5db32239 100644 --- a/install/deferred/timescaledb-install.sh +++ b/install/deferred/timescaledb-install.sh @@ -4,22 +4,22 @@ # Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -source /dev/stdin <<< "$FUNCTIONS_FILE_PATH" +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - curl \ - sudo \ - mc \ - gnupg \ - apt-transport-https \ - lsb-release + curl \ + sudo \ + mc \ + gnupg \ + apt-transport-https \ + lsb-release msg_ok "Installed Dependencies" msg_info "Setting up PostgreSQL Repository" @@ -60,10 +60,10 @@ cat </etc/postgresql/17/main/postgresql.conf # FILE LOCATIONS #------------------------------------------------------------------------------ -data_directory = '/var/lib/postgresql/17/main' -hba_file = '/etc/postgresql/17/main/pg_hba.conf' -ident_file = '/etc/postgresql/17/main/pg_ident.conf' -external_pid_file = '/var/run/postgresql/17-main.pid' +data_directory = '/var/lib/postgresql/17/main' +hba_file = '/etc/postgresql/17/main/pg_hba.conf' +ident_file = '/etc/postgresql/17/main/pg_ident.conf' +external_pid_file = '/var/run/postgresql/17-main.pid' #------------------------------------------------------------------------------ # CONNECTIONS AND AUTHENTICATION @@ -71,10 +71,10 @@ external_pid_file = '/var/run/postgresql/17-main.pid' # - Connection Settings - -listen_addresses = '*' -port = 5432 -max_connections = 100 -unix_socket_directories = '/var/run/postgresql' +listen_addresses = '*' +port = 5432 +max_connections = 100 +unix_socket_directories = '/var/run/postgresql' # - SSL - @@ -86,8 +86,8 @@ ssl_key_file = '/etc/ssl/private/ssl-cert-snakeoil.key' # RESOURCE USAGE (except WAL) #------------------------------------------------------------------------------ -shared_buffers = 128MB -dynamic_shared_memory_type = posix +shared_buffers = 128MB +dynamic_shared_memory_type = posix #------------------------------------------------------------------------------ # WRITE-AHEAD LOG @@ -102,14 +102,14 @@ min_wal_size = 80MB # - What to Log - -log_line_prefix = '%m [%p] %q%u@%d ' +log_line_prefix = '%m [%p] %q%u@%d ' log_timezone = 'Etc/UTC' #------------------------------------------------------------------------------ # PROCESS TITLE #------------------------------------------------------------------------------ -cluster_name = '17/main' +cluster_name = '17/main' #------------------------------------------------------------------------------ # CLIENT CONNECTION DEFAULTS @@ -119,23 +119,22 @@ cluster_name = '17/main' datestyle = 'iso, mdy' timezone = 'Etc/UTC' -lc_messages = 'C' -lc_monetary = 'C' -lc_numeric = 'C' -lc_time = 'C' +lc_messages = 'C' +lc_monetary = 'C' +lc_numeric = 'C' +lc_time = 'C' default_text_search_config = 'pg_catalog.english' #------------------------------------------------------------------------------ # CONFIG FILE INCLUDES #------------------------------------------------------------------------------ -include_dir = 'conf.d' +include_dir = 'conf.d' EOF systemctl restart postgresql msg_ok "Installed PostgreSQL" - msg_info "Setup TimescaleDB" echo "deb https://packagecloud.io/timescale/timescaledb/debian/ $(lsb_release -c -s) main" | sudo tee /etc/apt/sources.list.d/timescaledb.list wget --quiet -O - https://packagecloud.io/timescale/timescaledb/gpgkey | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/timescaledb.gpg @@ -147,11 +146,11 @@ msg_ok "Setup TimescaleDB" read -r -p "Would you like to add Adminer? " prompt if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then - msg_info "Installing Adminer" - $STD apt install -y adminer - $STD a2enconf adminer - systemctl reload apache2 - msg_ok "Installed Adminer" + msg_info "Installing Adminer" + $STD apt install -y adminer + $STD a2enconf adminer + systemctl reload apache2 + msg_ok "Installed Adminer" fi motd_ssh diff --git a/install/deferred/vikunja-install.sh b/install/deferred/vikunja-install.sh index f56d073d3..b51da342d 100644 --- a/install/deferred/vikunja-install.sh +++ b/install/deferred/vikunja-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os diff --git a/install/dispatcharr-install.sh b/install/dispatcharr-install.sh index 98d9435a9..17c236486 100644 --- a/install/dispatcharr-install.sh +++ b/install/dispatcharr-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os @@ -20,15 +20,15 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - build-essential \ - gcc \ - libpcre3-dev \ - libpq-dev \ - nginx \ - redis-server \ - ffmpeg \ - procps \ - streamlink + build-essential \ + gcc \ + libpcre3-dev \ + libpq-dev \ + nginx \ + redis-server \ + ffmpeg \ + procps \ + streamlink msg_ok "Installed Dependencies" PYTHON_VERSION="3.13" setup_uv @@ -47,10 +47,10 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8' $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" { - echo "Dispatcharr-Credentials" - echo "Dispatcharr Database Name: $DB_NAME" - echo "Dispatcharr Database User: $DB_USER" - echo "Dispatcharr Database Password: $DB_PASS" + echo "Dispatcharr-Credentials" + echo "Dispatcharr Database Name: $DB_NAME" + echo "Dispatcharr Database User: $DB_USER" + echo "Dispatcharr Database Password: $DB_PASS" } >>~/dispatcharr.creds msg_ok "Set up PostgreSQL Database" @@ -63,10 +63,10 @@ mapfile -t EXTRA_INDEX_URLS < <(grep -E '^(--(extra-)?index-url|-i)\s' requireme UV_INDEX_ARGS=(--index-url "$PYPI_URL" --index-strategy unsafe-best-match) for u in "${EXTRA_INDEX_URLS[@]}"; do - [[ -n "$u" && "$u" != "$PYPI_URL" ]] && UV_INDEX_ARGS+=(--extra-index-url "$u") + [[ -n "$u" && "$u" != "$PYPI_URL" ]] && UV_INDEX_ARGS+=(--extra-index-url "$u") done if [[ -f requirements.txt ]]; then - $STD uv pip install --system "${UV_INDEX_ARGS[@]}" -r requirements.txt + $STD uv pip install --system "${UV_INDEX_ARGS[@]}" -r requirements.txt fi $STD uv pip install --system "${UV_INDEX_ARGS[@]}" gunicorn gevent celery daphne ln -sf /usr/bin/ffmpeg /opt/dispatcharr/env/bin/ffmpeg diff --git a/install/docspell-install.sh b/install/docspell-install.sh index 8b963d18c..3cdf2b7cd 100644 --- a/install/docspell-install.sh +++ b/install/docspell-install.sh @@ -7,7 +7,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os @@ -23,15 +23,14 @@ $STD apt-get install -y \ ca-certificates \ apt-transport-https \ tesseract-ocr - #tesseract-ocr-deu \ - #tesseract-ocr-eng \ - #unpaper \ - #unoconv \ - #wkhtmltopdf \ - #ocrmypdf +#tesseract-ocr-deu \ +#tesseract-ocr-eng \ +#unpaper \ +#unoconv \ +#wkhtmltopdf \ +#ocrmypdf msg_ok "Installed Dependencies" - setup_gs JAVA_VERSION="21" setup_java POSTGRES_VERSION="16" setup_postgresql @@ -55,13 +54,11 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" } >>~/docspell.creds msg_ok "Set up PostgreSQL Database" - fetch_and_deploy_gh_release "docspell-joex" "eikek/docspell" "binary" "latest" "/opt/docspell-joex" "docspell-joex_*all.deb" fetch_and_deploy_gh_release "docspell-restserver" "eikek/docspell" "binary" "latest" "/opt/docspell-restserver" "docspell-restserver_*all.deb" fetch_and_deploy_gh_release "docspell-dsc" "docspell/dsc" "singlefile" "latest" "/usr/bin" "dsc" fetch_and_deploy_gh_release "apache-solr" "apache/solr" "tarball" "latest" "/opt/docspell" - msg_info "Setup Docspell" ln -s /etc/docspell-joex /opt/docspell/docspell-joex && ln -s /etc/docspell-restserver /opt/docspell/docspell-restserver && ln -s /usr/bin/dsc /opt/docspell/dsc sed -i \ diff --git a/install/ente-install.sh b/install/ente-install.sh index b65277581..f9afec8f0 100644 --- a/install/ente-install.sh +++ b/install/ente-install.sh @@ -8,18 +8,18 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - libsodium23 \ - libsodium-dev \ - pkg-config \ - caddy \ - gcc + libsodium23 \ + libsodium-dev \ + pkg-config \ + caddy \ + gcc msg_ok "Installed Dependencies" PG_VERSION="17" setup_postgresql @@ -37,10 +37,10 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8' $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" { - echo "Ente Credentials" - echo "Database Name: $DB_NAME" - echo "Database User: $DB_USER" - echo "Database Password: $DB_PASS" + echo "Ente Credentials" + echo "Database Name: $DB_NAME" + echo "Database User: $DB_USER" + echo "Database Password: $DB_PASS" } >>~/ente.creds msg_ok "Set up PostgreSQL" @@ -52,10 +52,10 @@ export CGO_ENABLED=1 CGO_CFLAGS="$(pkg-config --cflags libsodium || true)" CGO_LDFLAGS="$(pkg-config --libs libsodium || true)" if [ -z "$CGO_CFLAGS" ]; then - CGO_CFLAGS="-I/usr/include" + CGO_CFLAGS="-I/usr/include" fi if [ -z "$CGO_LDFLAGS" ]; then - CGO_LDFLAGS="-lsodium" + CGO_LDFLAGS="-lsodium" fi export CGO_CFLAGS export CGO_LDFLAGS diff --git a/install/freepbx-install.sh b/install/freepbx-install.sh index c5da11380..24c14a0a8 100644 --- a/install/freepbx-install.sh +++ b/install/freepbx-install.sh @@ -12,7 +12,7 @@ INSTALL_PATH="/opt/sng_freepbx_debian_install.sh" source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os @@ -24,80 +24,80 @@ msg_ok "Remove Firewall module is set to: $REMOVE_FIREWALL" msg_info "Downloading FreePBX installation script..." if curl -fsSL "$INSTALL_URL" -o "$INSTALL_PATH"; then - msg_ok "Download completed successfully" + msg_ok "Download completed successfully" else - curl_exit_code=$? - msg_error "Error downloading FreePBX installation script (curl exit code: $curl_exit_code)" - msg_error "Aborting!" - exit 1 + curl_exit_code=$? + msg_error "Error downloading FreePBX installation script (curl exit code: $curl_exit_code)" + msg_error "Aborting!" + exit 1 fi if [[ "$VERBOSE" == "yes" ]]; then - msg_info "Installing FreePBX (Verbose)\n" + msg_info "Installing FreePBX (Verbose)\n" else - msg_info "Installing FreePBX, be patient, this takes time..." + msg_info "Installing FreePBX, be patient, this takes time..." fi $STD bash "$INSTALL_PATH" if [[ $ONLY_OPENSOURCE == "yes" ]]; then - msg_info "Removing Commercial modules..." + msg_info "Removing Commercial modules..." - end_count=0 - max=5 - count=0 - while fwconsole ma list | awk '/Commercial/ {found=1} END {exit !found}'; do - count=$((count + 1)) - while read -r module; do - msg_info "Removing module: $module" + end_count=0 + max=5 + count=0 + while fwconsole ma list | awk '/Commercial/ {found=1} END {exit !found}'; do + count=$((count + 1)) + while read -r module; do + msg_info "Removing module: $module" - if [[ "$REMOVE_FIREWALL" == "no" ]] && [[ "$module" == "sysadmin" ]]; then - msg_warn "Skipping sysadmin module removal, it is required for Firewall!" - continue - fi + if [[ "$REMOVE_FIREWALL" == "no" ]] && [[ "$module" == "sysadmin" ]]; then + msg_warn "Skipping sysadmin module removal, it is required for Firewall!" + continue + fi - code=0 - $STD fwconsole ma -f remove $module || code=$? - if [[ $code -ne 0 ]]; then - msg_error "Module $module could not be removed - error code $code" - else - msg_ok "Module $module removed successfully" - fi - done < <(fwconsole ma list | awk '/Commercial/ {print $2}') + code=0 + $STD fwconsole ma -f remove $module || code=$? + if [[ $code -ne 0 ]]; then + msg_error "Module $module could not be removed - error code $code" + else + msg_ok "Module $module removed successfully" + fi + done < <(fwconsole ma list | awk '/Commercial/ {print $2}') - [[ $count -ge $max ]] && break + [[ $count -ge $max ]] && break - com_list=$(fwconsole ma list) - end_count=$(awk '/Commercial/ {count++} END {print count + 0}' <<< "$com_list") - awk '/Commercial/ {found=1} END {exit !found}' <<< "$com_list" || break - if [[ "$REMOVE_FIREWALL" == "no" ]] && \ - [[ $end_count -eq 1 ]] && \ - [[ $(awk '/Commercial/ {print $2}' <<< "$com_list") == "sysadmin" ]]; then - break + com_list=$(fwconsole ma list) + end_count=$(awk '/Commercial/ {count++} END {print count + 0}' <<<"$com_list") + awk '/Commercial/ {found=1} END {exit !found}' <<<"$com_list" || break + if [[ "$REMOVE_FIREWALL" == "no" ]] && + [[ $end_count -eq 1 ]] && + [[ $(awk '/Commercial/ {print $2}' <<<"$com_list") == "sysadmin" ]]; then + break + fi + + msg_warn "Not all commercial modules could be removed, retrying (attempt $count of $max)..." + done + + if [[ $REMOVE_FIREWALL == "yes" ]] && [[ $end_count -gt 0 ]]; then + msg_info "Removing Firewall module..." + if $STD fwconsole ma -f remove firewall; then + msg_ok "Firewall module removed successfully" + else + msg_error "Firewall module could not be removed, please check manually!" + fi fi - msg_warn "Not all commercial modules could be removed, retrying (attempt $count of $max)..." - done - - if [[ $REMOVE_FIREWALL == "yes" ]] && [[ $end_count -gt 0 ]]; then - msg_info "Removing Firewall module..." - if $STD fwconsole ma -f remove firewall; then - msg_ok "Firewall module removed successfully" + if [[ $end_count -eq 0 ]]; then + msg_ok "All commercial modules removed successfully" + elif [[ $end_count -eq 1 ]] && [[ $REMOVE_FIREWALL == "no" ]] && [[ $(fwconsole ma list | awk '/Commercial/ {print $2}') == "sysadmin" ]]; then + msg_ok "Only sysadmin module left, which is required for Firewall, skipping removal" else - msg_error "Firewall module could not be removed, please check manually!" + msg_warn "Some commercial modules could not be removed, please check the web interface for removal manually!" fi - fi - if [[ $end_count -eq 0 ]]; then - msg_ok "All commercial modules removed successfully" - elif [[ $end_count -eq 1 ]] && [[ $REMOVE_FIREWALL == "no" ]] && [[ $(fwconsole ma list | awk '/Commercial/ {print $2}') == "sysadmin" ]]; then - msg_ok "Only sysadmin module left, which is required for Firewall, skipping removal" - else - msg_warn "Some commercial modules could not be removed, please check the web interface for removal manually!" - fi - - msg_info "Reloading FreePBX..." - $STD fwconsole reload - msg_ok "FreePBX reloaded completely" + msg_info "Reloading FreePBX..." + $STD fwconsole reload + msg_ok "FreePBX reloaded completely" fi msg_ok "Installed FreePBX finished" diff --git a/install/frigate-install.sh b/install/frigate-install.sh index cbd8357cf..e6852aaac 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os @@ -84,9 +84,9 @@ EOF mkdir -p /config ln -sf /config/config.yml /opt/frigate/config/config.yml if [[ "$CTTYPE" == "0" ]]; then - sed -i -e 's/^kvm:x:104:$/render:x:104:root,frigate/' -e 's/^render:x:105:root$/kvm:x:105:/' /etc/group + sed -i -e 's/^kvm:x:104:$/render:x:104:root,frigate/' -e 's/^render:x:105:root$/kvm:x:105:/' /etc/group else - sed -i -e 's/^kvm:x:104:$/render:x:104:frigate/' -e 's/^render:x:105:$/kvm:x:105:/' /etc/group + sed -i -e 's/^kvm:x:104:$/render:x:104:frigate/' -e 's/^render:x:105:$/kvm:x:105:/' /etc/group fi echo "tmpfs /tmp/cache tmpfs defaults 0 0" >>/etc/fstab mkdir -p /media/frigate @@ -97,18 +97,18 @@ EOF msg_ok "Config ready" if grep -q -o -m1 -E 'avx[^ ]*' /proc/cpuinfo; then - msg_ok "AVX Support Detected" - msg_info "Installing Openvino Object Detection Model (Resilience)" - $STD pip install -r /opt/frigate/docker/main/requirements-ov.txt - cd /opt/frigate/models - export ENABLE_ANALYTICS=NO - $STD /usr/local/bin/omz_downloader --name ssdlite_mobilenet_v2 --num_attempts 2 - $STD /usr/local/bin/omz_converter --name ssdlite_mobilenet_v2 --precision FP16 --mo /usr/local/bin/mo - cd / - cp -r /opt/frigate/models/public/ssdlite_mobilenet_v2 openvino-model - curl -fsSL "https://github.com/openvinotoolkit/open_model_zoo/raw/master/data/dataset_classes/coco_91cl_bkgr.txt" -o "openvino-model/coco_91cl_bkgr.txt" - sed -i 's/truck/car/g' openvino-model/coco_91cl_bkgr.txt - cat <>/config/config.yml + msg_ok "AVX Support Detected" + msg_info "Installing Openvino Object Detection Model (Resilience)" + $STD pip install -r /opt/frigate/docker/main/requirements-ov.txt + cd /opt/frigate/models + export ENABLE_ANALYTICS=NO + $STD /usr/local/bin/omz_downloader --name ssdlite_mobilenet_v2 --num_attempts 2 + $STD /usr/local/bin/omz_converter --name ssdlite_mobilenet_v2 --precision FP16 --mo /usr/local/bin/mo + cd / + cp -r /opt/frigate/models/public/ssdlite_mobilenet_v2 openvino-model + curl -fsSL "https://github.com/openvinotoolkit/open_model_zoo/raw/master/data/dataset_classes/coco_91cl_bkgr.txt" -o "openvino-model/coco_91cl_bkgr.txt" + sed -i 's/truck/car/g' openvino-model/coco_91cl_bkgr.txt + cat <>/config/config.yml detectors: ov: type: openvino @@ -122,9 +122,9 @@ model: input_pixel_format: bgr labelmap_path: /openvino-model/coco_91cl_bkgr.txt EOF - msg_ok "Installed Openvino Object Detection Model" + msg_ok "Installed Openvino Object Detection Model" else - cat <>/config/config.yml + cat <>/config/config.yml model: path: /cpu_model.tflite EOF diff --git a/install/garmin-grafana-install.sh b/install/garmin-grafana-install.sh index fb201f8ec..23fb87979 100644 --- a/install/garmin-grafana-install.sh +++ b/install/garmin-grafana-install.sh @@ -9,7 +9,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os @@ -77,9 +77,9 @@ $STD grafana-cli plugins install marcusolsson-hourly-heatmap-panel $STD systemctl restart grafana-server # Output credentials to file { - echo "Grafana Credentials" - echo "Grafana User: ${GRAFANA_USER}" - echo "Grafana Password: ${GRAFANA_PASS}" + echo "Grafana Credentials" + echo "Grafana User: ${GRAFANA_USER}" + echo "Grafana Password: ${GRAFANA_PASS}" } >>~/garmin-grafana.creds msg_ok "Set up Grafana" @@ -90,7 +90,7 @@ curl -fsSL -o "${RELEASE}.zip" "https://github.com/arpanghosh8453/garmin-grafana unzip -q "${RELEASE}.zip" # Remove the v prefix to RELEASE if it exists if [[ "${RELEASE}" == v* ]]; then - RELEASE="${RELEASE:1}" + RELEASE="${RELEASE:1}" fi mv "garmin-grafana-${RELEASE}/" "/opt/garmin-grafana" mkdir -p /opt/garmin-grafana/.garminconnect @@ -112,9 +112,9 @@ msg_info "Setting up garmin-grafana" # Check if using Chinese garmin servers read -rp "Are you using Garmin in mainland China? (y/N): " prompt if [[ "${prompt,,}" =~ ^(y|yes|Y)$ ]]; then - GARMIN_CN="True" + GARMIN_CN="True" else - GARMIN_CN="False" + GARMIN_CN="False" fi cat </opt/garmin-grafana/.env @@ -131,24 +131,24 @@ EOF # garmin-grafana usually prompts the user for email and password (and MFA) on first run, # then stores a refreshable token. We try to avoid storing user credentials in the env vars if [ -z "$(ls -A /opt/garmin-grafana/.garminconnect)" ]; then - read -r -p "Please enter your Garmin Connect Email: " GARMIN_EMAIL - read -r -p "Please enter your Garmin Connect Password (this is used to generate a token and NOT stored): " GARMIN_PASSWORD - read -r -p "Please enter your MFA Code (if applicable, leave blank if not): " GARMIN_MFA - # Run the script once to prompt for credential - msg_info "Creating Garmin credentials, this will timeout in 60 seconds" - timeout 60s uv run --env-file /opt/garmin-grafana/.env --project /opt/garmin-grafana/ /opt/garmin-grafana/src/garmin_grafana/garmin_fetch.py <>~/ghostfolio.creds msg_ok "Set up Database" @@ -101,11 +101,11 @@ HOST=0.0.0.0 EOF if [[ -n "${COINGECKO_DEMO_KEY:-}" ]]; then - echo "API_KEY_COINGECKO_DEMO=$COINGECKO_DEMO_KEY" >>/opt/ghostfolio/.env + echo "API_KEY_COINGECKO_DEMO=$COINGECKO_DEMO_KEY" >>/opt/ghostfolio/.env fi if [[ -n "${COINGECKO_PRO_KEY:-}" ]]; then - echo "API_KEY_COINGECKO_PRO=$COINGECKO_PRO_KEY" >>/opt/ghostfolio/.env + echo "API_KEY_COINGECKO_PRO=$COINGECKO_PRO_KEY" >>/opt/ghostfolio/.env fi msg_ok "Set up Environment" diff --git a/install/globaleaks-install.sh b/install/globaleaks-install.sh index dd72c3282..173899a95 100644 --- a/install/globaleaks-install.sh +++ b/install/globaleaks-install.sh @@ -7,7 +7,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os diff --git a/install/hanko-install.sh b/install/hanko-install.sh index 96f2308c8..40ab829d9 100644 --- a/install/hanko-install.sh +++ b/install/hanko-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os @@ -28,10 +28,10 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8' $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC'" { - echo "Hanko-Credentials" - echo "Hanko Database User: $DB_USER" - echo "Hanko Database Password: $DB_PASS" - echo "Hanko Database Name: $DB_NAME" + echo "Hanko-Credentials" + echo "Hanko Database User: $DB_USER" + echo "Hanko Database Password: $DB_PASS" + echo "Hanko Database Name: $DB_NAME" } >>~/hanko.creds msg_ok "Set up PostgreSQL Database" @@ -39,7 +39,7 @@ msg_info "Setup Hanko" fetch_and_deploy_gh_release "hanko" "teamhanko/hanko" "prebuild" "latest" "/opt/hanko" "hanko_Linux_x86_64.tar.gz" curl -fsSL https://raw.githubusercontent.com/teamhanko/hanko/refs/heads/main/backend/config/config.yaml -o /opt/hanko/config.yaml env DB_USER="$DB_USER" DB_PASS="$DB_PASS" APP_SECRET="$APP_SECRET" \ - yq eval ' + yq eval ' .database.user = strenv(DB_USER) | .database.password = strenv(DB_PASS) | .database.host = "localhost" | diff --git a/install/joplin-server-install.sh b/install/joplin-server-install.sh index 45f201660..07b7920ee 100644 --- a/install/joplin-server-install.sh +++ b/install/joplin-server-install.sh @@ -8,15 +8,15 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - git \ - rsync + git \ + rsync msg_ok "Installed Dependencies" PG_VERSION="17" setup_postgresql @@ -38,10 +38,10 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8' $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC'" { - echo "Joplin-Credentials" - echo "Joplin Database User: $DB_USER" - echo "Joplin Database Password: $DB_PASS" - echo "Joplin Database Name: $DB_NAME" + echo "Joplin-Credentials" + echo "Joplin Database User: $DB_USER" + echo "Joplin Database Password: $DB_PASS" + echo "Joplin Database Name: $DB_NAME" } >>~/joplin.creds msg_ok "Set up PostgreSQL Database" diff --git a/install/kanba-install.sh b/install/kanba-install.sh index a11e59b51..d7335fd56 100644 --- a/install/kanba-install.sh +++ b/install/kanba-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os @@ -29,10 +29,10 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8' $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" { - echo "Kanba-Credentials" - echo "Kanba Database Name: $DB_NAME" - echo "Kanba Database User: $DB_USER" - echo "Kanba Database Password: $DB_PASS" + echo "Kanba-Credentials" + echo "Kanba Database Name: $DB_NAME" + echo "Kanba Database User: $DB_USER" + echo "Kanba Database Password: $DB_PASS" } >>~/kanba.creds msg_ok "Set up PostgreSQL Database" diff --git a/install/leantime-install.sh b/install/leantime-install.sh index db67e925e..b5c2f5e7a 100644 --- a/install/leantime-install.sh +++ b/install/leantime-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os @@ -24,10 +24,10 @@ $STD mysql -u root -e "CREATE DATABASE $DB_NAME;" $STD mysql -u root -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED WITH mysql_native_password AS PASSWORD('$DB_PASS');" $STD mysql -u root -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" { - echo "Leantime Credentials" - echo "Database User: $DB_USER" - echo "Database Password: $DB_PASS" - echo "Database Name: $DB_NAME" + echo "Leantime Credentials" + echo "Database User: $DB_USER" + echo "Database Password: $DB_PASS" + echo "Database Name: $DB_NAME" } >>~/leantime.creds msg_ok "Set up Database" @@ -59,10 +59,10 @@ cat </etc/apache2/sites-enabled/000-default.conf EOF mv "/opt/leantime/config/sample.env" "/opt/leantime/config/.env" sed -i -e "s|^LEAN_DB_DATABASE.*|LEAN_DB_DATABASE = '$DB_NAME'|" \ - -e "s|^LEAN_DB_USER.*|LEAN_DB_USER = '$DB_USER'|" \ - -e "s|^LEAN_DB_PASSWORD.*|LEAN_DB_PASSWORD = '$DB_PASS'|" \ - -e "s|^LEAN_SESSION_PASSWORD.*|LEAN_SESSION_PASSWORD = '$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13)'|" \ - "/opt/leantime/config/.env" + -e "s|^LEAN_DB_USER.*|LEAN_DB_USER = '$DB_USER'|" \ + -e "s|^LEAN_DB_PASSWORD.*|LEAN_DB_PASSWORD = '$DB_PASS'|" \ + -e "s|^LEAN_SESSION_PASSWORD.*|LEAN_SESSION_PASSWORD = '$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13)'|" \ + "/opt/leantime/config/.env" $STD a2enmod -q proxy_fcgi setenvif rewrite $STD a2enconf -q "php8.4-fpm" sed -i -e "s/^;extension.\(curl\|fileinfo\|gd\|intl\|ldap\|mbstring\|exif\|mysqli\|odbc\|openssl\|pdo_mysql\)/extension=\1/g" "/etc/php/8.4/apache2/php.ini" diff --git a/install/librenms-install.sh b/install/librenms-install.sh index 46f3c5bfd..296f03519 100644 --- a/install/librenms-install.sh +++ b/install/librenms-install.sh @@ -8,25 +8,25 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - lsb-release \ - ca-certificates \ - acl \ - fping \ - graphviz \ - imagemagick \ - mtr-tiny \ - nginx \ - nmap \ - rrdtool \ - snmp \ - snmpd + lsb-release \ + ca-certificates \ + acl \ + fping \ + graphviz \ + imagemagick \ + mtr-tiny \ + nginx \ + nmap \ + rrdtool \ + snmp \ + snmpd msg_ok "Installed Dependencies" PHP_VERSION="8.3" PHP_FPM="YES" PHP_MODULE="gmp,mysql,snmp" setup_php @@ -36,7 +36,7 @@ PYTHON_VERSION="3.13" setup_uv msg_info "Installing Python" $STD apt-get install -y \ - python3-{dotenv,pymysql,redis,setuptools,systemd,pip} + python3-{dotenv,pymysql,redis,setuptools,systemd,pip} msg_ok "Installed Python" msg_info "Configuring Database" @@ -47,10 +47,10 @@ $STD mariadb -u root -e "CREATE DATABASE $DB_NAME CHARACTER SET utf8mb4 COLLATE $STD mariadb -u root -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';" $STD mariadb -u root -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" { - echo "LibreNMS-Credentials" - echo "LibreNMS Database User: $DB_USER" - echo "LibreNMS Database Password: $DB_PASS" - echo "LibreNMS Database Name: $DB_NAME" + echo "LibreNMS-Credentials" + echo "LibreNMS Database User: $DB_USER" + echo "LibreNMS Database Password: $DB_PASS" + echo "LibreNMS Database Name: $DB_NAME" } >>~/librenms.creds msg_ok "Configured Database" diff --git a/install/livebook-install.sh b/install/livebook-install.sh index 1818616e4..251f078db 100644 --- a/install/livebook-install.sh +++ b/install/livebook-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os @@ -47,7 +47,7 @@ $STD mix local.hex --force $STD mix local.rebar --force $STD mix escript.install hex livebook --force -cat < /opt/livebook/.env +cat </opt/livebook/.env export HOME=/opt/livebook export ERLANG_VERSION=$ERLANG_VERSION export ELIXIR_VERSION=$ELIXIR_VERSION @@ -89,7 +89,7 @@ systemctl enable -q --now livebook msg_ok "Installed Livebook" msg_info "Saving Livebook credentials" -cat < /opt/livebook/livebook.creds +cat </opt/livebook/livebook.creds Livebook-Credentials Livebook Password: $LIVEBOOK_PASSWORD EOF diff --git a/install/manyfold-install.sh b/install/manyfold-install.sh index 7f2ee7581..be4dcd84b 100644 --- a/install/manyfold-install.sh +++ b/install/manyfold-install.sh @@ -7,23 +7,23 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - lsb-release \ - rbenv \ - libpq-dev \ - libarchive-dev \ - git \ - libmariadb-dev \ - redis-server \ - nginx \ - libffi-dev \ - libyaml-dev + lsb-release \ + rbenv \ + libpq-dev \ + libarchive-dev \ + git \ + libmariadb-dev \ + redis-server \ + nginx \ + libffi-dev \ + libyaml-dev msg_ok "Installed Dependencies" PG_VERSION="16" setup_postgresql @@ -38,10 +38,10 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8' $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" { - echo "Manyfold Credentials" - echo "Manyfold Database User: $DB_USER" - echo "Manyfold Database Password: $DB_PASS" - echo "Manyfold Database Name: $DB_NAME" + echo "Manyfold Credentials" + echo "Manyfold Database User: $DB_USER" + echo "Manyfold Database Password: $DB_PASS" + echo "Manyfold Database Name: $DB_NAME" } >>~/manyfold.creds msg_ok "Set up PostgreSQL" diff --git a/install/maxun-install.sh b/install/maxun-install.sh index e0caea03a..27e3e710a 100644 --- a/install/maxun-install.sh +++ b/install/maxun-install.sh @@ -8,37 +8,37 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - openssl \ - redis \ - libgbm1 \ - libnss3 \ - libatk1.0-0 \ - libatk-bridge2.0-0 \ - libdrm2 \ - libxkbcommon0 \ - libglib2.0-0 \ - libdbus-1-3 \ - libx11-xcb1 \ - libxcb1 \ - libxcomposite1 \ - libxcursor1 \ - libxdamage1 \ - libxext6 \ - libxi6 \ - libxtst6 \ - ca-certificates \ - libxrandr2 \ - libasound2 \ - libxss1 \ - libxinerama1 \ - nginx + openssl \ + redis \ + libgbm1 \ + libnss3 \ + libatk1.0-0 \ + libatk-bridge2.0-0 \ + libdrm2 \ + libxkbcommon0 \ + libglib2.0-0 \ + libdbus-1-3 \ + libx11-xcb1 \ + libxcb1 \ + libxcomposite1 \ + libxcursor1 \ + libxdamage1 \ + libxext6 \ + libxi6 \ + libxtst6 \ + ca-certificates \ + libxrandr2 \ + libasound2 \ + libxss1 \ + libxinerama1 \ + nginx msg_ok "Installed Dependencies" PG_VERSION=17 setup_postgresql @@ -63,13 +63,13 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8' $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC'" { - echo "Maxun-Credentials" - echo "Maxun Database User: $DB_USER" - echo "Maxun Database Password: $DB_PASS" - echo "Maxun Database Name: $DB_NAME" - echo "Maxun JWT Secret: $JWT_SECRET" - echo "Maxun Encryption Key: $ENCRYPTION_KEY" - echo "Maxun Session Secret: $SESSION_SECRET" + echo "Maxun-Credentials" + echo "Maxun Database User: $DB_USER" + echo "Maxun Database Password: $DB_PASS" + echo "Maxun Database Name: $DB_NAME" + echo "Maxun JWT Secret: $JWT_SECRET" + echo "Maxun Encryption Key: $ENCRYPTION_KEY" + echo "Maxun Session Secret: $SESSION_SECRET" } >>~/maxun.creds msg_ok "Set up Database" @@ -98,9 +98,9 @@ LimitNOFILE=65536 WantedBy=multi-user.target EOF { - echo "__________________" - echo "MinIO Admin User: $MINIO_USER" - echo "MinIO Admin Password: $MINIO_PASS" + echo "__________________" + echo "MinIO Admin User: $MINIO_USER" + echo "MinIO Admin Password: $MINIO_PASS" } >>~/maxun.creds cat </etc/default/minio MINIO_ROOT_USER=${MINIO_USER} diff --git a/install/notesnook-install.sh b/install/notesnook-install.sh index 64afd6cbe..ab95849eb 100644 --- a/install/notesnook-install.sh +++ b/install/notesnook-install.sh @@ -8,16 +8,16 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - make \ - git \ - caddy + make \ + git \ + caddy msg_ok "Installed Dependencies" LOCAL_IP=$(hostname -I | awk '{print $1}') diff --git a/install/npmplus-install.sh b/install/npmplus-install.sh index 58688240f..e8e5f1bfb 100644 --- a/install/npmplus-install.sh +++ b/install/npmplus-install.sh @@ -8,16 +8,16 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apk add \ - tzdata \ - gawk \ - yq + tzdata \ + gawk \ + yq msg_ok "Installed Dependencies" msg_info "Installing Docker & Compose" @@ -26,7 +26,7 @@ $STD rc-service docker start $STD rc-update add docker default get_latest_release() { - curl -fsSL https://api.github.com/repos/$1/releases/latest | grep '"tag_name":' | cut -d'"' -f4 + curl -fsSL https://api.github.com/repos/$1/releases/latest | grep '"tag_name":' | cut -d'"' -f4 } DOCKER_COMPOSE_LATEST_VERSION=$(get_latest_release "docker/compose") DOCKER_CONFIG=${DOCKER_CONFIG:-$HOME/.docker} @@ -42,17 +42,17 @@ msg_ok "Fetched NPMplus" attempts=0 while true; do - read -r -p "${TAB3}Enter your TZ Identifier (e.g., Europe/Berlin): " TZ_INPUT - if validate_tz "$TZ_INPUT"; then - break - fi - msg_error "Invalid timezone! Please enter a valid TZ identifier." + read -r -p "${TAB3}Enter your TZ Identifier (e.g., Europe/Berlin): " TZ_INPUT + if validate_tz "$TZ_INPUT"; then + break + fi + msg_error "Invalid timezone! Please enter a valid TZ identifier." - attempts=$((attempts + 1)) - if [[ "$attempts" -ge 3 ]]; then - msg_error "Maximum attempts reached. Exiting." - exit 1 - fi + attempts=$((attempts + 1)) + if [[ "$attempts" -ge 3 ]]; then + msg_error "Maximum attempts reached. Exiting." + exit 1 + fi done read -r -p "${TAB3}Enter your ACME Email: " ACME_EMAIL_INPUT @@ -67,20 +67,20 @@ msg_info "Building and Starting NPMplus (Patience)" $STD docker compose up -d CONTAINER_ID="" for i in {1..60}; do - CONTAINER_ID=$(docker ps --filter "name=npmplus" --format "{{.ID}}") - if [[ -n "$CONTAINER_ID" ]]; then - STATUS=$(docker inspect --format '{{.State.Health.Status}}' "$CONTAINER_ID" 2>/dev/null || echo "starting") - if [[ "$STATUS" == "healthy" ]]; then - msg_ok "NPMplus is running and healthy" - break - elif [[ "$STATUS" == "unhealthy" ]]; then - msg_error "NPMplus container is unhealthy! Check logs." - docker logs "$CONTAINER_ID" - exit 1 + CONTAINER_ID=$(docker ps --filter "name=npmplus" --format "{{.ID}}") + if [[ -n "$CONTAINER_ID" ]]; then + STATUS=$(docker inspect --format '{{.State.Health.Status}}' "$CONTAINER_ID" 2>/dev/null || echo "starting") + if [[ "$STATUS" == "healthy" ]]; then + msg_ok "NPMplus is running and healthy" + break + elif [[ "$STATUS" == "unhealthy" ]]; then + msg_error "NPMplus container is unhealthy! Check logs." + docker logs "$CONTAINER_ID" + exit 1 + fi fi - fi - sleep 2 - [[ $i -eq 60 ]] && msg_error "NPMplus container did not become healthy within 120s." && docker logs "$CONTAINER_ID" && exit 1 + sleep 2 + [[ $i -eq 60 ]] && msg_error "NPMplus container did not become healthy within 120s." && docker logs "$CONTAINER_ID" && exit 1 done msg_ok "Builded and started NPMplus" @@ -91,34 +91,34 @@ msg_info "Retrieving Default Login (Patience)" PASSWORD_FOUND=0 for i in {1..60}; do - PASSWORD_LINE=$( - { awk '/Creating a new user:/{print; exit}' < <(docker logs "$CONTAINER_ID" 2>&1); } || true - ) + PASSWORD_LINE=$( + { awk '/Creating a new user:/{print; exit}' < <(docker logs "$CONTAINER_ID" 2>&1); } || true + ) - if [[ -n "${PASSWORD_LINE:-}" ]]; then - PASSWORD="${PASSWORD_LINE#*password: }" - printf 'username: admin@example.org\npassword: %s\n' "$PASSWORD" >/opt/.npm_pwd - msg_ok "Saved default login to /opt/.npm_pwd" - PASSWORD_FOUND=1 - break - fi - sleep 2 + if [[ -n "${PASSWORD_LINE:-}" ]]; then + PASSWORD="${PASSWORD_LINE#*password: }" + printf 'username: admin@example.org\npassword: %s\n' "$PASSWORD" >/opt/.npm_pwd + msg_ok "Saved default login to /opt/.npm_pwd" + PASSWORD_FOUND=1 + break + fi + sleep 2 done if [[ $PASSWORD_FOUND -eq 0 ]]; then - PASSWORD_LINE=$( - timeout 30s bash -c ' + PASSWORD_LINE=$( + timeout 30s bash -c ' docker logs -f --since=0s --tail=0 "$1" 2>&1 | awk "/Creating a new user:/{print; exit}" ' _ "$CONTAINER_ID" || true - ) - if [[ -n "${PASSWORD_LINE:-}" ]]; then - PASSWORD="${PASSWORD_LINE#*password: }" - printf 'username: admin@example.org\npassword: %s\n' "$PASSWORD" >/opt/.npm_pwd - msg_ok "Saved default login to /opt/.npm_pwd (live)" - PASSWORD_FOUND=1 - fi + ) + if [[ -n "${PASSWORD_LINE:-}" ]]; then + PASSWORD="${PASSWORD_LINE#*password: }" + printf 'username: admin@example.org\npassword: %s\n' "$PASSWORD" >/opt/.npm_pwd + msg_ok "Saved default login to /opt/.npm_pwd (live)" + PASSWORD_FOUND=1 + fi fi if [[ $PASSWORD_FOUND -eq 0 ]]; then - msg_error "Could not retrieve default login after 120s." - echo -e "\nYou can manually check the container logs with:\n docker logs $CONTAINER_ID | grep 'Creating a new user:'\n" + msg_error "Could not retrieve default login after 120s." + echo -e "\nYou can manually check the container logs with:\n docker logs $CONTAINER_ID | grep 'Creating a new user:'\n" fi diff --git a/install/opencloud-install.sh b/install/opencloud-install.sh index 16d7f3845..a791ed0b1 100644 --- a/install/opencloud-install.sh +++ b/install/opencloud-install.sh @@ -8,22 +8,22 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os read -r -p "Enter the hostname of your OpenCloud server (eg cloud.domain.tld): " oc_host if [[ "$oc_host" ]]; then - OC_HOST="$oc_host" + OC_HOST="$oc_host" fi read -r -p "Enter the hostname of your Collabora server (eg collabora.domain.tld): " collabora_host if [[ "$collabora_host" ]]; then - COLLABORA_HOST="$collabora_host" + COLLABORA_HOST="$collabora_host" fi read -r -p "Enter the hostname of your WOPI server (eg wopiserver.domain.tld): " wopi_host if [[ "$wopi_host" ]]; then - WOPI_HOST="$wopi_host" + WOPI_HOST="$wopi_host" fi msg_info "Installing Collabora Online" diff --git a/install/postiz-install.sh b/install/postiz-install.sh index 7f55e9f40..2ed155c82 100644 --- a/install/postiz-install.sh +++ b/install/postiz-install.sh @@ -8,20 +8,20 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os msg_info "Installing dependencies" $STD apt-get install -y \ - build-essential \ - python3-pip \ - supervisor \ - debian-keyring \ - debian-archive-keyring \ - apt-transport-https \ - redis + build-essential \ + python3-pip \ + supervisor \ + debian-keyring \ + debian-archive-keyring \ + apt-transport-https \ + redis msg_ok "Installed dependencies" NODE_VERSION="20" setup_nodejs @@ -37,10 +37,10 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8' $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC'" { - echo "Postiz DB Credentials" - echo "Postiz Database User: $DB_USER" - echo "Postiz Database Password: $DB_PASS" - echo "Postiz Database Name: $DB_NAME" + echo "Postiz DB Credentials" + echo "Postiz Database User: $DB_USER" + echo "Postiz Database Password: $DB_PASS" + echo "Postiz Database Name: $DB_NAME" } >>~/postiz.creds msg_ok "Set up PostgreSQL Database" diff --git a/install/proxmox-datacenter-manager-install.sh b/install/proxmox-datacenter-manager-install.sh index 6c62976e9..c4a3f3877 100644 --- a/install/proxmox-datacenter-manager-install.sh +++ b/install/proxmox-datacenter-manager-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os @@ -19,9 +19,9 @@ echo "deb [signed-by=/usr/share/keyrings/proxmox-archive-keyring.gpg] http://dow $STD apt-get update DEBIAN_FRONTEND=noninteractive $STD apt-get -o Dpkg::Options::="--force-confdef" \ - -o Dpkg::Options::="--force-confold" \ - install -y proxmox-datacenter-manager \ - proxmox-datacenter-manager-ui + -o Dpkg::Options::="--force-confold" \ + install -y proxmox-datacenter-manager \ + proxmox-datacenter-manager-ui msg_ok "Installed Proxmox Datacenter Manager" motd_ssh diff --git a/install/romm-install.sh b/install/romm-install.sh index 36c791c46..bd5f9a287 100644 --- a/install/romm-install.sh +++ b/install/romm-install.sh @@ -9,27 +9,27 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os msg_info "Installing dependencies" $STD apt-get install -y \ - acl \ - build-essential \ - libssl-dev \ - libffi-dev \ - python3-dev \ - python3-pip \ - python3-venv \ - libmariadb3 \ - libmariadb-dev \ - libpq-dev \ - redis-tools \ - p7zip \ - tzdata \ - jq + acl \ + build-essential \ + libssl-dev \ + libffi-dev \ + python3-dev \ + python3-pip \ + python3-venv \ + libmariadb3 \ + libmariadb-dev \ + libpq-dev \ + redis-tools \ + p7zip \ + tzdata \ + jq msg_ok "Installed core dependencies" PYTHON_VERSION="3.12" setup_uv @@ -44,10 +44,10 @@ $STD mariadb -u root -e "CREATE DATABASE IF NOT EXISTS $DB_NAME CHARACTER SET ut $STD mariadb -u root -e "CREATE USER IF NOT EXISTS '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';" $STD mariadb -u root -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" { - echo "RomM-Credentials" - echo "RomM Database User: $DB_USER" - echo "RomM Database Password: $DB_PASS" - echo "RomM Database Name: $DB_NAME" + echo "RomM-Credentials" + echo "RomM Database User: $DB_USER" + echo "RomM Database Password: $DB_PASS" + echo "RomM Database Name: $DB_NAME" } >~/romm.creds chmod 600 ~/romm.creds msg_ok "Configured Database" @@ -55,11 +55,11 @@ msg_ok "Configured Database" msg_info "Creating romm user and directories" id -u romm &>/dev/null || useradd -r -m -d /var/lib/romm -s /bin/bash romm mkdir -p /opt/romm \ - /var/lib/romm/config \ - /var/lib/romm/resources \ - /var/lib/romm/assets/{saves,states,screenshots} \ - /var/lib/romm/library/roms/{gba,gbc,ps} \ - /var/lib/romm/library/bios/{gba,ps} + /var/lib/romm/config \ + /var/lib/romm/resources \ + /var/lib/romm/assets/{saves,states,screenshots} \ + /var/lib/romm/library/roms/{gba,gbc,ps} \ + /var/lib/romm/library/bios/{gba,ps} chown -R romm:romm /opt/romm /var/lib/romm msg_ok "Created romm user and directories" @@ -71,10 +71,10 @@ $STD mariadb -u root -e "CREATE DATABASE $DB_NAME CHARACTER SET utf8mb4 COLLATE $STD mariadb -u root -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';" $STD mariadb -u root -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" { - echo "RomM-Credentials" - echo "RomM Database User: $DB_USER" - echo "RomM Database Password: $DB_PASS" - echo "RomM Database Name: $DB_NAME" + echo "RomM-Credentials" + echo "RomM Database User: $DB_USER" + echo "RomM Database Password: $DB_PASS" + echo "RomM Database Name: $DB_NAME" } >~/romm.creds msg_ok "Configured Database" diff --git a/install/rybbit-install.sh b/install/rybbit-install.sh index 62a825b9a..1f9481f99 100644 --- a/install/rybbit-install.sh +++ b/install/rybbit-install.sh @@ -8,19 +8,18 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - caddy \ - apt-transport-https \ - ca-certificates + caddy \ + apt-transport-https \ + ca-certificates msg_ok "Installed Dependencies" - setup_clickhouse PG_VERSION=17 setup_postgresql NODE_VERSION="20" NODE_MODULE="next" setup_nodejs @@ -38,10 +37,10 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8' $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC'" { - echo "Rybbit-Credentials" - echo "Rybbit Database User: $DB_USER" - echo "Rybbit Database Password: $DB_PASS" - echo "Rybbit Database Name: $DB_NAME" + echo "Rybbit-Credentials" + echo "Rybbit Database User: $DB_USER" + echo "Rybbit Database Password: $DB_PASS" + echo "Rybbit Database Name: $DB_NAME" } >>~/rybbit.creds msg_ok "Set up PostgreSQL Database" diff --git a/install/scraparr-install.sh b/install/scraparr-install.sh index 8c0de782c..18b024253 100644 --- a/install/scraparr-install.sh +++ b/install/scraparr-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os diff --git a/install/signoz-install.sh b/install/signoz-install.sh index 8a35766b0..f506f315d 100644 --- a/install/signoz-install.sh +++ b/install/signoz-install.sh @@ -8,15 +8,15 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - apt-transport-https \ - ca-certificates + apt-transport-https \ + ca-certificates msg_ok "Installed Dependencies" JAVA_VERSION="21" setup_java @@ -66,7 +66,7 @@ Restart=on-failure [Install] WantedBy=multi-user.target EOF -systemctl enable -q --now zookeeper +systemctl enable -q --now zookeeper msg_ok "Setup Zookeeper" msg_info "Configuring ClickHouse" @@ -104,8 +104,8 @@ fetch_and_deploy_gh_release "signoz-schema-migrator" "SigNoz/signoz-otel-collect msg_info "Running ClickHouse migrations" cd /opt/signoz-schema-migrator/bin -$STD ./signoz-schema-migrator sync --dsn="tcp://localhost:9000?password=" --replication=true --up= -$STD ./signoz-schema-migrator async --dsn="tcp://localhost:9000?password=" --replication=true --up= +$STD ./signoz-schema-migrator sync --dsn="tcp://localhost:9000?password=" --replication=true --up= +$STD ./signoz-schema-migrator async --dsn="tcp://localhost:9000?password=" --replication=true --up= msg_ok "ClickHouse Migrations Completed" fetch_and_deploy_gh_release "signoz" "SigNoz/signoz" "prebuild" "latest" "/opt/signoz" "signoz-community_linux_amd64.tar.gz" diff --git a/install/tunarr-install.sh b/install/tunarr-install.sh index 854db7137..57d7f16ef 100644 --- a/install/tunarr-install.sh +++ b/install/tunarr-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os @@ -16,18 +16,18 @@ update_os msg_info "Setting Up Hardware Acceleration" $STD apt-get -y install {va-driver-all,ocl-icd-libopencl1,intel-opencl-icd,vainfo,intel-gpu-tools} if [[ "$CTTYPE" == "0" ]]; then - chgrp video /dev/dri - chmod 755 /dev/dri - chmod 660 /dev/dri/* - $STD adduser $(id -u -n) video - $STD adduser $(id -u -n) render + chgrp video /dev/dri + chmod 755 /dev/dri + chmod 660 /dev/dri/* + $STD adduser $(id -u -n) video + $STD adduser $(id -u -n) render fi msg_ok "Set Up Hardware Acceleration" read -r -p "${TAB3}Do you need the intel-media-va-driver-non-free driver for HW encoding (Debian 12 only)? " prompt if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then - msg_info "Installing Intel Hardware Acceleration (non-free)" - cat </etc/apt/sources.list.d/non-free.list + msg_info "Installing Intel Hardware Acceleration (non-free)" + cat </etc/apt/sources.list.d/non-free.list deb http://deb.debian.org/debian bookworm non-free non-free-firmware deb-src http://deb.debian.org/debian bookworm non-free non-free-firmware @@ -38,11 +38,11 @@ deb-src http://deb.debian.org/debian-security bookworm-security non-free non-fre deb http://deb.debian.org/debian bookworm-updates non-free non-free-firmware deb-src http://deb.debian.org/debian bookworm-updates non-free non-free-firmware EOF - $STD apt-get update - $STD apt-get -y install {intel-media-va-driver-non-free,ocl-icd-libopencl1,intel-opencl-icd,vainfo,intel-gpu-tools} + $STD apt-get update + $STD apt-get -y install {intel-media-va-driver-non-free,ocl-icd-libopencl1,intel-opencl-icd,vainfo,intel-gpu-tools} else - msg_info "Installing Intel Hardware Acceleration" - $STD apt-get -y install {va-driver-all,ocl-icd-libopencl1,intel-opencl-icd,vainfo,intel-gpu-tools} + msg_info "Installing Intel Hardware Acceleration" + $STD apt-get -y install {va-driver-all,ocl-icd-libopencl1,intel-opencl-icd,vainfo,intel-gpu-tools} fi msg_ok "Installed and Set Up Intel Hardware Acceleration" diff --git a/install/ubuntu-install.sh b/install/ubuntu-install.sh index 97283d838..aa5766a09 100644 --- a/install/ubuntu-install.sh +++ b/install/ubuntu-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os diff --git a/install/viseron-install.sh b/install/viseron-install.sh index f15a0f424..67cf18c40 100644 --- a/install/viseron-install.sh +++ b/install/viseron-install.sh @@ -8,19 +8,19 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - python3-opencv jq \ - libgl1-mesa-glx libglib2.0-0 \ - libgstreamer1.0-0 libgstreamer-plugins-base1.0-0 \ - gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-libav \ - build-essential python3-dev python3-gi pkg-config libcairo2-dev gir1.2-glib-2.0 \ - cmake gfortran libopenblas-dev liblapack-dev libgirepository1.0-dev git + python3-opencv jq \ + libgl1-mesa-glx libglib2.0-0 \ + libgstreamer1.0-0 libgstreamer-plugins-base1.0-0 \ + gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-libav \ + build-essential python3-dev python3-gi pkg-config libcairo2-dev gir1.2-glib-2.0 \ + cmake gfortran libopenblas-dev liblapack-dev libgirepository1.0-dev git msg_ok "Installed Dependencies" PYTHON_VERSION="3.12" setup_uv @@ -36,10 +36,10 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8' $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC'" { - echo "Hanko-Credentials" - echo "Hanko Database User: $DB_USER" - echo "Hanko Database Password: $DB_PASS" - echo "Hanko Database Name: $DB_NAME" + echo "Hanko-Credentials" + echo "Hanko Database User: $DB_USER" + echo "Hanko Database Password: $DB_PASS" + echo "Hanko Database Name: $DB_NAME" } >>~/hanko.creds msg_ok "Set up PostgreSQL Database" @@ -61,16 +61,16 @@ msg_ok "Python Environment Setup" msg_info "Setup Viseron (Patience)" if ls /dev/nvidia* >/dev/null 2>&1; then - msg_info "GPU detected → Installing PyTorch with CUDA" - UV_HTTP_TIMEOUT=600 uv pip install --python /opt/viseron/.venv/bin/python \ - torch==2.8.0 torchvision==0.19.0 torchaudio==2.8.0 - msg_ok "Installed Torch with CUDA" + msg_info "GPU detected → Installing PyTorch with CUDA" + UV_HTTP_TIMEOUT=600 uv pip install --python /opt/viseron/.venv/bin/python \ + torch==2.8.0 torchvision==0.19.0 torchaudio==2.8.0 + msg_ok "Installed Torch with CUDA" else - msg_info "No GPU detected → Installing CPU-only PyTorch" - UV_HTTP_TIMEOUT=600 uv pip install --python /opt/viseron/.venv/bin/python \ - torch==2.8.0+cpu torchvision==0.19.0+cpu torchaudio==2.8.0+cpu \ - --extra-index-url https://download.pytorch.org/whl/cpu - msg_ok "Installed Torch CPU-only" + msg_info "No GPU detected → Installing CPU-only PyTorch" + UV_HTTP_TIMEOUT=600 uv pip install --python /opt/viseron/.venv/bin/python \ + torch==2.8.0+cpu torchvision==0.19.0+cpu torchaudio==2.8.0+cpu \ + --extra-index-url https://download.pytorch.org/whl/cpu + msg_ok "Installed Torch CPU-only" fi UV_HTTP_TIMEOUT=600 uv pip install --python /opt/viseron/.venv/bin/python -e /opt/viseron/. UV_HTTP_TIMEOUT=600 uv pip install --python /opt/viseron/.venv/bin/python -r /opt/viseron/requirements.txt diff --git a/install/wallabag-install.sh b/install/wallabag-install.sh index 351e8537d..3bcbce9cc 100644 --- a/install/wallabag-install.sh +++ b/install/wallabag-install.sh @@ -7,17 +7,17 @@ source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os msg_info "Installing Dependencies (Patience)" $STD apt-get install -y \ - make \ - apache2 \ - libapache2-mod-php \ - redis + make \ + apache2 \ + libapache2-mod-php \ + redis msg_ok "Installed Dependencies" setup_mariadb @@ -33,10 +33,10 @@ $STD mariadb -u root -e "CREATE DATABASE $DB_NAME;" $STD mariadb -u root -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';" $STD mariadb -u root -e "GRANT ALL PRIVILEGES ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" { - echo "Wallabag Credentials" - echo "Database User: $DB_USER" - echo "Database Password: $DB_PASS" - echo "Database Name: $DB_NAME" + echo "Wallabag Credentials" + echo "Database User: $DB_USER" + echo "Database Password: $DB_PASS" + echo "Database Name: $DB_NAME" } >>~/wallabag.creds msg_ok "Set up Database" @@ -48,12 +48,12 @@ useradd -d /opt/wallabag -s /bin/bash -M wallabag chown -R wallabag:wallabag /opt/wallabag mv /opt/wallabag/app/config/parameters.yml.dist /opt/wallabag/app/config/parameters.yml sed -i \ - -e 's|database_name: wallabag|database_name: wallabag_db|' \ - -e 's|database_port: ~|database_port: 3306|' \ - -e 's|database_user: root|database_user: wallabag|' \ - -e 's|database_password: ~|database_password: '"$DB_PASS"'|' \ - -e 's|secret: .*|secret: '"$SECRET_KEY"'|' \ - /opt/wallabag/app/config/parameters.yml + -e 's|database_name: wallabag|database_name: wallabag_db|' \ + -e 's|database_port: ~|database_port: 3306|' \ + -e 's|database_user: root|database_user: wallabag|' \ + -e 's|database_password: ~|database_password: '"$DB_PASS"'|' \ + -e 's|secret: .*|secret: '"$SECRET_KEY"'|' \ + /opt/wallabag/app/config/parameters.yml export COMPOSER_ALLOW_SUPERUSER=1 sudo -u wallabag make install --no-interaction From a5a58d87b4e04804225105f8663b672e8c66b476 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 15 Sep 2025 15:43:58 +0200 Subject: [PATCH 0918/1733] Improve error handling and silent logging Expanded explain_exit_code with more exit codes for various tools (APT, Node.js, Python, databases) for better diagnostics. Updated silent() to set BASH_COMMAND on failure for improved error context. --- misc/core.func | 7 ++++++- misc/error_handler.func | 45 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/misc/core.func b/misc/core.func index 5937b3dd1..f4257b3eb 100644 --- a/misc/core.func +++ b/misc/core.func @@ -193,8 +193,13 @@ set_std_mode() { SILENT_LOGFILE="/tmp/silent.$$.log" silent() { + local cmd="$*" "$@" >>"$SILENT_LOGFILE" 2>&1 - return $? + local rc=$? + if [[ $rc -ne 0 ]]; then + BASH_COMMAND="$cmd" + fi + return $rc } # Function to download & save header files diff --git a/misc/error_handler.func b/misc/error_handler.func index 6bf08dc1e..d16a1a021 100644 --- a/misc/error_handler.func +++ b/misc/error_handler.func @@ -10,14 +10,55 @@ explain_exit_code() { local code="$1" case "$code" in + # --- Generic / Shell --- 1) echo "General error / Operation not permitted" ;; 2) echo "Misuse of shell builtins (e.g. syntax error)" ;; 126) echo "Command invoked cannot execute (permission problem?)" ;; 127) echo "Command not found" ;; 128) echo "Invalid argument to exit" ;; 130) echo "Terminated by Ctrl+C (SIGINT)" ;; - 137) echo "Killed (SIGKILL / out of memory?)" ;; + 137) echo "Killed (SIGKILL / Out of memory?)" ;; + 139) echo "Segmentation fault (core dumped)" ;; 143) echo "Terminated (SIGTERM)" ;; + + # --- Package manager / APT / DPKG --- + 100) echo "APT: Package manager error (broken packages / dependency problems)" ;; + 101) echo "APT: Configuration error (bad sources.list, malformed config)" ;; + 255) echo "DPKG: Fatal internal error" ;; + + # --- Node.js / npm / pnpm / yarn --- + 243) echo "Node.js: Out of memory (JavaScript heap out of memory)" ;; + 245) echo "Node.js: Invalid command-line option" ;; + 246) echo "Node.js: Internal JavaScript Parse Error" ;; + 247) echo "Node.js: Fatal internal error" ;; + 248) echo "Node.js: Invalid C++ addon / N-API failure" ;; + 249) echo "Node.js: Inspector error" ;; + 254) echo "npm/pnpm/yarn: Unknown fatal error" ;; + + # --- Python / pip / uv --- + 210) echo "Python: Virtualenv / uv environment missing or broken" ;; + 211) echo "Python: Dependency resolution failed" ;; + 212) echo "Python: Installation aborted (permissions or EXTERNALLY-MANAGED)" ;; + + # --- PostgreSQL --- + 231) echo "PostgreSQL: Connection failed (server not running / wrong socket)" ;; + 232) echo "PostgreSQL: Authentication failed (bad user/password)" ;; + 233) echo "PostgreSQL: Database does not exist" ;; + 234) echo "PostgreSQL: Fatal error in query / syntax" ;; + + # --- MySQL / MariaDB --- + 241) echo "MySQL/MariaDB: Connection failed (server not running / wrong socket)" ;; + 242) echo "MySQL/MariaDB: Authentication failed (bad user/password)" ;; + 243) echo "MySQL/MariaDB: Database does not exist" ;; + 244) echo "MySQL/MariaDB: Fatal error in query / syntax" ;; + + # --- MongoDB --- + 251) echo "MongoDB: Connection failed (server not running)" ;; + 252) echo "MongoDB: Authentication failed (bad user/password)" ;; + 253) echo "MongoDB: Database not found" ;; + 254) echo "MongoDB: Fatal query error" ;; + + # --- Proxmox Custom Codes --- 200) echo "Custom: Failed to create lock file" ;; 203) echo "Custom: Missing CTID variable" ;; 204) echo "Custom: Missing PCT_OSTYPE variable" ;; @@ -32,6 +73,8 @@ explain_exit_code() { 222) echo "Custom: Template download failed after 3 attempts" ;; 223) echo "Custom: Template not available after download" ;; 231) echo "Custom: LXC stack upgrade/retry failed" ;; + + # --- Default --- *) echo "Unknown error" ;; esac } From 266d121e92c2556da54b1fe0b73f65dc5fd5aa35 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 15 Sep 2025 15:52:52 +0200 Subject: [PATCH 0919/1733] Update build.func --- misc/build.func | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index e6b49e4e3..754be4c6e 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1978,7 +1978,9 @@ EOF' fi msg_ok "Customized LXC Container" install_ssh_keys_into_ct - lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)" + if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)"; then + exit $? + fi } destroy_lxc() { From f92479dfb0455671b0a8e95de07205aef3b5c678 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 15 Sep 2025 15:53:30 +0200 Subject: [PATCH 0920/1733] Update core.func --- misc/core.func | 2 ++ 1 file changed, 2 insertions(+) diff --git a/misc/core.func b/misc/core.func index f4257b3eb..7a15fd56d 100644 --- a/misc/core.func +++ b/misc/core.func @@ -194,8 +194,10 @@ SILENT_LOGFILE="/tmp/silent.$$.log" silent() { local cmd="$*" + set +Eeuo pipefail "$@" >>"$SILENT_LOGFILE" 2>&1 local rc=$? + set -Eeuo pipefail if [[ $rc -ne 0 ]]; then BASH_COMMAND="$cmd" fi From 7ca4f0e9fb17a3282cf33b4ddc61580779c346e7 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 15 Sep 2025 16:00:06 +0200 Subject: [PATCH 0921/1733] Update core.func --- misc/core.func | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/misc/core.func b/misc/core.func index 7a15fd56d..90a255a38 100644 --- a/misc/core.func +++ b/misc/core.func @@ -194,10 +194,12 @@ SILENT_LOGFILE="/tmp/silent.$$.log" silent() { local cmd="$*" - set +Eeuo pipefail + + trap - ERR "$@" >>"$SILENT_LOGFILE" 2>&1 local rc=$? - set -Eeuo pipefail + trap 'error_handler' ERR + if [[ $rc -ne 0 ]]; then BASH_COMMAND="$cmd" fi From 2510dd7da031910fad5b530ec2dba2fe8e2f1661 Mon Sep 17 00:00:00 2001 From: cobaltgit Date: Mon, 15 Sep 2025 17:56:32 +0100 Subject: [PATCH 0922/1733] alpine-caddy: revert to manually setting up go and xcaddy Alpine repos currently have an outdated version of Golang, latest version of caddy refuses to build with xcaddy --- install/alpine-caddy-install.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/install/alpine-caddy-install.sh b/install/alpine-caddy-install.sh index 5774087c8..c3bd7cf49 100644 --- a/install/alpine-caddy-install.sh +++ b/install/alpine-caddy-install.sh @@ -19,8 +19,13 @@ msg_ok "Installed Caddy" read -r -p "${TAB3}Would you like to install xCaddy Addon? " prompt if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then + GO_VERSION="$(curl -fsSL https://go.dev/VERSION?m=text | head -1 | cut -c3-)" setup_go msg_info "Setup xCaddy" - $STD apk add --no-cache xcaddy + cd /opt + RELEASE=$(curl -fsSL https://api.github.com/repos/caddyserver/xcaddy/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + curl -fsSL "https://github.com/caddyserver/xcaddy/releases/download/${RELEASE}/xcaddy_${RELEASE:1}_linux_amd64.tar.gz" -o "xcaddy_${RELEASE:1}_linux_amd64.tar.gz" + $STD tar xzf xcaddy_"${RELEASE:1}"_linux_amd64.tar.gz -C /usr/local/bin + rm -rf /opt/xcaddy* $STD xcaddy build msg_ok "Setup xCaddy" fi From ef69b4d0a72b2570ffcfa4ec6b926b93c927def2 Mon Sep 17 00:00:00 2001 From: cobaltgit Date: Mon, 15 Sep 2025 18:04:28 +0100 Subject: [PATCH 0923/1733] alpine-caddy: only extract xcaddy binary from release tarball --- install/alpine-caddy-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/alpine-caddy-install.sh b/install/alpine-caddy-install.sh index c3bd7cf49..9e798acca 100644 --- a/install/alpine-caddy-install.sh +++ b/install/alpine-caddy-install.sh @@ -6,7 +6,7 @@ # Source: https://caddyserver.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color +colorYea verb_ip6 catch_errors setting_up_container @@ -24,7 +24,7 @@ if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then cd /opt RELEASE=$(curl -fsSL https://api.github.com/repos/caddyserver/xcaddy/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') curl -fsSL "https://github.com/caddyserver/xcaddy/releases/download/${RELEASE}/xcaddy_${RELEASE:1}_linux_amd64.tar.gz" -o "xcaddy_${RELEASE:1}_linux_amd64.tar.gz" - $STD tar xzf xcaddy_"${RELEASE:1}"_linux_amd64.tar.gz -C /usr/local/bin + $STD tar xzf xcaddy_"${RELEASE:1}"_linux_amd64.tar.gz -C /usr/local/bin xcaddy rm -rf /opt/xcaddy* $STD xcaddy build msg_ok "Setup xCaddy" From cadd02ed4b1ee6db73a2a361b5e4999d5a1619a1 Mon Sep 17 00:00:00 2001 From: cobaltgit Date: Mon, 15 Sep 2025 18:05:16 +0100 Subject: [PATCH 0924/1733] alpine-caddy: fix typo! ok i guess we should implement colorYea :kekw: --- install/alpine-caddy-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/alpine-caddy-install.sh b/install/alpine-caddy-install.sh index 9e798acca..6873dced1 100644 --- a/install/alpine-caddy-install.sh +++ b/install/alpine-caddy-install.sh @@ -6,7 +6,7 @@ # Source: https://caddyserver.com/ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -colorYea +color verb_ip6 catch_errors setting_up_container From cde9ae6d4b5c6a523d74f80a791f208925ad9e3d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 08:39:21 +0200 Subject: [PATCH 0925/1733] Update core.func --- misc/core.func | 2 ++ 1 file changed, 2 insertions(+) diff --git a/misc/core.func b/misc/core.func index 90a255a38..42207a5b9 100644 --- a/misc/core.func +++ b/misc/core.func @@ -202,7 +202,9 @@ silent() { if [[ $rc -ne 0 ]]; then BASH_COMMAND="$cmd" + error_handler "$rc" fi + return $rc } From 5c750affa5a78c6671a2ac373ad47bcfb060b934 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 08:58:01 +0200 Subject: [PATCH 0926/1733] error-log --- misc/core.func | 4 +--- misc/error_handler.func | 13 +++++++++++-- misc/install.func | 3 +-- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/misc/core.func b/misc/core.func index 42207a5b9..17bf5f9c0 100644 --- a/misc/core.func +++ b/misc/core.func @@ -202,10 +202,8 @@ silent() { if [[ $rc -ne 0 ]]; then BASH_COMMAND="$cmd" - error_handler "$rc" + return $rc fi - - return $rc } # Function to download & save header files diff --git a/misc/error_handler.func b/misc/error_handler.func index d16a1a021..673927f48 100644 --- a/misc/error_handler.func +++ b/misc/error_handler.func @@ -81,11 +81,13 @@ explain_exit_code() { # === Error handler ============================================================ error_handler() { - local exit_code=$? + local exit_code=${1:-$?} local line_number=${BASH_LINENO[0]:-unknown} local command=${BASH_COMMAND:-unknown} - # Exitcode 0 = kein Fehler → ignorieren + # $STD im Kommando entfernen (nur kosmetisch) + command=${command//\$STD /} + if [[ "$exit_code" -eq 0 ]]; then return 0 fi @@ -107,6 +109,13 @@ error_handler() { } >>"$DEBUG_LOGFILE" fi + # Silent-Log + if [[ -n "${SILENT_LOGFILE:-}" && -s "$SILENT_LOGFILE" ]]; then + echo "--- Last 20 lines of silent log ($SILENT_LOGFILE) ---" + tail -n 20 "$SILENT_LOGFILE" + echo "---------------------------------------------------" + fi + exit "$exit_code" } diff --git a/misc/install.func b/misc/install.func index 1248c2733..b27bfc017 100644 --- a/misc/install.func +++ b/misc/install.func @@ -2,8 +2,7 @@ # Author: tteck (tteckster) # Co-Author: MickLesk # Co-Author: michelroegl-brunner -# License: MIT -# https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE if ! command -v curl >/dev/null 2>&1; then printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 From 4d29e2043cfa25575eb3379f94740dea2c5f3ddd Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Tue, 16 Sep 2025 08:59:19 +0200 Subject: [PATCH 0927/1733] First installer for warracker --- ct/warracker.sh | 60 ++++++++++++++++++ install/warracker-install.sh | 117 +++++++++++++++++++++++++++++++++++ 2 files changed, 177 insertions(+) create mode 100644 ct/warracker.sh create mode 100644 install/warracker-install.sh diff --git a/ct/warracker.sh b/ct/warracker.sh new file mode 100644 index 000000000..01ca7eea6 --- /dev/null +++ b/ct/warracker.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/bvdberg01/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: BvdBerg01 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/sassanix/Warracker/ + +APP="warracker" +var_tags="${var_tags:-warranty}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-4096}" +var_disk="${var_disk:-20}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +init_error_traps + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/signoz ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "signoz" "SigNoz/signoz"; then + msg_info "Stopping Services" + systemctl stop warracker + systemctl stop ngninx + msg_ok "Stopped Services" + + fetch_and_deploy_gh_release "warracker" "sassanix/Warracker" "tarball" "latest" "/opt/warracker" + + msg_info "Updating ${APP}" + + msg_ok "Updated $APP" + + msg_info "Starting Services" + systemctl start warracker + systemctl start ngninx + msg_ok "Started Services" + + msg_ok "Updated Successfully" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" diff --git a/install/warracker-install.sh b/install/warracker-install.sh new file mode 100644 index 000000000..18ce630cf --- /dev/null +++ b/install/warracker-install.sh @@ -0,0 +1,117 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: bvdberg01 +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://github.com/sassanix/Warracker/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +init_error_traps +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt-get install -y \ + apt-transport-https \ + ca-certificates\ + nginx +msg_ok "Installed Dependencies" + +PYTHON_VERSION="3.11" setup_uv +PG_VERSION="17" setup_postgresql + +msg_info "Installing Postgresql" +DB_NAME="warranty_db" +DB_USER="warranty_user" +DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) +DB_ADMIN_USER="warracker_admin" +DB_ADMIN_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) +systemctl start postgresql +$STD sudo -u postgres psql -c "CREATE USER $DB_USER WITH PASSWORD '$DB_PASS';" +$STD sudo -u postgres psql -c "CREATE USER $DB_ADMIN_USER WITH PASSWORD '$DB_ADMIN_PASS' SUPERUSER;" +$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME OWNER $DB_ADMIN_USER;" +$STD sudo -u postgres psql -d "$DB_NAME" -c "GRANT USAGE ON SCHEMA public TO $DB_USER;" +$STD sudo -u postgres psql -d "$DB_NAME" -c "GRANT CREATE ON SCHEMA public TO $DB_USER;" +$STD sudo -u postgres psql -d "$DB_NAME" -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO $DB_USER;" +{ + echo "Application Credentials" + echo "DB_NAME: $DB_NAME" + echo "DB_USER: $DB_USER" + echo "DB_PASS: $DB_PASS" + echo "DB_ADMIN_USER: $DB_ADMIN_USER" + echo "DB_ADMIN_PASS: $DB_ADMIN_PASS" +} >>~/warracker.creds +msg_ok "Installed PostgreSQL" + +fetch_and_deploy_gh_release "warracker" "sassanix/Warracker" "tarball" "latest" "/opt/warracker" + +msg_info "Installing Warracker" +cd /opt/warracker/backend +$STD uv venv .venv +$STD source .venv/bin/activate +$STD uv pip install -r requirements.txt +mv /opt/warracker/env.example /opt/warracker/.env +sed -i \ + -e "s/your_secure_database_password/$DB_PASS/" \ + -e "s/your_secure_admin_password/$DB_ADMIN_PASS/" \ + -e "s|^# DB_PORT=5432$|DB_HOST=127.0.0.1|" \ + /opt/warracker/.env + +mv /opt/warracker/nginx.conf /etc/nginx/sites-available/warracker.conf +sed -i \ + -e "s|alias /var/www/html/locales/;|alias /opt/warracker/locales/;|" \ + -e "s|/var/www/html|/opt/warracker/frontend|g" \ + -e "s/client_max_body_size __NGINX_MAX_BODY_SIZE_CONFIG_VALUE__/client_max_body_size 32M/" \ + /etc/nginx/sites-available/warracker.conf +ln -s /etc/nginx/sites-available/warracker.conf /etc/nginx/sites-enabled/warracker.conf +rm /etc/nginx/sites-enabled/default +systemctl restart nginx + +mkdir -p /data/uploads + +msg_ok "Installed Warracker" + +msg_info "Creating Services" +cat </etc/systemd/system/warrackermigration.service +[Unit] +Description=Warracker Migration Service +After=network.target + +[Service] +Type=oneshot +WorkingDirectory=/opt/warracker/backend/migrations +EnvironmentFile=/opt/warracker/.env +ExecStart=/opt/warracker/backend/.venv/bin/python apply_migrations.py + +[Install] +WantedBy=multi-user.target +EOF + +cat </etc/systemd/system/warracker.service +[Unit] +Description=Warracker Service +After=network.target warrackermigration.service +Requires=warrackermigration.service + +[Service] +WorkingDirectory=/opt/warracker +EnvironmentFile=/opt/warracker/.env +ExecStart=/opt/warracker/backend/.venv/bin/gunicorn --config /opt/warracker/backend/gunicorn_config.py backend:create_app() --bind 127.0.0.1:5000 +Restart=always + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now warracker +msg_ok "Created Services" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 8cd55d108ced45e0e0888120606d367707a8bf49 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 16 Sep 2025 06:59:44 +0000 Subject: [PATCH 0928/1733] Update .app files --- ct/headers/warracker | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/warracker diff --git a/ct/headers/warracker b/ct/headers/warracker new file mode 100644 index 000000000..b9e7c55d2 --- /dev/null +++ b/ct/headers/warracker @@ -0,0 +1,6 @@ + __ + _ ______ _______________ ______/ /_____ _____ +| | /| / / __ `/ ___/ ___/ __ `/ ___/ //_/ _ \/ ___/ +| |/ |/ / /_/ / / / / / /_/ / /__/ ,< / __/ / +|__/|__/\__,_/_/ /_/ \__,_/\___/_/|_|\___/_/ + From d078f1d15802e62e1841e879593c3457612894af Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 09:51:35 +0200 Subject: [PATCH 0929/1733] Update core.func --- misc/core.func | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/misc/core.func b/misc/core.func index 17bf5f9c0..42207a5b9 100644 --- a/misc/core.func +++ b/misc/core.func @@ -202,8 +202,10 @@ silent() { if [[ $rc -ne 0 ]]; then BASH_COMMAND="$cmd" - return $rc + error_handler "$rc" fi + + return $rc } # Function to download & save header files From 30c77b44192df1ea2386f308825553af63c24236 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 09:55:15 +0200 Subject: [PATCH 0930/1733] outputs --- misc/create_lxc.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/misc/create_lxc.sh b/misc/create_lxc.sh index ace589d4d..a85f27478 100644 --- a/misc/create_lxc.sh +++ b/misc/create_lxc.sh @@ -358,7 +358,7 @@ grep -qw "rootdir" <<<"$STORAGE_CONTENT" || { msg_error "Storage '$CONTAINER_STORAGE' does not support 'rootdir'. Cannot create LXC." exit 217 } -msg_ok "Storage '$CONTAINER_STORAGE' supports 'rootdir'" +$STD msg_ok "Storage '$CONTAINER_STORAGE' supports 'rootdir'" msg_info "Validating content types of template storage '$TEMPLATE_STORAGE'" TEMPLATE_CONTENT=$(grep -A4 -E "^[^:]+: $TEMPLATE_STORAGE" /etc/pve/storage.cfg | grep content | awk '{$1=""; print $0}' | xargs) @@ -366,7 +366,7 @@ msg_debug "Template storage '$TEMPLATE_STORAGE' has content types: $TEMPLATE_CON if ! grep -qw "vztmpl" <<<"$TEMPLATE_CONTENT"; then msg_warn "Template storage '$TEMPLATE_STORAGE' does not declare 'vztmpl'. This may cause pct create to fail." else - msg_ok "Template storage '$TEMPLATE_STORAGE' supports 'vztmpl'" + $STD msg_ok "Template storage '$TEMPLATE_STORAGE' supports 'vztmpl'" fi # Free space check @@ -457,7 +457,7 @@ elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then msg_warn "Template appears corrupted, but no online version exists. Keeping local file." fi else - msg_ok "Template $TEMPLATE is present and valid." + $STD msg_ok "Template $TEMPLATE is present and valid." fi if [[ "$TEMPLATE_SOURCE" == "local" && -n "$ONLINE_TEMPLATE" && "$TEMPLATE" != "$ONLINE_TEMPLATE" ]]; then From ce79bd0c08df3ac2ee2342318a48d816e5331e1f Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 09:57:35 +0200 Subject: [PATCH 0931/1733] outsource --- misc/build.func | 111 ++++++++++++++++++++++-------------------------- misc/core.func | 67 +++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 61 deletions(-) diff --git a/misc/build.func b/misc/build.func index 754be4c6e..4d5ac2b65 100644 --- a/misc/build.func +++ b/misc/build.func @@ -68,61 +68,61 @@ fi # exit 143 # } -# Check if the shell is using bash -shell_check() { - if [[ "$(basename "$SHELL")" != "bash" ]]; then - clear - msg_error "Your default shell is currently not set to Bash. To use these scripts, please switch to the Bash shell." - echo -e "\nExiting..." - sleep 2 - exit - fi -} +# # Check if the shell is using bash +# shell_check() { +# if [[ "$(basename "$SHELL")" != "bash" ]]; then +# clear +# msg_error "Your default shell is currently not set to Bash. To use these scripts, please switch to the Bash shell." +# echo -e "\nExiting..." +# sleep 2 +# exit +# fi +# } -# Run as root only -root_check() { - if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then - clear - msg_error "Please run this script as root." - echo -e "\nExiting..." - sleep 2 - exit - fi -} +# # Run as root only +# root_check() { +# if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then +# clear +# msg_error "Please run this script as root." +# echo -e "\nExiting..." +# sleep 2 +# exit +# fi +# } -# This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported. -# Supported: Proxmox VE 8.0.x – 8.9.x and 9.0 (NOT 9.1+) -pve_check() { - local PVE_VER - PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" +# # This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported. +# # Supported: Proxmox VE 8.0.x – 8.9.x and 9.0 (NOT 9.1+) +# pve_check() { +# local PVE_VER +# PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" - # Check for Proxmox VE 8.x: allow 8.0–8.9 - if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then - local MINOR="${BASH_REMATCH[1]}" - if ((MINOR < 0 || MINOR > 9)); then - msg_error "This version of Proxmox VE is not supported." - msg_error "Supported: Proxmox VE version 8.0 – 8.9" - exit 1 - fi - return 0 - fi +# # Check for Proxmox VE 8.x: allow 8.0–8.9 +# if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then +# local MINOR="${BASH_REMATCH[1]}" +# if ((MINOR < 0 || MINOR > 9)); then +# msg_error "This version of Proxmox VE is not supported." +# msg_error "Supported: Proxmox VE version 8.0 – 8.9" +# exit 1 +# fi +# return 0 +# fi - # Check for Proxmox VE 9.x: allow ONLY 9.0 - if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then - local MINOR="${BASH_REMATCH[1]}" - if ((MINOR != 0)); then - msg_error "This version of Proxmox VE is not yet supported." - msg_error "Supported: Proxmox VE version 9.0" - exit 1 - fi - return 0 - fi +# # Check for Proxmox VE 9.x: allow ONLY 9.0 +# if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then +# local MINOR="${BASH_REMATCH[1]}" +# if ((MINOR != 0)); then +# msg_error "This version of Proxmox VE is not yet supported." +# msg_error "Supported: Proxmox VE version 9.0" +# exit 1 +# fi +# return 0 +# fi - # All other unsupported versions - msg_error "This version of Proxmox VE is not supported." - msg_error "Supported versions: Proxmox VE 8.0 – 8.x or 9.0" - exit 1 -} +# # All other unsupported versions +# msg_error "This version of Proxmox VE is not supported." +# msg_error "Supported versions: Proxmox VE 8.0 – 8.x or 9.0" +# exit 1 +# } # When a node is running tens of containers, it's possible to exceed the kernel's cryptographic key storage allocations. # These are tuneable, so verify if the currently deployment is approaching the limits, advise the user on how to tune the limits, and exit the script. @@ -170,17 +170,6 @@ maxkeys_check() { echo -e "${CM}${GN} All kernel key limits are within safe thresholds.${CL}" } -# This function checks the system architecture and exits if it's not "amd64". -arch_check() { - if [ "$(dpkg --print-architecture)" != "amd64" ]; then - echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" - echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" - echo -e "Exiting..." - sleep 2 - exit - fi -} - # Function to get the current IP address based on the distribution get_current_ip() { if [ -f /etc/os-release ]; then diff --git a/misc/core.func b/misc/core.func index 42207a5b9..1cbc1d19a 100644 --- a/misc/core.func +++ b/misc/core.func @@ -208,6 +208,73 @@ silent() { return $rc } +# Check if the shell is using bash +shell_check() { + if [[ "$(basename "$SHELL")" != "bash" ]]; then + clear + msg_error "Your default shell is currently not set to Bash. To use these scripts, please switch to the Bash shell." + echo -e "\nExiting..." + sleep 2 + exit + fi +} + +# Run as root only +root_check() { + if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then + clear + msg_error "Please run this script as root." + echo -e "\nExiting..." + sleep 2 + exit + fi +} + +# This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported. +# Supported: Proxmox VE 8.0.x – 8.9.x and 9.0 (NOT 9.1+) +pve_check() { + local PVE_VER + PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" + + # Check for Proxmox VE 8.x: allow 8.0–8.9 + if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then + local MINOR="${BASH_REMATCH[1]}" + if ((MINOR < 0 || MINOR > 9)); then + msg_error "This version of Proxmox VE is not supported." + msg_error "Supported: Proxmox VE version 8.0 – 8.9" + exit 1 + fi + return 0 + fi + + # Check for Proxmox VE 9.x: allow ONLY 9.0 + if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then + local MINOR="${BASH_REMATCH[1]}" + if ((MINOR != 0)); then + msg_error "This version of Proxmox VE is not yet supported." + msg_error "Supported: Proxmox VE version 9.0" + exit 1 + fi + return 0 + fi + + # All other unsupported versions + msg_error "This version of Proxmox VE is not supported." + msg_error "Supported versions: Proxmox VE 8.0 – 8.x or 9.0" + exit 1 +} + +# This function checks the system architecture and exits if it's not "amd64". +arch_check() { + if [ "$(dpkg --print-architecture)" != "amd64" ]; then + echo -e "\n ${INFO}${YWB}This script will not work with PiMox! \n" + echo -e "\n ${YWB}Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" + echo -e "Exiting..." + sleep 2 + exit + fi +} + # Function to download & save header files get_header() { local app_name=$(echo "${APP,,}" | tr -d ' ') From 0e66031e3ca92fa0ce2e6b6642d36304150f38a2 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 09:59:06 +0200 Subject: [PATCH 0932/1733] error-handler --- misc/core.func | 4 +--- misc/error_handler.func | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/misc/core.func b/misc/core.func index 1cbc1d19a..1fc9e0349 100644 --- a/misc/core.func +++ b/misc/core.func @@ -202,10 +202,8 @@ silent() { if [[ $rc -ne 0 ]]; then BASH_COMMAND="$cmd" - error_handler "$rc" + return $rc fi - - return $rc } # Check if the shell is using bash diff --git a/misc/error_handler.func b/misc/error_handler.func index 673927f48..2e9ee0198 100644 --- a/misc/error_handler.func +++ b/misc/error_handler.func @@ -85,8 +85,8 @@ error_handler() { local line_number=${BASH_LINENO[0]:-unknown} local command=${BASH_COMMAND:-unknown} - # $STD im Kommando entfernen (nur kosmetisch) - command=${command//\$STD /} + # $STD removal to show the actual failing command + command="${command//\$STD/}" if [[ "$exit_code" -eq 0 ]]; then return 0 From f0f3032824f1cb25b2a9bc6d019f7e4210828478 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 10:02:54 +0200 Subject: [PATCH 0933/1733] cleanup --- misc/{ => deferred}/config-file.func | 0 misc/{ => deferred}/github.func | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename misc/{ => deferred}/config-file.func (100%) rename misc/{ => deferred}/github.func (100%) diff --git a/misc/config-file.func b/misc/deferred/config-file.func similarity index 100% rename from misc/config-file.func rename to misc/deferred/config-file.func diff --git a/misc/github.func b/misc/deferred/github.func similarity index 100% rename from misc/github.func rename to misc/deferred/github.func From 2b22c47976a79f1fee51e35a602177d50ec74674 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 10:04:37 +0200 Subject: [PATCH 0934/1733] Update core.func --- misc/core.func | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/misc/core.func b/misc/core.func index 1fc9e0349..011864a1b 100644 --- a/misc/core.func +++ b/misc/core.func @@ -189,7 +189,6 @@ set_std_mode() { fi } -# Silent execution function SILENT_LOGFILE="/tmp/silent.$$.log" silent() { @@ -202,8 +201,10 @@ silent() { if [[ $rc -ne 0 ]]; then BASH_COMMAND="$cmd" - return $rc + error_handler "$rc" fi + + return $rc } # Check if the shell is using bash From b9ce802c8f233a631b1dfe195aa4ba0df797bc13 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 10:07:28 +0200 Subject: [PATCH 0935/1733] Update core.func --- misc/core.func | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/misc/core.func b/misc/core.func index 011864a1b..d78bb553c 100644 --- a/misc/core.func +++ b/misc/core.func @@ -193,17 +193,13 @@ SILENT_LOGFILE="/tmp/silent.$$.log" silent() { local cmd="$*" - - trap - ERR + set +Eeuo pipefail "$@" >>"$SILENT_LOGFILE" 2>&1 local rc=$? - trap 'error_handler' ERR - + set -Eeuo pipefail if [[ $rc -ne 0 ]]; then BASH_COMMAND="$cmd" - error_handler "$rc" fi - return $rc } From c24defefd8f31972d78bf3a78e22a44a9652c8af Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 10:20:09 +0200 Subject: [PATCH 0936/1733] Update core.func --- misc/core.func | 1 + 1 file changed, 1 insertion(+) diff --git a/misc/core.func b/misc/core.func index d78bb553c..17516f637 100644 --- a/misc/core.func +++ b/misc/core.func @@ -199,6 +199,7 @@ silent() { set -Eeuo pipefail if [[ $rc -ne 0 ]]; then BASH_COMMAND="$cmd" + error_handler "$rc" fi return $rc } From 61c9c5ce803c6c41ba4579c47b3ebe5d89cde902 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 10:20:47 +0200 Subject: [PATCH 0937/1733] Update error_handler.func --- misc/error_handler.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/error_handler.func b/misc/error_handler.func index 2e9ee0198..7d78fab47 100644 --- a/misc/error_handler.func +++ b/misc/error_handler.func @@ -96,7 +96,7 @@ error_handler() { explanation="$(explain_exit_code "$exit_code")" printf "\e[?25h" - echo -e "\n${RD}[ERROR]${CL} in line ${RD}${line_number}${CL}: exit code ${RD}${exit_code}${CL} (${explanation}): while executing command ${YW}${command}${CL}\n" + echo -e "\n${RD}[ERROR]${CL} in line ${RD}${line_number}${CL}: exit code ${RD}${exit_code}${CL} (${explanation}): while executing command ${YWB}${command}${CL}\n" if [[ -n "${DEBUG_LOGFILE:-}" ]]; then { From 67ebb3782c76accbec9459f1281ba785c6442d56 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 10:26:00 +0200 Subject: [PATCH 0938/1733] fixes --- misc/core.func | 3 +-- misc/error_handler.func | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/misc/core.func b/misc/core.func index 17516f637..086a105e1 100644 --- a/misc/core.func +++ b/misc/core.func @@ -198,8 +198,7 @@ silent() { local rc=$? set -Eeuo pipefail if [[ $rc -ne 0 ]]; then - BASH_COMMAND="$cmd" - error_handler "$rc" + error_handler "$rc" "$cmd" fi return $rc } diff --git a/misc/error_handler.func b/misc/error_handler.func index 7d78fab47..2e47ece08 100644 --- a/misc/error_handler.func +++ b/misc/error_handler.func @@ -82,10 +82,9 @@ explain_exit_code() { # === Error handler ============================================================ error_handler() { local exit_code=${1:-$?} + local command=${2:-${BASH_COMMAND:-unknown}} local line_number=${BASH_LINENO[0]:-unknown} - local command=${BASH_COMMAND:-unknown} - # $STD removal to show the actual failing command command="${command//\$STD/}" if [[ "$exit_code" -eq 0 ]]; then From ba51df5bb1225d5076732bbc4eb4e57efdb7f39b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 10:41:55 +0200 Subject: [PATCH 0939/1733] testing --- misc/core.func | 10 +++++++--- misc/error_handler.func | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/misc/core.func b/misc/core.func index 086a105e1..011864a1b 100644 --- a/misc/core.func +++ b/misc/core.func @@ -193,13 +193,17 @@ SILENT_LOGFILE="/tmp/silent.$$.log" silent() { local cmd="$*" - set +Eeuo pipefail + + trap - ERR "$@" >>"$SILENT_LOGFILE" 2>&1 local rc=$? - set -Eeuo pipefail + trap 'error_handler' ERR + if [[ $rc -ne 0 ]]; then - error_handler "$rc" "$cmd" + BASH_COMMAND="$cmd" + error_handler "$rc" fi + return $rc } diff --git a/misc/error_handler.func b/misc/error_handler.func index 2e47ece08..fc39f6a4a 100644 --- a/misc/error_handler.func +++ b/misc/error_handler.func @@ -85,6 +85,7 @@ error_handler() { local command=${2:-${BASH_COMMAND:-unknown}} local line_number=${BASH_LINENO[0]:-unknown} + # clean up cosmetic STD variable command="${command//\$STD/}" if [[ "$exit_code" -eq 0 ]]; then @@ -95,7 +96,7 @@ error_handler() { explanation="$(explain_exit_code "$exit_code")" printf "\e[?25h" - echo -e "\n${RD}[ERROR]${CL} in line ${RD}${line_number}${CL}: exit code ${RD}${exit_code}${CL} (${explanation}): while executing command ${YWB}${command}${CL}\n" + echo -e "\n${RD}[ERROR]${CL} in line ${RD}${line_number}${CL}: exit code ${RD}${exit_code}${CL} (${explanation}): while executing command ${YW}${command}${CL}\n" if [[ -n "${DEBUG_LOGFILE:-}" ]]; then { @@ -108,7 +109,6 @@ error_handler() { } >>"$DEBUG_LOGFILE" fi - # Silent-Log if [[ -n "${SILENT_LOGFILE:-}" && -s "$SILENT_LOGFILE" ]]; then echo "--- Last 20 lines of silent log ($SILENT_LOGFILE) ---" tail -n 20 "$SILENT_LOGFILE" From cb29e50080b8303bd10189659dc803a58b82af03 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 10:49:47 +0200 Subject: [PATCH 0940/1733] testing --- misc/core.func | 12 +++++------- misc/error_handler.func | 3 +-- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/misc/core.func b/misc/core.func index 011864a1b..bfa597bdb 100644 --- a/misc/core.func +++ b/misc/core.func @@ -193,18 +193,16 @@ SILENT_LOGFILE="/tmp/silent.$$.log" silent() { local cmd="$*" - - trap - ERR + set +Eeuo pipefail "$@" >>"$SILENT_LOGFILE" 2>&1 local rc=$? - trap 'error_handler' ERR + set -Eeuo pipefail if [[ $rc -ne 0 ]]; then - BASH_COMMAND="$cmd" - error_handler "$rc" + # Store real command in a helper variable + LAST_SILENT_CMD="$cmd" + return $rc # let ERR trap fire fi - - return $rc } # Check if the shell is using bash diff --git a/misc/error_handler.func b/misc/error_handler.func index fc39f6a4a..d370cb566 100644 --- a/misc/error_handler.func +++ b/misc/error_handler.func @@ -82,10 +82,9 @@ explain_exit_code() { # === Error handler ============================================================ error_handler() { local exit_code=${1:-$?} - local command=${2:-${BASH_COMMAND:-unknown}} local line_number=${BASH_LINENO[0]:-unknown} + local command=${LAST_SILENT_CMD:-${BASH_COMMAND:-unknown}} - # clean up cosmetic STD variable command="${command//\$STD/}" if [[ "$exit_code" -eq 0 ]]; then From d89e19db7458e1ebf02e962a6b67e206238b068d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 10:56:56 +0200 Subject: [PATCH 0941/1733] testing error --- misc/core.func | 6 +++--- misc/error_handler.func | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/misc/core.func b/misc/core.func index bfa597bdb..985685a19 100644 --- a/misc/core.func +++ b/misc/core.func @@ -199,10 +199,10 @@ silent() { set -Eeuo pipefail if [[ $rc -ne 0 ]]; then - # Store real command in a helper variable - LAST_SILENT_CMD="$cmd" - return $rc # let ERR trap fire + error_handler "$rc" "$cmd" fi + + return $rc } # Check if the shell is using bash diff --git a/misc/error_handler.func b/misc/error_handler.func index d370cb566..edbf6f1a6 100644 --- a/misc/error_handler.func +++ b/misc/error_handler.func @@ -82,8 +82,8 @@ explain_exit_code() { # === Error handler ============================================================ error_handler() { local exit_code=${1:-$?} + local command=${2:-${BASH_COMMAND:-unknown}} local line_number=${BASH_LINENO[0]:-unknown} - local command=${LAST_SILENT_CMD:-${BASH_COMMAND:-unknown}} command="${command//\$STD/}" @@ -95,7 +95,7 @@ error_handler() { explanation="$(explain_exit_code "$exit_code")" printf "\e[?25h" - echo -e "\n${RD}[ERROR]${CL} in line ${RD}${line_number}${CL}: exit code ${RD}${exit_code}${CL} (${explanation}): while executing command ${YW}${command}${CL}\n" + echo -e "\n${RD}[ERROR]${CL} in line ${RD}${line_number}${CL}: exit code ${RD}${exit_code}${CL} (${explanation}): while executing command ${YWB}${command}${CL}\n" if [[ -n "${DEBUG_LOGFILE:-}" ]]; then { From 9d496e563b9e958b2971048f1d8dfc8bbd6cf8e6 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 11:02:21 +0200 Subject: [PATCH 0942/1733] Update core.func --- misc/core.func | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/misc/core.func b/misc/core.func index 985685a19..53c403044 100644 --- a/misc/core.func +++ b/misc/core.func @@ -199,7 +199,19 @@ silent() { set -Eeuo pipefail if [[ $rc -ne 0 ]]; then - error_handler "$rc" "$cmd" + local explanation + explanation="$(explain_exit_code "$rc")" + + printf "\e[?25h" + echo -e "\n${RD}[ERROR]${CL}: exit code ${RD}${rc}${CL} (${explanation}): while executing command ${YW}${cmd}${CL}\n" + + if [[ -s "$SILENT_LOGFILE" ]]; then + echo "--- Last 20 lines of silent log ($SILENT_LOGFILE) ---" + tail -n 20 "$SILENT_LOGFILE" + echo "---------------------------------------------------" + fi + + exit "$rc" fi return $rc From 983ea68da6f2ee89dff63cf383d71d3c1d9dd809 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 11:09:59 +0200 Subject: [PATCH 0943/1733] Update core.func --- misc/core.func | 2 -- 1 file changed, 2 deletions(-) diff --git a/misc/core.func b/misc/core.func index 53c403044..e3c9d265d 100644 --- a/misc/core.func +++ b/misc/core.func @@ -213,8 +213,6 @@ silent() { exit "$rc" fi - - return $rc } # Check if the shell is using bash From 773ae42241497234f5c412971c8e040207f2bf6a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 11:16:01 +0200 Subject: [PATCH 0944/1733] test --- misc/core.func | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/misc/core.func b/misc/core.func index e3c9d265d..b614fb158 100644 --- a/misc/core.func +++ b/misc/core.func @@ -193,26 +193,20 @@ SILENT_LOGFILE="/tmp/silent.$$.log" silent() { local cmd="$*" + local caller_line="${BASH_LINENO[0]:-unknown}" # Capture the line where silent was called + set +Eeuo pipefail "$@" >>"$SILENT_LOGFILE" 2>&1 local rc=$? set -Eeuo pipefail if [[ $rc -ne 0 ]]; then - local explanation - explanation="$(explain_exit_code "$rc")" - - printf "\e[?25h" - echo -e "\n${RD}[ERROR]${CL}: exit code ${RD}${rc}${CL} (${explanation}): while executing command ${YW}${cmd}${CL}\n" - - if [[ -s "$SILENT_LOGFILE" ]]; then - echo "--- Last 20 lines of silent log ($SILENT_LOGFILE) ---" - tail -n 20 "$SILENT_LOGFILE" - echo "---------------------------------------------------" - fi - - exit "$rc" + # Call error_handler with proper line number context + BASH_LINENO[0]=$caller_line # Set the line number for error_handler + error_handler "$rc" "$cmd" fi + + return $rc } # Check if the shell is using bash From ca023e077e5fc88c4e9fce9b5ab33d5dd6526ef0 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 11:19:19 +0200 Subject: [PATCH 0945/1733] Update core.func --- misc/core.func | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/misc/core.func b/misc/core.func index b614fb158..16bc289d7 100644 --- a/misc/core.func +++ b/misc/core.func @@ -193,7 +193,7 @@ SILENT_LOGFILE="/tmp/silent.$$.log" silent() { local cmd="$*" - local caller_line="${BASH_LINENO[0]:-unknown}" # Capture the line where silent was called + local caller_line="${BASH_LINENO[0]:-unknown}" set +Eeuo pipefail "$@" >>"$SILENT_LOGFILE" 2>&1 @@ -201,12 +201,20 @@ silent() { set -Eeuo pipefail if [[ $rc -ne 0 ]]; then - # Call error_handler with proper line number context - BASH_LINENO[0]=$caller_line # Set the line number for error_handler - error_handler "$rc" "$cmd" - fi + local explanation + explanation="$(explain_exit_code "$rc")" - return $rc + printf "\e[?25h" + echo -e "\n${RD}[ERROR]${CL} in line ${RD}${caller_line}${CL}: exit code ${RD}${rc}${CL} (${explanation}): while executing command ${YWB}${cmd}${CL}\n" + + if [[ -s "$SILENT_LOGFILE" ]]; then + echo "--- Last 20 lines of silent log ($SILENT_LOGFILE) ---" + tail -n 20 "$SILENT_LOGFILE" + echo "---------------------------------------------------" + fi + + exit "$rc" + fi } # Check if the shell is using bash From 7c0f758e2a973bf74e8fba7432ab4d2875787c6e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 11:28:20 +0200 Subject: [PATCH 0946/1733] Update core.func --- misc/core.func | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/misc/core.func b/misc/core.func index 16bc289d7..cee80d6b0 100644 --- a/misc/core.func +++ b/misc/core.func @@ -194,19 +194,39 @@ SILENT_LOGFILE="/tmp/silent.$$.log" silent() { local cmd="$*" local caller_line="${BASH_LINENO[0]:-unknown}" + local caller_func="${FUNCNAME[1]:-main}" + local caller_file="${BASH_SOURCE[1]:-unknown}" + + # Debug info + if [[ "${DEBUG:-0}" == "1" ]]; then + echo "[DEBUG] Calling silent from $caller_file:$caller_line in function $caller_func" >&2 + echo "[DEBUG] Command: $cmd" >&2 + fi set +Eeuo pipefail + trap - ERR + "$@" >>"$SILENT_LOGFILE" 2>&1 local rc=$? + set -Eeuo pipefail + trap 'error_handler' ERR if [[ $rc -ne 0 ]]; then + # Source explain_exit_code if needed + if ! declare -f explain_exit_code >/dev/null 2>&1; then + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) + fi + local explanation explanation="$(explain_exit_code "$rc")" printf "\e[?25h" echo -e "\n${RD}[ERROR]${CL} in line ${RD}${caller_line}${CL}: exit code ${RD}${rc}${CL} (${explanation}): while executing command ${YWB}${cmd}${CL}\n" + # Additional context + echo -e "${RD}Context:${CL} Called from ${caller_func} in ${caller_file}" + if [[ -s "$SILENT_LOGFILE" ]]; then echo "--- Last 20 lines of silent log ($SILENT_LOGFILE) ---" tail -n 20 "$SILENT_LOGFILE" From 8aa95f954968f13e9c515b65bc5ddb403d0f07c6 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 11:44:24 +0200 Subject: [PATCH 0947/1733] Update core.func --- misc/core.func | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/misc/core.func b/misc/core.func index cee80d6b0..660c656b7 100644 --- a/misc/core.func +++ b/misc/core.func @@ -194,14 +194,6 @@ SILENT_LOGFILE="/tmp/silent.$$.log" silent() { local cmd="$*" local caller_line="${BASH_LINENO[0]:-unknown}" - local caller_func="${FUNCNAME[1]:-main}" - local caller_file="${BASH_SOURCE[1]:-unknown}" - - # Debug info - if [[ "${DEBUG:-0}" == "1" ]]; then - echo "[DEBUG] Calling silent from $caller_file:$caller_line in function $caller_func" >&2 - echo "[DEBUG] Command: $cmd" >&2 - fi set +Eeuo pipefail trap - ERR @@ -222,15 +214,19 @@ silent() { explanation="$(explain_exit_code "$rc")" printf "\e[?25h" - echo -e "\n${RD}[ERROR]${CL} in line ${RD}${caller_line}${CL}: exit code ${RD}${rc}${CL} (${explanation}): while executing command ${YWB}${cmd}${CL}\n" - - # Additional context - echo -e "${RD}Context:${CL} Called from ${caller_func} in ${caller_file}" + echo -e "\n${RD}[ERROR]${CL} in line ${RD}${caller_line}${CL}: exit code ${RD}${rc}${CL} (${explanation})" + echo -e "${RD}Command:${CL} ${YWB}${cmd}${CL}\n" if [[ -s "$SILENT_LOGFILE" ]]; then - echo "--- Last 20 lines of silent log ($SILENT_LOGFILE) ---" - tail -n 20 "$SILENT_LOGFILE" - echo "---------------------------------------------------" + local log_lines=$(wc -l <"$SILENT_LOGFILE") + echo "--- Last 10 lines of silent log ---" + tail -n 10 "$SILENT_LOGFILE" + echo "-----------------------------------" + + # Show how to view full log if there are more lines + if [[ $log_lines -gt 10 ]]; then + echo -e "${YW}View full log (${log_lines} lines):${CL} cat $SILENT_LOGFILE" + fi fi exit "$rc" From 6e16aa01d62fe0ce9dc811bcc7255a2a2096a154 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 11:51:18 +0200 Subject: [PATCH 0948/1733] Update GlobaLeaks scripts for Debian 13 (trixie) support Changed default OS version from Debian 12 to 13 and updated the GlobaLeaks APT repository to use 'trixie' instead of 'bookworm'. Also switched apt-get commands to apt for consistency and modern usage. --- ct/globaleaks.sh | 24 ++++++++++++------------ install/globaleaks-install.sh | 6 +++--- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/ct/globaleaks.sh b/ct/globaleaks.sh index 16e60b1ac..3e1abc897 100644 --- a/ct/globaleaks.sh +++ b/ct/globaleaks.sh @@ -11,7 +11,7 @@ var_disk="${var_disk:-4}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_os="${var_os:-debian}" -var_version="${var_version:-12}" +var_version="${var_version:-13}" header_info "$APP" variables @@ -19,18 +19,18 @@ color init_error_traps function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -f /usr/sbin/globaleaks ]]; then - msg_error "No ${APP} installation found!" - exit - fi + header_info + check_container_storage + check_container_resources + if [[ ! -f /usr/sbin/globaleaks ]]; then + msg_error "No ${APP} installation found!" + exit + fi - msg_info "Updating $APP LXC" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "Updated $APP LXC" + msg_info "Updating $APP LXC" + $STD apt update + $STD apt -y upgrade + msg_ok "Updated $APP LXC" } start diff --git a/install/globaleaks-install.sh b/install/globaleaks-install.sh index 173899a95..f97263ee1 100644 --- a/install/globaleaks-install.sh +++ b/install/globaleaks-install.sh @@ -14,9 +14,9 @@ update_os msg_info "Setup GlobaLeaks" curl -fsSL https://deb.globaleaks.org/globaleaks.asc | gpg --dearmor >/etc/apt/trusted.gpg.d/globaleaks.asc -echo "deb [signed-by=/etc/apt/trusted.gpg.d/globaleaks.asc] http://deb.globaleaks.org bookworm main" >/etc/apt/sources.list.d/globaleaks.list -$STD apt-get update -$STD apt-get -y install globaleaks +echo "deb [signed-by=/etc/apt/trusted.gpg.d/globaleaks.asc] http://deb.globaleaks.org trixie main" >/etc/apt/sources.list.d/globaleaks.list +$STD apt update +$STD apt -y install globaleaks msg_ok "Setup GlobaLeaks" motd_ssh From 9a1f0de47e0844770350a4c251d85ff2a2cdb89e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 13:05:02 +0200 Subject: [PATCH 0949/1733] Refactor app defaults save logic and remove error handlers Reworks the app defaults save/update logic in misc/build.func to support updating existing defaults with a diff and interactive menu, and adds robust var_* parsing and whitelisting. Removes legacy error and curl handler functions from misc/core.func, streamlining error handling and reducing code duplication. --- misc/build.func | 329 ++++++++++++++++++++++++++++++------------------ misc/core.func | 129 ------------------- 2 files changed, 207 insertions(+), 251 deletions(-) diff --git a/misc/build.func b/misc/build.func index 4d5ac2b65..7674808c6 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1017,7 +1017,7 @@ var_unprivileged=1 # Storage # Example: "local", "docker", ... # var_template_storage=local -# var_container_storage=docker +# var_container_storage=local # Resources var_cpu=1 @@ -1173,178 +1173,263 @@ get_app_defaults_path() { # - Only writes whitelisted var_* keys. # - Extracts raw values from flags like ",gw=..." ",mtu=..." etc. # ------------------------------------------------------------------------------ -maybe_offer_save_app_defaults() { - local app_vars_path - app_vars_path="$(get_app_defaults_path)" +if ! declare -p VAR_WHITELIST >/dev/null 2>&1; then + declare -ag VAR_WHITELIST=( + var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_ctid var_disk var_fuse + var_gateway var_hostname var_ipv6_method var_ipv6_static var_mac var_mtu + var_net var_ns var_pw var_ram var_tags var_tun var_unprivileged + var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage + ) +fi - # Only offer if file does not exist yet - if [ -f "$app_vars_path" ]; then - return 0 +_is_whitelisted_key() { + local k="$1" + local w + for w in "${VAR_WHITELIST[@]}"; do [[ "$k" == "$w" ]] && return 0; done + return 1 +} + +_sanitize_value() { + # Disallow Command-Substitution / Shell-Meta + case "$1" in + *'$('*|*'`'*|*';'*|*'&'*|*'<('* ) echo ""; return 0 ;; + esac + echo "$1" +} + +# Map-Parser: liest var_* aus Datei in eine assoziative Map (Name hart: _VARS_IN) +declare -A _VARS_IN +_load_vars_file_to_map() { + local file="$1" + _VARS_IN=() + [[ -f "$file" ]] || return 0 + local line + while IFS= read -r line || [[ -n "$line" ]]; do + line="${line#"${line%%[![:space:]]*}"}"; line="${line%"${line##*[![:space:]]}"}" + [[ -z "$line" || "$line" == \#* ]] && continue + if [[ "$line" =~ ^([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then + local k="${BASH_REMATCH[1]}" + local v="${BASH_REMATCH[2]}" + [[ "$k" == var_* ]] || continue + _is_whitelisted_key "$k" || continue + # Quotes strippen + if [[ "$v" =~ ^\"(.*)\"$ ]]; then v="${BASH_REMATCH[1]}"; fi + if [[ "$v" =~ ^\'(.*)\'$ ]]; then v="${BASH_REMATCH[1]}"; fi + _VARS_IN["$k"]="$v" + fi + done <"$file" +} + +# Diff zweier Dateien mit var_* → gibt in $1 (old) vs $2 (new) eine menschenlesbare Diff-Liste aus +_build_vars_diff() { + local oldf="$1" newf="$2" + local k + local -A OLD=() NEW=() + _load_vars_file_to_map "$oldf"; for k in "${!_VARS_IN[@]}"; do OLD["$k"]="${_VARS_IN[$k]}"; done + _load_vars_file_to_map "$newf"; for k in "${!_VARS_IN[@]}"; do NEW["$k"]="${_VARS_IN[$k]}"; done + + local out + out+="# Diff for ${APP} (${NSAPP})\n" + out+="# Old: ${oldf}\n# New: ${newf}\n\n" + + local found_change=0 + + # Changed & Removed + for k in "${!OLD[@]}"; do + if [[ -v NEW["$k"] ]]; then + if [[ "${OLD[$k]}" != "${NEW[$k]}" ]]; then + out+="~ ${k}\n - old: ${OLD[$k]}\n + new: ${NEW[$k]}\n" + found_change=1 + fi + else + out+="- ${k}\n - old: ${OLD[$k]}\n" + found_change=1 + fi + done + + # Added + for k in "${!NEW[@]}"; do + if [[ ! -v OLD["$k"] ]]; then + out+="+ ${k}\n + new: ${NEW[$k]}\n" + found_change=1 + fi + done + + if [[ $found_change -eq 0 ]]; then + out+="(No differences)\n" fi - # Ask user (English prompt as requested) - if ! whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --yesno "Save these advanced settings as defaults for ${APP}?\n\nThis will create:\n${app_vars_path}" 12 72; then - return 0 - fi + printf "%b" "$out" +} - # Ensure directory exists - mkdir -p "$(dirname "$app_vars_path")" +# Baut aus der aktuellen Advanced-Auswahl eine temporäre .vars-Datei +_build_current_app_vars_tmp() { + local tmpf + tmpf="$(mktemp -p /tmp "${NSAPP:-app}.vars.new.XXXXXX")" - # Normalizers (extract raw values from flags used during building) - local _val - - # NET/GATE: NET is either 'dhcp' or a CIDR; GATE holds ',gw=IP' or '' + # NET/GW local _net="${NET:-}" local _gate="" - if [[ "${GATE:-}" =~ ^,gw= ]]; then - _gate="${GATE#,gw=}" - fi + [[ "${GATE:-}" =~ ^,gw= ]] && _gate="${GATE#,gw=}" - # IPv6: method + optional static + optional gateway + # IPv6 local _ipv6_method="${IPV6_METHOD:-auto}" local _ipv6_static="" local _ipv6_gateway="" case "$_ipv6_method" in - static) - _ipv6_static="${IPV6_ADDR:-}" - _ipv6_gateway="${IPV6_GATE:-}" - ;; + static) + _ipv6_static="${IPV6_ADDR:-}" + _ipv6_gateway="${IPV6_GATE:-}" + ;; esac - # MTU: MTU looks like ',mtu=1500' or '' - local _mtu="" - if [[ "${MTU:-}" =~ ^,mtu= ]]; then - _mtu="${MTU#,mtu=}" - fi + # MTU/VLAN/MAC + local _mtu="" _vlan="" _mac="" + [[ "${MTU:-}" =~ ^,mtu= ]] && _mtu="${MTU#,mtu=}" + [[ "${VLAN:-}" =~ ^,tag= ]] && _vlan="${VLAN#,tag=}" + [[ "${MAC:-}" =~ ^,hwaddr=]] && _mac="${MAC#,hwaddr=}" - # VLAN: ',tag=NN' or '' - local _vlan="" - if [[ "${VLAN:-}" =~ ^,tag= ]]; then - _vlan="${VLAN#,tag=}" - fi + # DNS/SD + local _ns="" _searchdomain="" + [[ "${NS:-}" =~ ^-nameserver= ]] && _ns="${NS#-nameserver=}" + [[ "${SD:-}" =~ ^-searchdomain= ]] && _searchdomain="${SD#-searchdomain=}" - # MAC: ',hwaddr=XX:XX:...' or '' - local _mac="" - if [[ "${MAC:-}" =~ ^,hwaddr= ]]; then - _mac="${MAC#,hwaddr=}" - fi - - # DNS nameserver: NS is like '-nameserver=IP' or '' - local _ns="" - if [[ "${NS:-}" =~ ^-nameserver= ]]; then - _ns="${NS#-nameserver=}" - fi - - # Search domain: SD is like '-searchdomain=foo' or '' - local _searchdomain="" - if [[ "${SD:-}" =~ ^-searchdomain= ]]; then - _searchdomain="${SD#-searchdomain=}" - fi - - # Authorized key: raw string already - local _ssh_auth="${SSH_AUTHORIZED_KEY:-}" - - # SSH enabled: "yes"/"no" + # SSH / APT / Features local _ssh="${SSH:-no}" - - # APT cacher + local _ssh_auth="${SSH_AUTHORIZED_KEY:-}" local _apt_cacher="${APT_CACHER:-}" local _apt_cacher_ip="${APT_CACHER_IP:-}" - - # Features local _fuse="${ENABLE_FUSE:-no}" local _tun="${ENABLE_TUN:-no}" - - # Tags: TAGS may include 'community-script;' etc. Keep as-is unless empty local _tags="${TAGS:-}" + local _verbose="${VERBOSE:-no}" - # Unprivileged container type: CT_TYPE is "1" (unpriv) or "0" (priv) + # Typ/Resourcen/Identity local _unpriv="${CT_TYPE:-1}" - - # Resources and names local _cpu="${CORE_COUNT:-1}" local _ram="${RAM_SIZE:-1024}" local _disk="${DISK_SIZE:-4}" local _hostname="${HN:-$NSAPP}" - # Verbose - local _verbose="${VERBOSE:-no}" - - # Optional storages if already known in this phase + # Storage (falls vorhanden) local _tpl_storage="${TEMPLATE_STORAGE:-}" local _ct_storage="${CONTAINER_STORAGE:-}" - # Sanitize function for values (basic safety for config file) - _sanitize_value() { - local s="$1" - # Disallow backticks, $(), <(), ;, & - case "$s" in - *'$('* | *'`'* | *';'* | *'&'* | *'<('*) - echo "" - ;; - *) - echo "$s" - ;; - esac - } - - # Build the file content { echo "# App-specific defaults for ${APP} (${NSAPP})" echo "# Generated on $(date -u '+%Y-%m-%dT%H:%M:%SZ')" - echo "# Only var_* keys are read by the loader." echo - # Container type echo "var_unprivileged=$(_sanitize_value "$_unpriv")" - - # Resources echo "var_cpu=$(_sanitize_value "$_cpu")" echo "var_ram=$(_sanitize_value "$_ram")" echo "var_disk=$(_sanitize_value "$_disk")" - # Network - [ -n "$BRG" ] && echo "var_brg=$(_sanitize_value "$BRG")" - [ -n "$_net" ] && echo "var_net=$(_sanitize_value "$_net")" - [ -n "$_gate" ] && echo "var_gateway=$(_sanitize_value "$_gate")" - [ -n "$_mtu" ] && echo "var_mtu=$(_sanitize_value "$_mtu")" - [ -n "$_vlan" ] && echo "var_vlan=$(_sanitize_value "$_vlan")" - [ -n "$_mac" ] && echo "var_mac=$(_sanitize_value "$_mac")" - [ -n "$_ns" ] && echo "var_ns=$(_sanitize_value "$_ns")" + [[ -n "${BRG:-}" ]] && echo "var_brg=$(_sanitize_value "$BRG")" + [[ -n "$_net" ]] && echo "var_net=$(_sanitize_value "$_net")" + [[ -n "$_gate" ]] && echo "var_gateway=$(_sanitize_value "$_gate")" + [[ -n "$_mtu" ]] && echo "var_mtu=$(_sanitize_value "$_mtu")" + [[ -n "$_vlan" ]] && echo "var_vlan=$(_sanitize_value "$_vlan")" + [[ -n "$_mac" ]] && echo "var_mac=$(_sanitize_value "$_mac")" + [[ -n "$_ns" ]] && echo "var_ns=$(_sanitize_value "$_ns")" - # IPv6 - [ -n "$_ipv6_method" ] && echo "var_ipv6_method=$(_sanitize_value "$_ipv6_method")" - [ -n "$_ipv6_static" ] && echo "var_ipv6_static=$(_sanitize_value "$_ipv6_static")" - # Note: we do not persist a dedicated var for IPv6 gateway; can be derived if needed + [[ -n "$_ipv6_method" ]] && echo "var_ipv6_method=$(_sanitize_value "$_ipv6_method")" + [[ -n "$_ipv6_static" ]] && echo "var_ipv6_static=$(_sanitize_value "$_ipv6_static")" - # SSH - [ -n "$_ssh" ] && echo "var_ssh=$(_sanitize_value "$_ssh")" - [ -n "$_ssh_auth" ] && echo "var_ssh_authorized_key=$(_sanitize_value "$_ssh_auth")" + [[ -n "$_ssh" ]] && echo "var_ssh=$(_sanitize_value "$_ssh")" + [[ -n "$_ssh_auth" ]] && echo "var_ssh_authorized_key=$(_sanitize_value "$_ssh_auth")" - # APT cacher - [ -n "$_apt_cacher" ] && echo "var_apt_cacher=$(_sanitize_value "$_apt_cacher")" - [ -n "$_apt_cacher_ip" ] && echo "var_apt_cacher_ip=$(_sanitize_value "$_apt_cacher_ip")" + [[ -n "$_apt_cacher" ]] && echo "var_apt_cacher=$(_sanitize_value "$_apt_cacher")" + [[ -n "$_apt_cacher_ip" ]] && echo "var_apt_cacher_ip=$(_sanitize_value "$_apt_cacher_ip")" - # Features / tags / verbosity - [ -n "$_fuse" ] && echo "var_fuse=$(_sanitize_value "$_fuse")" - [ -n "$_tun" ] && echo "var_tun=$(_sanitize_value "$_tun")" - [ -n "$_tags" ] && echo "var_tags=$(_sanitize_value "$_tags")" - [ -n "$_verbose" ] && echo "var_verbose=$(_sanitize_value "$_verbose")" + [[ -n "$_fuse" ]] && echo "var_fuse=$(_sanitize_value "$_fuse")" + [[ -n "$_tun" ]] && echo "var_tun=$(_sanitize_value "$_tun")" + [[ -n "$_tags" ]] && echo "var_tags=$(_sanitize_value "$_tags")" + [[ -n "$_verbose" ]] && echo "var_verbose=$(_sanitize_value "$_verbose")" - # Identity (optional) - [ -n "$_hostname" ] && echo "var_hostname=$(_sanitize_value "$_hostname")" - [ -n "$_searchdomain" ] && echo "var_searchdomain=$(_sanitize_value "$_searchdomain")" + [[ -n "$_hostname" ]] && echo "var_hostname=$(_sanitize_value "$_hostname")" + [[ -n "$_searchdomain" ]] && echo "var_searchdomain=$(_sanitize_value "$_searchdomain")" - # Storage (optional, if known at this stage) - [ -n "$_tpl_storage" ] && echo "var_template_storage=$(_sanitize_value "$_tpl_storage")" - [ -n "$_ct_storage" ] && echo "var_container_storage=$(_sanitize_value "$_ct_storage")" - } >"$app_vars_path" + [[ -n "$_tpl_storage" ]] && echo "var_template_storage=$(_sanitize_value "$_tpl_storage")" + [[ -n "$_ct_storage" ]] && echo "var_container_storage=$(_sanitize_value "$_ct_storage")" + } >"$tmpf" - chmod 0644 "$app_vars_path" - msg_ok "Saved app defaults: ${app_vars_path}" + echo "$tmpf" } +# ----------------------------------------------- +# maybe_offer_save_app_defaults (Create/Update) +# - UPDATE-Pfad mit Diff & Menü (Update = Default) +# ----------------------------------------------- +maybe_offer_save_app_defaults() { + local app_vars_path + app_vars_path="$(get_app_defaults_path)" + + # Immer Kandidat aus aktueller Auswahl bauen + local new_tmp diff_tmp + new_tmp="$(_build_current_app_vars_tmp)" + diff_tmp="$(mktemp -p /tmp "${NSAPP:-app}.vars.diff.XXXXXX")" + + # 1) Wenn Datei noch nicht existiert → Erstellen wie bisher + if [[ ! -f "$app_vars_path" ]]; then + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --yesno "Save these advanced settings as defaults for ${APP}?\n\nThis will create:\n${app_vars_path}" 12 72; then + mkdir -p "$(dirname "$app_vars_path")" + install -m 0644 "$new_tmp" "$app_vars_path" + msg_ok "Saved app defaults: ${app_vars_path}" + fi + rm -f "$new_tmp" "$diff_tmp" + return 0 + fi + + # 2) Datei existiert → Diff bauen + _build_vars_diff "$app_vars_path" "$new_tmp" >"$diff_tmp" + + # Wenn kein Unterschied → nichts tun + if grep -q "^(No differences)$" "$diff_tmp"; then + rm -f "$new_tmp" "$diff_tmp" + return 0 + fi + + # 3) Menü mit Default-Auswahl "Update Defaults" + while true; do + local sel + sel="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "APP DEFAULTS – ${APP}" \ + --menu "Differences detected. What do you want to do?" 20 78 10 \ + "Update Defaults" "Write new values to ${app_vars_path}" \ + "Keep Current" "Keep existing defaults (no changes)" \ + "View Diff" "Show a detailed diff" \ + "Cancel" "Abort without changes" \ + --default-item "Update Defaults" \ + 3>&1 1>&2 2>&3)" || { sel="Cancel"; } + + case "$sel" in + "Update Defaults") + install -m 0644 "$new_tmp" "$app_vars_path" + msg_ok "Updated app defaults: ${app_vars_path}" + break + ;; + "Keep Current") + msg_info "Keeping current app defaults: ${app_vars_path}" + break + ;; + "View Diff") + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "Diff – ${APP}" \ + --scrolltext --textbox "$diff_tmp" 25 100 + ;; + "Cancel"|*) + msg_info "Canceled. No changes to app defaults." + break + ;; + esac + done + + rm -f "$new_tmp" "$diff_tmp" +} + + install_script() { pve_check shell_check diff --git a/misc/core.func b/misc/core.func index 660c656b7..26ee3e12c 100644 --- a/misc/core.func +++ b/misc/core.func @@ -20,87 +20,6 @@ load_functions() { # add more } -# ============================================================================ -# Error & Signal Handling – robust, universal, subshell-safe -# ============================================================================ - -# _stop_spinner_on_error() { -# [[ -n "${SPINNER_PID:-}" ]] && kill "$SPINNER_PID" 2>/dev/null && wait "$SPINNER_PID" 2>/dev/null || true -# } - -_tool_error_hint() { - local cmd="$1" - local code="$2" - case "$cmd" in - curl) - case "$code" in - 6) echo "Curl: Could not resolve host (DNS problem)" ;; - 7) echo "Curl: Failed to connect to host (connection refused)" ;; - 22) echo "Curl: HTTP error (404/403 etc)" ;; - 28) echo "Curl: Operation timeout" ;; - *) echo "Curl: Unknown error ($code)" ;; - esac - ;; - wget) - echo "Wget failed – URL unreachable or permission denied" - ;; - systemctl) - echo "Systemd unit failure – check service name and permissions" - ;; - jq) - echo "jq parse error – malformed JSON or missing key" - ;; - mariadb | mysql) - echo "MySQL/MariaDB command failed – check credentials or DB" - ;; - unzip) - echo "unzip failed – corrupt file or missing permission" - ;; - tar) - echo "tar failed – invalid format or missing binary" - ;; - node | npm | pnpm | yarn) - echo "Node tool failed – check version compatibility or package.json" - ;; - *) echo "" ;; - esac -} - -# on_error() { -# local code="$?" -# local line="${BASH_LINENO[0]:-unknown}" -# local cmd="${BASH_COMMAND:-unknown}" - -# # Signalcode unterdrücken, falls INT/TERM kommt -# [[ "$code" == "130" || "$code" == "143" ]] && return - -# _stop_spinner_on_error -# msg_error "Script failed at line $line with exit code $code: $cmd" -# exit "$code" -# } - -# on_exit() { -# _stop_spinner_on_error -# [[ "${VERBOSE:-no}" == "yes" ]] && msg_info "Script exited cleanly" -# } - -# on_interrupt() { -# _stop_spinner_on_error -# msg_error "Interrupted by user (CTRL+C)" -# exit 130 -# } - -# on_terminate() { -# _stop_spinner_on_error -# msg_error "Terminated by signal (SIGTERM)" -# exit 143 -# } - -# catch_errors() { -# set -Eeuo pipefail -# trap 'error_handler $LINENO "$BASH_COMMAND"' ERR -# } - # ------------------------------------------------------------------------------ # Sets ANSI color codes used for styled terminal output. # ------------------------------------------------------------------------------ @@ -368,43 +287,6 @@ is_verbose_mode() { [[ "$verbose" != "no" || ! -t 2 ]] } -# ------------------------------------------------------------------------------ -# Handles specific curl error codes and displays descriptive messages. -# ------------------------------------------------------------------------------ -__curl_err_handler() { - local exit_code="$1" - local target="$2" - local curl_msg="$3" - - case $exit_code in - 1) msg_error "Unsupported protocol: $target" ;; - 2) msg_error "Curl init failed: $target" ;; - 3) msg_error "Malformed URL: $target" ;; - 5) msg_error "Proxy resolution failed: $target" ;; - 6) msg_error "Host resolution failed: $target" ;; - 7) msg_error "Connection failed: $target" ;; - 9) msg_error "Access denied: $target" ;; - 18) msg_error "Partial file transfer: $target" ;; - 22) msg_error "HTTP error (e.g. 400/404): $target" ;; - 23) msg_error "Write error on local system: $target" ;; - 26) msg_error "Read error from local file: $target" ;; - 28) msg_error "Timeout: $target" ;; - 35) msg_error "SSL connect error: $target" ;; - 47) msg_error "Too many redirects: $target" ;; - 51) msg_error "SSL cert verify failed: $target" ;; - 52) msg_error "Empty server response: $target" ;; - 55) msg_error "Send error: $target" ;; - 56) msg_error "Receive error: $target" ;; - 60) msg_error "SSL CA not trusted: $target" ;; - 67) msg_error "Login denied by server: $target" ;; - 78) msg_error "Remote file not found (404): $target" ;; - *) msg_error "Curl failed with code $exit_code: $target" ;; - esac - - [[ -n "$curl_msg" ]] && printf "%s\n" "$curl_msg" >&2 - exit 1 -} - fatal() { msg_error "$1" kill -INT $$ @@ -505,17 +387,6 @@ function msg_debug() { fi } -run_container_safe() { - local ct="$1" - shift - local cmd="$*" - - lxc-attach -n "$ct" -- bash -euo pipefail -c " - trap 'echo Aborted in container; exit 130' SIGINT SIGTERM - $cmd - " || __handle_general_error "lxc-attach to CT $ct" -} - check_or_create_swap() { msg_info "Checking for active swap" From 6ca38e23ae7c15b0b831b0800ff5e49692aaefea Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 13:05:53 +0200 Subject: [PATCH 0950/1733] Expand and refactor install_script menu options Added new preset and menu options for App Defaults, Diagnostics, and Storage Settings. Refactored the menu to be dynamically built and improved handling for each choice, including better diagnostics toggling and support for app-specific defaults. This enhances flexibility and user experience in the installation script. --- misc/build.func | 216 +++++++++++++++++++++++++++--------------------- 1 file changed, 120 insertions(+), 96 deletions(-) diff --git a/misc/build.func b/misc/build.func index 7674808c6..a543428c1 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1450,114 +1450,138 @@ install_script() { # --- PRESET support --- if [ -n "${PRESET:-}" ]; then case "$PRESET" in - DEFAULT | default | 1) - CHOICE="1" - ;; - VERBOSE | verbose | 2) - CHOICE="2" - ;; - ADVANCED | advanced | 3) - CHOICE="3" - ;; - DEFAULT_VARS | default_vars | 4) - CHOICE="4" - ;; - *) - echo -e "\n${CROSS}${RD}Invalid PRESET value: ${PRESET}${CL}\n" - exit 1 - ;; + DEFAULT | default | 1) + CHOICE="1" + ;; + VERBOSE | verbose | 2) + CHOICE="2" + ;; + ADVANCED | advanced | 3) + CHOICE="3" + ;; + MYDEFAULTS | mydefaults | 4) + CHOICE="4" + ;; + APPDEFAULTS | appdefaults | 5) + CHOICE="5" + ;; + DIAGNOSTICS | diagnostics | 6) + CHOICE="6" + ;; + STORAGE | storage | 7) + CHOICE="7" + ;; + *) + echo -e "\n${CROSS}${RD}Invalid PRESET value: ${PRESET}${CL}\n" + exit 1 + ;; esac else - #"4" "Use Config File" \ - #"5" "Manage Default Storage" \ - while true; do - TMP_CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "SETTINGS" \ - --menu "Choose an option:" 20 60 6 \ - "1" "Default Settings" \ - "2" "Default Settings (with verbose)" \ - "3" "Advanced Settings" \ - "4" "My Default Vars" \ - "5" "Diagnostic Settings" \ - "6" "Exit" \ - --default-item "1" 3>&1 1>&2 2>&3) || true + # Build dynamic menu + local menu_items=( + "1" "Default Settings" + "2" "Default Settings (Verbose)" + "3" "Advanced Install" + "4" "My Defaults" + ) + if [ -f "$(get_app_defaults_path)" ]; then + menu_items+=("5" "App Defaults for ${APP}") + fi + menu_items+=( + "6" "Diagnostic Settings" + "7" "Storage Settings" + "8" "Exit" + ) - if [ -z "$TMP_CHOICE" ]; then - echo -e "\n${CROSS}${RD}Menu canceled. Exiting script.${CL}\n" - exit 0 - fi + TMP_CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "SETTINGS" \ + --menu "Choose an option:" 20 60 9 \ + "${menu_items[@]}" \ + --default-item "1" 3>&1 1>&2 2>&3) || true - CHOICE="$TMP_CHOICE" - break - done + if [ -z "$TMP_CHOICE" ]; then + echo -e "\n${CROSS}${RD}Menu canceled. Exiting script.${CL}\n" + exit 0 + fi + CHOICE="$TMP_CHOICE" fi case $CHOICE in - 1) - header_info - echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME${CL}" - VERBOSE="no" - METHOD="default" - base_settings "$VERBOSE" - echo_default - ;; - 2) - header_info - echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME (${VERBOSE_CROPPED}Verbose)${CL}" - VERBOSE="yes" - METHOD="default" - base_settings "$VERBOSE" - echo_default - ;; - 3) - header_info - echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings on node $PVEHOST_NAME${CL}" - METHOD="advanced" - base_settings - advanced_settings - maybe_offer_save_app_defaults - ;; - # 4) - # header_info - # echo -e "${INFO}${HOLD} ${GN}Using Config File on node $PVEHOST_NAME${CL}" - # METHOD="advanced" - # source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/config-file.func) - # config_file - # ;; - 4) - # My Defaults (default.vars) - default_var_settings || { - msg_error "Failed to apply default.vars" - exit 1 - } - ;; - 5) - if [[ $DIAGNOSTICS == "yes" ]]; then - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS SETTINGS" --yesno "Send Diagnostics of LXC Installation?\n\nCurrent setting: ${DIAGNOSTICS}" 10 58 \ - --yes-button "No" --no-button "Back"; then - DIAGNOSTICS="no" - sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=no/' /usr/local/community-scripts/diagnostics - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS SETTINGS" --msgbox "Diagnostics settings changed to ${DIAGNOSTICS}." 8 58 + 1) + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME${CL}" + VERBOSE="no" + METHOD="default" + base_settings "$VERBOSE" + echo_default + ;; + 2) + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME (Verbose)${CL}" + VERBOSE="yes" + METHOD="default" + base_settings "$VERBOSE" + echo_default + ;; + 3) + header_info + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Install on node $PVEHOST_NAME${CL}" + METHOD="advanced" + base_settings + advanced_settings + maybe_offer_save_app_defaults + ;; + 4) + default_var_settings || { + msg_error "Failed to apply default.vars" + exit 1 + } + ;; + 5) + # App Defaults + if [ -f "$(get_app_defaults_path)" ]; then + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using App Defaults for ${APP} on node $PVEHOST_NAME${CL}" + METHOD="appdefaults" + base_settings + _load_vars_file "$(get_app_defaults_path)" + echo_default + else + msg_error "No App Defaults available for ${APP}" + exit 1 fi - else - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS SETTINGS" --yesno "Send Diagnostics of LXC Installation?\n\nCurrent setting: ${DIAGNOSTICS}" 10 58 \ - --yes-button "Yes" --no-button "Back"; then - DIAGNOSTICS="yes" - sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=yes/' /usr/local/community-scripts/diagnostics - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS SETTINGS" --msgbox "Diagnostics settings changed to ${DIAGNOSTICS}." 8 58 + ;; + 6) + if [[ $DIAGNOSTICS == "yes" ]]; then + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTIC SETTINGS" --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ + --yes-button "No" --no-button "Back"; then + DIAGNOSTICS="no" + sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=no/' /usr/local/community-scripts/diagnostics + whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 + fi + else + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTIC SETTINGS" --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ + --yes-button "Yes" --no-button "Back"; then + DIAGNOSTICS="yes" + sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=yes/' /usr/local/community-scripts/diagnostics + whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 + fi fi - fi - ;; - 6) - echo -e "\n${CROSS}${RD}Script terminated. Have a great day!${CL}\n" - exit 0 - ;; - *) - echo -e "${CROSS}${RD}Invalid option, please try again.${CL}" - ;; + ;; + 7) + storage_settings_menu + ;; + 8) + echo -e "\n${CROSS}${RD}Script terminated.${CL}\n" + exit 0 + ;; + *) + echo -e "${CROSS}${RD}Invalid option, please try again.${CL}" + ;; esac } + check_container_resources() { # Check actual RAM & Cores current_ram=$(free -m | awk 'NR==2{print $2}') From d6a11838245bdd5c734f91363dc8b5e1a33a1bac Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 13:06:13 +0200 Subject: [PATCH 0951/1733] Update build.func --- misc/build.func | 86 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/misc/build.func b/misc/build.func index a543428c1..a2a8837c8 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1581,6 +1581,92 @@ install_script() { esac } +# Check and prompt for storage if missing or invalid +check_storage_or_prompt() { + local vars_file="$1" + local changed=0 + + if [[ ! -f "$vars_file" ]]; then + msg_warn "No vars file found at $vars_file" + return 1 + fi + + # Helper: validate storage + _validate_storage() { + local s="$1" + [[ -n "$s" ]] || return 1 + pvesm status -content images | awk 'NR>1 {print $1}' | grep -qx "$s" + } + + # Load current values + local ct_store tpl_store + ct_store="$(grep -E '^var_container_storage=' "$vars_file" | cut -d= -f2-)" + tpl_store="$(grep -E '^var_template_storage=' "$vars_file" | cut -d= -f2-)" + + # Container storage + if ! _validate_storage "$ct_store"; then + local new_ct + new_ct=$(pvesm status -content images | awk 'NR>1 {print $1" "$2" "$6}') + new_ct=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "Select Container Storage" \ + --menu "Choose container storage:" 20 60 10 $new_ct 3>&1 1>&2 2>&3) || return 1 + if [[ -n "$new_ct" ]]; then + sed -i "/^var_container_storage=/d" "$vars_file" + echo "var_container_storage=$new_ct" >>"$vars_file" + changed=1 + msg_ok "Updated container storage in $vars_file → $new_ct" + fi + fi + + # Template storage + if ! _validate_storage "$tpl_store"; then + local new_tpl + new_tpl=$(pvesm status -content vztmpl | awk 'NR>1 {print $1" "$2" "$6}') + new_tpl=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "Select Template Storage" \ + --menu "Choose template storage:" 20 60 10 $new_tpl 3>&1 1>&2 2>&3) || return 1 + if [[ -n "$new_tpl" ]]; then + sed -i "/^var_template_storage=/d" "$vars_file" + echo "var_template_storage=$new_tpl" >>"$vars_file" + changed=1 + msg_ok "Updated template storage in $vars_file → $new_tpl" + fi + fi + + return $changed +} + +# Storage Settings menu +storage_settings_menu() { + local menu_items=( + "1" "Check & update My Defaults (default.vars)" + ) + if [ -f "$(get_app_defaults_path)" ]; then + menu_items+=("2" "Check & update App Defaults for ${APP}") + fi + menu_items+=("3" "Back") + + local choice + choice=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "STORAGE SETTINGS" \ + --menu "Select what to update:" 15 60 5 \ + "${menu_items[@]}" 3>&1 1>&2 2>&3) || return 0 + + case "$choice" in + 1) + check_storage_or_prompt "/usr/local/community-scripts/default.vars" + ;; + 2) + if [ -f "$(get_app_defaults_path)" ]; then + check_storage_or_prompt "$(get_app_defaults_path)" + fi + ;; + 3) + return 0 + ;; + esac +} + check_container_resources() { # Check actual RAM & Cores From 4c4f4ecfe6ae91dc0e075fb12ee6c8b2e8f22e1d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 13:13:20 +0200 Subject: [PATCH 0952/1733] Refactor and modernize build.func script Refactored case statements and conditional checks to use more idiomatic and consistent Bash syntax. Replaced German comments with English, improved readability by splitting long lines, and standardized variable assignments. Enhanced maintainability by using case/esac and if/then constructs, and replaced double-bracketed conditionals with POSIX-compliant single brackets where appropriate. --- misc/build.func | 402 +++++++++++++++++++++++++----------------------- 1 file changed, 209 insertions(+), 193 deletions(-) diff --git a/misc/build.func b/misc/build.func index a2a8837c8..65a1d9ba9 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1192,7 +1192,10 @@ _is_whitelisted_key() { _sanitize_value() { # Disallow Command-Substitution / Shell-Meta case "$1" in - *'$('*|*'`'*|*';'*|*'&'*|*'<('* ) echo ""; return 0 ;; + *'$('* | *'`'* | *';'* | *'&'* | *'<('*) + echo "" + return 0 + ;; esac echo "$1" } @@ -1205,7 +1208,8 @@ _load_vars_file_to_map() { [[ -f "$file" ]] || return 0 local line while IFS= read -r line || [[ -n "$line" ]]; do - line="${line#"${line%%[![:space:]]*}"}"; line="${line%"${line##*[![:space:]]}"}" + line="${line#"${line%%[![:space:]]*}"}" + line="${line%"${line##*[![:space:]]}"}" [[ -z "$line" || "$line" == \#* ]] && continue if [[ "$line" =~ ^([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then local k="${BASH_REMATCH[1]}" @@ -1225,8 +1229,10 @@ _build_vars_diff() { local oldf="$1" newf="$2" local k local -A OLD=() NEW=() - _load_vars_file_to_map "$oldf"; for k in "${!_VARS_IN[@]}"; do OLD["$k"]="${_VARS_IN[$k]}"; done - _load_vars_file_to_map "$newf"; for k in "${!_VARS_IN[@]}"; do NEW["$k"]="${_VARS_IN[$k]}"; done + _load_vars_file_to_map "$oldf" + for k in "${!_VARS_IN[@]}"; do OLD["$k"]="${_VARS_IN[$k]}"; done + _load_vars_file_to_map "$newf" + for k in "${!_VARS_IN[@]}"; do NEW["$k"]="${_VARS_IN[$k]}"; done local out out+="# Diff for ${APP} (${NSAPP})\n" @@ -1262,58 +1268,70 @@ _build_vars_diff() { printf "%b" "$out" } -# Baut aus der aktuellen Advanced-Auswahl eine temporäre .vars-Datei +# Build a temporary .vars file from current advanced settings _build_current_app_vars_tmp() { - local tmpf - tmpf="$(mktemp -p /tmp "${NSAPP:-app}.vars.new.XXXXXX")" + tmpf="$(mktemp /tmp/${NSAPP:-app}.vars.new.XXXXXX)" # NET/GW - local _net="${NET:-}" - local _gate="" - [[ "${GATE:-}" =~ ^,gw= ]] && _gate="${GATE#,gw=}" - - # IPv6 - local _ipv6_method="${IPV6_METHOD:-auto}" - local _ipv6_static="" - local _ipv6_gateway="" - case "$_ipv6_method" in - static) - _ipv6_static="${IPV6_ADDR:-}" - _ipv6_gateway="${IPV6_GATE:-}" - ;; + _net="${NET:-}" + _gate="" + case "${GATE:-}" in + ,gw=*) _gate=$(echo "$GATE" | sed 's/^,gw=//') ;; esac - # MTU/VLAN/MAC - local _mtu="" _vlan="" _mac="" - [[ "${MTU:-}" =~ ^,mtu= ]] && _mtu="${MTU#,mtu=}" - [[ "${VLAN:-}" =~ ^,tag= ]] && _vlan="${VLAN#,tag=}" - [[ "${MAC:-}" =~ ^,hwaddr=]] && _mac="${MAC#,hwaddr=}" + # IPv6 + _ipv6_method="${IPV6_METHOD:-auto}" + _ipv6_static="" + _ipv6_gateway="" + if [ "$_ipv6_method" = "static" ]; then + _ipv6_static="${IPV6_ADDR:-}" + _ipv6_gateway="${IPV6_GATE:-}" + fi - # DNS/SD - local _ns="" _searchdomain="" - [[ "${NS:-}" =~ ^-nameserver= ]] && _ns="${NS#-nameserver=}" - [[ "${SD:-}" =~ ^-searchdomain= ]] && _searchdomain="${SD#-searchdomain=}" + # MTU/VLAN/MAC + _mtu="" + _vlan="" + _mac="" + case "${MTU:-}" in + ,mtu=*) _mtu=$(echo "$MTU" | sed 's/^,mtu=//') ;; + esac + case "${VLAN:-}" in + ,tag=*) _vlan=$(echo "$VLAN" | sed 's/^,tag=//') ;; + esac + case "${MAC:-}" in + ,hwaddr=*) _mac=$(echo "$MAC" | sed 's/^,hwaddr=//') ;; + esac + + # DNS / Searchdomain + _ns="" + _searchdomain="" + case "${NS:-}" in + -nameserver=*) _ns=$(echo "$NS" | sed 's/^-nameserver=//') ;; + esac + case "${SD:-}" in + -searchdomain=*) _searchdomain=$(echo "$SD" | sed 's/^-searchdomain=//') ;; + esac # SSH / APT / Features - local _ssh="${SSH:-no}" - local _ssh_auth="${SSH_AUTHORIZED_KEY:-}" - local _apt_cacher="${APT_CACHER:-}" - local _apt_cacher_ip="${APT_CACHER_IP:-}" - local _fuse="${ENABLE_FUSE:-no}" - local _tun="${ENABLE_TUN:-no}" - local _tags="${TAGS:-}" - local _verbose="${VERBOSE:-no}" + _ssh="${SSH:-no}" + _ssh_auth="${SSH_AUTHORIZED_KEY:-}" + _apt_cacher="${APT_CACHER:-}" + _apt_cacher_ip="${APT_CACHER_IP:-}" + _fuse="${ENABLE_FUSE:-no}" + _tun="${ENABLE_TUN:-no}" + _tags="${TAGS:-}" + _verbose="${VERBOSE:-no}" - # Typ/Resourcen/Identity - local _unpriv="${CT_TYPE:-1}" - local _cpu="${CORE_COUNT:-1}" - local _ram="${RAM_SIZE:-1024}" - local _disk="${DISK_SIZE:-4}" - local _hostname="${HN:-$NSAPP}" + # Type / Resources / Identity + _unpriv="${CT_TYPE:-1}" + _cpu="${CORE_COUNT:-1}" + _ram="${RAM_SIZE:-1024}" + _disk="${DISK_SIZE:-4}" + _hostname="${HN:-$NSAPP}" - # Storage (falls vorhanden) - local _tpl_storage="${TEMPLATE_STORAGE:-}" - local _ct_storage="${CONTAINER_STORAGE:-}" + # Storage + _tpl_storage="${TEMPLATE_STORAGE:-}" + _ct_storage="${CONTAINER_STORAGE:-}" { echo "# App-specific defaults for ${APP} (${NSAPP})" @@ -1325,33 +1343,33 @@ _build_current_app_vars_tmp() { echo "var_ram=$(_sanitize_value "$_ram")" echo "var_disk=$(_sanitize_value "$_disk")" - [[ -n "${BRG:-}" ]] && echo "var_brg=$(_sanitize_value "$BRG")" - [[ -n "$_net" ]] && echo "var_net=$(_sanitize_value "$_net")" - [[ -n "$_gate" ]] && echo "var_gateway=$(_sanitize_value "$_gate")" - [[ -n "$_mtu" ]] && echo "var_mtu=$(_sanitize_value "$_mtu")" - [[ -n "$_vlan" ]] && echo "var_vlan=$(_sanitize_value "$_vlan")" - [[ -n "$_mac" ]] && echo "var_mac=$(_sanitize_value "$_mac")" - [[ -n "$_ns" ]] && echo "var_ns=$(_sanitize_value "$_ns")" + [ -n "${BRG:-}" ] && echo "var_brg=$(_sanitize_value "$BRG")" + [ -n "$_net" ] && echo "var_net=$(_sanitize_value "$_net")" + [ -n "$_gate" ] && echo "var_gateway=$(_sanitize_value "$_gate")" + [ -n "$_mtu" ] && echo "var_mtu=$(_sanitize_value "$_mtu")" + [ -n "$_vlan" ] && echo "var_vlan=$(_sanitize_value "$_vlan")" + [ -n "$_mac" ] && echo "var_mac=$(_sanitize_value "$_mac")" + [ -n "$_ns" ] && echo "var_ns=$(_sanitize_value "$_ns")" - [[ -n "$_ipv6_method" ]] && echo "var_ipv6_method=$(_sanitize_value "$_ipv6_method")" - [[ -n "$_ipv6_static" ]] && echo "var_ipv6_static=$(_sanitize_value "$_ipv6_static")" + [ -n "$_ipv6_method" ] && echo "var_ipv6_method=$(_sanitize_value "$_ipv6_method")" + [ -n "$_ipv6_static" ] && echo "var_ipv6_static=$(_sanitize_value "$_ipv6_static")" - [[ -n "$_ssh" ]] && echo "var_ssh=$(_sanitize_value "$_ssh")" - [[ -n "$_ssh_auth" ]] && echo "var_ssh_authorized_key=$(_sanitize_value "$_ssh_auth")" + [ -n "$_ssh" ] && echo "var_ssh=$(_sanitize_value "$_ssh")" + [ -n "$_ssh_auth" ] && echo "var_ssh_authorized_key=$(_sanitize_value "$_ssh_auth")" - [[ -n "$_apt_cacher" ]] && echo "var_apt_cacher=$(_sanitize_value "$_apt_cacher")" - [[ -n "$_apt_cacher_ip" ]] && echo "var_apt_cacher_ip=$(_sanitize_value "$_apt_cacher_ip")" + [ -n "$_apt_cacher" ] && echo "var_apt_cacher=$(_sanitize_value "$_apt_cacher")" + [ -n "$_apt_cacher_ip" ] && echo "var_apt_cacher_ip=$(_sanitize_value "$_apt_cacher_ip")" - [[ -n "$_fuse" ]] && echo "var_fuse=$(_sanitize_value "$_fuse")" - [[ -n "$_tun" ]] && echo "var_tun=$(_sanitize_value "$_tun")" - [[ -n "$_tags" ]] && echo "var_tags=$(_sanitize_value "$_tags")" - [[ -n "$_verbose" ]] && echo "var_verbose=$(_sanitize_value "$_verbose")" + [ -n "$_fuse" ] && echo "var_fuse=$(_sanitize_value "$_fuse")" + [ -n "$_tun" ] && echo "var_tun=$(_sanitize_value "$_tun")" + [ -n "$_tags" ] && echo "var_tags=$(_sanitize_value "$_tags")" + [ -n "$_verbose" ] && echo "var_verbose=$(_sanitize_value "$_verbose")" - [[ -n "$_hostname" ]] && echo "var_hostname=$(_sanitize_value "$_hostname")" - [[ -n "$_searchdomain" ]] && echo "var_searchdomain=$(_sanitize_value "$_searchdomain")" + [ -n "$_hostname" ] && echo "var_hostname=$(_sanitize_value "$_hostname")" + [ -n "$_searchdomain" ] && echo "var_searchdomain=$(_sanitize_value "$_searchdomain")" - [[ -n "$_tpl_storage" ]] && echo "var_template_storage=$(_sanitize_value "$_tpl_storage")" - [[ -n "$_ct_storage" ]] && echo "var_container_storage=$(_sanitize_value "$_ct_storage")" + [ -n "$_tpl_storage" ] && echo "var_template_storage=$(_sanitize_value "$_tpl_storage")" + [ -n "$_ct_storage" ] && echo "var_container_storage=$(_sanitize_value "$_ct_storage")" } >"$tmpf" echo "$tmpf" @@ -1398,38 +1416,37 @@ maybe_offer_save_app_defaults() { --title "APP DEFAULTS – ${APP}" \ --menu "Differences detected. What do you want to do?" 20 78 10 \ "Update Defaults" "Write new values to ${app_vars_path}" \ - "Keep Current" "Keep existing defaults (no changes)" \ - "View Diff" "Show a detailed diff" \ - "Cancel" "Abort without changes" \ + "Keep Current" "Keep existing defaults (no changes)" \ + "View Diff" "Show a detailed diff" \ + "Cancel" "Abort without changes" \ --default-item "Update Defaults" \ 3>&1 1>&2 2>&3)" || { sel="Cancel"; } case "$sel" in - "Update Defaults") - install -m 0644 "$new_tmp" "$app_vars_path" - msg_ok "Updated app defaults: ${app_vars_path}" - break - ;; - "Keep Current") - msg_info "Keeping current app defaults: ${app_vars_path}" - break - ;; - "View Diff") - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "Diff – ${APP}" \ - --scrolltext --textbox "$diff_tmp" 25 100 - ;; - "Cancel"|*) - msg_info "Canceled. No changes to app defaults." - break - ;; + "Update Defaults") + install -m 0644 "$new_tmp" "$app_vars_path" + msg_ok "Updated app defaults: ${app_vars_path}" + break + ;; + "Keep Current") + msg_info "Keeping current app defaults: ${app_vars_path}" + break + ;; + "View Diff") + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "Diff – ${APP}" \ + --scrolltext --textbox "$diff_tmp" 25 100 + ;; + "Cancel" | *) + msg_info "Canceled. No changes to app defaults." + break + ;; esac done rm -f "$new_tmp" "$diff_tmp" } - install_script() { pve_check shell_check @@ -1450,31 +1467,31 @@ install_script() { # --- PRESET support --- if [ -n "${PRESET:-}" ]; then case "$PRESET" in - DEFAULT | default | 1) - CHOICE="1" - ;; - VERBOSE | verbose | 2) - CHOICE="2" - ;; - ADVANCED | advanced | 3) - CHOICE="3" - ;; - MYDEFAULTS | mydefaults | 4) - CHOICE="4" - ;; - APPDEFAULTS | appdefaults | 5) - CHOICE="5" - ;; - DIAGNOSTICS | diagnostics | 6) - CHOICE="6" - ;; - STORAGE | storage | 7) - CHOICE="7" - ;; - *) - echo -e "\n${CROSS}${RD}Invalid PRESET value: ${PRESET}${CL}\n" - exit 1 - ;; + DEFAULT | default | 1) + CHOICE="1" + ;; + VERBOSE | verbose | 2) + CHOICE="2" + ;; + ADVANCED | advanced | 3) + CHOICE="3" + ;; + MYDEFAULTS | mydefaults | 4) + CHOICE="4" + ;; + APPDEFAULTS | appdefaults | 5) + CHOICE="5" + ;; + DIAGNOSTICS | diagnostics | 6) + CHOICE="6" + ;; + STORAGE | storage | 7) + CHOICE="7" + ;; + *) + echo -e "\n${CROSS}${RD}Invalid PRESET value: ${PRESET}${CL}\n" + exit 1 + ;; esac else # Build dynamic menu @@ -1507,77 +1524,77 @@ install_script() { fi case $CHOICE in - 1) + 1) + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME${CL}" + VERBOSE="no" + METHOD="default" + base_settings "$VERBOSE" + echo_default + ;; + 2) + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME (Verbose)${CL}" + VERBOSE="yes" + METHOD="default" + base_settings "$VERBOSE" + echo_default + ;; + 3) + header_info + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Install on node $PVEHOST_NAME${CL}" + METHOD="advanced" + base_settings + advanced_settings + maybe_offer_save_app_defaults + ;; + 4) + default_var_settings || { + msg_error "Failed to apply default.vars" + exit 1 + } + ;; + 5) + # App Defaults + if [ -f "$(get_app_defaults_path)" ]; then header_info - echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME${CL}" - VERBOSE="no" - METHOD="default" - base_settings "$VERBOSE" - echo_default - ;; - 2) - header_info - echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME (Verbose)${CL}" - VERBOSE="yes" - METHOD="default" - base_settings "$VERBOSE" - echo_default - ;; - 3) - header_info - echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Install on node $PVEHOST_NAME${CL}" - METHOD="advanced" + echo -e "${DEFAULT}${BOLD}${BL}Using App Defaults for ${APP} on node $PVEHOST_NAME${CL}" + METHOD="appdefaults" base_settings - advanced_settings - maybe_offer_save_app_defaults - ;; - 4) - default_var_settings || { - msg_error "Failed to apply default.vars" - exit 1 - } - ;; - 5) - # App Defaults - if [ -f "$(get_app_defaults_path)" ]; then - header_info - echo -e "${DEFAULT}${BOLD}${BL}Using App Defaults for ${APP} on node $PVEHOST_NAME${CL}" - METHOD="appdefaults" - base_settings - _load_vars_file "$(get_app_defaults_path)" - echo_default - else - msg_error "No App Defaults available for ${APP}" - exit 1 + _load_vars_file "$(get_app_defaults_path)" + echo_default + else + msg_error "No App Defaults available for ${APP}" + exit 1 + fi + ;; + 6) + if [[ $DIAGNOSTICS == "yes" ]]; then + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTIC SETTINGS" --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ + --yes-button "No" --no-button "Back"; then + DIAGNOSTICS="no" + sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=no/' /usr/local/community-scripts/diagnostics + whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 fi - ;; - 6) - if [[ $DIAGNOSTICS == "yes" ]]; then - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTIC SETTINGS" --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ - --yes-button "No" --no-button "Back"; then - DIAGNOSTICS="no" - sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=no/' /usr/local/community-scripts/diagnostics - whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 - fi - else - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTIC SETTINGS" --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ - --yes-button "Yes" --no-button "Back"; then - DIAGNOSTICS="yes" - sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=yes/' /usr/local/community-scripts/diagnostics - whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 - fi + else + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTIC SETTINGS" --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ + --yes-button "Yes" --no-button "Back"; then + DIAGNOSTICS="yes" + sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=yes/' /usr/local/community-scripts/diagnostics + whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 fi - ;; - 7) - storage_settings_menu - ;; - 8) - echo -e "\n${CROSS}${RD}Script terminated.${CL}\n" - exit 0 - ;; - *) - echo -e "${CROSS}${RD}Invalid option, please try again.${CL}" - ;; + fi + ;; + 7) + storage_settings_menu + ;; + 8) + echo -e "\n${CROSS}${RD}Script terminated.${CL}\n" + exit 0 + ;; + *) + echo -e "${CROSS}${RD}Invalid option, please try again.${CL}" + ;; esac } @@ -1653,21 +1670,20 @@ storage_settings_menu() { "${menu_items[@]}" 3>&1 1>&2 2>&3) || return 0 case "$choice" in - 1) - check_storage_or_prompt "/usr/local/community-scripts/default.vars" - ;; - 2) - if [ -f "$(get_app_defaults_path)" ]; then - check_storage_or_prompt "$(get_app_defaults_path)" - fi - ;; - 3) - return 0 - ;; + 1) + check_storage_or_prompt "/usr/local/community-scripts/default.vars" + ;; + 2) + if [ -f "$(get_app_defaults_path)" ]; then + check_storage_or_prompt "$(get_app_defaults_path)" + fi + ;; + 3) + return 0 + ;; esac } - check_container_resources() { # Check actual RAM & Cores current_ram=$(free -m | awk 'NR==2{print $2}') From 311d3b27865136f31b4537fbe3a577b2d9c5d68b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 13:44:49 +0200 Subject: [PATCH 0953/1733] Update build.func --- misc/build.func | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index 65a1d9ba9..8dc4fdc08 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1410,12 +1410,15 @@ maybe_offer_save_app_defaults() { fi # 3) Menü mit Default-Auswahl "Update Defaults" + local app_vars_file + app_vars_file="$(basename "$app_vars_path")" + while true; do local sel sel="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ --title "APP DEFAULTS – ${APP}" \ --menu "Differences detected. What do you want to do?" 20 78 10 \ - "Update Defaults" "Write new values to ${app_vars_path}" \ + "Update Defaults" "Write new values to ${app_vars_file}" \ "Keep Current" "Keep existing defaults (no changes)" \ "View Diff" "Show a detailed diff" \ "Cancel" "Abort without changes" \ @@ -1553,6 +1556,7 @@ install_script() { msg_error "Failed to apply default.vars" exit 1 } + check_storage_or_prompt "/usr/local/community-scripts/default.vars" ;; 5) # App Defaults @@ -1563,6 +1567,7 @@ install_script() { base_settings _load_vars_file "$(get_app_defaults_path)" echo_default + check_storage_or_prompt "$(get_app_defaults_path)" else msg_error "No App Defaults available for ${APP}" exit 1 From 6eb408569e66fabc84b3788e4b336bc390866c4b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 14:05:17 +0200 Subject: [PATCH 0954/1733] comments over comments --- misc/build.func | 336 +++++++++++++++++++++++++++++------------------- 1 file changed, 203 insertions(+), 133 deletions(-) diff --git a/misc/build.func b/misc/build.func index 8dc4fdc08..b66a0786b 100644 --- a/misc/build.func +++ b/misc/build.func @@ -5,6 +5,16 @@ # Co-Author: michelroegl-brunner # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# ------------------------------------------------------------------------------ +# variables() +# +# - Normalize application name (NSAPP = lowercase, no spaces) +# - Build installer filename (var_install) +# - Define regex for integer validation +# - Fetch hostname of Proxmox node +# - Set default values for diagnostics/method +# - Generate random UUID for tracking +# ------------------------------------------------------------------------------ variables() { NSAPP=$(echo "${APP,,}" | tr -d ' ') # This function sets the NSAPP variable by converting the value of the APP variable to lowercase and removing any spaces. var_install="${NSAPP}-install" # sets the var_install variable by appending "-install" to the value of NSAPP. @@ -16,6 +26,13 @@ variables() { #CT_TYPE=${var_unprivileged:-$CT_TYPE} } +# ------------------------------------------------------------------------------ +# Load core + error handler functions from community-scripts repo +# +# - Prefer curl if available, fallback to wget +# - Load: core.func, error_handler.func, api.func +# - Initialize error traps after loading +# ------------------------------------------------------------------------------ source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) if command -v curl >/dev/null 2>&1; then @@ -32,101 +49,16 @@ elif command -v wget >/dev/null 2>&1; then #echo "(build.func) Loaded core.func via wget" fi -# set -Eeuo pipefail -# trap 'error_handler $? $LINENO "$BASH_COMMAND"' ERR -# trap on_exit EXIT -# trap on_interrupt INT -# trap on_terminate TERM +# ------------------------------------------------------------------------------ +# maxkeys_check() +# +# - Reads kernel keyring limits (maxkeys, maxbytes) +# - Checks current usage for LXC user (UID 100000) +# - Warns if usage is close to limits and suggests sysctl tuning +# - Exits if thresholds are exceeded +# - https://cleveruptime.com/docs/files/proc-key-users | https://docs.kernel.org/security/keys/core.html +# ------------------------------------------------------------------------------ -# error_handler() { -# local exit_code="$1" -# local line_number="$2" -# local command="${3:-}" - -# if [[ "$exit_code" -eq 0 ]]; then -# return 0 -# fi - -# printf "\e[?25h" -# echo -e "\n${RD}[ERROR]${CL} in line ${RD}${line_number}${CL}: exit code ${RD}${exit_code}${CL}: while executing command ${YW}${command}${CL}\n" -# exit "$exit_code" -# } - -# on_exit() { -# local exit_code="$?" -# [[ -n "${lockfile:-}" && -e "$lockfile" ]] && rm -f "$lockfile" -# exit "$exit_code" -# } - -# on_interrupt() { -# echo -e "\n${RD}Interrupted by user (SIGINT)${CL}" -# exit 130 -# } - -# on_terminate() { -# echo -e "\n${RD}Terminated by signal (SIGTERM)${CL}" -# exit 143 -# } - -# # Check if the shell is using bash -# shell_check() { -# if [[ "$(basename "$SHELL")" != "bash" ]]; then -# clear -# msg_error "Your default shell is currently not set to Bash. To use these scripts, please switch to the Bash shell." -# echo -e "\nExiting..." -# sleep 2 -# exit -# fi -# } - -# # Run as root only -# root_check() { -# if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then -# clear -# msg_error "Please run this script as root." -# echo -e "\nExiting..." -# sleep 2 -# exit -# fi -# } - -# # This function checks the version of Proxmox Virtual Environment (PVE) and exits if the version is not supported. -# # Supported: Proxmox VE 8.0.x – 8.9.x and 9.0 (NOT 9.1+) -# pve_check() { -# local PVE_VER -# PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" - -# # Check for Proxmox VE 8.x: allow 8.0–8.9 -# if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then -# local MINOR="${BASH_REMATCH[1]}" -# if ((MINOR < 0 || MINOR > 9)); then -# msg_error "This version of Proxmox VE is not supported." -# msg_error "Supported: Proxmox VE version 8.0 – 8.9" -# exit 1 -# fi -# return 0 -# fi - -# # Check for Proxmox VE 9.x: allow ONLY 9.0 -# if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then -# local MINOR="${BASH_REMATCH[1]}" -# if ((MINOR != 0)); then -# msg_error "This version of Proxmox VE is not yet supported." -# msg_error "Supported: Proxmox VE version 9.0" -# exit 1 -# fi -# return 0 -# fi - -# # All other unsupported versions -# msg_error "This version of Proxmox VE is not supported." -# msg_error "Supported versions: Proxmox VE 8.0 – 8.x or 9.0" -# exit 1 -# } - -# When a node is running tens of containers, it's possible to exceed the kernel's cryptographic key storage allocations. -# These are tuneable, so verify if the currently deployment is approaching the limits, advise the user on how to tune the limits, and exit the script. -# https://cleveruptime.com/docs/files/proc-key-users | https://docs.kernel.org/security/keys/core.html maxkeys_check() { # Read kernel parameters per_user_maxkeys=$(cat /proc/sys/kernel/keys/maxkeys 2>/dev/null || echo 0) @@ -170,7 +102,13 @@ maxkeys_check() { echo -e "${CM}${GN} All kernel key limits are within safe thresholds.${CL}" } -# Function to get the current IP address based on the distribution +# ------------------------------------------------------------------------------ +# get_current_ip() +# +# - Returns current container IP depending on OS type +# - Debian/Ubuntu: uses `hostname -I` +# - Alpine: parses eth0 via `ip -4 addr` +# ------------------------------------------------------------------------------ get_current_ip() { if [ -f /etc/os-release ]; then # Check for Debian/Ubuntu (uses hostname -I) @@ -186,7 +124,12 @@ get_current_ip() { echo "$CURRENT_IP" } -# Function to update the IP address in the MOTD file +# ------------------------------------------------------------------------------ +# update_motd_ip() +# +# - Updates /etc/motd with current container IP +# - Removes old IP entries to avoid duplicates +# ------------------------------------------------------------------------------ update_motd_ip() { MOTD_FILE="/etc/motd" @@ -200,7 +143,13 @@ update_motd_ip() { fi } -# This function checks if the script is running through SSH and prompts the user to confirm if they want to proceed or exit. +# ------------------------------------------------------------------------------ +# ssh_check() +# +# - Detects if script is running over SSH +# - Warns user and recommends using Proxmox shell +# - User can choose to continue or abort +# ------------------------------------------------------------------------------ ssh_check() { if [ -n "${SSH_CLIENT:+x}" ]; then if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's advisable to utilize the Proxmox shell rather than SSH, as there may be potential complications with variable retrieval. Proceed using SSH?" 10 72; then @@ -213,6 +162,13 @@ ssh_check() { fi } +# ------------------------------------------------------------------------------ +# install_ssh_keys_into_ct() +# +# - Installs SSH keys into container root account if SSH is enabled +# - Uses pct push or direct input to authorized_keys +# - Falls back to warning if no keys provided +# ------------------------------------------------------------------------------ install_ssh_keys_into_ct() { [[ "$SSH" != "yes" ]] && return 0 @@ -237,6 +193,13 @@ install_ssh_keys_into_ct() { return 0 } +# ------------------------------------------------------------------------------ +# base_settings() +# +# - Defines all base/default variables for container creation +# - Reads from environment variables (var_*) +# - Provides fallback defaults for OS type/version +# ------------------------------------------------------------------------------ base_settings() { # Default Settings CT_TYPE=${var_unprivileged:-"1"} @@ -275,15 +238,19 @@ base_settings() { fi } -# This function displays the default values for various settings. +# ------------------------------------------------------------------------------ +# echo_default() +# +# - Prints summary of default values (ID, OS, type, disk, RAM, CPU, etc.) +# - Uses icons and formatting for readability +# - Convert CT_TYPE to description +# ------------------------------------------------------------------------------ echo_default() { - # Convert CT_TYPE to description CT_TYPE_DESC="Unprivileged" if [ "$CT_TYPE" -eq 0 ]; then CT_TYPE_DESC="Privileged" fi - # Output the selected values with icons echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}${CT_ID}${CL}" echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os ($var_version)${CL}" echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" @@ -297,13 +264,26 @@ echo_default() { echo -e " " } -# This function is called when the user decides to exit the script. It clears the screen and displays an exit message. +# ------------------------------------------------------------------------------ +# exit_script() +# +# - Called when user cancels an action +# - Clears screen and exits gracefully +# ------------------------------------------------------------------------------ exit_script() { clear echo -e "\n${CROSS}${RD}User exited script${CL}\n" exit } +# ------------------------------------------------------------------------------ +# find_host_ssh_keys() +# +# - Scans system for available SSH keys +# - Supports defaults (~/.ssh, /etc/ssh/authorized_keys) +# - Returns list of files containing valid SSH public keys +# - Sets FOUND_HOST_KEY_COUNT to number of keys found +# ------------------------------------------------------------------------------ find_host_ssh_keys() { local re='(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))' local -a files=() cand=() @@ -356,7 +336,14 @@ find_host_ssh_keys() { ) } -# This function allows the user to configure advanced settings for the script. +# ------------------------------------------------------------------------------ +# advanced_settings() +# +# - Interactive whiptail menu for advanced configuration +# - Lets user set container type, password, CT ID, hostname, disk, CPU, RAM +# - Supports IPv4/IPv6, DNS, MAC, VLAN, tags, SSH keys, FUSE, verbose mode +# - Ends with confirmation or re-entry if cancelled +# ------------------------------------------------------------------------------ advanced_settings() { whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox --title "Here is an instructional tip:" "To make a selection, use the Spacebar." 8 58 # Setting Default Tag for Advanced Settings @@ -890,6 +877,13 @@ advanced_settings() { fi } +# ------------------------------------------------------------------------------ +# diagnostics_check() +# +# - Ensures diagnostics config file exists at /usr/local/community-scripts/diagnostics +# - Asks user whether to send anonymous diagnostic data +# - Saves DIAGNOSTICS=yes/no in the config file +# ------------------------------------------------------------------------------ diagnostics_check() { if ! [ -d "/usr/local/community-scripts" ]; then mkdir -p /usr/local/community-scripts @@ -1159,6 +1153,13 @@ EOF echo_default } +# ------------------------------------------------------------------------------ +# get_app_defaults_path() +# +# - Returns full path for app-specific defaults file +# - Example: /usr/local/community-scripts/defaults/.vars +# ------------------------------------------------------------------------------ + get_app_defaults_path() { local n="${NSAPP:-${APP,,}}" echo "/usr/local/community-scripts/defaults/${n}.vars" @@ -1200,7 +1201,7 @@ _sanitize_value() { echo "$1" } -# Map-Parser: liest var_* aus Datei in eine assoziative Map (Name hart: _VARS_IN) +# Map-Parser: read var_* from file into _VARS_IN associative array declare -A _VARS_IN _load_vars_file_to_map() { local file="$1" @@ -1216,7 +1217,7 @@ _load_vars_file_to_map() { local v="${BASH_REMATCH[2]}" [[ "$k" == var_* ]] || continue _is_whitelisted_key "$k" || continue - # Quotes strippen + # strip Quotes if [[ "$v" =~ ^\"(.*)\"$ ]]; then v="${BASH_REMATCH[1]}"; fi if [[ "$v" =~ ^\'(.*)\'$ ]]; then v="${BASH_REMATCH[1]}"; fi _VARS_IN["$k"]="$v" @@ -1224,7 +1225,7 @@ _load_vars_file_to_map() { done <"$file" } -# Diff zweier Dateien mit var_* → gibt in $1 (old) vs $2 (new) eine menschenlesbare Diff-Liste aus +# Diff function for two var_* files -> produces human-readable diff list for $1 (old) vs $2 (new) _build_vars_diff() { local oldf="$1" newf="$2" local k @@ -1375,20 +1376,23 @@ _build_current_app_vars_tmp() { echo "$tmpf" } -# ----------------------------------------------- -# maybe_offer_save_app_defaults (Create/Update) -# - UPDATE-Pfad mit Diff & Menü (Update = Default) -# ----------------------------------------------- +# ------------------------------------------------------------------------------ +# maybe_offer_save_app_defaults() +# +# - Called after advanced_settings() +# - Offers to save current values as app defaults if not existing +# - If file exists: shows diff and allows Update, Keep, View Diff, or Cancel +# ------------------------------------------------------------------------------ maybe_offer_save_app_defaults() { local app_vars_path app_vars_path="$(get_app_defaults_path)" - # Immer Kandidat aus aktueller Auswahl bauen + # always build from current settings local new_tmp diff_tmp new_tmp="$(_build_current_app_vars_tmp)" diff_tmp="$(mktemp -p /tmp "${NSAPP:-app}.vars.diff.XXXXXX")" - # 1) Wenn Datei noch nicht existiert → Erstellen wie bisher + # 1) if no file → offer to create if [[ ! -f "$app_vars_path" ]]; then if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ --yesno "Save these advanced settings as defaults for ${APP}?\n\nThis will create:\n${app_vars_path}" 12 72; then @@ -1400,16 +1404,16 @@ maybe_offer_save_app_defaults() { return 0 fi - # 2) Datei existiert → Diff bauen + # 2) if file exists → build diff _build_vars_diff "$app_vars_path" "$new_tmp" >"$diff_tmp" - # Wenn kein Unterschied → nichts tun + # if no differences → do nothing if grep -q "^(No differences)$" "$diff_tmp"; then rm -f "$new_tmp" "$diff_tmp" return 0 fi - # 3) Menü mit Default-Auswahl "Update Defaults" + # 3) if file exists → show menu with default selection "Update Defaults" local app_vars_file app_vars_file="$(basename "$app_vars_path")" @@ -1450,6 +1454,14 @@ maybe_offer_save_app_defaults() { rm -f "$new_tmp" "$diff_tmp" } +# ------------------------------------------------------------------------------ +# install_script() +# +# - Main entrypoint for installation mode +# - Runs safety checks (pve_check, root_check, maxkeys_check, diagnostics_check) +# - Builds interactive menu (Default, Verbose, Advanced, My Defaults, App Defaults, Diagnostics, Storage, Exit) +# - Applies chosen settings and triggers container build +# ------------------------------------------------------------------------------ install_script() { pve_check shell_check @@ -1603,7 +1615,13 @@ install_script() { esac } -# Check and prompt for storage if missing or invalid +# ------------------------------------------------------------------------------ +# check_storage_or_prompt() +# +# - Validates container/template storage +# - If invalid or missing, prompts user to select new storage +# - Updates vars file accordingly +# ------------------------------------------------------------------------------ check_storage_or_prompt() { local vars_file="$1" local changed=0 @@ -1613,7 +1631,6 @@ check_storage_or_prompt() { return 1 fi - # Helper: validate storage _validate_storage() { local s="$1" [[ -n "$s" ]] || return 1 @@ -1658,7 +1675,12 @@ check_storage_or_prompt() { return $changed } -# Storage Settings menu +# ------------------------------------------------------------------------------ +# storage_settings_menu() +# +# - Menu for managing storage defaults +# - Options: update My Defaults or App Defaults storage +# ------------------------------------------------------------------------------ storage_settings_menu() { local menu_items=( "1" "Check & update My Defaults (default.vars)" @@ -1689,18 +1711,21 @@ storage_settings_menu() { esac } +# ------------------------------------------------------------------------------ +# check_container_resources() +# +# - Compares host RAM/CPU with required values +# - Warns if under-provisioned and asks user to continue or abort +# ------------------------------------------------------------------------------ check_container_resources() { - # Check actual RAM & Cores current_ram=$(free -m | awk 'NR==2{print $2}') current_cpu=$(nproc) - # Check whether the current RAM is less than the required RAM or the CPU cores are less than required if [[ "$current_ram" -lt "$var_ram" ]] || [[ "$current_cpu" -lt "$var_cpu" ]]; then echo -e "\n${INFO}${HOLD} ${GN}Required: ${var_cpu} CPU, ${var_ram}MB RAM ${CL}| ${RD}Current: ${current_cpu} CPU, ${current_ram}MB RAM${CL}" echo -e "${YWB}Please ensure that the ${APP} LXC is configured with at least ${var_cpu} vCPU and ${var_ram} MB RAM for the build process.${CL}\n" echo -ne "${INFO}${HOLD} May cause data loss! ${INFO} Continue update with under-provisioned LXC? " read -r prompt - # Check if the input is 'yes', otherwise exit with status 1 if [[ ! ${prompt,,} =~ ^(yes)$ ]]; then echo -e "${CROSS}${HOLD} ${YWB}Exiting based on user input.${CL}" exit 1 @@ -1710,17 +1735,20 @@ check_container_resources() { fi } +# ------------------------------------------------------------------------------ +# check_container_storage() +# +# - Checks /boot partition usage +# - Warns if usage >80% and asks user confirmation before proceeding +# ------------------------------------------------------------------------------ check_container_storage() { - # Check if the /boot partition is more than 80% full total_size=$(df /boot --output=size | tail -n 1) local used_size=$(df /boot --output=used | tail -n 1) usage=$((100 * used_size / total_size)) if ((usage > 80)); then - # Prompt the user for confirmation to continue echo -e "${INFO}${HOLD} ${YWB}Warning: Storage is dangerously low (${usage}%).${CL}" echo -ne "Continue anyway? " read -r prompt - # Check if the input is 'y' or 'yes', otherwise exit with status 1 if [[ ! ${prompt,,} =~ ^(y|yes)$ ]]; then echo -e "${CROSS}${HOLD}${YWB}Exiting based on user input.${CL}" exit 1 @@ -1728,6 +1756,12 @@ check_container_storage() { fi } +# ------------------------------------------------------------------------------ +# ssh_extract_keys_from_file() +# +# - Extracts valid SSH public keys from given file +# - Supports RSA, Ed25519, ECDSA and filters out comments/invalid lines +# ------------------------------------------------------------------------------ ssh_extract_keys_from_file() { local f="$1" [[ -r "$f" ]] || return 0 @@ -1744,6 +1778,12 @@ ssh_extract_keys_from_file() { ' } +# ------------------------------------------------------------------------------ +# ssh_build_choices_from_files() +# +# - Builds interactive whiptail checklist of available SSH keys +# - Generates fingerprint, type and comment for each key +# ------------------------------------------------------------------------------ ssh_build_choices_from_files() { local -a files=("$@") CHOICES=() @@ -1759,23 +1799,22 @@ ssh_build_choices_from_files() { id_*) [[ "$f" != *.pub ]] && continue ;; esac - # jede Key-Zeile mappen -> K| + # map every key in file while IFS= read -r key; do [[ -n "$key" ]] || continue - # Fingerprint/Type/Comment hübsch machen (best effort) typ="" fp="" cmt="" - # Nur der pure Key-Teil (ohne Optionen) ist schon in 'key' enthalten + # Only the pure key part (without options) is already included in ‘key’. read -r _typ _b64 _cmt <<<"$key" typ="${_typ:-key}" cmt="${_cmt:-}" - # Fingerprint via ssh-keygen (falls verfügbar) + # Fingerprint via ssh-keygen (if available) if command -v ssh-keygen >/dev/null 2>&1; then fp="$(printf '%s\n' "$key" | ssh-keygen -lf - 2>/dev/null | awk '{print $2}')" fi - # Label kürzen + # Label shorten [[ ${#cmt} -gt 40 ]] && cmt="${cmt:0:37}..." ln=$((ln + 1)) @@ -1787,7 +1826,12 @@ ssh_build_choices_from_files() { done } -# Sucht Standard-Quellen (authorized_keys, *.pub, /etc/ssh/authorized_keys.d/*) +# ------------------------------------------------------------------------------ +# ssh_discover_default_files() +# +# - Scans standard paths for SSH keys +# - Includes ~/.ssh/*.pub, /etc/ssh/authorized_keys, etc. +# ------------------------------------------------------------------------------ ssh_discover_default_files() { local -a cand=() shopt -s nullglob @@ -1798,6 +1842,14 @@ ssh_discover_default_files() { printf '%s\0' "${cand[@]}" } +# ------------------------------------------------------------------------------ +# start() +# +# - Entry point of script +# - On Proxmox host: calls install_script +# - In silent mode: runs update_script +# - Otherwise: shows update/setting menu +# ------------------------------------------------------------------------------ start() { source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) if command -v pveversion >/dev/null 2>&1; then @@ -1833,7 +1885,14 @@ start() { fi } -# This function collects user settings and integrates all the collected information. +# ------------------------------------------------------------------------------ +# build_container() +# +# - Creates and configures the LXC container +# - Builds network string and applies features (FUSE, TUN, VAAPI passthrough) +# - Starts container and waits for network connectivity +# - Installs base packages, SSH keys, and runs -install.sh +# ------------------------------------------------------------------------------ build_container() { # if [ "$VERBOSE" == "yes" ]; then set -x; fi @@ -2203,7 +2262,13 @@ destroy_lxc() { fi } -# This function sets the description of the container. +# ------------------------------------------------------------------------------ +# description() +# +# - Sets container description with HTML content (logo, links, badges) +# - Restarts ping-instances.service if present +# - Posts status "done" to API +# ------------------------------------------------------------------------------ description() { IP=$(pct exec "$CTID" ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) @@ -2238,8 +2303,6 @@ description() { EOF ) - - # Set Description in LXC pct set "$CTID" -description "$DESCRIPTION" if [[ -f /etc/systemd/system/ping-instances.service ]]; then @@ -2249,6 +2312,13 @@ EOF post_update_to_api "done" "none" } +# ------------------------------------------------------------------------------ +# api_exit_script() +# +# - Exit trap handler +# - Reports exit codes to API with detailed reason +# - Handles known codes (100–209) and maps them to errors +# ------------------------------------------------------------------------------ api_exit_script() { exit_code=$? if [ $exit_code -ne 0 ]; then From df2e4e60309cd577572e545312875a2277e4496a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 14:10:44 +0200 Subject: [PATCH 0955/1733] Update build.func --- misc/build.func | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/misc/build.func b/misc/build.func index b66a0786b..3ac18cd88 100644 --- a/misc/build.func +++ b/misc/build.func @@ -792,7 +792,7 @@ advanced_settings() { case "$SSH_KEY_MODE" in found) SEL=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT HOST KEYS" \ - --checklist "Select one or more keys to import:" 20 20 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script + --checklist "Select one or more keys to import:" 20 140 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script for tag in $SEL; do tag="${tag%\"}" tag="${tag#\"}" @@ -1203,26 +1203,29 @@ _sanitize_value() { # Map-Parser: read var_* from file into _VARS_IN associative array declare -A _VARS_IN -_load_vars_file_to_map() { +_load_vars_file() { local file="$1" - _VARS_IN=() - [[ -f "$file" ]] || return 0 - local line - while IFS= read -r line || [[ -n "$line" ]]; do + [ -f "$file" ] || return 0 + msg_info "Loading defaults from ${file}" + local line key val + while IFS= read -r line || [ -n "$line" ]; do line="${line#"${line%%[![:space:]]*}"}" line="${line%"${line##*[![:space:]]}"}" - [[ -z "$line" || "$line" == \#* ]] && continue - if [[ "$line" =~ ^([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then - local k="${BASH_REMATCH[1]}" - local v="${BASH_REMATCH[2]}" - [[ "$k" == var_* ]] || continue - _is_whitelisted_key "$k" || continue - # strip Quotes - if [[ "$v" =~ ^\"(.*)\"$ ]]; then v="${BASH_REMATCH[1]}"; fi - if [[ "$v" =~ ^\'(.*)\'$ ]]; then v="${BASH_REMATCH[1]}"; fi - _VARS_IN["$k"]="$v" - fi + [ -z "$line" ] && continue + case "$line" in + \#*) continue ;; + esac + key=$(printf "%s" "$line" | cut -d= -f1) + val=$(printf "%s" "$line" | cut -d= -f2-) + case "$key" in + var_*) + if _is_whitelisted_key "$key"; then + [ -z "${!key+x}" ] && export "$key=$val" + fi + ;; + esac done <"$file" + msg_ok "Loaded ${file}" } # Diff function for two var_* files -> produces human-readable diff list for $1 (old) vs $2 (new) From 28fce51699ba920f579ed3083d99760b60eaf493 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 14:11:50 +0200 Subject: [PATCH 0956/1733] Update build.func --- misc/build.func | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/misc/build.func b/misc/build.func index 3ac18cd88..028089241 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1642,8 +1642,8 @@ check_storage_or_prompt() { # Load current values local ct_store tpl_store - ct_store="$(grep -E '^var_container_storage=' "$vars_file" | cut -d= -f2-)" - tpl_store="$(grep -E '^var_template_storage=' "$vars_file" | cut -d= -f2-)" + ct_store="$(awk -F= '/^var_container_storage=/ {print $2}' "$vars_file" | head -n1)" + tpl_store="$(awk -F= '/^var_template_storage=/ {print $2}' "$vars_file" | head -n1)" # Container storage if ! _validate_storage "$ct_store"; then From 022863386e5331ca4d7187b3893084e2e02e0b38 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 14:30:55 +0200 Subject: [PATCH 0957/1733] Update build.func --- misc/build.func | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/misc/build.func b/misc/build.func index 028089241..94ff765fb 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1629,21 +1629,22 @@ check_storage_or_prompt() { local vars_file="$1" local changed=0 - if [[ ! -f "$vars_file" ]]; then + if [ ! -f "$vars_file" ]; then msg_warn "No vars file found at $vars_file" return 1 fi + # Helper: validate storage ID _validate_storage() { local s="$1" - [[ -n "$s" ]] || return 1 + [ -n "$s" ] || return 1 pvesm status -content images | awk 'NR>1 {print $1}' | grep -qx "$s" } - # Load current values + # Load current values (safe with grep/cut, no BRG/HN etc.) local ct_store tpl_store - ct_store="$(awk -F= '/^var_container_storage=/ {print $2}' "$vars_file" | head -n1)" - tpl_store="$(awk -F= '/^var_template_storage=/ {print $2}' "$vars_file" | head -n1)" + ct_store=$(grep -E '^var_container_storage=' "$vars_file" | cut -d= -f2-) + tpl_store=$(grep -E '^var_template_storage=' "$vars_file" | cut -d= -f2-) # Container storage if ! _validate_storage "$ct_store"; then @@ -1652,7 +1653,7 @@ check_storage_or_prompt() { new_ct=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ --title "Select Container Storage" \ --menu "Choose container storage:" 20 60 10 $new_ct 3>&1 1>&2 2>&3) || return 1 - if [[ -n "$new_ct" ]]; then + if [ -n "$new_ct" ]; then sed -i "/^var_container_storage=/d" "$vars_file" echo "var_container_storage=$new_ct" >>"$vars_file" changed=1 @@ -1667,7 +1668,7 @@ check_storage_or_prompt() { new_tpl=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ --title "Select Template Storage" \ --menu "Choose template storage:" 20 60 10 $new_tpl 3>&1 1>&2 2>&3) || return 1 - if [[ -n "$new_tpl" ]]; then + if [ -n "$new_tpl" ]; then sed -i "/^var_template_storage=/d" "$vars_file" echo "var_template_storage=$new_tpl" >>"$vars_file" changed=1 From d55c7776eccebed0e29e73b306d731322c87e034 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 14:31:46 +0200 Subject: [PATCH 0958/1733] Update build.func --- misc/build.func | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/misc/build.func b/misc/build.func index 94ff765fb..6586c6d4d 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1643,8 +1643,8 @@ check_storage_or_prompt() { # Load current values (safe with grep/cut, no BRG/HN etc.) local ct_store tpl_store - ct_store=$(grep -E '^var_container_storage=' "$vars_file" | cut -d= -f2-) - tpl_store=$(grep -E '^var_template_storage=' "$vars_file" | cut -d= -f2-) + ct_store=$(awk -F= '/^var_container_storage=/ {print $2; exit}' "$vars_file") + tpl_store=$(awk -F= '/^var_template_storage=/ {print $2; exit}' "$vars_file") # Container storage if ! _validate_storage "$ct_store"; then From d3eecd770a9fd1175876cdfdb135b260f63790e8 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 14:38:35 +0200 Subject: [PATCH 0959/1733] Update build.func --- misc/build.func | 2 ++ 1 file changed, 2 insertions(+) diff --git a/misc/build.func b/misc/build.func index 6586c6d4d..23950e5ab 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1626,6 +1626,7 @@ install_script() { # - Updates vars file accordingly # ------------------------------------------------------------------------------ check_storage_or_prompt() { + set +u local vars_file="$1" local changed=0 @@ -1677,6 +1678,7 @@ check_storage_or_prompt() { fi return $changed + set -u } # ------------------------------------------------------------------------------ From 47d3048115524fad11cd81be972a9624c83e75cd Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 14:44:58 +0200 Subject: [PATCH 0960/1733] Update build.func --- misc/build.func | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/misc/build.func b/misc/build.func index 23950e5ab..b54e5ea8e 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1626,13 +1626,12 @@ install_script() { # - Updates vars file accordingly # ------------------------------------------------------------------------------ check_storage_or_prompt() { - set +u local vars_file="$1" local changed=0 if [ ! -f "$vars_file" ]; then msg_warn "No vars file found at $vars_file" - return 1 + return 0 fi # Helper: validate storage ID @@ -1642,7 +1641,7 @@ check_storage_or_prompt() { pvesm status -content images | awk 'NR>1 {print $1}' | grep -qx "$s" } - # Load current values (safe with grep/cut, no BRG/HN etc.) + # Load current values (empty if not set) local ct_store tpl_store ct_store=$(awk -F= '/^var_container_storage=/ {print $2; exit}' "$vars_file") tpl_store=$(awk -F= '/^var_template_storage=/ {print $2; exit}' "$vars_file") @@ -1653,7 +1652,7 @@ check_storage_or_prompt() { new_ct=$(pvesm status -content images | awk 'NR>1 {print $1" "$2" "$6}') new_ct=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ --title "Select Container Storage" \ - --menu "Choose container storage:" 20 60 10 $new_ct 3>&1 1>&2 2>&3) || return 1 + --menu "Choose container storage:" 20 60 10 $new_ct 3>&1 1>&2 2>&3) || return 0 if [ -n "$new_ct" ]; then sed -i "/^var_container_storage=/d" "$vars_file" echo "var_container_storage=$new_ct" >>"$vars_file" @@ -1668,7 +1667,7 @@ check_storage_or_prompt() { new_tpl=$(pvesm status -content vztmpl | awk 'NR>1 {print $1" "$2" "$6}') new_tpl=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ --title "Select Template Storage" \ - --menu "Choose template storage:" 20 60 10 $new_tpl 3>&1 1>&2 2>&3) || return 1 + --menu "Choose template storage:" 20 60 10 $new_tpl 3>&1 1>&2 2>&3) || return 0 if [ -n "$new_tpl" ]; then sed -i "/^var_template_storage=/d" "$vars_file" echo "var_template_storage=$new_tpl" >>"$vars_file" @@ -1677,8 +1676,8 @@ check_storage_or_prompt() { fi fi - return $changed - set -u + # Always succeed (no aborts from here) + return 0 } # ------------------------------------------------------------------------------ From 86186a5dbdc9c0cafe60b4185881b0f9803fa25b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 14:50:57 +0200 Subject: [PATCH 0961/1733] Update build.func --- misc/build.func | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/misc/build.func b/misc/build.func index b54e5ea8e..070118f04 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1704,10 +1704,12 @@ storage_settings_menu() { case "$choice" in 1) check_storage_or_prompt "/usr/local/community-scripts/default.vars" + _echo_storage_summary "/usr/local/community-scripts/default.vars" ;; 2) if [ -f "$(get_app_defaults_path)" ]; then check_storage_or_prompt "$(get_app_defaults_path)" + _echo_storage_summary "$(get_app_defaults_path)" fi ;; 3) @@ -1716,6 +1718,18 @@ storage_settings_menu() { esac } +# --- Hilfsfunktion nur für Storage-Ausgabe --- +_echo_storage_summary() { + local vars_file="$1" + local ct_store tpl_store + ct_store=$(awk -F= '/^var_container_storage=/ {print $2; exit}' "$vars_file") + tpl_store=$(awk -F= '/^var_template_storage=/ {print $2; exit}' "$vars_file") + + echo -e "\n${INFO}${BOLD}${DGN}Storage settings from ${vars_file}:${CL}" + echo -e " 📦 Container Storage: ${BGN}${ct_store:-}${CL}" + echo -e " 📦 Template Storage: ${BGN}${tpl_store:-}${CL}\n" +} + # ------------------------------------------------------------------------------ # check_container_resources() # From 094f99d4bda5347dac711688c016db9120857655 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 15:01:39 +0200 Subject: [PATCH 0962/1733] Update build.func --- misc/build.func | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/misc/build.func b/misc/build.func index 070118f04..cc3dd7a91 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1718,7 +1718,6 @@ storage_settings_menu() { esac } -# --- Hilfsfunktion nur für Storage-Ausgabe --- _echo_storage_summary() { local vars_file="$1" local ct_store tpl_store @@ -1915,7 +1914,7 @@ start() { build_container() { # if [ "$VERBOSE" == "yes" ]; then set -x; fi - NET_STRING="-net0 name=eth0,bridge=$BRG$MAC,ip=$NET$GATE$VLAN$MTU" + NET_STRING="-net0 name=eth0,bridge=${BRG:-vmbr0}${MAC:-},ip=${NET:-dhcp}${GATE:-}${VLAN:-}${MTU:-}" case "$IPV6_METHOD" in auto) NET_STRING="$NET_STRING,ip6=auto" ;; dhcp) NET_STRING="$NET_STRING,ip6=dhcp" ;; From eb6c16f3132d00c048f37c38ad07bc127d7d5710 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 15:13:10 +0200 Subject: [PATCH 0963/1733] Update build.func --- misc/build.func | 2 ++ 1 file changed, 2 insertions(+) diff --git a/misc/build.func b/misc/build.func index cc3dd7a91..b7867c984 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1604,9 +1604,11 @@ install_script() { whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 fi fi + return 0 ;; 7) storage_settings_menu + return 0 ;; 8) echo -e "\n${CROSS}${RD}Script terminated.${CL}\n" From b980b7db48a2dfb5303335e2ba4902c890252014 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 15:33:09 +0200 Subject: [PATCH 0964/1733] Update build.func --- misc/build.func | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index b7867c984..549375b85 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1873,7 +1873,8 @@ ssh_discover_default_files() { start() { source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) if command -v pveversion >/dev/null 2>&1; then - install_script + install_script || return 0 + return 0 elif [ ! -z ${PHS_SILENT+x} ] && [[ "${PHS_SILENT}" == "1" ]]; then VERBOSE="no" set_std_mode From 79b8b32bd2ea726705d653b75d4bdd80be48f79b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 15:36:26 +0200 Subject: [PATCH 0965/1733] Update globaleaks-install.sh --- install/globaleaks-install.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/install/globaleaks-install.sh b/install/globaleaks-install.sh index f97263ee1..80e588dce 100644 --- a/install/globaleaks-install.sh +++ b/install/globaleaks-install.sh @@ -13,8 +13,9 @@ network_check update_os msg_info "Setup GlobaLeaks" -curl -fsSL https://deb.globaleaks.org/globaleaks.asc | gpg --dearmor >/etc/apt/trusted.gpg.d/globaleaks.asc -echo "deb [signed-by=/etc/apt/trusted.gpg.d/globaleaks.asc] http://deb.globaleaks.org trixie main" >/etc/apt/sources.list.d/globaleaks.list +DISTRO_CODENAME="$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release)" +curl -fsSL https://deb.globaleaks.org/globaleaks.asc | gpg --dearmor -o /etc/apt/trusted.gpg.d/globaleaks.gpg +echo "deb [signed-by=/etc/apt/trusted.gpg.d/globaleaks.gpg] http://deb.globaleaks.org $DISTRO_CODENAME/" >/etc/apt/sources.list.d/globaleaks.list $STD apt update $STD apt -y install globaleaks msg_ok "Setup GlobaLeaks" From adf51e70868a96839efa8f14b662d500295c6276 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 16:02:19 +0200 Subject: [PATCH 0966/1733] Update error_handler.func --- misc/error_handler.func | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/misc/error_handler.func b/misc/error_handler.func index edbf6f1a6..219969696 100644 --- a/misc/error_handler.func +++ b/misc/error_handler.func @@ -137,7 +137,10 @@ on_terminate() { # === Init traps =============================================================== init_error_traps() { - set -Eeuo pipefail + set -Ee -o pipefail + if [ "${STRICT_UNSET:-0}" = "1" ]; then + set -u + fi trap 'error_handler' ERR trap on_exit EXIT trap on_interrupt INT From 473602289812544308a86ab76d42fdc3e17336ba Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 16:08:09 +0200 Subject: [PATCH 0967/1733] Update build.func --- misc/build.func | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/misc/build.func b/misc/build.func index 549375b85..474572fc2 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1457,6 +1457,28 @@ maybe_offer_save_app_defaults() { rm -f "$new_tmp" "$diff_tmp" } +diagnostics_menu() { + if [ "${DIAGNOSTICS:-no}" = "yes" ]; then + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "DIAGNOSTIC SETTINGS" \ + --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ + --yes-button "No" --no-button "Back"; then + DIAGNOSTICS="no" + sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=no/' /usr/local/community-scripts/diagnostics + whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 + fi + else + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "DIAGNOSTIC SETTINGS" \ + --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ + --yes-button "Yes" --no-button "Back"; then + DIAGNOSTICS="yes" + sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=yes/' /usr/local/community-scripts/diagnostics + whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 + fi + fi +} + # ------------------------------------------------------------------------------ # install_script() # @@ -1589,26 +1611,12 @@ install_script() { fi ;; 6) - if [[ $DIAGNOSTICS == "yes" ]]; then - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTIC SETTINGS" --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ - --yes-button "No" --no-button "Back"; then - DIAGNOSTICS="no" - sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=no/' /usr/local/community-scripts/diagnostics - whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 - fi - else - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTIC SETTINGS" --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ - --yes-button "Yes" --no-button "Back"; then - DIAGNOSTICS="yes" - sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=yes/' /usr/local/community-scripts/diagnostics - whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 - fi - fi - return 0 + diagnostics_menu + exit 0 ;; 7) storage_settings_menu - return 0 + exit 0 ;; 8) echo -e "\n${CROSS}${RD}Script terminated.${CL}\n" From 108ff22a2857b8e276c8e87a367b169c18b1ff3d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 16:12:30 +0200 Subject: [PATCH 0968/1733] Update docker-vm.sh --- vm/docker-vm.sh | 650 ++++++++++++++++++++++++------------------------ 1 file changed, 330 insertions(+), 320 deletions(-) diff --git a/vm/docker-vm.sh b/vm/docker-vm.sh index 137baf92f..65a59ed75 100644 --- a/vm/docker-vm.sh +++ b/vm/docker-vm.sh @@ -9,8 +9,8 @@ set -e source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) function header_info() { - clear - cat <<"EOF" + clear + cat <<"EOF" ____ __ _ ____ ___ / __ \____ _____/ /_____ _____ | | / / |/ / / / / / __ \/ ___/ //_/ _ \/ ___/ | | / / /|_/ / @@ -70,50 +70,50 @@ trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM function error_handler() { - local exit_code="$?" - local line_number="$1" - local command="$2" - local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" - post_update_to_api "failed" "${command}" - echo -e "\n$error_message\n" - cleanup_vmid + local exit_code="$?" + local line_number="$1" + local command="$2" + local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" + post_update_to_api "failed" "${command}" + echo -e "\n$error_message\n" + cleanup_vmid } function get_valid_nextid() { - local try_id - try_id=$(pvesh get /cluster/nextid) - while true; do - if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then - try_id=$((try_id + 1)) - continue - fi - if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then - try_id=$((try_id + 1)) - continue - fi - break - done - echo "$try_id" + local try_id + try_id=$(pvesh get /cluster/nextid) + while true; do + if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then + try_id=$((try_id + 1)) + continue + fi + if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then + try_id=$((try_id + 1)) + continue + fi + break + done + echo "$try_id" } function cleanup_vmid() { - if qm status $VMID &>/dev/null; then - qm stop $VMID &>/dev/null || true - qm destroy $VMID &>/dev/null || true - fi + if qm status $VMID &>/dev/null; then + qm stop $VMID &>/dev/null || true + qm destroy $VMID &>/dev/null || true + fi } function cleanup() { - popd >/dev/null || true - post_update_to_api "done" "none" - rm -rf "$TEMP_DIR" + popd >/dev/null || true + post_update_to_api "done" "none" + rm -rf "$TEMP_DIR" } TEMP_DIR=$(mktemp -d) pushd "$TEMP_DIR" >/dev/null if ! whiptail --backtitle "Proxmox VE Helper Scripts" --title "Docker VM" --yesno "This will create a New Docker VM. Proceed?" 10 58; then - header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit + header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit fi function msg_info() { echo -ne "${TAB}${YW}${HOLD}$1${HOLD}"; } @@ -121,210 +121,210 @@ function msg_ok() { echo -e "${BFR}${CM}${GN}$1${CL}"; } function msg_error() { echo -e "${BFR}${CROSS}${RD}$1${CL}"; } function check_root() { - if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then - clear - msg_error "Please run this script as root." - echo -e "\nExiting..." - sleep 2 - exit - fi + if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then + clear + msg_error "Please run this script as root." + echo -e "\nExiting..." + sleep 2 + exit + fi } # Supported: Proxmox VE 8.0.x – 8.9.x and 9.0 (NOT 9.1+) pve_check() { - local PVE_VER - PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" - if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then - local MINOR="${BASH_REMATCH[1]}" - ((MINOR >= 0 && MINOR <= 9)) && return 0 - msg_error "This version of Proxmox VE is not supported." - exit 1 - fi - if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then - local MINOR="${BASH_REMATCH[1]}" - ((MINOR == 0)) && return 0 - msg_error "This version of Proxmox VE is not yet supported (9.1+)." - exit 1 - fi - msg_error "This version of Proxmox VE is not supported (need 8.x or 9.0)." + local PVE_VER + PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" + if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then + local MINOR="${BASH_REMATCH[1]}" + ((MINOR >= 0 && MINOR <= 9)) && return 0 + msg_error "This version of Proxmox VE is not supported." exit 1 + fi + if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then + local MINOR="${BASH_REMATCH[1]}" + ((MINOR == 0)) && return 0 + msg_error "This version of Proxmox VE is not yet supported (9.1+)." + exit 1 + fi + msg_error "This version of Proxmox VE is not supported (need 8.x or 9.0)." + exit 1 } function arch_check() { - if [ "$(dpkg --print-architecture)" != "amd64" ]; then - echo -e "\n ${INFO}This script will not work with PiMox! \n" - echo -e "\n Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" - echo -e "Exiting..." - sleep 2 - exit - fi + if [ "$(dpkg --print-architecture)" != "amd64" ]; then + echo -e "\n ${INFO}This script will not work with PiMox! \n" + echo -e "\n Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" + echo -e "Exiting..." + sleep 2 + exit + fi } function ssh_check() { - if command -v pveversion >/dev/null 2>&1 && [ -n "${SSH_CLIENT:+x}" ]; then - if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Proceed anyway?" 10 62; then :; else - clear - exit - fi + if command -v pveversion >/dev/null 2>&1 && [ -n "${SSH_CLIENT:+x}" ]; then + if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Proceed anyway?" 10 62; then :; else + clear + exit fi + fi } function exit-script() { - clear - echo -e "\n${CROSS}${RD}User exited script${CL}\n" - exit + clear + echo -e "\n${CROSS}${RD}User exited script${CL}\n" + exit } function default_settings() { - VMID=$(get_valid_nextid) - FORMAT=",efitype=4m" - DISK_CACHE="" - DISK_SIZE="10G" - HN="docker" - CPU_TYPE="" - CORE_COUNT="2" - RAM_SIZE="4096" - BRG="vmbr0" - MAC="$GEN_MAC" - VLAN="" - MTU="" - START_VM="yes" - METHOD="default" - echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}q35${CL}" - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" - echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" - echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" - echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" - echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" - echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" - echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" - echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" - echo -e "${CREATING}${BOLD}${DGN}Creating a Docker VM using the above default settings${CL}" + VMID=$(get_valid_nextid) + FORMAT=",efitype=4m" + DISK_CACHE="" + DISK_SIZE="10G" + HN="docker" + CPU_TYPE="" + CORE_COUNT="2" + RAM_SIZE="4096" + BRG="vmbr0" + MAC="$GEN_MAC" + VLAN="" + MTU="" + START_VM="yes" + METHOD="default" + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}q35${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" + echo -e "${CREATING}${BOLD}${DGN}Creating a Docker VM using the above default settings${CL}" } function advanced_settings() { - METHOD="advanced" - [ -z "${VMID:-}" ] && VMID=$(get_valid_nextid) - while true; do - if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - [ -z "$VMID" ] && VMID=$(get_valid_nextid) - if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then - echo -e "${CROSS}${RD} ID $VMID is already in use${CL}" - sleep 2 - continue - fi - echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" - break - else exit-script; fi - done - - FORMAT=",efitype=4m" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}q35${CL}" - - if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') - if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then DISK_SIZE="${DISK_SIZE}G"; fi - [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]] || { - echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size.${CL}" - exit-script - } - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" + METHOD="advanced" + [ -z "${VMID:-}" ] && VMID=$(get_valid_nextid) + while true; do + if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + [ -z "$VMID" ] && VMID=$(get_valid_nextid) + if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then + echo -e "${CROSS}${RD} ID $VMID is already in use${CL}" + sleep 2 + continue + fi + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" + break else exit-script; fi + done - if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ - "0" "None (Default)" ON "1" "Write Through" OFF 3>&1 1>&2 2>&3); then - if [ "$DISK_CACHE" = "1" ]; then - DISK_CACHE="cache=writethrough," - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" - else - DISK_CACHE="" - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" - fi - else exit-script; fi + FORMAT=",efitype=4m" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}q35${CL}" - if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 docker --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - if [ -z "$VM_NAME" ]; then HN="docker"; else HN=$(echo ${VM_NAME,,} | tr -d ' '); fi - echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" - else exit-script; fi + if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') + if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then DISK_SIZE="${DISK_SIZE}G"; fi + [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]] || { + echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size.${CL}" + exit-script + } + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" + else exit-script; fi - if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ - "0" "KVM64 (Default)" ON "1" "Host" OFF 3>&1 1>&2 2>&3); then - if [ "$CPU_TYPE1" = "1" ]; then - CPU_TYPE=" -cpu host" - echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" - else - CPU_TYPE="" - echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" - fi - else exit-script; fi - - if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - [ -z "$CORE_COUNT" ] && CORE_COUNT="2" - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" - else exit-script; fi - - if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - [ -z "$RAM_SIZE" ] && RAM_SIZE="2048" - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" - else exit-script; fi - - if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - [ -z "$BRG" ] && BRG="vmbr0" - echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" - else exit-script; fi - - if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - if [ -z "$MAC1" ]; then MAC="$GEN_MAC"; else MAC="$MAC1"; fi - echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" - else exit-script; fi - - if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - if [ -z "$VLAN1" ]; then - VLAN1="Default" - VLAN="" - else VLAN=",tag=$VLAN1"; fi - echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" - else exit-script; fi - - if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - if [ -z "$MTU1" ]; then - MTU1="Default" - MTU="" - else MTU=",mtu=$MTU1"; fi - echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" - else exit-script; fi - - if whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58; then - echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" - START_VM="yes" + if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ + "0" "None (Default)" ON "1" "Write Through" OFF 3>&1 1>&2 2>&3); then + if [ "$DISK_CACHE" = "1" ]; then + DISK_CACHE="cache=writethrough," + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" else - echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}" - START_VM="no" + DISK_CACHE="" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" fi + else exit-script; fi - if whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Docker VM?" --no-button Do-Over 10 58; then - echo -e "${CREATING}${BOLD}${DGN}Creating a Docker VM using the above advanced settings${CL}" + if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 docker --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z "$VM_NAME" ]; then HN="docker"; else HN=$(echo ${VM_NAME,,} | tr -d ' '); fi + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" + else exit-script; fi + + if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ + "0" "KVM64 (Default)" ON "1" "Host" OFF 3>&1 1>&2 2>&3); then + if [ "$CPU_TYPE1" = "1" ]; then + CPU_TYPE=" -cpu host" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" else - header_info - echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" - advanced_settings + CPU_TYPE="" + echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" fi + else exit-script; fi + + if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + [ -z "$CORE_COUNT" ] && CORE_COUNT="2" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + else exit-script; fi + + if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + [ -z "$RAM_SIZE" ] && RAM_SIZE="2048" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" + else exit-script; fi + + if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + [ -z "$BRG" ] && BRG="vmbr0" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + else exit-script; fi + + if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z "$MAC1" ]; then MAC="$GEN_MAC"; else MAC="$MAC1"; fi + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" + else exit-script; fi + + if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z "$VLAN1" ]; then + VLAN1="Default" + VLAN="" + else VLAN=",tag=$VLAN1"; fi + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" + else exit-script; fi + + if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z "$MTU1" ]; then + MTU1="Default" + MTU="" + else MTU=",mtu=$MTU1"; fi + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" + else exit-script; fi + + if whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58; then + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" + START_VM="yes" + else + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}" + START_VM="no" + fi + + if whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Docker VM?" --no-button Do-Over 10 58; then + echo -e "${CREATING}${BOLD}${DGN}Creating a Docker VM using the above advanced settings${CL}" + else + header_info + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" + advanced_settings + fi } function start_script() { - if whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58; then - header_info - echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}" - default_settings - else - header_info - echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" - advanced_settings - fi + if whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58; then + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}" + default_settings + else + header_info + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" + advanced_settings + fi } check_root @@ -335,72 +335,72 @@ start_script post_to_api_vm function choose_os() { - if OS_CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ - --title "Choose Base OS" \ - --radiolist "Select the OS for the Docker VM:" 12 60 3 \ - "debian12" "Debian 12 (Bookworm, stable & best for scripts)" ON \ - "debian13" "Debian 13 (Trixie, newer, but repos lag)" OFF \ - "ubuntu24" "Ubuntu 24.04 LTS (modern kernel, GPU/AI friendly)" OFF \ - 3>&1 1>&2 2>&3); then - case "$OS_CHOICE" in - debian12) - var_os="debian" - var_version="12" - URL="https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-nocloud-$(dpkg --print-architecture).qcow2" - ;; - debian13) - var_os="debian" - var_version="13" - URL="https://cloud.debian.org/images/cloud/trixie/latest/debian-13-nocloud-$(dpkg --print-architecture).qcow2" - ;; - ubuntu24) - var_os="ubuntu" - var_version="24.04" - URL="https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-$(dpkg --print-architecture).img" - ;; - esac - echo -e "${OS}${BOLD}${DGN}Selected OS: ${BGN}${OS_CHOICE}${CL}" - else - exit-script - fi + if OS_CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "Choose Base OS" \ + --radiolist "Select the OS for the Docker VM:" 12 60 3 \ + "debian12" "Debian 12 (Bookworm, stable & best for scripts)" ON \ + "debian13" "Debian 13 (Trixie, newer, but repos lag)" OFF \ + "ubuntu24" "Ubuntu 24.04 LTS (modern kernel, GPU/AI friendly)" OFF \ + 3>&1 1>&2 2>&3); then + case "$OS_CHOICE" in + debian12) + var_os="debian" + var_version="12" + URL="https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-nocloud-$(dpkg --print-architecture).qcow2" + ;; + debian13) + var_os="debian" + var_version="13" + URL="https://cloud.debian.org/images/cloud/trixie/latest/debian-13-nocloud-$(dpkg --print-architecture).qcow2" + ;; + ubuntu24) + var_os="ubuntu" + var_version="24.04" + URL="https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-$(dpkg --print-architecture).img" + ;; + esac + echo -e "${OS}${BOLD}${DGN}Selected OS: ${BGN}${OS_CHOICE}${CL}" + else + exit-script + fi } PVE_VER=$(pveversion | awk -F'/' '{print $2}' | cut -d'-' -f1 | cut -d'.' -f1) if [ "$PVE_VER" -eq 8 ]; then - INSTALL_MODE="direct" + INSTALL_MODE="direct" elif [ "$PVE_VER" -eq 9 ]; then - INSTALL_MODE="firstboot" + INSTALL_MODE="firstboot" else - msg_error "Unsupported Proxmox VE version: $PVE_VER" - exit 1 + msg_error "Unsupported Proxmox VE version: $PVE_VER" + exit 1 fi # ---------- Storage selection ---------- msg_info "Validating Storage" while read -r line; do - TAG=$(echo $line | awk '{print $1}') - TYPE=$(echo $line | awk '{printf "%-10s", $2}') - FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') - ITEM=" Type: $TYPE Free: $FREE " - OFFSET=2 - if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then - MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) - fi - STORAGE_MENU+=("$TAG" "$ITEM" "OFF") + TAG=$(echo $line | awk '{print $1}') + TYPE=$(echo $line | awk '{printf "%-10s", $2}') + FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') + ITEM=" Type: $TYPE Free: $FREE " + OFFSET=2 + if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then + MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) + fi + STORAGE_MENU+=("$TAG" "$ITEM" "OFF") done < <(pvesm status -content images | awk 'NR>1') VALID=$(pvesm status -content images | awk 'NR>1') if [ -z "$VALID" ]; then - msg_error "Unable to detect a valid storage location." - exit + msg_error "Unable to detect a valid storage location." + exit elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then - STORAGE=${STORAGE_MENU[0]} + STORAGE=${STORAGE_MENU[0]} else - while [ -z "${STORAGE:+x}" ]; do - STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ - "Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \ - 16 $(($MSG_MAX_LENGTH + 23)) 6 \ - "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) - done + while [ -z "${STORAGE:+x}" ]; do + STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ + "Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \ + 16 $(($MSG_MAX_LENGTH + 23)) 6 \ + "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) + done fi msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." @@ -414,39 +414,43 @@ msg_ok "Downloaded ${CL}${BL}${FILE}${CL}" # Ubuntu RAW → qcow2 if [[ "$FILE" == *.img ]]; then - msg_info "Converting RAW image to qcow2" - qemu-img convert -O qcow2 "$FILE" "${FILE%.img}.qcow2" - rm -f "$FILE" - FILE="${FILE%.img}.qcow2" - msg_ok "Converted to ${CL}${BL}${FILE}${CL}" + msg_info "Converting RAW image to qcow2" + qemu-img convert -O qcow2 "$FILE" "${FILE%.img}.qcow2" + rm -f "$FILE" + FILE="${FILE%.img}.qcow2" + msg_ok "Converted to ${CL}${BL}${FILE}${CL}" fi # ---------- Ensure libguestfs-tools ---------- -if ! command -v virt-customize &>/dev/null; then - msg_info "Installing libguestfs-tools on host" - apt-get -qq update >/dev/null - apt-get -qq install -y libguestfs-tools lsb-release >/dev/null - msg_ok "Installed libguestfs-tools" +INSTALL_MODE="direct" +if ! command -v virt-customize >/dev/null 2>&1; then + msg_info "Installing libguestfs-tools" + apt-get -qq update >/dev/null + apt-get -qq install -y libguestfs-tools >/dev/null + msg_ok "Installed libguestfs-tools" fi +# Some PVE9 nodes need this for guestfs +export LIBGUESTFS_BACKEND=direct + # ---------- Decide distro codename & Docker repo base ---------- if [[ "$URL" == *"/bookworm/"* || "$FILE" == *"debian-12-"* ]]; then - CODENAME="bookworm" - DOCKER_BASE="https://download.docker.com/linux/debian" + CODENAME="bookworm" + DOCKER_BASE="https://download.docker.com/linux/debian" elif [[ "$URL" == *"/trixie/"* || "$FILE" == *"debian-13-"* ]]; then - CODENAME="trixie" - DOCKER_BASE="https://download.docker.com/linux/debian" + CODENAME="trixie" + DOCKER_BASE="https://download.docker.com/linux/debian" elif [[ "$URL" == *"/noble/"* || "$FILE" == *"noble-"* ]]; then - CODENAME="noble" - DOCKER_BASE="https://download.docker.com/linux/ubuntu" + CODENAME="noble" + DOCKER_BASE="https://download.docker.com/linux/ubuntu" else - CODENAME="bookworm" - DOCKER_BASE="https://download.docker.com/linux/debian" + CODENAME="bookworm" + DOCKER_BASE="https://download.docker.com/linux/debian" fi # Map Debian trixie → bookworm (Docker-Repo oft später) REPO_CODENAME="$CODENAME" if [[ "$DOCKER_BASE" == *"linux/debian"* && "$CODENAME" == "trixie" ]]; then - REPO_CODENAME="bookworm" + REPO_CODENAME="bookworm" fi # ---------- Detect PVE major version (again; independent var) ---------- @@ -455,39 +459,41 @@ if [ "$PVE_MAJ" -eq 8 ]; then INSTALL_MODE="direct"; else INSTALL_MODE="firstboo # ---------- Optional: allow manual override ---------- if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Docker Installation Mode" \ - --yesno "Detected PVE ${PVE_MAJ}. Use ${INSTALL_MODE^^} mode?\n\nYes = ${INSTALL_MODE^^}\nNo = Switch to the other mode" 11 70; then :; else - if [ "$INSTALL_MODE" = "direct" ]; then INSTALL_MODE="firstboot"; else INSTALL_MODE="direct"; fi + --yesno "Detected PVE ${PVE_MAJ}. Use ${INSTALL_MODE^^} mode?\n\nYes = ${INSTALL_MODE^^}\nNo = Switch to the other mode" 11 70; then :; else + if [ "$INSTALL_MODE" = "direct" ]; then INSTALL_MODE="firstboot"; else INSTALL_MODE="direct"; fi fi # ---------- PVE8: Direct install into image via virt-customize ---------- if [ "$INSTALL_MODE" = "direct" ]; then - msg_info "Injecting Docker directly into image (${CODENAME}, $(basename "$DOCKER_BASE"))" - virt-customize -q -a "${FILE}" \ - --install qemu-guest-agent,apt-transport-https,ca-certificates,curl,gnupg,lsb-release \ - --run-command "install -m 0755 -d /etc/apt/keyrings" \ - --run-command "curl -fsSL ${DOCKER_BASE}/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg" \ - --run-command "chmod a+r /etc/apt/keyrings/docker.gpg" \ - --run-command "echo 'deb [arch=\$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] ${DOCKER_BASE} ${REPO_CODENAME} stable' > /etc/apt/sources.list.d/docker.list" \ - --run-command "apt-get update -qq" \ - --run-command "apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin" \ - --run-command "systemctl enable docker" \ - --run-command "systemctl enable qemu-guest-agent" >/dev/null + msg_info "Injecting Docker & QGA into image (${CODENAME}, repo base: $(basename "$DOCKER_BASE"))" + # robust retry wrapper + vrun() { virt-customize -q -a "${FILE}" "$@" >/dev/null; } - # PATH-Fix separat - virt-customize -q -a "${FILE}" \ - --run-command "sed -i 's#^ENV_SUPATH.*#ENV_SUPATH PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin#' /etc/login.defs || true" \ - --run-command "sed -i 's#^ENV_PATH.*#ENV_PATH PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin#' /etc/login.defs || true" \ - --run-command "printf 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\n' >/etc/environment" \ - --run-command "grep -q 'export PATH=' /root/.bashrc || echo 'export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' >> /root/.bashrc" >/dev/null + vrun \ + --install qemu-guest-agent,apt-transport-https,ca-certificates,curl,gnupg,lsb-release \ + --run-command "install -m 0755 -d /etc/apt/keyrings" \ + --run-command "curl -fsSL ${DOCKER_BASE}/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg" \ + --run-command "chmod a+r /etc/apt/keyrings/docker.gpg" \ + --run-command "echo 'deb [arch=\$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] ${DOCKER_BASE} ${REPO_CODENAME} stable' > /etc/apt/sources.list.d/docker.list" \ + --run-command "apt-get update -qq" \ + --run-command "apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin" \ + --run-command "systemctl enable docker qemu-guest-agent" - msg_ok "Docker injected into image" + # PATH / login.defs Korrekturen + vrun \ + --run-command "sed -i 's#^ENV_SUPATH.*#ENV_SUPATH PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin#' /etc/login.defs || true" \ + --run-command "sed -i 's#^ENV_PATH.*#ENV_PATH PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin#' /etc/login.defs || true" \ + --run-command "printf 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\n' >/etc/environment" \ + --run-command "grep -q 'export PATH=' /root/.bashrc || echo 'export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' >> /root/.bashrc" + + msg_ok "Docker & QGA injected" fi # ---------- PVE9: First-boot installer inside guest ---------- if [ "$INSTALL_MODE" = "firstboot" ]; then - msg_info "Preparing first-boot Docker installer (${CODENAME}, $(basename "$DOCKER_BASE"))" - mkdir -p firstboot - cat >firstboot/firstboot-docker.sh <<'EOSH' + msg_info "Preparing first-boot Docker installer (${CODENAME}, $(basename "$DOCKER_BASE"))" + mkdir -p firstboot + cat >firstboot/firstboot-docker.sh <<'EOSH' #!/usr/bin/env bash set -euxo pipefail @@ -549,9 +555,9 @@ main() { } main EOSH - chmod +x firstboot/firstboot-docker.sh + chmod +x firstboot/firstboot-docker.sh - cat >firstboot/firstboot-docker.service <<'EOUNIT' + cat >firstboot/firstboot-docker.service <<'EOUNIT' [Unit] Description=First boot: install Docker & QGA After=network-online.target cloud-init.service @@ -571,18 +577,18 @@ RemainAfterExit=no WantedBy=multi-user.target EOUNIT - echo "$HN" >firstboot/hostname + echo "$HN" >firstboot/hostname - virt-customize -q -a "${FILE}" \ - --copy-in firstboot/firstboot-docker.sh:/usr/local/sbin \ - --copy-in firstboot/firstboot-docker.service:/etc/systemd/system \ - --copy-in firstboot/hostname:/etc \ - --run-command "chmod +x /usr/local/sbin/firstboot-docker.sh" \ - --run-command "systemctl enable firstboot-docker.service" \ - --run-command "echo -n > /etc/machine-id" \ - --run-command "truncate -s 0 /etc/hostname && mv /etc/hostname /etc/hostname.orig && echo '${HN}' >/etc/hostname" >/dev/null + virt-customize -q -a "${FILE}" \ + --copy-in firstboot/firstboot-docker.sh:/usr/local/sbin \ + --copy-in firstboot/firstboot-docker.service:/etc/systemd/system \ + --copy-in firstboot/hostname:/etc \ + --run-command "chmod +x /usr/local/sbin/firstboot-docker.sh" \ + --run-command "systemctl enable firstboot-docker.service" \ + --run-command "echo -n > /etc/machine-id" \ + --run-command "truncate -s 0 /etc/hostname && mv /etc/hostname /etc/hostname.orig && echo '${HN}' >/etc/hostname" >/dev/null - msg_ok "First-boot Docker installer injected" + msg_ok "First-boot Docker installer injected" fi # ---------- Expand partition offline ---------- @@ -595,8 +601,8 @@ msg_ok "Expanded image to full size" # ---------- Create VM shell (q35) ---------- msg_info "Creating a Docker VM shell" qm create "$VMID" -machine q35 -bios ovmf -agent 1 -tablet 0 -localtime 1 ${CPU_TYPE} \ - -cores "$CORE_COUNT" -memory "$RAM_SIZE" -name "$HN" -tags community-script \ - -net0 "virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU" -onboot 1 -ostype l26 -scsihw virtio-scsi-pci >/dev/null + -cores "$CORE_COUNT" -memory "$RAM_SIZE" -name "$HN" -tags community-script \ + -net0 "virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU" -onboot 1 -ostype l26 -scsihw virtio-scsi-pci >/dev/null msg_ok "Created VM shell" # ---------- Import disk ---------- @@ -606,20 +612,24 @@ IMPORT_OUT="$("${IMPORT_CMD[@]}" "$VMID" "${FILE}" "$STORAGE" --format qcow2 2>& DISK_REF="$(printf '%s\n' "$IMPORT_OUT" | sed -n "s/.*successfully imported disk '\([^']\+\)'.*/\1/p" | tr -d "\r\"'")" [[ -z "$DISK_REF" ]] && DISK_REF="$(pvesm list "$STORAGE" | awk -v id="$VMID" '$5 ~ ("vm-"id"-disk-") {print $1":"$5}' | sort | tail -n1)" [[ -z "$DISK_REF" ]] && { - msg_error "Unable to determine imported disk reference." - echo "$IMPORT_OUT" - exit 1 + msg_error "Unable to determine imported disk reference." + echo "$IMPORT_OUT" + exit 1 } msg_ok "Imported disk (${CL}${BL}${DISK_REF}${CL})" # ---------- Attach EFI + root disk ---------- msg_info "Attaching EFI and root disk" qm set "$VMID" \ - --efidisk0 "${STORAGE}:0${FORMAT}" \ - --scsi0 "${DISK_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE}" \ - --boot order=scsi0 \ - --serial0 socket >/dev/null -qm set "$VMID" --agent enabled=1 >/dev/null + --efidisk0 "${STORAGE}:0${FORMAT}" \ + --scsi0 "${DISK_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE}" \ + --boot order=scsi0 \ + --serial0 socket >/dev/null +qm set "$VMID" --agent enabled=1,fstrim_cloned_disks=1 >/dev/null +qm set "$VMID" --ide2 "${STORAGE}:cloudinit" >/dev/null +qm set "$VMID" --ciuser root --cipassword '' --sshkeys "/root/.ssh/authorized_keys" >/dev/null || true +qm set "$VMID" --ipconfig0 "ip=dhcp" >/dev/null +qm set "$VMID" --nameserver "1.1.1.1 9.9.9.9" --searchdomain "lan" >/dev/null msg_ok "Attached EFI and root disk" # ---------- Ensure final size (PVE layer) ---------- @@ -629,7 +639,7 @@ msg_ok "Resized disk" # ---------- Description ---------- DESCRIPTION=$( - cat < Logo @@ -663,9 +673,9 @@ qm set "$VMID" -description "$DESCRIPTION" >/dev/null msg_ok "Created a Docker VM ${CL}${BL}(${HN})" if [ "$START_VM" == "yes" ]; then - msg_info "Starting Docker VM" - qm start $VMID - msg_ok "Started Docker VM" + msg_info "Starting Docker VM" + qm start $VMID + msg_ok "Started Docker VM" fi post_update_to_api "done" "none" From ee435699162b2464b72657d95f5454e3100a0c09 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 16 Sep 2025 16:14:20 +0200 Subject: [PATCH 0969/1733] Update build.func --- misc/build.func | 55 +++++++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/misc/build.func b/misc/build.func index 474572fc2..80ac79488 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1697,35 +1697,40 @@ check_storage_or_prompt() { # - Options: update My Defaults or App Defaults storage # ------------------------------------------------------------------------------ storage_settings_menu() { - local menu_items=( - "1" "Check & update My Defaults (default.vars)" - ) - if [ -f "$(get_app_defaults_path)" ]; then - menu_items+=("2" "Check & update App Defaults for ${APP}") - fi - menu_items+=("3" "Back") + local vars_file="/usr/local/community-scripts/default.vars" - local choice - choice=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + check_storage_or_prompt "$vars_file" + _echo_storage_summary "$vars_file" + + # Always ask user if they want to update, even if values are valid + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ --title "STORAGE SETTINGS" \ - --menu "Select what to update:" 15 60 5 \ - "${menu_items[@]}" 3>&1 1>&2 2>&3) || return 0 + --yesno "Do you want to update the storage defaults?\n\nCurrent values will be kept unless you select new ones." 12 72; then - case "$choice" in - 1) - check_storage_or_prompt "/usr/local/community-scripts/default.vars" - _echo_storage_summary "/usr/local/community-scripts/default.vars" - ;; - 2) - if [ -f "$(get_app_defaults_path)" ]; then - check_storage_or_prompt "$(get_app_defaults_path)" - _echo_storage_summary "$(get_app_defaults_path)" + # container storage selection + local new_ct + new_ct=$(pvesm status -content images | awk 'NR>1 {print $1" "$2" "$6}') + new_ct=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "Select Container Storage" \ + --menu "Choose container storage:" 20 60 10 $new_ct 3>&1 1>&2 2>&3) || true + if [ -n "$new_ct" ]; then + sed -i '/^var_container_storage=/d' "$vars_file" + echo "var_container_storage=$new_ct" >>"$vars_file" + msg_ok "Updated container storage → $new_ct" fi - ;; - 3) - return 0 - ;; - esac + + # template storage selection + local new_tpl + new_tpl=$(pvesm status -content vztmpl | awk 'NR>1 {print $1" "$2" "$6}') + new_tpl=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "Select Template Storage" \ + --menu "Choose template storage:" 20 60 10 $new_tpl 3>&1 1>&2 2>&3) || true + if [ -n "$new_tpl" ]; then + sed -i '/^var_template_storage=/d' "$vars_file" + echo "var_template_storage=$new_tpl" >>"$vars_file" + msg_ok "Updated template storage → $new_tpl" + fi + fi } _echo_storage_summary() { From 4cde83ce3cb80a668ecff32eb41b1db4e9151811 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 17 Sep 2025 08:56:10 +0200 Subject: [PATCH 0970/1733] Update globaleaks-install.sh --- install/globaleaks-install.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/install/globaleaks-install.sh b/install/globaleaks-install.sh index 80e588dce..08e8348d7 100644 --- a/install/globaleaks-install.sh +++ b/install/globaleaks-install.sh @@ -18,6 +18,9 @@ curl -fsSL https://deb.globaleaks.org/globaleaks.asc | gpg --dearmor -o /etc/apt echo "deb [signed-by=/etc/apt/trusted.gpg.d/globaleaks.gpg] http://deb.globaleaks.org $DISTRO_CODENAME/" >/etc/apt/sources.list.d/globaleaks.list $STD apt update $STD apt -y install globaleaks +echo 'APPARMOR_SANDBOXING=0' | sudo tee -a /etc/default/globaleaks +echo 'NETWORK_SANDBOXING=0' | sudo tee -a /etc/default/globaleaks +systemctl restart globaleaks msg_ok "Setup GlobaLeaks" motd_ssh From 256e22d9bc0aca1d2ad4d489dfb7a3ebfbc6eb7e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 17 Sep 2025 10:08:51 +0200 Subject: [PATCH 0971/1733] Fix API endpoint in data fetching function --- frontend/src/app/data-dev/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/data-dev/page.tsx b/frontend/src/app/data-dev/page.tsx index 8b53cade8..0559e5bb1 100644 --- a/frontend/src/app/data-dev/page.tsx +++ b/frontend/src/app/data-dev/page.tsx @@ -29,7 +29,7 @@ const DataFetcher: React.FC = () => { const fetchPaginatedData = async () => { setLoading(true); try { - const response = await fetch(`https://api.htl-braunau.at/dev/data/paginated?page=${currentPage}&limit=${itemsPerPage === 0 ? '' : itemsPerPage}`); + const response = await fetch(`https://api.htl-braunau.at/data/paginated?page=${currentPage}&limit=${itemsPerPage === 0 ? '' : itemsPerPage}`); if (!response.ok) throw new Error(`Failed to fetch data: ${response.statusText}`); const result: DataModel[] = await response.json(); setData(result); From d8756722bc4e797c74b0c99ad4a2b2ee104a076e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 17 Sep 2025 10:52:03 +0200 Subject: [PATCH 0972/1733] Update build.func --- misc/build.func | 75 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 6 deletions(-) diff --git a/misc/build.func b/misc/build.func index 80ac79488..6083b6c3e 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1593,10 +1593,10 @@ install_script() { msg_error "Failed to apply default.vars" exit 1 } - check_storage_or_prompt "/usr/local/community-scripts/default.vars" + select_container_storage "/usr/local/community-scripts/default.vars" + select_template_storage "/usr/local/community-scripts/default.vars" ;; 5) - # App Defaults if [ -f "$(get_app_defaults_path)" ]; then header_info echo -e "${DEFAULT}${BOLD}${BL}Using App Defaults for ${APP} on node $PVEHOST_NAME${CL}" @@ -1604,10 +1604,8 @@ install_script() { base_settings _load_vars_file "$(get_app_defaults_path)" echo_default - check_storage_or_prompt "$(get_app_defaults_path)" - else - msg_error "No App Defaults available for ${APP}" - exit 1 + select_container_storage "$(get_app_defaults_path)" + select_template_storage "$(get_app_defaults_path)" fi ;; 6) @@ -1733,6 +1731,71 @@ storage_settings_menu() { fi } +select_container_storage() { + local vars_file="$1" + local current + current=$(awk -F= '/^var_container_storage=/ {print $2; exit}' "$vars_file") + + local storages + storages=$(pvesm status -content images | awk 'NR>1 {print $1}') + + local count + count=$(echo "$storages" | wc -l) + + local choice + if [ "$count" -eq 1 ]; then + choice="$storages" + else + # Build menu list: ID + type + local menu_items + menu_items=$(pvesm status -content images | awk -v cur="$current" 'NR>1 {printf "%s %s\n", $1, $2}') + + choice=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "Select Container Storage" \ + --menu "Choose container storage:" 20 60 10 \ + --default-item "$current" \ + $menu_items 3>&1 1>&2 2>&3) || return 1 + fi + + if [ -n "$choice" ]; then + sed -i '/^var_container_storage=/d' "$vars_file" + echo "var_container_storage=$choice" >>"$vars_file" + msg_ok "Updated container storage → $choice" + fi +} + +select_template_storage() { + local vars_file="$1" + local current + current=$(awk -F= '/^var_template_storage=/ {print $2; exit}' "$vars_file") + + local storages + storages=$(pvesm status -content vztmpl | awk 'NR>1 {print $1}') + + local count + count=$(echo "$storages" | wc -l) + + local choice + if [ "$count" -eq 1 ]; then + choice="$storages" + else + local menu_items + menu_items=$(pvesm status -content vztmpl | awk -v cur="$current" 'NR>1 {printf "%s %s\n", $1, $2}') + + choice=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "Select Template Storage" \ + --menu "Choose template storage:" 20 60 10 \ + --default-item "$current" \ + $menu_items 3>&1 1>&2 2>&3) || return 1 + fi + + if [ -n "$choice" ]; then + sed -i '/^var_template_storage=/d' "$vars_file" + echo "var_template_storage=$choice" >>"$vars_file" + msg_ok "Updated template storage → $choice" + fi +} + _echo_storage_summary() { local vars_file="$1" local ct_store tpl_store From e2306361c5b1383cf87a497c9f1dca41765ec263 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 17 Sep 2025 11:17:54 +0200 Subject: [PATCH 0973/1733] Update build.func --- misc/build.func | 178 +++++++++++++++++++++++++----------------------- 1 file changed, 91 insertions(+), 87 deletions(-) diff --git a/misc/build.func b/misc/build.func index 6083b6c3e..928a3bc73 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1613,7 +1613,11 @@ install_script() { exit 0 ;; 7) - storage_settings_menu + header_info + echo -e "${DEFAULT}${BOLD}${BL}Manage Storage Settings on node $PVEHOST_NAME${CL}" + select_container_storage "/usr/local/community-scripts/default.vars" + select_template_storage "/usr/local/community-scripts/default.vars" + _echo_storage_summary "/usr/local/community-scripts/default.vars" exit 0 ;; 8) @@ -1633,103 +1637,103 @@ install_script() { # - If invalid or missing, prompts user to select new storage # - Updates vars file accordingly # ------------------------------------------------------------------------------ -check_storage_or_prompt() { - local vars_file="$1" - local changed=0 +# check_storage_or_prompt() { +# local vars_file="$1" +# local changed=0 - if [ ! -f "$vars_file" ]; then - msg_warn "No vars file found at $vars_file" - return 0 - fi +# if [ ! -f "$vars_file" ]; then +# msg_warn "No vars file found at $vars_file" +# return 0 +# fi - # Helper: validate storage ID - _validate_storage() { - local s="$1" - [ -n "$s" ] || return 1 - pvesm status -content images | awk 'NR>1 {print $1}' | grep -qx "$s" - } +# # Helper: validate storage ID +# _validate_storage() { +# local s="$1" +# [ -n "$s" ] || return 1 +# pvesm status -content images | awk 'NR>1 {print $1}' | grep -qx "$s" +# } - # Load current values (empty if not set) - local ct_store tpl_store - ct_store=$(awk -F= '/^var_container_storage=/ {print $2; exit}' "$vars_file") - tpl_store=$(awk -F= '/^var_template_storage=/ {print $2; exit}' "$vars_file") +# # Load current values (empty if not set) +# local ct_store tpl_store +# ct_store=$(awk -F= '/^var_container_storage=/ {print $2; exit}' "$vars_file") +# tpl_store=$(awk -F= '/^var_template_storage=/ {print $2; exit}' "$vars_file") - # Container storage - if ! _validate_storage "$ct_store"; then - local new_ct - new_ct=$(pvesm status -content images | awk 'NR>1 {print $1" "$2" "$6}') - new_ct=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "Select Container Storage" \ - --menu "Choose container storage:" 20 60 10 $new_ct 3>&1 1>&2 2>&3) || return 0 - if [ -n "$new_ct" ]; then - sed -i "/^var_container_storage=/d" "$vars_file" - echo "var_container_storage=$new_ct" >>"$vars_file" - changed=1 - msg_ok "Updated container storage in $vars_file → $new_ct" - fi - fi +# # Container storage +# if ! _validate_storage "$ct_store"; then +# local new_ct +# new_ct=$(pvesm status -content images | awk 'NR>1 {print $1" "$2" "$6}') +# new_ct=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ +# --title "Select Container Storage" \ +# --menu "Choose container storage:" 20 60 10 $new_ct 3>&1 1>&2 2>&3) || return 0 +# if [ -n "$new_ct" ]; then +# sed -i "/^var_container_storage=/d" "$vars_file" +# echo "var_container_storage=$new_ct" >>"$vars_file" +# changed=1 +# msg_ok "Updated container storage in $vars_file → $new_ct" +# fi +# fi - # Template storage - if ! _validate_storage "$tpl_store"; then - local new_tpl - new_tpl=$(pvesm status -content vztmpl | awk 'NR>1 {print $1" "$2" "$6}') - new_tpl=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "Select Template Storage" \ - --menu "Choose template storage:" 20 60 10 $new_tpl 3>&1 1>&2 2>&3) || return 0 - if [ -n "$new_tpl" ]; then - sed -i "/^var_template_storage=/d" "$vars_file" - echo "var_template_storage=$new_tpl" >>"$vars_file" - changed=1 - msg_ok "Updated template storage in $vars_file → $new_tpl" - fi - fi +# # Template storage +# if ! _validate_storage "$tpl_store"; then +# local new_tpl +# new_tpl=$(pvesm status -content vztmpl | awk 'NR>1 {print $1" "$2" "$6}') +# new_tpl=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ +# --title "Select Template Storage" \ +# --menu "Choose template storage:" 20 60 10 $new_tpl 3>&1 1>&2 2>&3) || return 0 +# if [ -n "$new_tpl" ]; then +# sed -i "/^var_template_storage=/d" "$vars_file" +# echo "var_template_storage=$new_tpl" >>"$vars_file" +# changed=1 +# msg_ok "Updated template storage in $vars_file → $new_tpl" +# fi +# fi - # Always succeed (no aborts from here) - return 0 -} +# # Always succeed (no aborts from here) +# return 0 +# } -# ------------------------------------------------------------------------------ -# storage_settings_menu() -# -# - Menu for managing storage defaults -# - Options: update My Defaults or App Defaults storage -# ------------------------------------------------------------------------------ -storage_settings_menu() { - local vars_file="/usr/local/community-scripts/default.vars" +# # ------------------------------------------------------------------------------ +# # storage_settings_menu() +# # +# # - Menu for managing storage defaults +# # - Options: update My Defaults or App Defaults storage +# # ------------------------------------------------------------------------------ +# storage_settings_menu() { +# local vars_file="/usr/local/community-scripts/default.vars" - check_storage_or_prompt "$vars_file" - _echo_storage_summary "$vars_file" +# check_storage_or_prompt "$vars_file" +# _echo_storage_summary "$vars_file" - # Always ask user if they want to update, even if values are valid - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "STORAGE SETTINGS" \ - --yesno "Do you want to update the storage defaults?\n\nCurrent values will be kept unless you select new ones." 12 72; then +# # Always ask user if they want to update, even if values are valid +# if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ +# --title "STORAGE SETTINGS" \ +# --yesno "Do you want to update the storage defaults?\n\nCurrent values will be kept unless you select new ones." 12 72; then - # container storage selection - local new_ct - new_ct=$(pvesm status -content images | awk 'NR>1 {print $1" "$2" "$6}') - new_ct=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "Select Container Storage" \ - --menu "Choose container storage:" 20 60 10 $new_ct 3>&1 1>&2 2>&3) || true - if [ -n "$new_ct" ]; then - sed -i '/^var_container_storage=/d' "$vars_file" - echo "var_container_storage=$new_ct" >>"$vars_file" - msg_ok "Updated container storage → $new_ct" - fi +# # container storage selection +# local new_ct +# new_ct=$(pvesm status -content images | awk 'NR>1 {print $1" "$2" "$6}') +# new_ct=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ +# --title "Select Container Storage" \ +# --menu "Choose container storage:" 20 60 10 $new_ct 3>&1 1>&2 2>&3) || true +# if [ -n "$new_ct" ]; then +# sed -i '/^var_container_storage=/d' "$vars_file" +# echo "var_container_storage=$new_ct" >>"$vars_file" +# msg_ok "Updated container storage → $new_ct" +# fi - # template storage selection - local new_tpl - new_tpl=$(pvesm status -content vztmpl | awk 'NR>1 {print $1" "$2" "$6}') - new_tpl=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "Select Template Storage" \ - --menu "Choose template storage:" 20 60 10 $new_tpl 3>&1 1>&2 2>&3) || true - if [ -n "$new_tpl" ]; then - sed -i '/^var_template_storage=/d' "$vars_file" - echo "var_template_storage=$new_tpl" >>"$vars_file" - msg_ok "Updated template storage → $new_tpl" - fi - fi -} +# # template storage selection +# local new_tpl +# new_tpl=$(pvesm status -content vztmpl | awk 'NR>1 {print $1" "$2" "$6}') +# new_tpl=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ +# --title "Select Template Storage" \ +# --menu "Choose template storage:" 20 60 10 $new_tpl 3>&1 1>&2 2>&3) || true +# if [ -n "$new_tpl" ]; then +# sed -i '/^var_template_storage=/d' "$vars_file" +# echo "var_template_storage=$new_tpl" >>"$vars_file" +# msg_ok "Updated template storage → $new_tpl" +# fi +# fi +# } select_container_storage() { local vars_file="$1" From 7e5cbc40726036756787dd883f5f012eff859790 Mon Sep 17 00:00:00 2001 From: cobaltgit Date: Wed, 17 Sep 2025 10:17:55 +0100 Subject: [PATCH 0974/1733] alpine-caddy: add default Caddyfile and webpage --- install/alpine-caddy-install.sh | 41 +++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/install/alpine-caddy-install.sh b/install/alpine-caddy-install.sh index 6873dced1..f1b59d425 100644 --- a/install/alpine-caddy-install.sh +++ b/install/alpine-caddy-install.sh @@ -30,6 +30,47 @@ if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then msg_ok "Setup xCaddy" fi +cat</etc/caddy/Caddyfile +# The Caddyfile is an easy way to configure your Caddy web server. +# +# Unless the file starts with a global options block, the first +# uncommented line is always the address of your site. +# +# To use your own domain name (with automatic HTTPS), first make +# sure your domain's A/AAAA DNS records are properly pointed to +# this machine's public IP, then replace ":80" below with your +# domain name. + +:80 { + # Set this path to your site's directory. + root * /var/www/html + + # Enable the static file server. + file_server + + # Another common task is to set up a reverse proxy: + # reverse_proxy localhost:8080 + + # Or serve a PHP site through php-fpm: + # php_fastcgi localhost:9000 +} + +# Refer to the Caddy docs for more information: +# https://caddyserver.com/docs/caddyfile +EOF +mkdir -p /var/www/html +cat</var/www/html/index.html + + + + Caddy works! + + +

Hello Caddy!

+

For more information, refer to the Caddy documentation

+ + +EOF msg_info "Enabling Caddy Service" $STD rc-update add caddy default msg_ok "Enabled Caddy Service" From 3fd2f00a913eff6669b1f18723febeaf5ad7badf Mon Sep 17 00:00:00 2001 From: cobaltgit Date: Wed, 17 Sep 2025 10:18:33 +0100 Subject: [PATCH 0975/1733] alpine-caddy: move config creation to install step --- install/alpine-caddy-install.sh | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/install/alpine-caddy-install.sh b/install/alpine-caddy-install.sh index f1b59d425..d34d9a3cc 100644 --- a/install/alpine-caddy-install.sh +++ b/install/alpine-caddy-install.sh @@ -15,21 +15,6 @@ update_os msg_info "Installing Caddy" $STD apk add --no-cache caddy caddy-openrc -msg_ok "Installed Caddy" - -read -r -p "${TAB3}Would you like to install xCaddy Addon? " prompt -if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then - GO_VERSION="$(curl -fsSL https://go.dev/VERSION?m=text | head -1 | cut -c3-)" setup_go - msg_info "Setup xCaddy" - cd /opt - RELEASE=$(curl -fsSL https://api.github.com/repos/caddyserver/xcaddy/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - curl -fsSL "https://github.com/caddyserver/xcaddy/releases/download/${RELEASE}/xcaddy_${RELEASE:1}_linux_amd64.tar.gz" -o "xcaddy_${RELEASE:1}_linux_amd64.tar.gz" - $STD tar xzf xcaddy_"${RELEASE:1}"_linux_amd64.tar.gz -C /usr/local/bin xcaddy - rm -rf /opt/xcaddy* - $STD xcaddy build - msg_ok "Setup xCaddy" -fi - cat</etc/caddy/Caddyfile # The Caddyfile is an easy way to configure your Caddy web server. # @@ -71,6 +56,21 @@ cat</var/www/html/index.html EOF +msg_ok "Installed Caddy" + +read -r -p "${TAB3}Would you like to install xCaddy Addon? " prompt +if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then + GO_VERSION="$(curl -fsSL https://go.dev/VERSION?m=text | head -1 | cut -c3-)" setup_go + msg_info "Setup xCaddy" + cd /opt + RELEASE=$(curl -fsSL https://api.github.com/repos/caddyserver/xcaddy/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + curl -fsSL "https://github.com/caddyserver/xcaddy/releases/download/${RELEASE}/xcaddy_${RELEASE:1}_linux_amd64.tar.gz" -o "xcaddy_${RELEASE:1}_linux_amd64.tar.gz" + $STD tar xzf xcaddy_"${RELEASE:1}"_linux_amd64.tar.gz -C /usr/local/bin xcaddy + rm -rf /opt/xcaddy* + $STD xcaddy build + msg_ok "Setup xCaddy" +fi + msg_info "Enabling Caddy Service" $STD rc-update add caddy default msg_ok "Enabled Caddy Service" From a3ee4b77fd05b866498b14c954a69a60cd51a7aa Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 17 Sep 2025 11:22:08 +0200 Subject: [PATCH 0976/1733] Update alpine-install.func --- misc/alpine-install.func | 2 ++ 1 file changed, 2 insertions(+) diff --git a/misc/alpine-install.func b/misc/alpine-install.func index 6feacd324..0ba607cae 100644 --- a/misc/alpine-install.func +++ b/misc/alpine-install.func @@ -8,7 +8,9 @@ if ! command -v curl >/dev/null 2>&1; then apk update && apk add curl >/dev/null 2>&1 fi source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) load_functions +init_error_traps # This function enables IPv6 if it's not disabled and sets verbose mode verb_ip6() { From de057c727cd9b6f8c343a608f574d3ae232e3e56 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 17 Sep 2025 11:56:41 +0200 Subject: [PATCH 0977/1733] Update build.func --- misc/build.func | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/misc/build.func b/misc/build.func index 928a3bc73..15c8ce2ba 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1334,8 +1334,8 @@ _build_current_app_vars_tmp() { _hostname="${HN:-$NSAPP}" # Storage - _tpl_storage="${TEMPLATE_STORAGE:-}" - _ct_storage="${CONTAINER_STORAGE:-}" + _tpl_storage="${TEMPLATE_STORAGE:-${var_template_storage:-}}" + _ct_storage="${CONTAINER_STORAGE:-${var_container_storage:-}}" { echo "# App-specific defaults for ${APP} (${NSAPP})" @@ -1615,11 +1615,22 @@ install_script() { 7) header_info echo -e "${DEFAULT}${BOLD}${BL}Manage Storage Settings on node $PVEHOST_NAME${CL}" - select_container_storage "/usr/local/community-scripts/default.vars" - select_template_storage "/usr/local/community-scripts/default.vars" - _echo_storage_summary "/usr/local/community-scripts/default.vars" + + local vars_file="/usr/local/community-scripts/default.vars" + if [ -f "$(get_app_defaults_path)" ]; then + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "STORAGE SETTINGS" \ + --yesno "Do you want to update App Defaults for ${APP} instead of global defaults?\n\nYes = App Defaults (${APP})\nNo = Global Defaults (default.vars)" 12 72; then + vars_file="$(get_app_defaults_path)" + fi + fi + + select_container_storage "$vars_file" + select_template_storage "$vars_file" + _echo_storage_summary "$vars_file" exit 0 ;; + 8) echo -e "\n${CROSS}${RD}Script terminated.${CL}\n" exit 0 From 81329f74b128e8178ee204b7271213aa19369323 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 17 Sep 2025 12:07:36 +0200 Subject: [PATCH 0978/1733] Update build.func --- misc/build.func | 2 -- 1 file changed, 2 deletions(-) diff --git a/misc/build.func b/misc/build.func index 15c8ce2ba..4a5ed9c50 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1604,8 +1604,6 @@ install_script() { base_settings _load_vars_file "$(get_app_defaults_path)" echo_default - select_container_storage "$(get_app_defaults_path)" - select_template_storage "$(get_app_defaults_path)" fi ;; 6) From ca28e419ef76b869ec13c1d975fabe3abb3c8823 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 17 Sep 2025 13:02:37 +0200 Subject: [PATCH 0979/1733] Update build.func --- misc/build.func | 576 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 572 insertions(+), 4 deletions(-) diff --git a/misc/build.func b/misc/build.func index 4a5ed9c50..9b319cee9 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2072,10 +2072,7 @@ build_container() { " export TEMPLATE_STORAGE="${var_template_storage:-}" export CONTAINER_STORAGE="${var_container_storage:-}" - bash -c "$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/create_lxc.sh)" || exit - if [ $? -ne 0 ]; then - exit 200 - fi + create_lxc_container || exit $? LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" @@ -2372,6 +2369,577 @@ destroy_lxc() { fi } +create_lxc_container() { + # ------------------------------------------------------------------------------ + # Optional verbose mode (debug tracing) + # ------------------------------------------------------------------------------ + if [[ "${CREATE_LXC_VERBOSE:-no}" == "yes" ]]; then set -x; fi + + # ------------------------------------------------------------------------------ + # Helpers (dynamic versioning / template parsing) + # ------------------------------------------------------------------------------ + pkg_ver() { dpkg-query -W -f='${Version}\n' "$1" 2>/dev/null || echo ""; } + pkg_cand() { apt-cache policy "$1" 2>/dev/null | awk '/Candidate:/ {print $2}'; } + + ver_ge() { dpkg --compare-versions "$1" ge "$2"; } + ver_gt() { dpkg --compare-versions "$1" gt "$2"; } + ver_lt() { dpkg --compare-versions "$1" lt "$2"; } + + # Extract Debian OS minor from template name: debian-13-standard_13.1-1_amd64.tar.zst => "13.1" + parse_template_osver() { sed -n 's/.*_\([0-9][0-9]*\(\.[0-9]\+\)\?\)-.*/\1/p' <<<"$1"; } + + # Offer upgrade for pve-container/lxc-pve if candidate > installed; optional auto-retry pct create + # Returns: + # 0 = no upgrade needed + # 1 = upgraded (and if do_retry=yes and retry succeeded, creation done) + # 2 = user declined + # 3 = upgrade attempted but failed OR retry failed + offer_lxc_stack_upgrade_and_maybe_retry() { + local do_retry="${1:-no}" # yes|no + local _pvec_i _pvec_c _lxcp_i _lxcp_c need=0 + + _pvec_i="$(pkg_ver pve-container)" + _lxcp_i="$(pkg_ver lxc-pve)" + _pvec_c="$(pkg_cand pve-container)" + _lxcp_c="$(pkg_cand lxc-pve)" + + if [[ -n "$_pvec_c" && "$_pvec_c" != "none" ]]; then + ver_gt "$_pvec_c" "${_pvec_i:-0}" && need=1 + fi + if [[ -n "$_lxcp_c" && "$_lxcp_c" != "none" ]]; then + ver_gt "$_lxcp_c" "${_lxcp_i:-0}" && need=1 + fi + if [[ $need -eq 0 ]]; then + msg_debug "No newer candidate for pve-container/lxc-pve (installed=$_pvec_i/$_lxcp_i, cand=$_pvec_c/$_lxcp_c)" + return 0 + fi + + echo + echo "An update for the Proxmox LXC stack is available:" + echo " pve-container: installed=${_pvec_i:-n/a} candidate=${_pvec_c:-n/a}" + echo " lxc-pve : installed=${_lxcp_i:-n/a} candidate=${_lxcp_c:-n/a}" + echo + read -rp "Do you want to upgrade now? [y/N] " _ans + case "${_ans,,}" in + y | yes) + msg_info "Upgrading Proxmox LXC stack (pve-container, lxc-pve)" + if apt-get update -qq >/dev/null && apt-get install -y --only-upgrade pve-container lxc-pve >/dev/null; then + msg_ok "LXC stack upgraded." + if [[ "$do_retry" == "yes" ]]; then + msg_info "Retrying container creation after upgrade" + if pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then + msg_ok "Container created successfully after upgrade." + return 1 + else + msg_error "pct create still failed after upgrade. See $LOGFILE" + return 3 + fi + fi + return 1 + else + msg_error "Upgrade failed. Please check APT output." + return 3 + fi + ;; + *) return 2 ;; + esac + } + + # ------------------------------------------------------------------------------ + # Storage discovery / selection helpers + # ------------------------------------------------------------------------------ + resolve_storage_preselect() { + local class="$1" preselect="$2" required_content="" + case "$class" in + template) required_content="vztmpl" ;; + container) required_content="rootdir" ;; + *) return 1 ;; + esac + [[ -z "$preselect" ]] && return 1 + if ! pvesm status -content "$required_content" | awk 'NR>1{print $1}' | grep -qx -- "$preselect"; then + msg_warn "Preselected storage '${preselect}' does not support content '${required_content}' (or not found)" + return 1 + fi + + local line total used free + line="$(pvesm status | awk -v s="$preselect" 'NR>1 && $1==s {print $0}')" + if [[ -z "$line" ]]; then + STORAGE_INFO="n/a" + else + total="$(awk '{print $4}' <<<"$line")" + used="$(awk '{print $5}' <<<"$line")" + free="$(awk '{print $6}' <<<"$line")" + local total_h used_h free_h + if command -v numfmt >/dev/null 2>&1; then + total_h="$(numfmt --to=iec --suffix=B --format %.1f "$total" 2>/dev/null || echo "$total")" + used_h="$(numfmt --to=iec --suffix=B --format %.1f "$used" 2>/dev/null || echo "$used")" + free_h="$(numfmt --to=iec --suffix=B --format %.1f "$free" 2>/dev/null || echo "$free")" + STORAGE_INFO="Free: ${free_h} Used: ${used_h}" + else + STORAGE_INFO="Free: ${free} Used: ${used}" + fi + fi + STORAGE_RESULT="$preselect" + return 0 + } + + check_storage_support() { + local CONTENT="$1" VALID=0 + while IFS= read -r line; do + local STORAGE_NAME + STORAGE_NAME=$(awk '{print $1}' <<<"$line") + [[ -n "$STORAGE_NAME" ]] && VALID=1 + done < <(pvesm status -content "$CONTENT" 2>/dev/null | awk 'NR>1') + [[ $VALID -eq 1 ]] + } + + select_storage() { + local CLASS=$1 CONTENT CONTENT_LABEL + case $CLASS in + container) + CONTENT='rootdir' + CONTENT_LABEL='Container' + ;; + template) + CONTENT='vztmpl' + CONTENT_LABEL='Container template' + ;; + iso) + CONTENT='iso' + CONTENT_LABEL='ISO image' + ;; + images) + CONTENT='images' + CONTENT_LABEL='VM Disk image' + ;; + backup) + CONTENT='backup' + CONTENT_LABEL='Backup' + ;; + snippets) + CONTENT='snippets' + CONTENT_LABEL='Snippets' + ;; + *) + msg_error "Invalid storage class '$CLASS'" + return 1 + ;; + esac + + if [[ "$CONTENT" == "rootdir" && -n "${STORAGE:-}" ]]; then + if pvesm status -content "$CONTENT" | awk 'NR>1 {print $1}' | grep -qx "$STORAGE"; then + STORAGE_RESULT="$STORAGE" + msg_info "Using preset storage: $STORAGE_RESULT for $CONTENT_LABEL" + return 0 + else + msg_error "Preset storage '$STORAGE' is not valid for content type '$CONTENT'." + return 2 + fi + fi + + declare -A STORAGE_MAP + local -a MENU=() + local COL_WIDTH=0 + + while read -r TAG TYPE _ TOTAL USED FREE _; do + [[ -n "$TAG" && -n "$TYPE" ]] || continue + local DISPLAY="${TAG} (${TYPE})" + local USED_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$USED") + local FREE_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$FREE") + local INFO="Free: ${FREE_FMT}B Used: ${USED_FMT}B" + STORAGE_MAP["$DISPLAY"]="$TAG" + MENU+=("$DISPLAY" "$INFO" "OFF") + ((${#DISPLAY} > COL_WIDTH)) && COL_WIDTH=${#DISPLAY} + done < <(pvesm status -content "$CONTENT" | awk 'NR>1') + + if [[ ${#MENU[@]} -eq 0 ]]; then + msg_error "No storage found for content type '$CONTENT'." + return 2 + fi + + if [[ $((${#MENU[@]} / 3)) -eq 1 ]]; then + STORAGE_RESULT="${STORAGE_MAP[${MENU[0]}]}" + STORAGE_INFO="${MENU[1]}" + return 0 + fi + + local WIDTH=$((COL_WIDTH + 42)) + while true; do + local DISPLAY_SELECTED + DISPLAY_SELECTED=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "Storage Pools" \ + --radiolist "Which storage pool for ${CONTENT_LABEL,,}?\n(Spacebar to select)" \ + 16 "$WIDTH" 6 "${MENU[@]}" 3>&1 1>&2 2>&3) || exit_script + + DISPLAY_SELECTED=$(sed 's/[[:space:]]*$//' <<<"$DISPLAY_SELECTED") + if [[ -z "$DISPLAY_SELECTED" || -z "${STORAGE_MAP[$DISPLAY_SELECTED]+_}" ]]; then + whiptail --msgbox "No valid storage selected. Please try again." 8 58 + continue + fi + STORAGE_RESULT="${STORAGE_MAP[$DISPLAY_SELECTED]}" + for ((i = 0; i < ${#MENU[@]}; i += 3)); do + if [[ "${MENU[$i]}" == "$DISPLAY_SELECTED" ]]; then + STORAGE_INFO="${MENU[$i + 1]}" + break + fi + done + return 0 + done + } + + # ------------------------------------------------------------------------------ + # Required input variables + # ------------------------------------------------------------------------------ + [[ "${CTID:-}" ]] || { + msg_error "You need to set 'CTID' variable." + exit 203 + } + [[ "${PCT_OSTYPE:-}" ]] || { + msg_error "You need to set 'PCT_OSTYPE' variable." + exit 204 + } + + msg_debug "CTID=$CTID" + msg_debug "PCT_OSTYPE=$PCT_OSTYPE" + msg_debug "PCT_OSVERSION=${PCT_OSVERSION:-default}" + + # ID checks + [[ "$CTID" -ge 100 ]] || { + msg_error "ID cannot be less than 100." + exit 205 + } + if qm status "$CTID" &>/dev/null || pct status "$CTID" &>/dev/null; then + echo -e "ID '$CTID' is already in use." + unset CTID + msg_error "Cannot use ID that is already in use." + exit 206 + fi + + # Storage capability check + check_storage_support "rootdir" || { + msg_error "No valid storage found for 'rootdir' [Container]" + exit 1 + } + check_storage_support "vztmpl" || { + msg_error "No valid storage found for 'vztmpl' [Template]" + exit 1 + } + + # Template storage selection + if resolve_storage_preselect template "${TEMPLATE_STORAGE:-}"; then + TEMPLATE_STORAGE="$STORAGE_RESULT" + TEMPLATE_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${TEMPLATE_STORAGE}${CL} (${TEMPLATE_STORAGE_INFO}) [Template]" + else + while true; do + if select_storage template; then + TEMPLATE_STORAGE="$STORAGE_RESULT" + TEMPLATE_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${TEMPLATE_STORAGE}${CL} (${TEMPLATE_STORAGE_INFO}) [Template]" + break + fi + done + fi + + # Container storage selection + if resolve_storage_preselect container "${CONTAINER_STORAGE:-}"; then + CONTAINER_STORAGE="$STORAGE_RESULT" + CONTAINER_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${CONTAINER_STORAGE}${CL} (${CONTAINER_STORAGE_INFO}) [Container]" + else + while true; do + if select_storage container; then + CONTAINER_STORAGE="$STORAGE_RESULT" + CONTAINER_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${CONTAINER_STORAGE}${CL} (${CONTAINER_STORAGE_INFO}) [Container]" + break + fi + done + fi + + # Validate content types + msg_info "Validating content types of storage '$CONTAINER_STORAGE'" + STORAGE_CONTENT=$(grep -A4 -E "^(zfspool|dir|lvmthin|lvm): $CONTAINER_STORAGE" /etc/pve/storage.cfg | grep content | awk '{$1=""; print $0}' | xargs) + msg_debug "Storage '$CONTAINER_STORAGE' has content types: $STORAGE_CONTENT" + grep -qw "rootdir" <<<"$STORAGE_CONTENT" || { + msg_error "Storage '$CONTAINER_STORAGE' does not support 'rootdir'. Cannot create LXC." + exit 217 + } + $STD msg_ok "Storage '$CONTAINER_STORAGE' supports 'rootdir'" + + msg_info "Validating content types of template storage '$TEMPLATE_STORAGE'" + TEMPLATE_CONTENT=$(grep -A4 -E "^[^:]+: $TEMPLATE_STORAGE" /etc/pve/storage.cfg | grep content | awk '{$1=""; print $0}' | xargs) + msg_debug "Template storage '$TEMPLATE_STORAGE' has content types: $TEMPLATE_CONTENT" + if ! grep -qw "vztmpl" <<<"$TEMPLATE_CONTENT"; then + msg_warn "Template storage '$TEMPLATE_STORAGE' does not declare 'vztmpl'. This may cause pct create to fail." + else + $STD msg_ok "Template storage '$TEMPLATE_STORAGE' supports 'vztmpl'" + fi + + # Free space check + STORAGE_FREE=$(pvesm status | awk -v s="$CONTAINER_STORAGE" '$1 == s { print $6 }') + REQUIRED_KB=$((${PCT_DISK_SIZE:-8} * 1024 * 1024)) + [[ "$STORAGE_FREE" -ge "$REQUIRED_KB" ]] || { + msg_error "Not enough space on '$CONTAINER_STORAGE'. Needed: ${PCT_DISK_SIZE:-8}G." + exit 214 + } + + # Cluster quorum (if cluster) + if [[ -f /etc/pve/corosync.conf ]]; then + msg_info "Checking cluster quorum" + if ! pvecm status | awk -F':' '/^Quorate/ { exit ($2 ~ /Yes/) ? 0 : 1 }'; then + msg_error "Cluster is not quorate. Start all nodes or configure quorum device (QDevice)." + exit 210 + fi + msg_ok "Cluster is quorate" + fi + + # ------------------------------------------------------------------------------ + # Template discovery & validation + # ------------------------------------------------------------------------------ + TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" + case "$PCT_OSTYPE" in + debian | ubuntu) TEMPLATE_PATTERN="-standard_" ;; + alpine | fedora | rocky | centos) TEMPLATE_PATTERN="-default_" ;; + *) TEMPLATE_PATTERN="" ;; + esac + + msg_info "Searching for template '$TEMPLATE_SEARCH'" + + mapfile -t LOCAL_TEMPLATES < <( + pveam list "$TEMPLATE_STORAGE" 2>/dev/null | + awk -v s="$TEMPLATE_SEARCH" -v p="$TEMPLATE_PATTERN" '$1 ~ s && $1 ~ p {print $1}' | + sed 's|.*/||' | sort -t - -k 2 -V + ) + + pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)." + mapfile -t ONLINE_TEMPLATES < <( + pveam available -section system 2>/dev/null | + sed -n "s/.*\($TEMPLATE_SEARCH.*$TEMPLATE_PATTERN.*\)/\1/p" | + sort -t - -k 2 -V + ) + ONLINE_TEMPLATE="" + [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" + + if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then + TEMPLATE="${LOCAL_TEMPLATES[-1]}" + TEMPLATE_SOURCE="local" + else + TEMPLATE="$ONLINE_TEMPLATE" + TEMPLATE_SOURCE="online" + fi + + TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" + if [[ -z "$TEMPLATE_PATH" ]]; then + TEMPLATE_BASE=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) + [[ -n "$TEMPLATE_BASE" ]] && TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE" + fi + [[ -n "$TEMPLATE_PATH" ]] || { + msg_error "Unable to resolve template path for $TEMPLATE_STORAGE. Check storage type and permissions." + exit 220 + } + + msg_ok "Template ${BL}$TEMPLATE${CL} [$TEMPLATE_SOURCE]" + msg_debug "Resolved TEMPLATE_PATH=$TEMPLATE_PATH" + + NEED_DOWNLOAD=0 + if [[ ! -f "$TEMPLATE_PATH" ]]; then + msg_info "Template not present locally – will download." + NEED_DOWNLOAD=1 + elif [[ ! -r "$TEMPLATE_PATH" ]]; then + msg_error "Template file exists but is not readable – check permissions." + exit 221 + elif [[ "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then + if [[ -n "$ONLINE_TEMPLATE" ]]; then + msg_warn "Template file too small (<1MB) – re-downloading." + NEED_DOWNLOAD=1 + else + msg_warn "Template looks too small, but no online version exists. Keeping local file." + fi + elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then + if [[ -n "$ONLINE_TEMPLATE" ]]; then + msg_warn "Template appears corrupted – re-downloading." + NEED_DOWNLOAD=1 + else + msg_warn "Template appears corrupted, but no online version exists. Keeping local file." + fi + else + $STD msg_ok "Template $TEMPLATE is present and valid." + fi + + if [[ "$TEMPLATE_SOURCE" == "local" && -n "$ONLINE_TEMPLATE" && "$TEMPLATE" != "$ONLINE_TEMPLATE" ]]; then + msg_warn "Local template is outdated: $TEMPLATE (latest available: $ONLINE_TEMPLATE)" + if whiptail --yesno "A newer template is available:\n$ONLINE_TEMPLATE\n\nDo you want to download and use it instead?" 12 70; then + TEMPLATE="$ONLINE_TEMPLATE" + NEED_DOWNLOAD=1 + else + msg_info "Continuing with local template $TEMPLATE" + fi + fi + + if [[ "$NEED_DOWNLOAD" -eq 1 ]]; then + [[ -f "$TEMPLATE_PATH" ]] && rm -f "$TEMPLATE_PATH" + for attempt in {1..3}; do + msg_info "Attempt $attempt: Downloading template $TEMPLATE to $TEMPLATE_STORAGE" + if pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null 2>&1; then + msg_ok "Template download successful." + break + fi + if [[ $attempt -eq 3 ]]; then + msg_error "Failed after 3 attempts. Please check network access, permissions, or manually run:\n pveam download $TEMPLATE_STORAGE $TEMPLATE" + exit 222 + fi + sleep $((attempt * 5)) + done + fi + + if ! pveam list "$TEMPLATE_STORAGE" 2>/dev/null | grep -q "$TEMPLATE"; then + msg_error "Template $TEMPLATE not available in storage $TEMPLATE_STORAGE after download." + exit 223 + fi + + # ------------------------------------------------------------------------------ + # Dynamic preflight for Debian 13.x: offer upgrade if available (no hard mins) + # ------------------------------------------------------------------------------ + if [[ "$PCT_OSTYPE" == "debian" ]]; then + OSVER="$(parse_template_osver "$TEMPLATE")" + if [[ -n "$OSVER" ]]; then + # Proactive, aber ohne Abbruch – nur Angebot + offer_lxc_stack_upgrade_and_maybe_retry "no" || true + fi + fi + + # ------------------------------------------------------------------------------ + # Create LXC Container + # ------------------------------------------------------------------------------ + msg_info "Creating LXC container" + + # Ensure subuid/subgid entries exist + grep -q "root:100000:65536" /etc/subuid || echo "root:100000:65536" >>/etc/subuid + grep -q "root:100000:65536" /etc/subgid || echo "root:100000:65536" >>/etc/subgid + + # Assemble pct options + PCT_OPTIONS=(${PCT_OPTIONS[@]:-${DEFAULT_PCT_OPTIONS[@]}}) + [[ " ${PCT_OPTIONS[*]} " =~ " -rootfs " ]] || PCT_OPTIONS+=(-rootfs "$CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}") + + # Lock by template file (avoid concurrent downloads/creates) + lockfile="/tmp/template.${TEMPLATE}.lock" + exec 9>"$lockfile" || { + msg_error "Failed to create lock file '$lockfile'." + exit 200 + } + flock -w 60 9 || { + msg_error "Timeout while waiting for template lock." + exit 211 + } + + LOGFILE="/tmp/pct_create_${CTID}.log" + msg_debug "pct create command: pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" + msg_debug "Logfile: $LOGFILE" + + # First attempt + if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >"$LOGFILE" 2>&1; then + msg_error "Container creation failed on ${TEMPLATE_STORAGE}. Checking template..." + + # Validate template file + if [[ ! -s "$TEMPLATE_PATH" || "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then + msg_warn "Template file too small or missing – re-downloading." + rm -f "$TEMPLATE_PATH" + pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" + elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then + if [[ -n "$ONLINE_TEMPLATE" ]]; then + msg_warn "Template appears corrupted – re-downloading." + rm -f "$TEMPLATE_PATH" + pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" + else + msg_warn "Template appears corrupted, but no online version exists. Skipping re-download." + fi + fi + + # Retry after repair + if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then + # Fallback to local storage + if [[ "$TEMPLATE_STORAGE" != "local" ]]; then + msg_warn "Retrying container creation with fallback to local storage..." + LOCAL_TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" + if [[ ! -f "$LOCAL_TEMPLATE_PATH" ]]; then + msg_info "Downloading template to local..." + pveam download local "$TEMPLATE" >/dev/null 2>&1 + fi + if pct create "$CTID" "local:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then + msg_ok "Container successfully created using local fallback." + else + # --- Dynamic stack upgrade + auto-retry on the well-known error pattern --- + if grep -qiE 'unsupported .* version' "$LOGFILE"; then + echo + echo "pct reported 'unsupported ... version' – your LXC stack might be too old for this template." + echo "We can try to upgrade 'pve-container' and 'lxc-pve' now and retry automatically." + if offer_lxc_stack_upgrade_and_maybe_retry "yes"; then + : # success after retry + else + rc=$? + case $rc in + 2) echo "Upgrade was declined. Please update and re-run: + apt update && apt install --only-upgrade pve-container lxc-pve" ;; + 3) echo "Upgrade and/or retry failed. Please inspect: $LOGFILE" ;; + esac + exit 231 + fi + else + msg_error "Container creation failed even with local fallback. See $LOGFILE" + if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then + set -x + bash -x -c "pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" 2>&1 | tee -a "$LOGFILE" + set +x + fi + exit 209 + fi + fi + else + msg_error "Container creation failed on local storage. See $LOGFILE" + # --- Dynamic stack upgrade + auto-retry on the well-known error pattern --- + if grep -qiE 'unsupported .* version' "$LOGFILE"; then + echo + echo "pct reported 'unsupported ... version' – your LXC stack might be too old for this template." + echo "We can try to upgrade 'pve-container' and 'lxc-pve' now and retry automatically." + if offer_lxc_stack_upgrade_and_maybe_retry "yes"; then + : # success after retry + else + rc=$? + case $rc in + 2) echo "Upgrade was declined. Please update and re-run: + apt update && apt install --only-upgrade pve-container lxc-pve" ;; + 3) echo "Upgrade and/or retry failed. Please inspect: $LOGFILE" ;; + esac + exit 231 + fi + else + if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then + set -x + bash -x -c "pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" 2>&1 | tee -a "$LOGFILE" + set +x + fi + exit 209 + fi + fi + fi + fi + + # Verify container exists + pct list | awk '{print $1}' | grep -qx "$CTID" || { + msg_error "Container ID $CTID not listed in 'pct list'. See $LOGFILE" + exit 215 + } + + # Verify config rootfs + grep -q '^rootfs:' "/etc/pve/lxc/$CTID.conf" || { + msg_error "RootFS entry missing in container config. See $LOGFILE" + exit 216 + } + + msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created." +} + # ------------------------------------------------------------------------------ # description() # From 8ce34b4ee0b33270776a549b819a73483e229f68 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 17 Sep 2025 13:59:14 +0200 Subject: [PATCH 0980/1733] Refactor storage selection and enforce user prompt Unifies and refactors storage selection logic to always prompt the user for container and template storage choices unless only one option exists, as per requirement #4. Removes legacy selection functions, updates all relevant install and menu flows to use the new logic, and ensures storage settings are consistently written to vars files and environment variables. Also improves code clarity and maintains storage summary output. --- misc/build.func | 215 ++++++++++++++++++++---------------------------- 1 file changed, 91 insertions(+), 124 deletions(-) diff --git a/misc/build.func b/misc/build.func index 9b319cee9..df1df170c 100644 --- a/misc/build.func +++ b/misc/build.func @@ -977,7 +977,7 @@ default_var_settings() { if printenv "$_k" >/dev/null 2>&1; then _HARD_ENV["$_k"]=1; fi done - # Find default.vars (first valid path wins) + # Find default.vars location local _find_default_vars _find_default_vars() { local f @@ -993,13 +993,20 @@ default_var_settings() { return 1 } - # Ensure default.vars exists, create with sane defaults if missing + # Create once, with storages already selected, no var_ctid/var_hostname lines local _ensure_default_vars _ensure_default_vars() { _find_default_vars >/dev/null 2>&1 && return 0 + local canonical="/usr/local/community-scripts/default.vars" msg_info "No default.vars found. Creating ${canonical}" mkdir -p /usr/local/community-scripts + + # Pick storages before writing the file (always ask unless only one) + # Create a minimal temp file to write into + : >"$canonical" + + # Base content (no var_ctid / var_hostname here) cat >"$canonical" <<'EOF' # Community-Scripts defaults (var_* only). Lines starting with # are comments. # Precedence: ENV var_* > default.vars > built-ins. @@ -1008,11 +1015,6 @@ default_var_settings() { # Container type var_unprivileged=1 -# Storage -# Example: "local", "docker", ... -# var_template_storage=local -# var_container_storage=local - # Resources var_cpu=1 var_disk=4 @@ -1045,11 +1047,12 @@ var_verbose=no # Security (root PW) – empty => autologin # var_pw= - -# Optional fixed CTID/hostname – empty => auto -# var_ctid= -# var_hostname= EOF + + # Now choose storages (always prompt unless just one exists) + choose_and_set_storage_for_file "$canonical" template + choose_and_set_storage_for_file "$canonical" container + chmod 0644 "$canonical" msg_ok "Created ${canonical}" } @@ -1091,34 +1094,23 @@ EOF var_val="${BASH_REMATCH[1]}" fi - # Unsafe check without regex (formatter-friendly) - local _unsafe="" - case "$var_val" in - *'$('*) _unsafe=1 ;; - *'`'*) _unsafe=1 ;; - *';'*) _unsafe=1 ;; - *'&'*) _unsafe=1 ;; - *'<('*) _unsafe=1 ;; - esac - if [[ -n "$_unsafe" ]]; then - msg_warn "Ignoring ${var_key} from ${file}: unsafe characters" - continue - fi - - # Hard env wins - if [[ -n "${_HARD_ENV[$var_key]:-}" ]]; then - continue - fi - + # Unsafe characters + case $var_val in + \"*\") + var_val=${var_val#\"} + var_val=${var_val%\"} + ;; + \'*\') + var_val=${var_val#\'} + var_val=${var_val%\'} + ;; + esac # Hard env wins + [[ -n "${_HARD_ENV[$var_key]:-}" ]] && continue # Set only if not already exported - if [[ -z "${!var_key+x}" ]]; then - export "${var_key}=${var_val}" - fi - + [[ -z "${!var_key+x}" ]] && export "${var_key}=${var_val}" else msg_warn "Malformed line in ${file}: ${line}" fi - done <"$file" msg_ok "Loaded ${file}" } @@ -1136,11 +1128,7 @@ EOF # 3) Map var_verbose → VERBOSE if [[ -n "${var_verbose:-}" ]]; then - case "${var_verbose,,}" in - 1 | yes | true | on) VERBOSE="yes" ;; - 0 | no | false | off) VERBOSE="no" ;; - *) VERBOSE="${var_verbose}" ;; - esac + case "${var_verbose,,}" in 1 | yes | true | on) VERBOSE="yes" ;; 0 | no | false | off) VERBOSE="no" ;; *) VERBOSE="${var_verbose}" ;; esac else VERBOSE="no" fi @@ -1457,6 +1445,15 @@ maybe_offer_save_app_defaults() { rm -f "$new_tmp" "$diff_tmp" } +ensure_storage_selection_for_vars_file() { + # $1 = vars_file + local vf="$1" + # Always prompt (unless only one choice for that content), per your requirement #4 + choose_and_set_storage_for_file "$vf" template + choose_and_set_storage_for_file "$vf" container + echo_storage_summary_from_file "$vf" +} + diagnostics_menu() { if [ "${DIAGNOSTICS:-no}" = "yes" ]; then if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ @@ -1571,6 +1568,8 @@ install_script() { METHOD="default" base_settings "$VERBOSE" echo_default + # Always ask storages for MyDefaults file (create if missing) + ensure_storage_selection_for_vars_file "/usr/local/community-scripts/default.vars" ;; 2) header_info @@ -1579,6 +1578,7 @@ install_script() { METHOD="default" base_settings "$VERBOSE" echo_default + ensure_storage_selection_for_vars_file "/usr/local/community-scripts/default.vars" ;; 3) header_info @@ -1586,6 +1586,8 @@ install_script() { METHOD="advanced" base_settings advanced_settings + # Always ask storages (stored to env var_* so app-defaults will include them) + ensure_storage_selection_for_vars_file "/usr/local/community-scripts/default.vars" maybe_offer_save_app_defaults ;; 4) @@ -1593,8 +1595,8 @@ install_script() { msg_error "Failed to apply default.vars" exit 1 } - select_container_storage "/usr/local/community-scripts/default.vars" - select_template_storage "/usr/local/community-scripts/default.vars" + # Always let user pick storages again (unless only one exists) + ensure_storage_selection_for_vars_file "/usr/local/community-scripts/default.vars" ;; 5) if [ -f "$(get_app_defaults_path)" ]; then @@ -1604,6 +1606,8 @@ install_script() { base_settings _load_vars_file "$(get_app_defaults_path)" echo_default + # Always let user confirm/change storages for the app defaults + ensure_storage_selection_for_vars_file "$(get_app_defaults_path)" fi ;; 6) @@ -1613,22 +1617,9 @@ install_script() { 7) header_info echo -e "${DEFAULT}${BOLD}${BL}Manage Storage Settings on node $PVEHOST_NAME${CL}" - - local vars_file="/usr/local/community-scripts/default.vars" - if [ -f "$(get_app_defaults_path)" ]; then - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "STORAGE SETTINGS" \ - --yesno "Do you want to update App Defaults for ${APP} instead of global defaults?\n\nYes = App Defaults (${APP})\nNo = Global Defaults (default.vars)" 12 72; then - vars_file="$(get_app_defaults_path)" - fi - fi - - select_container_storage "$vars_file" - select_template_storage "$vars_file" - _echo_storage_summary "$vars_file" + ensure_storage_selection_for_vars_file "/usr/local/community-scripts/default.vars" exit 0 ;; - 8) echo -e "\n${CROSS}${RD}Script terminated.${CL}\n" exit 0 @@ -1744,72 +1735,58 @@ install_script() { # fi # } -select_container_storage() { - local vars_file="$1" - local current - current=$(awk -F= '/^var_container_storage=/ {print $2; exit}' "$vars_file") - - local storages - storages=$(pvesm status -content images | awk 'NR>1 {print $1}') - - local count - count=$(echo "$storages" | wc -l) - - local choice - if [ "$count" -eq 1 ]; then - choice="$storages" - else - # Build menu list: ID + type - local menu_items - menu_items=$(pvesm status -content images | awk -v cur="$current" 'NR>1 {printf "%s %s\n", $1, $2}') - - choice=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "Select Container Storage" \ - --menu "Choose container storage:" 20 60 10 \ - --default-item "$current" \ - $menu_items 3>&1 1>&2 2>&3) || return 1 - fi - - if [ -n "$choice" ]; then - sed -i '/^var_container_storage=/d' "$vars_file" - echo "var_container_storage=$choice" >>"$vars_file" - msg_ok "Updated container storage → $choice" - fi +# ===== Unified storage selection & writing to vars files ===== +_write_storage_to_vars() { + # $1 = vars_file, $2 = key (var_container_storage / var_template_storage), $3 = value + local vf="$1" key="$2" val="$3" + # remove uncommented and commented versions to avoid duplicates + sed -i "/^[#[:space:]]*${key}=/d" "$vf" + echo "${key}=${val}" >>"$vf" } -select_template_storage() { - local vars_file="$1" - local current - current=$(awk -F= '/^var_template_storage=/ {print $2; exit}' "$vars_file") +choose_and_set_storage_for_file() { + # $1 = vars_file, $2 = class ('container'|'template') + local vf="$1" class="$2" key="" current="" + case "$class" in + container) key="var_container_storage" ;; + template) key="var_template_storage" ;; + *) + msg_error "Unknown storage class: $class" + return 1 + ;; + esac - local storages - storages=$(pvesm status -content vztmpl | awk 'NR>1 {print $1}') + current=$(awk -F= -v k="^${key}=" '$0 ~ k {print $2; exit}' "$vf") + # If only one storage exists for the content type, auto-pick. Else always ask (your wish #4). + local content="rootdir" + [[ "$class" == "template" ]] && content="vztmpl" local count - count=$(echo "$storages" | wc -l) + count=$(pvesm status -content "$content" | awk 'NR>1{print $1}' | wc -l) - local choice - if [ "$count" -eq 1 ]; then - choice="$storages" + if [[ "$count" -eq 1 ]]; then + STORAGE_RESULT=$(pvesm status -content "$content" | awk 'NR>1{print $1; exit}') + STORAGE_INFO="" else - local menu_items - menu_items=$(pvesm status -content vztmpl | awk -v cur="$current" 'NR>1 {printf "%s %s\n", $1, $2}') - - choice=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "Select Template Storage" \ - --menu "Choose template storage:" 20 60 10 \ - --default-item "$current" \ - $menu_items 3>&1 1>&2 2>&3) || return 1 + # If the current value is preselectable, we could show it, but per your requirement we always offer selection + select_storage "$class" || return 1 fi - if [ -n "$choice" ]; then - sed -i '/^var_template_storage=/d' "$vars_file" - echo "var_template_storage=$choice" >>"$vars_file" - msg_ok "Updated template storage → $choice" + _write_storage_to_vars "$vf" "$key" "$STORAGE_RESULT" + + # Keep environment in sync for later steps (e.g. app-default save) + if [[ "$class" == "container" ]]; then + export var_container_storage="$STORAGE_RESULT" + export CONTAINER_STORAGE="$STORAGE_RESULT" + else + export var_template_storage="$STORAGE_RESULT" + export TEMPLATE_STORAGE="$STORAGE_RESULT" fi + + msg_ok "Updated ${key} → ${STORAGE_RESULT}" } -_echo_storage_summary() { +echo_storage_summary_from_file() { local vars_file="$1" local ct_store tpl_store ct_store=$(awk -F= '/^var_container_storage=/ {print $2; exit}' "$vars_file") @@ -1817,7 +1794,7 @@ _echo_storage_summary() { echo -e "\n${INFO}${BOLD}${DGN}Storage settings from ${vars_file}:${CL}" echo -e " 📦 Container Storage: ${BGN}${ct_store:-}${CL}" - echo -e " 📦 Template Storage: ${BGN}${tpl_store:-}${CL}\n" + echo -e " 📦 Template Storage: ${BGN}${tpl_store:-}${CL}\n" } # ------------------------------------------------------------------------------ @@ -2448,6 +2425,7 @@ create_lxc_container() { # ------------------------------------------------------------------------------ # Storage discovery / selection helpers # ------------------------------------------------------------------------------ + # ===== Storage discovery / selection helpers (ported from create_lxc.sh) ===== resolve_storage_preselect() { local class="$1" preselect="$2" required_content="" case "$class" in @@ -2526,17 +2504,6 @@ create_lxc_container() { ;; esac - if [[ "$CONTENT" == "rootdir" && -n "${STORAGE:-}" ]]; then - if pvesm status -content "$CONTENT" | awk 'NR>1 {print $1}' | grep -qx "$STORAGE"; then - STORAGE_RESULT="$STORAGE" - msg_info "Using preset storage: $STORAGE_RESULT for $CONTENT_LABEL" - return 0 - else - msg_error "Preset storage '$STORAGE' is not valid for content type '$CONTENT'." - return 2 - fi - fi - declare -A STORAGE_MAP local -a MENU=() local COL_WIDTH=0 @@ -2566,10 +2533,10 @@ create_lxc_container() { local WIDTH=$((COL_WIDTH + 42)) while true; do local DISPLAY_SELECTED - DISPLAY_SELECTED=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + DISPLAY_SELECTED=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ --title "Storage Pools" \ --radiolist "Which storage pool for ${CONTENT_LABEL,,}?\n(Spacebar to select)" \ - 16 "$WIDTH" 6 "${MENU[@]}" 3>&1 1>&2 2>&3) || exit_script + 16 "$WIDTH" 6 "${MENU[@]}" 3>&1 1>&2 2>&3) || { exit_script; } DISPLAY_SELECTED=$(sed 's/[[:space:]]*$//' <<<"$DISPLAY_SELECTED") if [[ -z "$DISPLAY_SELECTED" || -z "${STORAGE_MAP[$DISPLAY_SELECTED]+_}" ]]; then From b622505d817c5dcfe1e88db63eb513487d1622a8 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 17 Sep 2025 14:58:16 +0200 Subject: [PATCH 0981/1733] Update build.func --- misc/build.func | 264 ++++++++++++++++++++++++------------------------ 1 file changed, 132 insertions(+), 132 deletions(-) diff --git a/misc/build.func b/misc/build.func index df1df170c..3592f9937 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2346,6 +2346,138 @@ destroy_lxc() { fi } +# ------------------------------------------------------------------------------ +# Storage discovery / selection helpers +# ------------------------------------------------------------------------------ +# ===== Storage discovery / selection helpers (ported from create_lxc.sh) ===== +resolve_storage_preselect() { + local class="$1" preselect="$2" required_content="" + case "$class" in + template) required_content="vztmpl" ;; + container) required_content="rootdir" ;; + *) return 1 ;; + esac + [[ -z "$preselect" ]] && return 1 + if ! pvesm status -content "$required_content" | awk 'NR>1{print $1}' | grep -qx -- "$preselect"; then + msg_warn "Preselected storage '${preselect}' does not support content '${required_content}' (or not found)" + return 1 + fi + + local line total used free + line="$(pvesm status | awk -v s="$preselect" 'NR>1 && $1==s {print $0}')" + if [[ -z "$line" ]]; then + STORAGE_INFO="n/a" + else + total="$(awk '{print $4}' <<<"$line")" + used="$(awk '{print $5}' <<<"$line")" + free="$(awk '{print $6}' <<<"$line")" + local total_h used_h free_h + if command -v numfmt >/dev/null 2>&1; then + total_h="$(numfmt --to=iec --suffix=B --format %.1f "$total" 2>/dev/null || echo "$total")" + used_h="$(numfmt --to=iec --suffix=B --format %.1f "$used" 2>/dev/null || echo "$used")" + free_h="$(numfmt --to=iec --suffix=B --format %.1f "$free" 2>/dev/null || echo "$free")" + STORAGE_INFO="Free: ${free_h} Used: ${used_h}" + else + STORAGE_INFO="Free: ${free} Used: ${used}" + fi + fi + STORAGE_RESULT="$preselect" + return 0 +} + +check_storage_support() { + local CONTENT="$1" VALID=0 + while IFS= read -r line; do + local STORAGE_NAME + STORAGE_NAME=$(awk '{print $1}' <<<"$line") + [[ -n "$STORAGE_NAME" ]] && VALID=1 + done < <(pvesm status -content "$CONTENT" 2>/dev/null | awk 'NR>1') + [[ $VALID -eq 1 ]] +} + +select_storage() { + local CLASS=$1 CONTENT CONTENT_LABEL + case $CLASS in + container) + CONTENT='rootdir' + CONTENT_LABEL='Container' + ;; + template) + CONTENT='vztmpl' + CONTENT_LABEL='Container template' + ;; + iso) + CONTENT='iso' + CONTENT_LABEL='ISO image' + ;; + images) + CONTENT='images' + CONTENT_LABEL='VM Disk image' + ;; + backup) + CONTENT='backup' + CONTENT_LABEL='Backup' + ;; + snippets) + CONTENT='snippets' + CONTENT_LABEL='Snippets' + ;; + *) + msg_error "Invalid storage class '$CLASS'" + return 1 + ;; + esac + + declare -A STORAGE_MAP + local -a MENU=() + local COL_WIDTH=0 + + while read -r TAG TYPE _ TOTAL USED FREE _; do + [[ -n "$TAG" && -n "$TYPE" ]] || continue + local DISPLAY="${TAG} (${TYPE})" + local USED_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$USED") + local FREE_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$FREE") + local INFO="Free: ${FREE_FMT}B Used: ${USED_FMT}B" + STORAGE_MAP["$DISPLAY"]="$TAG" + MENU+=("$DISPLAY" "$INFO" "OFF") + ((${#DISPLAY} > COL_WIDTH)) && COL_WIDTH=${#DISPLAY} + done < <(pvesm status -content "$CONTENT" | awk 'NR>1') + + if [[ ${#MENU[@]} -eq 0 ]]; then + msg_error "No storage found for content type '$CONTENT'." + return 2 + fi + + if [[ $((${#MENU[@]} / 3)) -eq 1 ]]; then + STORAGE_RESULT="${STORAGE_MAP[${MENU[0]}]}" + STORAGE_INFO="${MENU[1]}" + return 0 + fi + + local WIDTH=$((COL_WIDTH + 42)) + while true; do + local DISPLAY_SELECTED + DISPLAY_SELECTED=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "Storage Pools" \ + --radiolist "Which storage pool for ${CONTENT_LABEL,,}?\n(Spacebar to select)" \ + 16 "$WIDTH" 6 "${MENU[@]}" 3>&1 1>&2 2>&3) || { exit_script; } + + DISPLAY_SELECTED=$(sed 's/[[:space:]]*$//' <<<"$DISPLAY_SELECTED") + if [[ -z "$DISPLAY_SELECTED" || -z "${STORAGE_MAP[$DISPLAY_SELECTED]+_}" ]]; then + whiptail --msgbox "No valid storage selected. Please try again." 8 58 + continue + fi + STORAGE_RESULT="${STORAGE_MAP[$DISPLAY_SELECTED]}" + for ((i = 0; i < ${#MENU[@]}; i += 3)); do + if [[ "${MENU[$i]}" == "$DISPLAY_SELECTED" ]]; then + STORAGE_INFO="${MENU[$i + 1]}" + break + fi + done + return 0 + done +} + create_lxc_container() { # ------------------------------------------------------------------------------ # Optional verbose mode (debug tracing) @@ -2422,138 +2554,6 @@ create_lxc_container() { esac } - # ------------------------------------------------------------------------------ - # Storage discovery / selection helpers - # ------------------------------------------------------------------------------ - # ===== Storage discovery / selection helpers (ported from create_lxc.sh) ===== - resolve_storage_preselect() { - local class="$1" preselect="$2" required_content="" - case "$class" in - template) required_content="vztmpl" ;; - container) required_content="rootdir" ;; - *) return 1 ;; - esac - [[ -z "$preselect" ]] && return 1 - if ! pvesm status -content "$required_content" | awk 'NR>1{print $1}' | grep -qx -- "$preselect"; then - msg_warn "Preselected storage '${preselect}' does not support content '${required_content}' (or not found)" - return 1 - fi - - local line total used free - line="$(pvesm status | awk -v s="$preselect" 'NR>1 && $1==s {print $0}')" - if [[ -z "$line" ]]; then - STORAGE_INFO="n/a" - else - total="$(awk '{print $4}' <<<"$line")" - used="$(awk '{print $5}' <<<"$line")" - free="$(awk '{print $6}' <<<"$line")" - local total_h used_h free_h - if command -v numfmt >/dev/null 2>&1; then - total_h="$(numfmt --to=iec --suffix=B --format %.1f "$total" 2>/dev/null || echo "$total")" - used_h="$(numfmt --to=iec --suffix=B --format %.1f "$used" 2>/dev/null || echo "$used")" - free_h="$(numfmt --to=iec --suffix=B --format %.1f "$free" 2>/dev/null || echo "$free")" - STORAGE_INFO="Free: ${free_h} Used: ${used_h}" - else - STORAGE_INFO="Free: ${free} Used: ${used}" - fi - fi - STORAGE_RESULT="$preselect" - return 0 - } - - check_storage_support() { - local CONTENT="$1" VALID=0 - while IFS= read -r line; do - local STORAGE_NAME - STORAGE_NAME=$(awk '{print $1}' <<<"$line") - [[ -n "$STORAGE_NAME" ]] && VALID=1 - done < <(pvesm status -content "$CONTENT" 2>/dev/null | awk 'NR>1') - [[ $VALID -eq 1 ]] - } - - select_storage() { - local CLASS=$1 CONTENT CONTENT_LABEL - case $CLASS in - container) - CONTENT='rootdir' - CONTENT_LABEL='Container' - ;; - template) - CONTENT='vztmpl' - CONTENT_LABEL='Container template' - ;; - iso) - CONTENT='iso' - CONTENT_LABEL='ISO image' - ;; - images) - CONTENT='images' - CONTENT_LABEL='VM Disk image' - ;; - backup) - CONTENT='backup' - CONTENT_LABEL='Backup' - ;; - snippets) - CONTENT='snippets' - CONTENT_LABEL='Snippets' - ;; - *) - msg_error "Invalid storage class '$CLASS'" - return 1 - ;; - esac - - declare -A STORAGE_MAP - local -a MENU=() - local COL_WIDTH=0 - - while read -r TAG TYPE _ TOTAL USED FREE _; do - [[ -n "$TAG" && -n "$TYPE" ]] || continue - local DISPLAY="${TAG} (${TYPE})" - local USED_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$USED") - local FREE_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$FREE") - local INFO="Free: ${FREE_FMT}B Used: ${USED_FMT}B" - STORAGE_MAP["$DISPLAY"]="$TAG" - MENU+=("$DISPLAY" "$INFO" "OFF") - ((${#DISPLAY} > COL_WIDTH)) && COL_WIDTH=${#DISPLAY} - done < <(pvesm status -content "$CONTENT" | awk 'NR>1') - - if [[ ${#MENU[@]} -eq 0 ]]; then - msg_error "No storage found for content type '$CONTENT'." - return 2 - fi - - if [[ $((${#MENU[@]} / 3)) -eq 1 ]]; then - STORAGE_RESULT="${STORAGE_MAP[${MENU[0]}]}" - STORAGE_INFO="${MENU[1]}" - return 0 - fi - - local WIDTH=$((COL_WIDTH + 42)) - while true; do - local DISPLAY_SELECTED - DISPLAY_SELECTED=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "Storage Pools" \ - --radiolist "Which storage pool for ${CONTENT_LABEL,,}?\n(Spacebar to select)" \ - 16 "$WIDTH" 6 "${MENU[@]}" 3>&1 1>&2 2>&3) || { exit_script; } - - DISPLAY_SELECTED=$(sed 's/[[:space:]]*$//' <<<"$DISPLAY_SELECTED") - if [[ -z "$DISPLAY_SELECTED" || -z "${STORAGE_MAP[$DISPLAY_SELECTED]+_}" ]]; then - whiptail --msgbox "No valid storage selected. Please try again." 8 58 - continue - fi - STORAGE_RESULT="${STORAGE_MAP[$DISPLAY_SELECTED]}" - for ((i = 0; i < ${#MENU[@]}; i += 3)); do - if [[ "${MENU[$i]}" == "$DISPLAY_SELECTED" ]]; then - STORAGE_INFO="${MENU[$i + 1]}" - break - fi - done - return 0 - done - } - # ------------------------------------------------------------------------------ # Required input variables # ------------------------------------------------------------------------------ From cfed8d6e69d3712b613a25e5036fc45d6c738ccf Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 17 Sep 2025 15:17:07 +0200 Subject: [PATCH 0982/1733] Update build.func --- misc/build.func | 3 +++ 1 file changed, 3 insertions(+) diff --git a/misc/build.func b/misc/build.func index 3592f9937..1b385399e 100644 --- a/misc/build.func +++ b/misc/build.func @@ -992,6 +992,9 @@ default_var_settings() { done return 1 } + # Allow override of storages via env (for non-interactive use cases) + [ -n "${var_template_storage:-}" ] && TEMPLATE_STORAGE="$var_template_storage" + [ -n "${var_container_storage:-}" ] && CONTAINER_STORAGE="$var_container_storage" # Create once, with storages already selected, no var_ctid/var_hostname lines local _ensure_default_vars From 6c9d1d0e23973d2c177facc12a5e9e3abe4bbefe Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 17 Sep 2025 15:26:17 +0200 Subject: [PATCH 0983/1733] Update build.func --- misc/build.func | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/misc/build.func b/misc/build.func index 1b385399e..0ad35c115 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1449,11 +1449,26 @@ maybe_offer_save_app_defaults() { } ensure_storage_selection_for_vars_file() { - # $1 = vars_file - local vf="$1" - # Always prompt (unless only one choice for that content), per your requirement #4 - choose_and_set_storage_for_file "$vf" template - choose_and_set_storage_for_file "$vf" container + vf="$1" + + # Read stored values (if any) + tpl=$(grep -E '^var_template_storage=' "$vf" | cut -d= -f2-) + ct=$(grep -E '^var_container_storage=' "$vf" | cut -d= -f2-) + + # Template storage + if [ -n "$tpl" ]; then + TEMPLATE_STORAGE="$tpl" + else + choose_and_set_storage_for_file "$vf" template + fi + + # Container storage + if [ -n "$ct" ]; then + CONTAINER_STORAGE="$ct" + else + choose_and_set_storage_for_file "$vf" container + fi + echo_storage_summary_from_file "$vf" } From b678f8828a29022751cd3a60a75bbc39bf154222 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 17 Sep 2025 15:34:04 +0200 Subject: [PATCH 0984/1733] Fix storage variable assignment in echo_storage_summary_from_file Corrects the logic for setting TEMPLATE_STORAGE and CONTAINER_STORAGE by checking if tpl and ct are set, respectively, and calling choose_and_set_storage_for_file if not. This ensures storage variables are properly initialized before use. --- misc/build.func | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/misc/build.func b/misc/build.func index 0ad35c115..85c7c9ef5 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1809,10 +1809,17 @@ echo_storage_summary_from_file() { local ct_store tpl_store ct_store=$(awk -F= '/^var_container_storage=/ {print $2; exit}' "$vars_file") tpl_store=$(awk -F= '/^var_template_storage=/ {print $2; exit}' "$vars_file") + if [ -n "$tpl" ]; then + TEMPLATE_STORAGE="$tpl" + else + choose_and_set_storage_for_file "$vf" template + fi - echo -e "\n${INFO}${BOLD}${DGN}Storage settings from ${vars_file}:${CL}" - echo -e " 📦 Container Storage: ${BGN}${ct_store:-}${CL}" - echo -e " 📦 Template Storage: ${BGN}${tpl_store:-}${CL}\n" + if [ -n "$ct" ]; then + CONTAINER_STORAGE="$ct" + else + choose_and_set_storage_for_file "$vf" container + fi } # ------------------------------------------------------------------------------ From 8b34ae346bb0699a2d842527b105a35d5b691e32 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 17 Sep 2025 15:41:13 +0200 Subject: [PATCH 0985/1733] Refactor install menu and add settings submenu Updated the install menu labels for clarity and dynamic numbering. Introduced a new settings_menu function to manage diagnostic, default, and storage settings, with conditional options based on the presence of app defaults. --- misc/build.func | 55 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/misc/build.func b/misc/build.func index 85c7c9ef5..8a62d55d1 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1551,18 +1551,21 @@ install_script() { else # Build dynamic menu local menu_items=( - "1" "Default Settings" - "2" "Default Settings (Verbose)" - "3" "Advanced Install" + "1" "Default Install" + "2" "Default Install (Verbose)" + "3" "Advanced Settings" "4" "My Defaults" ) + + local next=5 if [ -f "$(get_app_defaults_path)" ]; then - menu_items+=("5" "App Defaults for ${APP}") + menu_items+=("$next" "App Defaults for ${APP}") + next=$((next + 1)) fi + menu_items+=( - "6" "Diagnostic Settings" - "7" "Storage Settings" - "8" "Exit" + "$next" "Settings" + "$((next + 1))" "Exit" ) TMP_CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ @@ -1753,6 +1756,44 @@ install_script() { # fi # } +settings_menu() { + while true; do + local settings_items=( + "1" "Manage API-Diagnostic Setting" + "2" "Edit Default.vars" + "3" "Edit Default Storage" + ) + if [ -f "$(get_app_defaults_path)" ]; then + settings_items+=("4" "Edit App.vars for ${APP}") + settings_items+=("5" "Exit") + else + settings_items+=("4" "Exit") + fi + + local choice + choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "SETTINGS MENU" \ + --ok-button "OK" --cancel-button "Back" \ + --menu "Choose a settings option:" 20 60 9 \ + "${settings_items[@]}" \ + 3>&1 1>&2 2>&3) || return 0 + + case "$choice" in + 1) diagnostics_menu ;; + 2) ${EDITOR:-nano} /usr/local/community-scripts/default.vars ;; + 3) edit_default_storage ;; + 4) + if [ -f "$(get_app_defaults_path)" ]; then + ${EDITOR:-nano} "$(get_app_defaults_path)" + else + return 0 + fi + ;; + 5) return 0 ;; + esac + done +} + # ===== Unified storage selection & writing to vars files ===== _write_storage_to_vars() { # $1 = vars_file, $2 = key (var_container_storage / var_template_storage), $3 = value From b9b52a6642bd9b9782688c966f76d691de843768 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 17 Sep 2025 15:49:12 +0200 Subject: [PATCH 0986/1733] Remove diagnostics and storage menu options Eliminates the diagnostics and storage options from the install_script function, consolidating the menu and redirecting the diagnostics option to settings_menu. This simplifies the script's menu structure and removes unused or deprecated functionality. --- misc/build.func | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/misc/build.func b/misc/build.func index 8a62d55d1..7e57aea3d 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1537,12 +1537,6 @@ install_script() { APPDEFAULTS | appdefaults | 5) CHOICE="5" ;; - DIAGNOSTICS | diagnostics | 6) - CHOICE="6" - ;; - STORAGE | storage | 7) - CHOICE="7" - ;; *) echo -e "\n${CROSS}${RD}Invalid PRESET value: ${PRESET}${CL}\n" exit 1 @@ -1632,16 +1626,10 @@ install_script() { fi ;; 6) - diagnostics_menu + settings_menu exit 0 ;; 7) - header_info - echo -e "${DEFAULT}${BOLD}${BL}Manage Storage Settings on node $PVEHOST_NAME${CL}" - ensure_storage_selection_for_vars_file "/usr/local/community-scripts/default.vars" - exit 0 - ;; - 8) echo -e "\n${CROSS}${RD}Script terminated.${CL}\n" exit 0 ;; From 92b1328f899dfde9ec7894726cad8362762f3b5f Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 17 Sep 2025 15:56:56 +0200 Subject: [PATCH 0987/1733] Update build.func --- misc/build.func | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/misc/build.func b/misc/build.func index 7e57aea3d..61d85a6cd 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1744,6 +1744,20 @@ install_script() { # fi # } +edit_default_storage() { + local vf="/usr/local/community-scripts/default.vars" + + # make sure file exists + if [ ! -f "$vf" ]; then + msg_info "No default.vars found, creating $vf" + mkdir -p /usr/local/community-scripts + touch "$vf" + fi + + # reuse the same Whiptail selection we already have + ensure_storage_selection_for_vars_file "$vf" +} + settings_menu() { while true; do local settings_items=( @@ -1764,7 +1778,7 @@ settings_menu() { --ok-button "OK" --cancel-button "Back" \ --menu "Choose a settings option:" 20 60 9 \ "${settings_items[@]}" \ - 3>&1 1>&2 2>&3) || return 0 + 3>&1 1>&2 2>&3) || break case "$choice" in 1) diagnostics_menu ;; @@ -1774,10 +1788,10 @@ settings_menu() { if [ -f "$(get_app_defaults_path)" ]; then ${EDITOR:-nano} "$(get_app_defaults_path)" else - return 0 + exit_script fi ;; - 5) return 0 ;; + 5) exit_script ;; esac done } From c7456236a08ee667cb48f552cfdc459a30fcca98 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 17 Sep 2025 16:02:42 +0200 Subject: [PATCH 0988/1733] Refactor settings menu and exit handling in build.func Simplifies the menu item logic for settings and app defaults, updates the whiptail menu to use custom button labels, and replaces direct exit calls with the exit_script function for consistent script termination. --- misc/build.func | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/misc/build.func b/misc/build.func index 61d85a6cd..4df11e113 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1551,26 +1551,22 @@ install_script() { "4" "My Defaults" ) - local next=5 if [ -f "$(get_app_defaults_path)" ]; then - menu_items+=("$next" "App Defaults for ${APP}") - next=$((next + 1)) + menu_items+=("5" "App Defaults for ${APP}") + menu_items+=("6" "Settings") + else + menu_items+=("5" "Settings") fi - menu_items+=( - "$next" "Settings" - "$((next + 1))" "Exit" - ) - TMP_CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ --title "SETTINGS" \ + --ok-button "OK" --cancel-button "Exit Script" \ --menu "Choose an option:" 20 60 9 \ "${menu_items[@]}" \ --default-item "1" 3>&1 1>&2 2>&3) || true if [ -z "$TMP_CHOICE" ]; then - echo -e "\n${CROSS}${RD}Menu canceled. Exiting script.${CL}\n" - exit 0 + exit_script fi CHOICE="$TMP_CHOICE" fi @@ -1629,10 +1625,6 @@ install_script() { settings_menu exit 0 ;; - 7) - echo -e "\n${CROSS}${RD}Script terminated.${CL}\n" - exit 0 - ;; *) echo -e "${CROSS}${RD}Invalid option, please try again.${CL}" ;; From 9b160ecd71494a7b280eee680ca5393bf07f942b Mon Sep 17 00:00:00 2001 From: cobaltgit Date: Wed, 17 Sep 2025 15:05:34 +0100 Subject: [PATCH 0989/1733] alpine-caddy: fix json --- frontend/public/json/alpine-caddy.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/public/json/alpine-caddy.json b/frontend/public/json/alpine-caddy.json index e8d8c5feb..54578adea 100644 --- a/frontend/public/json/alpine-caddy.json +++ b/frontend/public/json/alpine-caddy.json @@ -1,10 +1,10 @@ { - "name": "Caddy", - "slug": "caddy", + "name": "Alpine-Caddy", + "slug": "alpine-caddy", "categories": [ 21 ], - "date_created": "2024-05-11", + "date_created": "2025-09-17", "type": "ct", "updateable": true, "privileged": false, @@ -17,13 +17,13 @@ "install_methods": [ { "type": "default", - "script": "ct/caddy.sh", + "script": "ct/alpine-caddy.sh", "resources": { "cpu": 1, - "ram": 512, - "hdd": 4, - "os": "debian", - "version": "12" + "ram": 256, + "hdd": 3, + "os": "alpine", + "version": "3.22" } }, { From b006376cf718a0b847bcf39a9654f2d577fb6dd2 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 17 Sep 2025 16:07:24 +0200 Subject: [PATCH 0990/1733] Update globaleaks-install.sh --- install/globaleaks-install.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/install/globaleaks-install.sh b/install/globaleaks-install.sh index 08e8348d7..ade3df767 100644 --- a/install/globaleaks-install.sh +++ b/install/globaleaks-install.sh @@ -18,8 +18,10 @@ curl -fsSL https://deb.globaleaks.org/globaleaks.asc | gpg --dearmor -o /etc/apt echo "deb [signed-by=/etc/apt/trusted.gpg.d/globaleaks.gpg] http://deb.globaleaks.org $DISTRO_CODENAME/" >/etc/apt/sources.list.d/globaleaks.list $STD apt update $STD apt -y install globaleaks -echo 'APPARMOR_SANDBOXING=0' | sudo tee -a /etc/default/globaleaks -echo 'NETWORK_SANDBOXING=0' | sudo tee -a /etc/default/globaleaks +systemctl stop globaleaks +echo 'APPARMOR_SANDBOXING=0' >>/etc/default/globaleaks +echo 'NETWORK_SANDBOXING=0' >>/etc/default/globaleaks +systemctl daemon-reexec systemctl restart globaleaks msg_ok "Setup GlobaLeaks" From 322fd681cb4b2ec10ee3e0ede5cf2a43a7d445ba Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 17 Sep 2025 16:13:02 +0200 Subject: [PATCH 0991/1733] Refactor install_script menu and preset handling Simplifies the install_script function by replacing the PRESET environment variable logic with direct CLI argument support for preset selection. Updates the whiptail menu options, streamlines case handling, and improves error handling for invalid options. This refactor makes the script interface more intuitive and reduces code complexity. --- misc/build.func | 81 +++++++++++++++---------------------------------- 1 file changed, 24 insertions(+), 57 deletions(-) diff --git a/misc/build.func b/misc/build.func index 4df11e113..cc850468b 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1519,97 +1519,62 @@ install_script() { timezone=$(cat /etc/timezone) header_info - # --- PRESET support --- - if [ -n "${PRESET:-}" ]; then - case "$PRESET" in - DEFAULT | default | 1) - CHOICE="1" - ;; - VERBOSE | verbose | 2) - CHOICE="2" - ;; - ADVANCED | advanced | 3) - CHOICE="3" - ;; - MYDEFAULTS | mydefaults | 4) - CHOICE="4" - ;; - APPDEFAULTS | appdefaults | 5) - CHOICE="5" - ;; - *) - echo -e "\n${CROSS}${RD}Invalid PRESET value: ${PRESET}${CL}\n" - exit 1 - ;; - esac - else - # Build dynamic menu + # --- Support CLI argument as direct preset (default, advanced, …) --- + CHOICE="${1:-}" + + # If no CLI argument → show whiptail menu + if [ -z "$CHOICE" ]; then local menu_items=( "1" "Default Install" - "2" "Default Install (Verbose)" - "3" "Advanced Settings" - "4" "My Defaults" + "2" "Advanced Install" + "3" "My Defaults" ) if [ -f "$(get_app_defaults_path)" ]; then - menu_items+=("5" "App Defaults for ${APP}") - menu_items+=("6" "Settings") - else + menu_items+=("4" "App Defaults for ${APP}") menu_items+=("5" "Settings") + else + menu_items+=("4" "Settings") fi TMP_CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ --title "SETTINGS" \ - --ok-button "OK" --cancel-button "Exit Script" \ + --ok-button "Select" --cancel-button "Exit Script" \ --menu "Choose an option:" 20 60 9 \ "${menu_items[@]}" \ - --default-item "1" 3>&1 1>&2 2>&3) || true + --default-item "1" 3>&1 1>&2 2>&3) || exit_script - if [ -z "$TMP_CHOICE" ]; then - exit_script - fi CHOICE="$TMP_CHOICE" fi - case $CHOICE in - 1) + # --- Main case --- + case "$CHOICE" in + 1 | default | DEFAULT) header_info echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME${CL}" VERBOSE="no" METHOD="default" base_settings "$VERBOSE" echo_default - # Always ask storages for MyDefaults file (create if missing) ensure_storage_selection_for_vars_file "/usr/local/community-scripts/default.vars" ;; - 2) - header_info - echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME (Verbose)${CL}" - VERBOSE="yes" - METHOD="default" - base_settings "$VERBOSE" - echo_default - ensure_storage_selection_for_vars_file "/usr/local/community-scripts/default.vars" - ;; - 3) + 2 | advanced | ADVANCED) header_info echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Install on node $PVEHOST_NAME${CL}" METHOD="advanced" base_settings advanced_settings - # Always ask storages (stored to env var_* so app-defaults will include them) ensure_storage_selection_for_vars_file "/usr/local/community-scripts/default.vars" maybe_offer_save_app_defaults ;; - 4) + 3 | mydefaults | MYDEFAULTS) default_var_settings || { msg_error "Failed to apply default.vars" exit 1 } - # Always let user pick storages again (unless only one exists) ensure_storage_selection_for_vars_file "/usr/local/community-scripts/default.vars" ;; - 5) + 4 | appdefaults | APPDEFAULTS) if [ -f "$(get_app_defaults_path)" ]; then header_info echo -e "${DEFAULT}${BOLD}${BL}Using App Defaults for ${APP} on node $PVEHOST_NAME${CL}" @@ -1617,16 +1582,18 @@ install_script() { base_settings _load_vars_file "$(get_app_defaults_path)" echo_default - # Always let user confirm/change storages for the app defaults ensure_storage_selection_for_vars_file "$(get_app_defaults_path)" + else + msg_error "No App Defaults available for ${APP}" + exit 1 fi ;; - 6) + 5 | settings | SETTINGS) settings_menu - exit 0 ;; *) - echo -e "${CROSS}${RD}Invalid option, please try again.${CL}" + echo -e "${CROSS}${RD}Invalid option: $CHOICE${CL}" + exit 1 ;; esac } From 95171c50259f5a7711c0d2adf68ef168775b3cd5 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 17 Sep 2025 16:17:11 +0200 Subject: [PATCH 0992/1733] Update build.func --- misc/build.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index cc850468b..54a3556cc 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1520,7 +1520,7 @@ install_script() { header_info # --- Support CLI argument as direct preset (default, advanced, …) --- - CHOICE="${1:-}" + CHOICE="${mode:-${1:-}}" # If no CLI argument → show whiptail menu if [ -z "$CHOICE" ]; then From f36f1b313232beaebb1ef7039bd3a80a235ea969 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 17 Sep 2025 16:55:06 +0200 Subject: [PATCH 0993/1733] Update whiptail menu titles and dimensions Changed menu titles to reference 'Community-Scripts' instead of generic 'SETTINGS' and adjusted menu width from 60 to 40 in install_script for improved clarity and UI consistency. --- misc/build.func | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/misc/build.func b/misc/build.func index 54a3556cc..f158bbd74 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1538,9 +1538,9 @@ install_script() { fi TMP_CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "SETTINGS" \ + --title "Community-Scripts Options" \ --ok-button "Select" --cancel-button "Exit Script" \ - --menu "Choose an option:" 20 60 9 \ + --menu "Choose an option:" 20 40 9 \ "${menu_items[@]}" \ --default-item "1" 3>&1 1>&2 2>&3) || exit_script @@ -1733,7 +1733,7 @@ settings_menu() { local choice choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ - --title "SETTINGS MENU" \ + --title "Community-Scripts SETTINGS Menu" \ --ok-button "OK" --cancel-button "Back" \ --menu "Choose a settings option:" 20 60 9 \ "${settings_items[@]}" \ From a0b9bdbbecd92a5af75a52704e7f5ff6abcb78d6 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 17 Sep 2025 16:56:43 +0200 Subject: [PATCH 0994/1733] Improve settings menu instructions in build script Updated the settings menu prompt to include navigation instructions for users, clarifying how to use TAB, Arrow keys, and ENTER to select options. --- misc/build.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index f158bbd74..61c6386d8 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1735,7 +1735,7 @@ settings_menu() { choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "Community-Scripts SETTINGS Menu" \ --ok-button "OK" --cancel-button "Back" \ - --menu "Choose a settings option:" 20 60 9 \ + --menu "Choose a settings option:\n\nUse TAB or Arrow keys to navigate, ENTER to select." 20 60 9 \ "${settings_items[@]}" \ 3>&1 1>&2 2>&3) || break From 8cd3defbd51f5030771fb84de1961e582a2956ae Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 17 Sep 2025 16:57:08 +0200 Subject: [PATCH 0995/1733] Update build.func --- misc/build.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index 61c6386d8..763541710 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1735,7 +1735,7 @@ settings_menu() { choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "Community-Scripts SETTINGS Menu" \ --ok-button "OK" --cancel-button "Back" \ - --menu "Choose a settings option:\n\nUse TAB or Arrow keys to navigate, ENTER to select." 20 60 9 \ + --menu "Choose a settings option:\n\nUse TAB or Arrow keys to navigate, ENTER to select." 30 45 9 \ "${settings_items[@]}" \ 3>&1 1>&2 2>&3) || break From 699aaefdb5099fba2f3e1d954773b97540de9474 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 17 Sep 2025 16:57:55 +0200 Subject: [PATCH 0996/1733] Update build.func --- misc/build.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index 763541710..f9a46ea99 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1735,7 +1735,7 @@ settings_menu() { choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ --title "Community-Scripts SETTINGS Menu" \ --ok-button "OK" --cancel-button "Back" \ - --menu "Choose a settings option:\n\nUse TAB or Arrow keys to navigate, ENTER to select." 30 45 9 \ + --menu "\n\nChoose a settings option:\n\nUse TAB or Arrow keys to navigate, ENTER to select." 20 60 9 \ "${settings_items[@]}" \ 3>&1 1>&2 2>&3) || break From 82707ce199e48d681af9ba07bdc5130799245d8a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 17 Sep 2025 17:00:39 +0200 Subject: [PATCH 0997/1733] Improve whiptail menu UI in install_script function Updated the whiptail menu in the install_script function to enhance usability. Changes include adjusting the backtitle, adding --fb and --notags options, updating the menu prompt with navigation instructions, and increasing the menu width for better display. --- misc/build.func | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/misc/build.func b/misc/build.func index f9a46ea99..31e08fbc7 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1537,13 +1537,16 @@ install_script() { menu_items+=("4" "Settings") fi - TMP_CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + TMP_CHOICE=$(whiptail \ + --backtitle "Proxmox VE Helper Scripts" \ --title "Community-Scripts Options" \ --ok-button "Select" --cancel-button "Exit Script" \ - --menu "Choose an option:" 20 40 9 \ + --fb --notags \ + --menu "\nChoose an option:\n\nUse TAB or Arrow keys to navigate, ENTER to select.\n" \ + 20 60 9 \ "${menu_items[@]}" \ - --default-item "1" 3>&1 1>&2 2>&3) || exit_script - + --default-item "1" \ + 3>&1 1>&2 2>&3) || exit_script CHOICE="$TMP_CHOICE" fi From 776c39b8fc0c0aabf1f56b361b75710ff4625764 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 17 Sep 2025 17:07:56 +0200 Subject: [PATCH 0998/1733] Update build.func --- misc/build.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index 31e08fbc7..f21070ce4 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1541,7 +1541,7 @@ install_script() { --backtitle "Proxmox VE Helper Scripts" \ --title "Community-Scripts Options" \ --ok-button "Select" --cancel-button "Exit Script" \ - --fb --notags \ + --notags \ --menu "\nChoose an option:\n\nUse TAB or Arrow keys to navigate, ENTER to select.\n" \ 20 60 9 \ "${menu_items[@]}" \ From 65bc905cff178e1f6de5adb7c44ca91cbbd94463 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 17 Sep 2025 17:08:52 +0200 Subject: [PATCH 0999/1733] Update build.func --- misc/build.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index f21070ce4..a7ada2294 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1542,7 +1542,7 @@ install_script() { --title "Community-Scripts Options" \ --ok-button "Select" --cancel-button "Exit Script" \ --notags \ - --menu "\nChoose an option:\n\nUse TAB or Arrow keys to navigate, ENTER to select.\n" \ + --menu "\nChoose an option:\n Use TAB or Arrow keys to navigate, ENTER to select.\n" \ 20 60 9 \ "${menu_items[@]}" \ --default-item "1" \ From 51eb81ac62a7539845334f83e5ef25cdba5a8766 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 17 Sep 2025 17:13:04 +0200 Subject: [PATCH 1000/1733] tested --- misc/alpine-tools.func | 35 ++++++++++++++----------------- misc/{ => deferred}/create_lxc.sh | 0 2 files changed, 16 insertions(+), 19 deletions(-) rename misc/{ => deferred}/create_lxc.sh (100%) diff --git a/misc/alpine-tools.func b/misc/alpine-tools.func index b03f4f084..4bd959d47 100644 --- a/misc/alpine-tools.func +++ b/misc/alpine-tools.func @@ -4,21 +4,20 @@ # Erwartet vorhandene msg_* und optional $STD aus deinem Framework. # ------------------------------ -# kleine Helfer +# helpers # ------------------------------ lower() { printf '%s' "$1" | tr '[:upper:]' '[:lower:]'; } has() { command -v "$1" >/dev/null 2>&1; } need_tool() { # usage: need_tool curl jq unzip ... - # installiert fehlende Tools via apk --no-cache + # setup missing tools via apk local missing=0 t for t in "$@"; do if ! has "$t"; then missing=1; fi done if [ "$missing" -eq 1 ]; then msg_info "Installing tools: $*" - # busybox 'apk' ist vorhanden auf Alpine apk add --no-cache "$@" >/dev/null 2>&1 || { msg_error "apk add failed for: $*" return 1 @@ -28,7 +27,7 @@ need_tool() { } net_resolves() { - # robust gegen fehlendes getent auf busybox + # better handling for missing getent on Alpine # usage: net_resolves api.github.com local host="$1" ping -c1 -W1 "$host" >/dev/null 2>&1 || nslookup "$host" >/dev/null 2>&1 @@ -61,7 +60,7 @@ download_with_progress() { } # ------------------------------ -# GitHub: Release prüfen +# GitHub: check Release # ------------------------------ check_for_gh_release() { # app, repo, [pinned] @@ -113,11 +112,11 @@ check_for_gh_release() { } # ------------------------------ -# GitHub: Release holen & deployen (Alpine) +# GitHub: get Release & deployen (Alpine) # modes: tarball | prebuild | singlefile # ------------------------------ fetch_and_deploy_gh() { - # $1 app, $2 repo, [$3 mode], [$4 version], [$5 target], [$6 asset_pattern] + # $1 app, $2 repo, [$3 mode], [$4 version], [$5 target], [$6 asset_pattern local app="$1" repo="$2" mode="${3:-tarball}" version="${4:-latest}" target="${5:-/opt/$1}" pattern="${6:-}" local app_lc app_lc="$(lower "$app" | tr -d ' ')" @@ -149,7 +148,7 @@ fetch_and_deploy_gh() { } fi - # Effektive Version + # correct Version version="$(printf '%s' "$json" | jq -r '.tag_name // empty')" version="${version#v}" @@ -174,7 +173,7 @@ fetch_and_deploy_gh() { return 1 } unpack="$(find "$tmpd" -mindepth 1 -maxdepth 1 -type d | head -n1)" - # Inhalte nach target kopieren (inkl. dotfiles) + # copy content of unpack to target (cd "$unpack" && tar -cf - .) | (cd "$target" && tar -xf -) || { msg_error "copy failed" rm -rf "$tmpd" @@ -201,7 +200,7 @@ fetch_and_deploy_gh() { rm -rf "$tmpd" return 1 } - # entpacken je nach Format + # unpack archive (Zip or tarball) case "$filename" in *.zip) need_tool unzip || { @@ -221,7 +220,7 @@ fetch_and_deploy_gh() { return 1 ;; esac - # top-level folder ggf. strippen + # top-level folder strippen if [ "$(find "$tmpd/unp" -mindepth 1 -maxdepth 1 -type d | wc -l)" -eq 1 ] && [ -z "$(find "$tmpd/unp" -mindepth 1 -maxdepth 1 -type f | head -n1)" ]; then unpack="$(find "$tmpd/unp" -mindepth 1 -maxdepth 1 -type d)" (cd "$unpack" && tar -cf - .) | (cd "$target" && tar -xf -) || { @@ -276,7 +275,7 @@ fetch_and_deploy_gh() { # yq (mikefarah) – Alpine # ------------------------------ setup_yq() { - # bevorzugt apk, optional FORCE_GH=1 → GitHub Binary + # prefer apk, unless FORCE_GH=1 if [ "${FORCE_GH:-0}" != "1" ] && apk info -e yq >/dev/null 2>&1; then msg_info "Updating yq via apk" apk add --no-cache --upgrade yq >/dev/null 2>&1 || true @@ -319,7 +318,7 @@ setup_adminer() { # ------------------------------ # uv – Alpine (musl tarball) -# Optional: PYTHON_VERSION="3.12" +# optional: PYTHON_VERSION="3.12" # ------------------------------ setup_uv() { need_tool curl tar || return 1 @@ -367,11 +366,11 @@ setup_uv() { return 1 } - # tar enthält ./uv + # tar contains ./uv if [ -x "$tmpd/uv" ]; then install -m 0755 "$tmpd/uv" "$UV_BIN" else - # fallback: in Unterordner + # fallback: in subfolder install -m 0755 "$tmpd"/*/uv "$UV_BIN" 2>/dev/null || { msg_error "uv binary not found in tar" rm -rf "$tmpd" @@ -383,7 +382,6 @@ setup_uv() { msg_ok "Setup uv $ver" if [ -n "${PYTHON_VERSION:-}" ]; then - # uv liefert cpython builds für musl; den neuesten Patchstand finden: local match match="$(uv python list --only-downloads 2>/dev/null | awk -v maj="$PYTHON_VERSION" ' $0 ~ "^cpython-"maj"\\." { print $0 }' | awk -F- '{print $2}' | sort -V | tail -n1)" @@ -417,7 +415,7 @@ setup_java() { msg_error "apk add $pkg failed" return 1 } - # JAVA_HOME setzen + # set JAVA_HOME local prof="/etc/profile.d/20-java.sh" if [ ! -f "$prof" ]; then echo 'export JAVA_HOME=$(dirname $(dirname $(readlink -f $(command -v java))))' >"$prof" @@ -428,7 +426,7 @@ setup_java() { } # ------------------------------ -# Go – Alpine (apk bevorzugt; optional GO_VERSION tarball) +# Go – Alpine (apk prefers, else tarball) # ------------------------------ setup_go() { if [ -z "${GO_VERSION:-}" ]; then @@ -441,7 +439,6 @@ setup_go() { return 0 fi - # explizite Version via offizielles tar.gz need_tool curl tar || return 1 local ARCH TARBALL URL TMP case "$(uname -m)" in diff --git a/misc/create_lxc.sh b/misc/deferred/create_lxc.sh similarity index 100% rename from misc/create_lxc.sh rename to misc/deferred/create_lxc.sh From 7659eae6afec79d788b32cd98963a20754beb1ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Wed, 17 Sep 2025 21:00:03 +0200 Subject: [PATCH 1001/1733] Update alpine-caddy.json --- frontend/public/json/alpine-caddy.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/public/json/alpine-caddy.json b/frontend/public/json/alpine-caddy.json index 54578adea..0f59a88de 100644 --- a/frontend/public/json/alpine-caddy.json +++ b/frontend/public/json/alpine-caddy.json @@ -17,13 +17,13 @@ "install_methods": [ { "type": "default", - "script": "ct/alpine-caddy.sh", + "script": "ct/caddy.sh", "resources": { "cpu": 1, - "ram": 256, - "hdd": 3, - "os": "alpine", - "version": "3.22" + "ram": 512, + "hdd": 4, + "os": "debian", + "version": "12" } }, { From 1ba35e7578a0f3ed55cdc05c1cfba965b9e1c26b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Wed, 17 Sep 2025 21:00:30 +0200 Subject: [PATCH 1002/1733] Rename alpine-caddy.json to caddy.json --- frontend/public/json/{alpine-caddy.json => caddy.json} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename frontend/public/json/{alpine-caddy.json => caddy.json} (100%) diff --git a/frontend/public/json/alpine-caddy.json b/frontend/public/json/caddy.json similarity index 100% rename from frontend/public/json/alpine-caddy.json rename to frontend/public/json/caddy.json From 91756d1d363e4cf9d309148ed18fa841460517a8 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 17 Sep 2025 21:14:10 +0200 Subject: [PATCH 1003/1733] Fix Caddy --- ct/alpine-caddy.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/alpine-caddy.sh b/ct/alpine-caddy.sh index 29e61b2ef..75cfe6366 100644 --- a/ct/alpine-caddy.sh +++ b/ct/alpine-caddy.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -catch_errors +init_error_traps function update_script() { header_info From 8b515d335280c923c89ab5e0010ee0702e500d24 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 17 Sep 2025 21:17:09 +0200 Subject: [PATCH 1004/1733] Fix Caddy --- install/alpine-caddy-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/alpine-caddy-install.sh b/install/alpine-caddy-install.sh index d34d9a3cc..8386dc2e6 100644 --- a/install/alpine-caddy-install.sh +++ b/install/alpine-caddy-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -catch_errors +init_error_traps setting_up_container network_check update_os From 9092dc732e0ea2d14cf9f24a2aae093d3a63bb4e Mon Sep 17 00:00:00 2001 From: Giovanni Pellerano Date: Wed, 17 Sep 2025 22:24:19 +0200 Subject: [PATCH 1005/1733] Simpligy globaleaks-install.sh This commits prevents the software to be started before disabling apparmor and network sandboxing --- install/globaleaks-install.sh | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/install/globaleaks-install.sh b/install/globaleaks-install.sh index ade3df767..966eb32dd 100644 --- a/install/globaleaks-install.sh +++ b/install/globaleaks-install.sh @@ -16,13 +16,9 @@ msg_info "Setup GlobaLeaks" DISTRO_CODENAME="$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release)" curl -fsSL https://deb.globaleaks.org/globaleaks.asc | gpg --dearmor -o /etc/apt/trusted.gpg.d/globaleaks.gpg echo "deb [signed-by=/etc/apt/trusted.gpg.d/globaleaks.gpg] http://deb.globaleaks.org $DISTRO_CODENAME/" >/etc/apt/sources.list.d/globaleaks.list +echo -ne 'APPARMOR_SANDBOXING=0\nNETWORK_SANDBOXING=0' >/etc/default/globaleaks $STD apt update $STD apt -y install globaleaks -systemctl stop globaleaks -echo 'APPARMOR_SANDBOXING=0' >>/etc/default/globaleaks -echo 'NETWORK_SANDBOXING=0' >>/etc/default/globaleaks -systemctl daemon-reexec -systemctl restart globaleaks msg_ok "Setup GlobaLeaks" motd_ssh From ee561c401405f1d6ec79e465d8e5dfdd8b17ee61 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner <73236783+michelroegl-brunner@users.noreply.github.com> Date: Thu, 18 Sep 2025 11:09:56 +0200 Subject: [PATCH 1006/1733] Add placeholder for Alpine-caddy CI/CD --- ct/caddy.sh | 1 + 1 file changed, 1 insertion(+) create mode 100644 ct/caddy.sh diff --git a/ct/caddy.sh b/ct/caddy.sh new file mode 100644 index 000000000..49f72a5ff --- /dev/null +++ b/ct/caddy.sh @@ -0,0 +1 @@ +#Placeholder for ALpine-caddy to run teh CI/CD From 8022e1984d788474e9371cb77db160e34051a473 Mon Sep 17 00:00:00 2001 From: Cobalt <65132371+cobaltgit@users.noreply.github.com> Date: Thu, 18 Sep 2025 10:57:40 +0100 Subject: [PATCH 1007/1733] alpine-caddy: change name and slug in frontend json --- frontend/public/json/caddy.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/public/json/caddy.json b/frontend/public/json/caddy.json index 0f59a88de..607a9f7e6 100644 --- a/frontend/public/json/caddy.json +++ b/frontend/public/json/caddy.json @@ -1,6 +1,6 @@ { - "name": "Alpine-Caddy", - "slug": "alpine-caddy", + "name": "Caddy", + "slug": "caddy", "categories": [ 21 ], From 8a9e9e7fcb9924b53dc8948695bc0aeaa7e6fb64 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 18 Sep 2025 12:01:41 +0200 Subject: [PATCH 1008/1733] Update alpine --- install/alpine-caddy-install.sh | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/install/alpine-caddy-install.sh b/install/alpine-caddy-install.sh index 8386dc2e6..10d155ed8 100644 --- a/install/alpine-caddy-install.sh +++ b/install/alpine-caddy-install.sh @@ -16,16 +16,6 @@ update_os msg_info "Installing Caddy" $STD apk add --no-cache caddy caddy-openrc cat</etc/caddy/Caddyfile -# The Caddyfile is an easy way to configure your Caddy web server. -# -# Unless the file starts with a global options block, the first -# uncommented line is always the address of your site. -# -# To use your own domain name (with automatic HTTPS), first make -# sure your domain's A/AAAA DNS records are properly pointed to -# this machine's public IP, then replace ":80" below with your -# domain name. - :80 { # Set this path to your site's directory. root * /var/www/html @@ -39,9 +29,6 @@ cat</etc/caddy/Caddyfile # Or serve a PHP site through php-fpm: # php_fastcgi localhost:9000 } - -# Refer to the Caddy docs for more information: -# https://caddyserver.com/docs/caddyfile EOF mkdir -p /var/www/html cat</var/www/html/index.html From 71cb94f3094c7a1b6301255a16598e29e7483aa2 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 18 Sep 2025 12:05:59 +0200 Subject: [PATCH 1009/1733] Update alpine-caddy --- install/alpine-caddy-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/alpine-caddy-install.sh b/install/alpine-caddy-install.sh index 10d155ed8..882e2e61d 100644 --- a/install/alpine-caddy-install.sh +++ b/install/alpine-caddy-install.sh @@ -15,7 +15,7 @@ update_os msg_info "Installing Caddy" $STD apk add --no-cache caddy caddy-openrc -cat</etc/caddy/Caddyfile +cat</etc/caddy/Caddyfile :80 { # Set this path to your site's directory. root * /var/www/html @@ -31,7 +31,7 @@ cat</etc/caddy/Caddyfile } EOF mkdir -p /var/www/html -cat</var/www/html/index.html +cat</var/www/html/index.html From 7101291349e22a9279fde93a461cd90b2fd871dc Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 18 Sep 2025 13:04:11 +0200 Subject: [PATCH 1010/1733] Update build.func --- misc/build.func | 89 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/misc/build.func b/misc/build.func index a7ada2294..45d5f3135 100644 --- a/misc/build.func +++ b/misc/build.func @@ -26,6 +26,94 @@ variables() { #CT_TYPE=${var_unprivileged:-$CT_TYPE} } +#!/usr/bin/env bash +# ----------------------------------------------------------------------------- +# Community-Scripts bootstrap loader +# - Always sources build.func from remote +# - Updates local core files only if build.func changed +# - Local cache: /usr/local/community-scripts/core +# ----------------------------------------------------------------------------- + +FUNC_DIR="/usr/local/community-scripts/core" +mkdir -p "$FUNC_DIR" + +BUILD_URL="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func" +BUILD_REV="$FUNC_DIR/build.rev" + +# --- Step 1: fetch build.func content once, compute hash --- +build_content="$(curl -fsSL "$BUILD_URL")" || { + echo "❌ Failed to fetch build.func" + exit 1 +} + +newhash=$(printf "%s" "$build_content" | sha256sum | awk '{print $1}') +oldhash=$(cat "$BUILD_REV" 2>/dev/null || echo "") + +# --- Step 2: if build.func changed, offer update for core files --- +if [ "$newhash" != "$oldhash" ]; then + echo "⚠️ build.func changed!" + + while true; do + read -rp "Refresh local core files? [y/N/diff]: " ans + case "$ans" in + [Yy]*) + echo "$newhash" >"$BUILD_REV" + + update_func_file() { + local file="$1" + local url="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/$file" + local local_path="$FUNC_DIR/$file" + + echo "⬇️ Downloading $file ..." + curl -fsSL "$url" -o "$local_path" || { + echo "❌ Failed to fetch $file" + exit 1 + } + echo "✔️ Updated $file" + } + + update_func_file core.func + update_func_file error_handler.func + update_func_file tools.func + break + ;; + [Dd]*) + for file in core.func error_handler.func tools.func; do + local_path="$FUNC_DIR/$file" + url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/$file" + remote_tmp="$(mktemp)" + + curl -fsSL "$url" -o "$remote_tmp" || continue + + if [ -f "$local_path" ]; then + echo "🔍 Diff for $file:" + diff -u "$local_path" "$remote_tmp" || echo "(no differences)" + else + echo "📦 New file $file will be installed" + fi + + rm -f "$remote_tmp" + done + ;; + *) + echo "❌ Skipped updating local core files" + break + ;; + esac + done +else + echo "✔️ build.func unchanged → using existing local core files" +fi + +# --- Step 3: always source local versions of the core files --- +source "$FUNC_DIR/core.func" +source "$FUNC_DIR/error_handler.func" +source "$FUNC_DIR/tools.func" + +# --- Step 4: finally, source build.func directly from memory --- +# (no tmp file needed) +source <(printf "%s" "$build_content") + # ------------------------------------------------------------------------------ # Load core + error handler functions from community-scripts repo # @@ -33,6 +121,7 @@ variables() { # - Load: core.func, error_handler.func, api.func # - Initialize error traps after loading # ------------------------------------------------------------------------------ + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) if command -v curl >/dev/null 2>&1; then From e6305be04f687b1f3801abc9cb58ffd68ce842ab Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 18 Sep 2025 13:07:07 +0200 Subject: [PATCH 1011/1733] Add workflow to bump build.func revision --- .github/workflows/revision-bump.yml | 44 +++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 .github/workflows/revision-bump.yml diff --git a/.github/workflows/revision-bump.yml b/.github/workflows/revision-bump.yml new file mode 100644 index 000000000..6e15d75b1 --- /dev/null +++ b/.github/workflows/revision-bump.yml @@ -0,0 +1,44 @@ +name: Bump build.func Revision + +on: + push: + branches: [ main ] + paths: + - 'misc/*.func' + - 'misc/*.sh' + +jobs: + bump-revision: + runs-on: ubuntu-latest + if: github.event.head_commit.author.name != 'github-actions[bot]' + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Detect misc changes + id: changes + run: | + git diff --name-only HEAD^ HEAD > changed_files.txt + echo "changed=$(cat changed_files.txt)" >> $GITHUB_OUTPUT + + - name: Bump Revision if needed + if: contains(steps.changes.outputs.changed, 'misc/') && !contains(steps.changes.outputs.changed, 'misc/build.func') + run: | + REV_FILE=".build-revision" + if [ ! -f "$REV_FILE" ]; then echo 0 > "$REV_FILE"; fi + REV_NUM=$(($(cat $REV_FILE) + 1)) + echo $REV_NUM > $REV_FILE + SHORT_SHA=$(git rev-parse --short HEAD) + REV_STR="Revision: r${REV_NUM} (git-${SHORT_SHA})" + sed -i "s/^# Revision:.*/# $REV_STR/" misc/build.func + echo "REV_STR=$REV_STR" >> $GITHUB_ENV + + - name: Commit & push revision + if: contains(steps.changes.outputs.changed, 'misc/') && !contains(steps.changes.outputs.changed, 'misc/build.func') + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add misc/build.func .build-revision + git commit -m "chore: bump build.func to $REV_STR" + git push From ff0e6794d4ef803377c0697dde80a708b2398099 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 18 Sep 2025 13:07:07 +0200 Subject: [PATCH 1012/1733] Update build.func --- misc/build.func | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/misc/build.func b/misc/build.func index 45d5f3135..77b452d7e 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1,9 +1,8 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) -# Co-Author: MickLesk -# Co-Author: michelroegl-brunner +# Copyright (c) 2021-2025 community-scripts ORG +# Author: tteck (tteckster) | MickLesk | michelroegl-brunner # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Revision: # ------------------------------------------------------------------------------ # variables() From 6d2282e5c31c93878aabea91e93ba3698b08ae9b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 18 Sep 2025 13:08:38 +0200 Subject: [PATCH 1013/1733] Update alpine-tools.func --- misc/alpine-tools.func | 32 +++----------------------------- 1 file changed, 3 insertions(+), 29 deletions(-) diff --git a/misc/alpine-tools.func b/misc/alpine-tools.func index 4bd959d47..788e8e006 100644 --- a/misc/alpine-tools.func +++ b/misc/alpine-tools.func @@ -469,15 +469,15 @@ setup_go() { # ------------------------------ # Composer – Alpine -# nutzt php83-cli + openssl + phar +# uses php83-cli + openssl + phar # ------------------------------ setup_composer() { local COMPOSER_BIN="/usr/local/bin/composer" if ! has php; then - # bevorzugt php83 auf Alpine 3.20/3.21+ + # prefers php83 msg_info "Installing PHP CLI for Composer" apk add --no-cache php83-cli php83-openssl php83-phar php83-iconv >/dev/null 2>&1 || { - # Fallback auf generisches php-cli + # Fallback to generic php if 83 not available apk add --no-cache php-cli php-openssl php-phar php-iconv >/dev/null 2>&1 || { msg_error "Failed to install php-cli for composer" return 1 @@ -504,29 +504,3 @@ setup_composer() { ensure_usr_local_bin_persist msg_ok "Composer ready: $(composer --version 2>/dev/null)" } - -# ------------------------------ -# Adminer/uv/go/java/yq/composer stehen oben -# ------------------------------ - -# ------------------------------ -# (Optional) LOCAL_IP import – POSIX-safe -# ------------------------------ -import_local_ip() { - # lädt LOCAL_IP aus /run/local-ip.env oder ermittelt es best effort - local IP_FILE="/run/local-ip.env" - if [ -f "$IP_FILE" ]; then - # shellcheck disable=SC1090 - . "$IP_FILE" - fi - if [ -z "${LOCAL_IP:-}" ]; then - LOCAL_IP="$(ip route get 1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i=="src"){print $(i+1); exit}}')" - [ -z "$LOCAL_IP" ] && LOCAL_IP="$(hostname -i 2>/dev/null | awk '{print $1}')" - [ -z "$LOCAL_IP" ] && { - msg_error "Could not determine LOCAL_IP" - return 1 - } - echo "LOCAL_IP=$LOCAL_IP" >"$IP_FILE" - fi - export LOCAL_IP -} From 84437b0f5e2b73d7d488bd7c5fadda4d0973df2e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 18 Sep 2025 13:10:37 +0200 Subject: [PATCH 1014/1733] Update revision-bump.yml --- .github/workflows/revision-bump.yml | 99 +++++++++++++++++++++++------ 1 file changed, 81 insertions(+), 18 deletions(-) diff --git a/.github/workflows/revision-bump.yml b/.github/workflows/revision-bump.yml index 6e15d75b1..a39deccd8 100644 --- a/.github/workflows/revision-bump.yml +++ b/.github/workflows/revision-bump.yml @@ -2,28 +2,63 @@ name: Bump build.func Revision on: push: - branches: [ main ] + branches: + - main paths: - - 'misc/*.func' - - 'misc/*.sh' + - "misc/*.func" + - "misc/*.sh" + workflow_dispatch: jobs: bump-revision: + if: github.repository == 'community-scripts/ProxmoxVE' runs-on: ubuntu-latest - if: github.event.head_commit.author.name != 'github-actions[bot]' - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Detect misc changes + permissions: + contents: write + pull-requests: write + + steps: + - name: Generate token for PR + id: generate-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ vars.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + + - name: Generate token for auto-merge + id: generate-token-merge + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ secrets.APP_ID_APPROVE_AND_MERGE }} + private-key: ${{ secrets.APP_KEY_APPROVE_AND_MERGE }} + + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: Get changed files id: changes run: | git diff --name-only HEAD^ HEAD > changed_files.txt - echo "changed=$(cat changed_files.txt)" >> $GITHUB_OUTPUT + echo "Changed files:" + cat changed_files.txt - - name: Bump Revision if needed - if: contains(steps.changes.outputs.changed, 'misc/') && !contains(steps.changes.outputs.changed, 'misc/build.func') + - name: Skip if only build.func changed + id: skipcheck + run: | + if grep -q "^misc/build.func$" changed_files.txt && [ $(wc -l < changed_files.txt) -eq 1 ]; then + echo "skip=true" >> $GITHUB_ENV + else + echo "skip=false" >> $GITHUB_ENV + fi + + - name: Disable file mode changes + run: git config core.fileMode false + + - name: Bump build.func revision + if: env.skip == 'false' run: | REV_FILE=".build-revision" if [ ! -f "$REV_FILE" ]; then echo 0 > "$REV_FILE"; fi @@ -31,14 +66,42 @@ jobs: echo $REV_NUM > $REV_FILE SHORT_SHA=$(git rev-parse --short HEAD) REV_STR="Revision: r${REV_NUM} (git-${SHORT_SHA})" + + echo "Updating build.func with $REV_STR" sed -i "s/^# Revision:.*/# $REV_STR/" misc/build.func echo "REV_STR=$REV_STR" >> $GITHUB_ENV - - name: Commit & push revision - if: contains(steps.changes.outputs.changed, 'misc/') && !contains(steps.changes.outputs.changed, 'misc/build.func') - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "GitHub Actions" + git config --global user.email "github-actions[bot]@users.noreply.github.com" git add misc/build.func .build-revision git commit -m "chore: bump build.func to $REV_STR" - git push + + - name: Create PR + if: env.skip == 'false' + run: | + BRANCH_NAME="pr-build-revision-$(date +'%Y%m%d%H%M%S')" + git checkout -b $BRANCH_NAME + git push origin $BRANCH_NAME + + gh pr create --title "[core] bump build.func to $REV_STR" \ + --body "This PR bumps build.func revision because files in misc/ changed." \ + --head $BRANCH_NAME \ + --base main \ + --label "automated pr" + env: + GH_TOKEN: ${{ steps.generate-token.outputs.token }} + + - name: Approve PR and merge + if: env.skip == 'false' + env: + GH_TOKEN: ${{ steps.generate-token-merge.outputs.token }} + run: | + PR_NUMBER=$(gh pr list --head "${BRANCH_NAME}" --json number --jq '.[].number') + if [ -n "$PR_NUMBER" ]; then + gh pr review $PR_NUMBER --approve + gh pr merge $PR_NUMBER --squash --admin + fi + + - name: Skip log + if: env.skip == 'true' + run: echo "Only build.func changed – nothing to do." From e6931c7c4f83602eeb3aeb3e91fce8ea435c9bb5 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 18 Sep 2025 13:11:23 +0200 Subject: [PATCH 1015/1733] Update alpine-tools.func --- misc/alpine-tools.func | 1 + 1 file changed, 1 insertion(+) diff --git a/misc/alpine-tools.func b/misc/alpine-tools.func index 788e8e006..663b5f1d3 100644 --- a/misc/alpine-tools.func +++ b/misc/alpine-tools.func @@ -483,6 +483,7 @@ setup_composer() { return 1 } } + msg_ok "PHP CLI ready: $(php -v | head -n1)" fi if [ -x "$COMPOSER_BIN" ]; then From 3deefd6ffb505d8d3dd5ecca2eb2837d1b37b6a3 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 18 Sep 2025 13:12:43 +0200 Subject: [PATCH 1016/1733] test --- misc/tools.func | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 6b619c449..381fe85a8 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -232,7 +232,7 @@ function setup_postgresql() { # # Variables: # MARIADB_VERSION - MariaDB version to install (e.g. 10.11, latest) (default: latest) -# ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ test setup_mariadb() { local MARIADB_VERSION="${MARIADB_VERSION:-latest}" @@ -2024,7 +2024,7 @@ EOF check_for_gh_release() { local app="$1" local source="$2" - local pinned_version="${3:-}" # optional + local pinned_version="${3:-}" # optional local current_file="$HOME/.${app,,}" msg_info "Check for update: ${app}" @@ -2044,7 +2044,7 @@ check_for_gh_release() { fi # get latest release -local release + local release release=$(curl -fsSL "https://api.github.com/repos/${source}/releases/latest" | jq -r '.tag_name' | sed 's/^v//') @@ -2080,4 +2080,4 @@ local release msg_ok "${app} is up to date (v${release})" return 1 fi -} \ No newline at end of file +} From 5777cd72f40916b306b608d93d43e3a3c1e28716 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 18 Sep 2025 13:13:58 +0200 Subject: [PATCH 1017/1733] Update paths in revision-bump workflow --- .github/workflows/revision-bump.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/revision-bump.yml b/.github/workflows/revision-bump.yml index a39deccd8..9ff8cbb8f 100644 --- a/.github/workflows/revision-bump.yml +++ b/.github/workflows/revision-bump.yml @@ -5,8 +5,9 @@ on: branches: - main paths: - - "misc/*.func" - - "misc/*.sh" + - "misc/**" + paths-ignore: + - "misc/build.func" workflow_dispatch: jobs: From 2844eaf9766ce7475d9af7cdd2efae24de9affe4 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 18 Sep 2025 13:14:08 +0200 Subject: [PATCH 1018/1733] Update tools.func --- misc/tools.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/tools.func b/misc/tools.func index 381fe85a8..6f574c5d3 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -232,7 +232,7 @@ function setup_postgresql() { # # Variables: # MARIADB_VERSION - MariaDB version to install (e.g. 10.11, latest) (default: latest) -# ------------------------------------------------------------------------------ test +# ------------------------------------------------------------------------------ te setup_mariadb() { local MARIADB_VERSION="${MARIADB_VERSION:-latest}" From 306bfcced9a297a769797298fb173b43d6cef67b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 18 Sep 2025 13:15:13 +0200 Subject: [PATCH 1019/1733] Update revision-bump.yml to include misc paths --- .github/workflows/revision-bump.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/revision-bump.yml b/.github/workflows/revision-bump.yml index 9ff8cbb8f..5295a5c97 100644 --- a/.github/workflows/revision-bump.yml +++ b/.github/workflows/revision-bump.yml @@ -4,10 +4,10 @@ on: push: branches: - main - paths: - - "misc/**" paths-ignore: - "misc/build.func" + paths: + - "misc/**" workflow_dispatch: jobs: From 261a1cec7926d9e6eaf0acf16a8826b27fec225b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 18 Sep 2025 13:15:21 +0200 Subject: [PATCH 1020/1733] Update tools.func --- misc/tools.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/tools.func b/misc/tools.func index 6f574c5d3..2122bf7fd 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -232,7 +232,7 @@ function setup_postgresql() { # # Variables: # MARIADB_VERSION - MariaDB version to install (e.g. 10.11, latest) (default: latest) -# ------------------------------------------------------------------------------ te +# ------------------------------------------------------------------------------ tes setup_mariadb() { local MARIADB_VERSION="${MARIADB_VERSION:-latest}" From f4edff3d95ef499e8d9656b8a3191969a408c7e8 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 18 Sep 2025 13:16:31 +0200 Subject: [PATCH 1021/1733] Remove paths-ignore for revision bump workflow --- .github/workflows/revision-bump.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/revision-bump.yml b/.github/workflows/revision-bump.yml index 5295a5c97..60f8135ac 100644 --- a/.github/workflows/revision-bump.yml +++ b/.github/workflows/revision-bump.yml @@ -4,8 +4,6 @@ on: push: branches: - main - paths-ignore: - - "misc/build.func" paths: - "misc/**" workflow_dispatch: From f1f2d8895d11d9d1a8dc8e1fd85a69cec63ba58e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 18 Sep 2025 13:16:41 +0200 Subject: [PATCH 1022/1733] Update tools.func --- misc/tools.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/tools.func b/misc/tools.func index 6f574c5d3..2122bf7fd 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -232,7 +232,7 @@ function setup_postgresql() { # # Variables: # MARIADB_VERSION - MariaDB version to install (e.g. 10.11, latest) (default: latest) -# ------------------------------------------------------------------------------ te +# ------------------------------------------------------------------------------ tes setup_mariadb() { local MARIADB_VERSION="${MARIADB_VERSION:-latest}" From 8b45fe992b0f1e2dd58cbe7a27fd7c0d5b4dd26c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 18 Sep 2025 13:17:50 +0200 Subject: [PATCH 1023/1733] Remove paths-ignore from revision-bump workflow --- .github/workflows/revision-bump.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/revision-bump.yml b/.github/workflows/revision-bump.yml index 5295a5c97..60f8135ac 100644 --- a/.github/workflows/revision-bump.yml +++ b/.github/workflows/revision-bump.yml @@ -4,8 +4,6 @@ on: push: branches: - main - paths-ignore: - - "misc/build.func" paths: - "misc/**" workflow_dispatch: From 435afc65ccae276509cb1d2dc1cda5176cd26c94 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 18 Sep 2025 13:18:33 +0200 Subject: [PATCH 1024/1733] testi --- misc/tools.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/tools.func b/misc/tools.func index 2122bf7fd..8bbbac5d4 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -232,7 +232,7 @@ function setup_postgresql() { # # Variables: # MARIADB_VERSION - MariaDB version to install (e.g. 10.11, latest) (default: latest) -# ------------------------------------------------------------------------------ tes +# ------------------------------------------------------------------------------ testi setup_mariadb() { local MARIADB_VERSION="${MARIADB_VERSION:-latest}" From ebd5630cfa7bb2bf83d288ac8c7779fe3d71cb1f Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 18 Sep 2025 13:19:46 +0200 Subject: [PATCH 1025/1733] Update repository condition for revision bump workflow --- .github/workflows/revision-bump.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/revision-bump.yml b/.github/workflows/revision-bump.yml index 60f8135ac..65242968a 100644 --- a/.github/workflows/revision-bump.yml +++ b/.github/workflows/revision-bump.yml @@ -10,7 +10,7 @@ on: jobs: bump-revision: - if: github.repository == 'community-scripts/ProxmoxVE' + if: github.repository == 'community-scripts/ProxmoxVED' runs-on: ubuntu-latest permissions: From c618b893da3bd3fda51daaafe817f9989bc2533d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 18 Sep 2025 13:20:21 +0200 Subject: [PATCH 1026/1733] Update tools.func --- misc/tools.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/tools.func b/misc/tools.func index 8bbbac5d4..381fe85a8 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -232,7 +232,7 @@ function setup_postgresql() { # # Variables: # MARIADB_VERSION - MariaDB version to install (e.g. 10.11, latest) (default: latest) -# ------------------------------------------------------------------------------ testi +# ------------------------------------------------------------------------------ test setup_mariadb() { local MARIADB_VERSION="${MARIADB_VERSION:-latest}" From 5905a392180ca9f18648d3f62d5eb8832d842575 Mon Sep 17 00:00:00 2001 From: cobaltgit Date: Thu, 18 Sep 2025 17:40:53 +0100 Subject: [PATCH 1027/1733] ct: add alpine-ntfy todo: add json, change port to 80 --- ct/alpine-ntfy.sh | 43 ++++++++++++++++++++++++++++++++++ ct/ntfy.sh | 1 + install/alpine-ntfy-install.sh | 24 +++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 ct/alpine-ntfy.sh create mode 100644 ct/ntfy.sh create mode 100644 install/alpine-ntfy-install.sh diff --git a/ct/alpine-ntfy.sh b/ct/alpine-ntfy.sh new file mode 100644 index 000000000..2fdba6796 --- /dev/null +++ b/ct/alpine-ntfy.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: cobalt (cobaltgit) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://ntfy.sh/ + +APP="ntfy" +var_tags="${var_tags:-notification}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-256}" +var_disk="${var_disk:-2}" +var_os="${var_os:-alpine}" +var_version="${var_version:-3.22}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +init_error_traps + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /var ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating $APP LXC" + $STD apk -U upgrade + msg_ok "Updated $APP LXC" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" diff --git a/ct/ntfy.sh b/ct/ntfy.sh new file mode 100644 index 000000000..c2c61a728 --- /dev/null +++ b/ct/ntfy.sh @@ -0,0 +1 @@ +# placeholder for CI/CD to run diff --git a/install/alpine-ntfy-install.sh b/install/alpine-ntfy-install.sh new file mode 100644 index 000000000..85e123e79 --- /dev/null +++ b/install/alpine-ntfy-install.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: cobalt (cobaltgit) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://ntfy.sh/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +init_error_traps +setting_up_container +network_check +update_os + +msg_info "Installing ntfy" +$STD apk add --no-cache ntfy ntfy-openrc +$STD rc-update add ntfy default +$STD service ntfy start +msg_ok "Installed ntfy" + +motd_ssh +customize + From 69e69330ddabab8b0508fa1c4f56b9b7366269f1 Mon Sep 17 00:00:00 2001 From: cobaltgit Date: Thu, 18 Sep 2025 17:42:22 +0100 Subject: [PATCH 1028/1733] alpine-ntfy: add json --- frontend/public/json/ntfy.json | 47 ++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 frontend/public/json/ntfy.json diff --git a/frontend/public/json/ntfy.json b/frontend/public/json/ntfy.json new file mode 100644 index 000000000..0bc32bbcc --- /dev/null +++ b/frontend/public/json/ntfy.json @@ -0,0 +1,47 @@ +{ + "name": "ntfy", + "slug": "ntfy", + "categories": [ + 19 + ], + "date_created": "2024-05-02", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 80, + "documentation": "https://docs.ntfy.sh/", + "website": "https://ntfy.sh/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/ntfy.webp", + "config_path": "/etc/ntfy/server.yml", + "description": "ntfy (pronounced notify) is a simple HTTP-based pub-sub notification service. It allows you to send notifications to your phone or desktop via scripts from any computer, and/or using a REST API. It's infinitely flexible, and 100% free software.", + "install_methods": [ + { + "type": "default", + "script": "ct/ntfy.sh", + "resources": { + "cpu": 1, + "ram": 512, + "hdd": 2, + "os": "debian", + "version": "12" + } + }, + { + "type": "alpine", + "script": "ct/alpine-ntfy.sh", + "resources": { + "cpu": 1, + "ram": 256, + "hdd": 2, + "os": "alpine", + "version": "3.22" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} + From 87456e68d83da69f83a317bdfeb0b3618002942a Mon Sep 17 00:00:00 2001 From: cobaltgit Date: Thu, 18 Sep 2025 17:58:49 +0100 Subject: [PATCH 1029/1733] alpine-ntfy: use setcap and bind to 80 --- ct/alpine-ntfy.sh | 3 ++- install/alpine-ntfy-install.sh | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/ct/alpine-ntfy.sh b/ct/alpine-ntfy.sh index 2fdba6796..118924908 100644 --- a/ct/alpine-ntfy.sh +++ b/ct/alpine-ntfy.sh @@ -23,12 +23,13 @@ function update_script() { header_info check_container_storage check_container_resources - if [[ ! -d /var ]]; then + if [[ ! -d /etc/ntfy ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating $APP LXC" $STD apk -U upgrade + setcap 'cap_net_bind_service=+ep' /usr/bin/ntfy msg_ok "Updated $APP LXC" exit } diff --git a/install/alpine-ntfy-install.sh b/install/alpine-ntfy-install.sh index 85e123e79..0369e8966 100644 --- a/install/alpine-ntfy-install.sh +++ b/install/alpine-ntfy-install.sh @@ -14,7 +14,9 @@ network_check update_os msg_info "Installing ntfy" -$STD apk add --no-cache ntfy ntfy-openrc +$STD apk add --no-cache ntfy ntfy-openrc libcap +sed -i '/^listen-http/s/^\(.*\)$/#\1\n/' /etc/ntfy/server.yml # listen on port 80 +setcap 'cap_net_bind_service=+ep' /usr/bin/ntfy # work around permission denied error when binding to :80 $STD rc-update add ntfy default $STD service ntfy start msg_ok "Installed ntfy" From 3a5e03f275614cb0c822a5ecc1129b7be7f817ba Mon Sep 17 00:00:00 2001 From: cobaltgit Date: Thu, 18 Sep 2025 18:00:08 +0100 Subject: [PATCH 1030/1733] alpine-ntfy: rename app to alpine-ntfy --- ct/alpine-ntfy.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/alpine-ntfy.sh b/ct/alpine-ntfy.sh index 118924908..f1cf340cd 100644 --- a/ct/alpine-ntfy.sh +++ b/ct/alpine-ntfy.sh @@ -5,7 +5,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://ntfy.sh/ -APP="ntfy" +APP="Alpine-ntfy" var_tags="${var_tags:-notification}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-256}" From 9c3ff0fda95b2ea93ce411dd77b679d665c7a821 Mon Sep 17 00:00:00 2001 From: cobaltgit Date: Thu, 18 Sep 2025 18:00:58 +0100 Subject: [PATCH 1031/1733] alpine-ntfy: restart ntfy after update --- ct/alpine-ntfy.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ct/alpine-ntfy.sh b/ct/alpine-ntfy.sh index f1cf340cd..94e47f77a 100644 --- a/ct/alpine-ntfy.sh +++ b/ct/alpine-ntfy.sh @@ -31,6 +31,10 @@ function update_script() { $STD apk -U upgrade setcap 'cap_net_bind_service=+ep' /usr/bin/ntfy msg_ok "Updated $APP LXC" + + msg_info "Restarting ntfy" + rc-service ntfy restart + msg_ok "Restarted ntfy" exit } From d1de6051945f81773d98cdb103b65bb1977a06b5 Mon Sep 17 00:00:00 2001 From: cobaltgit Date: Thu, 18 Sep 2025 18:02:03 +0100 Subject: [PATCH 1032/1733] alpine-caddy: revert thingy, forgot to change branch n allat --- frontend/public/json/caddy.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/public/json/caddy.json b/frontend/public/json/caddy.json index 607a9f7e6..0f59a88de 100644 --- a/frontend/public/json/caddy.json +++ b/frontend/public/json/caddy.json @@ -1,6 +1,6 @@ { - "name": "Caddy", - "slug": "caddy", + "name": "Alpine-Caddy", + "slug": "alpine-caddy", "categories": [ 21 ], From a17c64f39d9f677386f413007bf263d2519cd9aa Mon Sep 17 00:00:00 2001 From: cobaltgit Date: Thu, 18 Sep 2025 18:06:54 +0100 Subject: [PATCH 1033/1733] alpine-caddy: remove commands, listen on 8080 for now? --- install/alpine-ntfy-install.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/install/alpine-ntfy-install.sh b/install/alpine-ntfy-install.sh index 0369e8966..85e123e79 100644 --- a/install/alpine-ntfy-install.sh +++ b/install/alpine-ntfy-install.sh @@ -14,9 +14,7 @@ network_check update_os msg_info "Installing ntfy" -$STD apk add --no-cache ntfy ntfy-openrc libcap -sed -i '/^listen-http/s/^\(.*\)$/#\1\n/' /etc/ntfy/server.yml # listen on port 80 -setcap 'cap_net_bind_service=+ep' /usr/bin/ntfy # work around permission denied error when binding to :80 +$STD apk add --no-cache ntfy ntfy-openrc $STD rc-update add ntfy default $STD service ntfy start msg_ok "Installed ntfy" From 6de1ce5463e8854df9cf0f0fe22c2afcc536314c Mon Sep 17 00:00:00 2001 From: cobaltgit Date: Thu, 18 Sep 2025 18:08:10 +0100 Subject: [PATCH 1034/1733] alpine-ntfy: revert, and lament using the wrong commit message HAH ohh it was comments, not commands --- install/alpine-ntfy-install.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/install/alpine-ntfy-install.sh b/install/alpine-ntfy-install.sh index 85e123e79..4fc3f89c5 100644 --- a/install/alpine-ntfy-install.sh +++ b/install/alpine-ntfy-install.sh @@ -14,7 +14,9 @@ network_check update_os msg_info "Installing ntfy" -$STD apk add --no-cache ntfy ntfy-openrc +$STD apk add --no-cache ntfy ntfy-openrc libcap +sed -i '/^listen-http/s/^\(.*\)$/#\1\n/' /etc/ntfy/server.yml +setcap 'cap_net_bind_service=+ep' /usr/bin/ntfy $STD rc-update add ntfy default $STD service ntfy start msg_ok "Installed ntfy" From b2239adf2f7edddda2e560395e429559b37be26e Mon Sep 17 00:00:00 2001 From: cobaltgit Date: Thu, 18 Sep 2025 18:17:22 +0100 Subject: [PATCH 1035/1733] alpine-ntfy: add header --- ct/headers/alpine-ntfy | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/alpine-ntfy diff --git a/ct/headers/alpine-ntfy b/ct/headers/alpine-ntfy new file mode 100644 index 000000000..bc4164342 --- /dev/null +++ b/ct/headers/alpine-ntfy @@ -0,0 +1,6 @@ + ___ __ _ __ ____ + / | / /___ (_)___ ___ ____ / /_/ __/_ __ + / /| | / / __ \/ / __ \/ _ \______/ __ \/ __/ /_/ / / / + / ___ |/ / /_/ / / / / / __/_____/ / / / /_/ __/ /_/ / +/_/ |_/_/ .___/_/_/ /_/\___/ /_/ /_/\__/_/ \__, / + /_/ /____/ From e2c8e71c5535fb15c6c6fed06b46c3682c2ae3f1 Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Thu, 18 Sep 2025 19:24:52 +0200 Subject: [PATCH 1036/1733] Update source URL for build function --- ct/warracker.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/warracker.sh b/ct/warracker.sh index 01ca7eea6..d4707ac7e 100644 --- a/ct/warracker.sh +++ b/ct/warracker.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/bvdberg01/ProxmoxVED/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: BvdBerg01 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE From 7699128a3010f06a3abad124a8599e58ca77d90c Mon Sep 17 00:00:00 2001 From: cobaltgit Date: Thu, 18 Sep 2025 19:06:16 +0100 Subject: [PATCH 1037/1733] alpine-ntfy: revert header --- ct/headers/alpine-ntfy | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 ct/headers/alpine-ntfy diff --git a/ct/headers/alpine-ntfy b/ct/headers/alpine-ntfy deleted file mode 100644 index bc4164342..000000000 --- a/ct/headers/alpine-ntfy +++ /dev/null @@ -1,6 +0,0 @@ - ___ __ _ __ ____ - / | / /___ (_)___ ___ ____ / /_/ __/_ __ - / /| | / / __ \/ / __ \/ _ \______/ __ \/ __/ /_/ / / / - / ___ |/ / /_/ / / / / / __/_____/ / / / /_/ __/ /_/ / -/_/ |_/_/ .___/_/_/ /_/\___/ /_/ /_/\__/_/ \__, / - /_/ /____/ From afecb023ccfe6308ee74c538a6980f2c929c13ef Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 19 Sep 2025 08:23:41 +0200 Subject: [PATCH 1038/1733] Update build.func --- misc/build.func | 148 ++++++++++++++++++++++++------------------------ 1 file changed, 74 insertions(+), 74 deletions(-) diff --git a/misc/build.func b/misc/build.func index 77b452d7e..c9c5abc89 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2,7 +2,7 @@ # Copyright (c) 2021-2025 community-scripts ORG # Author: tteck (tteckster) | MickLesk | michelroegl-brunner # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Revision: +# Revision: # ------------------------------------------------------------------------------ # variables() @@ -33,93 +33,93 @@ variables() { # - Local cache: /usr/local/community-scripts/core # ----------------------------------------------------------------------------- -FUNC_DIR="/usr/local/community-scripts/core" -mkdir -p "$FUNC_DIR" +# FUNC_DIR="/usr/local/community-scripts/core" +# mkdir -p "$FUNC_DIR" -BUILD_URL="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func" -BUILD_REV="$FUNC_DIR/build.rev" +# BUILD_URL="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func" +# BUILD_REV="$FUNC_DIR/build.rev" -# --- Step 1: fetch build.func content once, compute hash --- -build_content="$(curl -fsSL "$BUILD_URL")" || { - echo "❌ Failed to fetch build.func" - exit 1 -} +# # --- Step 1: fetch build.func content once, compute hash --- +# build_content="$(curl -fsSL "$BUILD_URL")" || { +# echo "❌ Failed to fetch build.func" +# exit 1 +# } -newhash=$(printf "%s" "$build_content" | sha256sum | awk '{print $1}') -oldhash=$(cat "$BUILD_REV" 2>/dev/null || echo "") +# newhash=$(printf "%s" "$build_content" | sha256sum | awk '{print $1}') +# oldhash=$(cat "$BUILD_REV" 2>/dev/null || echo "") -# --- Step 2: if build.func changed, offer update for core files --- -if [ "$newhash" != "$oldhash" ]; then - echo "⚠️ build.func changed!" +# # --- Step 2: if build.func changed, offer update for core files --- +# if [ "$newhash" != "$oldhash" ]; then +# echo "⚠️ build.func changed!" - while true; do - read -rp "Refresh local core files? [y/N/diff]: " ans - case "$ans" in - [Yy]*) - echo "$newhash" >"$BUILD_REV" +# while true; do +# read -rp "Refresh local core files? [y/N/diff]: " ans +# case "$ans" in +# [Yy]*) +# echo "$newhash" >"$BUILD_REV" - update_func_file() { - local file="$1" - local url="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/$file" - local local_path="$FUNC_DIR/$file" +# update_func_file() { +# local file="$1" +# local url="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/$file" +# local local_path="$FUNC_DIR/$file" - echo "⬇️ Downloading $file ..." - curl -fsSL "$url" -o "$local_path" || { - echo "❌ Failed to fetch $file" - exit 1 - } - echo "✔️ Updated $file" - } +# echo "⬇️ Downloading $file ..." +# curl -fsSL "$url" -o "$local_path" || { +# echo "❌ Failed to fetch $file" +# exit 1 +# } +# echo "✔️ Updated $file" +# } - update_func_file core.func - update_func_file error_handler.func - update_func_file tools.func - break - ;; - [Dd]*) - for file in core.func error_handler.func tools.func; do - local_path="$FUNC_DIR/$file" - url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/$file" - remote_tmp="$(mktemp)" +# update_func_file core.func +# update_func_file error_handler.func +# update_func_file tools.func +# break +# ;; +# [Dd]*) +# for file in core.func error_handler.func tools.func; do +# local_path="$FUNC_DIR/$file" +# url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/$file" +# remote_tmp="$(mktemp)" - curl -fsSL "$url" -o "$remote_tmp" || continue +# curl -fsSL "$url" -o "$remote_tmp" || continue - if [ -f "$local_path" ]; then - echo "🔍 Diff for $file:" - diff -u "$local_path" "$remote_tmp" || echo "(no differences)" - else - echo "📦 New file $file will be installed" - fi +# if [ -f "$local_path" ]; then +# echo "🔍 Diff for $file:" +# diff -u "$local_path" "$remote_tmp" || echo "(no differences)" +# else +# echo "📦 New file $file will be installed" +# fi - rm -f "$remote_tmp" - done - ;; - *) - echo "❌ Skipped updating local core files" - break - ;; - esac - done -else - echo "✔️ build.func unchanged → using existing local core files" -fi +# rm -f "$remote_tmp" +# done +# ;; +# *) +# echo "❌ Skipped updating local core files" +# break +# ;; +# esac +# done +# else +# echo "✔️ build.func unchanged → using existing local core files" +# fi -# --- Step 3: always source local versions of the core files --- -source "$FUNC_DIR/core.func" -source "$FUNC_DIR/error_handler.func" -source "$FUNC_DIR/tools.func" +# # --- Step 3: always source local versions of the core files --- +# source "$FUNC_DIR/core.func" +# source "$FUNC_DIR/error_handler.func" +# source "$FUNC_DIR/tools.func" -# --- Step 4: finally, source build.func directly from memory --- -# (no tmp file needed) -source <(printf "%s" "$build_content") +# # --- Step 4: finally, source build.func directly from memory --- +# # (no tmp file needed) +# source <(printf "%s" "$build_content") -# ------------------------------------------------------------------------------ -# Load core + error handler functions from community-scripts repo -# -# - Prefer curl if available, fallback to wget -# - Load: core.func, error_handler.func, api.func -# - Initialize error traps after loading -# ------------------------------------------------------------------------------ +# # ------------------------------------------------------------------------------ +# # Load core + error handler functions from community-scripts repo +# # +# # - Prefer curl if available, fallback to wget +# # - Load: core.func, error_handler.func, api.func +# # - Initialize error traps after loading +# # ------------------------------------------------------------------------------ source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) From 271665dfefdeb35a3b25b65d1776288168de1323 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 19 Sep 2025 08:26:22 +0200 Subject: [PATCH 1039/1733] Update build.func --- misc/build.func | 149 ++++++++++++++++++++++++------------------------ 1 file changed, 74 insertions(+), 75 deletions(-) diff --git a/misc/build.func b/misc/build.func index c9c5abc89..3e58389b9 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2,7 +2,7 @@ # Copyright (c) 2021-2025 community-scripts ORG # Author: tteck (tteckster) | MickLesk | michelroegl-brunner # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Revision: +# Revision: 1 # ------------------------------------------------------------------------------ # variables() @@ -25,7 +25,6 @@ variables() { #CT_TYPE=${var_unprivileged:-$CT_TYPE} } -#!/usr/bin/env bash # ----------------------------------------------------------------------------- # Community-Scripts bootstrap loader # - Always sources build.func from remote @@ -33,93 +32,93 @@ variables() { # - Local cache: /usr/local/community-scripts/core # ----------------------------------------------------------------------------- -# FUNC_DIR="/usr/local/community-scripts/core" -# mkdir -p "$FUNC_DIR" +FUNC_DIR="/usr/local/community-scripts/core" +mkdir -p "$FUNC_DIR" -# BUILD_URL="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func" -# BUILD_REV="$FUNC_DIR/build.rev" +BUILD_URL="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func" +BUILD_REV="$FUNC_DIR/build.rev" -# # --- Step 1: fetch build.func content once, compute hash --- -# build_content="$(curl -fsSL "$BUILD_URL")" || { -# echo "❌ Failed to fetch build.func" -# exit 1 -# } +# --- Step 1: fetch build.func content once, compute hash --- +build_content="$(curl -fsSL "$BUILD_URL")" || { + echo "❌ Failed to fetch build.func" + exit 1 +} -# newhash=$(printf "%s" "$build_content" | sha256sum | awk '{print $1}') -# oldhash=$(cat "$BUILD_REV" 2>/dev/null || echo "") +newhash=$(printf "%s" "$build_content" | sha256sum | awk '{print $1}') +oldhash=$(cat "$BUILD_REV" 2>/dev/null || echo "") -# # --- Step 2: if build.func changed, offer update for core files --- -# if [ "$newhash" != "$oldhash" ]; then -# echo "⚠️ build.func changed!" +# --- Step 2: if build.func changed, offer update for core files --- +if [ "$newhash" != "$oldhash" ]; then + echo "⚠️ build.func changed!" -# while true; do -# read -rp "Refresh local core files? [y/N/diff]: " ans -# case "$ans" in -# [Yy]*) -# echo "$newhash" >"$BUILD_REV" + while true; do + read -rp "Refresh local core files? [y/N/diff]: " ans + case "$ans" in + [Yy]*) + echo "$newhash" >"$BUILD_REV" -# update_func_file() { -# local file="$1" -# local url="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/$file" -# local local_path="$FUNC_DIR/$file" + update_func_file() { + local file="$1" + local url="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/$file" + local local_path="$FUNC_DIR/$file" -# echo "⬇️ Downloading $file ..." -# curl -fsSL "$url" -o "$local_path" || { -# echo "❌ Failed to fetch $file" -# exit 1 -# } -# echo "✔️ Updated $file" -# } + echo "⬇️ Downloading $file ..." + curl -fsSL "$url" -o "$local_path" || { + echo "❌ Failed to fetch $file" + exit 1 + } + echo "✔️ Updated $file" + } -# update_func_file core.func -# update_func_file error_handler.func -# update_func_file tools.func -# break -# ;; -# [Dd]*) -# for file in core.func error_handler.func tools.func; do -# local_path="$FUNC_DIR/$file" -# url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/$file" -# remote_tmp="$(mktemp)" + update_func_file core.func + update_func_file error_handler.func + update_func_file tools.func + break + ;; + [Dd]*) + for file in core.func error_handler.func tools.func; do + local_path="$FUNC_DIR/$file" + url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/$file" + remote_tmp="$(mktemp)" -# curl -fsSL "$url" -o "$remote_tmp" || continue + curl -fsSL "$url" -o "$remote_tmp" || continue -# if [ -f "$local_path" ]; then -# echo "🔍 Diff for $file:" -# diff -u "$local_path" "$remote_tmp" || echo "(no differences)" -# else -# echo "📦 New file $file will be installed" -# fi + if [ -f "$local_path" ]; then + echo "🔍 Diff for $file:" + diff -u "$local_path" "$remote_tmp" || echo "(no differences)" + else + echo "📦 New file $file will be installed" + fi -# rm -f "$remote_tmp" -# done -# ;; -# *) -# echo "❌ Skipped updating local core files" -# break -# ;; -# esac -# done -# else -# echo "✔️ build.func unchanged → using existing local core files" -# fi + rm -f "$remote_tmp" + done + ;; + *) + echo "❌ Skipped updating local core files" + break + ;; + esac + done +else + echo "✔️ build.func unchanged → using existing local core files" +fi -# # --- Step 3: always source local versions of the core files --- -# source "$FUNC_DIR/core.func" -# source "$FUNC_DIR/error_handler.func" -# source "$FUNC_DIR/tools.func" +# --- Step 3: always source local versions of the core files --- +source "$FUNC_DIR/core.func" +source "$FUNC_DIR/error_handler.func" +source "$FUNC_DIR/tools.func" -# # --- Step 4: finally, source build.func directly from memory --- -# # (no tmp file needed) -# source <(printf "%s" "$build_content") +# --- Step 4: finally, source build.func directly from memory --- +# (no tmp file needed) +source <(printf "%s" "$build_content") -# # ------------------------------------------------------------------------------ -# # Load core + error handler functions from community-scripts repo -# # -# # - Prefer curl if available, fallback to wget -# # - Load: core.func, error_handler.func, api.func -# # - Initialize error traps after loading -# # ------------------------------------------------------------------------------ +# ------------------------------------------------------------------------------ +# Load core + error handler functions from community-scripts repo +# +# - Prefer curl if available, fallback to wget +# - Load: core.func, error_handler.func, api.func +# - Initialize error traps after loading +# ------------------------------------------------------------------------------ source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) From 864e63c04d70c2e6452f3f575fdbf7e5e0ed0869 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 19 Sep 2025 08:27:08 +0200 Subject: [PATCH 1040/1733] Update build.func --- misc/build.func | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index 3e58389b9..46c9203c2 100644 --- a/misc/build.func +++ b/misc/build.func @@ -37,6 +37,7 @@ mkdir -p "$FUNC_DIR" BUILD_URL="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func" BUILD_REV="$FUNC_DIR/build.rev" +DEVMODE="${DEVMODE:-no}" # --- Step 1: fetch build.func content once, compute hash --- build_content="$(curl -fsSL "$BUILD_URL")" || { @@ -100,7 +101,9 @@ if [ "$newhash" != "$oldhash" ]; then esac done else - echo "✔️ build.func unchanged → using existing local core files" + if [ "$DEVMODE" != "yes" ]; then + echo "✔️ build.func unchanged → using existing local core files" + fi fi # --- Step 3: always source local versions of the core files --- From 17cb74a8f0c39dd775947b6b1b1a7913ce0ed0fa Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 19 Sep 2025 08:28:10 +0200 Subject: [PATCH 1041/1733] Update build.func --- misc/build.func | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/misc/build.func b/misc/build.func index 46c9203c2..1802ab0d2 100644 --- a/misc/build.func +++ b/misc/build.func @@ -106,6 +106,11 @@ else fi fi +if [ -n "${_COMMUNITY_SCRIPTS_LOADER:-}" ]; then + return 0 2>/dev/null || exit 0 +fi +_COMMUNITY_SCRIPTS_LOADER=1 + # --- Step 3: always source local versions of the core files --- source "$FUNC_DIR/core.func" source "$FUNC_DIR/error_handler.func" From fe3de0715786c7cb991b72be3a34aeb29a7de083 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 19 Sep 2025 08:41:24 +0200 Subject: [PATCH 1042/1733] Add clean install option to fetch_and_deploy_gh_release Introduces a CLEAN_INSTALL environment variable to optionally remove all files in the target directory before extracting a GitHub release. This ensures a clean deployment when required. --- misc/tools.func | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/misc/tools.func b/misc/tools.func index 381fe85a8..88950d690 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -858,6 +858,9 @@ function fetch_and_deploy_gh_release() { msg_info "Fetching GitHub release: $app ($version)" + local clean_install=false + [[ -n "${CLEAN_INSTALL:-}" && "$CLEAN_INSTALL" == "1" ]] && clean_install=true + ### Tarball Mode ### if [[ "$mode" == "tarball" || "$mode" == "source" ]]; then url=$(echo "$json" | jq -r '.tarball_url // empty') @@ -871,6 +874,10 @@ function fetch_and_deploy_gh_release() { } mkdir -p "$target" + if [[ "${CLEAN_INSTALL:-0}" == "1" ]]; then + rm -rf "${target:?}/"* + fi + tar -xzf "$tmpdir/$filename" -C "$tmpdir" local unpack_dir unpack_dir=$(find "$tmpdir" -mindepth 1 -maxdepth 1 -type d | head -n1) @@ -940,7 +947,7 @@ function fetch_and_deploy_gh_release() { } } - ### Prebuild Mode ### + ### Prebuild Mode ### elif [[ "$mode" == "prebuild" ]]; then local pattern="${6%\"}" pattern="${pattern#\"}" @@ -977,6 +984,9 @@ function fetch_and_deploy_gh_release() { local unpack_tmp unpack_tmp=$(mktemp -d) mkdir -p "$target" + if [[ "${CLEAN_INSTALL:-0}" == "1" ]]; then + rm -rf "${target:?}/"* + fi if [[ "$filename" == *.zip ]]; then if ! command -v unzip &>/dev/null; then From b840a8aa325250f3c3e4208eec74c294fff582cc Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 19 Sep 2025 08:53:49 +0200 Subject: [PATCH 1043/1733] Replace init_error_traps with catch_errors in scripts Replaces all occurrences of the 'init_error_traps' function with 'catch_errors' across container and install scripts for consistency in error handling. Also adjusts indentation and formatting in some scripts for improved readability. --- ct/alpine-caddy.sh | 30 +-- ct/alpine.sh | 2 +- ct/debian.sh | 2 +- ct/deferred/alpine-homarr.sh | 2 +- ct/deferred/ampache.sh | 2 +- ct/deferred/ghostfolio.sh | 2 +- ct/deferred/hoodik.sh | 2 +- ct/deferred/jumpserver.sh | 2 +- ct/deferred/kasm.sh | 2 +- ct/deferred/koel.sh | 2 +- ct/deferred/librespeed.sh | 2 +- ct/deferred/netbootxyz.sh | 2 +- ct/deferred/nginxproxymanager.sh | 2 +- ct/deferred/ocis.sh | 2 +- ct/deferred/openwebui.sh | 2 +- ct/deferred/pixelfed.sh | 2 +- ct/deferred/polaris.sh | 2 +- ct/deferred/roundcubemail.sh | 2 +- ct/deferred/squirrelserversmanager.sh | 2 +- ct/deferred/vikunja.sh | 2 +- ct/dispatcharr.sh | 2 +- ct/docspell.sh | 2 +- ct/ente.sh | 2 +- ct/freepbx.sh | 2 +- ct/frigate.sh | 2 +- ct/garmin-grafana.sh | 2 +- ct/ghostfolio.sh | 2 +- ct/globaleaks.sh | 24 +- ct/hanko.sh | 2 +- ct/joplin-server.sh | 2 +- ct/kanba.sh | 2 +- ct/leantime.sh | 2 +- ct/librenms.sh | 2 +- ct/livebook.sh | 2 +- ct/manyfold.sh | 2 +- ct/maxun.sh | 2 +- ct/notesnook.sh | 2 +- ct/npmplus.sh | 2 +- ct/opencloud.sh | 2 +- ct/postiz.sh | 2 +- ct/proxmox-datacenter-manager.sh | 2 +- ct/romm.sh | 2 +- ct/rybbit.sh | 2 +- ct/scraparr.sh | 2 +- ct/signoz.sh | 2 +- ct/tunarr.sh | 2 +- ct/ubuntu.sh | 2 +- ct/viseron.sh | 2 +- ct/wallabag.sh | 2 +- ct/warracker.sh | 2 +- install/alpine-caddy-install.sh | 24 +- install/alpine-garage-install.sh | 2 +- install/alpine-install.sh | 2 +- install/debian-install.sh | 2 +- install/deferred/ampache-install.sh | 2 +- install/deferred/freepbx-install_backup.sh | 2 +- install/deferred/funkwhale-install.sh | 2 +- install/deferred/ghostfolio-install.sh | 2 +- install/deferred/hoodik-install.sh | 2 +- install/deferred/jumpserver-install.sh | 2 +- install/deferred/kasm-install.sh | 2 +- install/deferred/koel-install.sh | 2 +- install/deferred/netbootxyz-install.sh | 2 +- install/deferred/nginxproxymanager-install.sh | 2 +- install/deferred/nimbus-install.sh | 2 +- install/deferred/ocis-install.sh | 2 +- install/deferred/openwebui-install.sh | 2 +- install/deferred/pixelfed-install.sh | 2 +- install/deferred/polaris-install.sh | 2 +- install/deferred/roundcubemail-install.sh | 2 +- .../squirrelserversmanager-install.sh | 2 +- install/deferred/timescaledb-install.sh | 2 +- install/deferred/vikunja-install.sh | 2 +- install/dispatcharr-install.sh | 2 +- install/docspell-install.sh | 2 +- install/ente-install.sh | 2 +- install/freepbx-install.sh | 2 +- install/frigate-install.sh | 2 +- install/garmin-grafana-install.sh | 2 +- install/ghostfolio-install.sh | 2 +- install/globaleaks-install.sh | 2 +- install/hanko-install.sh | 2 +- install/joplin-server-install.sh | 2 +- install/kanba-install.sh | 2 +- install/leantime-install.sh | 2 +- install/librenms-install.sh | 2 +- install/livebook-install.sh | 2 +- install/manyfold-install.sh | 2 +- install/maxun-install.sh | 2 +- install/notesnook-install.sh | 2 +- install/npmplus-install.sh | 2 +- install/opencloud-install.sh | 2 +- install/postiz-install.sh | 2 +- install/proxmox-datacenter-manager-install.sh | 2 +- install/romm-install.sh | 2 +- install/rybbit-install.sh | 2 +- install/scraparr-install.sh | 2 +- install/signoz-install.sh | 2 +- install/tunarr-install.sh | 24 +- install/ubuntu-install.sh | 2 +- install/viseron-install.sh | 2 +- install/wallabag-install.sh | 2 +- install/warracker-install.sh | 33 ++- misc/alpine-install.func | 204 +++++++------- misc/build.func | 4 +- misc/error_handler.func | 208 +++++++-------- misc/install.func | 248 +++++++++--------- 107 files changed, 497 insertions(+), 498 deletions(-) diff --git a/ct/alpine-caddy.sh b/ct/alpine-caddy.sh index 75cfe6366..89fe099d3 100644 --- a/ct/alpine-caddy.sh +++ b/ct/alpine-caddy.sh @@ -17,24 +17,24 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /etc/caddy ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - msg_info "Updating $APP LXC" - $STD apk -U upgrade - msg_ok "Updated $APP LXC" + header_info + check_container_storage + check_container_resources + if [[ ! -d /etc/caddy ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating $APP LXC" + $STD apk -U upgrade + msg_ok "Updated $APP LXC" - msg_info "Restarting Caddy" - rc-service caddy restart - msg_ok "Restarted Caddy" - exit + msg_info "Restarting Caddy" + rc-service caddy restart + msg_ok "Restarted Caddy" + exit } start diff --git a/ct/alpine.sh b/ct/alpine.sh index 4940d6fc5..ea946a60e 100644 --- a/ct/alpine.sh +++ b/ct/alpine.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/debian.sh b/ct/debian.sh index dad596833..a7ed93b3d 100644 --- a/ct/debian.sh +++ b/ct/debian.sh @@ -19,7 +19,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/deferred/alpine-homarr.sh b/ct/deferred/alpine-homarr.sh index def7cca2e..c78398d65 100644 --- a/ct/deferred/alpine-homarr.sh +++ b/ct/deferred/alpine-homarr.sh @@ -18,7 +18,7 @@ header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/deferred/ampache.sh b/ct/deferred/ampache.sh index ccf300e22..ada18f098 100644 --- a/ct/deferred/ampache.sh +++ b/ct/deferred/ampache.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/deferred/ghostfolio.sh b/ct/deferred/ghostfolio.sh index 360390927..56002d405 100644 --- a/ct/deferred/ghostfolio.sh +++ b/ct/deferred/ghostfolio.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/deferred/hoodik.sh b/ct/deferred/hoodik.sh index 1fee14b13..0e7597b14 100644 --- a/ct/deferred/hoodik.sh +++ b/ct/deferred/hoodik.sh @@ -18,7 +18,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/deferred/jumpserver.sh b/ct/deferred/jumpserver.sh index 1658a3683..f512024c9 100644 --- a/ct/deferred/jumpserver.sh +++ b/ct/deferred/jumpserver.sh @@ -17,7 +17,7 @@ var_unprivileged="1" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/deferred/kasm.sh b/ct/deferred/kasm.sh index ba78e8d57..565c71fbd 100644 --- a/ct/deferred/kasm.sh +++ b/ct/deferred/kasm.sh @@ -19,7 +19,7 @@ var_tun="${var_tun:-yes}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/deferred/koel.sh b/ct/deferred/koel.sh index b0c12d864..a4b6ea7e8 100644 --- a/ct/deferred/koel.sh +++ b/ct/deferred/koel.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/deferred/librespeed.sh b/ct/deferred/librespeed.sh index 4861a5688..95650828b 100644 --- a/ct/deferred/librespeed.sh +++ b/ct/deferred/librespeed.sh @@ -17,7 +17,7 @@ var_unprivileged="1" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/deferred/netbootxyz.sh b/ct/deferred/netbootxyz.sh index 60832ff44..201073af9 100644 --- a/ct/deferred/netbootxyz.sh +++ b/ct/deferred/netbootxyz.sh @@ -26,7 +26,7 @@ var_os="${var_os:-debian}" var_version="${var_version:-12}" variables color -init_error_traps +catch_errors function default_settings() { CT_TYPE="1" diff --git a/ct/deferred/nginxproxymanager.sh b/ct/deferred/nginxproxymanager.sh index 41f1aecdf..b1ff024d7 100644 --- a/ct/deferred/nginxproxymanager.sh +++ b/ct/deferred/nginxproxymanager.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/deferred/ocis.sh b/ct/deferred/ocis.sh index 44f5f1aec..167b4593d 100644 --- a/ct/deferred/ocis.sh +++ b/ct/deferred/ocis.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/deferred/openwebui.sh b/ct/deferred/openwebui.sh index 47a9e74a1..85341bd9b 100644 --- a/ct/deferred/openwebui.sh +++ b/ct/deferred/openwebui.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/deferred/pixelfed.sh b/ct/deferred/pixelfed.sh index 44ac53b3b..8ddab519c 100644 --- a/ct/deferred/pixelfed.sh +++ b/ct/deferred/pixelfed.sh @@ -16,7 +16,7 @@ var_version="${var_version:-12}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/deferred/polaris.sh b/ct/deferred/polaris.sh index c43b57dec..c290d7706 100644 --- a/ct/deferred/polaris.sh +++ b/ct/deferred/polaris.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/deferred/roundcubemail.sh b/ct/deferred/roundcubemail.sh index 6f65e5277..75d0e3bcd 100644 --- a/ct/deferred/roundcubemail.sh +++ b/ct/deferred/roundcubemail.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/deferred/squirrelserversmanager.sh b/ct/deferred/squirrelserversmanager.sh index 751a6026b..4403ab5f0 100644 --- a/ct/deferred/squirrelserversmanager.sh +++ b/ct/deferred/squirrelserversmanager.sh @@ -16,7 +16,7 @@ var_unprivileged="${var_unprivileged:-1}" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/deferred/vikunja.sh b/ct/deferred/vikunja.sh index 687d47002..fd2483512 100644 --- a/ct/deferred/vikunja.sh +++ b/ct/deferred/vikunja.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/dispatcharr.sh b/ct/dispatcharr.sh index 979cd3d07..e2a2971ee 100644 --- a/ct/dispatcharr.sh +++ b/ct/dispatcharr.sh @@ -18,7 +18,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/docspell.sh b/ct/docspell.sh index a9667ceaa..93eb6806f 100644 --- a/ct/docspell.sh +++ b/ct/docspell.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/ente.sh b/ct/ente.sh index 7f8b3132b..5733886ca 100644 --- a/ct/ente.sh +++ b/ct/ente.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/freepbx.sh b/ct/freepbx.sh index 96f8dd029..0552674bf 100644 --- a/ct/freepbx.sh +++ b/ct/freepbx.sh @@ -18,7 +18,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/frigate.sh b/ct/frigate.sh index 2cc5d1139..479c6b98d 100644 --- a/ct/frigate.sh +++ b/ct/frigate.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-0}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/garmin-grafana.sh b/ct/garmin-grafana.sh index f34fec36f..f0742dd82 100644 --- a/ct/garmin-grafana.sh +++ b/ct/garmin-grafana.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors # this only updates garmin-grafana, not influxdb or grafana, which are upgraded with apt function update_script() { diff --git a/ct/ghostfolio.sh b/ct/ghostfolio.sh index d74aead6b..5acd7f6a0 100644 --- a/ct/ghostfolio.sh +++ b/ct/ghostfolio.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/globaleaks.sh b/ct/globaleaks.sh index 3e1abc897..03bc2f081 100644 --- a/ct/globaleaks.sh +++ b/ct/globaleaks.sh @@ -16,21 +16,21 @@ var_version="${var_version:-13}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -f /usr/sbin/globaleaks ]]; then - msg_error "No ${APP} installation found!" - exit - fi + header_info + check_container_storage + check_container_resources + if [[ ! -f /usr/sbin/globaleaks ]]; then + msg_error "No ${APP} installation found!" + exit + fi - msg_info "Updating $APP LXC" - $STD apt update - $STD apt -y upgrade - msg_ok "Updated $APP LXC" + msg_info "Updating $APP LXC" + $STD apt update + $STD apt -y upgrade + msg_ok "Updated $APP LXC" } start diff --git a/ct/hanko.sh b/ct/hanko.sh index 53e295d27..de3079c26 100644 --- a/ct/hanko.sh +++ b/ct/hanko.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/joplin-server.sh b/ct/joplin-server.sh index 02983e108..9f4896fc8 100644 --- a/ct/joplin-server.sh +++ b/ct/joplin-server.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/kanba.sh b/ct/kanba.sh index 04f85acef..270f5e00f 100644 --- a/ct/kanba.sh +++ b/ct/kanba.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/leantime.sh b/ct/leantime.sh index e6fe9864d..13b960613 100644 --- a/ct/leantime.sh +++ b/ct/leantime.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/librenms.sh b/ct/librenms.sh index 3acd3812f..aaa71a1af 100644 --- a/ct/librenms.sh +++ b/ct/librenms.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/livebook.sh b/ct/livebook.sh index 7c8f32762..5f801ac75 100755 --- a/ct/livebook.sh +++ b/ct/livebook.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/manyfold.sh b/ct/manyfold.sh index d5b464843..2d782d551 100644 --- a/ct/manyfold.sh +++ b/ct/manyfold.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/maxun.sh b/ct/maxun.sh index 78a865fab..a5561dc56 100644 --- a/ct/maxun.sh +++ b/ct/maxun.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/notesnook.sh b/ct/notesnook.sh index ddb87f824..3d1fbb3cf 100644 --- a/ct/notesnook.sh +++ b/ct/notesnook.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/npmplus.sh b/ct/npmplus.sh index e6bb0b985..c3b1755a3 100644 --- a/ct/npmplus.sh +++ b/ct/npmplus.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { UPD=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "UPDATE MODE" --radiolist --cancel-button Exit-Script "Spacebar = Select" 14 60 2 \ diff --git a/ct/opencloud.sh b/ct/opencloud.sh index e97c3af38..2c428f93c 100644 --- a/ct/opencloud.sh +++ b/ct/opencloud.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/postiz.sh b/ct/postiz.sh index 32ec0ac34..e53553677 100644 --- a/ct/postiz.sh +++ b/ct/postiz.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/proxmox-datacenter-manager.sh b/ct/proxmox-datacenter-manager.sh index 577963832..dfcb11208 100644 --- a/ct/proxmox-datacenter-manager.sh +++ b/ct/proxmox-datacenter-manager.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/romm.sh b/ct/romm.sh index 771aed1d3..129da9a34 100644 --- a/ct/romm.sh +++ b/ct/romm.sh @@ -18,7 +18,7 @@ var_fuse="${var_fuse:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/rybbit.sh b/ct/rybbit.sh index 6e3caddd6..e523bde2b 100644 --- a/ct/rybbit.sh +++ b/ct/rybbit.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/scraparr.sh b/ct/scraparr.sh index bca478dfc..efe595ddc 100644 --- a/ct/scraparr.sh +++ b/ct/scraparr.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/signoz.sh b/ct/signoz.sh index 3e7d5bcc4..6efeec713 100644 --- a/ct/signoz.sh +++ b/ct/signoz.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/tunarr.sh b/ct/tunarr.sh index 9af3bebcf..f8d697699 100644 --- a/ct/tunarr.sh +++ b/ct/tunarr.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info check_container_storage diff --git a/ct/ubuntu.sh b/ct/ubuntu.sh index 5bae645b9..f208d7d09 100644 --- a/ct/ubuntu.sh +++ b/ct/ubuntu.sh @@ -19,7 +19,7 @@ var_unprivileged="${var_unprivileged:-0}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/viseron.sh b/ct/viseron.sh index 6534ff2eb..f65e15de5 100644 --- a/ct/viseron.sh +++ b/ct/viseron.sh @@ -14,7 +14,7 @@ header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/wallabag.sh b/ct/wallabag.sh index 910948ff6..9325189a7 100644 --- a/ct/wallabag.sh +++ b/ct/wallabag.sh @@ -22,7 +22,7 @@ base_settings # Core variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/ct/warracker.sh b/ct/warracker.sh index d4707ac7e..8e090b84c 100644 --- a/ct/warracker.sh +++ b/ct/warracker.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info diff --git a/install/alpine-caddy-install.sh b/install/alpine-caddy-install.sh index 882e2e61d..433808442 100644 --- a/install/alpine-caddy-install.sh +++ b/install/alpine-caddy-install.sh @@ -8,14 +8,14 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os msg_info "Installing Caddy" $STD apk add --no-cache caddy caddy-openrc -cat</etc/caddy/Caddyfile +cat </etc/caddy/Caddyfile :80 { # Set this path to your site's directory. root * /var/www/html @@ -31,7 +31,7 @@ cat</etc/caddy/Caddyfile } EOF mkdir -p /var/www/html -cat</var/www/html/index.html +cat </var/www/html/index.html @@ -47,15 +47,15 @@ msg_ok "Installed Caddy" read -r -p "${TAB3}Would you like to install xCaddy Addon? " prompt if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then - GO_VERSION="$(curl -fsSL https://go.dev/VERSION?m=text | head -1 | cut -c3-)" setup_go - msg_info "Setup xCaddy" - cd /opt - RELEASE=$(curl -fsSL https://api.github.com/repos/caddyserver/xcaddy/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - curl -fsSL "https://github.com/caddyserver/xcaddy/releases/download/${RELEASE}/xcaddy_${RELEASE:1}_linux_amd64.tar.gz" -o "xcaddy_${RELEASE:1}_linux_amd64.tar.gz" - $STD tar xzf xcaddy_"${RELEASE:1}"_linux_amd64.tar.gz -C /usr/local/bin xcaddy - rm -rf /opt/xcaddy* - $STD xcaddy build - msg_ok "Setup xCaddy" + GO_VERSION="$(curl -fsSL https://go.dev/VERSION?m=text | head -1 | cut -c3-)" setup_go + msg_info "Setup xCaddy" + cd /opt + RELEASE=$(curl -fsSL https://api.github.com/repos/caddyserver/xcaddy/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + curl -fsSL "https://github.com/caddyserver/xcaddy/releases/download/${RELEASE}/xcaddy_${RELEASE:1}_linux_amd64.tar.gz" -o "xcaddy_${RELEASE:1}_linux_amd64.tar.gz" + $STD tar xzf xcaddy_"${RELEASE:1}"_linux_amd64.tar.gz -C /usr/local/bin xcaddy + rm -rf /opt/xcaddy* + $STD xcaddy build + msg_ok "Setup xCaddy" fi msg_info "Enabling Caddy Service" diff --git a/install/alpine-garage-install.sh b/install/alpine-garage-install.sh index c9ca62e5c..5138656a8 100644 --- a/install/alpine-garage-install.sh +++ b/install/alpine-garage-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/alpine-install.sh b/install/alpine-install.sh index 61acecea5..4922f1641 100644 --- a/install/alpine-install.sh +++ b/install/alpine-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/debian-install.sh b/install/debian-install.sh index b4e0bf32a..e62591849 100644 --- a/install/debian-install.sh +++ b/install/debian-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/deferred/ampache-install.sh b/install/deferred/ampache-install.sh index 868c7f9a8..35dfe1430 100644 --- a/install/deferred/ampache-install.sh +++ b/install/deferred/ampache-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/deferred/freepbx-install_backup.sh b/install/deferred/freepbx-install_backup.sh index 5b42cc0ea..a5d41953c 100644 --- a/install/deferred/freepbx-install_backup.sh +++ b/install/deferred/freepbx-install_backup.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/deferred/funkwhale-install.sh b/install/deferred/funkwhale-install.sh index baf8f8e8f..573abe1ea 100644 --- a/install/deferred/funkwhale-install.sh +++ b/install/deferred/funkwhale-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/deferred/ghostfolio-install.sh b/install/deferred/ghostfolio-install.sh index 91e27db27..9a5d30e43 100644 --- a/install/deferred/ghostfolio-install.sh +++ b/install/deferred/ghostfolio-install.sh @@ -9,7 +9,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/deferred/hoodik-install.sh b/install/deferred/hoodik-install.sh index 51ed398f1..d6505fd46 100644 --- a/install/deferred/hoodik-install.sh +++ b/install/deferred/hoodik-install.sh @@ -9,7 +9,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/deferred/jumpserver-install.sh b/install/deferred/jumpserver-install.sh index 23203327c..4ccfb09eb 100644 --- a/install/deferred/jumpserver-install.sh +++ b/install/deferred/jumpserver-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/deferred/kasm-install.sh b/install/deferred/kasm-install.sh index e55cd17c4..236d15ba8 100644 --- a/install/deferred/kasm-install.sh +++ b/install/deferred/kasm-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/deferred/koel-install.sh b/install/deferred/koel-install.sh index 4f0336516..38c14d5fa 100644 --- a/install/deferred/koel-install.sh +++ b/install/deferred/koel-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/deferred/netbootxyz-install.sh b/install/deferred/netbootxyz-install.sh index e38772932..1a7f71f7e 100644 --- a/install/deferred/netbootxyz-install.sh +++ b/install/deferred/netbootxyz-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/deferred/nginxproxymanager-install.sh b/install/deferred/nginxproxymanager-install.sh index 3fd212796..34db0d6a1 100644 --- a/install/deferred/nginxproxymanager-install.sh +++ b/install/deferred/nginxproxymanager-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/deferred/nimbus-install.sh b/install/deferred/nimbus-install.sh index 5940020c4..c49430481 100644 --- a/install/deferred/nimbus-install.sh +++ b/install/deferred/nimbus-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/deferred/ocis-install.sh b/install/deferred/ocis-install.sh index 98be246e1..987b0397e 100644 --- a/install/deferred/ocis-install.sh +++ b/install/deferred/ocis-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/deferred/openwebui-install.sh b/install/deferred/openwebui-install.sh index 9eb6c3c08..0e9384d8f 100644 --- a/install/deferred/openwebui-install.sh +++ b/install/deferred/openwebui-install.sh @@ -9,7 +9,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/deferred/pixelfed-install.sh b/install/deferred/pixelfed-install.sh index 526922a45..0996ec330 100644 --- a/install/deferred/pixelfed-install.sh +++ b/install/deferred/pixelfed-install.sh @@ -9,7 +9,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/deferred/polaris-install.sh b/install/deferred/polaris-install.sh index f9f70c6ed..4f4e77277 100644 --- a/install/deferred/polaris-install.sh +++ b/install/deferred/polaris-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/deferred/roundcubemail-install.sh b/install/deferred/roundcubemail-install.sh index 40982d9ca..cfa37f067 100644 --- a/install/deferred/roundcubemail-install.sh +++ b/install/deferred/roundcubemail-install.sh @@ -10,7 +10,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/deferred/squirrelserversmanager-install.sh b/install/deferred/squirrelserversmanager-install.sh index 86eab5fb1..9e6cedd03 100644 --- a/install/deferred/squirrelserversmanager-install.sh +++ b/install/deferred/squirrelserversmanager-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/deferred/timescaledb-install.sh b/install/deferred/timescaledb-install.sh index e5db32239..edcb20faa 100644 --- a/install/deferred/timescaledb-install.sh +++ b/install/deferred/timescaledb-install.sh @@ -7,7 +7,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/deferred/vikunja-install.sh b/install/deferred/vikunja-install.sh index b51da342d..f56d073d3 100644 --- a/install/deferred/vikunja-install.sh +++ b/install/deferred/vikunja-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/dispatcharr-install.sh b/install/dispatcharr-install.sh index 17c236486..1daedb24a 100644 --- a/install/dispatcharr-install.sh +++ b/install/dispatcharr-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/docspell-install.sh b/install/docspell-install.sh index 3cdf2b7cd..43534d727 100644 --- a/install/docspell-install.sh +++ b/install/docspell-install.sh @@ -7,7 +7,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/ente-install.sh b/install/ente-install.sh index f9afec8f0..b08386d42 100644 --- a/install/ente-install.sh +++ b/install/ente-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/freepbx-install.sh b/install/freepbx-install.sh index 24c14a0a8..4a4bd2715 100644 --- a/install/freepbx-install.sh +++ b/install/freepbx-install.sh @@ -12,7 +12,7 @@ INSTALL_PATH="/opt/sng_freepbx_debian_install.sh" source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/frigate-install.sh b/install/frigate-install.sh index e6852aaac..018261a1d 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/garmin-grafana-install.sh b/install/garmin-grafana-install.sh index 23fb87979..70070f8c1 100644 --- a/install/garmin-grafana-install.sh +++ b/install/garmin-grafana-install.sh @@ -9,7 +9,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/ghostfolio-install.sh b/install/ghostfolio-install.sh index 7988124bf..090b209b6 100644 --- a/install/ghostfolio-install.sh +++ b/install/ghostfolio-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/globaleaks-install.sh b/install/globaleaks-install.sh index 966eb32dd..650c8cbd3 100644 --- a/install/globaleaks-install.sh +++ b/install/globaleaks-install.sh @@ -7,7 +7,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/hanko-install.sh b/install/hanko-install.sh index 40ab829d9..e1ec43c9d 100644 --- a/install/hanko-install.sh +++ b/install/hanko-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/joplin-server-install.sh b/install/joplin-server-install.sh index 07b7920ee..6187f565b 100644 --- a/install/joplin-server-install.sh +++ b/install/joplin-server-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/kanba-install.sh b/install/kanba-install.sh index d7335fd56..10a306ddc 100644 --- a/install/kanba-install.sh +++ b/install/kanba-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/leantime-install.sh b/install/leantime-install.sh index b5c2f5e7a..98aa77a19 100644 --- a/install/leantime-install.sh +++ b/install/leantime-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/librenms-install.sh b/install/librenms-install.sh index 296f03519..ad1cbb6fe 100644 --- a/install/librenms-install.sh +++ b/install/librenms-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/livebook-install.sh b/install/livebook-install.sh index 251f078db..5d922b721 100644 --- a/install/livebook-install.sh +++ b/install/livebook-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/manyfold-install.sh b/install/manyfold-install.sh index be4dcd84b..f7bb7c2f6 100644 --- a/install/manyfold-install.sh +++ b/install/manyfold-install.sh @@ -7,7 +7,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/maxun-install.sh b/install/maxun-install.sh index 27e3e710a..004546bc5 100644 --- a/install/maxun-install.sh +++ b/install/maxun-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/notesnook-install.sh b/install/notesnook-install.sh index ab95849eb..cedb85cad 100644 --- a/install/notesnook-install.sh +++ b/install/notesnook-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/npmplus-install.sh b/install/npmplus-install.sh index e8e5f1bfb..f63192c79 100644 --- a/install/npmplus-install.sh +++ b/install/npmplus-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/opencloud-install.sh b/install/opencloud-install.sh index a791ed0b1..59f15950e 100644 --- a/install/opencloud-install.sh +++ b/install/opencloud-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/postiz-install.sh b/install/postiz-install.sh index 2ed155c82..5b9e35923 100644 --- a/install/postiz-install.sh +++ b/install/postiz-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/proxmox-datacenter-manager-install.sh b/install/proxmox-datacenter-manager-install.sh index c4a3f3877..e9a3c3006 100644 --- a/install/proxmox-datacenter-manager-install.sh +++ b/install/proxmox-datacenter-manager-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/romm-install.sh b/install/romm-install.sh index bd5f9a287..438e4e5a7 100644 --- a/install/romm-install.sh +++ b/install/romm-install.sh @@ -9,7 +9,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/rybbit-install.sh b/install/rybbit-install.sh index 1f9481f99..055a101a0 100644 --- a/install/rybbit-install.sh +++ b/install/rybbit-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/scraparr-install.sh b/install/scraparr-install.sh index 18b024253..8c0de782c 100644 --- a/install/scraparr-install.sh +++ b/install/scraparr-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/signoz-install.sh b/install/signoz-install.sh index f506f315d..fb52d12bc 100644 --- a/install/signoz-install.sh +++ b/install/signoz-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/tunarr-install.sh b/install/tunarr-install.sh index 57d7f16ef..854db7137 100644 --- a/install/tunarr-install.sh +++ b/install/tunarr-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os @@ -16,18 +16,18 @@ update_os msg_info "Setting Up Hardware Acceleration" $STD apt-get -y install {va-driver-all,ocl-icd-libopencl1,intel-opencl-icd,vainfo,intel-gpu-tools} if [[ "$CTTYPE" == "0" ]]; then - chgrp video /dev/dri - chmod 755 /dev/dri - chmod 660 /dev/dri/* - $STD adduser $(id -u -n) video - $STD adduser $(id -u -n) render + chgrp video /dev/dri + chmod 755 /dev/dri + chmod 660 /dev/dri/* + $STD adduser $(id -u -n) video + $STD adduser $(id -u -n) render fi msg_ok "Set Up Hardware Acceleration" read -r -p "${TAB3}Do you need the intel-media-va-driver-non-free driver for HW encoding (Debian 12 only)? " prompt if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then - msg_info "Installing Intel Hardware Acceleration (non-free)" - cat </etc/apt/sources.list.d/non-free.list + msg_info "Installing Intel Hardware Acceleration (non-free)" + cat </etc/apt/sources.list.d/non-free.list deb http://deb.debian.org/debian bookworm non-free non-free-firmware deb-src http://deb.debian.org/debian bookworm non-free non-free-firmware @@ -38,11 +38,11 @@ deb-src http://deb.debian.org/debian-security bookworm-security non-free non-fre deb http://deb.debian.org/debian bookworm-updates non-free non-free-firmware deb-src http://deb.debian.org/debian bookworm-updates non-free non-free-firmware EOF - $STD apt-get update - $STD apt-get -y install {intel-media-va-driver-non-free,ocl-icd-libopencl1,intel-opencl-icd,vainfo,intel-gpu-tools} + $STD apt-get update + $STD apt-get -y install {intel-media-va-driver-non-free,ocl-icd-libopencl1,intel-opencl-icd,vainfo,intel-gpu-tools} else - msg_info "Installing Intel Hardware Acceleration" - $STD apt-get -y install {va-driver-all,ocl-icd-libopencl1,intel-opencl-icd,vainfo,intel-gpu-tools} + msg_info "Installing Intel Hardware Acceleration" + $STD apt-get -y install {va-driver-all,ocl-icd-libopencl1,intel-opencl-icd,vainfo,intel-gpu-tools} fi msg_ok "Installed and Set Up Intel Hardware Acceleration" diff --git a/install/ubuntu-install.sh b/install/ubuntu-install.sh index aa5766a09..97283d838 100644 --- a/install/ubuntu-install.sh +++ b/install/ubuntu-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/viseron-install.sh b/install/viseron-install.sh index 67cf18c40..e0835a595 100644 --- a/install/viseron-install.sh +++ b/install/viseron-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/wallabag-install.sh b/install/wallabag-install.sh index 3bcbce9cc..2c1231d21 100644 --- a/install/wallabag-install.sh +++ b/install/wallabag-install.sh @@ -7,7 +7,7 @@ source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os diff --git a/install/warracker-install.sh b/install/warracker-install.sh index 18ce630cf..fb83fba04 100644 --- a/install/warracker-install.sh +++ b/install/warracker-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os @@ -16,8 +16,7 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ apt-transport-https \ - ca-certificates\ - nginx + ca-certificates nginx msg_ok "Installed Dependencies" PYTHON_VERSION="3.11" setup_uv @@ -37,12 +36,12 @@ $STD sudo -u postgres psql -d "$DB_NAME" -c "GRANT USAGE ON SCHEMA public TO $DB $STD sudo -u postgres psql -d "$DB_NAME" -c "GRANT CREATE ON SCHEMA public TO $DB_USER;" $STD sudo -u postgres psql -d "$DB_NAME" -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO $DB_USER;" { - echo "Application Credentials" - echo "DB_NAME: $DB_NAME" - echo "DB_USER: $DB_USER" - echo "DB_PASS: $DB_PASS" - echo "DB_ADMIN_USER: $DB_ADMIN_USER" - echo "DB_ADMIN_PASS: $DB_ADMIN_PASS" + echo "Application Credentials" + echo "DB_NAME: $DB_NAME" + echo "DB_USER: $DB_USER" + echo "DB_PASS: $DB_PASS" + echo "DB_ADMIN_USER: $DB_ADMIN_USER" + echo "DB_ADMIN_PASS: $DB_ADMIN_PASS" } >>~/warracker.creds msg_ok "Installed PostgreSQL" @@ -55,17 +54,17 @@ $STD source .venv/bin/activate $STD uv pip install -r requirements.txt mv /opt/warracker/env.example /opt/warracker/.env sed -i \ - -e "s/your_secure_database_password/$DB_PASS/" \ - -e "s/your_secure_admin_password/$DB_ADMIN_PASS/" \ - -e "s|^# DB_PORT=5432$|DB_HOST=127.0.0.1|" \ - /opt/warracker/.env + -e "s/your_secure_database_password/$DB_PASS/" \ + -e "s/your_secure_admin_password/$DB_ADMIN_PASS/" \ + -e "s|^# DB_PORT=5432$|DB_HOST=127.0.0.1|" \ + /opt/warracker/.env mv /opt/warracker/nginx.conf /etc/nginx/sites-available/warracker.conf sed -i \ - -e "s|alias /var/www/html/locales/;|alias /opt/warracker/locales/;|" \ - -e "s|/var/www/html|/opt/warracker/frontend|g" \ - -e "s/client_max_body_size __NGINX_MAX_BODY_SIZE_CONFIG_VALUE__/client_max_body_size 32M/" \ - /etc/nginx/sites-available/warracker.conf + -e "s|alias /var/www/html/locales/;|alias /opt/warracker/locales/;|" \ + -e "s|/var/www/html|/opt/warracker/frontend|g" \ + -e "s/client_max_body_size __NGINX_MAX_BODY_SIZE_CONFIG_VALUE__/client_max_body_size 32M/" \ + /etc/nginx/sites-available/warracker.conf ln -s /etc/nginx/sites-available/warracker.conf /etc/nginx/sites-enabled/warracker.conf rm /etc/nginx/sites-enabled/default systemctl restart nginx diff --git a/misc/alpine-install.func b/misc/alpine-install.func index 0ba607cae..ce396f75c 100644 --- a/misc/alpine-install.func +++ b/misc/alpine-install.func @@ -5,22 +5,22 @@ # https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE if ! command -v curl >/dev/null 2>&1; then - apk update && apk add curl >/dev/null 2>&1 + apk update && apk add curl >/dev/null 2>&1 fi source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) load_functions -init_error_traps +catch_errors # This function enables IPv6 if it's not disabled and sets verbose mode verb_ip6() { - set_std_mode # Set STD mode based on VERBOSE + set_std_mode # Set STD mode based on VERBOSE - if [ "$DISABLEIPV6" == "yes" ]; then - $STD sysctl -w net.ipv6.conf.all.disable_ipv6=1 - echo "net.ipv6.conf.all.disable_ipv6 = 1" >>/etc/sysctl.conf - $STD rc-update add sysctl default - fi + if [ "$DISABLEIPV6" == "yes" ]; then + $STD sysctl -w net.ipv6.conf.all.disable_ipv6=1 + echo "net.ipv6.conf.all.disable_ipv6 = 1" >>/etc/sysctl.conf + $STD rc-update add sysctl default + fi } set -Eeuo pipefail @@ -30,149 +30,149 @@ trap on_interrupt INT trap on_terminate TERM error_handler() { - local exit_code="$1" - local line_number="$2" - local command="$3" + local exit_code="$1" + local line_number="$2" + local command="$3" - # Exitcode 0 = kein Fehler → ignorieren - if [[ "$exit_code" -eq 0 ]]; then - return 0 - fi + # Exitcode 0 = kein Fehler → ignorieren + if [[ "$exit_code" -eq 0 ]]; then + return 0 + fi - printf "\e[?25h" - echo -e "\n${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}\n" - exit "$exit_code" + printf "\e[?25h" + echo -e "\n${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}\n" + exit "$exit_code" } on_exit() { - local exit_code="$?" - [[ -n "${lockfile:-}" && -e "$lockfile" ]] && rm -f "$lockfile" - exit "$exit_code" + local exit_code="$?" + [[ -n "${lockfile:-}" && -e "$lockfile" ]] && rm -f "$lockfile" + exit "$exit_code" } on_interrupt() { - echo -e "\n${RD}Interrupted by user (SIGINT)${CL}" - exit 130 + echo -e "\n${RD}Interrupted by user (SIGINT)${CL}" + exit 130 } on_terminate() { - echo -e "\n${RD}Terminated by signal (SIGTERM)${CL}" - exit 143 + echo -e "\n${RD}Terminated by signal (SIGTERM)${CL}" + exit 143 } # This function sets up the Container OS by generating the locale, setting the timezone, and checking the network connection setting_up_container() { - msg_info "Setting up Container OS" - while [ $i -gt 0 ]; do - if [ "$(ip addr show | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | cut -d'/' -f1)" != "" ]; then - break - fi - echo 1>&2 -en "${CROSS}${RD} No Network! " - sleep $RETRY_EVERY - i=$((i - 1)) - done + msg_info "Setting up Container OS" + while [ $i -gt 0 ]; do + if [ "$(ip addr show | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | cut -d'/' -f1)" != "" ]; then + break + fi + echo 1>&2 -en "${CROSS}${RD} No Network! " + sleep $RETRY_EVERY + i=$((i - 1)) + done - if [ "$(ip addr show | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | cut -d'/' -f1)" = "" ]; then - echo 1>&2 -e "\n${CROSS}${RD} No Network After $RETRY_NUM Tries${CL}" - echo -e "${NETWORK}Check Network Settings" - exit 1 - fi - msg_ok "Set up Container OS" - msg_ok "Network Connected: ${BL}$(ip addr show | grep 'inet ' | awk '{print $2}' | cut -d'/' -f1 | tail -n1)${CL}" + if [ "$(ip addr show | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}' | cut -d'/' -f1)" = "" ]; then + echo 1>&2 -e "\n${CROSS}${RD} No Network After $RETRY_NUM Tries${CL}" + echo -e "${NETWORK}Check Network Settings" + exit 1 + fi + msg_ok "Set up Container OS" + msg_ok "Network Connected: ${BL}$(ip addr show | grep 'inet ' | awk '{print $2}' | cut -d'/' -f1 | tail -n1)${CL}" } # This function checks the network connection by pinging a known IP address and prompts the user to continue if the internet is not connected network_check() { - set +e - trap - ERR - if ping -c 1 -W 1 1.1.1.1 &>/dev/null || ping -c 1 -W 1 8.8.8.8 &>/dev/null || ping -c 1 -W 1 9.9.9.9 &>/dev/null; then - msg_ok "Internet Connected" - else - msg_error "Internet NOT Connected" - read -r -p "Would you like to continue anyway? " prompt - if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then - echo -e "${INFO}${RD}Expect Issues Without Internet${CL}" + set +e + trap - ERR + if ping -c 1 -W 1 1.1.1.1 &>/dev/null || ping -c 1 -W 1 8.8.8.8 &>/dev/null || ping -c 1 -W 1 9.9.9.9 &>/dev/null; then + msg_ok "Internet Connected" else - echo -e "${NETWORK}Check Network Settings" - exit 1 + msg_error "Internet NOT Connected" + read -r -p "Would you like to continue anyway? " prompt + if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then + echo -e "${INFO}${RD}Expect Issues Without Internet${CL}" + else + echo -e "${NETWORK}Check Network Settings" + exit 1 + fi fi - fi - RESOLVEDIP=$(getent hosts github.com | awk '{ print $1 }') - if [[ -z "$RESOLVEDIP" ]]; then msg_error "DNS Lookup Failure"; else msg_ok "DNS Resolved github.com to ${BL}$RESOLVEDIP${CL}"; fi - set -e - trap 'error_handler $LINENO "$BASH_COMMAND"' ERR + RESOLVEDIP=$(getent hosts github.com | awk '{ print $1 }') + if [[ -z "$RESOLVEDIP" ]]; then msg_error "DNS Lookup Failure"; else msg_ok "DNS Resolved github.com to ${BL}$RESOLVEDIP${CL}"; fi + set -e + trap 'error_handler $LINENO "$BASH_COMMAND"' ERR } # This function updates the Container OS by running apt-get update and upgrade update_os() { - msg_info "Updating Container OS" - $STD apk update && $STD apk upgrade - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-tools.func) - msg_ok "Updated Container OS" + msg_info "Updating Container OS" + $STD apk update && $STD apk upgrade + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-tools.func) + msg_ok "Updated Container OS" } # This function modifies the message of the day (motd) and SSH settings motd_ssh() { - echo "export TERM='xterm-256color'" >>/root/.bashrc - IP=$(ip -4 addr show eth0 | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) + echo "export TERM='xterm-256color'" >>/root/.bashrc + IP=$(ip -4 addr show eth0 | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) - if [ -f "/etc/os-release" ]; then - OS_NAME=$(grep ^NAME /etc/os-release | cut -d= -f2 | tr -d '"') - OS_VERSION=$(grep ^VERSION_ID /etc/os-release | cut -d= -f2 | tr -d '"') - else - OS_NAME="Alpine Linux" - OS_VERSION="Unknown" - fi + if [ -f "/etc/os-release" ]; then + OS_NAME=$(grep ^NAME /etc/os-release | cut -d= -f2 | tr -d '"') + OS_VERSION=$(grep ^VERSION_ID /etc/os-release | cut -d= -f2 | tr -d '"') + else + OS_NAME="Alpine Linux" + OS_VERSION="Unknown" + fi - PROFILE_FILE="/etc/profile.d/00_lxc-details.sh" - echo "echo -e \"\"" >"$PROFILE_FILE" - echo -e "echo -e \"${BOLD}${YW}${APPLICATION} LXC Container - DEV Repository${CL}\"" >>"$PROFILE_FILE" - echo -e "echo -e \"${RD}WARNING: This is a DEVELOPMENT version (ProxmoxVED). Do NOT use in production!${CL}\"" >>"$PROFILE_FILE" - echo -e "echo -e \"${YW} OS: ${GN}${OS_NAME} - Version: ${OS_VERSION}${CL}\"" >>"$PROFILE_FILE" - echo -e "echo -e \"${YW} Hostname: ${GN}\$(hostname)${CL}\"" >>"$PROFILE_FILE" - echo -e "echo -e \"${YW} IP Address: ${GN}${IP}${CL}\"" >>"$PROFILE_FILE" - echo -e "echo -e \"${YW} Repository: ${GN}https://github.com/community-scripts/ProxmoxVED${CL}\"" >>"$PROFILE_FILE" - echo "echo \"\"" >>"$PROFILE_FILE" + PROFILE_FILE="/etc/profile.d/00_lxc-details.sh" + echo "echo -e \"\"" >"$PROFILE_FILE" + echo -e "echo -e \"${BOLD}${YW}${APPLICATION} LXC Container - DEV Repository${CL}\"" >>"$PROFILE_FILE" + echo -e "echo -e \"${RD}WARNING: This is a DEVELOPMENT version (ProxmoxVED). Do NOT use in production!${CL}\"" >>"$PROFILE_FILE" + echo -e "echo -e \"${YW} OS: ${GN}${OS_NAME} - Version: ${OS_VERSION}${CL}\"" >>"$PROFILE_FILE" + echo -e "echo -e \"${YW} Hostname: ${GN}\$(hostname)${CL}\"" >>"$PROFILE_FILE" + echo -e "echo -e \"${YW} IP Address: ${GN}${IP}${CL}\"" >>"$PROFILE_FILE" + echo -e "echo -e \"${YW} Repository: ${GN}https://github.com/community-scripts/ProxmoxVED${CL}\"" >>"$PROFILE_FILE" + echo "echo \"\"" >>"$PROFILE_FILE" - if [[ "${SSH_ROOT}" == "yes" ]]; then - $STD rc-update add sshd - sed -i "s/#PermitRootLogin prohibit-password/PermitRootLogin yes/g" /etc/ssh/sshd_config - $STD /etc/init.d/sshd start - fi + if [[ "${SSH_ROOT}" == "yes" ]]; then + $STD rc-update add sshd + sed -i "s/#PermitRootLogin prohibit-password/PermitRootLogin yes/g" /etc/ssh/sshd_config + $STD /etc/init.d/sshd start + fi } # Validate Timezone for some LXC's validate_tz() { - [[ -f "/usr/share/zoneinfo/$1" ]] + [[ -f "/usr/share/zoneinfo/$1" ]] } # This function customizes the container and enables passwordless login for the root user customize() { - if [[ "$PASSWORD" == "" ]]; then - msg_info "Customizing Container" - passwd -d root >/dev/null 2>&1 + if [[ "$PASSWORD" == "" ]]; then + msg_info "Customizing Container" + passwd -d root >/dev/null 2>&1 - # Ensure agetty is available - apk add --no-cache --force-broken-world util-linux >/dev/null 2>&1 + # Ensure agetty is available + apk add --no-cache --force-broken-world util-linux >/dev/null 2>&1 - # Create persistent autologin boot script - mkdir -p /etc/local.d - cat <<'EOF' >/etc/local.d/autologin.start + # Create persistent autologin boot script + mkdir -p /etc/local.d + cat <<'EOF' >/etc/local.d/autologin.start #!/bin/sh sed -i 's|^tty1::respawn:.*|tty1::respawn:/sbin/agetty --autologin root --noclear tty1 38400 linux|' /etc/inittab kill -HUP 1 EOF - touch /root/.hushlogin + touch /root/.hushlogin - chmod +x /etc/local.d/autologin.start - rc-update add local >/dev/null 2>&1 + chmod +x /etc/local.d/autologin.start + rc-update add local >/dev/null 2>&1 - # Apply autologin immediately for current session - /etc/local.d/autologin.start + # Apply autologin immediately for current session + /etc/local.d/autologin.start - msg_ok "Customized Container" - fi + msg_ok "Customized Container" + fi - echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update - chmod +x /usr/bin/update + echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update + chmod +x /usr/bin/update } diff --git a/misc/build.func b/misc/build.func index 1802ab0d2..c112f27db 100644 --- a/misc/build.func +++ b/misc/build.func @@ -134,13 +134,13 @@ if command -v curl >/dev/null 2>&1; then source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) load_functions - init_error_traps + catch_errors #echo "(build.func) Loaded core.func via curl" elif command -v wget >/dev/null 2>&1; then source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) load_functions - init_error_traps + catch_errors #echo "(build.func) Loaded core.func via wget" fi diff --git a/misc/error_handler.func b/misc/error_handler.func index 219969696..d2f21d087 100644 --- a/misc/error_handler.func +++ b/misc/error_handler.func @@ -8,141 +8,141 @@ # ------------------------------------------------------------------------------ explain_exit_code() { - local code="$1" - case "$code" in - # --- Generic / Shell --- - 1) echo "General error / Operation not permitted" ;; - 2) echo "Misuse of shell builtins (e.g. syntax error)" ;; - 126) echo "Command invoked cannot execute (permission problem?)" ;; - 127) echo "Command not found" ;; - 128) echo "Invalid argument to exit" ;; - 130) echo "Terminated by Ctrl+C (SIGINT)" ;; - 137) echo "Killed (SIGKILL / Out of memory?)" ;; - 139) echo "Segmentation fault (core dumped)" ;; - 143) echo "Terminated (SIGTERM)" ;; + local code="$1" + case "$code" in + # --- Generic / Shell --- + 1) echo "General error / Operation not permitted" ;; + 2) echo "Misuse of shell builtins (e.g. syntax error)" ;; + 126) echo "Command invoked cannot execute (permission problem?)" ;; + 127) echo "Command not found" ;; + 128) echo "Invalid argument to exit" ;; + 130) echo "Terminated by Ctrl+C (SIGINT)" ;; + 137) echo "Killed (SIGKILL / Out of memory?)" ;; + 139) echo "Segmentation fault (core dumped)" ;; + 143) echo "Terminated (SIGTERM)" ;; - # --- Package manager / APT / DPKG --- - 100) echo "APT: Package manager error (broken packages / dependency problems)" ;; - 101) echo "APT: Configuration error (bad sources.list, malformed config)" ;; - 255) echo "DPKG: Fatal internal error" ;; + # --- Package manager / APT / DPKG --- + 100) echo "APT: Package manager error (broken packages / dependency problems)" ;; + 101) echo "APT: Configuration error (bad sources.list, malformed config)" ;; + 255) echo "DPKG: Fatal internal error" ;; - # --- Node.js / npm / pnpm / yarn --- - 243) echo "Node.js: Out of memory (JavaScript heap out of memory)" ;; - 245) echo "Node.js: Invalid command-line option" ;; - 246) echo "Node.js: Internal JavaScript Parse Error" ;; - 247) echo "Node.js: Fatal internal error" ;; - 248) echo "Node.js: Invalid C++ addon / N-API failure" ;; - 249) echo "Node.js: Inspector error" ;; - 254) echo "npm/pnpm/yarn: Unknown fatal error" ;; + # --- Node.js / npm / pnpm / yarn --- + 243) echo "Node.js: Out of memory (JavaScript heap out of memory)" ;; + 245) echo "Node.js: Invalid command-line option" ;; + 246) echo "Node.js: Internal JavaScript Parse Error" ;; + 247) echo "Node.js: Fatal internal error" ;; + 248) echo "Node.js: Invalid C++ addon / N-API failure" ;; + 249) echo "Node.js: Inspector error" ;; + 254) echo "npm/pnpm/yarn: Unknown fatal error" ;; - # --- Python / pip / uv --- - 210) echo "Python: Virtualenv / uv environment missing or broken" ;; - 211) echo "Python: Dependency resolution failed" ;; - 212) echo "Python: Installation aborted (permissions or EXTERNALLY-MANAGED)" ;; + # --- Python / pip / uv --- + 210) echo "Python: Virtualenv / uv environment missing or broken" ;; + 211) echo "Python: Dependency resolution failed" ;; + 212) echo "Python: Installation aborted (permissions or EXTERNALLY-MANAGED)" ;; - # --- PostgreSQL --- - 231) echo "PostgreSQL: Connection failed (server not running / wrong socket)" ;; - 232) echo "PostgreSQL: Authentication failed (bad user/password)" ;; - 233) echo "PostgreSQL: Database does not exist" ;; - 234) echo "PostgreSQL: Fatal error in query / syntax" ;; + # --- PostgreSQL --- + 231) echo "PostgreSQL: Connection failed (server not running / wrong socket)" ;; + 232) echo "PostgreSQL: Authentication failed (bad user/password)" ;; + 233) echo "PostgreSQL: Database does not exist" ;; + 234) echo "PostgreSQL: Fatal error in query / syntax" ;; - # --- MySQL / MariaDB --- - 241) echo "MySQL/MariaDB: Connection failed (server not running / wrong socket)" ;; - 242) echo "MySQL/MariaDB: Authentication failed (bad user/password)" ;; - 243) echo "MySQL/MariaDB: Database does not exist" ;; - 244) echo "MySQL/MariaDB: Fatal error in query / syntax" ;; + # --- MySQL / MariaDB --- + 241) echo "MySQL/MariaDB: Connection failed (server not running / wrong socket)" ;; + 242) echo "MySQL/MariaDB: Authentication failed (bad user/password)" ;; + 243) echo "MySQL/MariaDB: Database does not exist" ;; + 244) echo "MySQL/MariaDB: Fatal error in query / syntax" ;; - # --- MongoDB --- - 251) echo "MongoDB: Connection failed (server not running)" ;; - 252) echo "MongoDB: Authentication failed (bad user/password)" ;; - 253) echo "MongoDB: Database not found" ;; - 254) echo "MongoDB: Fatal query error" ;; + # --- MongoDB --- + 251) echo "MongoDB: Connection failed (server not running)" ;; + 252) echo "MongoDB: Authentication failed (bad user/password)" ;; + 253) echo "MongoDB: Database not found" ;; + 254) echo "MongoDB: Fatal query error" ;; - # --- Proxmox Custom Codes --- - 200) echo "Custom: Failed to create lock file" ;; - 203) echo "Custom: Missing CTID variable" ;; - 204) echo "Custom: Missing PCT_OSTYPE variable" ;; - 205) echo "Custom: Invalid CTID (<100)" ;; - 209) echo "Custom: Container creation failed" ;; - 210) echo "Custom: Cluster not quorate" ;; - 214) echo "Custom: Not enough storage space" ;; - 215) echo "Custom: Container ID not listed" ;; - 216) echo "Custom: RootFS entry missing in config" ;; - 217) echo "Custom: Storage does not support rootdir" ;; - 220) echo "Custom: Unable to resolve template path" ;; - 222) echo "Custom: Template download failed after 3 attempts" ;; - 223) echo "Custom: Template not available after download" ;; - 231) echo "Custom: LXC stack upgrade/retry failed" ;; + # --- Proxmox Custom Codes --- + 200) echo "Custom: Failed to create lock file" ;; + 203) echo "Custom: Missing CTID variable" ;; + 204) echo "Custom: Missing PCT_OSTYPE variable" ;; + 205) echo "Custom: Invalid CTID (<100)" ;; + 209) echo "Custom: Container creation failed" ;; + 210) echo "Custom: Cluster not quorate" ;; + 214) echo "Custom: Not enough storage space" ;; + 215) echo "Custom: Container ID not listed" ;; + 216) echo "Custom: RootFS entry missing in config" ;; + 217) echo "Custom: Storage does not support rootdir" ;; + 220) echo "Custom: Unable to resolve template path" ;; + 222) echo "Custom: Template download failed after 3 attempts" ;; + 223) echo "Custom: Template not available after download" ;; + 231) echo "Custom: LXC stack upgrade/retry failed" ;; - # --- Default --- - *) echo "Unknown error" ;; - esac + # --- Default --- + *) echo "Unknown error" ;; + esac } # === Error handler ============================================================ error_handler() { - local exit_code=${1:-$?} - local command=${2:-${BASH_COMMAND:-unknown}} - local line_number=${BASH_LINENO[0]:-unknown} + local exit_code=${1:-$?} + local command=${2:-${BASH_COMMAND:-unknown}} + local line_number=${BASH_LINENO[0]:-unknown} - command="${command//\$STD/}" + command="${command//\$STD/}" - if [[ "$exit_code" -eq 0 ]]; then - return 0 - fi + if [[ "$exit_code" -eq 0 ]]; then + return 0 + fi - local explanation - explanation="$(explain_exit_code "$exit_code")" + local explanation + explanation="$(explain_exit_code "$exit_code")" - printf "\e[?25h" - echo -e "\n${RD}[ERROR]${CL} in line ${RD}${line_number}${CL}: exit code ${RD}${exit_code}${CL} (${explanation}): while executing command ${YWB}${command}${CL}\n" + printf "\e[?25h" + echo -e "\n${RD}[ERROR]${CL} in line ${RD}${line_number}${CL}: exit code ${RD}${exit_code}${CL} (${explanation}): while executing command ${YWB}${command}${CL}\n" - if [[ -n "${DEBUG_LOGFILE:-}" ]]; then - { - echo "------ ERROR ------" - echo "Timestamp : $(date '+%Y-%m-%d %H:%M:%S')" - echo "Exit Code : $exit_code ($explanation)" - echo "Line : $line_number" - echo "Command : $command" - echo "-------------------" - } >>"$DEBUG_LOGFILE" - fi + if [[ -n "${DEBUG_LOGFILE:-}" ]]; then + { + echo "------ ERROR ------" + echo "Timestamp : $(date '+%Y-%m-%d %H:%M:%S')" + echo "Exit Code : $exit_code ($explanation)" + echo "Line : $line_number" + echo "Command : $command" + echo "-------------------" + } >>"$DEBUG_LOGFILE" + fi - if [[ -n "${SILENT_LOGFILE:-}" && -s "$SILENT_LOGFILE" ]]; then - echo "--- Last 20 lines of silent log ($SILENT_LOGFILE) ---" - tail -n 20 "$SILENT_LOGFILE" - echo "---------------------------------------------------" - fi + if [[ -n "${SILENT_LOGFILE:-}" && -s "$SILENT_LOGFILE" ]]; then + echo "--- Last 20 lines of silent log ($SILENT_LOGFILE) ---" + tail -n 20 "$SILENT_LOGFILE" + echo "---------------------------------------------------" + fi - exit "$exit_code" + exit "$exit_code" } # === Exit handler ============================================================= on_exit() { - local exit_code=$? - [[ -n "${lockfile:-}" && -e "$lockfile" ]] && rm -f "$lockfile" - exit "$exit_code" + local exit_code=$? + [[ -n "${lockfile:-}" && -e "$lockfile" ]] && rm -f "$lockfile" + exit "$exit_code" } # === Signal handlers ========================================================== on_interrupt() { - echo -e "\n${RD}Interrupted by user (SIGINT)${CL}" - exit 130 + echo -e "\n${RD}Interrupted by user (SIGINT)${CL}" + exit 130 } on_terminate() { - echo -e "\n${RD}Terminated by signal (SIGTERM)${CL}" - exit 143 + echo -e "\n${RD}Terminated by signal (SIGTERM)${CL}" + exit 143 } # === Init traps =============================================================== -init_error_traps() { - set -Ee -o pipefail - if [ "${STRICT_UNSET:-0}" = "1" ]; then - set -u - fi - trap 'error_handler' ERR - trap on_exit EXIT - trap on_interrupt INT - trap on_terminate TERM +catch_errors() { + set -Ee -o pipefail + if [ "${STRICT_UNSET:-0}" = "1" ]; then + set -u + fi + trap 'error_handler' ERR + trap on_exit EXIT + trap on_interrupt INT + trap on_terminate TERM } diff --git a/misc/install.func b/misc/install.func index b27bfc017..f741b921d 100644 --- a/misc/install.func +++ b/misc/install.func @@ -5,23 +5,23 @@ # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE if ! command -v curl >/dev/null 2>&1; then - printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 - apt-get update >/dev/null 2>&1 - apt-get install -y curl >/dev/null 2>&1 + printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 + apt-get update >/dev/null 2>&1 + apt-get install -y curl >/dev/null 2>&1 fi source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) load_functions -init_error_traps +catch_errors # This function enables IPv6 if it's not disabled and sets verbose mode verb_ip6() { - set_std_mode # Set STD mode based on VERBOSE + set_std_mode # Set STD mode based on VERBOSE - if [ "$DISABLEIPV6" == "yes" ]; then - echo "net.ipv6.conf.all.disable_ipv6 = 1" >>/etc/sysctl.conf - $STD sysctl -p - fi + if [ "$DISABLEIPV6" == "yes" ]; then + echo "net.ipv6.conf.all.disable_ipv6 = 1" >>/etc/sysctl.conf + $STD sysctl -p + fi } # # This function sets error handling options and defines the error_handler function to handle errors @@ -48,92 +48,92 @@ verb_ip6() { # This function sets up the Container OS by generating the locale, setting the timezone, and checking the network connection setting_up_container() { - msg_info "Setting up Container OS" - for ((i = RETRY_NUM; i > 0; i--)); do - if [ "$(hostname -I)" != "" ]; then - break + msg_info "Setting up Container OS" + for ((i = RETRY_NUM; i > 0; i--)); do + if [ "$(hostname -I)" != "" ]; then + break + fi + echo 1>&2 -en "${CROSS}${RD} No Network! " + sleep $RETRY_EVERY + done + if [ "$(hostname -I)" = "" ]; then + echo 1>&2 -e "\n${CROSS}${RD} No Network After $RETRY_NUM Tries${CL}" + echo -e "${NETWORK}Check Network Settings" + exit 1 fi - echo 1>&2 -en "${CROSS}${RD} No Network! " - sleep $RETRY_EVERY - done - if [ "$(hostname -I)" = "" ]; then - echo 1>&2 -e "\n${CROSS}${RD} No Network After $RETRY_NUM Tries${CL}" - echo -e "${NETWORK}Check Network Settings" - exit 1 - fi - rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED - systemctl disable -q --now systemd-networkd-wait-online.service - msg_ok "Set up Container OS" - #msg_custom "${CM}" "${GN}" "Network Connected: ${BL}$(hostname -I)" - msg_ok "Network Connected: ${BL}$(hostname -I)" + rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED + systemctl disable -q --now systemd-networkd-wait-online.service + msg_ok "Set up Container OS" + #msg_custom "${CM}" "${GN}" "Network Connected: ${BL}$(hostname -I)" + msg_ok "Network Connected: ${BL}$(hostname -I)" } # This function checks the network connection by pinging a known IP address and prompts the user to continue if the internet is not connected network_check() { - set +e - trap - ERR - ipv4_connected=false - ipv6_connected=false - sleep 1 + set +e + trap - ERR + ipv4_connected=false + ipv6_connected=false + sleep 1 - # Check IPv4 connectivity to Google, Cloudflare & Quad9 DNS servers. - if ping -c 1 -W 1 1.1.1.1 &>/dev/null || ping -c 1 -W 1 8.8.8.8 &>/dev/null || ping -c 1 -W 1 9.9.9.9 &>/dev/null; then - msg_ok "IPv4 Internet Connected" - ipv4_connected=true - else - msg_error "IPv4 Internet Not Connected" - fi - - # Check IPv6 connectivity to Google, Cloudflare & Quad9 DNS servers. - if ping6 -c 1 -W 1 2606:4700:4700::1111 &>/dev/null || ping6 -c 1 -W 1 2001:4860:4860::8888 &>/dev/null || ping6 -c 1 -W 1 2620:fe::fe &>/dev/null; then - msg_ok "IPv6 Internet Connected" - ipv6_connected=true - else - msg_error "IPv6 Internet Not Connected" - fi - - # If both IPv4 and IPv6 checks fail, prompt the user - if [[ $ipv4_connected == false && $ipv6_connected == false ]]; then - read -r -p "No Internet detected, would you like to continue anyway? " prompt - if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then - echo -e "${INFO}${RD}Expect Issues Without Internet${CL}" + # Check IPv4 connectivity to Google, Cloudflare & Quad9 DNS servers. + if ping -c 1 -W 1 1.1.1.1 &>/dev/null || ping -c 1 -W 1 8.8.8.8 &>/dev/null || ping -c 1 -W 1 9.9.9.9 &>/dev/null; then + msg_ok "IPv4 Internet Connected" + ipv4_connected=true else - echo -e "${NETWORK}Check Network Settings" - exit 1 + msg_error "IPv4 Internet Not Connected" fi - fi - # DNS resolution checks for GitHub-related domains (IPv4 and/or IPv6) - GIT_HOSTS=("github.com" "raw.githubusercontent.com" "api.github.com" "git.community-scripts.org") - GIT_STATUS="Git DNS:" - DNS_FAILED=false - - for HOST in "${GIT_HOSTS[@]}"; do - RESOLVEDIP=$(getent hosts "$HOST" | awk '{ print $1 }' | grep -E '(^([0-9]{1,3}\.){3}[0-9]{1,3}$)|(^[a-fA-F0-9:]+$)' | head -n1) - if [[ -z "$RESOLVEDIP" ]]; then - GIT_STATUS+="$HOST:($DNSFAIL)" - DNS_FAILED=true + # Check IPv6 connectivity to Google, Cloudflare & Quad9 DNS servers. + if ping6 -c 1 -W 1 2606:4700:4700::1111 &>/dev/null || ping6 -c 1 -W 1 2001:4860:4860::8888 &>/dev/null || ping6 -c 1 -W 1 2620:fe::fe &>/dev/null; then + msg_ok "IPv6 Internet Connected" + ipv6_connected=true else - GIT_STATUS+=" $HOST:($DNSOK)" + msg_error "IPv6 Internet Not Connected" fi - done - if [[ "$DNS_FAILED" == true ]]; then - fatal "$GIT_STATUS" - else - msg_ok "$GIT_STATUS" - fi + # If both IPv4 and IPv6 checks fail, prompt the user + if [[ $ipv4_connected == false && $ipv6_connected == false ]]; then + read -r -p "No Internet detected, would you like to continue anyway? " prompt + if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then + echo -e "${INFO}${RD}Expect Issues Without Internet${CL}" + else + echo -e "${NETWORK}Check Network Settings" + exit 1 + fi + fi - set -e - trap 'error_handler $LINENO "$BASH_COMMAND"' ERR + # DNS resolution checks for GitHub-related domains (IPv4 and/or IPv6) + GIT_HOSTS=("github.com" "raw.githubusercontent.com" "api.github.com" "git.community-scripts.org") + GIT_STATUS="Git DNS:" + DNS_FAILED=false + + for HOST in "${GIT_HOSTS[@]}"; do + RESOLVEDIP=$(getent hosts "$HOST" | awk '{ print $1 }' | grep -E '(^([0-9]{1,3}\.){3}[0-9]{1,3}$)|(^[a-fA-F0-9:]+$)' | head -n1) + if [[ -z "$RESOLVEDIP" ]]; then + GIT_STATUS+="$HOST:($DNSFAIL)" + DNS_FAILED=true + else + GIT_STATUS+=" $HOST:($DNSOK)" + fi + done + + if [[ "$DNS_FAILED" == true ]]; then + fatal "$GIT_STATUS" + else + msg_ok "$GIT_STATUS" + fi + + set -e + trap 'error_handler $LINENO "$BASH_COMMAND"' ERR } # This function updates the Container OS by running apt-get update and upgrade update_os() { - msg_info "Updating Container OS" - if [[ "$CACHER" == "yes" ]]; then - echo "Acquire::http::Proxy-Auto-Detect \"/usr/local/bin/apt-proxy-detect.sh\";" >/etc/apt/apt.conf.d/00aptproxy - cat <<'EOF' >/usr/local/bin/apt-proxy-detect.sh + msg_info "Updating Container OS" + if [[ "$CACHER" == "yes" ]]; then + echo "Acquire::http::Proxy-Auto-Detect \"/usr/local/bin/apt-proxy-detect.sh\";" >/etc/apt/apt.conf.d/00aptproxy + cat <<'EOF' >/usr/local/bin/apt-proxy-detect.sh #!/bin/bash if nc -w1 -z "${CACHER_IP}" 3142; then echo -n "http://${CACHER_IP}:3142" @@ -141,66 +141,66 @@ else echo -n "DIRECT" fi EOF - chmod +x /usr/local/bin/apt-proxy-detect.sh - fi - $STD apt-get update - $STD apt-get -o Dpkg::Options::="--force-confold" -y dist-upgrade - rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED - msg_ok "Updated Container OS" - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) + chmod +x /usr/local/bin/apt-proxy-detect.sh + fi + $STD apt-get update + $STD apt-get -o Dpkg::Options::="--force-confold" -y dist-upgrade + rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED + msg_ok "Updated Container OS" + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) } # This function modifies the message of the day (motd) and SSH settings motd_ssh() { - grep -qxF "export TERM='xterm-256color'" /root/.bashrc || echo "export TERM='xterm-256color'" >>/root/.bashrc + grep -qxF "export TERM='xterm-256color'" /root/.bashrc || echo "export TERM='xterm-256color'" >>/root/.bashrc - if [ -f "/etc/os-release" ]; then - OS_NAME=$(grep ^NAME /etc/os-release | cut -d= -f2 | tr -d '"') - OS_VERSION=$(grep ^VERSION_ID /etc/os-release | cut -d= -f2 | tr -d '"') - elif [ -f "/etc/debian_version" ]; then - OS_NAME="Debian" - OS_VERSION=$(cat /etc/debian_version) - fi + if [ -f "/etc/os-release" ]; then + OS_NAME=$(grep ^NAME /etc/os-release | cut -d= -f2 | tr -d '"') + OS_VERSION=$(grep ^VERSION_ID /etc/os-release | cut -d= -f2 | tr -d '"') + elif [ -f "/etc/debian_version" ]; then + OS_NAME="Debian" + OS_VERSION=$(cat /etc/debian_version) + fi - PROFILE_FILE="/etc/profile.d/00_lxc-details.sh" - echo "echo -e \"\"" >"$PROFILE_FILE" - echo -e "echo -e \"${BOLD}${YW}${APPLICATION} LXC Container - DEV Repository${CL}\"" >>"$PROFILE_FILE" - echo -e "echo -e \"${RD}WARNING: This is a DEVELOPMENT version (ProxmoxVED). Do NOT use in production!${CL}\"" >>"$PROFILE_FILE" - echo -e "echo -e \"${YW} OS: ${GN}${OS_NAME} - Version: ${OS_VERSION}${CL}\"" >>"$PROFILE_FILE" - echo -e "echo -e \"${YW} Hostname: ${GN}\$(hostname)${CL}\"" >>"$PROFILE_FILE" - echo -e "echo -e \"${YW} IP Address: ${GN}\$(hostname -I | awk '{print \$1}')${CL}\"" >>"$PROFILE_FILE" - echo -e "echo -e \"${YW} Repository: ${GN}https://github.com/community-scripts/ProxmoxVED${CL}\"" >>"$PROFILE_FILE" - echo "echo \"\"" >>"$PROFILE_FILE" + PROFILE_FILE="/etc/profile.d/00_lxc-details.sh" + echo "echo -e \"\"" >"$PROFILE_FILE" + echo -e "echo -e \"${BOLD}${YW}${APPLICATION} LXC Container - DEV Repository${CL}\"" >>"$PROFILE_FILE" + echo -e "echo -e \"${RD}WARNING: This is a DEVELOPMENT version (ProxmoxVED). Do NOT use in production!${CL}\"" >>"$PROFILE_FILE" + echo -e "echo -e \"${YW} OS: ${GN}${OS_NAME} - Version: ${OS_VERSION}${CL}\"" >>"$PROFILE_FILE" + echo -e "echo -e \"${YW} Hostname: ${GN}\$(hostname)${CL}\"" >>"$PROFILE_FILE" + echo -e "echo -e \"${YW} IP Address: ${GN}\$(hostname -I | awk '{print \$1}')${CL}\"" >>"$PROFILE_FILE" + echo -e "echo -e \"${YW} Repository: ${GN}https://github.com/community-scripts/ProxmoxVED${CL}\"" >>"$PROFILE_FILE" + echo "echo \"\"" >>"$PROFILE_FILE" - chmod -x /etc/update-motd.d/* + chmod -x /etc/update-motd.d/* - if [[ "${SSH_ROOT}" == "yes" ]]; then - sed -i "s/#PermitRootLogin prohibit-password/PermitRootLogin yes/g" /etc/ssh/sshd_config - systemctl restart sshd - fi + if [[ "${SSH_ROOT}" == "yes" ]]; then + sed -i "s/#PermitRootLogin prohibit-password/PermitRootLogin yes/g" /etc/ssh/sshd_config + systemctl restart sshd + fi } # This function customizes the container by modifying the getty service and enabling auto-login for the root user customize() { - if [[ "$PASSWORD" == "" ]]; then - msg_info "Customizing Container" - GETTY_OVERRIDE="/etc/systemd/system/container-getty@1.service.d/override.conf" - mkdir -p $(dirname $GETTY_OVERRIDE) - cat <$GETTY_OVERRIDE + if [[ "$PASSWORD" == "" ]]; then + msg_info "Customizing Container" + GETTY_OVERRIDE="/etc/systemd/system/container-getty@1.service.d/override.conf" + mkdir -p $(dirname $GETTY_OVERRIDE) + cat <$GETTY_OVERRIDE [Service] ExecStart= ExecStart=-/sbin/agetty --autologin root --noclear --keep-baud tty%I 115200,38400,9600 \$TERM EOF - systemctl daemon-reload - systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//') - msg_ok "Customized Container" - fi - echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update - chmod +x /usr/bin/update - if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then - mkdir -p /root/.ssh - echo "${SSH_AUTHORIZED_KEY}" >/root/.ssh/authorized_keys - chmod 700 /root/.ssh - chmod 600 /root/.ssh/authorized_keys - fi + systemctl daemon-reload + systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//') + msg_ok "Customized Container" + fi + echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update + chmod +x /usr/bin/update + if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then + mkdir -p /root/.ssh + echo "${SSH_AUTHORIZED_KEY}" >/root/.ssh/authorized_keys + chmod 700 /root/.ssh + chmod 600 /root/.ssh/authorized_keys + fi } From f924fa60634a5a63889fed6dd05356be785b4329 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 19 Sep 2025 08:59:17 +0200 Subject: [PATCH 1044/1733] Update tunarr.sh --- ct/tunarr.sh | 90 ++++++++++++++++++++++++++-------------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/ct/tunarr.sh b/ct/tunarr.sh index f8d697699..d804606c3 100644 --- a/ct/tunarr.sh +++ b/ct/tunarr.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 tteck +# Copyright (c) 2021-2025 community-scripts ORG # Author: chrisbenincasa # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://tunarr.com/ @@ -19,51 +19,51 @@ variables color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/tunarr ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - if check_for_gh_release "tunarr" "chrisbenincasa/tunarr"; then - msg_info "Stopping ${APP}" - systemctl stop tunarr - msg_ok "Stopped ${APP}" - - msg_info "Creating Backup" - tar -czf "/opt/${APP}_backup_$(date +%F).tar.gz" /usr/.local/share/tunarr - msg_ok "Backup Created" - - fetch_and_deploy_gh_release "tunarr" "chrisbenincasa/tunarr" "singlefile" "latest" "/opt/tunarr" "*linux-x64" - - msg_info "Starting ${APP}" - systemctl start tunarr - msg_ok "Started ${APP}" - - msg_ok "Updated Successfully" - fi - - if check_for_gh_release "ersatztv-ffmpeg" "ErsatzTV/ErsatzTV-ffmpeg"; then - msg_info "Stopping ${APP}" - systemctl stop tunarr - msg_ok "Stopped ${APP}" - - fetch_and_deploy_gh_release "ersatztv-ffmpeg" "ErsatzTV/ErsatzTV-ffmpeg" "prebuild" "latest" "/opt/ErsatzTV-ffmpeg" "*-linux64-gpl-7.1.tar.xz" - - msg_info "Set ErsatzTV-ffmpeg links" - chmod +x /opt/ErsatzTV-ffmpeg/bin/* - ln -sf /opt/ErsatzTV-ffmpeg/bin/ffmpeg /usr/local/bin/ffmpeg - ln -sf /opt/ErsatzTV-ffmpeg/bin/ffplay /usr/local/bin/ffplay - ln -sf /opt/ErsatzTV-ffmpeg/bin/ffprobe /usr/local/bin/ffprobe - msg_ok "ffmpeg links set" - - msg_info "Starting ${APP}" - systemctl start tunarr - msg_ok "Started ${APP}" - msg_ok "Updated Successfully" - fi + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/tunarr ]]; then + msg_error "No ${APP} Installation Found!" exit + fi + if check_for_gh_release "tunarr" "chrisbenincasa/tunarr"; then + msg_info "Stopping ${APP}" + systemctl stop tunarr + msg_ok "Stopped ${APP}" + + msg_info "Creating Backup" + tar -czf "/opt/${APP}_backup_$(date +%F).tar.gz" /usr/.local/share/tunarr + msg_ok "Backup Created" + + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "tunarr" "chrisbenincasa/tunarr" "singlefile" "latest" "/opt/tunarr" "*linux-x64" + + msg_info "Starting ${APP}" + systemctl start tunarr + msg_ok "Started ${APP}" + + msg_ok "Updated Successfully" + fi + + if check_for_gh_release "ersatztv-ffmpeg" "ErsatzTV/ErsatzTV-ffmpeg"; then + msg_info "Stopping ${APP}" + systemctl stop tunarr + msg_ok "Stopped ${APP}" + + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "ersatztv-ffmpeg" "ErsatzTV/ErsatzTV-ffmpeg" "prebuild" "latest" "/opt/ErsatzTV-ffmpeg" "*-linux64-gpl-7.1.tar.xz" + + msg_info "Set ErsatzTV-ffmpeg links" + chmod +x /opt/ErsatzTV-ffmpeg/bin/* + ln -sf /opt/ErsatzTV-ffmpeg/bin/ffmpeg /usr/local/bin/ffmpeg + ln -sf /opt/ErsatzTV-ffmpeg/bin/ffplay /usr/local/bin/ffplay + ln -sf /opt/ErsatzTV-ffmpeg/bin/ffprobe /usr/local/bin/ffprobe + msg_ok "ffmpeg links set" + + msg_info "Starting ${APP}" + systemctl start tunarr + msg_ok "Started ${APP}" + msg_ok "Updated Successfully" + fi + exit } start From 6f21fec430c243c88a5ce29fa777bb2894ce038a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 19 Sep 2025 09:00:30 +0200 Subject: [PATCH 1045/1733] Update tunarr.sh --- ct/tunarr.sh | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/ct/tunarr.sh b/ct/tunarr.sh index d804606c3..cc8d2aed9 100644 --- a/ct/tunarr.sh +++ b/ct/tunarr.sh @@ -11,7 +11,7 @@ var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-5}" var_os="${var_os:-debian}" -var_version="${var_version:-12}" +var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" @@ -27,9 +27,9 @@ function update_script() { exit fi if check_for_gh_release "tunarr" "chrisbenincasa/tunarr"; then - msg_info "Stopping ${APP}" + msg_info "Stopping Service" systemctl stop tunarr - msg_ok "Stopped ${APP}" + msg_ok "Stopped Service" msg_info "Creating Backup" tar -czf "/opt/${APP}_backup_$(date +%F).tar.gz" /usr/.local/share/tunarr @@ -37,17 +37,16 @@ function update_script() { CLEAN_INSTALL=1 fetch_and_deploy_gh_release "tunarr" "chrisbenincasa/tunarr" "singlefile" "latest" "/opt/tunarr" "*linux-x64" - msg_info "Starting ${APP}" + msg_info "Starting Service" systemctl start tunarr - msg_ok "Started ${APP}" - - msg_ok "Updated Successfully" + msg_ok "Started Service" + msg_ok "Update Successfully" fi if check_for_gh_release "ersatztv-ffmpeg" "ErsatzTV/ErsatzTV-ffmpeg"; then - msg_info "Stopping ${APP}" + msg_info "Stopping Service" systemctl stop tunarr - msg_ok "Stopped ${APP}" + msg_ok "Stopped Service" CLEAN_INSTALL=1 fetch_and_deploy_gh_release "ersatztv-ffmpeg" "ErsatzTV/ErsatzTV-ffmpeg" "prebuild" "latest" "/opt/ErsatzTV-ffmpeg" "*-linux64-gpl-7.1.tar.xz" @@ -58,10 +57,10 @@ function update_script() { ln -sf /opt/ErsatzTV-ffmpeg/bin/ffprobe /usr/local/bin/ffprobe msg_ok "ffmpeg links set" - msg_info "Starting ${APP}" + msg_info "Starting Service" systemctl start tunarr - msg_ok "Started ${APP}" - msg_ok "Updated Successfully" + msg_ok "Started Service" + msg_ok "Update Successfully" fi exit } From 6d907e50ed96b211be9bf348bf83a9623baf716c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 19 Sep 2025 09:03:51 +0200 Subject: [PATCH 1046/1733] fixes --- ct/tunarr.sh | 8 ++++++-- install/tunarr-install.sh | 32 +++++++++++++++++++------------- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/ct/tunarr.sh b/ct/tunarr.sh index cc8d2aed9..0934e9a85 100644 --- a/ct/tunarr.sh +++ b/ct/tunarr.sh @@ -32,8 +32,12 @@ function update_script() { msg_ok "Stopped Service" msg_info "Creating Backup" - tar -czf "/opt/${APP}_backup_$(date +%F).tar.gz" /usr/.local/share/tunarr - msg_ok "Backup Created" + if [ -d "/usr/local/share/tunarr" ]; then + tar -czf "/opt/${APP}_backup_$(date +%F).tar.gz" /usr/local/share/tunarr $STD + msg_ok "Backup Created" + else + msg_error "Backup failed: /usr/local/share/tunarr does not exist" + fi CLEAN_INSTALL=1 fetch_and_deploy_gh_release "tunarr" "chrisbenincasa/tunarr" "singlefile" "latest" "/opt/tunarr" "*linux-x64" diff --git a/install/tunarr-install.sh b/install/tunarr-install.sh index 854db7137..b93e569d2 100644 --- a/install/tunarr-install.sh +++ b/install/tunarr-install.sh @@ -27,22 +27,27 @@ msg_ok "Set Up Hardware Acceleration" read -r -p "${TAB3}Do you need the intel-media-va-driver-non-free driver for HW encoding (Debian 12 only)? " prompt if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then msg_info "Installing Intel Hardware Acceleration (non-free)" - cat </etc/apt/sources.list.d/non-free.list + cat <<'EOF' >/etc/apt/sources.list.d/non-free.sources +Types: deb deb-src +URIs: http://deb.debian.org/debian +Suites: trixie +Components: non-free non-free-firmware -deb http://deb.debian.org/debian bookworm non-free non-free-firmware -deb-src http://deb.debian.org/debian bookworm non-free non-free-firmware +Types: deb deb-src +URIs: http://deb.debian.org/debian-security +Suites: trixie-security +Components: non-free non-free-firmware -deb http://deb.debian.org/debian-security bookworm-security non-free non-free-firmware -deb-src http://deb.debian.org/debian-security bookworm-security non-free non-free-firmware - -deb http://deb.debian.org/debian bookworm-updates non-free non-free-firmware -deb-src http://deb.debian.org/debian bookworm-updates non-free non-free-firmware +Types: deb deb-src +URIs: http://deb.debian.org/debian +Suites: trixie-updates +Components: non-free non-free-firmware EOF - $STD apt-get update - $STD apt-get -y install {intel-media-va-driver-non-free,ocl-icd-libopencl1,intel-opencl-icd,vainfo,intel-gpu-tools} + $STD apt update + $STD apt -y install {intel-media-va-driver-non-free,ocl-icd-libopencl1,intel-opencl-icd,vainfo,intel-gpu-tools} else msg_info "Installing Intel Hardware Acceleration" - $STD apt-get -y install {va-driver-all,ocl-icd-libopencl1,intel-opencl-icd,vainfo,intel-gpu-tools} + $STD apt -y install {va-driver-all,ocl-icd-libopencl1,intel-opencl-icd,vainfo,intel-gpu-tools} fi msg_ok "Installed and Set Up Intel Hardware Acceleration" @@ -80,6 +85,7 @@ motd_ssh customize msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean +$STD apt -y autoremove +$STD apt -y autoclean +$STD apt -y clean msg_ok "Cleaned" From 703c4d96b572a648393b415580616dc1a003221e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 19 Sep 2025 09:04:32 +0200 Subject: [PATCH 1047/1733] Update tunarr.json --- frontend/public/json/tunarr.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/public/json/tunarr.json b/frontend/public/json/tunarr.json index d77f28d19..5529ab511 100644 --- a/frontend/public/json/tunarr.json +++ b/frontend/public/json/tunarr.json @@ -12,7 +12,7 @@ "interface_port": 8000, "documentation": "https://tunarr.com/", "website": "https://tunarr.com/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/png/tunarr.png", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/tunarr.webp", "description": "Create a classic TV experience using your own media - IPTV backed by Plex/Jellyfin/Emby.", "install_methods": [ { @@ -23,7 +23,7 @@ "ram": 1024, "hdd": 5, "os": "Debian", - "version": "12" + "version": "13" } } ], From d2e2b33c83f48e7909344897dbff598c35285656 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 19 Sep 2025 09:09:17 +0200 Subject: [PATCH 1048/1733] Update build.func --- misc/build.func | 146 ++++++++++++++++++++++++------------------------ 1 file changed, 73 insertions(+), 73 deletions(-) diff --git a/misc/build.func b/misc/build.func index c112f27db..ecff03297 100644 --- a/misc/build.func +++ b/misc/build.func @@ -32,93 +32,93 @@ variables() { # - Local cache: /usr/local/community-scripts/core # ----------------------------------------------------------------------------- -FUNC_DIR="/usr/local/community-scripts/core" -mkdir -p "$FUNC_DIR" +# FUNC_DIR="/usr/local/community-scripts/core" +# mkdir -p "$FUNC_DIR" -BUILD_URL="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func" -BUILD_REV="$FUNC_DIR/build.rev" -DEVMODE="${DEVMODE:-no}" +# BUILD_URL="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func" +# BUILD_REV="$FUNC_DIR/build.rev" +# DEVMODE="${DEVMODE:-no}" -# --- Step 1: fetch build.func content once, compute hash --- -build_content="$(curl -fsSL "$BUILD_URL")" || { - echo "❌ Failed to fetch build.func" - exit 1 -} +# # --- Step 1: fetch build.func content once, compute hash --- +# build_content="$(curl -fsSL "$BUILD_URL")" || { +# echo "❌ Failed to fetch build.func" +# exit 1 +# } -newhash=$(printf "%s" "$build_content" | sha256sum | awk '{print $1}') -oldhash=$(cat "$BUILD_REV" 2>/dev/null || echo "") +# newhash=$(printf "%s" "$build_content" | sha256sum | awk '{print $1}') +# oldhash=$(cat "$BUILD_REV" 2>/dev/null || echo "") -# --- Step 2: if build.func changed, offer update for core files --- -if [ "$newhash" != "$oldhash" ]; then - echo "⚠️ build.func changed!" +# # --- Step 2: if build.func changed, offer update for core files --- +# if [ "$newhash" != "$oldhash" ]; then +# echo "⚠️ build.func changed!" - while true; do - read -rp "Refresh local core files? [y/N/diff]: " ans - case "$ans" in - [Yy]*) - echo "$newhash" >"$BUILD_REV" +# while true; do +# read -rp "Refresh local core files? [y/N/diff]: " ans +# case "$ans" in +# [Yy]*) +# echo "$newhash" >"$BUILD_REV" - update_func_file() { - local file="$1" - local url="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/$file" - local local_path="$FUNC_DIR/$file" +# update_func_file() { +# local file="$1" +# local url="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/$file" +# local local_path="$FUNC_DIR/$file" - echo "⬇️ Downloading $file ..." - curl -fsSL "$url" -o "$local_path" || { - echo "❌ Failed to fetch $file" - exit 1 - } - echo "✔️ Updated $file" - } +# echo "⬇️ Downloading $file ..." +# curl -fsSL "$url" -o "$local_path" || { +# echo "❌ Failed to fetch $file" +# exit 1 +# } +# echo "✔️ Updated $file" +# } - update_func_file core.func - update_func_file error_handler.func - update_func_file tools.func - break - ;; - [Dd]*) - for file in core.func error_handler.func tools.func; do - local_path="$FUNC_DIR/$file" - url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/$file" - remote_tmp="$(mktemp)" +# update_func_file core.func +# update_func_file error_handler.func +# update_func_file tools.func +# break +# ;; +# [Dd]*) +# for file in core.func error_handler.func tools.func; do +# local_path="$FUNC_DIR/$file" +# url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/$file" +# remote_tmp="$(mktemp)" - curl -fsSL "$url" -o "$remote_tmp" || continue +# curl -fsSL "$url" -o "$remote_tmp" || continue - if [ -f "$local_path" ]; then - echo "🔍 Diff for $file:" - diff -u "$local_path" "$remote_tmp" || echo "(no differences)" - else - echo "📦 New file $file will be installed" - fi +# if [ -f "$local_path" ]; then +# echo "🔍 Diff for $file:" +# diff -u "$local_path" "$remote_tmp" || echo "(no differences)" +# else +# echo "📦 New file $file will be installed" +# fi - rm -f "$remote_tmp" - done - ;; - *) - echo "❌ Skipped updating local core files" - break - ;; - esac - done -else - if [ "$DEVMODE" != "yes" ]; then - echo "✔️ build.func unchanged → using existing local core files" - fi -fi +# rm -f "$remote_tmp" +# done +# ;; +# *) +# echo "❌ Skipped updating local core files" +# break +# ;; +# esac +# done +# else +# if [ "$DEVMODE" != "yes" ]; then +# echo "✔️ build.func unchanged → using existing local core files" +# fi +# fi -if [ -n "${_COMMUNITY_SCRIPTS_LOADER:-}" ]; then - return 0 2>/dev/null || exit 0 -fi -_COMMUNITY_SCRIPTS_LOADER=1 +# if [ -n "${_COMMUNITY_SCRIPTS_LOADER:-}" ]; then +# return 0 2>/dev/null || exit 0 +# fi +# _COMMUNITY_SCRIPTS_LOADER=1 -# --- Step 3: always source local versions of the core files --- -source "$FUNC_DIR/core.func" -source "$FUNC_DIR/error_handler.func" -source "$FUNC_DIR/tools.func" +# # --- Step 3: always source local versions of the core files --- +# source "$FUNC_DIR/core.func" +# source "$FUNC_DIR/error_handler.func" +# source "$FUNC_DIR/tools.func" -# --- Step 4: finally, source build.func directly from memory --- -# (no tmp file needed) -source <(printf "%s" "$build_content") +# # --- Step 4: finally, source build.func directly from memory --- +# # (no tmp file needed) +# source <(printf "%s" "$build_content") # ------------------------------------------------------------------------------ # Load core + error handler functions from community-scripts repo From d4b006aec1959cfa5d135826771dfe33e40fd258 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Fri, 19 Sep 2025 09:10:54 +0200 Subject: [PATCH 1049/1733] Add Outline for testing --- ct/outline.sh | 67 ++++++++++++++++++++++++++++ install/outline-install.sh | 90 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 ct/outline.sh create mode 100644 install/outline-install.sh diff --git a/ct/outline.sh b/ct/outline.sh new file mode 100644 index 000000000..c94eb63bc --- /dev/null +++ b/ct/outline.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/outline/outline + +APP="Outline" +var_tags="${var_tags:-documentation}" +var_disk="${var_disk:-8}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-4096}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/outline ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "outline" "outline/outline"; then + msg_info "Stopping Services" + systemctl stop outline + msg_ok "Services Stopped" + + msg_info "Creating backup" + cp /opt/outline/.env /opt + msg_ok "Backup created" + + fetch_and_deploy_gh_release "outline" "outline/outline" "tarball" + + msg_info "Updating ${APP}" + cd /opt/outline + mv /opt/.env /opt/outline + export NODE_ENV=development + export NODE_OPTIONS="--max-old-space-size=3584" + $STD yarn install --frozen-lockfile + export NODE_ENV=production + $STD yarn build + msg_ok "Updated ${APP}" + + msg_info "Starting Services" + systemctl start outline + msg_ok "Started Services" + msg_ok "Updated Successfully" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" diff --git a/install/outline-install.sh b/install/outline-install.sh new file mode 100644 index 000000000..068d1b38f --- /dev/null +++ b/install/outline-install.sh @@ -0,0 +1,90 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/outline/outline + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt-get install -y \ + mkcert \ + git \ + redis +msg_ok "Installed Dependencies" + +NODE_VERSION="20" NODE_MODULE="yarn@latest" setup_nodejs +PG_VERSION="16" setup_postgresql + +msg_info "Set up PostgreSQL Database" +DB_NAME="outline" +DB_USER="outline" +DB_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" +$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" +$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" +{ + echo "Outline-Credentials" + echo "Outline Database User: $DB_USER" + echo "Outline Database Password: $DB_PASS" + echo "Outline Database Name: $DB_NAME" +} >>~/outline.creds +msg_ok "Set up PostgreSQL Database" + +fetch_and_deploy_gh_release "outline" "outline/outline" "tarball" + +msg_info "Configuring Outline (Patience)" +SECRET_KEY="$(openssl rand -hex 32)" +LOCAL_IP="$(hostname -I | awk '{print $1}')" +cd /opt/outline +cp .env.sample .env +export NODE_ENV=development +sed -i 's/NODE_ENV=production/NODE_ENV=development/g' /opt/outline/.env +sed -i "s/generate_a_new_key/${SECRET_KEY}/g" /opt/outline/.env +sed -i "s/user:pass@postgres/${DB_USER}:${DB_PASS}@localhost/g" /opt/outline/.env +sed -i 's/redis:6379/localhost:6379/g' /opt/outline/.env +sed -i "5s#URL=#URL=http://${LOCAL_IP}#g" /opt/outline/.env +sed -i 's/FORCE_HTTPS=true/FORCE_HTTPS=false/g' /opt/outline/.env +export NODE_OPTIONS="--max-old-space-size=3584" +$STD yarn install --frozen-lockfile +export NODE_ENV=production +sed -i 's/NODE_ENV=development/NODE_ENV=production/g' /opt/outline/.env +$STD yarn build +msg_ok "Configured Outline" + +msg_info "Creating Service" +cat </etc/systemd/system/outline.service +[Unit] +Description=Outline Service +After=network.target + +[Service] +Type=simple +User=root +WorkingDirectory=/opt/outline +ExecStart=/usr/bin/yarn start +Restart=always +EnvironmentFile=/opt/outline/.env + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now outline +msg_ok "Created Service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 53d5127c5ad8d326bb5ce6c95ee18b5870f7a3f7 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Fri, 19 Sep 2025 09:13:40 +0200 Subject: [PATCH 1050/1733] Update Outline --- ct/outline.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/outline.sh b/ct/outline.sh index c94eb63bc..41bfa2fbb 100644 --- a/ct/outline.sh +++ b/ct/outline.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE From 14bac9468fd87efb0e95166fd150c5cdf5799fe0 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Fri, 19 Sep 2025 09:23:39 +0200 Subject: [PATCH 1051/1733] Pin Outline version --- install/outline-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/outline-install.sh b/install/outline-install.sh index 068d1b38f..ed496101a 100644 --- a/install/outline-install.sh +++ b/install/outline-install.sh @@ -40,7 +40,7 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" } >>~/outline.creds msg_ok "Set up PostgreSQL Database" -fetch_and_deploy_gh_release "outline" "outline/outline" "tarball" +fetch_and_deploy_gh_release "outline" "outline/outline" "tarball" "v0.87.3" msg_info "Configuring Outline (Patience)" SECRET_KEY="$(openssl rand -hex 32)" From d3c0a0773d564836b6ab7157c3fb5881c5921bb0 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 19 Sep 2025 09:35:04 +0200 Subject: [PATCH 1052/1733] Refactor backup and package install in tunarr scripts Removes backup creation from the tunarr update script to streamline the update process. Refactors package installation in the install script for better readability and updates messaging for open package installation. --- ct/tunarr.sh | 8 -------- install/tunarr-install.sh | 15 ++++++++++++--- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/ct/tunarr.sh b/ct/tunarr.sh index 0934e9a85..f0ed721ab 100644 --- a/ct/tunarr.sh +++ b/ct/tunarr.sh @@ -31,14 +31,6 @@ function update_script() { systemctl stop tunarr msg_ok "Stopped Service" - msg_info "Creating Backup" - if [ -d "/usr/local/share/tunarr" ]; then - tar -czf "/opt/${APP}_backup_$(date +%F).tar.gz" /usr/local/share/tunarr $STD - msg_ok "Backup Created" - else - msg_error "Backup failed: /usr/local/share/tunarr does not exist" - fi - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "tunarr" "chrisbenincasa/tunarr" "singlefile" "latest" "/opt/tunarr" "*linux-x64" msg_info "Starting Service" diff --git a/install/tunarr-install.sh b/install/tunarr-install.sh index b93e569d2..4039de2f8 100644 --- a/install/tunarr-install.sh +++ b/install/tunarr-install.sh @@ -43,11 +43,20 @@ URIs: http://deb.debian.org/debian Suites: trixie-updates Components: non-free non-free-firmware EOF + $STD apt update - $STD apt -y install {intel-media-va-driver-non-free,ocl-icd-libopencl1,intel-opencl-icd,vainfo,intel-gpu-tools} + $STD apt -y install \ + intel-media-va-driver-non-free \ + ocl-icd-libopencl1 \ + vainfo \ + intel-gpu-tools else - msg_info "Installing Intel Hardware Acceleration" - $STD apt -y install {va-driver-all,ocl-icd-libopencl1,intel-opencl-icd,vainfo,intel-gpu-tools} + msg_info "Installing Intel Hardware Acceleration (open packages)" + $STD apt -y install \ + va-driver-all \ + ocl-icd-libopencl1 \ + vainfo \ + intel-gpu-tools fi msg_ok "Installed and Set Up Intel Hardware Acceleration" From 177e45cc2325aa904d52f06390b8e2aba51ddef8 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 19 Sep 2025 09:35:40 +0200 Subject: [PATCH 1053/1733] Update tunarr-install.sh --- install/tunarr-install.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/install/tunarr-install.sh b/install/tunarr-install.sh index 4039de2f8..9efb86142 100644 --- a/install/tunarr-install.sh +++ b/install/tunarr-install.sh @@ -48,6 +48,10 @@ EOF $STD apt -y install \ intel-media-va-driver-non-free \ ocl-icd-libopencl1 \ + mesa-opencl-icd \ + mesa-va-drivers \ + libmfx1 \ + libvpl2 \ vainfo \ intel-gpu-tools else @@ -55,6 +59,8 @@ else $STD apt -y install \ va-driver-all \ ocl-icd-libopencl1 \ + mesa-opencl-icd \ + mesa-va-drivers \ vainfo \ intel-gpu-tools fi From c2ee70029ded6443d9d613405e77973a8d04be08 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 19 Sep 2025 09:38:33 +0200 Subject: [PATCH 1054/1733] Refactor hardware acceleration setup in install script Improves package installation formatting, removes unnecessary device permission changes, and updates prompt to reference Debian 13 for the non-free Intel driver. Also refines user group addition commands for clarity and reliability. --- install/tunarr-install.sh | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/install/tunarr-install.sh b/install/tunarr-install.sh index 9efb86142..606ea4efa 100644 --- a/install/tunarr-install.sh +++ b/install/tunarr-install.sh @@ -14,17 +14,18 @@ network_check update_os msg_info "Setting Up Hardware Acceleration" -$STD apt-get -y install {va-driver-all,ocl-icd-libopencl1,intel-opencl-icd,vainfo,intel-gpu-tools} +$STD apt-get -y install \ + va-driver-all \ + ocl-icd-libopencl1 \ + vainfo \ + intel-gpu-tools if [[ "$CTTYPE" == "0" ]]; then - chgrp video /dev/dri - chmod 755 /dev/dri - chmod 660 /dev/dri/* - $STD adduser $(id -u -n) video - $STD adduser $(id -u -n) render + $STD adduser "$(id -un)" video + $STD adduser "$(id -un)" render fi -msg_ok "Set Up Hardware Acceleration" +msg_ok "Base Hardware Acceleration Set Up" -read -r -p "${TAB3}Do you need the intel-media-va-driver-non-free driver for HW encoding (Debian 12 only)? " prompt +read -r -p "${TAB3}Do you need the intel-media-va-driver-non-free driver for HW encoding (Debian 13 only)? " prompt if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then msg_info "Installing Intel Hardware Acceleration (non-free)" cat <<'EOF' >/etc/apt/sources.list.d/non-free.sources From 91d2f5f5eedf0e5d9b90e9144240c0bb95db40b9 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 19 Sep 2025 09:51:14 +0200 Subject: [PATCH 1055/1733] Update tunarr-install.sh --- install/tunarr-install.sh | 6 ------ 1 file changed, 6 deletions(-) diff --git a/install/tunarr-install.sh b/install/tunarr-install.sh index 606ea4efa..ebad1df24 100644 --- a/install/tunarr-install.sh +++ b/install/tunarr-install.sh @@ -14,11 +14,6 @@ network_check update_os msg_info "Setting Up Hardware Acceleration" -$STD apt-get -y install \ - va-driver-all \ - ocl-icd-libopencl1 \ - vainfo \ - intel-gpu-tools if [[ "$CTTYPE" == "0" ]]; then $STD adduser "$(id -un)" video $STD adduser "$(id -un)" render @@ -51,7 +46,6 @@ EOF ocl-icd-libopencl1 \ mesa-opencl-icd \ mesa-va-drivers \ - libmfx1 \ libvpl2 \ vainfo \ intel-gpu-tools From 1ac0e0e792922a7e8c57ba01da35b042a4e61498 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 19 Sep 2025 09:55:41 +0200 Subject: [PATCH 1056/1733] Update outline.sh --- ct/outline.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/outline.sh b/ct/outline.sh index 41bfa2fbb..24a42b12c 100644 --- a/ct/outline.sh +++ b/ct/outline.sh @@ -37,7 +37,7 @@ function update_script() { cp /opt/outline/.env /opt msg_ok "Backup created" - fetch_and_deploy_gh_release "outline" "outline/outline" "tarball" + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "outline" "outline/outline" "tarball" msg_info "Updating ${APP}" cd /opt/outline From 37e6c5070e66a7211c536e35be821a016e3d5fa7 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 19 Sep 2025 07:55:57 +0000 Subject: [PATCH 1057/1733] Update .app files --- ct/headers/outline | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/outline diff --git a/ct/headers/outline b/ct/headers/outline new file mode 100644 index 000000000..66e98f4d2 --- /dev/null +++ b/ct/headers/outline @@ -0,0 +1,6 @@ + ____ __ ___ + / __ \__ __/ /_/ (_)___ ___ + / / / / / / / __/ / / __ \/ _ \ +/ /_/ / /_/ / /_/ / / / / / __/ +\____/\__,_/\__/_/_/_/ /_/\___/ + From a03d46f5b2fa0210f047ad98637c289ec55fc0d0 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 19 Sep 2025 11:03:21 +0200 Subject: [PATCH 1058/1733] Update build.func --- misc/build.func | 72 +++++++++++++++++++++++-------------------------- 1 file changed, 34 insertions(+), 38 deletions(-) diff --git a/misc/build.func b/misc/build.func index ecff03297..8ad9ec13b 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2372,47 +2372,43 @@ EOF if [ "$var_os" != "alpine" ]; then msg_info "Waiting for network in LXC container" sleep 2 + for i in {1..10}; do - # 1. Primary check: ICMP ping (fastest, but may be blocked by ISP/firewall) - if pct exec "$CTID" -- ping -c1 -W1 deb.debian.org >/dev/null 2>&1; then - msg_ok "Network in LXC is reachable (ping)" - break + # --- Check 1: Has CT an IP? --- + ip_in_lxc=$(pct exec "$CTID" -- ip -4 addr show dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) + if [ -z "$ip_in_lxc" ]; then + msg_warn "No IP in LXC yet (try $i/10) – waiting..." + sleep 3 + continue fi - # Wait and retry if not reachable yet - if [ "$i" -lt 10 ]; then - if [ "$i" -le 3 ]; then - sleep 2 - else - msg_warn "No network in LXC yet (try $i/10) – waiting..." - sleep 3 - fi - else - # After 10 unsuccessful ping attempts, try HTTP connectivity via wget as fallback - msg_warn "Ping failed 10 times. Trying HTTP connectivity check (wget) as fallback..." - if pct exec "$CTID" -- wget -q --spider http://deb.debian.org; then - msg_ok "Network in LXC is reachable (wget fallback)" - else - msg_error "No network in LXC after all checks." - read -r -p "Set fallback DNS (1.1.1.1/8.8.8.8)? [y/N]: " choice - case "$choice" in - [yY]*) - pct set "$CTID" --nameserver 1.1.1.1 - pct set "$CTID" --nameserver 8.8.8.8 - # Final attempt with wget after DNS change - if pct exec "$CTID" -- wget -q --spider http://deb.debian.org; then - msg_ok "Network reachable after DNS fallback" - else - msg_error "Still no network/DNS in LXC! Aborting customization." - exit_script - fi - ;; - *) - msg_error "Aborted by user – no DNS fallback set." - exit_script - ;; - esac - fi + + # --- Check 2: Can reach gateway? --- + if ! pct exec "$CTID" -- ping -c1 -W1 "$GATEWAY" >/dev/null 2>&1; then + msg_warn "CT $CTID has IP $ip_in_lxc but cannot reach gateway $GATEWAY (try $i/10)" + sleep 3 + continue + fi + + # --- Check 3: Can resolve DNS? --- + if pct exec "$CTID" -- getent hosts deb.debian.org >/dev/null 2>&1; then + msg_ok "Network in LXC is reachable (DNS OK, IP $ip_in_lxc)" break + else + msg_warn "CT $CTID has IP $ip_in_lxc, gateway OK, but DNS failed (try $i/10)" + sleep 3 + fi + + # --- End of loop fallback --- + if [ "$i" -eq 10 ]; then + msg_warn "DNS still failing after 10 attempts. Applying fallback resolv.conf..." + pct exec "$CTID" -- bash -c 'echo "nameserver 1.1.1.1" > /etc/resolv.conf && echo "nameserver 8.8.8.8" >> /etc/resolv.conf' + + if pct exec "$CTID" -- getent hosts deb.debian.org >/dev/null 2>&1; then + msg_ok "Network reachable after DNS fallback" + else + msg_error "Still no DNS/network in LXC! Aborting customization." + exit_script + fi fi done fi From 419c780153c8b5cd3f9c2dc4fd6706ddaaef683d Mon Sep 17 00:00:00 2001 From: Cobalt <65132371+cobaltgit@users.noreply.github.com> Date: Thu, 18 Sep 2025 10:57:40 +0100 Subject: [PATCH 1059/1733] alpine-caddy: change name and slug in frontend json --- frontend/public/json/caddy.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/public/json/caddy.json b/frontend/public/json/caddy.json index 0f59a88de..607a9f7e6 100644 --- a/frontend/public/json/caddy.json +++ b/frontend/public/json/caddy.json @@ -1,6 +1,6 @@ { - "name": "Alpine-Caddy", - "slug": "alpine-caddy", + "name": "Caddy", + "slug": "caddy", "categories": [ 21 ], From e9d15d413ee13949a1541ea1dddea9ef61f2e496 Mon Sep 17 00:00:00 2001 From: cobaltgit Date: Thu, 18 Sep 2025 17:40:53 +0100 Subject: [PATCH 1060/1733] ct: add alpine-ntfy todo: add json, change port to 80 --- ct/alpine-ntfy.sh | 43 ++++++++++++++++++++++++++++++++++ ct/ntfy.sh | 1 + install/alpine-ntfy-install.sh | 24 +++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 ct/alpine-ntfy.sh create mode 100644 ct/ntfy.sh create mode 100644 install/alpine-ntfy-install.sh diff --git a/ct/alpine-ntfy.sh b/ct/alpine-ntfy.sh new file mode 100644 index 000000000..2fdba6796 --- /dev/null +++ b/ct/alpine-ntfy.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: cobalt (cobaltgit) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://ntfy.sh/ + +APP="ntfy" +var_tags="${var_tags:-notification}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-256}" +var_disk="${var_disk:-2}" +var_os="${var_os:-alpine}" +var_version="${var_version:-3.22}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +init_error_traps + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /var ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating $APP LXC" + $STD apk -U upgrade + msg_ok "Updated $APP LXC" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" diff --git a/ct/ntfy.sh b/ct/ntfy.sh new file mode 100644 index 000000000..c2c61a728 --- /dev/null +++ b/ct/ntfy.sh @@ -0,0 +1 @@ +# placeholder for CI/CD to run diff --git a/install/alpine-ntfy-install.sh b/install/alpine-ntfy-install.sh new file mode 100644 index 000000000..85e123e79 --- /dev/null +++ b/install/alpine-ntfy-install.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: cobalt (cobaltgit) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://ntfy.sh/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +init_error_traps +setting_up_container +network_check +update_os + +msg_info "Installing ntfy" +$STD apk add --no-cache ntfy ntfy-openrc +$STD rc-update add ntfy default +$STD service ntfy start +msg_ok "Installed ntfy" + +motd_ssh +customize + From 0f774893149e5bf2fc9a82f386ff92b2e828715b Mon Sep 17 00:00:00 2001 From: cobaltgit Date: Thu, 18 Sep 2025 17:42:22 +0100 Subject: [PATCH 1061/1733] alpine-ntfy: add json --- frontend/public/json/ntfy.json | 47 ++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 frontend/public/json/ntfy.json diff --git a/frontend/public/json/ntfy.json b/frontend/public/json/ntfy.json new file mode 100644 index 000000000..0bc32bbcc --- /dev/null +++ b/frontend/public/json/ntfy.json @@ -0,0 +1,47 @@ +{ + "name": "ntfy", + "slug": "ntfy", + "categories": [ + 19 + ], + "date_created": "2024-05-02", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 80, + "documentation": "https://docs.ntfy.sh/", + "website": "https://ntfy.sh/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/ntfy.webp", + "config_path": "/etc/ntfy/server.yml", + "description": "ntfy (pronounced notify) is a simple HTTP-based pub-sub notification service. It allows you to send notifications to your phone or desktop via scripts from any computer, and/or using a REST API. It's infinitely flexible, and 100% free software.", + "install_methods": [ + { + "type": "default", + "script": "ct/ntfy.sh", + "resources": { + "cpu": 1, + "ram": 512, + "hdd": 2, + "os": "debian", + "version": "12" + } + }, + { + "type": "alpine", + "script": "ct/alpine-ntfy.sh", + "resources": { + "cpu": 1, + "ram": 256, + "hdd": 2, + "os": "alpine", + "version": "3.22" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} + From 331f7c31f9047a415c5d182d9eb2dab80920babc Mon Sep 17 00:00:00 2001 From: cobaltgit Date: Thu, 18 Sep 2025 17:58:49 +0100 Subject: [PATCH 1062/1733] alpine-ntfy: use setcap and bind to 80 --- ct/alpine-ntfy.sh | 3 ++- install/alpine-ntfy-install.sh | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/ct/alpine-ntfy.sh b/ct/alpine-ntfy.sh index 2fdba6796..118924908 100644 --- a/ct/alpine-ntfy.sh +++ b/ct/alpine-ntfy.sh @@ -23,12 +23,13 @@ function update_script() { header_info check_container_storage check_container_resources - if [[ ! -d /var ]]; then + if [[ ! -d /etc/ntfy ]]; then msg_error "No ${APP} Installation Found!" exit fi msg_info "Updating $APP LXC" $STD apk -U upgrade + setcap 'cap_net_bind_service=+ep' /usr/bin/ntfy msg_ok "Updated $APP LXC" exit } diff --git a/install/alpine-ntfy-install.sh b/install/alpine-ntfy-install.sh index 85e123e79..0369e8966 100644 --- a/install/alpine-ntfy-install.sh +++ b/install/alpine-ntfy-install.sh @@ -14,7 +14,9 @@ network_check update_os msg_info "Installing ntfy" -$STD apk add --no-cache ntfy ntfy-openrc +$STD apk add --no-cache ntfy ntfy-openrc libcap +sed -i '/^listen-http/s/^\(.*\)$/#\1\n/' /etc/ntfy/server.yml # listen on port 80 +setcap 'cap_net_bind_service=+ep' /usr/bin/ntfy # work around permission denied error when binding to :80 $STD rc-update add ntfy default $STD service ntfy start msg_ok "Installed ntfy" From 05a8a13bfd65c8ad98019ada2e3ef1bb6dacf5cb Mon Sep 17 00:00:00 2001 From: cobaltgit Date: Thu, 18 Sep 2025 18:00:08 +0100 Subject: [PATCH 1063/1733] alpine-ntfy: rename app to alpine-ntfy --- ct/alpine-ntfy.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/alpine-ntfy.sh b/ct/alpine-ntfy.sh index 118924908..f1cf340cd 100644 --- a/ct/alpine-ntfy.sh +++ b/ct/alpine-ntfy.sh @@ -5,7 +5,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Source: https://ntfy.sh/ -APP="ntfy" +APP="Alpine-ntfy" var_tags="${var_tags:-notification}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-256}" From 0e0352c2f9e7b7b14bfd28427e44c448f08d87ac Mon Sep 17 00:00:00 2001 From: cobaltgit Date: Thu, 18 Sep 2025 18:00:58 +0100 Subject: [PATCH 1064/1733] alpine-ntfy: restart ntfy after update --- ct/alpine-ntfy.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ct/alpine-ntfy.sh b/ct/alpine-ntfy.sh index f1cf340cd..94e47f77a 100644 --- a/ct/alpine-ntfy.sh +++ b/ct/alpine-ntfy.sh @@ -31,6 +31,10 @@ function update_script() { $STD apk -U upgrade setcap 'cap_net_bind_service=+ep' /usr/bin/ntfy msg_ok "Updated $APP LXC" + + msg_info "Restarting ntfy" + rc-service ntfy restart + msg_ok "Restarted ntfy" exit } From 8770140fc49418ae8ea762bfa910aaba0686d53d Mon Sep 17 00:00:00 2001 From: cobaltgit Date: Thu, 18 Sep 2025 18:02:03 +0100 Subject: [PATCH 1065/1733] alpine-caddy: revert thingy, forgot to change branch n allat --- frontend/public/json/caddy.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/public/json/caddy.json b/frontend/public/json/caddy.json index 607a9f7e6..0f59a88de 100644 --- a/frontend/public/json/caddy.json +++ b/frontend/public/json/caddy.json @@ -1,6 +1,6 @@ { - "name": "Caddy", - "slug": "caddy", + "name": "Alpine-Caddy", + "slug": "alpine-caddy", "categories": [ 21 ], From 0207cdbc925fbaabfc8817fbd39075ae204e1309 Mon Sep 17 00:00:00 2001 From: cobaltgit Date: Thu, 18 Sep 2025 18:06:54 +0100 Subject: [PATCH 1066/1733] alpine-caddy: remove commands, listen on 8080 for now? --- install/alpine-ntfy-install.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/install/alpine-ntfy-install.sh b/install/alpine-ntfy-install.sh index 0369e8966..85e123e79 100644 --- a/install/alpine-ntfy-install.sh +++ b/install/alpine-ntfy-install.sh @@ -14,9 +14,7 @@ network_check update_os msg_info "Installing ntfy" -$STD apk add --no-cache ntfy ntfy-openrc libcap -sed -i '/^listen-http/s/^\(.*\)$/#\1\n/' /etc/ntfy/server.yml # listen on port 80 -setcap 'cap_net_bind_service=+ep' /usr/bin/ntfy # work around permission denied error when binding to :80 +$STD apk add --no-cache ntfy ntfy-openrc $STD rc-update add ntfy default $STD service ntfy start msg_ok "Installed ntfy" From 73e7b0d9f350d27fec9e6834bb12d6b0949ffe44 Mon Sep 17 00:00:00 2001 From: cobaltgit Date: Thu, 18 Sep 2025 18:08:10 +0100 Subject: [PATCH 1067/1733] alpine-ntfy: revert, and lament using the wrong commit message HAH ohh it was comments, not commands --- install/alpine-ntfy-install.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/install/alpine-ntfy-install.sh b/install/alpine-ntfy-install.sh index 85e123e79..4fc3f89c5 100644 --- a/install/alpine-ntfy-install.sh +++ b/install/alpine-ntfy-install.sh @@ -14,7 +14,9 @@ network_check update_os msg_info "Installing ntfy" -$STD apk add --no-cache ntfy ntfy-openrc +$STD apk add --no-cache ntfy ntfy-openrc libcap +sed -i '/^listen-http/s/^\(.*\)$/#\1\n/' /etc/ntfy/server.yml +setcap 'cap_net_bind_service=+ep' /usr/bin/ntfy $STD rc-update add ntfy default $STD service ntfy start msg_ok "Installed ntfy" From ef76064f7c9b3a728ee803a38d6a99802abdb9da Mon Sep 17 00:00:00 2001 From: cobaltgit Date: Thu, 18 Sep 2025 18:17:22 +0100 Subject: [PATCH 1068/1733] alpine-ntfy: add header --- ct/headers/alpine-ntfy | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/alpine-ntfy diff --git a/ct/headers/alpine-ntfy b/ct/headers/alpine-ntfy new file mode 100644 index 000000000..bc4164342 --- /dev/null +++ b/ct/headers/alpine-ntfy @@ -0,0 +1,6 @@ + ___ __ _ __ ____ + / | / /___ (_)___ ___ ____ / /_/ __/_ __ + / /| | / / __ \/ / __ \/ _ \______/ __ \/ __/ /_/ / / / + / ___ |/ / /_/ / / / / / __/_____/ / / / /_/ __/ /_/ / +/_/ |_/_/ .___/_/_/ /_/\___/ /_/ /_/\__/_/ \__, / + /_/ /____/ From 06e2ed38a003ccd46e53fd39e73974262a98982b Mon Sep 17 00:00:00 2001 From: cobaltgit Date: Thu, 18 Sep 2025 19:06:16 +0100 Subject: [PATCH 1069/1733] alpine-ntfy: revert header --- ct/headers/alpine-ntfy | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 ct/headers/alpine-ntfy diff --git a/ct/headers/alpine-ntfy b/ct/headers/alpine-ntfy deleted file mode 100644 index bc4164342..000000000 --- a/ct/headers/alpine-ntfy +++ /dev/null @@ -1,6 +0,0 @@ - ___ __ _ __ ____ - / | / /___ (_)___ ___ ____ / /_/ __/_ __ - / /| | / / __ \/ / __ \/ _ \______/ __ \/ __/ /_/ / / / - / ___ |/ / /_/ / / / / / __/_____/ / / / /_/ __/ /_/ / -/_/ |_/_/ .___/_/_/ /_/\___/ /_/ /_/\__/_/ \__, / - /_/ /____/ From f2271e46fdb078bdcf4af1e0391aff8a2102db5a Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 19 Sep 2025 09:44:33 +0000 Subject: [PATCH 1070/1733] Update .app files --- ct/headers/alpine-ntfy | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/alpine-ntfy diff --git a/ct/headers/alpine-ntfy b/ct/headers/alpine-ntfy new file mode 100644 index 000000000..bc4164342 --- /dev/null +++ b/ct/headers/alpine-ntfy @@ -0,0 +1,6 @@ + ___ __ _ __ ____ + / | / /___ (_)___ ___ ____ / /_/ __/_ __ + / /| | / / __ \/ / __ \/ _ \______/ __ \/ __/ /_/ / / / + / ___ |/ / /_/ / / / / / __/_____/ / / / /_/ __/ /_/ / +/_/ |_/_/ .___/_/_/ /_/\___/ /_/ /_/\__/_/ \__, / + /_/ /____/ From 19299fdde41da8ae0af1ea65a3d7a5c42c52cca9 Mon Sep 17 00:00:00 2001 From: Cobalt <65132371+cobaltgit@users.noreply.github.com> Date: Fri, 19 Sep 2025 11:29:33 +0100 Subject: [PATCH 1071/1733] Replace init_error_traps with catch_errors --- install/alpine-ntfy-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/alpine-ntfy-install.sh b/install/alpine-ntfy-install.sh index 4fc3f89c5..04aeddc07 100644 --- a/install/alpine-ntfy-install.sh +++ b/install/alpine-ntfy-install.sh @@ -8,7 +8,7 @@ source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 -init_error_traps +catch_errors setting_up_container network_check update_os From 2a92cf8c26812d73881e3360021245601539aaf8 Mon Sep 17 00:00:00 2001 From: Cobalt <65132371+cobaltgit@users.noreply.github.com> Date: Fri, 19 Sep 2025 11:29:44 +0100 Subject: [PATCH 1072/1733] ditto --- ct/alpine-ntfy.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/alpine-ntfy.sh b/ct/alpine-ntfy.sh index 94e47f77a..4fc65b5d1 100644 --- a/ct/alpine-ntfy.sh +++ b/ct/alpine-ntfy.sh @@ -17,7 +17,7 @@ var_unprivileged="${var_unprivileged:-1}" header_info "$APP" variables color -init_error_traps +catch_errors function update_script() { header_info From fe35a5e62cfd863a9f6f4111dafd68b3d958fd68 Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Fri, 19 Sep 2025 17:42:53 +0200 Subject: [PATCH 1073/1733] Add build-essential and libpq-dev to dependencies --- install/warracker-install.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/install/warracker-install.sh b/install/warracker-install.sh index fb83fba04..95c051a74 100644 --- a/install/warracker-install.sh +++ b/install/warracker-install.sh @@ -16,7 +16,10 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ apt-transport-https \ - ca-certificates nginx + build-essential \ + ca-certificates \ + libpq-dev \ + nginx msg_ok "Installed Dependencies" PYTHON_VERSION="3.11" setup_uv From a82f2520fbe88044cf068e0dd25ef4e0c71cd0e8 Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Fri, 19 Sep 2025 19:56:12 +0200 Subject: [PATCH 1074/1733] Update Warracker installation script for versioning --- install/warracker-install.sh | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/install/warracker-install.sh b/install/warracker-install.sh index 95c051a74..8b395e0df 100644 --- a/install/warracker-install.sh +++ b/install/warracker-install.sh @@ -48,19 +48,20 @@ $STD sudo -u postgres psql -d "$DB_NAME" -c "ALTER DEFAULT PRIVILEGES IN SCHEMA } >>~/warracker.creds msg_ok "Installed PostgreSQL" -fetch_and_deploy_gh_release "warracker" "sassanix/Warracker" "tarball" "latest" "/opt/warracker" +fetch_and_deploy_gh_release "warracker" "sassanix/Warracker" "tarball" "0.10.1.10-beta" "/opt/warracker" msg_info "Installing Warracker" cd /opt/warracker/backend $STD uv venv .venv $STD source .venv/bin/activate $STD uv pip install -r requirements.txt -mv /opt/warracker/env.example /opt/warracker/.env +mv /opt/warracker/env.example /opt/.env sed -i \ -e "s/your_secure_database_password/$DB_PASS/" \ -e "s/your_secure_admin_password/$DB_ADMIN_PASS/" \ -e "s|^# DB_PORT=5432$|DB_HOST=127.0.0.1|" \ - /opt/warracker/.env + -e "s|your_very_secure_flask_secret_key_change_this_in_production|$(openssl rand -base64 32 | tr -d '\n')|" \ + /opt/.env mv /opt/warracker/nginx.conf /etc/nginx/sites-available/warracker.conf sed -i \ @@ -85,7 +86,7 @@ After=network.target [Service] Type=oneshot WorkingDirectory=/opt/warracker/backend/migrations -EnvironmentFile=/opt/warracker/.env +EnvironmentFile=/opt/.env ExecStart=/opt/warracker/backend/.venv/bin/python apply_migrations.py [Install] @@ -100,7 +101,7 @@ Requires=warrackermigration.service [Service] WorkingDirectory=/opt/warracker -EnvironmentFile=/opt/warracker/.env +EnvironmentFile=/opt/.env ExecStart=/opt/warracker/backend/.venv/bin/gunicorn --config /opt/warracker/backend/gunicorn_config.py backend:create_app() --bind 127.0.0.1:5000 Restart=always From 026756981c53170a5ea809f49bea28548a58ad08 Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Fri, 19 Sep 2025 19:57:15 +0200 Subject: [PATCH 1075/1733] Remove redundant update messages in warracker.sh --- ct/warracker.sh | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ct/warracker.sh b/ct/warracker.sh index 8e090b84c..9da5cf518 100644 --- a/ct/warracker.sh +++ b/ct/warracker.sh @@ -30,16 +30,13 @@ function update_script() { if check_for_gh_release "signoz" "SigNoz/signoz"; then msg_info "Stopping Services" + systemctl stop warrackermigration systemctl stop warracker systemctl stop ngninx msg_ok "Stopped Services" fetch_and_deploy_gh_release "warracker" "sassanix/Warracker" "tarball" "latest" "/opt/warracker" - msg_info "Updating ${APP}" - - msg_ok "Updated $APP" - msg_info "Starting Services" systemctl start warracker systemctl start ngninx From 8135cfd596eba93fd63ca93185d7f0fe1aa6d953 Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Fri, 19 Sep 2025 20:03:05 +0200 Subject: [PATCH 1076/1733] Update Warracker version in install script --- install/warracker-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/warracker-install.sh b/install/warracker-install.sh index 8b395e0df..0c3c2260b 100644 --- a/install/warracker-install.sh +++ b/install/warracker-install.sh @@ -48,7 +48,7 @@ $STD sudo -u postgres psql -d "$DB_NAME" -c "ALTER DEFAULT PRIVILEGES IN SCHEMA } >>~/warracker.creds msg_ok "Installed PostgreSQL" -fetch_and_deploy_gh_release "warracker" "sassanix/Warracker" "tarball" "0.10.1.10-beta" "/opt/warracker" +fetch_and_deploy_gh_release "warracker" "sassanix/Warracker" "tarball" "0.10.1.10" "/opt/warracker" msg_info "Installing Warracker" cd /opt/warracker/backend From cf9ae4500bb4075363f52d733e751e9baef40bc6 Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Fri, 19 Sep 2025 20:09:47 +0200 Subject: [PATCH 1077/1733] Change directory check from /opt/signoz to /opt/warracker --- ct/warracker.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/warracker.sh b/ct/warracker.sh index 9da5cf518..c2b2a683c 100644 --- a/ct/warracker.sh +++ b/ct/warracker.sh @@ -23,7 +23,7 @@ function update_script() { header_info check_container_storage check_container_resources - if [[ ! -d /opt/signoz ]]; then + if [[ ! -d /opt/warracker ]]; then msg_error "No ${APP} Installation Found!" exit fi From 3c1bf165c9f8e817d06eff0708ec577d2673050a Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Fri, 19 Sep 2025 20:10:43 +0200 Subject: [PATCH 1078/1733] Fix typo in nginx service commands --- ct/warracker.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/warracker.sh b/ct/warracker.sh index c2b2a683c..c9aa36b9e 100644 --- a/ct/warracker.sh +++ b/ct/warracker.sh @@ -32,14 +32,14 @@ function update_script() { msg_info "Stopping Services" systemctl stop warrackermigration systemctl stop warracker - systemctl stop ngninx + systemctl stop nginx msg_ok "Stopped Services" fetch_and_deploy_gh_release "warracker" "sassanix/Warracker" "tarball" "latest" "/opt/warracker" msg_info "Starting Services" systemctl start warracker - systemctl start ngninx + systemctl start nginx msg_ok "Started Services" msg_ok "Updated Successfully" From 5ac0836d6c58d51dd2c7c3c0bde21d3bec9c8469 Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Fri, 19 Sep 2025 20:12:38 +0200 Subject: [PATCH 1079/1733] Update Python version from 3.11 to 3.12 --- install/warracker-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/warracker-install.sh b/install/warracker-install.sh index 0c3c2260b..a5a8bc177 100644 --- a/install/warracker-install.sh +++ b/install/warracker-install.sh @@ -22,7 +22,7 @@ $STD apt-get install -y \ nginx msg_ok "Installed Dependencies" -PYTHON_VERSION="3.11" setup_uv +PYTHON_VERSION="3.12" setup_uv PG_VERSION="17" setup_postgresql msg_info "Installing Postgresql" From 1ab24bb22ce75d679b45e98c97532a5b5c9d5bf3 Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Fri, 19 Sep 2025 20:20:16 +0200 Subject: [PATCH 1080/1733] Update Warracker installation to use latest release --- install/warracker-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/warracker-install.sh b/install/warracker-install.sh index a5a8bc177..a4fb3c481 100644 --- a/install/warracker-install.sh +++ b/install/warracker-install.sh @@ -48,7 +48,7 @@ $STD sudo -u postgres psql -d "$DB_NAME" -c "ALTER DEFAULT PRIVILEGES IN SCHEMA } >>~/warracker.creds msg_ok "Installed PostgreSQL" -fetch_and_deploy_gh_release "warracker" "sassanix/Warracker" "tarball" "0.10.1.10" "/opt/warracker" +fetch_and_deploy_gh_release "warracker" "sassanix/Warracker" "tarball" "latest" "/opt/warracker" msg_info "Installing Warracker" cd /opt/warracker/backend From c6ae026aaf228b5353a0ce2c2dd3d37ed7807e1e Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Fri, 19 Sep 2025 20:27:41 +0200 Subject: [PATCH 1081/1733] Update warracker.json with new project details --- frontend/public/json/warracker.json | 40 +++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 frontend/public/json/warracker.json diff --git a/frontend/public/json/warracker.json b/frontend/public/json/warracker.json new file mode 100644 index 000000000..a276b04dc --- /dev/null +++ b/frontend/public/json/warracker.json @@ -0,0 +1,40 @@ +{ + "name": "Warracker", + "slug": "warracker", + "categories": [ + 12 + ], + "date_created": "2025-09-19", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 80, + "documentation": null, + "config_path": "/opt/.env", + "website": "https://warracker.com/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/warracker.webp", + "description": "Warracker is an open source, self-hostable warranty tracker to monitor expirations, store receipts, files. You own the data, your rules!", + "install_methods": [ + { + "type": "default", + "script": "ct/warracker.sh", + "resources": { + "cpu": 1, + "ram": 512, + "hdd": 4, + "os": "Debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "The first user you register will be the admin user.", + "type": "info" + } + ] +} From 566230585baff2a7ba361d570ee492aca0a647ed Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Fri, 19 Sep 2025 20:28:25 +0200 Subject: [PATCH 1082/1733] Update default resource limits and service checks --- ct/warracker.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ct/warracker.sh b/ct/warracker.sh index c9aa36b9e..b1f92bd0f 100644 --- a/ct/warracker.sh +++ b/ct/warracker.sh @@ -7,9 +7,9 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV APP="warracker" var_tags="${var_tags:-warranty}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-4096}" -var_disk="${var_disk:-20}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-512}" +var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" @@ -28,7 +28,7 @@ function update_script() { exit fi - if check_for_gh_release "signoz" "SigNoz/signoz"; then + if check_for_gh_release "warracker" "sassanix/Warracker"; then msg_info "Stopping Services" systemctl stop warrackermigration systemctl stop warracker From 129afe83d7fc056ecceb83a02a8897fefbafbe94 Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Fri, 19 Sep 2025 21:12:02 +0200 Subject: [PATCH 1083/1733] Simplify dependency installation in script --- install/warracker-install.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/install/warracker-install.sh b/install/warracker-install.sh index a4fb3c481..7b92cdf0b 100644 --- a/install/warracker-install.sh +++ b/install/warracker-install.sh @@ -15,9 +15,7 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - apt-transport-https \ build-essential \ - ca-certificates \ libpq-dev \ nginx msg_ok "Installed Dependencies" From eb4a91eb50e18bec65db432b919341cad6ff6f31 Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Fri, 19 Sep 2025 21:18:16 +0200 Subject: [PATCH 1084/1733] Refactor warracker-install.sh for Nginx and services --- install/warracker-install.sh | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/install/warracker-install.sh b/install/warracker-install.sh index 7b92cdf0b..c10fc723e 100644 --- a/install/warracker-install.sh +++ b/install/warracker-install.sh @@ -60,7 +60,10 @@ sed -i \ -e "s|^# DB_PORT=5432$|DB_HOST=127.0.0.1|" \ -e "s|your_very_secure_flask_secret_key_change_this_in_production|$(openssl rand -base64 32 | tr -d '\n')|" \ /opt/.env +mkdir -p /data/uploads +msg_ok "Installed Warracker" +msg_info "Configuring Nginx" mv /opt/warracker/nginx.conf /etc/nginx/sites-available/warracker.conf sed -i \ -e "s|alias /var/www/html/locales/;|alias /opt/warracker/locales/;|" \ @@ -71,11 +74,9 @@ ln -s /etc/nginx/sites-available/warracker.conf /etc/nginx/sites-enabled/warrack rm /etc/nginx/sites-enabled/default systemctl restart nginx -mkdir -p /data/uploads +msg_ok "Configured Nginx" -msg_ok "Installed Warracker" - -msg_info "Creating Services" +msg_info "Creating systemd services" cat </etc/systemd/system/warrackermigration.service [Unit] Description=Warracker Migration Service @@ -107,7 +108,7 @@ Restart=always WantedBy=multi-user.target EOF systemctl enable -q --now warracker -msg_ok "Created Services" +msg_ok "Started Warracker Services" motd_ssh customize From 527c31a94ecf623c090ef6166f8c22699e5aff9d Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Sat, 20 Sep 2025 18:13:44 +0200 Subject: [PATCH 1085/1733] Enhance warracker.sh to update application Added commands to update the application and install dependencies. --- ct/warracker.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ct/warracker.sh b/ct/warracker.sh index b1f92bd0f..c435c60a9 100644 --- a/ct/warracker.sh +++ b/ct/warracker.sh @@ -37,11 +37,17 @@ function update_script() { fetch_and_deploy_gh_release "warracker" "sassanix/Warracker" "tarball" "latest" "/opt/warracker" + msg_info "Updating $APP" + cd /opt/warracker/backend + $STD uv venv .venv + $STD source .venv/bin/activate + $STD uv pip install -r requirements.txt + msg_ok "Updated $APP" + msg_info "Starting Services" systemctl start warracker systemctl start nginx msg_ok "Started Services" - msg_ok "Updated Successfully" fi exit From 192ab11b623f3edab50f3cc3efd9236698a60b2d Mon Sep 17 00:00:00 2001 From: Bas van den Berg <74251551+bvdberg01@users.noreply.github.com> Date: Sun, 21 Sep 2025 13:34:39 +0200 Subject: [PATCH 1086/1733] Manyfold-script deferred --- ct/{ => deferred}/manyfold.sh | 0 frontend/public/json/manyfold.json | 35 ---------------------- install/{ => deferred}/manyfold-install.sh | 0 3 files changed, 35 deletions(-) rename ct/{ => deferred}/manyfold.sh (100%) delete mode 100644 frontend/public/json/manyfold.json rename install/{ => deferred}/manyfold-install.sh (100%) diff --git a/ct/manyfold.sh b/ct/deferred/manyfold.sh similarity index 100% rename from ct/manyfold.sh rename to ct/deferred/manyfold.sh diff --git a/frontend/public/json/manyfold.json b/frontend/public/json/manyfold.json deleted file mode 100644 index bb232666c..000000000 --- a/frontend/public/json/manyfold.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "Manyfold", - "slug": "manyfold", - "categories": [ - 24 - ], - "date_created": "2025-03-18", - "type": "ct", - "updateable": false, - "privileged": false, - "interface_port": 80, - "documentation": "https://manyfold.app/sysadmin/", - "website": "https://manyfold.app/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/manyfold.webp", - "config_path": "", - "description": "Manyfold is an open source, self-hosted web application for managing a collection of 3d models, particularly focused on 3d printing.", - "install_methods": [ - { - "type": "default", - "script": "ct/manyfold.sh", - "resources": { - "cpu": 2, - "ram": 1024, - "hdd": 15, - "os": "debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] -} diff --git a/install/manyfold-install.sh b/install/deferred/manyfold-install.sh similarity index 100% rename from install/manyfold-install.sh rename to install/deferred/manyfold-install.sh From 923a24a1ccea912610e188585882640f6f49f280 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 22 Sep 2025 09:34:28 +0200 Subject: [PATCH 1087/1733] Update shell detection --- misc/core.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/core.func b/misc/core.func index 26ee3e12c..fde0281fc 100644 --- a/misc/core.func +++ b/misc/core.func @@ -154,7 +154,7 @@ silent() { # Check if the shell is using bash shell_check() { - if [[ "$(basename "$SHELL")" != "bash" ]]; then + if [[ "$(ps -p $$ -o comm=)" != "bash" ]]; then clear msg_error "Your default shell is currently not set to Bash. To use these scripts, please switch to the Bash shell." echo -e "\nExiting..." From 2db707079b7b92a339d8fe370ab1ab4a338a2b73 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 22 Sep 2025 11:31:19 +0200 Subject: [PATCH 1088/1733] Update build.func --- misc/build.func | 70 ++++++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 38 deletions(-) diff --git a/misc/build.func b/misc/build.func index 8ad9ec13b..b00eac9cc 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2371,46 +2371,40 @@ EOF if [ "$var_os" != "alpine" ]; then msg_info "Waiting for network in LXC container" - sleep 2 - for i in {1..10}; do - # --- Check 1: Has CT an IP? --- - ip_in_lxc=$(pct exec "$CTID" -- ip -4 addr show dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) - if [ -z "$ip_in_lxc" ]; then - msg_warn "No IP in LXC yet (try $i/10) – waiting..." - sleep 3 - continue - fi - - # --- Check 2: Can reach gateway? --- - if ! pct exec "$CTID" -- ping -c1 -W1 "$GATEWAY" >/dev/null 2>&1; then - msg_warn "CT $CTID has IP $ip_in_lxc but cannot reach gateway $GATEWAY (try $i/10)" - sleep 3 - continue - fi - - # --- Check 3: Can resolve DNS? --- - if pct exec "$CTID" -- getent hosts deb.debian.org >/dev/null 2>&1; then - msg_ok "Network in LXC is reachable (DNS OK, IP $ip_in_lxc)" - break - else - msg_warn "CT $CTID has IP $ip_in_lxc, gateway OK, but DNS failed (try $i/10)" - sleep 3 - fi - - # --- End of loop fallback --- - if [ "$i" -eq 10 ]; then - msg_warn "DNS still failing after 10 attempts. Applying fallback resolv.conf..." - pct exec "$CTID" -- bash -c 'echo "nameserver 1.1.1.1" > /etc/resolv.conf && echo "nameserver 8.8.8.8" >> /etc/resolv.conf' - - if pct exec "$CTID" -- getent hosts deb.debian.org >/dev/null 2>&1; then - msg_ok "Network reachable after DNS fallback" - else - msg_error "Still no DNS/network in LXC! Aborting customization." - exit_script - fi - fi + # Global timeout 20s, aborts early as soon as the network is ready + timeout 20 bash -c ' + # --- Wait until the container has an IP --- + until ip_in_lxc=$(pct exec '"$CTID"' -- ip -4 addr show dev eth0 | awk "/inet / {print \$2}" | cut -d/ -f1) && [ -n "$ip_in_lxc" ]; do + echo " ⏳ No IP in LXC yet – waiting..." + sleep 1 done + + # --- Optional: trigger ARP to speed up gateway availability --- + if pct exec '"$CTID"' -- command -v arping >/dev/null 2>&1; then + pct exec '"$CTID"' -- arping -c1 -I eth0 '"$GATEWAY"' >/dev/null 2>&1 || true + fi + + # --- Wait until the gateway is reachable --- + until pct exec '"$CTID"' -- ping -c1 -W1 '"$GATEWAY"' >/dev/null 2>&1; do + echo " ⏳ CT '"$CTID"' has IP $ip_in_lxc but cannot reach gateway '"$GATEWAY"' – retry..." + sleep 1 + done + ' + + # --- DNS check --- + if pct exec "$CTID" -- getent hosts deb.debian.org >/dev/null 2>&1; then + msg_ok "Network in LXC is reachable (DNS OK, IP $ip_in_lxc)" + else + msg_warn "Network reachable, but DNS failed – applying fallback resolv.conf" + pct exec "$CTID" -- bash -c 'echo "nameserver 1.1.1.1" > /etc/resolv.conf && echo "nameserver 8.8.8.8" >> /etc/resolv.conf' + if pct exec "$CTID" -- getent hosts deb.debian.org >/dev/null 2>&1; then + msg_ok "Network reachable after DNS fallback" + else + msg_error "Still no DNS/network in LXC! Aborting customization." + exit_script + fi + fi fi msg_info "Customizing LXC Container" From 6373e16d449deb2329f803e6e6f6dba6b2f631be Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 22 Sep 2025 11:34:44 +0200 Subject: [PATCH 1089/1733] Refactor network string construction in build_container Improves readability and flexibility by building the NET_STRING variable incrementally, using parameter expansion to include only set values for MAC, GATE, VLAN, and MTU. This change makes the network configuration more robust and easier to maintain. --- misc/build.func | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index b00eac9cc..0bfe74308 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2108,7 +2108,12 @@ start() { build_container() { # if [ "$VERBOSE" == "yes" ]; then set -x; fi - NET_STRING="-net0 name=eth0,bridge=${BRG:-vmbr0}${MAC:-},ip=${NET:-dhcp}${GATE:-}${VLAN:-}${MTU:-}" + NET_STRING="-net0 name=eth0,bridge=${BRG:-vmbr0}" + NET_STRING+="${MAC:+,hwaddr=$MAC}" + NET_STRING+=",ip=${NET:-dhcp}" + NET_STRING+="${GATE:+,gw=$GATE}" + NET_STRING+="${VLAN:+,tag=$VLAN}" + NET_STRING+="${MTU:+,mtu=$MTU}" case "$IPV6_METHOD" in auto) NET_STRING="$NET_STRING,ip6=auto" ;; dhcp) NET_STRING="$NET_STRING,ip6=dhcp" ;; From 44e64d8b98fa5e19a0bac71668822061547b976a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 22 Sep 2025 11:38:22 +0200 Subject: [PATCH 1090/1733] Improve LXC network readiness and DNS fallback logic Refactors the LXC network wait loop to use a for-loop with up to 20 attempts, providing clearer warnings and early exits when network and DNS are ready. Adds more robust handling for DNS failures by applying a fallback resolv.conf only after repeated failures, improving reliability and user feedback during container setup. --- misc/build.func | 70 +++++++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/misc/build.func b/misc/build.func index 0bfe74308..13b476568 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2377,39 +2377,47 @@ EOF if [ "$var_os" != "alpine" ]; then msg_info "Waiting for network in LXC container" - # Global timeout 20s, aborts early as soon as the network is ready - timeout 20 bash -c ' - # --- Wait until the container has an IP --- - until ip_in_lxc=$(pct exec '"$CTID"' -- ip -4 addr show dev eth0 | awk "/inet / {print \$2}" | cut -d/ -f1) && [ -n "$ip_in_lxc" ]; do - echo " ⏳ No IP in LXC yet – waiting..." - sleep 1 - done + # Try up to 20 times (about 20s total), break early when ready + for i in {1..20}; do + ip_in_lxc=$(pct exec "$CTID" -- ip -4 addr show dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) - # --- Optional: trigger ARP to speed up gateway availability --- - if pct exec '"$CTID"' -- command -v arping >/dev/null 2>&1; then - pct exec '"$CTID"' -- arping -c1 -I eth0 '"$GATEWAY"' >/dev/null 2>&1 || true - fi - - # --- Wait until the gateway is reachable --- - until pct exec '"$CTID"' -- ping -c1 -W1 '"$GATEWAY"' >/dev/null 2>&1; do - echo " ⏳ CT '"$CTID"' has IP $ip_in_lxc but cannot reach gateway '"$GATEWAY"' – retry..." - sleep 1 - done - ' - - # --- DNS check --- - if pct exec "$CTID" -- getent hosts deb.debian.org >/dev/null 2>&1; then - msg_ok "Network in LXC is reachable (DNS OK, IP $ip_in_lxc)" - else - msg_warn "Network reachable, but DNS failed – applying fallback resolv.conf" - pct exec "$CTID" -- bash -c 'echo "nameserver 1.1.1.1" > /etc/resolv.conf && echo "nameserver 8.8.8.8" >> /etc/resolv.conf' - if pct exec "$CTID" -- getent hosts deb.debian.org >/dev/null 2>&1; then - msg_ok "Network reachable after DNS fallback" - else - msg_error "Still no DNS/network in LXC! Aborting customization." - exit_script + if [ -z "$ip_in_lxc" ]; then + msg_warn "No IP in LXC yet (try $i/20) – waiting..." + sleep 1 + continue fi - fi + + # Optional ARP warm-up + if pct exec "$CTID" -- command -v arping >/dev/null 2>&1; then + pct exec "$CTID" -- arping -c1 -I eth0 "$GATEWAY" >/dev/null 2>&1 || true + fi + + if ! pct exec "$CTID" -- ping -c1 -W1 "$GATEWAY" >/dev/null 2>&1; then + msg_warn "CT $CTID has IP $ip_in_lxc but cannot reach gateway $GATEWAY (try $i/20)" + sleep 1 + continue + fi + + if pct exec "$CTID" -- getent hosts deb.debian.org >/dev/null 2>&1; then + msg_ok "Network in LXC is reachable (DNS OK, IP $ip_in_lxc)" + break + else + msg_warn "CT $CTID has IP $ip_in_lxc, gateway OK, but DNS failed (try $i/20)" + sleep 1 + fi + + if [ "$i" -eq 20 ]; then + msg_warn "DNS still failing after 20 attempts. Applying fallback resolv.conf..." + pct exec "$CTID" -- bash -c 'echo "nameserver 1.1.1.1" > /etc/resolv.conf && echo "nameserver 8.8.8.8" >> /etc/resolv.conf' + + if pct exec "$CTID" -- getent hosts deb.debian.org >/dev/null 2>&1; then + msg_ok "Network reachable after DNS fallback" + else + msg_error "Still no DNS/network in LXC! Aborting customization." + exit_script + fi + fi + done fi msg_info "Customizing LXC Container" From 00750681ed93897b879105e9c9935c58d941b2e0 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 22 Sep 2025 12:35:24 +0200 Subject: [PATCH 1091/1733] Update build.func --- misc/build.func | 71 ++++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/misc/build.func b/misc/build.func index 13b476568..99b2d56ec 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2377,47 +2377,46 @@ EOF if [ "$var_os" != "alpine" ]; then msg_info "Waiting for network in LXC container" - # Try up to 20 times (about 20s total), break early when ready + # --- Step 1: Wait until the CT has an IP --- for i in {1..20}; do ip_in_lxc=$(pct exec "$CTID" -- ip -4 addr show dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) - - if [ -z "$ip_in_lxc" ]; then - msg_warn "No IP in LXC yet (try $i/20) – waiting..." - sleep 1 - continue - fi - - # Optional ARP warm-up - if pct exec "$CTID" -- command -v arping >/dev/null 2>&1; then - pct exec "$CTID" -- arping -c1 -I eth0 "$GATEWAY" >/dev/null 2>&1 || true - fi - - if ! pct exec "$CTID" -- ping -c1 -W1 "$GATEWAY" >/dev/null 2>&1; then - msg_warn "CT $CTID has IP $ip_in_lxc but cannot reach gateway $GATEWAY (try $i/20)" - sleep 1 - continue - fi - - if pct exec "$CTID" -- getent hosts deb.debian.org >/dev/null 2>&1; then - msg_ok "Network in LXC is reachable (DNS OK, IP $ip_in_lxc)" + if [ -n "$ip_in_lxc" ]; then break - else - msg_warn "CT $CTID has IP $ip_in_lxc, gateway OK, but DNS failed (try $i/20)" - sleep 1 - fi - - if [ "$i" -eq 20 ]; then - msg_warn "DNS still failing after 20 attempts. Applying fallback resolv.conf..." - pct exec "$CTID" -- bash -c 'echo "nameserver 1.1.1.1" > /etc/resolv.conf && echo "nameserver 8.8.8.8" >> /etc/resolv.conf' - - if pct exec "$CTID" -- getent hosts deb.debian.org >/dev/null 2>&1; then - msg_ok "Network reachable after DNS fallback" - else - msg_error "Still no DNS/network in LXC! Aborting customization." - exit_script - fi fi + sleep 1 done + + if [ -z "$ip_in_lxc" ]; then + msg_error "No IP assigned to CT $CTID after 20s" + exit 1 + fi + + # --- Step 2: Wait until gateway responds --- + for i in {1..20}; do + if pct exec "$CTID" -- ping -c1 -W1 "$GATEWAY" >/dev/null 2>&1; then + break + fi + sleep 1 + done + + if [ "$i" -eq 20 ]; then + msg_error "Gateway $GATEWAY unreachable from CT $CTID (IP $ip_in_lxc)" + exit 1 + fi + + # --- Step 3: DNS check --- + if pct exec "$CTID" -- getent hosts deb.debian.org >/dev/null 2>&1; then + msg_ok "Network in LXC is reachable (DNS OK, IP $ip_in_lxc)" + else + msg_warn "Network reachable (IP $ip_in_lxc), but DNS failed – applying fallback resolv.conf" + pct exec "$CTID" -- bash -c 'echo "nameserver 1.1.1.1" > /etc/resolv.conf && echo "nameserver 8.8.8.8" >> /etc/resolv.conf' + if pct exec "$CTID" -- getent hosts deb.debian.org >/dev/null 2>&1; then + msg_ok "Network reachable after DNS fallback" + else + msg_error "Still no DNS/network in LXC! Aborting customization." + exit_script + fi + fi fi msg_info "Customizing LXC Container" From 5b6bbd1ed02df9b93bda75d7bfdadfbc911e53b7 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 22 Sep 2025 12:41:26 +0200 Subject: [PATCH 1092/1733] Improve LXC network wait and gateway check logic Refactors the LXC container network initialization to simplify IP wait logic, reduce gateway ping attempts from 20 to 10, and provide clearer status messages. Now warns instead of failing if the gateway is unreachable but the container has an IP, improving robustness in network checks. --- misc/build.func | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/misc/build.func b/misc/build.func index 99b2d56ec..4d6c1b89a 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2377,12 +2377,10 @@ EOF if [ "$var_os" != "alpine" ]; then msg_info "Waiting for network in LXC container" - # --- Step 1: Wait until the CT has an IP --- + # --- Step 1: Wait for IP --- for i in {1..20}; do ip_in_lxc=$(pct exec "$CTID" -- ip -4 addr show dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) - if [ -n "$ip_in_lxc" ]; then - break - fi + [ -n "$ip_in_lxc" ] && break sleep 1 done @@ -2391,20 +2389,23 @@ EOF exit 1 fi - # --- Step 2: Wait until gateway responds --- - for i in {1..20}; do + # --- Step 2: Try to reach gateway --- + gw_ok=0 + for i in {1..10}; do if pct exec "$CTID" -- ping -c1 -W1 "$GATEWAY" >/dev/null 2>&1; then + gw_ok=1 break fi sleep 1 done - if [ "$i" -eq 20 ]; then - msg_error "Gateway $GATEWAY unreachable from CT $CTID (IP $ip_in_lxc)" - exit 1 + if [ "$gw_ok" -eq 1 ]; then + msg_ok "CT $CTID gateway $GATEWAY reachable (IP $ip_in_lxc)" + else + msg_warn "CT $CTID has IP $ip_in_lxc but gateway $GATEWAY did not reply" fi - # --- Step 3: DNS check --- + # --- Step 3: DNS / Internet check --- if pct exec "$CTID" -- getent hosts deb.debian.org >/dev/null 2>&1; then msg_ok "Network in LXC is reachable (DNS OK, IP $ip_in_lxc)" else From 1f8a76e8e2281616b790f1836ea1337b36a9b785 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 22 Sep 2025 13:06:12 +0200 Subject: [PATCH 1093/1733] Refactor VAAPI passthrough device selection logic Replaces legacy VAAPI device detection and selection with a modular, more robust approach. Adds helpers for group ID resolution, device deduplication, and dynamic whiptail dialog sizing. Improves user prompts, error handling, and fallback logic for both privileged and unprivileged containers. The new implementation is more maintainable and user-friendly. --- misc/build.func | 385 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 275 insertions(+), 110 deletions(-) diff --git a/misc/build.func b/misc/build.func index 4d6c1b89a..841cd94d1 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2215,139 +2215,304 @@ EOF done if [[ "$CT_TYPE" == "0" || "$is_vaapi_app" == "true" ]]; then - VAAPI_DEVICES=() - SINGLE_VAAPI_DEVICE="" - seen_ids=() + # Standard: don’t include fb0 unless explicitly wanted + vaapi_select_and_apply "$CTID" "$CT_TYPE" || msg_warn "VAAPI setup skipped/partial." + fi - for bypath in /dev/dri/by-path/*-render /dev/dri/renderD*; do - [[ -e "$bypath" ]] || continue - dev_render=$(readlink -f "$bypath") || continue - id=$(basename "$dev_render") - [[ " ${seen_ids[*]} " == *" $id "* ]] && continue - seen_ids+=("$id") + # --- VAAPI: helper to resolve GID dynamically + _vaapi_gid() { + local g="$1" gid + gid="$(getent group "$g" | cut -d: -f3)" + if [[ -z "$gid" ]]; then + case "$g" in + video) echo 44 ;; # default fallback + render) echo 104 ;; # default fallback + *) echo 44 ;; + esac + else + echo "$gid" + fi + } - card_index="${id#renderD}" - card="/dev/dri/card${card_index}" - combo_devices=("$dev_render") - if [[ "${#combo_devices[@]}" -eq 1 && -e /dev/dri/card0 ]]; then - combo_devices+=("/dev/dri/card0") + # --- VAAPI: detect devices, dedupe, pretty labels + _vaapi_pairs() { + local seen=() by real id idx card pci pci_info name + shopt -s nullglob + for by in /dev/dri/by-path/*-render /dev/dri/renderD*; do + [[ -e "$by" ]] || continue + real="$(readlink -f "$by" || true)" + [[ -e "$real" ]] || continue + id="$(basename "$real")" + if [[ " ${seen[*]} " == *" $id "* ]]; then + continue fi - #echo "combo_devices=${combo_devices[*]}" + seen+=("$id") - pci_addr=$(basename "$bypath" | cut -d- -f1 --complement | sed 's/-render//' || true) - pci_info=$(lspci -nn | grep "${pci_addr#0000:}" || true) - name="${pci_info#*: }" - [[ -z "$name" ]] && name="Unknown GPU ($pci_addr)" + idx="${id#renderD}" + if [[ "$idx" =~ ^[0-9]+$ ]]; then + idx=$((idx - 128)) + else + idx=0 + fi + card="/dev/dri/card${idx}" + [[ -e "$card" ]] || card="" - label="$(basename "$dev_render")" - [[ -e "$card" ]] && label+=" + $(basename "$card")" - label+=" – $name" - msg_debug "[VAAPI DEBUG] Adding VAAPI combo: ${combo_devices[*]} ($label)" + if [[ "$by" == *"/by-path/"* ]]; then + pci="$(basename "$by" | sed -E 's/^pci-([0-9a-fA-F:.]+)-render/\1/')" + pci_info="$(lspci -nn 2>/dev/null | grep -i "${pci#0000:}" || true)" + name="${pci_info#*: }" + [[ -z "$name" ]] && name="GPU ${pci}" + else + name="DRM $(basename "$real")" + fi - VAAPI_DEVICES+=("$( - IFS=: - echo "${combo_devices[*]}" - )" "$label" "OFF") + label="$(basename "$real")" + [[ -n "$card" ]] && label+=" + $(basename "$card")" + label+=" – ${name}" + + printf "%s:%s\t%s\n" "$real" "$card" "$label" done + shopt -u nullglob + } - [[ -e /dev/fb0 ]] && VAAPI_DEVICES+=("/dev/fb0" "fb0 - Framebuffer" "OFF") + # --- VAAPI: whiptail dimension helper + _whiptail_dims() { + local n="$1" L="$2" + local maxW=$((L + 8)) + ((maxW < 70)) && maxW=70 + ((maxW > 100)) && maxW=100 + local H=$((10 + n * 2)) + ((H > 22)) && H=22 + echo "$H $maxW" + } - GID_VIDEO=$(getent group video | cut -d: -f3) - GID_RENDER=$(getent group render | cut -d: -f3) - [[ -z "$GID_VIDEO" ]] && GID_VIDEO=44 && msg_warn "'video' group not found, falling back to GID 44" - [[ -z "$GID_RENDER" ]] && GID_RENDER=104 && msg_warn "'render' group not found, falling back to GID 104" + # --- VAAPI: main selector and config writer + vaapi_select_and_apply() { + local CTID="$1" CTTYPE="$2" + local LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" - if [[ "${#VAAPI_DEVICES[@]}" -eq 0 ]]; then - msg_warn "No VAAPI-compatible devices found." - elif [[ "${#VAAPI_DEVICES[@]}" -eq 3 ]]; then - #msg_info "Only one VAAPI-compatible device found – enabling passthrough." + local pairs=() items=() maxlen=0 n h w + mapfile -t pairs < <(_vaapi_pairs) - IFS=":" read -ra devices <<<"${VAAPI_DEVICES[0]//\"/}" - IDX=0 - DID_MOUNT_DRI=0 + if ((${#pairs[@]} == 0)); then + msg_warn "No VAAPI devices detected – skipping passthrough." + return 0 + fi - for d in "${devices[@]}"; do - if [[ "$CT_TYPE" == "0" ]]; then + for p in "${pairs[@]}"; do + local devs="${p%%$'\t'*}" + local label="${p#*$'\t'}" + items+=("$devs" "$label" "OFF") + ((${#label} > maxlen)) && maxlen=${#label} + done + n=$((${#items[@]} / 3)) + read -r h w < <(_whiptail_dims "$n" "$maxlen") + + if [[ "$CTTYPE" == "0" ]]; then + whiptail --title "VAAPI passthrough" --msgbox "\ +VAAPI passthrough will be enabled. + +• Privileged CT: full DRM access +• You may need to install drivers inside the container + (e.g. intel-media-driver, vainfo)." 12 "$w" + else + whiptail --title "VAAPI passthrough (unprivileged)" --msgbox "\ +Unprivileged CT: VAAPI may be limited. + +If it fails, consider using a privileged container. +You may still need drivers inside the CT." 12 "$w" + fi + + local SELECTED + SELECTED="$( + whiptail --title "VAAPI Device Selection" \ + --checklist "Select GPU / VAAPI device(s) to passthrough:" "$h" "$w" "$((n > 6 ? 6 : n))" \ + "${items[@]}" 3>&1 1>&2 2>&3 + )" || { + msg_warn "VAAPI selection cancelled." + return 0 + } + + [[ -z "$SELECTED" ]] && { + msg_warn "No devices selected – skipping passthrough." + return 0 + } + + local DID_MOUNT_DRI=0 idx=0 + for dev in $SELECTED; do + dev="${dev%\"}" + dev="${dev#\"}" + IFS=":" read -r path card <<<"$dev" + for d in "$path" "$card"; do + [[ -n "$d" && -e "$d" ]] || continue + if [[ "$CTTYPE" == "0" ]]; then if [[ "$DID_MOUNT_DRI" -eq 0 && -d /dev/dri ]]; then echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" DID_MOUNT_DRI=1 fi - if ! major_minor=$(stat -c '%t:%T' "$d" 2>/dev/null | awk -F: '{ printf "%d:%d", "0x"$1, "0x"$2 }'); then + if mm=$(stat -c '%t:%T' "$d" 2>/dev/null | awk -F: '{printf "%d:%d","0x"$1,"0x"$2}'); then + echo "lxc.cgroup2.devices.allow: c $mm rwm" >>"$LXC_CONFIG" + echo "lxc.mount.entry: $d $d none bind,optional,create=file" >>"$LXC_CONFIG" + else msg_warn "Could not stat $d – skipping." - continue fi - echo "lxc.cgroup2.devices.allow: c $major_minor rwm" >>"$LXC_CONFIG" - echo "lxc.mount.entry: $d $d none bind,optional,create=file" >>"$LXC_CONFIG" else - GID=$([[ "$d" =~ render ]] && echo "$GID_RENDER" || echo "$GID_VIDEO") - echo "dev${IDX}: $d,gid=${GID}" >>"$LXC_CONFIG" - IDX=$((IDX + 1)) + local gid + if [[ "$d" =~ renderD ]]; then + gid="$(_vaapi_gid render)" + else + gid="$(_vaapi_gid video)" + fi + echo "dev${idx}: $d,gid=${gid}" >>"$LXC_CONFIG" + idx=$((idx + 1)) fi done + done - else - if [[ "$CT_TYPE" == "0" ]]; then - whiptail --title "VAAPI passthrough" --msgbox "\ -✅ VAAPI passthrough has been enabled - -GPU hardware acceleration will be available inside the container -(e.g., for Jellyfin, Plex, Frigate, etc.) - -ℹ️ Note: You may need to install drivers manually inside the container, -such as 'intel-media-driver', 'libva2', or 'vainfo'." 15 74 - else - whiptail --title "VAAPI passthrough (limited)" --msgbox "\ -⚠️ VAAPI passthrough in unprivileged containers may be limited - -Some drivers (e.g., iHD) require privileged access to DRM subsystems. -If VAAPI fails, consider switching to a privileged container. - -ℹ️ Note: You may need to install drivers manually inside the container, -such as 'intel-media-driver', 'libva2', or 'vainfo'." 15 74 - fi - - SELECTED_DEVICES=$(whiptail --title "VAAPI Device Selection" \ - --checklist "Select VAAPI device(s) / GPU(s) to passthrough:" 20 100 6 \ - "${VAAPI_DEVICES[@]}" 3>&1 1>&2 2>&3) - - if [[ $? -ne 0 ]]; then - exit_script - msg_error "VAAPI passthrough selection cancelled by user." - fi - - if [[ -n "$SELECTED_DEVICES" ]]; then - IDX=0 - DID_MOUNT_DRI=0 - for dev in $SELECTED_DEVICES; do - dev="${dev%\"}" - dev="${dev#\"}" # strip quotes - IFS=":" read -ra devices <<<"$dev" - msg_debug "[VAAPI DEBUG] Autopassthrough for devices: ${devices[*]}" - for d in "${devices[@]}"; do - if [[ "$CT_TYPE" == "0" ]]; then - if [[ "$DID_MOUNT_DRI" -eq 0 && -d /dev/dri ]]; then - echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" - DID_MOUNT_DRI=1 - fi - if ! major_minor=$(stat -c '%t:%T' "$d" 2>/dev/null | awk -F: '{ printf "%d:%d", "0x"$1, "0x"$2 }'); then - msg_warn "Could not stat $d – skipping." - continue - fi - echo "lxc.cgroup2.devices.allow: c $major_minor rwm" >>"$LXC_CONFIG" - echo "lxc.mount.entry: $d $d none bind,optional,create=file" >>"$LXC_CONFIG" - else - GID=$([[ "$d" =~ render ]] && echo "$GID_RENDER" || echo "$GID_VIDEO") - - echo "dev${IDX}: $d,gid=${GID}" >>"$LXC_CONFIG" - IDX=$((IDX + 1)) - fi - done - done - else - msg_warn "No VAAPI devices selected – passthrough skipped." - fi + # Fallback if card0/card1 flip causes missing nodes + if [[ "$CTTYPE" == "0" ]]; then + cat <<'EOF' >>"$LXC_CONFIG" +# VAAPI fallback: bind /dev/dri if specific nodes are missing +lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir +lxc.cgroup2.devices.allow: c 226:* rwm +EOF fi - fi + } + + # if [[ "$CT_TYPE" == "0" || "$is_vaapi_app" == "true" ]]; then + # VAAPI_DEVICES=() + # SINGLE_VAAPI_DEVICE="" + # seen_ids=() + + # for bypath in /dev/dri/by-path/*-render /dev/dri/renderD*; do + # [[ -e "$bypath" ]] || continue + # dev_render=$(readlink -f "$bypath") || continue + # id=$(basename "$dev_render") + # [[ " ${seen_ids[*]} " == *" $id "* ]] && continue + # seen_ids+=("$id") + + # card_index="${id#renderD}" + # card="/dev/dri/card${card_index}" + # combo_devices=("$dev_render") + # if [[ "${#combo_devices[@]}" -eq 1 && -e /dev/dri/card0 ]]; then + # combo_devices+=("/dev/dri/card0") + # fi + # #echo "combo_devices=${combo_devices[*]}" + + # pci_addr=$(basename "$bypath" | cut -d- -f1 --complement | sed 's/-render//' || true) + # pci_info=$(lspci -nn | grep "${pci_addr#0000:}" || true) + # name="${pci_info#*: }" + # [[ -z "$name" ]] && name="Unknown GPU ($pci_addr)" + + # label="$(basename "$dev_render")" + # [[ -e "$card" ]] && label+=" + $(basename "$card")" + # label+=" – $name" + # msg_debug "[VAAPI DEBUG] Adding VAAPI combo: ${combo_devices[*]} ($label)" + + # VAAPI_DEVICES+=("$( + # IFS=: + # echo "${combo_devices[*]}" + # )" "$label" "OFF") + # done + + # [[ -e /dev/fb0 ]] && VAAPI_DEVICES+=("/dev/fb0" "fb0 - Framebuffer" "OFF") + + # GID_VIDEO=$(getent group video | cut -d: -f3) + # GID_RENDER=$(getent group render | cut -d: -f3) + # [[ -z "$GID_VIDEO" ]] && GID_VIDEO=44 && msg_warn "'video' group not found, falling back to GID 44" + # [[ -z "$GID_RENDER" ]] && GID_RENDER=104 && msg_warn "'render' group not found, falling back to GID 104" + + # if [[ "${#VAAPI_DEVICES[@]}" -eq 0 ]]; then + # msg_warn "No VAAPI-compatible devices found." + # elif [[ "${#VAAPI_DEVICES[@]}" -eq 3 ]]; then + # #msg_info "Only one VAAPI-compatible device found – enabling passthrough." + + # IFS=":" read -ra devices <<<"${VAAPI_DEVICES[0]//\"/}" + # IDX=0 + # DID_MOUNT_DRI=0 + + # for d in "${devices[@]}"; do + # if [[ "$CT_TYPE" == "0" ]]; then + # if [[ "$DID_MOUNT_DRI" -eq 0 && -d /dev/dri ]]; then + # echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" + # DID_MOUNT_DRI=1 + # fi + # if ! major_minor=$(stat -c '%t:%T' "$d" 2>/dev/null | awk -F: '{ printf "%d:%d", "0x"$1, "0x"$2 }'); then + # msg_warn "Could not stat $d – skipping." + # continue + # fi + # echo "lxc.cgroup2.devices.allow: c $major_minor rwm" >>"$LXC_CONFIG" + # echo "lxc.mount.entry: $d $d none bind,optional,create=file" >>"$LXC_CONFIG" + # else + # GID=$([[ "$d" =~ render ]] && echo "$GID_RENDER" || echo "$GID_VIDEO") + # echo "dev${IDX}: $d,gid=${GID}" >>"$LXC_CONFIG" + # IDX=$((IDX + 1)) + # fi + # done + + # else + # if [[ "$CT_TYPE" == "0" ]]; then + # whiptail --title "VAAPI passthrough" --msgbox "\ + # ✅ VAAPI passthrough has been enabled + + # GPU hardware acceleration will be available inside the container + # (e.g., for Jellyfin, Plex, Frigate, etc.) + + # ℹ️ Note: You may need to install drivers manually inside the container, + # such as 'intel-media-driver', 'libva2', or 'vainfo'." 15 74 + # else + # whiptail --title "VAAPI passthrough (limited)" --msgbox "\ + # ⚠️ VAAPI passthrough in unprivileged containers may be limited + + # Some drivers (e.g., iHD) require privileged access to DRM subsystems. + # If VAAPI fails, consider switching to a privileged container. + + # ℹ️ Note: You may need to install drivers manually inside the container, + # such as 'intel-media-driver', 'libva2', or 'vainfo'." 15 74 + # fi + + # SELECTED_DEVICES=$(whiptail --title "VAAPI Device Selection" \ + # --checklist "Select VAAPI device(s) / GPU(s) to passthrough:" 20 100 6 \ + # "${VAAPI_DEVICES[@]}" 3>&1 1>&2 2>&3) + + # if [[ $? -ne 0 ]]; then + # exit_script + # msg_error "VAAPI passthrough selection cancelled by user." + # fi + + # if [[ -n "$SELECTED_DEVICES" ]]; then + # IDX=0 + # DID_MOUNT_DRI=0 + # for dev in $SELECTED_DEVICES; do + # dev="${dev%\"}" + # dev="${dev#\"}" # strip quotes + # IFS=":" read -ra devices <<<"$dev" + # msg_debug "[VAAPI DEBUG] Autopassthrough for devices: ${devices[*]}" + # for d in "${devices[@]}"; do + # if [[ "$CT_TYPE" == "0" ]]; then + # if [[ "$DID_MOUNT_DRI" -eq 0 && -d /dev/dri ]]; then + # echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" + # DID_MOUNT_DRI=1 + # fi + # if ! major_minor=$(stat -c '%t:%T' "$d" 2>/dev/null | awk -F: '{ printf "%d:%d", "0x"$1, "0x"$2 }'); then + # msg_warn "Could not stat $d – skipping." + # continue + # fi + # echo "lxc.cgroup2.devices.allow: c $major_minor rwm" >>"$LXC_CONFIG" + # echo "lxc.mount.entry: $d $d none bind,optional,create=file" >>"$LXC_CONFIG" + # else + # GID=$([[ "$d" =~ render ]] && echo "$GID_RENDER" || echo "$GID_VIDEO") + + # echo "dev${IDX}: $d,gid=${GID}" >>"$LXC_CONFIG" + # IDX=$((IDX + 1)) + # fi + # done + # done + # else + # msg_warn "No VAAPI devices selected – passthrough skipped." + # fi + # fi + # fi # TUN device passthrough if [ "$ENABLE_TUN" == "yes" ]; then From 8aae603267a4a245f5a7ff9f4c58c2dd48449150 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 22 Sep 2025 13:17:20 +0200 Subject: [PATCH 1094/1733] Refactor VAAPI passthrough to external script Replaces the inlined VAAPI passthrough logic in misc/build.func with calls to an external passthrough.func script, streamlining and centralizing hardware passthrough handling. Adds a new misc/passthrough.func file and introduces a hwaccel_setup_in_ct helper in misc/tools.func for hardware acceleration setup inside containers. --- misc/build.func | 318 +----------------------------------------- misc/passthrough.func | 0 misc/tools.func | 77 ++++++++++ 3 files changed, 81 insertions(+), 314 deletions(-) create mode 100644 misc/passthrough.func diff --git a/misc/build.func b/misc/build.func index 841cd94d1..2475c3243 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2199,320 +2199,10 @@ lxc.mount.entry: /dev/ttyACM1 dev/ttyACM1 none bind,optional,create= EOF fi - # VAAPI passthrough for privileged containers or known apps - VAAPI_APPS=( - "immich" "Channels" "Emby" "ErsatzTV" "Frigate" "Jellyfin" - "Plex" "Scrypted" "Tdarr" "Unmanic" "Ollama" "FileFlows" - "Open WebUI" "Debian" "Tunarr" - ) - - is_vaapi_app=false - for vaapi_app in "${VAAPI_APPS[@]}"; do - if [[ "$APP" == "$vaapi_app" ]]; then - is_vaapi_app=true - break - fi - done - - if [[ "$CT_TYPE" == "0" || "$is_vaapi_app" == "true" ]]; then - # Standard: don’t include fb0 unless explicitly wanted - vaapi_select_and_apply "$CTID" "$CT_TYPE" || msg_warn "VAAPI setup skipped/partial." - fi - - # --- VAAPI: helper to resolve GID dynamically - _vaapi_gid() { - local g="$1" gid - gid="$(getent group "$g" | cut -d: -f3)" - if [[ -z "$gid" ]]; then - case "$g" in - video) echo 44 ;; # default fallback - render) echo 104 ;; # default fallback - *) echo 44 ;; - esac - else - echo "$gid" - fi - } - - # --- VAAPI: detect devices, dedupe, pretty labels - _vaapi_pairs() { - local seen=() by real id idx card pci pci_info name - shopt -s nullglob - for by in /dev/dri/by-path/*-render /dev/dri/renderD*; do - [[ -e "$by" ]] || continue - real="$(readlink -f "$by" || true)" - [[ -e "$real" ]] || continue - id="$(basename "$real")" - if [[ " ${seen[*]} " == *" $id "* ]]; then - continue - fi - seen+=("$id") - - idx="${id#renderD}" - if [[ "$idx" =~ ^[0-9]+$ ]]; then - idx=$((idx - 128)) - else - idx=0 - fi - card="/dev/dri/card${idx}" - [[ -e "$card" ]] || card="" - - if [[ "$by" == *"/by-path/"* ]]; then - pci="$(basename "$by" | sed -E 's/^pci-([0-9a-fA-F:.]+)-render/\1/')" - pci_info="$(lspci -nn 2>/dev/null | grep -i "${pci#0000:}" || true)" - name="${pci_info#*: }" - [[ -z "$name" ]] && name="GPU ${pci}" - else - name="DRM $(basename "$real")" - fi - - label="$(basename "$real")" - [[ -n "$card" ]] && label+=" + $(basename "$card")" - label+=" – ${name}" - - printf "%s:%s\t%s\n" "$real" "$card" "$label" - done - shopt -u nullglob - } - - # --- VAAPI: whiptail dimension helper - _whiptail_dims() { - local n="$1" L="$2" - local maxW=$((L + 8)) - ((maxW < 70)) && maxW=70 - ((maxW > 100)) && maxW=100 - local H=$((10 + n * 2)) - ((H > 22)) && H=22 - echo "$H $maxW" - } - - # --- VAAPI: main selector and config writer - vaapi_select_and_apply() { - local CTID="$1" CTTYPE="$2" - local LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" - - local pairs=() items=() maxlen=0 n h w - mapfile -t pairs < <(_vaapi_pairs) - - if ((${#pairs[@]} == 0)); then - msg_warn "No VAAPI devices detected – skipping passthrough." - return 0 - fi - - for p in "${pairs[@]}"; do - local devs="${p%%$'\t'*}" - local label="${p#*$'\t'}" - items+=("$devs" "$label" "OFF") - ((${#label} > maxlen)) && maxlen=${#label} - done - n=$((${#items[@]} / 3)) - read -r h w < <(_whiptail_dims "$n" "$maxlen") - - if [[ "$CTTYPE" == "0" ]]; then - whiptail --title "VAAPI passthrough" --msgbox "\ -VAAPI passthrough will be enabled. - -• Privileged CT: full DRM access -• You may need to install drivers inside the container - (e.g. intel-media-driver, vainfo)." 12 "$w" - else - whiptail --title "VAAPI passthrough (unprivileged)" --msgbox "\ -Unprivileged CT: VAAPI may be limited. - -If it fails, consider using a privileged container. -You may still need drivers inside the CT." 12 "$w" - fi - - local SELECTED - SELECTED="$( - whiptail --title "VAAPI Device Selection" \ - --checklist "Select GPU / VAAPI device(s) to passthrough:" "$h" "$w" "$((n > 6 ? 6 : n))" \ - "${items[@]}" 3>&1 1>&2 2>&3 - )" || { - msg_warn "VAAPI selection cancelled." - return 0 - } - - [[ -z "$SELECTED" ]] && { - msg_warn "No devices selected – skipping passthrough." - return 0 - } - - local DID_MOUNT_DRI=0 idx=0 - for dev in $SELECTED; do - dev="${dev%\"}" - dev="${dev#\"}" - IFS=":" read -r path card <<<"$dev" - for d in "$path" "$card"; do - [[ -n "$d" && -e "$d" ]] || continue - if [[ "$CTTYPE" == "0" ]]; then - if [[ "$DID_MOUNT_DRI" -eq 0 && -d /dev/dri ]]; then - echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" - DID_MOUNT_DRI=1 - fi - if mm=$(stat -c '%t:%T' "$d" 2>/dev/null | awk -F: '{printf "%d:%d","0x"$1,"0x"$2}'); then - echo "lxc.cgroup2.devices.allow: c $mm rwm" >>"$LXC_CONFIG" - echo "lxc.mount.entry: $d $d none bind,optional,create=file" >>"$LXC_CONFIG" - else - msg_warn "Could not stat $d – skipping." - fi - else - local gid - if [[ "$d" =~ renderD ]]; then - gid="$(_vaapi_gid render)" - else - gid="$(_vaapi_gid video)" - fi - echo "dev${idx}: $d,gid=${gid}" >>"$LXC_CONFIG" - idx=$((idx + 1)) - fi - done - done - - # Fallback if card0/card1 flip causes missing nodes - if [[ "$CTTYPE" == "0" ]]; then - cat <<'EOF' >>"$LXC_CONFIG" -# VAAPI fallback: bind /dev/dri if specific nodes are missing -lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir -lxc.cgroup2.devices.allow: c 226:* rwm -EOF - fi - } - - # if [[ "$CT_TYPE" == "0" || "$is_vaapi_app" == "true" ]]; then - # VAAPI_DEVICES=() - # SINGLE_VAAPI_DEVICE="" - # seen_ids=() - - # for bypath in /dev/dri/by-path/*-render /dev/dri/renderD*; do - # [[ -e "$bypath" ]] || continue - # dev_render=$(readlink -f "$bypath") || continue - # id=$(basename "$dev_render") - # [[ " ${seen_ids[*]} " == *" $id "* ]] && continue - # seen_ids+=("$id") - - # card_index="${id#renderD}" - # card="/dev/dri/card${card_index}" - # combo_devices=("$dev_render") - # if [[ "${#combo_devices[@]}" -eq 1 && -e /dev/dri/card0 ]]; then - # combo_devices+=("/dev/dri/card0") - # fi - # #echo "combo_devices=${combo_devices[*]}" - - # pci_addr=$(basename "$bypath" | cut -d- -f1 --complement | sed 's/-render//' || true) - # pci_info=$(lspci -nn | grep "${pci_addr#0000:}" || true) - # name="${pci_info#*: }" - # [[ -z "$name" ]] && name="Unknown GPU ($pci_addr)" - - # label="$(basename "$dev_render")" - # [[ -e "$card" ]] && label+=" + $(basename "$card")" - # label+=" – $name" - # msg_debug "[VAAPI DEBUG] Adding VAAPI combo: ${combo_devices[*]} ($label)" - - # VAAPI_DEVICES+=("$( - # IFS=: - # echo "${combo_devices[*]}" - # )" "$label" "OFF") - # done - - # [[ -e /dev/fb0 ]] && VAAPI_DEVICES+=("/dev/fb0" "fb0 - Framebuffer" "OFF") - - # GID_VIDEO=$(getent group video | cut -d: -f3) - # GID_RENDER=$(getent group render | cut -d: -f3) - # [[ -z "$GID_VIDEO" ]] && GID_VIDEO=44 && msg_warn "'video' group not found, falling back to GID 44" - # [[ -z "$GID_RENDER" ]] && GID_RENDER=104 && msg_warn "'render' group not found, falling back to GID 104" - - # if [[ "${#VAAPI_DEVICES[@]}" -eq 0 ]]; then - # msg_warn "No VAAPI-compatible devices found." - # elif [[ "${#VAAPI_DEVICES[@]}" -eq 3 ]]; then - # #msg_info "Only one VAAPI-compatible device found – enabling passthrough." - - # IFS=":" read -ra devices <<<"${VAAPI_DEVICES[0]//\"/}" - # IDX=0 - # DID_MOUNT_DRI=0 - - # for d in "${devices[@]}"; do - # if [[ "$CT_TYPE" == "0" ]]; then - # if [[ "$DID_MOUNT_DRI" -eq 0 && -d /dev/dri ]]; then - # echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" - # DID_MOUNT_DRI=1 - # fi - # if ! major_minor=$(stat -c '%t:%T' "$d" 2>/dev/null | awk -F: '{ printf "%d:%d", "0x"$1, "0x"$2 }'); then - # msg_warn "Could not stat $d – skipping." - # continue - # fi - # echo "lxc.cgroup2.devices.allow: c $major_minor rwm" >>"$LXC_CONFIG" - # echo "lxc.mount.entry: $d $d none bind,optional,create=file" >>"$LXC_CONFIG" - # else - # GID=$([[ "$d" =~ render ]] && echo "$GID_RENDER" || echo "$GID_VIDEO") - # echo "dev${IDX}: $d,gid=${GID}" >>"$LXC_CONFIG" - # IDX=$((IDX + 1)) - # fi - # done - - # else - # if [[ "$CT_TYPE" == "0" ]]; then - # whiptail --title "VAAPI passthrough" --msgbox "\ - # ✅ VAAPI passthrough has been enabled - - # GPU hardware acceleration will be available inside the container - # (e.g., for Jellyfin, Plex, Frigate, etc.) - - # ℹ️ Note: You may need to install drivers manually inside the container, - # such as 'intel-media-driver', 'libva2', or 'vainfo'." 15 74 - # else - # whiptail --title "VAAPI passthrough (limited)" --msgbox "\ - # ⚠️ VAAPI passthrough in unprivileged containers may be limited - - # Some drivers (e.g., iHD) require privileged access to DRM subsystems. - # If VAAPI fails, consider switching to a privileged container. - - # ℹ️ Note: You may need to install drivers manually inside the container, - # such as 'intel-media-driver', 'libva2', or 'vainfo'." 15 74 - # fi - - # SELECTED_DEVICES=$(whiptail --title "VAAPI Device Selection" \ - # --checklist "Select VAAPI device(s) / GPU(s) to passthrough:" 20 100 6 \ - # "${VAAPI_DEVICES[@]}" 3>&1 1>&2 2>&3) - - # if [[ $? -ne 0 ]]; then - # exit_script - # msg_error "VAAPI passthrough selection cancelled by user." - # fi - - # if [[ -n "$SELECTED_DEVICES" ]]; then - # IDX=0 - # DID_MOUNT_DRI=0 - # for dev in $SELECTED_DEVICES; do - # dev="${dev%\"}" - # dev="${dev#\"}" # strip quotes - # IFS=":" read -ra devices <<<"$dev" - # msg_debug "[VAAPI DEBUG] Autopassthrough for devices: ${devices[*]}" - # for d in "${devices[@]}"; do - # if [[ "$CT_TYPE" == "0" ]]; then - # if [[ "$DID_MOUNT_DRI" -eq 0 && -d /dev/dri ]]; then - # echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" - # DID_MOUNT_DRI=1 - # fi - # if ! major_minor=$(stat -c '%t:%T' "$d" 2>/dev/null | awk -F: '{ printf "%d:%d", "0x"$1, "0x"$2 }'); then - # msg_warn "Could not stat $d – skipping." - # continue - # fi - # echo "lxc.cgroup2.devices.allow: c $major_minor rwm" >>"$LXC_CONFIG" - # echo "lxc.mount.entry: $d $d none bind,optional,create=file" >>"$LXC_CONFIG" - # else - # GID=$([[ "$d" =~ render ]] && echo "$GID_RENDER" || echo "$GID_VIDEO") - - # echo "dev${IDX}: $d,gid=${GID}" >>"$LXC_CONFIG" - # IDX=$((IDX + 1)) - # fi - # done - # done - # else - # msg_warn "No VAAPI devices selected – passthrough skipped." - # fi - # fi - # fi + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/passthrough.func) + usb_handle_passthrough "$CTID" "$CT_TYPE" + vaapi_handle_passthrough "$CTID" "$CT_TYPE" "$APP" + nvidia_handle_passthrough "$CTID" "$CT_TYPE" "$APP" # TUN device passthrough if [ "$ENABLE_TUN" == "yes" ]; then diff --git a/misc/passthrough.func b/misc/passthrough.func new file mode 100644 index 000000000..e69de29bb diff --git a/misc/tools.func b/misc/tools.func index 88950d690..db74a7a00 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -2091,3 +2091,80 @@ check_for_gh_release() { return 1 fi } + +# ------------------------------------------------------------------------------ +# Hardware acceleration setup inside container +# Works with: Debian 12 (bookworm), Debian 13 (trixie), Ubuntu 24.04 (noble) +# Usage: hwaccel_setup_in_ct [--nonfree-intel] +# ------------------------------------------------------------------------------ + +hwaccel_setup_in_ct() { + local CTTYPE="$1" NONFREE=0 + [[ "$2" == "--nonfree-intel" ]] && NONFREE=1 + + # Detect OS info inside the container + local ID VERSION_CODENAME + if [[ -r /etc/os-release ]]; then + . /etc/os-release + fi + ID="${ID:-debian}" + VERSION_CODENAME="${VERSION_CODENAME:-bookworm}" + + msg_info "Setting up hardware acceleration for ${ID^} ($VERSION_CODENAME)" + + case "$ID" in + debian | ubuntu) + if ((NONFREE)) && [[ "$VERSION_CODENAME" =~ (trixie|noble) ]]; then + # Debian 13 / Ubuntu 24.04 → non-free Intel driver + cat >/etc/apt/sources.list.d/non-free.sources <<'SRC' +Types: deb deb-src +URIs: http://deb.debian.org/debian +Suites: trixie +Components: non-free non-free-firmware + +Types: deb deb-src +URIs: http://deb.debian.org/debian-security +Suites: trixie-security +Components: non-free non-free-firmware + +Types: deb deb-src +URIs: http://deb.debian.org/debian +Suites: trixie-updates +Components: non-free non-free-firmware +SRC + + $STD apt-get update + $STD apt-get install -y \ + intel-media-va-driver-non-free \ + ocl-icd-libopencl1 \ + mesa-opencl-icd \ + mesa-va-drivers \ + libvpl2 \ + vainfo \ + intel-gpu-tools + else + # Debian 12 (bookworm) and fallback for Debian 13/Ubuntu 24.04 without non-free + $STD apt-get update + $STD apt-get install -y \ + va-driver-all \ + ocl-icd-libopencl1 \ + mesa-opencl-icd \ + mesa-va-drivers \ + vainfo \ + intel-gpu-tools + fi + ;; + *) + msg_warn "Unsupported distribution ($ID $VERSION_CODENAME) – skipping HW accel setup" + return 0 + ;; + esac + + # Add current user to video/render groups (only for privileged CTs) + if [[ "$CTTYPE" == "0" ]]; then + $STD adduser "$(id -un)" video || true + $STD adduser "$(id -un)" render || true + fi + + msg_ok "Hardware acceleration is ready" +} From 50a2b06c764bba5185cc8aacff636808f1430cb0 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 22 Sep 2025 13:18:15 +0200 Subject: [PATCH 1095/1733] Add host-side passthrough logic for VAAPI and NVIDIA in LXC Introduces passthrough.func, a Bash script to manage host-side passthrough configuration for VAAPI and NVIDIA devices in LXC containers. The script provides functions for USB, VAAPI, and NVIDIA device passthrough, including device selection dialogs, configuration file updates, and compatibility handling for privileged and unprivileged containers. --- misc/passthrough.func | 236 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 236 insertions(+) diff --git a/misc/passthrough.func b/misc/passthrough.func index e69de29bb..43c96beb0 100644 --- a/misc/passthrough.func +++ b/misc/passthrough.func @@ -0,0 +1,236 @@ +#!/usr/bin/env bash +# passthrough.func — host-side passthrough logic (VAAPI & NVIDIA) for LXC +# This file ONLY touches host config (/etc/pve/lxc/.conf) and whiptail. +# Inside-CT package setup lives in tools.func (hwaccel_setup_in_ct / nvidia_setup_in_ct). + +# --------------------------- Common helpers ----------------------------------- + +_whiptail_dims() { + local n="$1" L="$2" + local maxW=$((L + 8)) + ((maxW < 70)) && maxW=70 + ((maxW > 100)) && maxW=100 + local H=$((10 + n * 2)) + ((H > 22)) && H=22 + echo "$H $maxW" +} + +# ------------------------------ USB ------------------------------------------- + +usb_handle_passthrough() { + local CTID="$1" CTTYPE="$2" + local LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" + + [[ "$CTTYPE" != "0" ]] && return 0 # USB passthrough only for privileged CTs + + cat <>"$LXC_CONFIG" +# USB passthrough +lxc.cgroup2.devices.allow: a +lxc.cap.drop: +lxc.cgroup2.devices.allow: c 188:* rwm +lxc.cgroup2.devices.allow: c 189:* rwm +lxc.mount.entry: /dev/serial/by-id dev/serial/by-id none bind,optional,create=dir +lxc.mount.entry: /dev/ttyUSB0 dev/ttyUSB0 none bind,optional,create=file +lxc.mount.entry: /dev/ttyUSB1 dev/ttyUSB1 none bind,optional,create=file +lxc.mount.entry: /dev/ttyACM0 dev/ttyACM0 none bind,optional,create=file +lxc.mount.entry: /dev/ttyACM1 dev/ttyACM1 none bind,optional,create=file +EOF +} + +# ------------------------------ VAAPI ----------------------------------------- + +_vaapi_gid() { + local g="$1" gid + gid="$(getent group "$g" | cut -d: -f3)" + if [[ -z "$gid" ]]; then + case "$g" in + video) echo 44 ;; + render) echo 104 ;; + *) echo 44 ;; + esac + else + echo "$gid" + fi +} + +_vaapi_pairs() { + local seen=() by real id idx card pci pci_info name + shopt -s nullglob + for by in /dev/dri/by-path/*-render /dev/dri/renderD*; do + [[ -e "$by" ]] || continue + real="$(readlink -f "$by" || true)" + [[ -e "$real" ]] || continue + id="$(basename "$real")" + [[ " ${seen[*]} " == *" $id "* ]] && continue + seen+=("$id") + + idx="${id#renderD}" + if [[ "$idx" =~ ^[0-9]+$ ]]; then + idx=$((idx - 128)) + else + idx=0 + fi + card="/dev/dri/card${idx}" + [[ -e "$card" ]] || card="" + + if [[ "$by" == *"/by-path/"* ]]; then + pci="$(basename "$by" | sed -E 's/^pci-([0-9a-fA-F:.]+)-render/\1/')" + pci_info="$(lspci -nn 2>/dev/null | grep -i "${pci#0000:}" || true)" + name="${pci_info#*: }" + [[ -z "$name" ]] && name="GPU ${pci}" + else + name="DRM $(basename "$real")" + fi + + label="$(basename "$real")" + [[ -n "$card" ]] && label+=" + $(basename "$card")" + label+=" – ${name}" + + printf "%s:%s\t%s\n" "$real" "$card" "$label" + done + shopt -u nullglob +} + +vaapi_select_and_apply() { + local CTID="$1" CTTYPE="$2" + local LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" + + local pairs=() items=() maxlen=0 n h w + mapfile -t pairs < <(_vaapi_pairs) + + if ((${#pairs[@]} == 0)); then + msg_warn "No VAAPI devices detected – skipping." + return 0 + fi + + for p in "${pairs[@]}"; do + local devs="${p%%$'\t'*}" + local label="${p#*$'\t'}" + items+=("$devs" "$label" "OFF") + ((${#label} > maxlen)) && maxlen=${#label} + done + n=$((${#items[@]} / 3)) + read -r h w < <(_whiptail_dims "$n" "$maxlen") + + if [[ "$CTTYPE" == "0" ]]; then + whiptail --title "VAAPI passthrough" --msgbox "\ +VAAPI passthrough will be enabled. + +• Privileged CT: full DRM access +• You may need to install drivers inside the CT (intel-media-driver, vainfo)." 12 "$w" + else + whiptail --title "VAAPI passthrough (unprivileged)" --msgbox "\ +Unprivileged CT: VAAPI may be limited. + +If it fails, consider a privileged CT. You may still need drivers inside the CT." 12 "$w" + fi + + local SELECTED + SELECTED="$( + whiptail --title "VAAPI Device Selection" \ + --checklist "Select GPU / VAAPI device(s) to passthrough:" "$h" "$w" "$((n > 6 ? 6 : n))" \ + "${items[@]}" 3>&1 1>&2 2>&3 + )" || { + msg_warn "VAAPI selection cancelled." + return 0 + } + + [[ -z "$SELECTED" ]] && { + msg_warn "No devices selected – skipping." + return 0 + } + + local DID_MOUNT_DRI=0 idx=0 + for dev in $SELECTED; do + dev="${dev%\"}" + dev="${dev#\"}" + IFS=":" read -r path card <<<"$dev" + for d in "$path" "$card"; do + [[ -n "$d" && -e "$d" ]] || continue + if [[ "$CTTYPE" == "0" ]]; then + if [[ "$DID_MOUNT_DRI" -eq 0 && -d /dev/dri ]]; then + echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" + DID_MOUNT_DRI=1 + fi + if mm=$(stat -c '%t:%T' "$d" 2>/dev/null | awk -F: '{printf "%d:%d","0x"$1,"0x"$2}'); then + echo "lxc.cgroup2.devices.allow: c $mm rwm" >>"$LXC_CONFIG" + echo "lxc.mount.entry: $d $d none bind,optional,create=file" >>"$LXC_CONFIG" + else + msg_warn "Could not stat $d – skipping." + fi + else + local gid + if [[ "$d" =~ renderD ]]; then gid="$(_vaapi_gid render)"; else gid="$(_vaapi_gid video)"; fi + echo "dev${idx}: $d,gid=${gid}" >>"$LXC_CONFIG" + idx=$((idx + 1)) + fi + done + done + + # fallback for card0/card1 flip + if [[ "$CTTYPE" == "0" ]]; then + cat <<'EOF' >>"$LXC_CONFIG" +# VAAPI fallback: bind /dev/dri and allow 226:* to survive node flips +lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir +lxc.cgroup2.devices.allow: c 226:* rwm +EOF + fi +} + +vaapi_handle_passthrough() { + local CTID="$1" CTTYPE="$2" APP="$3" + + # Allowlist of apps that benefit from VAAPI even in unpriv CTs + local VAAPI_APPS=(immich Channels Emby ErsatzTV Frigate Jellyfin Plex Scrypted Tdarr Unmanic Ollama FileFlows "Open WebUI" Tunarr Debian) + local is_vaapi_app=false a + for a in "${VAAPI_APPS[@]}"; do [[ "$APP" == "$a" ]] && is_vaapi_app=true && break; done + + if [[ "$CTTYPE" == "0" || "$is_vaapi_app" == "true" ]]; then + vaapi_select_and_apply "$CTID" "$CTTYPE" + fi +} + +# ----------------------------- NVIDIA ----------------------------------------- + +nvidia_passthrough_to_lxc() { + local CTID="$1" CTTYPE="${2:-0}" + local LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" + local found=0 dev mm + + for dev in /dev/nvidia0 /dev/nvidia1 /dev/nvidiactl /dev/nvidia-uvm /dev/nvidia-uvm-tools /dev/nvidia-modeset; do + [[ -e "$dev" ]] || continue + found=1 + if mm="$(stat -c '%t:%T' "$dev" 2>/dev/null | awk -F: '{printf "%d:%d","0x"$1,"0x"$2}')"; then + echo "lxc.cgroup2.devices.allow: c $mm rwm" >>"$LXC_CONFIG" + echo "lxc.mount.entry: $dev $dev none bind,optional,create=file" >>"$LXC_CONFIG" + fi + done + + if [[ "$found" -eq 0 ]]; then + msg_warn "No /dev/nvidia* devices found on host; skipping NVIDIA passthrough." + return 0 + fi + + # optional: expose /dev/dri for apps probing VAAPI; harmless with NVIDIA + if [[ -d /dev/dri && "$CTTYPE" == "0" ]]; then + echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" + fi + + msg_ok "NVIDIA devices mapped to CT ${CTID}" +} + +nvidia_handle_passthrough() { + local CTID="$1" CTTYPE="$2" APP="$3" + + # Only offer if NVIDIA devices exist on host + compgen -G "/dev/nvidia*" >/dev/null || return 0 + + if whiptail --title "NVIDIA passthrough" \ + --yesno "NVIDIA GPU detected on host.\n\nMap /dev/nvidia* into CT ${CTID} and install NVIDIA userland inside the container?" 12 70; then + nvidia_passthrough_to_lxc "$CTID" "$CTTYPE" + # flag for in-CT install (consumed by *-install.sh via tools.func:nvidia_setup_in_ct) + export ENABLE_NVIDIA_IN_CT=1 + else + msg_warn "Skipped NVIDIA passthrough by user choice." + fi +} From 44a89c7cea1f440c70d38b69e99a0c6be60ccde3 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 22 Sep 2025 13:20:33 +0200 Subject: [PATCH 1096/1733] Update passthrough.func --- misc/passthrough.func | 112 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/misc/passthrough.func b/misc/passthrough.func index 43c96beb0..aaae1e707 100644 --- a/misc/passthrough.func +++ b/misc/passthrough.func @@ -234,3 +234,115 @@ nvidia_handle_passthrough() { msg_warn "Skipped NVIDIA passthrough by user choice." fi } +# ------------------------------------------------------------------------------ +# Hardware acceleration setup inside container (Intel/AMD via VAAPI) +# Debian 12 (bookworm), Debian 13 (trixie), Ubuntu 24.04 (noble) +# Usage: hwaccel_setup_in_ct [--nonfree-intel] +# ------------------------------------------------------------------------------ +hwaccel_setup_in_ct() { + local CTTYPE="$1" NONFREE=0 + [[ "$2" == "--nonfree-intel" ]] && NONFREE=1 + + local ID VERSION_CODENAME + if [[ -r /etc/os-release ]]; then . /etc/os-release; fi + ID="${ID:-debian}" + VERSION_CODENAME="${VERSION_CODENAME:-bookworm}" + + msg_info "Setting up VAAPI userland for ${ID^} ($VERSION_CODENAME)" + + case "$ID" in + debian | ubuntu) + if ((NONFREE)) && [[ "$VERSION_CODENAME" =~ (trixie|noble) ]]; then + # Debian 13 / Ubuntu 24.04 — enable non-free Intel media (Debian deb822) + if [[ "$VERSION_CODENAME" == "trixie" ]]; then + cat >/etc/apt/sources.list.d/non-free.sources <<'SRC' +Types: deb deb-src +URIs: http://deb.debian.org/debian +Suites: trixie +Components: non-free non-free-firmware + +Types: deb deb-src +URIs: http://deb.debian.org/debian-security +Suites: trixie-security +Components: non-free non-free-firmware + +Types: deb deb-src +URIs: http://deb.debian.org/debian +Suites: trixie-updates +Components: non-free non-free-firmware +SRC + fi + + $STD apt-get update + $STD apt-get install -y \ + intel-media-va-driver-non-free \ + ocl-icd-libopencl1 \ + mesa-opencl-icd \ + mesa-va-drivers \ + libvpl2 \ + vainfo \ + intel-gpu-tools + else + $STD apt-get update + $STD apt-get install -y \ + va-driver-all \ + ocl-icd-libopencl1 \ + mesa-opencl-icd \ + mesa-va-drivers \ + vainfo \ + intel-gpu-tools + fi + ;; + *) + msg_warn "Unsupported distro ($ID $VERSION_CODENAME) – skipping VAAPI setup." + return 0 + ;; + esac + + if [[ "$CTTYPE" == "0" ]]; then + $STD adduser "$(id -un)" video || true + $STD adduser "$(id -un)" render || true + fi + + msg_ok "VAAPI userland ready" +} + +# ------------------------------------------------------------------------------ +# NVIDIA userland inside container +# Debian 12/13, Ubuntu 24.04 +# Usage: nvidia_setup_in_ct +# ------------------------------------------------------------------------------ +nvidia_setup_in_ct() { + local CTTYPE="$1" + + local ID VERSION_CODENAME + if [[ -r /etc/os-release ]]; then . /etc/os-release; fi + ID="${ID:-debian}" + VERSION_CODENAME="${VERSION_CODENAME:-bookworm}" + + msg_info "Installing NVIDIA userland on ${ID^} ($VERSION_CODENAME)" + + case "$ID" in + debian | ubuntu) + $STD apt-get update + $STD apt-get install -y \ + nvidia-driver \ + nvidia-utils \ + libnvidia-encode1 \ + libcuda1 || { + msg_error "Failed to install NVIDIA packages" + return 1 + } + ;; + *) + msg_warn "Unsupported distro ($ID $VERSION_CODENAME) – skipping NVIDIA setup." + return 0 + ;; + esac + + if [[ "$CTTYPE" == "0" ]]; then + $STD adduser "$(id -un)" video || true + fi + + msg_ok "NVIDIA userland ready" +} From 54373244589b3e3bb8dbfefaf2d2fcc861634810 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 22 Sep 2025 13:22:19 +0200 Subject: [PATCH 1097/1733] Update passthrough.func --- misc/passthrough.func | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/misc/passthrough.func b/misc/passthrough.func index aaae1e707..5ea481523 100644 --- a/misc/passthrough.func +++ b/misc/passthrough.func @@ -187,6 +187,9 @@ vaapi_handle_passthrough() { if [[ "$CTTYPE" == "0" || "$is_vaapi_app" == "true" ]]; then vaapi_select_and_apply "$CTID" "$CTTYPE" + + # Run userland install inside CT via pct exec + pct exec "$CTID" -- bash -lc "_hwaccel_setup_in_ct '$CTTYPE'" fi } @@ -227,13 +230,30 @@ nvidia_handle_passthrough() { if whiptail --title "NVIDIA passthrough" \ --yesno "NVIDIA GPU detected on host.\n\nMap /dev/nvidia* into CT ${CTID} and install NVIDIA userland inside the container?" 12 70; then + + # 1. Host: map devices into LXC config nvidia_passthrough_to_lxc "$CTID" "$CTTYPE" - # flag for in-CT install (consumed by *-install.sh via tools.func:nvidia_setup_in_ct) - export ENABLE_NVIDIA_IN_CT=1 + + # 2. CT must be running before pct exec + if ! pct status "$CTID" | grep -q "status: running"; then + msg_info "Starting CT $CTID for NVIDIA setup" + pct start "$CTID" + for i in {1..10}; do + if pct status "$CTID" | grep -q "status: running"; then + break + fi + sleep 1 + done + fi + + # 3. Run NVIDIA setup inside CT + pct exec "$CTID" -- bash -lc "_nvidia_setup_in_ct '$CTTYPE'" + else msg_warn "Skipped NVIDIA passthrough by user choice." fi } + # ------------------------------------------------------------------------------ # Hardware acceleration setup inside container (Intel/AMD via VAAPI) # Debian 12 (bookworm), Debian 13 (trixie), Ubuntu 24.04 (noble) From d1258c02ac172c7e024713e3b3f2f798c6967f4b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 22 Sep 2025 13:42:27 +0200 Subject: [PATCH 1098/1733] Update passthrough.func --- misc/passthrough.func | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/misc/passthrough.func b/misc/passthrough.func index 5ea481523..fa0706aa1 100644 --- a/misc/passthrough.func +++ b/misc/passthrough.func @@ -180,15 +180,28 @@ EOF vaapi_handle_passthrough() { local CTID="$1" CTTYPE="$2" APP="$3" - # Allowlist of apps that benefit from VAAPI even in unpriv CTs local VAAPI_APPS=(immich Channels Emby ErsatzTV Frigate Jellyfin Plex Scrypted Tdarr Unmanic Ollama FileFlows "Open WebUI" Tunarr Debian) local is_vaapi_app=false a for a in "${VAAPI_APPS[@]}"; do [[ "$APP" == "$a" ]] && is_vaapi_app=true && break; done if [[ "$CTTYPE" == "0" || "$is_vaapi_app" == "true" ]]; then + # 1. write config vaapi_select_and_apply "$CTID" "$CTTYPE" - # Run userland install inside CT via pct exec + # 2. ensure CT is running + if ! pct status "$CTID" | grep -q "status: running"; then + msg_info "Starting CT $CTID for VAAPI setup" + pct start "$CTID" + for i in {1..10}; do + if pct status "$CTID" | grep -q "status: running"; then + msg_ok "CT $CTID is running" + break + fi + sleep 1 + done + fi + + # 3. run setup inside CT pct exec "$CTID" -- bash -lc "_hwaccel_setup_in_ct '$CTTYPE'" fi } From 333ac1edcd747ce818ece52da67a8a20b579fc7e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 22 Sep 2025 13:59:34 +0200 Subject: [PATCH 1099/1733] Automate VAAPI and NVIDIA setup inside LXC containers Added direct installation of VAAPI and NVIDIA drivers inside LXC containers during setup, removing reliance on custom in-container scripts. The process now ensures required packages are installed and user permissions are set, improving automation and compatibility for supported distributions. --- misc/build.func | 3 +++ misc/passthrough.func | 60 +++++++++++++++++++++++++------------------ 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/misc/build.func b/misc/build.func index 2475c3243..78f36429c 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2276,6 +2276,9 @@ EOF fi msg_info "Customizing LXC Container" + # Container erfolgreich gestartet + vaapi_inside_setup "$CTID" "$CT_TYPE" + nvidia_inside_setup "$CTID" "$CT_TYPE" if [ "$var_os" == "alpine" ]; then sleep 3 pct exec "$CTID" -- /bin/sh -c 'cat </etc/apk/repositories diff --git a/misc/passthrough.func b/misc/passthrough.func index fa0706aa1..e7807a6ef 100644 --- a/misc/passthrough.func +++ b/misc/passthrough.func @@ -181,28 +181,41 @@ vaapi_handle_passthrough() { local CTID="$1" CTTYPE="$2" APP="$3" local VAAPI_APPS=(immich Channels Emby ErsatzTV Frigate Jellyfin Plex Scrypted Tdarr Unmanic Ollama FileFlows "Open WebUI" Tunarr Debian) - local is_vaapi_app=false a + local is_vaapi_app=false for a in "${VAAPI_APPS[@]}"; do [[ "$APP" == "$a" ]] && is_vaapi_app=true && break; done if [[ "$CTTYPE" == "0" || "$is_vaapi_app" == "true" ]]; then - # 1. write config vaapi_select_and_apply "$CTID" "$CTTYPE" - # 2. ensure CT is running - if ! pct status "$CTID" | grep -q "status: running"; then - msg_info "Starting CT $CTID for VAAPI setup" + # ensure CT is running + if ! pct status "$CTID" | grep -q running; then pct start "$CTID" for i in {1..10}; do - if pct status "$CTID" | grep -q "status: running"; then - msg_ok "CT $CTID is running" - break - fi + pct status "$CTID" | grep -q running && break sleep 1 done fi - # 3. run setup inside CT - pct exec "$CTID" -- bash -lc "_hwaccel_setup_in_ct '$CTTYPE'" + # run apt install directly inside CT + pct exec "$CTID" -- bash -lc ' + . /etc/os-release + case "$VERSION_CODENAME" in + trixie|noble) + apt-get update + apt-get install -y intel-media-va-driver-non-free ocl-icd-libopencl1 \ + mesa-opencl-icd mesa-va-drivers libvpl2 vainfo intel-gpu-tools + ;; + *) + apt-get update + apt-get install -y va-driver-all ocl-icd-libopencl1 \ + mesa-opencl-icd mesa-va-drivers vainfo intel-gpu-tools + ;; + esac + if [[ "'"$CTTYPE"'" == "0" ]]; then + adduser "$(id -un)" video || true + adduser "$(id -un)" render || true + fi + ' fi } @@ -238,32 +251,29 @@ nvidia_passthrough_to_lxc() { nvidia_handle_passthrough() { local CTID="$1" CTTYPE="$2" APP="$3" - # Only offer if NVIDIA devices exist on host compgen -G "/dev/nvidia*" >/dev/null || return 0 if whiptail --title "NVIDIA passthrough" \ - --yesno "NVIDIA GPU detected on host.\n\nMap /dev/nvidia* into CT ${CTID} and install NVIDIA userland inside the container?" 12 70; then + --yesno "NVIDIA GPU detected. Map /dev/nvidia* into CT $CTID and install drivers inside?" 12 70; then - # 1. Host: map devices into LXC config nvidia_passthrough_to_lxc "$CTID" "$CTTYPE" - # 2. CT must be running before pct exec - if ! pct status "$CTID" | grep -q "status: running"; then - msg_info "Starting CT $CTID for NVIDIA setup" + if ! pct status "$CTID" | grep -q running; then pct start "$CTID" for i in {1..10}; do - if pct status "$CTID" | grep -q "status: running"; then - break - fi + pct status "$CTID" | grep -q running && break sleep 1 done fi - # 3. Run NVIDIA setup inside CT - pct exec "$CTID" -- bash -lc "_nvidia_setup_in_ct '$CTTYPE'" - - else - msg_warn "Skipped NVIDIA passthrough by user choice." + pct exec "$CTID" -- bash -lc ' + . /etc/os-release + apt-get update + apt-get install -y nvidia-driver nvidia-utils libnvidia-encode1 libcuda1 + if [[ "'"$CTTYPE"'" == "0" ]]; then + adduser "$(id -un)" video || true + fi + ' fi } From 821c4f36f6f118a6a6a970ddca7214b6f633a0bc Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 22 Sep 2025 13:59:56 +0200 Subject: [PATCH 1100/1733] Update passthrough.func --- misc/passthrough.func | 113 ------------------------------------------ 1 file changed, 113 deletions(-) diff --git a/misc/passthrough.func b/misc/passthrough.func index e7807a6ef..690848fad 100644 --- a/misc/passthrough.func +++ b/misc/passthrough.func @@ -276,116 +276,3 @@ nvidia_handle_passthrough() { ' fi } - -# ------------------------------------------------------------------------------ -# Hardware acceleration setup inside container (Intel/AMD via VAAPI) -# Debian 12 (bookworm), Debian 13 (trixie), Ubuntu 24.04 (noble) -# Usage: hwaccel_setup_in_ct [--nonfree-intel] -# ------------------------------------------------------------------------------ -hwaccel_setup_in_ct() { - local CTTYPE="$1" NONFREE=0 - [[ "$2" == "--nonfree-intel" ]] && NONFREE=1 - - local ID VERSION_CODENAME - if [[ -r /etc/os-release ]]; then . /etc/os-release; fi - ID="${ID:-debian}" - VERSION_CODENAME="${VERSION_CODENAME:-bookworm}" - - msg_info "Setting up VAAPI userland for ${ID^} ($VERSION_CODENAME)" - - case "$ID" in - debian | ubuntu) - if ((NONFREE)) && [[ "$VERSION_CODENAME" =~ (trixie|noble) ]]; then - # Debian 13 / Ubuntu 24.04 — enable non-free Intel media (Debian deb822) - if [[ "$VERSION_CODENAME" == "trixie" ]]; then - cat >/etc/apt/sources.list.d/non-free.sources <<'SRC' -Types: deb deb-src -URIs: http://deb.debian.org/debian -Suites: trixie -Components: non-free non-free-firmware - -Types: deb deb-src -URIs: http://deb.debian.org/debian-security -Suites: trixie-security -Components: non-free non-free-firmware - -Types: deb deb-src -URIs: http://deb.debian.org/debian -Suites: trixie-updates -Components: non-free non-free-firmware -SRC - fi - - $STD apt-get update - $STD apt-get install -y \ - intel-media-va-driver-non-free \ - ocl-icd-libopencl1 \ - mesa-opencl-icd \ - mesa-va-drivers \ - libvpl2 \ - vainfo \ - intel-gpu-tools - else - $STD apt-get update - $STD apt-get install -y \ - va-driver-all \ - ocl-icd-libopencl1 \ - mesa-opencl-icd \ - mesa-va-drivers \ - vainfo \ - intel-gpu-tools - fi - ;; - *) - msg_warn "Unsupported distro ($ID $VERSION_CODENAME) – skipping VAAPI setup." - return 0 - ;; - esac - - if [[ "$CTTYPE" == "0" ]]; then - $STD adduser "$(id -un)" video || true - $STD adduser "$(id -un)" render || true - fi - - msg_ok "VAAPI userland ready" -} - -# ------------------------------------------------------------------------------ -# NVIDIA userland inside container -# Debian 12/13, Ubuntu 24.04 -# Usage: nvidia_setup_in_ct -# ------------------------------------------------------------------------------ -nvidia_setup_in_ct() { - local CTTYPE="$1" - - local ID VERSION_CODENAME - if [[ -r /etc/os-release ]]; then . /etc/os-release; fi - ID="${ID:-debian}" - VERSION_CODENAME="${VERSION_CODENAME:-bookworm}" - - msg_info "Installing NVIDIA userland on ${ID^} ($VERSION_CODENAME)" - - case "$ID" in - debian | ubuntu) - $STD apt-get update - $STD apt-get install -y \ - nvidia-driver \ - nvidia-utils \ - libnvidia-encode1 \ - libcuda1 || { - msg_error "Failed to install NVIDIA packages" - return 1 - } - ;; - *) - msg_warn "Unsupported distro ($ID $VERSION_CODENAME) – skipping NVIDIA setup." - return 0 - ;; - esac - - if [[ "$CTTYPE" == "0" ]]; then - $STD adduser "$(id -un)" video || true - fi - - msg_ok "NVIDIA userland ready" -} From ddf5781e551d5d088551b1779042317ee3f4349e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 22 Sep 2025 14:09:59 +0200 Subject: [PATCH 1101/1733] Refactor VAAPI and NVIDIA passthrough setup for LXC Consolidates and simplifies VAAPI and NVIDIA passthrough logic by introducing unified GPU app detection and moving userland package installation into dedicated *_inside_setup functions. Updates build.func to use new function names and ensures setup is only performed for relevant apps and non-Alpine containers. Improves maintainability and clarity of passthrough.func. --- misc/build.func | 11 ++-- misc/passthrough.func | 116 ++++++++++++++++-------------------------- 2 files changed, 51 insertions(+), 76 deletions(-) diff --git a/misc/build.func b/misc/build.func index 78f36429c..af0a07ac1 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2201,8 +2201,8 @@ EOF source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/passthrough.func) usb_handle_passthrough "$CTID" "$CT_TYPE" - vaapi_handle_passthrough "$CTID" "$CT_TYPE" "$APP" - nvidia_handle_passthrough "$CTID" "$CT_TYPE" "$APP" + vaapi_select_and_apply "$CTID" "$CT_TYPE" + nvidia_passthrough_to_lxc "$CTID" "$CT_TYPE" # TUN device passthrough if [ "$ENABLE_TUN" == "yes" ]; then @@ -2276,9 +2276,10 @@ EOF fi msg_info "Customizing LXC Container" - # Container erfolgreich gestartet - vaapi_inside_setup "$CTID" "$CT_TYPE" - nvidia_inside_setup "$CTID" "$CT_TYPE" + if [ "$var_os" != "alpine" ]; then + vaapi_inside_setup "$CTID" "$CT_TYPE" "$APP" + nvidia_inside_setup "$CTID" "$CT_TYPE" "$APP" + fi if [ "$var_os" == "alpine" ]; then sleep 3 pct exec "$CTID" -- /bin/sh -c 'cat </etc/apk/repositories diff --git a/misc/passthrough.func b/misc/passthrough.func index 690848fad..bf81a55ea 100644 --- a/misc/passthrough.func +++ b/misc/passthrough.func @@ -1,7 +1,7 @@ #!/usr/bin/env bash # passthrough.func — host-side passthrough logic (VAAPI & NVIDIA) for LXC # This file ONLY touches host config (/etc/pve/lxc/.conf) and whiptail. -# Inside-CT package setup lives in tools.func (hwaccel_setup_in_ct / nvidia_setup_in_ct). +# Inside-CT package setup lives in *_inside_setup (called from build.func). # --------------------------- Common helpers ----------------------------------- @@ -15,6 +15,9 @@ _whiptail_dims() { echo "$H $maxW" } +# Apps that benefit from GPU passthrough (VAAPI + NVIDIA) +_GPU_APPS=(immich Channels Emby ErsatzTV Frigate Jellyfin Plex Scrypted Tdarr Unmanic Ollama FileFlows "Open WebUI" Tunarr Debian) + # ------------------------------ USB ------------------------------------------- usb_handle_passthrough() { @@ -112,18 +115,11 @@ vaapi_select_and_apply() { n=$((${#items[@]} / 3)) read -r h w < <(_whiptail_dims "$n" "$maxlen") - if [[ "$CTTYPE" == "0" ]]; then - whiptail --title "VAAPI passthrough" --msgbox "\ + whiptail --title "VAAPI passthrough" --msgbox "\ VAAPI passthrough will be enabled. -• Privileged CT: full DRM access -• You may need to install drivers inside the CT (intel-media-driver, vainfo)." 12 "$w" - else - whiptail --title "VAAPI passthrough (unprivileged)" --msgbox "\ -Unprivileged CT: VAAPI may be limited. - -If it fails, consider a privileged CT. You may still need drivers inside the CT." 12 "$w" - fi +Privileged CT = full DRM access +Unprivileged CT = may be limited." 12 "$w" local SELECTED SELECTED="$( @@ -177,46 +173,34 @@ EOF fi } -vaapi_handle_passthrough() { +vaapi_inside_setup() { local CTID="$1" CTTYPE="$2" APP="$3" - local VAAPI_APPS=(immich Channels Emby ErsatzTV Frigate Jellyfin Plex Scrypted Tdarr Unmanic Ollama FileFlows "Open WebUI" Tunarr Debian) - local is_vaapi_app=false - for a in "${VAAPI_APPS[@]}"; do [[ "$APP" == "$a" ]] && is_vaapi_app=true && break; done + local is_gpu_app=false + for a in "${_GPU_APPS[@]}"; do [[ "$APP" == "$a" ]] && is_gpu_app=true && break; done + [[ "$CTTYPE" == "0" || "$is_gpu_app" == "true" ]] || return 0 - if [[ "$CTTYPE" == "0" || "$is_vaapi_app" == "true" ]]; then - vaapi_select_and_apply "$CTID" "$CTTYPE" - - # ensure CT is running - if ! pct status "$CTID" | grep -q running; then - pct start "$CTID" - for i in {1..10}; do - pct status "$CTID" | grep -q running && break - sleep 1 - done + msg_info "Installing VAAPI userland inside CT $CTID" + pct exec "$CTID" -- bash -lc ' + . /etc/os-release + case "$VERSION_CODENAME" in + trixie|noble) + apt-get update + apt-get install -y intel-media-va-driver-non-free ocl-icd-libopencl1 \ + mesa-opencl-icd mesa-va-drivers libvpl2 vainfo intel-gpu-tools + ;; + *) + apt-get update + apt-get install -y va-driver-all ocl-icd-libopencl1 \ + mesa-opencl-icd mesa-va-drivers vainfo intel-gpu-tools + ;; + esac + if [[ "'"$CTTYPE"'" == "0" ]]; then + adduser "$(id -un)" video || true + adduser "$(id -un)" render || true fi - - # run apt install directly inside CT - pct exec "$CTID" -- bash -lc ' - . /etc/os-release - case "$VERSION_CODENAME" in - trixie|noble) - apt-get update - apt-get install -y intel-media-va-driver-non-free ocl-icd-libopencl1 \ - mesa-opencl-icd mesa-va-drivers libvpl2 vainfo intel-gpu-tools - ;; - *) - apt-get update - apt-get install -y va-driver-all ocl-icd-libopencl1 \ - mesa-opencl-icd mesa-va-drivers vainfo intel-gpu-tools - ;; - esac - if [[ "'"$CTTYPE"'" == "0" ]]; then - adduser "$(id -un)" video || true - adduser "$(id -un)" render || true - fi - ' - fi + ' + msg_ok "VAAPI setup done in CT $CTID" } # ----------------------------- NVIDIA ----------------------------------------- @@ -240,7 +224,6 @@ nvidia_passthrough_to_lxc() { return 0 fi - # optional: expose /dev/dri for apps probing VAAPI; harmless with NVIDIA if [[ -d /dev/dri && "$CTTYPE" == "0" ]]; then echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" fi @@ -248,31 +231,22 @@ nvidia_passthrough_to_lxc() { msg_ok "NVIDIA devices mapped to CT ${CTID}" } -nvidia_handle_passthrough() { +nvidia_inside_setup() { local CTID="$1" CTTYPE="$2" APP="$3" + local is_gpu_app=false + for a in "${_GPU_APPS[@]}"; do [[ "$APP" == "$a" ]] && is_gpu_app=true && break; done + [[ "$CTTYPE" == "0" || "$is_gpu_app" == "true" ]] || return 0 + compgen -G "/dev/nvidia*" >/dev/null || return 0 - - if whiptail --title "NVIDIA passthrough" \ - --yesno "NVIDIA GPU detected. Map /dev/nvidia* into CT $CTID and install drivers inside?" 12 70; then - - nvidia_passthrough_to_lxc "$CTID" "$CTTYPE" - - if ! pct status "$CTID" | grep -q running; then - pct start "$CTID" - for i in {1..10}; do - pct status "$CTID" | grep -q running && break - sleep 1 - done + msg_info "Installing NVIDIA userland inside CT $CTID" + pct exec "$CTID" -- bash -lc ' + . /etc/os-release + apt-get update + apt-get install -y nvidia-driver nvidia-utils libnvidia-encode1 libcuda1 + if [[ "'"$CTTYPE"'" == "0" ]]; then + adduser "$(id -un)" video || true fi - - pct exec "$CTID" -- bash -lc ' - . /etc/os-release - apt-get update - apt-get install -y nvidia-driver nvidia-utils libnvidia-encode1 libcuda1 - if [[ "'"$CTTYPE"'" == "0" ]]; then - adduser "$(id -un)" video || true - fi - ' - fi + ' + msg_ok "NVIDIA setup done in CT $CTID" } From 7dd84a1c99718d8503d2522b51a6b64863116d8d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 22 Sep 2025 14:14:35 +0200 Subject: [PATCH 1102/1733] Move VAAPI and NVIDIA setup to debian-install.sh The VAAPI and NVIDIA setup functions are now called directly in debian-install.sh instead of within the LXC container customization block in build.func. The related code in build.func has been commented out to avoid duplicate execution. --- install/debian-install.sh | 3 +++ misc/build.func | 8 ++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/install/debian-install.sh b/install/debian-install.sh index e62591849..d33247254 100644 --- a/install/debian-install.sh +++ b/install/debian-install.sh @@ -17,6 +17,9 @@ msg_info "Installing Dependencies" $STD apt-get install -y gpg msg_ok "Installed Dependencies" +vaapi_inside_setup "$CTID" "$CT_TYPE" "$APP" +nvidia_inside_setup "$CTID" "$CT_TYPE" "$APP" + #setup_mariadb #FFMPEG_VERSION="n7.1.1" FFMPEG_TYPE="full" setup_ffmpeg diff --git a/misc/build.func b/misc/build.func index af0a07ac1..9a349c659 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2276,10 +2276,10 @@ EOF fi msg_info "Customizing LXC Container" - if [ "$var_os" != "alpine" ]; then - vaapi_inside_setup "$CTID" "$CT_TYPE" "$APP" - nvidia_inside_setup "$CTID" "$CT_TYPE" "$APP" - fi + #if [ "$var_os" != "alpine" ]; then + # vaapi_inside_setup "$CTID" "$CT_TYPE" "$APP" + # nvidia_inside_setup "$CTID" "$CT_TYPE" "$APP" + #fi if [ "$var_os" == "alpine" ]; then sleep 3 pct exec "$CTID" -- /bin/sh -c 'cat </etc/apk/repositories From da1c78e295591ac5838ce569d837a3461e7036b2 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 22 Sep 2025 14:27:21 +0200 Subject: [PATCH 1103/1733] Refactor GPU passthrough selection and setup logic Replaces direct USB and GPU passthrough configuration in build.func with a unified select_hw_passthrough function. Refactors passthrough.func to add interactive selection for VAAPI and NVIDIA devices, streamlines device detection, and updates userland installation functions for both VAAPI and NVIDIA. Cleans up and simplifies device mapping and group ID logic. --- misc/build.func | 20 +--- misc/passthrough.func | 255 ++++++++++++++++++++++++------------------ 2 files changed, 145 insertions(+), 130 deletions(-) diff --git a/misc/build.func b/misc/build.func index 9a349c659..203bf4a9d 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2183,26 +2183,8 @@ build_container() { LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" - # USB passthrough for privileged LXC (CT_TYPE=0) - if [ "$CT_TYPE" == "0" ]; then - cat <>"$LXC_CONFIG" -# USB passthrough -lxc.cgroup2.devices.allow: a -lxc.cap.drop: -lxc.cgroup2.devices.allow: c 188:* rwm -lxc.cgroup2.devices.allow: c 189:* rwm -lxc.mount.entry: /dev/serial/by-id dev/serial/by-id none bind,optional,create=dir -lxc.mount.entry: /dev/ttyUSB0 dev/ttyUSB0 none bind,optional,create=file -lxc.mount.entry: /dev/ttyUSB1 dev/ttyUSB1 none bind,optional,create=file -lxc.mount.entry: /dev/ttyACM0 dev/ttyACM0 none bind,optional,create=file -lxc.mount.entry: /dev/ttyACM1 dev/ttyACM1 none bind,optional,create=file -EOF - fi - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/passthrough.func) - usb_handle_passthrough "$CTID" "$CT_TYPE" - vaapi_select_and_apply "$CTID" "$CT_TYPE" - nvidia_passthrough_to_lxc "$CTID" "$CT_TYPE" + select_hw_passthrough "$CTID" "$CT_TYPE" "$APP" # TUN device passthrough if [ "$ENABLE_TUN" == "yes" ]; then diff --git a/misc/passthrough.func b/misc/passthrough.func index bf81a55ea..0d974ddd3 100644 --- a/misc/passthrough.func +++ b/misc/passthrough.func @@ -15,8 +15,61 @@ _whiptail_dims() { echo "$H $maxW" } +select_hw_passthrough() { + local CTID="$1" CTTYPE="$2" APP="$3" + local LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" + + local choices=() + [[ -d /dev/dri ]] && choices+=("VAAPI" "Intel/AMD GPU via VAAPI" OFF) + compgen -G "/dev/nvidia*" >/dev/null && choices+=("NVIDIA" "NVIDIA GPU passthrough" OFF) + + [[ ${#choices[@]} -eq 0 ]] && { + msg_info "No GPU devices detected" + return + } + + local HEIGHT WIDTH + HEIGHT=12 + WIDTH=70 + local SELECTED + SELECTED=$(whiptail --title "GPU Passthrough" \ + --checklist "Select GPU passthrough for CT $CTID:" $HEIGHT $WIDTH 2 \ + "${choices[@]}" 3>&1 1>&2 2>&3) || return + + # export flags for install.sh + for sel in $SELECTED; do + case "$sel" in + "\"VAAPI\"") + export ENABLE_VAAPI=1 + vaapi_select_and_apply "$CTID" "$CTTYPE" + ;; + "\"NVIDIA\"") + export ENABLE_NVIDIA=1 + nvidia_passthrough_to_lxc "$CTID" "$CTTYPE" + ;; + esac + done +} + # Apps that benefit from GPU passthrough (VAAPI + NVIDIA) -_GPU_APPS=(immich Channels Emby ErsatzTV Frigate Jellyfin Plex Scrypted Tdarr Unmanic Ollama FileFlows "Open WebUI" Tunarr Debian) +# Apps that benefit from GPU passthrough (VAAPI + NVIDIA) +_GPU_APPS=( + immich + Channels + Emby + ErsatzTV + Frigate + Jellyfin + Plex + Scrypted + Tdarr + Unmanic + Ollama + FileFlows + "Open WebUI" + Tunarr + Debian +) # ------------------------------ USB ------------------------------------------- @@ -45,15 +98,11 @@ EOF _vaapi_gid() { local g="$1" gid gid="$(getent group "$g" | cut -d: -f3)" - if [[ -z "$gid" ]]; then - case "$g" in - video) echo 44 ;; - render) echo 104 ;; - *) echo 44 ;; - esac - else - echo "$gid" - fi + case "$g" in + video) echo "${gid:-44}" ;; + render) echo "${gid:-104}" ;; + *) echo "${gid:-44}" ;; + esac } _vaapi_pairs() { @@ -68,11 +117,7 @@ _vaapi_pairs() { seen+=("$id") idx="${id#renderD}" - if [[ "$idx" =~ ^[0-9]+$ ]]; then - idx=$((idx - 128)) - else - idx=0 - fi + [[ "$idx" =~ ^[0-9]+$ ]] && idx=$((idx - 128)) || idx=0 card="/dev/dri/card${idx}" [[ -e "$card" ]] || card="" @@ -88,7 +133,6 @@ _vaapi_pairs() { label="$(basename "$real")" [[ -n "$card" ]] && label+=" + $(basename "$card")" label+=" – ${name}" - printf "%s:%s\t%s\n" "$real" "$card" "$label" done shopt -u nullglob @@ -98,42 +142,30 @@ vaapi_select_and_apply() { local CTID="$1" CTTYPE="$2" local LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" - local pairs=() items=() maxlen=0 n h w mapfile -t pairs < <(_vaapi_pairs) - - if ((${#pairs[@]} == 0)); then + ((${#pairs[@]} == 0)) && { msg_warn "No VAAPI devices detected – skipping." - return 0 - fi + return + } + local items=() maxlen=0 for p in "${pairs[@]}"; do - local devs="${p%%$'\t'*}" - local label="${p#*$'\t'}" + local devs="${p%%$'\t'*}" label="${p#*$'\t'}" items+=("$devs" "$label" "OFF") ((${#label} > maxlen)) && maxlen=${#label} done - n=$((${#items[@]} / 3)) - read -r h w < <(_whiptail_dims "$n" "$maxlen") - - whiptail --title "VAAPI passthrough" --msgbox "\ -VAAPI passthrough will be enabled. - -Privileged CT = full DRM access -Unprivileged CT = may be limited." 12 "$w" + read -r h w < <(_whiptail_dims $((${#items[@]} / 3)) "$maxlen") local SELECTED - SELECTED="$( - whiptail --title "VAAPI Device Selection" \ - --checklist "Select GPU / VAAPI device(s) to passthrough:" "$h" "$w" "$((n > 6 ? 6 : n))" \ - "${items[@]}" 3>&1 1>&2 2>&3 - )" || { + SELECTED="$(whiptail --title "VAAPI Device Selection" \ + --checklist "Select VAAPI devices for CT $CTID:" "$h" "$w" 6 \ + "${items[@]}" 3>&1 1>&2 2>&3)" || { msg_warn "VAAPI selection cancelled." - return 0 + return } - [[ -z "$SELECTED" ]] && { - msg_warn "No devices selected – skipping." - return 0 + msg_warn "No VAAPI devices selected." + return } local DID_MOUNT_DRI=0 idx=0 @@ -144,109 +176,110 @@ Unprivileged CT = may be limited." 12 "$w" for d in "$path" "$card"; do [[ -n "$d" && -e "$d" ]] || continue if [[ "$CTTYPE" == "0" ]]; then - if [[ "$DID_MOUNT_DRI" -eq 0 && -d /dev/dri ]]; then + [[ $DID_MOUNT_DRI -eq 0 && -d /dev/dri ]] && { echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" DID_MOUNT_DRI=1 - fi - if mm=$(stat -c '%t:%T' "$d" 2>/dev/null | awk -F: '{printf "%d:%d","0x"$1,"0x"$2}'); then + } + if mm=$(stat -c '%t:%T' "$d" | awk -F: '{printf "%d:%d","0x"$1,"0x"$2}'); then echo "lxc.cgroup2.devices.allow: c $mm rwm" >>"$LXC_CONFIG" echo "lxc.mount.entry: $d $d none bind,optional,create=file" >>"$LXC_CONFIG" - else - msg_warn "Could not stat $d – skipping." fi else - local gid - if [[ "$d" =~ renderD ]]; then gid="$(_vaapi_gid render)"; else gid="$(_vaapi_gid video)"; fi + gid=$([[ "$d" =~ renderD ]] && _vaapi_gid render || _vaapi_gid video) echo "dev${idx}: $d,gid=${gid}" >>"$LXC_CONFIG" idx=$((idx + 1)) fi done done - - # fallback for card0/card1 flip - if [[ "$CTTYPE" == "0" ]]; then - cat <<'EOF' >>"$LXC_CONFIG" -# VAAPI fallback: bind /dev/dri and allow 226:* to survive node flips + [[ "$CTTYPE" == "0" ]] && cat <<'EOF' >>"$LXC_CONFIG" +# VAAPI fallback lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir lxc.cgroup2.devices.allow: c 226:* rwm EOF - fi -} - -vaapi_inside_setup() { - local CTID="$1" CTTYPE="$2" APP="$3" - - local is_gpu_app=false - for a in "${_GPU_APPS[@]}"; do [[ "$APP" == "$a" ]] && is_gpu_app=true && break; done - [[ "$CTTYPE" == "0" || "$is_gpu_app" == "true" ]] || return 0 - - msg_info "Installing VAAPI userland inside CT $CTID" - pct exec "$CTID" -- bash -lc ' - . /etc/os-release - case "$VERSION_CODENAME" in - trixie|noble) - apt-get update - apt-get install -y intel-media-va-driver-non-free ocl-icd-libopencl1 \ - mesa-opencl-icd mesa-va-drivers libvpl2 vainfo intel-gpu-tools - ;; - *) - apt-get update - apt-get install -y va-driver-all ocl-icd-libopencl1 \ - mesa-opencl-icd mesa-va-drivers vainfo intel-gpu-tools - ;; - esac - if [[ "'"$CTTYPE"'" == "0" ]]; then - adduser "$(id -un)" video || true - adduser "$(id -un)" render || true - fi - ' - msg_ok "VAAPI setup done in CT $CTID" } # ----------------------------- NVIDIA ----------------------------------------- nvidia_passthrough_to_lxc() { - local CTID="$1" CTTYPE="${2:-0}" + local CTID="$1" CTTYPE="$2" local LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" - local found=0 dev mm + local found=0 - for dev in /dev/nvidia0 /dev/nvidia1 /dev/nvidiactl /dev/nvidia-uvm /dev/nvidia-uvm-tools /dev/nvidia-modeset; do + for dev in /dev/nvidia*; do [[ -e "$dev" ]] || continue found=1 - if mm="$(stat -c '%t:%T' "$dev" 2>/dev/null | awk -F: '{printf "%d:%d","0x"$1,"0x"$2}')"; then + if mm="$(stat -c '%t:%T' "$dev" | awk -F: '{printf "%d:%d","0x"$1,"0x"$2}')"; then echo "lxc.cgroup2.devices.allow: c $mm rwm" >>"$LXC_CONFIG" echo "lxc.mount.entry: $dev $dev none bind,optional,create=file" >>"$LXC_CONFIG" fi done + ((found == 0)) && { + msg_warn "No NVIDIA devices found." + return + } - if [[ "$found" -eq 0 ]]; then - msg_warn "No /dev/nvidia* devices found on host; skipping NVIDIA passthrough." - return 0 - fi - - if [[ -d /dev/dri && "$CTTYPE" == "0" ]]; then - echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" - fi - + [[ -d /dev/dri && "$CTTYPE" == "0" ]] && echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" msg_ok "NVIDIA devices mapped to CT ${CTID}" } -nvidia_inside_setup() { - local CTID="$1" CTTYPE="$2" APP="$3" +install_vaapi_userland_interactive() { + . /etc/os-release + if [[ "$VERSION_CODENAME" == "trixie" ]]; then + read -r -p "${TAB3}Do you need the intel-media-va-driver-non-free driver for HW encoding (Debian 13 only)? " prompt + if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then + msg_info "Installing Intel Hardware Acceleration (non-free)" + cat <<'EOF' >/etc/apt/sources.list.d/non-free.sources +Types: deb deb-src +URIs: http://deb.debian.org/debian +Suites: trixie +Components: non-free non-free-firmware - local is_gpu_app=false - for a in "${_GPU_APPS[@]}"; do [[ "$APP" == "$a" ]] && is_gpu_app=true && break; done - [[ "$CTTYPE" == "0" || "$is_gpu_app" == "true" ]] || return 0 +Types: deb deb-src +URIs: http://deb.debian.org/debian-security +Suites: trixie-security +Components: non-free non-free-firmware - compgen -G "/dev/nvidia*" >/dev/null || return 0 - msg_info "Installing NVIDIA userland inside CT $CTID" - pct exec "$CTID" -- bash -lc ' - . /etc/os-release - apt-get update - apt-get install -y nvidia-driver nvidia-utils libnvidia-encode1 libcuda1 - if [[ "'"$CTTYPE"'" == "0" ]]; then - adduser "$(id -un)" video || true +Types: deb deb-src +URIs: http://deb.debian.org/debian +Suites: trixie-updates +Components: non-free non-free-firmware +EOF + $STD apt-get update + $STD apt-get install -y \ + intel-media-va-driver-non-free \ + ocl-icd-libopencl1 \ + mesa-opencl-icd \ + mesa-va-drivers \ + libvpl2 \ + vainfo \ + intel-gpu-tools + msg_ok "Installed Intel Hardware Acceleration (non-free)" + return fi - ' - msg_ok "NVIDIA setup done in CT $CTID" + fi + + msg_info "Installing Intel Hardware Acceleration (open packages)" + $STD apt-get update + $STD apt-get install -y \ + va-driver-all \ + ocl-icd-libopencl1 \ + mesa-opencl-icd \ + mesa-va-drivers \ + vainfo \ + intel-gpu-tools + msg_ok "Installed Intel Hardware Acceleration (open packages)" +} + +install_nvidia_userland_interactive() { + msg_info "Installing NVIDIA Userland" + $STD apt-get update + $STD apt-get install -y \ + nvidia-driver \ + nvidia-utils \ + libnvidia-encode1 \ + libcuda1 || { + msg_error "Failed to install NVIDIA packages" + return 1 + } + msg_ok "Installed NVIDIA Userland" } From 3d0994254402b5a26ca1cb27c639567f5377648f Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 22 Sep 2025 14:29:15 +0200 Subject: [PATCH 1104/1733] Update debian-install.sh --- install/debian-install.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/install/debian-install.sh b/install/debian-install.sh index d33247254..299e40028 100644 --- a/install/debian-install.sh +++ b/install/debian-install.sh @@ -17,8 +17,7 @@ msg_info "Installing Dependencies" $STD apt-get install -y gpg msg_ok "Installed Dependencies" -vaapi_inside_setup "$CTID" "$CT_TYPE" "$APP" -nvidia_inside_setup "$CTID" "$CT_TYPE" "$APP" +install_vaapi_userland_interactive #setup_mariadb From 020f55e26c8f19f1908e28e87c02d5eb33891c90 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 22 Sep 2025 14:30:16 +0200 Subject: [PATCH 1105/1733] Auto-select GPU passthrough when only one option Improves the select_hw_passthrough function to automatically select the available GPU passthrough option if only one is detected, instead of prompting the user. This streamlines the user experience when only a single GPU type is present. --- misc/passthrough.func | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/misc/passthrough.func b/misc/passthrough.func index 0d974ddd3..f914e4c2a 100644 --- a/misc/passthrough.func +++ b/misc/passthrough.func @@ -23,20 +23,24 @@ select_hw_passthrough() { [[ -d /dev/dri ]] && choices+=("VAAPI" "Intel/AMD GPU via VAAPI" OFF) compgen -G "/dev/nvidia*" >/dev/null && choices+=("NVIDIA" "NVIDIA GPU passthrough" OFF) + # no GPUs found [[ ${#choices[@]} -eq 0 ]] && { msg_info "No GPU devices detected" return } - local HEIGHT WIDTH - HEIGHT=12 - WIDTH=70 local SELECTED - SELECTED=$(whiptail --title "GPU Passthrough" \ - --checklist "Select GPU passthrough for CT $CTID:" $HEIGHT $WIDTH 2 \ - "${choices[@]}" 3>&1 1>&2 2>&3) || return + if [[ ${#choices[@]} -eq 2 ]]; then + # both available → show whiptail + SELECTED=$(whiptail --title "GPU Passthrough" \ + --checklist "Select GPU passthrough for CT $CTID:" 12 70 2 \ + "${choices[@]}" 3>&1 1>&2 2>&3) || return + else + # only one option → auto-select + SELECTED="\"${choices[0]}\"" + msg_info "Auto-selecting GPU passthrough: ${choices[0]}" + fi - # export flags for install.sh for sel in $SELECTED; do case "$sel" in "\"VAAPI\"") @@ -51,7 +55,6 @@ select_hw_passthrough() { done } -# Apps that benefit from GPU passthrough (VAAPI + NVIDIA) # Apps that benefit from GPU passthrough (VAAPI + NVIDIA) _GPU_APPS=( immich From dc45da17fe7b4e7c050d56a719a42a8368b3177e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 22 Sep 2025 14:33:30 +0200 Subject: [PATCH 1106/1733] Add GPU app check and normalize GPU app list Introduces the _is_gpu_app function to check if an app benefits from GPU passthrough, and updates the GPU app list to use lowercase names for consistency. The select_hw_passthrough function now only proceeds for GPU apps or when CTTYPE is 0. --- misc/passthrough.func | 43 +++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/misc/passthrough.func b/misc/passthrough.func index f914e4c2a..8524b1b77 100644 --- a/misc/passthrough.func +++ b/misc/passthrough.func @@ -19,6 +19,10 @@ select_hw_passthrough() { local CTID="$1" CTTYPE="$2" APP="$3" local LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" + if ! _is_gpu_app "$APP" && [[ "$CTTYPE" != "0" ]]; then + return + fi + local choices=() [[ -d /dev/dri ]] && choices+=("VAAPI" "Intel/AMD GPU via VAAPI" OFF) compgen -G "/dev/nvidia*" >/dev/null && choices+=("NVIDIA" "NVIDIA GPU passthrough" OFF) @@ -58,22 +62,33 @@ select_hw_passthrough() { # Apps that benefit from GPU passthrough (VAAPI + NVIDIA) _GPU_APPS=( immich - Channels - Emby - ErsatzTV - Frigate - Jellyfin - Plex - Scrypted - Tdarr - Unmanic - Ollama - FileFlows - "Open WebUI" - Tunarr - Debian + channels + emby + ersatztv + frigate + jellyfin + plex + scrypted + tdarr + unmanic + ollama + fileflows + "open webui" + tunarr + debian ) +_is_gpu_app() { + local app="$1" + local a + shopt -s nocasematch + for a in "${_GPU_APPS[@]}"; do + [[ "$app" == "$a" ]] && shopt -u nocasematch && return 0 + done + shopt -u nocasematch + return 1 +} + # ------------------------------ USB ------------------------------------------- usb_handle_passthrough() { From 176f86de5d2bf11b79aac636b04fc3b2b68d6310 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 22 Sep 2025 14:48:40 +0200 Subject: [PATCH 1107/1733] Auto-select VAAPI device if only one is available Improves the vaapi_select_and_apply function to automatically select the VAAPI device when only one is detected, bypassing the whiptail selection dialog. This streamlines the user experience by reducing unnecessary prompts. --- misc/passthrough.func | 46 ++++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/misc/passthrough.func b/misc/passthrough.func index 8524b1b77..4c793eeb6 100644 --- a/misc/passthrough.func +++ b/misc/passthrough.func @@ -166,26 +166,34 @@ vaapi_select_and_apply() { return } - local items=() maxlen=0 - for p in "${pairs[@]}"; do - local devs="${p%%$'\t'*}" label="${p#*$'\t'}" - items+=("$devs" "$label" "OFF") - ((${#label} > maxlen)) && maxlen=${#label} - done - read -r h w < <(_whiptail_dims $((${#items[@]} / 3)) "$maxlen") - + # only one device -> auto-select local SELECTED - SELECTED="$(whiptail --title "VAAPI Device Selection" \ - --checklist "Select VAAPI devices for CT $CTID:" "$h" "$w" 6 \ - "${items[@]}" 3>&1 1>&2 2>&3)" || { - msg_warn "VAAPI selection cancelled." - return - } - [[ -z "$SELECTED" ]] && { - msg_warn "No VAAPI devices selected." - return - } + if [[ ${#pairs[@]} -eq 1 ]]; then + SELECTED="${pairs[0]%%$'\t'*}" + msg_info "Auto-selecting VAAPI device: ${pairs[0]#*$'\t'}" + else + # more than one device -> show whiptail + local items=() maxlen=0 + for p in "${pairs[@]}"; do + local devs="${p%%$'\t'*}" label="${p#*$'\t'}" + items+=("$devs" "$label" "OFF") + ((${#label} > maxlen)) && maxlen=${#label} + done + read -r h w < <(_whiptail_dims $((${#items[@]} / 3)) "$maxlen") + SELECTED="$(whiptail --title "VAAPI Device Selection" \ + --checklist "Select VAAPI devices for CT $CTID:" "$h" "$w" 6 \ + "${items[@]}" 3>&1 1>&2 2>&3)" || { + msg_warn "VAAPI selection cancelled." + return + } + [[ -z "$SELECTED" ]] && { + msg_warn "No VAAPI devices selected." + return + } + fi + + # Apply selection to LXC config local DID_MOUNT_DRI=0 idx=0 for dev in $SELECTED; do dev="${dev%\"}" @@ -209,6 +217,8 @@ vaapi_select_and_apply() { fi done done + + # Fallback only for privileged CTs [[ "$CTTYPE" == "0" ]] && cat <<'EOF' >>"$LXC_CONFIG" # VAAPI fallback lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir From 8dec778dffd29141d50f9871fe621e2b1b0cd67c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 22 Sep 2025 14:51:52 +0200 Subject: [PATCH 1108/1733] Update build.func --- misc/build.func | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/misc/build.func b/misc/build.func index 203bf4a9d..d6b0af725 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2301,18 +2301,37 @@ EOF' } destroy_lxc() { - if [[ -n "$CT_ID" ]]; then - read -p "Remove this Container? " prompt - if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then - pct stop "$CT_ID" &>/dev/null - pct destroy "$CT_ID" &>/dev/null - msg_ok "Removed this Container" - else - msg_info "Container was not removed." - fi - else + if [[ -z "$CT_ID" ]]; then msg_error "No CT_ID found. Nothing to remove." + return 1 fi + + # Abbruch bei Ctrl-C / Ctrl-D / ESC + trap 'echo; msg_error "Aborted by user (SIGINT/SIGQUIT)"; return 130' INT QUIT + + local prompt + if ! read -rp "Remove this Container? " prompt; then + # read gibt != 0 zurück bei Ctrl-D/ESC + msg_error "Aborted input (Ctrl-D/ESC)" + return 130 + fi + + case "${prompt,,}" in + y | yes) + if pct stop "$CT_ID" &>/dev/null && pct destroy "$CT_ID" &>/dev/null; then + msg_ok "Removed Container $CT_ID" + else + msg_error "Failed to remove Container $CT_ID" + return 1 + fi + ;; + "" | n | no) + msg_info "Container was not removed." + ;; + *) + msg_warn "Invalid response. Container was not removed." + ;; + esac } # ------------------------------------------------------------------------------ From a185f8ac517bc0f3ea3a430e66d50ee7b238ff1f Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 22 Sep 2025 14:55:06 +0200 Subject: [PATCH 1109/1733] Update passthrough.func --- misc/passthrough.func | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/misc/passthrough.func b/misc/passthrough.func index 4c793eeb6..c1fa18dd4 100644 --- a/misc/passthrough.func +++ b/misc/passthrough.func @@ -1,6 +1,6 @@ #!/usr/bin/env bash # passthrough.func — host-side passthrough logic (VAAPI & NVIDIA) for LXC -# This file ONLY touches host config (/etc/pve/lxc/.conf) and whiptail. +# This file ONLY touches host config (/etc/pve/lxc/.conf) and whiptail. # Inside-CT package setup lives in *_inside_setup (called from build.func). # --------------------------- Common helpers ----------------------------------- @@ -16,10 +16,10 @@ _whiptail_dims() { } select_hw_passthrough() { - local CTID="$1" CTTYPE="$2" APP="$3" - local LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" + local CT_ID="$1" CT_TYPE="$2" APP="$3" + local LXC_CONFIG="/etc/pve/lxc/${CT_ID}.conf" - if ! _is_gpu_app "$APP" && [[ "$CTTYPE" != "0" ]]; then + if ! _is_gpu_app "$APP" && [[ "$CT_TYPE" != "0" ]]; then return fi @@ -37,7 +37,7 @@ select_hw_passthrough() { if [[ ${#choices[@]} -eq 2 ]]; then # both available → show whiptail SELECTED=$(whiptail --title "GPU Passthrough" \ - --checklist "Select GPU passthrough for CT $CTID:" 12 70 2 \ + --checklist "Select GPU passthrough for CT $CT_ID:" 12 70 2 \ "${choices[@]}" 3>&1 1>&2 2>&3) || return else # only one option → auto-select @@ -49,11 +49,11 @@ select_hw_passthrough() { case "$sel" in "\"VAAPI\"") export ENABLE_VAAPI=1 - vaapi_select_and_apply "$CTID" "$CTTYPE" + vaapi_select_and_apply "$CT_ID" "$CT_TYPE" ;; "\"NVIDIA\"") export ENABLE_NVIDIA=1 - nvidia_passthrough_to_lxc "$CTID" "$CTTYPE" + nvidia_passthrough_to_lxc "$CT_ID" "$CT_TYPE" ;; esac done @@ -92,10 +92,10 @@ _is_gpu_app() { # ------------------------------ USB ------------------------------------------- usb_handle_passthrough() { - local CTID="$1" CTTYPE="$2" - local LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" + local CT_ID="$1" CT_TYPE="$2" + local LXC_CONFIG="/etc/pve/lxc/${CT_ID}.conf" - [[ "$CTTYPE" != "0" ]] && return 0 # USB passthrough only for privileged CTs + [[ "$CT_TYPE" != "0" ]] && return 0 # USB passthrough only for privileged CTs cat <>"$LXC_CONFIG" # USB passthrough @@ -157,8 +157,8 @@ _vaapi_pairs() { } vaapi_select_and_apply() { - local CTID="$1" CTTYPE="$2" - local LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" + local CT_ID="$1" CT_TYPE="$2" + local LXC_CONFIG="/etc/pve/lxc/${CT_ID}.conf" mapfile -t pairs < <(_vaapi_pairs) ((${#pairs[@]} == 0)) && { @@ -182,7 +182,7 @@ vaapi_select_and_apply() { read -r h w < <(_whiptail_dims $((${#items[@]} / 3)) "$maxlen") SELECTED="$(whiptail --title "VAAPI Device Selection" \ - --checklist "Select VAAPI devices for CT $CTID:" "$h" "$w" 6 \ + --checklist "Select VAAPI devices for CT $CT_ID:" "$h" "$w" 6 \ "${items[@]}" 3>&1 1>&2 2>&3)" || { msg_warn "VAAPI selection cancelled." return @@ -201,7 +201,7 @@ vaapi_select_and_apply() { IFS=":" read -r path card <<<"$dev" for d in "$path" "$card"; do [[ -n "$d" && -e "$d" ]] || continue - if [[ "$CTTYPE" == "0" ]]; then + if [[ "$CT_TYPE" == "0" ]]; then [[ $DID_MOUNT_DRI -eq 0 && -d /dev/dri ]] && { echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" DID_MOUNT_DRI=1 @@ -219,7 +219,7 @@ vaapi_select_and_apply() { done # Fallback only for privileged CTs - [[ "$CTTYPE" == "0" ]] && cat <<'EOF' >>"$LXC_CONFIG" + [[ "$CT_TYPE" == "0" ]] && cat <<'EOF' >>"$LXC_CONFIG" # VAAPI fallback lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir lxc.cgroup2.devices.allow: c 226:* rwm @@ -229,8 +229,8 @@ EOF # ----------------------------- NVIDIA ----------------------------------------- nvidia_passthrough_to_lxc() { - local CTID="$1" CTTYPE="$2" - local LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" + local CT_ID="$1" CT_TYPE="$2" + local LXC_CONFIG="/etc/pve/lxc/${CT_ID}.conf" local found=0 for dev in /dev/nvidia*; do @@ -246,8 +246,8 @@ nvidia_passthrough_to_lxc() { return } - [[ -d /dev/dri && "$CTTYPE" == "0" ]] && echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" - msg_ok "NVIDIA devices mapped to CT ${CTID}" + [[ -d /dev/dri && "$CT_TYPE" == "0" ]] && echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" + msg_ok "NVIDIA devices mapped to CT ${CT_ID}" } install_vaapi_userland_interactive() { From 2ff50065fb6a5b7b493671e1a83b171430dfcbe4 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 22 Sep 2025 18:30:25 +0200 Subject: [PATCH 1110/1733] Add UpSnap script --- ct/upsnap.sh | 54 +++++++++++++++++++++++++++++++++++++++ install/upsnap-install.sh | 53 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 ct/upsnap.sh create mode 100644 install/upsnap-install.sh diff --git a/ct/upsnap.sh b/ct/upsnap.sh new file mode 100644 index 000000000..4200d6d5b --- /dev/null +++ b/ct/upsnap.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/seriousm4x/UpSnap + +APP="UpSnap" +var_tags="${var_tags:-notes}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-512}" +var_disk="${var_disk:-1024}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/upsnap ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "upsnap" "seriousm4x/UpSnap"; then + msg_info "Stopping Services" + systemctl stop upsnap + msg_ok "Stopped Services" + + fetch_and_deploy_gh_release "upsnap" "seriousm4x/UpSnap" "prebuild" "latest" "/opt/upsnap" "UpSnap_*_linux_amd64.zip" + + msg_info "Starting Services" + systemctl start upsnap + msg_ok "Started Services" + + msg_ok "Updated Successfully" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8090${CL}" diff --git a/install/upsnap-install.sh b/install/upsnap-install.sh new file mode 100644 index 000000000..08914a85e --- /dev/null +++ b/install/upsnap-install.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/seriousm4x/UpSnap + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt-get install -y \ + nmap \ + samba \ + samba-common-bin \ + openssh-client \ + openssh-server \ + sshpass +msg_ok "Installed Dependencies" + +fetch_and_deploy_gh_release "upsnap" "seriousm4x/UpSnap" "prebuild" "latest" "/opt/upsnap" "UpSnap_*_linux_amd64.zip" + +msg_info "Creating Service" +cat </etc/systemd/system/upsnap.service +[Unit] +Description=UpSnap Service +Documentation=https://github.com/seriousm4x/UpSnap/wiki +After=network.target + +[Service] +Type=simple +Restart=on-failure +WorkingDirectory=/opt/upsnap +ExecStart=/opt/upsnap/upsnap serve --http=0.0.0.0:8090 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now upsnap +msg_ok "Service Created" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 61a9618026124f89ab9981f84aa2474a21c5f7a4 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 22 Sep 2025 18:47:20 +0200 Subject: [PATCH 1111/1733] Update UpSnap --- ct/upsnap.sh | 2 +- install/upsnap-install.sh | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ct/upsnap.sh b/ct/upsnap.sh index 4200d6d5b..44e895613 100644 --- a/ct/upsnap.sh +++ b/ct/upsnap.sh @@ -6,7 +6,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV # Source: https://github.com/seriousm4x/UpSnap APP="UpSnap" -var_tags="${var_tags:-notes}" +var_tags="${var_tags:-network}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-1024}" diff --git a/install/upsnap-install.sh b/install/upsnap-install.sh index 08914a85e..7ad997591 100644 --- a/install/upsnap-install.sh +++ b/install/upsnap-install.sh @@ -24,6 +24,7 @@ $STD apt-get install -y \ msg_ok "Installed Dependencies" fetch_and_deploy_gh_release "upsnap" "seriousm4x/UpSnap" "prebuild" "latest" "/opt/upsnap" "UpSnap_*_linux_amd64.zip" +setcap 'cap_net_raw=+ep' /opt/upsnap/upsnap msg_info "Creating Service" cat </etc/systemd/system/upsnap.service @@ -34,6 +35,7 @@ After=network.target [Service] Type=simple +User=root Restart=on-failure WorkingDirectory=/opt/upsnap ExecStart=/opt/upsnap/upsnap serve --http=0.0.0.0:8090 From e168aee4bcd9e695eac84f00ce7543d160c883b6 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 22 Sep 2025 16:47:40 +0000 Subject: [PATCH 1112/1733] Update .app files --- ct/headers/manyfold | 6 ------ ct/headers/upsnap | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) delete mode 100644 ct/headers/manyfold create mode 100644 ct/headers/upsnap diff --git a/ct/headers/manyfold b/ct/headers/manyfold deleted file mode 100644 index 1f6a66f2d..000000000 --- a/ct/headers/manyfold +++ /dev/null @@ -1,6 +0,0 @@ - __ ___ ____ __ __ - / |/ /___ _____ __ __/ __/___ / /___/ / - / /|_/ / __ `/ __ \/ / / / /_/ __ \/ / __ / - / / / / /_/ / / / / /_/ / __/ /_/ / / /_/ / -/_/ /_/\__,_/_/ /_/\__, /_/ \____/_/\__,_/ - /____/ diff --git a/ct/headers/upsnap b/ct/headers/upsnap new file mode 100644 index 000000000..0a3180772 --- /dev/null +++ b/ct/headers/upsnap @@ -0,0 +1,6 @@ + __ __ _____ + / / / /___ / ___/____ ____ _____ + / / / / __ \\__ \/ __ \/ __ `/ __ \ +/ /_/ / /_/ /__/ / / / / /_/ / /_/ / +\____/ .___/____/_/ /_/\__,_/ .___/ + /_/ /_/ From 384565abe79b8e4f00ffbe807538add7a82adfd0 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 22 Sep 2025 18:55:03 +0200 Subject: [PATCH 1113/1733] Add UpSnap json --- frontend/public/json/upsnap.json | 40 ++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 frontend/public/json/upsnap.json diff --git a/frontend/public/json/upsnap.json b/frontend/public/json/upsnap.json new file mode 100644 index 000000000..f545d92be --- /dev/null +++ b/frontend/public/json/upsnap.json @@ -0,0 +1,40 @@ +{ + "name": "UpSnap", + "slug": "upsnap", + "categories": [ + 4 + ], + "date_created": "2025-09-12", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 8090, + "documentation": "https://github.com/seriousm4x/UpSnap/wiki", + "config_path": "", + "website": "https://github.com/seriousm4x/UpSnap", + "logo": "hhttps://cdn.jsdelivr.net/gh/selfhst/icons/webp/upsnap.webp", + "description": "A simple wake on lan web app written with SvelteKit, Go and PocketBase.", + "install_methods": [ + { + "type": "default", + "script": "ct/upsnap.sh", + "resources": { + "cpu": 1, + "ram": 512, + "hdd": 1, + "os": "Debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "The first user you register will be the admin user.", + "type": "info" + } + ] +} From 726a5425af42cf53b1c76a0cc03055a3f51f9511 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 22 Sep 2025 19:24:43 +0200 Subject: [PATCH 1114/1733] Update UpSnap --- ct/upsnap.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/upsnap.sh b/ct/upsnap.sh index 44e895613..40f31825a 100644 --- a/ct/upsnap.sh +++ b/ct/upsnap.sh @@ -9,7 +9,7 @@ APP="UpSnap" var_tags="${var_tags:-network}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" -var_disk="${var_disk:-1024}" +var_disk="${var_disk:-1}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" From 2da3b1bb15f5dd2ea5713f3da1131364755e8621 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 22 Sep 2025 19:50:13 +0200 Subject: [PATCH 1115/1733] deb13 --- ct/upsnap.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/upsnap.sh b/ct/upsnap.sh index 40f31825a..203962e99 100644 --- a/ct/upsnap.sh +++ b/ct/upsnap.sh @@ -9,9 +9,9 @@ APP="UpSnap" var_tags="${var_tags:-network}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" -var_disk="${var_disk:-1}" +var_disk="${var_disk:-2}" var_os="${var_os:-debian}" -var_version="${var_version:-12}" +var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" From 9d35c3124321ee8df028a6916d20cb2ea0d26ef2 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 22 Sep 2025 19:51:00 +0200 Subject: [PATCH 1116/1733] deb13 --- install/upsnap-install.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/install/upsnap-install.sh b/install/upsnap-install.sh index 7ad997591..3e9c1afd9 100644 --- a/install/upsnap-install.sh +++ b/install/upsnap-install.sh @@ -14,7 +14,7 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt-get install -y \ +$STD apt install -y \ nmap \ samba \ samba-common-bin \ @@ -50,6 +50,7 @@ motd_ssh customize msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean +$STD apt -y autoremove +$STD apt -y autoclean +$STD apt -y clean msg_ok "Cleaned" From a431ee99e8648784cb3acf2ca4b517f693fd829b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 22 Sep 2025 19:51:55 +0200 Subject: [PATCH 1117/1733] deb13 --- frontend/public/json/upsnap.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/public/json/upsnap.json b/frontend/public/json/upsnap.json index f545d92be..433589b32 100644 --- a/frontend/public/json/upsnap.json +++ b/frontend/public/json/upsnap.json @@ -21,9 +21,9 @@ "resources": { "cpu": 1, "ram": 512, - "hdd": 1, + "hdd": 2, "os": "Debian", - "version": "12" + "version": "13" } } ], From 9926ed24e355239dd04fe1b9366b9ca39e94a3bf Mon Sep 17 00:00:00 2001 From: Jocelyn Knight Date: Fri, 19 Sep 2025 14:28:55 -0700 Subject: [PATCH 1118/1733] Add: Verdaccio LXC --- ct/headers/verdaccio | 6 ++ ct/verdaccio.sh | 46 +++++++++++ frontend/public/json/verdaccio.json | 52 ++++++++++++ install/verdaccio-install.sh | 118 ++++++++++++++++++++++++++++ 4 files changed, 222 insertions(+) create mode 100644 ct/headers/verdaccio create mode 100644 ct/verdaccio.sh create mode 100644 frontend/public/json/verdaccio.json create mode 100644 install/verdaccio-install.sh diff --git a/ct/headers/verdaccio b/ct/headers/verdaccio new file mode 100644 index 000000000..76128a0b2 --- /dev/null +++ b/ct/headers/verdaccio @@ -0,0 +1,6 @@ + _ __ __ _ + | | / /__ _________/ /___ ___________(_)___ + | | / / _ \/ ___/ __ / __ `/ ___/ ___/ / __ \ + | |/ / __/ / / /_/ / /_/ / /__/ /__/ / /_/ / + |___/\___/_/ \__,_/\__,_/\___/\___/_/\____/ + \ No newline at end of file diff --git a/ct/verdaccio.sh b/ct/verdaccio.sh new file mode 100644 index 000000000..1b8d158de --- /dev/null +++ b/ct/verdaccio.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: BrynnJKnight +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://verdaccio.org/ + +APP="Verdaccio" +var_tags="${var_tags:-dev-tools;npm;registry}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-8}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -f /etc/systemd/system/verdaccio.service ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + NODE_VERSION="22" setup_nodejs + + msg_info "Updating ${APP} LXC" + $STD npm update -g verdaccio + systemctl restart verdaccio + msg_ok "Updated Successfully" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:4873${CL}" \ No newline at end of file diff --git a/frontend/public/json/verdaccio.json b/frontend/public/json/verdaccio.json new file mode 100644 index 000000000..b8e7443bb --- /dev/null +++ b/frontend/public/json/verdaccio.json @@ -0,0 +1,52 @@ +{ + "name": "Verdaccio", + "slug": "verdaccio", + "categories": [ + 20 + ], + "date_created": "2025-01-19", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 4873, + "documentation": "https://verdaccio.org/docs/what-is-verdaccio", + "website": "https://verdaccio.org/", + "logo": "https://verdaccio.org/img/logo/symbol/png/verdaccio-tiny.png", + "config_path": "/etc/verdaccio/config.yaml", + "description": "Verdaccio is a lightweight private npm proxy registry built with Node.js. It allows you to host your own npm registry with minimal configuration, providing a private npm repository for your projects. Verdaccio supports npm, yarn, and pnpm, and can cache packages from the public npm registry, allowing for faster installs and protection against npm registry outages. It includes a web interface for browsing packages, authentication and authorization features, and can be easily integrated into your development workflow.", + "install_methods": [ + { + "type": "default", + "script": "ct/verdaccio.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 8, + "os": "debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": "Create via CLI", + "password": "Create via CLI" + }, + "notes": [ + { + "text": "Default configuration proxies to npmjs.org for packages not found locally.", + "type": "info" + }, + { + "text": "To create the first user, run: npm adduser --registry http://:4873", + "type": "info" + }, + { + "text": "After creating a user, you can publish packages with: npm publish --registry http://:4873", + "type": "info" + }, + { + "text": "Configuration file located at /etc/verdaccio/config.yaml", + "type": "info" + } + ] +} \ No newline at end of file diff --git a/install/verdaccio-install.sh b/install/verdaccio-install.sh new file mode 100644 index 000000000..b19c504cb --- /dev/null +++ b/install/verdaccio-install.sh @@ -0,0 +1,118 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: BrynnJKnight +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://verdaccio.org/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt-get install -y \ + ca-certificates \ + build-essential +msg_ok "Installed Dependencies" + +NODE_VERSION="22" setup_nodejs + +msg_info "Installing Verdaccio" +$STD npm install --global verdaccio +msg_ok "Installed Verdaccio" + +msg_info "Configuring Verdaccio" +HOST_IP=$(hostname -I | awk '{print $1}') +mkdir -p /etc/verdaccio +mkdir -p /var/lib/verdaccio + +cat </etc/verdaccio/config.yaml +# Verdaccio configuration +storage: /var/lib/verdaccio +auth: + htpasswd: + file: /var/lib/verdaccio/htpasswd + max_users: 1000 +uplinks: + npmjs: + url: https://registry.npmjs.org/ +packages: + '@*/*': + access: \$all + publish: \$authenticated + proxy: npmjs + '**': + access: \$all + publish: \$authenticated + proxy: npmjs +middlewares: + audit: + enabled: true +logs: + - {type: stdout, format: pretty, level: http} +listen: + - 0.0.0.0:4873 +web: + enable: true + title: Verdaccio + gravatar: true + sort_packages: asc + login: true +EOF + +chown -R root:root /etc/verdaccio +chown -R root:root /var/lib/verdaccio +chmod -R 755 /etc/verdaccio +chmod -R 755 /var/lib/verdaccio +msg_ok "Configured Verdaccio" + +msg_info "Creating Service" +cat </etc/systemd/system/verdaccio.service +[Unit] +Description=Verdaccio lightweight private npm proxy registry +After=network.target + +[Service] +Type=simple +ExecStart=/usr/bin/verdaccio --config /etc/verdaccio/config.yaml +Restart=on-failure +StandardOutput=journal +StandardError=journal +SyslogIdentifier=verdaccio +KillMode=control-group + +[Install] +WantedBy=multi-user.target +EOF + +systemctl enable -q --now verdaccio +msg_ok "Created Service" + +msg_info "Creating Update Script" +cat <<'EOF' >/usr/bin/update +#!/bin/bash +set -euo pipefail +NODE_VERSION="22" +export NVM_DIR="/opt/nvm" +[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" +nvm use $NODE_VERSION >/dev/null 2>&1 + +echo "Updating Verdaccio..." +npm update -g verdaccio +systemctl restart verdaccio +echo "Verdaccio has been updated successfully." +EOF +chmod +x /usr/bin/update +msg_ok "Created Update Script" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" \ No newline at end of file From 4a2786ac4d0228fe88ed6e04b8be165c418d47d9 Mon Sep 17 00:00:00 2001 From: Jocelyn Knight Date: Sat, 20 Sep 2025 10:44:24 -0700 Subject: [PATCH 1119/1733] Update: Address PR feedback - Change credentials from 'Create via CLI' to null - Remove redundant informational notes - Move config and storage from /etc and /var/lib to /opt - Remove custom update script (use built-in) - Update source URL to ProxmoxVED - Switch to Debian 13 (Trixie) --- ct/verdaccio.sh | 4 ++-- frontend/public/json/verdaccio.json | 20 ++++------------- install/verdaccio-install.sh | 35 +++++++---------------------- 3 files changed, 14 insertions(+), 45 deletions(-) diff --git a/ct/verdaccio.sh b/ct/verdaccio.sh index 1b8d158de..45bf1a0e4 100644 --- a/ct/verdaccio.sh +++ b/ct/verdaccio.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: BrynnJKnight # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -11,7 +11,7 @@ var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" -var_version="${var_version:-12}" +var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" diff --git a/frontend/public/json/verdaccio.json b/frontend/public/json/verdaccio.json index b8e7443bb..710c54b98 100644 --- a/frontend/public/json/verdaccio.json +++ b/frontend/public/json/verdaccio.json @@ -12,7 +12,7 @@ "documentation": "https://verdaccio.org/docs/what-is-verdaccio", "website": "https://verdaccio.org/", "logo": "https://verdaccio.org/img/logo/symbol/png/verdaccio-tiny.png", - "config_path": "/etc/verdaccio/config.yaml", + "config_path": "/opt/verdaccio/config/config.yaml", "description": "Verdaccio is a lightweight private npm proxy registry built with Node.js. It allows you to host your own npm registry with minimal configuration, providing a private npm repository for your projects. Verdaccio supports npm, yarn, and pnpm, and can cache packages from the public npm registry, allowing for faster installs and protection against npm registry outages. It includes a web interface for browsing packages, authentication and authorization features, and can be easily integrated into your development workflow.", "install_methods": [ { @@ -23,30 +23,18 @@ "ram": 2048, "hdd": 8, "os": "debian", - "version": "12" + "version": "13" } } ], "default_credentials": { - "username": "Create via CLI", - "password": "Create via CLI" + "username": null, + "password": null }, "notes": [ - { - "text": "Default configuration proxies to npmjs.org for packages not found locally.", - "type": "info" - }, { "text": "To create the first user, run: npm adduser --registry http://:4873", "type": "info" - }, - { - "text": "After creating a user, you can publish packages with: npm publish --registry http://:4873", - "type": "info" - }, - { - "text": "Configuration file located at /etc/verdaccio/config.yaml", - "type": "info" } ] } \ No newline at end of file diff --git a/install/verdaccio-install.sh b/install/verdaccio-install.sh index b19c504cb..965517264 100644 --- a/install/verdaccio-install.sh +++ b/install/verdaccio-install.sh @@ -26,16 +26,15 @@ $STD npm install --global verdaccio msg_ok "Installed Verdaccio" msg_info "Configuring Verdaccio" -HOST_IP=$(hostname -I | awk '{print $1}') -mkdir -p /etc/verdaccio -mkdir -p /var/lib/verdaccio +mkdir -p /opt/verdaccio/config +mkdir -p /opt/verdaccio/storage -cat </etc/verdaccio/config.yaml +cat </opt/verdaccio/config/config.yaml # Verdaccio configuration -storage: /var/lib/verdaccio +storage: /opt/verdaccio/storage auth: htpasswd: - file: /var/lib/verdaccio/htpasswd + file: /opt/verdaccio/storage/htpasswd max_users: 1000 uplinks: npmjs: @@ -64,10 +63,8 @@ web: login: true EOF -chown -R root:root /etc/verdaccio -chown -R root:root /var/lib/verdaccio -chmod -R 755 /etc/verdaccio -chmod -R 755 /var/lib/verdaccio +chown -R root:root /opt/verdaccio +chmod -R 755 /opt/verdaccio msg_ok "Configured Verdaccio" msg_info "Creating Service" @@ -78,7 +75,7 @@ After=network.target [Service] Type=simple -ExecStart=/usr/bin/verdaccio --config /etc/verdaccio/config.yaml +ExecStart=/usr/bin/verdaccio --config /opt/verdaccio/config/config.yaml Restart=on-failure StandardOutput=journal StandardError=journal @@ -92,22 +89,6 @@ EOF systemctl enable -q --now verdaccio msg_ok "Created Service" -msg_info "Creating Update Script" -cat <<'EOF' >/usr/bin/update -#!/bin/bash -set -euo pipefail -NODE_VERSION="22" -export NVM_DIR="/opt/nvm" -[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" -nvm use $NODE_VERSION >/dev/null 2>&1 - -echo "Updating Verdaccio..." -npm update -g verdaccio -systemctl restart verdaccio -echo "Verdaccio has been updated successfully." -EOF -chmod +x /usr/bin/update -msg_ok "Created Update Script" motd_ssh customize From 5c88f66ac22186b804358b723ea2e0eb3408a8e9 Mon Sep 17 00:00:00 2001 From: Jocelyn Knight Date: Sat, 20 Sep 2025 10:56:39 -0700 Subject: [PATCH 1120/1733] Fix: Correct date_created from January to September --- frontend/public/json/verdaccio.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/verdaccio.json b/frontend/public/json/verdaccio.json index 710c54b98..e1194b785 100644 --- a/frontend/public/json/verdaccio.json +++ b/frontend/public/json/verdaccio.json @@ -4,7 +4,7 @@ "categories": [ 20 ], - "date_created": "2025-01-19", + "date_created": "2025-09-19", "type": "ct", "updateable": true, "privileged": false, From 4d2171b270d8a10f6a6e377e96f7253532f32c90 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 22 Sep 2025 18:41:57 +0000 Subject: [PATCH 1121/1733] Update .app files --- ct/headers/verdaccio | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ct/headers/verdaccio b/ct/headers/verdaccio index 76128a0b2..bf03eeff7 100644 --- a/ct/headers/verdaccio +++ b/ct/headers/verdaccio @@ -1,6 +1,6 @@ - _ __ __ _ - | | / /__ _________/ /___ ___________(_)___ - | | / / _ \/ ___/ __ / __ `/ ___/ ___/ / __ \ - | |/ / __/ / / /_/ / /_/ / /__/ /__/ / /_/ / - |___/\___/_/ \__,_/\__,_/\___/\___/_/\____/ - \ No newline at end of file + _ __ __ _ +| | / /__ _________/ /___ ___________(_)___ +| | / / _ \/ ___/ __ / __ `/ ___/ ___/ / __ \ +| |/ / __/ / / /_/ / /_/ / /__/ /__/ / /_/ / +|___/\___/_/ \__,_/\__,_/\___/\___/_/\____/ + From f5affeda71e106951526bd8dddfca2e32770d947 Mon Sep 17 00:00:00 2001 From: Vincent <114195376+HydroshieldMKII@users.noreply.github.com> Date: Mon, 22 Sep 2025 15:46:17 -0400 Subject: [PATCH 1122/1733] Init app install + json + default ct update --- ct/guardian.sh | 77 ++++++++++++++++++++++ frontend/public/json/guardian.json | 33 ++++++++++ install/guardian-install.sh | 101 +++++++++++++++++++++++++++++ 3 files changed, 211 insertions(+) create mode 100644 ct/guardian.sh create mode 100644 frontend/public/json/guardian.json create mode 100644 install/guardian-install.sh diff --git a/ct/guardian.sh b/ct/guardian.sh new file mode 100644 index 000000000..42aaa0bc1 --- /dev/null +++ b/ct/guardian.sh @@ -0,0 +1,77 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: HydroshieldMKII +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/HydroshieldMKII/Guardian + +APP="Guardian" +var_tags="${var_tags:-media;monitoring}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + # Check if installation is present | -f for file, -d for folder + if [[ ! -d "/opt/${APP}" ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + # Crawling the new version and checking whether an update is required + RELEASE=$(curl -fsSL [RELEASE_URL] | [PARSE_RELEASE_COMMAND]) + if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then + # Stopping Services + msg_info "Stopping $APP" + systemctl stop [SERVICE_NAME] + msg_ok "Stopped $APP" + + # Creating Backup + msg_info "Creating Backup" + tar -czf "/opt/${APP}_backup_$(date +%F).tar.gz" [IMPORTANT_PATHS] + msg_ok "Backup Created" + + # Execute Update + msg_info "Updating $APP to v${RELEASE}" + [UPDATE_COMMANDS] + msg_ok "Updated $APP to v${RELEASE}" + + # Starting Services + msg_info "Starting $APP" + systemctl start [SERVICE_NAME] + msg_ok "Started $APP" + + # Cleaning up + msg_info "Cleaning Up" + rm -rf [TEMP_FILES] + msg_ok "Cleanup Completed" + + # Last Action + echo "${RELEASE}" >/opt/${APP}_version.txt + msg_ok "Update Successful" + else + msg_ok "No update required. ${APP} is already at v${RELEASE}" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:[PORT]${CL}" diff --git a/frontend/public/json/guardian.json b/frontend/public/json/guardian.json new file mode 100644 index 000000000..4bc9f6dc0 --- /dev/null +++ b/frontend/public/json/guardian.json @@ -0,0 +1,33 @@ +{ + "name": "Guardian", + "slug": "guardian", + "categories": [13], + "date_created": "2025-09-22", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 3000, + "documentation": null, + "config_path": "", + "website": "https://github.com/HydroshieldMKII/Guardian", + "logo": null, + "description": "A tool to manage devices access to your Plex server with a nice Dashboard ", + "install_methods": [ + { + "type": "default", + "script": "ct/guardian.sh", + "resources": { + "cpu": 4, + "ram": 2048, + "hdd": 6, + "os": "Debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} diff --git a/install/guardian-install.sh b/install/guardian-install.sh new file mode 100644 index 000000000..77d73eed0 --- /dev/null +++ b/install/guardian-install.sh @@ -0,0 +1,101 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: HydroshieldMKII +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/HydroshieldMKII/Guardian + +# Import Functions und Setup +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +# Installing Dependencies +msg_info "Installing Dependencies" +$STD apt-get install -y \ + git \ + nodejs \ + npm \ + sqlite +msg_ok "Installed Dependencies" + +# Setup App +msg_info "Setup ${APPLICATION}" +RELEASE=$(curl -fsSL https://api.github.com/repos/HydroshieldMKII/Guardian/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') +curl -fsSL -o "${RELEASE}.zip" "https://github.com/HydroshieldMKII/Guardian/archive/refs/tags/${RELEASE}.zip" +unzip -q "${RELEASE}.zip" +mv "${APPLICATION}-${RELEASE}/" "/opt/${APPLICATION}" +# +# +# +echo "${RELEASE}" >/opt/"${APPLICATION}"_version.txt +msg_ok "Setup ${APPLICATION}" + +# ===== Build Backend ===== +msg_info "Building backend" +cd /opt/${APPLICATION}/backend +npm ci +npm run build +msg_ok "Built backend" + +# ===== Build Frontend ===== +msg_info "Building frontend" +cd /opt/${APPLICATION}/frontend +npm ci +npm run build +msg_ok "Built frontend" + +# ===== Backend Service ===== +msg_info "Creating Backend Service" +cat </etc/systemd/system/guardian-backend.service +[Unit] +Description=Guardian Backend +After=network.target + +[Service] +WorkingDirectory=/opt/Guardian/backend +Environment=NODE_ENV=development +ExecStart=/usr/bin/node dist/main.js +Restart=always +RestartSec=3 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now guardian-backend +msg_ok "Created Backend Service" + +# ===== Frontend Service ===== +msg_info "Creating Frontend Service" +cat </etc/systemd/system/guardian-frontend.service +[Unit] +Description=Guardian Frontend +After=guardian-backend.service network.target +Wants=guardian-backend.service + +[Service] +WorkingDirectory=/opt/Guardian/frontend +Environment=NODE_ENV=development +ExecStart=/usr/bin/npm run start +Restart=always +RestartSec=3 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now guardian-frontend +msg_ok "Created Frontend Service" + +motd_ssh +customize + +# Cleanup +msg_info "Cleaning up" +rm -f "${RELEASE}".zip +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 08c4d18fc871bbffe4ee07e6a47a7544be4651d3 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 23 Sep 2025 09:56:24 +0200 Subject: [PATCH 1123/1733] Update upsnap.json --- frontend/public/json/upsnap.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/upsnap.json b/frontend/public/json/upsnap.json index 433589b32..82952a8a1 100644 --- a/frontend/public/json/upsnap.json +++ b/frontend/public/json/upsnap.json @@ -12,7 +12,7 @@ "documentation": "https://github.com/seriousm4x/UpSnap/wiki", "config_path": "", "website": "https://github.com/seriousm4x/UpSnap", - "logo": "hhttps://cdn.jsdelivr.net/gh/selfhst/icons/webp/upsnap.webp", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/upsnap.webp", "description": "A simple wake on lan web app written with SvelteKit, Go and PocketBase.", "install_methods": [ { From f11d455d5a11153444c89cf9e45bff175a43dc2f Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 23 Sep 2025 12:54:12 +0200 Subject: [PATCH 1124/1733] Improve network string construction in build_container Refactored the build_container function to handle MAC, gateway, VLAN, and MTU parameters more robustly, allowing for both direct and preformatted input. This change ensures correct formatting and prevents duplicate parameter prefixes in the network string. --- misc/build.func | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/misc/build.func b/misc/build.func index d6b0af725..d0df26b36 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2109,11 +2109,43 @@ build_container() { # if [ "$VERBOSE" == "yes" ]; then set -x; fi NET_STRING="-net0 name=eth0,bridge=${BRG:-vmbr0}" - NET_STRING+="${MAC:+,hwaddr=$MAC}" + + # MAC + if [[ -n "$MAC" ]]; then + case "$MAC" in + ,hwaddr=*) NET_STRING+="$MAC" ;; + *) NET_STRING+=",hwaddr=$MAC" ;; + esac + fi + + # IP (immer zwingend, Standard dhcp) NET_STRING+=",ip=${NET:-dhcp}" - NET_STRING+="${GATE:+,gw=$GATE}" - NET_STRING+="${VLAN:+,tag=$VLAN}" - NET_STRING+="${MTU:+,mtu=$MTU}" + + # Gateway + if [[ -n "$GATE" ]]; then + case "$GATE" in + ,gw=*) NET_STRING+="$GATE" ;; + *) NET_STRING+=",gw=$GATE" ;; + esac + fi + + # VLAN + if [[ -n "$VLAN" ]]; then + case "$VLAN" in + ,tag=*) NET_STRING+="$VLAN" ;; + *) NET_STRING+=",tag=$VLAN" ;; + esac + fi + + # MTU + if [[ -n "$MTU" ]]; then + case "$MTU" in + ,mtu=*) NET_STRING+="$MTU" ;; + *) NET_STRING+=",mtu=$MTU" ;; + esac + fi + + # IPv6 Handling case "$IPV6_METHOD" in auto) NET_STRING="$NET_STRING,ip6=auto" ;; dhcp) NET_STRING="$NET_STRING,ip6=dhcp" ;; From ac85426b7afa7a520904904575a571d1888ed8f8 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 23 Sep 2025 13:00:10 +0200 Subject: [PATCH 1125/1733] Update build.func --- misc/build.func | 1 + 1 file changed, 1 insertion(+) diff --git a/misc/build.func b/misc/build.func index d0df26b36..e48e17b67 100644 --- a/misc/build.func +++ b/misc/build.func @@ -22,6 +22,7 @@ variables() { DIAGNOSTICS="yes" # sets the DIAGNOSTICS variable to "yes", used for the API call. METHOD="default" # sets the METHOD variable to "default", used for the API call. RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" # generates a random UUID and sets it to the RANDOM_UUID variable. + CTTYPE="${CTTYPE:-${CT_TYPE:-1}}" #CT_TYPE=${var_unprivileged:-$CT_TYPE} } From a17aa696b600d7b30406658c2862a30d83f1a2e1 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 23 Sep 2025 13:28:11 +0200 Subject: [PATCH 1126/1733] Update passthrough.func --- misc/passthrough.func | 2 ++ 1 file changed, 2 insertions(+) diff --git a/misc/passthrough.func b/misc/passthrough.func index c1fa18dd4..9c19cdbe1 100644 --- a/misc/passthrough.func +++ b/misc/passthrough.func @@ -3,6 +3,8 @@ # This file ONLY touches host config (/etc/pve/lxc/.conf) and whiptail. # Inside-CT package setup lives in *_inside_setup (called from build.func). +CTTYPE="${CTTYPE:-${CT_TYPE:-1}}" + # --------------------------- Common helpers ----------------------------------- _whiptail_dims() { From 82c6e6e6fb144a5508b6a68ace4a6813d520f177 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 23 Sep 2025 13:51:46 +0200 Subject: [PATCH 1127/1733] Refactor GPU and USB passthrough setup in container scripts Introduced a unified gpu_inside_setup function in build.func to handle VAAPI and NVIDIA userland installation inside containers. Replaced bracket conditionals with if statements in passthrough.func for improved readability and maintainability, and made minor logic clarifications for privileged container checks. --- misc/build.func | 57 ++++++++++++++++++++++++++++++++++++++++--- misc/passthrough.func | 13 +++++++--- 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/misc/build.func b/misc/build.func index e48e17b67..9f63fea5f 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2290,11 +2290,60 @@ EOF fi fi + gpu_inside_setup() { + local CTID="$1" + + # VAAPI inside (Debian/Ubuntu) + if [[ "${ENABLE_VAAPI:-0}" -eq 1 ]]; then + msg_info "Installing VAAPI userland inside CT ${CTID}" + pct exec "$CTID" -- bash -lc ' + set -e + . /etc/os-release || true + if [[ "${VERSION_CODENAME:-}" == "trixie" ]]; then + cat >/etc/apt/sources.list.d/non-free.sources </etc/apk/repositories diff --git a/misc/passthrough.func b/misc/passthrough.func index 9c19cdbe1..6eab5f0b2 100644 --- a/misc/passthrough.func +++ b/misc/passthrough.func @@ -97,8 +97,9 @@ usb_handle_passthrough() { local CT_ID="$1" CT_TYPE="$2" local LXC_CONFIG="/etc/pve/lxc/${CT_ID}.conf" - [[ "$CT_TYPE" != "0" ]] && return 0 # USB passthrough only for privileged CTs - + if [[ "$CT_TYPE" != "0" ]]; then + return 0 # USB passthrough only for privileged CTs + fi cat <>"$LXC_CONFIG" # USB passthrough lxc.cgroup2.devices.allow: a @@ -221,11 +222,13 @@ vaapi_select_and_apply() { done # Fallback only for privileged CTs - [[ "$CT_TYPE" == "0" ]] && cat <<'EOF' >>"$LXC_CONFIG" + if [[ "$CT_TYPE" == "0" ]]; then + cat <<'EOF' >>"$LXC_CONFIG" # VAAPI fallback lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir lxc.cgroup2.devices.allow: c 226:* rwm EOF + fi } # ----------------------------- NVIDIA ----------------------------------------- @@ -248,7 +251,9 @@ nvidia_passthrough_to_lxc() { return } - [[ -d /dev/dri && "$CT_TYPE" == "0" ]] && echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" + if [[ -d /dev/dri && "$CT_TYPE" == "0" ]]; then + echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" + fi msg_ok "NVIDIA devices mapped to CT ${CT_ID}" } From 66792ea9d7ac9a82e790bbd10b7a2595c98c9184 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 23 Sep 2025 14:05:23 +0200 Subject: [PATCH 1128/1733] Update build.func --- misc/build.func | 3 --- 1 file changed, 3 deletions(-) diff --git a/misc/build.func b/misc/build.func index 9f63fea5f..656432b02 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2216,9 +2216,6 @@ build_container() { LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/passthrough.func) - select_hw_passthrough "$CTID" "$CT_TYPE" "$APP" - # TUN device passthrough if [ "$ENABLE_TUN" == "yes" ]; then cat <>"$LXC_CONFIG" From 1d5601af6f18f3199d9aa5cf23022f959f0d0d69 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 23 Sep 2025 14:27:35 +0200 Subject: [PATCH 1129/1733] Add GPU and USB passthrough support to LXC build Introduces automatic detection and configuration of GPU (VAAPI/NVIDIA) and USB passthrough for LXC containers, including userland package installation and verification. Refactors and expands hardware passthrough logic, adds support for Coral TPU, and improves network and gateway checks. Removes deprecated storage menu and diagnostics code. --- misc/build.func | 489 +++++++++++++++++++++++++++++++----------------- 1 file changed, 313 insertions(+), 176 deletions(-) diff --git a/misc/build.func b/misc/build.func index 656432b02..b47aaa48b 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1697,111 +1697,6 @@ install_script() { esac } -# ------------------------------------------------------------------------------ -# check_storage_or_prompt() -# -# - Validates container/template storage -# - If invalid or missing, prompts user to select new storage -# - Updates vars file accordingly -# ------------------------------------------------------------------------------ -# check_storage_or_prompt() { -# local vars_file="$1" -# local changed=0 - -# if [ ! -f "$vars_file" ]; then -# msg_warn "No vars file found at $vars_file" -# return 0 -# fi - -# # Helper: validate storage ID -# _validate_storage() { -# local s="$1" -# [ -n "$s" ] || return 1 -# pvesm status -content images | awk 'NR>1 {print $1}' | grep -qx "$s" -# } - -# # Load current values (empty if not set) -# local ct_store tpl_store -# ct_store=$(awk -F= '/^var_container_storage=/ {print $2; exit}' "$vars_file") -# tpl_store=$(awk -F= '/^var_template_storage=/ {print $2; exit}' "$vars_file") - -# # Container storage -# if ! _validate_storage "$ct_store"; then -# local new_ct -# new_ct=$(pvesm status -content images | awk 'NR>1 {print $1" "$2" "$6}') -# new_ct=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "Select Container Storage" \ -# --menu "Choose container storage:" 20 60 10 $new_ct 3>&1 1>&2 2>&3) || return 0 -# if [ -n "$new_ct" ]; then -# sed -i "/^var_container_storage=/d" "$vars_file" -# echo "var_container_storage=$new_ct" >>"$vars_file" -# changed=1 -# msg_ok "Updated container storage in $vars_file → $new_ct" -# fi -# fi - -# # Template storage -# if ! _validate_storage "$tpl_store"; then -# local new_tpl -# new_tpl=$(pvesm status -content vztmpl | awk 'NR>1 {print $1" "$2" "$6}') -# new_tpl=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "Select Template Storage" \ -# --menu "Choose template storage:" 20 60 10 $new_tpl 3>&1 1>&2 2>&3) || return 0 -# if [ -n "$new_tpl" ]; then -# sed -i "/^var_template_storage=/d" "$vars_file" -# echo "var_template_storage=$new_tpl" >>"$vars_file" -# changed=1 -# msg_ok "Updated template storage in $vars_file → $new_tpl" -# fi -# fi - -# # Always succeed (no aborts from here) -# return 0 -# } - -# # ------------------------------------------------------------------------------ -# # storage_settings_menu() -# # -# # - Menu for managing storage defaults -# # - Options: update My Defaults or App Defaults storage -# # ------------------------------------------------------------------------------ -# storage_settings_menu() { -# local vars_file="/usr/local/community-scripts/default.vars" - -# check_storage_or_prompt "$vars_file" -# _echo_storage_summary "$vars_file" - -# # Always ask user if they want to update, even if values are valid -# if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "STORAGE SETTINGS" \ -# --yesno "Do you want to update the storage defaults?\n\nCurrent values will be kept unless you select new ones." 12 72; then - -# # container storage selection -# local new_ct -# new_ct=$(pvesm status -content images | awk 'NR>1 {print $1" "$2" "$6}') -# new_ct=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "Select Container Storage" \ -# --menu "Choose container storage:" 20 60 10 $new_ct 3>&1 1>&2 2>&3) || true -# if [ -n "$new_ct" ]; then -# sed -i '/^var_container_storage=/d' "$vars_file" -# echo "var_container_storage=$new_ct" >>"$vars_file" -# msg_ok "Updated container storage → $new_ct" -# fi - -# # template storage selection -# local new_tpl -# new_tpl=$(pvesm status -content vztmpl | awk 'NR>1 {print $1" "$2" "$6}') -# new_tpl=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "Select Template Storage" \ -# --menu "Choose template storage:" 20 60 10 $new_tpl 3>&1 1>&2 2>&3) || true -# if [ -n "$new_tpl" ]; then -# sed -i '/^var_template_storage=/d' "$vars_file" -# echo "var_template_storage=$new_tpl" >>"$vars_file" -# msg_ok "Updated template storage → $new_tpl" -# fi -# fi -# } - edit_default_storage() { local vf="/usr/local/community-scripts/default.vars" @@ -2167,10 +2062,6 @@ build_container() { FEATURES="$FEATURES,fuse=1" fi - #if [[ $DIAGNOSTICS == "yes" ]]; then - # post_to_api - #fi - TEMP_DIR=$(mktemp -d) pushd "$TEMP_DIR" >/dev/null if [ "$var_os" == "alpine" ]; then @@ -2183,7 +2074,6 @@ build_container() { export CACHER="$APT_CACHER" export CACHER_IP="$APT_CACHER_IP" export tz="$timezone" - #export DISABLEIPV6="$DISABLEIP6" export APPLICATION="$APP" export app="$NSAPP" export PASSWORD="$PW" @@ -2216,6 +2106,221 @@ build_container() { LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" + # ============================================================================ + # GPU/USB PASSTHROUGH CONFIGURATION + # ============================================================================ + + # List of applications that benefit from GPU acceleration + GPU_APPS=( + "immich" "channels" "emby" "ersatztv" "frigate" + "jellyfin" "plex" "scrypted" "tdarr" "unmanic" + "ollama" "fileflows" "open-webui" "tunarr" "debian" + "handbrake" "sunshine" "moonlight" "kodi" "stremio" + ) + + # ------------------------------------------------------------------------------ + # Helper Functions for GPU/USB Configuration + # ------------------------------------------------------------------------------ + + # Get device GID dynamically + get_device_gid() { + local group="$1" + local gid + gid=$(getent group "$group" 2>/dev/null | cut -d: -f3) + if [[ -z "$gid" ]]; then + case "$group" in + video) gid=44 ;; + render) gid=104 ;; + *) gid=44 ;; + esac + fi + echo "$gid" + } + + # Check if app needs GPU + is_gpu_app() { + local app="${1,,}" + for gpu_app in "${GPU_APPS[@]}"; do + [[ "$app" == "${gpu_app,,}" ]] && return 0 + done + return 1 + } + + # Detect available GPU devices + detect_gpu_devices() { + VAAPI_DEVICES=() + NVIDIA_DEVICES=() + + # Detect VAAPI devices (Intel/AMD) + if [[ -d /dev/dri ]]; then + for device in /dev/dri/renderD* /dev/dri/card*; do + [[ -e "$device" ]] && VAAPI_DEVICES+=("$device") + done + fi + + # Detect NVIDIA devices + for device in /dev/nvidia*; do + [[ -e "$device" ]] && NVIDIA_DEVICES+=("$device") + done + } + + # Configure USB passthrough for privileged containers + configure_usb_passthrough() { + if [[ "$CT_TYPE" != "0" ]]; then + return 0 + fi + + msg_info "Configuring automatic USB passthrough (privileged container)" + cat <>"$LXC_CONFIG" +# USB passthrough (privileged container) +lxc.cgroup2.devices.allow: a +lxc.cap.drop: +lxc.cgroup2.devices.allow: c 188:* rwm +lxc.cgroup2.devices.allow: c 189:* rwm +lxc.mount.entry: /dev/serial/by-id dev/serial/by-id none bind,optional,create=dir +lxc.mount.entry: /dev/ttyUSB0 dev/ttyUSB0 none bind,optional,create=file +lxc.mount.entry: /dev/ttyUSB1 dev/ttyUSB1 none bind,optional,create=file +lxc.mount.entry: /dev/ttyACM0 dev/ttyACM0 none bind,optional,create=file +lxc.mount.entry: /dev/ttyACM1 dev/ttyACM1 none bind,optional,create=file +EOF + msg_ok "USB passthrough configured" + } + + # Configure VAAPI device + configure_vaapi_device() { + local device="$1" + local dev_index="$2" + + if [[ "$CT_TYPE" == "0" ]]; then + # Privileged container + local major minor + major=$(stat -c '%t' "$device") + minor=$(stat -c '%T' "$device") + major=$((0x$major)) + minor=$((0x$minor)) + echo "lxc.cgroup2.devices.allow: c $major:$minor rwm" >>"$LXC_CONFIG" + echo "lxc.mount.entry: $device dev/$(basename "$device") none bind,optional,create=file" >>"$LXC_CONFIG" + else + # Unprivileged container + local gid + if [[ "$device" =~ renderD ]]; then + gid=$(get_device_gid "render") + else + gid=$(get_device_gid "video") + fi + echo "dev${dev_index}: $device,gid=$gid" >>"$LXC_CONFIG" + fi + } + + # Configure NVIDIA devices + configure_nvidia_devices() { + for device in "${NVIDIA_DEVICES[@]}"; do + if [[ "$CT_TYPE" == "0" ]]; then + local major minor + major=$(stat -c '%t' "$device") + minor=$(stat -c '%T' "$device") + major=$((0x$major)) + minor=$((0x$minor)) + echo "lxc.cgroup2.devices.allow: c $major:$minor rwm" >>"$LXC_CONFIG" + echo "lxc.mount.entry: $device dev/$(basename "$device") none bind,optional,create=file" >>"$LXC_CONFIG" + else + msg_warn "NVIDIA passthrough to unprivileged container may require additional configuration" + fi + done + + if [[ -d /dev/dri ]] && [[ "$CT_TYPE" == "0" ]]; then + echo "lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" + fi + } + + # Main GPU configuration logic + configure_gpu_passthrough() { + detect_gpu_devices + + # Check if we should configure GPU + local should_configure=false + if [[ "$CT_TYPE" == "0" ]] || is_gpu_app "$APP"; then + should_configure=true + fi + + if [[ "$should_configure" == "false" ]]; then + return 0 + fi + + # No GPU devices available + if [[ ${#VAAPI_DEVICES[@]} -eq 0 ]] && [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then + msg_info "No GPU devices detected on host" + return 0 + fi + + # Build selection options + local choices=() + local SELECTED_GPUS=() + + if [[ ${#VAAPI_DEVICES[@]} -gt 0 ]]; then + choices+=("VAAPI" "Intel/AMD GPU (${#VAAPI_DEVICES[@]} devices)" "OFF") + fi + + if [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then + choices+=("NVIDIA" "NVIDIA GPU (${#NVIDIA_DEVICES[@]} devices)" "OFF") + fi + + # Auto-select if only one type available + if [[ ${#choices[@]} -eq 3 ]]; then + SELECTED_GPUS=("${choices[0]}") + msg_info "Auto-configuring ${choices[0]} GPU passthrough" + elif [[ ${#choices[@]} -gt 3 ]]; then + # Show selection dialog + local selected + selected=$(whiptail --title "GPU Passthrough Selection" \ + --checklist "Multiple GPU types detected. Select which to pass through:" \ + 12 60 $((${#choices[@]} / 3)) \ + "${choices[@]}" 3>&1 1>&2 2>&3) || { + msg_info "GPU passthrough skipped" + return 0 + } + + for item in $selected; do + SELECTED_GPUS+=("${item//\"/}") + done + fi + + # Apply configuration for selected GPUs + local dev_index=0 + for gpu_type in "${SELECTED_GPUS[@]}"; do + case "$gpu_type" in + VAAPI) + msg_info "Configuring VAAPI passthrough (${#VAAPI_DEVICES[@]} devices)" + for device in "${VAAPI_DEVICES[@]}"; do + configure_vaapi_device "$device" "$dev_index" + ((dev_index++)) + done + if [[ "$CT_TYPE" == "0" ]] && [[ -d /dev/dri ]]; then + echo "lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" + fi + export ENABLE_VAAPI=1 + ;; + NVIDIA) + msg_info "Configuring NVIDIA passthrough" + configure_nvidia_devices + export ENABLE_NVIDIA=1 + ;; + esac + done + + [[ ${#SELECTED_GPUS[@]} -gt 0 ]] && msg_ok "GPU passthrough configured" + } + + # ------------------------------------------------------------------------------ + # Apply all hardware passthrough configurations + # ------------------------------------------------------------------------------ + + # USB passthrough (automatic for privileged) + configure_usb_passthrough + + # GPU passthrough (based on container type and app) + configure_gpu_passthrough + # TUN device passthrough if [ "$ENABLE_TUN" == "yes" ]; then cat <>"$LXC_CONFIG" @@ -2224,11 +2329,20 @@ lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file EOF fi - # This starts the container and executes -install.sh + # Coral TPU passthrough (if available) + if [[ -e /dev/apex_0 ]]; then + msg_info "Detected Coral TPU - configuring passthrough" + echo "lxc.mount.entry: /dev/apex_0 dev/apex_0 none bind,optional,create=file" >>"$LXC_CONFIG" + fi + + # ============================================================================ + # START CONTAINER AND INSTALL USERLAND + # ============================================================================ + msg_info "Starting LXC Container" pct start "$CTID" - # wait for status 'running' + # Wait for container to be running for i in {1..10}; do if pct status "$CTID" | grep -q "status: running"; then msg_ok "Started LXC Container" @@ -2241,10 +2355,11 @@ EOF fi done + # Wait for network (skip for Alpine initially) if [ "$var_os" != "alpine" ]; then msg_info "Waiting for network in LXC container" - # --- Step 1: Wait for IP --- + # Wait for IP for i in {1..20}; do ip_in_lxc=$(pct exec "$CTID" -- ip -4 addr show dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) [ -n "$ip_in_lxc" ] && break @@ -2256,10 +2371,10 @@ EOF exit 1 fi - # --- Step 2: Try to reach gateway --- + # Try to reach gateway gw_ok=0 for i in {1..10}; do - if pct exec "$CTID" -- ping -c1 -W1 "$GATEWAY" >/dev/null 2>&1; then + if pct exec "$CTID" -- ping -c1 -W1 "${GATEWAY:-8.8.8.8}" >/dev/null 2>&1; then gw_ok=1 break fi @@ -2267,80 +2382,85 @@ EOF done if [ "$gw_ok" -eq 1 ]; then - msg_ok "CT $CTID gateway $GATEWAY reachable (IP $ip_in_lxc)" + msg_ok "Network in LXC is reachable (IP $ip_in_lxc)" else - msg_warn "CT $CTID has IP $ip_in_lxc but gateway $GATEWAY did not reply" - fi - - # --- Step 3: DNS / Internet check --- - if pct exec "$CTID" -- getent hosts deb.debian.org >/dev/null 2>&1; then - msg_ok "Network in LXC is reachable (DNS OK, IP $ip_in_lxc)" - else - msg_warn "Network reachable (IP $ip_in_lxc), but DNS failed – applying fallback resolv.conf" - pct exec "$CTID" -- bash -c 'echo "nameserver 1.1.1.1" > /etc/resolv.conf && echo "nameserver 8.8.8.8" >> /etc/resolv.conf' - if pct exec "$CTID" -- getent hosts deb.debian.org >/dev/null 2>&1; then - msg_ok "Network reachable after DNS fallback" - else - msg_error "Still no DNS/network in LXC! Aborting customization." - exit_script - fi + msg_warn "Network reachable but gateway check failed" fi fi - gpu_inside_setup() { - local CTID="$1" + # Install GPU userland packages + install_gpu_userland() { + local gpu_type="$1" - # VAAPI inside (Debian/Ubuntu) - if [[ "${ENABLE_VAAPI:-0}" -eq 1 ]]; then - msg_info "Installing VAAPI userland inside CT ${CTID}" - pct exec "$CTID" -- bash -lc ' - set -e - . /etc/os-release || true - if [[ "${VERSION_CODENAME:-}" == "trixie" ]]; then - cat >/etc/apt/sources.list.d/non-free.sources </dev/null 2>&1 + ' || msg_warn "Some VAAPI packages may not be available in Alpine" + ;; + NVIDIA) + msg_warn "NVIDIA drivers are not readily available in Alpine Linux" + ;; + esac + else + case "$gpu_type" in + VAAPI) + msg_info "Installing VAAPI userland packages" + pct exec "$CTID" -- bash -c ' + . /etc/os-release || true + if [[ "${VERSION_CODENAME:-}" == "trixie" ]]; then + cat >/etc/apt/sources.list.d/non-free.sources </dev/null 2>&1 + DEBIAN_FRONTEND=noninteractive apt-get install -y \ + intel-media-va-driver-non-free \ + mesa-va-drivers \ + libvpl2 \ + vainfo \ + ocl-icd-libopencl1 \ + mesa-opencl-icd \ + intel-gpu-tools >/dev/null 2>&1 + ' && msg_ok "VAAPI userland installed" || msg_warn "Some VAAPI packages failed to install" + ;; + NVIDIA) + msg_info "Installing NVIDIA userland packages" + pct exec "$CTID" -- bash -c ' + apt-get update >/dev/null 2>&1 + DEBIAN_FRONTEND=noninteractive apt-get install -y \ + nvidia-driver \ + nvidia-utils \ + libnvidia-encode1 \ + libcuda1 >/dev/null 2>&1 + ' && msg_ok "NVIDIA userland installed" || msg_warn "Some NVIDIA packages failed to install" + ;; + esac fi } + # Customize container msg_info "Customizing LXC Container" - gpu_inside_setup "$CTID" + + # Install GPU userland if configured + if [[ "${ENABLE_VAAPI:-0}" == "1" ]]; then + install_gpu_userland "VAAPI" + fi + + if [[ "${ENABLE_NVIDIA:-0}" == "1" ]]; then + install_gpu_userland "NVIDIA" + fi + + # Continue with standard container setup if [ "$var_os" == "alpine" ]; then sleep 3 pct exec "$CTID" -- /bin/sh -c 'cat </etc/apk/repositories @@ -2350,7 +2470,6 @@ EOF' pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses jq >/dev/null" else sleep 3 - pct exec "$CTID" -- bash -c "sed -i '/$LANG/ s/^# //' /etc/locale.gen" pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \ echo LANG=\$locale_line >/etc/default/locale && \ @@ -2372,8 +2491,26 @@ EOF' exit 1 } fi + msg_ok "Customized LXC Container" + + # Verify GPU access if enabled + if [[ "${ENABLE_VAAPI:-0}" == "1" ]] && [ "$var_os" != "alpine" ]; then + pct exec "$CTID" -- bash -c "vainfo >/dev/null 2>&1" && + msg_ok "VAAPI verified working" || + msg_warn "VAAPI verification failed - may need additional configuration" + fi + + if [[ "${ENABLE_NVIDIA:-0}" == "1" ]] && [ "$var_os" != "alpine" ]; then + pct exec "$CTID" -- bash -c "nvidia-smi >/dev/null 2>&1" && + msg_ok "NVIDIA verified working" || + msg_warn "NVIDIA verification failed - may need additional configuration" + fi + + # Install SSH keys install_ssh_keys_into_ct + + # Run application installer if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)"; then exit $? fi From f333fc5db7cc31b82cdcc90444bbebb9b38f84e8 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 23 Sep 2025 14:29:44 +0200 Subject: [PATCH 1130/1733] Improve shell detection in shell_check function Enhanced the shell_check function to handle both 'bash' and '-bash' as valid shells, added a debug message to display the detected shell, and changed the exit code to 1 for clearer error signaling. --- misc/core.func | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/misc/core.func b/misc/core.func index fde0281fc..c74d244f6 100644 --- a/misc/core.func +++ b/misc/core.func @@ -152,14 +152,19 @@ silent() { fi } +# Check if the shell is using bash # Check if the shell is using bash shell_check() { - if [[ "$(ps -p $$ -o comm=)" != "bash" ]]; then + local CURRENT_SHELL + CURRENT_SHELL="$(ps -p $$ -o comm=)" + echo "DEBUG: Detected shell is: '$CURRENT_SHELL'" + + if [[ "$CURRENT_SHELL" != "bash" && "$CURRENT_SHELL" != "-bash" ]]; then clear msg_error "Your default shell is currently not set to Bash. To use these scripts, please switch to the Bash shell." echo -e "\nExiting..." sleep 2 - exit + exit 1 fi } From 435890eee2a200a31f115e49ad6f3d931cb7ec28 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 23 Sep 2025 14:32:19 +0200 Subject: [PATCH 1131/1733] Update core.func --- misc/core.func | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/misc/core.func b/misc/core.func index c74d244f6..00c2d6e6f 100644 --- a/misc/core.func +++ b/misc/core.func @@ -152,16 +152,15 @@ silent() { fi } -# Check if the shell is using bash # Check if the shell is using bash shell_check() { local CURRENT_SHELL - CURRENT_SHELL="$(ps -p $$ -o comm=)" + CURRENT_SHELL="$(ps -p $$ -o comm= | xargs)" # trims whitespace echo "DEBUG: Detected shell is: '$CURRENT_SHELL'" - if [[ "$CURRENT_SHELL" != "bash" && "$CURRENT_SHELL" != "-bash" ]]; then + if [[ -z "$BASH_VERSION" ]]; then clear - msg_error "Your default shell is currently not set to Bash. To use these scripts, please switch to the Bash shell." + msg_error "This script requires bash, but current shell is: $CURRENT_SHELL" echo -e "\nExiting..." sleep 2 exit 1 From d0f6818386d6f93bf9777ab4b625918c7e101369 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 23 Sep 2025 14:39:39 +0200 Subject: [PATCH 1132/1733] Move and improve ssh_check and shell_check functions The ssh_check function was moved from build.func to core.func and enhanced to allow an override and to skip warnings for localhost connections. The shell_check function was simplified to directly check for Bash and provide clearer messaging. These changes centralize environment checks and improve user experience. --- misc/build.func | 19 ------------------- misc/core.func | 43 ++++++++++++++++++++++++++++++++++++------- 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/misc/build.func b/misc/build.func index b47aaa48b..fa1321485 100644 --- a/misc/build.func +++ b/misc/build.func @@ -239,25 +239,6 @@ update_motd_ip() { fi } -# ------------------------------------------------------------------------------ -# ssh_check() -# -# - Detects if script is running over SSH -# - Warns user and recommends using Proxmox shell -# - User can choose to continue or abort -# ------------------------------------------------------------------------------ -ssh_check() { - if [ -n "${SSH_CLIENT:+x}" ]; then - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's advisable to utilize the Proxmox shell rather than SSH, as there may be potential complications with variable retrieval. Proceed using SSH?" 10 72; then - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox --title "Proceed using SSH" "You've chosen to proceed using SSH. If any issues arise, please run the script in the Proxmox shell before creating a repository issue." 10 72 - else - clear - echo "Exiting due to SSH usage. Please consider using the Proxmox shell." - exit - fi - fi -} - # ------------------------------------------------------------------------------ # install_ssh_keys_into_ct() # diff --git a/misc/core.func b/misc/core.func index 00c2d6e6f..9d6e156b8 100644 --- a/misc/core.func +++ b/misc/core.func @@ -154,16 +154,12 @@ silent() { # Check if the shell is using bash shell_check() { - local CURRENT_SHELL - CURRENT_SHELL="$(ps -p $$ -o comm= | xargs)" # trims whitespace - echo "DEBUG: Detected shell is: '$CURRENT_SHELL'" - - if [[ -z "$BASH_VERSION" ]]; then + if [[ "$(ps -p $$ -o comm=)" != "bash" ]]; then clear - msg_error "This script requires bash, but current shell is: $CURRENT_SHELL" + msg_error "Your default shell is currently not set to Bash. To use these scripts, please switch to the Bash shell." echo -e "\nExiting..." sleep 2 - exit 1 + exit fi } @@ -223,6 +219,39 @@ arch_check() { fi } +# ------------------------------------------------------------------------------ +# ssh_check() +# +# - Detects if script is running over SSH +# - Warns user and recommends using Proxmox shell +# - User can choose to continue or abort +# ------------------------------------------------------------------------------ +ssh_check() { + # Skip if override + if [[ "${ALLOW_SSH:-0}" -eq 1 ]]; then return; fi + + if [ -n "$SSH_CLIENT" ]; then + local client_ip server_port + client_ip=$(awk '{print $1}' <<<"$SSH_CLIENT") + server_port=$(awk '{print $3}' <<<"$SSH_CLIENT") + + # Wenn client_ip = localhost oder host-IP → kein Warnpopup + if [[ "$client_ip" == "127.0.0.1" || "$client_ip" == "$(hostname -I | awk '{print $1}')" ]]; then + return + fi + + # Andernfalls Popup + if ! whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --defaultno \ + --title "SSH DETECTED" \ + --yesno "It's advisable to utilize the Proxmox shell rather than SSH..." 10 72; then + clear + echo "Exiting due to SSH usage." + exit 1 + fi + fi +} + # Function to download & save header files get_header() { local app_name=$(echo "${APP,,}" | tr -d ' ') From e5e7c5fa9f32c7a1fd7a53f23ffcb7bdd5eedb86 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 23 Sep 2025 14:41:58 +0200 Subject: [PATCH 1133/1733] Update core.func --- misc/core.func | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/misc/core.func b/misc/core.func index 9d6e156b8..19e3c9770 100644 --- a/misc/core.func +++ b/misc/core.func @@ -227,28 +227,15 @@ arch_check() { # - User can choose to continue or abort # ------------------------------------------------------------------------------ ssh_check() { - # Skip if override - if [[ "${ALLOW_SSH:-0}" -eq 1 ]]; then return; fi - if [ -n "$SSH_CLIENT" ]; then - local client_ip server_port - client_ip=$(awk '{print $1}' <<<"$SSH_CLIENT") - server_port=$(awk '{print $3}' <<<"$SSH_CLIENT") + local client_ip=$(awk '{print $1}' <<<"$SSH_CLIENT") + local host_ip=$(hostname -I | awk '{print $1}') - # Wenn client_ip = localhost oder host-IP → kein Warnpopup - if [[ "$client_ip" == "127.0.0.1" || "$client_ip" == "$(hostname -I | awk '{print $1}')" ]]; then - return + if [[ "$client_ip" == "127.0.0.1" || "$client_ip" == "$host_ip" ]]; then + return # WebUI-Shell → kein Hinweis fi - # Andernfalls Popup - if ! whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --defaultno \ - --title "SSH DETECTED" \ - --yesno "It's advisable to utilize the Proxmox shell rather than SSH..." 10 72; then - clear - echo "Exiting due to SSH usage." - exit 1 - fi + msg_warn "Running via external SSH (client: $client_ip)." fi } From 6e43ed60eb1e6d689c3e50c3763705f67d410385 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 23 Sep 2025 14:42:48 +0200 Subject: [PATCH 1134/1733] Update core.func --- misc/core.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/core.func b/misc/core.func index 19e3c9770..c2c1ea0c6 100644 --- a/misc/core.func +++ b/misc/core.func @@ -232,7 +232,7 @@ ssh_check() { local host_ip=$(hostname -I | awk '{print $1}') if [[ "$client_ip" == "127.0.0.1" || "$client_ip" == "$host_ip" ]]; then - return # WebUI-Shell → kein Hinweis + return fi msg_warn "Running via external SSH (client: $client_ip)." From 8f98298b68f0e448431d10eb836d964e92142a01 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner <73236783+michelroegl-brunner@users.noreply.github.com> Date: Tue, 23 Sep 2025 14:50:22 +0200 Subject: [PATCH 1135/1733] Clean up diagnostics comments in build.func Removed commented lines related to diagnostics information. --- misc/build.func | 4 ---- 1 file changed, 4 deletions(-) diff --git a/misc/build.func b/misc/build.func index fa1321485..e854b8938 100644 --- a/misc/build.func +++ b/misc/build.func @@ -980,13 +980,11 @@ DIAGNOSTICS=yes #To send diagnostics, set the variable 'DIAGNOSTICS' to "yes" in /usr/local/community-scripts/diagnostics, or use the menue. #This will enable the diagnostics feature. #The following information will be sent: -#"ct_type" #"disk_size" #"core_count" #"ram_size" #"os_type" #"os_version" -#"disableip6" #"nsapp" #"method" #"pve_version" @@ -1007,13 +1005,11 @@ DIAGNOSTICS=no #To send diagnostics, set the variable 'DIAGNOSTICS' to "yes" in /usr/local/community-scripts/diagnostics, or use the menue. #This will enable the diagnostics feature. #The following information will be sent: -#"ct_type" #"disk_size" #"core_count" #"ram_size" #"os_type" #"os_version" -#"disableip6" #"nsapp" #"method" #"pve_version" From 9d39569bdae4a250f3d05107d1b821598fb62d87 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 23 Sep 2025 14:53:26 +0200 Subject: [PATCH 1136/1733] Refactor device detection in build_container function Replaces glob loops with compgen for detecting VAAPI and NVIDIA devices in /dev. This improves robustness and avoids issues when no matching devices are present. --- misc/build.func | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/misc/build.func b/misc/build.func index e854b8938..ed6da7a4d 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2128,16 +2128,15 @@ build_container() { VAAPI_DEVICES=() NVIDIA_DEVICES=() - # Detect VAAPI devices (Intel/AMD) - if [[ -d /dev/dri ]]; then - for device in /dev/dri/renderD* /dev/dri/card*; do - [[ -e "$device" ]] && VAAPI_DEVICES+=("$device") - done - fi + for device in $(compgen -G "/dev/dri/renderD*" || true); do + VAAPI_DEVICES+=("$device") + done + for device in $(compgen -G "/dev/dri/card*" || true); do + VAAPI_DEVICES+=("$device") + done - # Detect NVIDIA devices - for device in /dev/nvidia*; do - [[ -e "$device" ]] && NVIDIA_DEVICES+=("$device") + for device in $(compgen -G "/dev/nvidia*" || true); do + NVIDIA_DEVICES+=("$device") done } From 6b40cf6feb37cf6c885b084cae68aded548eed64 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 23 Sep 2025 15:00:41 +0200 Subject: [PATCH 1137/1733] Update build.func --- misc/build.func | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/misc/build.func b/misc/build.func index ed6da7a4d..e973f9b23 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2263,6 +2263,10 @@ EOF # Apply configuration for selected GPUs local dev_index=0 + for device in "${VAAPI_DEVICES[@]}"; do + configure_vaapi_device "$device" "$dev_index" + ((dev_index++)) + done for gpu_type in "${SELECTED_GPUS[@]}"; do case "$gpu_type" in VAAPI) From 22118056caa487008bb8f0fcbc3db05b7de9b051 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 23 Sep 2025 15:19:28 +0200 Subject: [PATCH 1138/1733] Update build.func --- misc/build.func | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/misc/build.func b/misc/build.func index e973f9b23..60783cfe1 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2263,10 +2263,6 @@ EOF # Apply configuration for selected GPUs local dev_index=0 - for device in "${VAAPI_DEVICES[@]}"; do - configure_vaapi_device "$device" "$dev_index" - ((dev_index++)) - done for gpu_type in "${SELECTED_GPUS[@]}"; do case "$gpu_type" in VAAPI) @@ -2289,6 +2285,7 @@ EOF done [[ ${#SELECTED_GPUS[@]} -gt 0 ]] && msg_ok "GPU passthrough configured" + } # ------------------------------------------------------------------------------ From 3a49db2b9ad1384dd016d96e859961a0a37cb85c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 23 Sep 2025 15:23:34 +0200 Subject: [PATCH 1139/1733] Update build.func --- misc/build.func | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index 60783cfe1..0610b13b1 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2269,7 +2269,8 @@ EOF msg_info "Configuring VAAPI passthrough (${#VAAPI_DEVICES[@]} devices)" for device in "${VAAPI_DEVICES[@]}"; do configure_vaapi_device "$device" "$dev_index" - ((dev_index++)) + : "${dev_index:=0}" + dev_index=$((dev_index + 1)) done if [[ "$CT_TYPE" == "0" ]] && [[ -d /dev/dri ]]; then echo "lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" From 922ec615c0918ebd59b94a8a374e6ca37e623c77 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 23 Sep 2025 15:28:05 +0200 Subject: [PATCH 1140/1733] Update debian-install.sh --- install/debian-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/debian-install.sh b/install/debian-install.sh index 299e40028..5f83d54af 100644 --- a/install/debian-install.sh +++ b/install/debian-install.sh @@ -17,7 +17,7 @@ msg_info "Installing Dependencies" $STD apt-get install -y gpg msg_ok "Installed Dependencies" -install_vaapi_userland_interactive +#install_vaapi_userland_interactive #setup_mariadb From 1a6fbdba5b118690c17c29ded9c709dc190eb7d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20B=C3=A9dard-Couture?= Date: Tue, 23 Sep 2025 20:56:15 -0400 Subject: [PATCH 1141/1733] New script version --- ct/frigate.sh | 2 +- install/frigate-install.sh | 352 +++++++++++++++++++++---------------- 2 files changed, 206 insertions(+), 148 deletions(-) diff --git a/ct/frigate.sh b/ct/frigate.sh index 479c6b98d..9dd49dc19 100644 --- a/ct/frigate.sh +++ b/ct/frigate.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG -# Authors: MickLesk (CanbiZ) +# Authors: MickLesk (CanbiZ) | Co-Author: remz1337 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://frigate.video/ diff --git a/install/frigate-install.sh b/install/frigate-install.sh index 018261a1d..7f40bc750 100644 --- a/install/frigate-install.sh +++ b/install/frigate-install.sh @@ -2,6 +2,7 @@ # Copyright (c) 2021-2025 community-scripts ORG # Authors: MickLesk (CanbiZ) +# Co-Authors: remz1337 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://frigate.video/ @@ -14,57 +15,187 @@ network_check update_os msg_info "Installing Dependencies (Patience)" -$STD apt-get install -y {git,ca-certificates,automake,build-essential,xz-utils,libtool,ccache,pkg-config,libgtk-3-dev,libavcodec-dev,libavformat-dev,libswscale-dev,libv4l-dev,libxvidcore-dev,libx264-dev,libjpeg-dev,libpng-dev,libtiff-dev,gfortran,openexr,libatlas-base-dev,libssl-dev,libtbb-dev,libdc1394-dev,libopenexr-dev,libgstreamer-plugins-base1.0-dev,libgstreamer1.0-dev,gcc,gfortran,libopenblas-dev,liblapack-dev,libusb-1.0-0-dev,jq,moreutils} +$STD apt-get install -y {jq,wget,xz-utils,python3,python3-dev,python3-distutils,gcc,pkg-config,libhdf5-dev,unzip,build-essential,automake,libtool,ccache,libusb-1.0-0-dev,apt-transport-https,python3.11,python3.11-dev,cmake,git,libgtk-3-dev,libavcodec-dev,libavformat-dev,libswscale-dev,libv4l-dev,libxvidcore-dev,libx264-dev,libjpeg-dev,libpng-dev,libtiff-dev,gfortran,openexr,libatlas-base-dev,libssl-dev,libtbbmalloc2,libtbb-dev,libdc1394-dev,libopenexr-dev,libgstreamer-plugins-base1.0-dev,libgstreamer1.0-dev,tclsh,libopenblas-dev,liblapack-dev,make,moreutils} msg_ok "Installed Dependencies" -msg_info "Setup Python3" -$STD apt-get install -y {python3,python3-dev,python3-setuptools,python3-distutils,python3-pip,python3-venv} -$STD pip install --upgrade pip -msg_ok "Setup Python3" +msg_info "Setting Up Hardware Acceleration" +$STD apt-get -y install {va-driver-all,ocl-icd-libopencl1,intel-opencl-icd,vainfo,intel-gpu-tools} +if [[ "$CTTYPE" == "0" ]]; then + chgrp video /dev/dri + chmod 755 /dev/dri + chmod 660 /dev/dri/* + sed -i -e 's/^kvm:x:104:$/render:x:104:root,frigate/' -e 's/^render:x:105:root$/kvm:x:105:/' /etc/group +else + sed -i -e 's/^kvm:x:104:$/render:x:104:frigate/' -e 's/^render:x:105:$/kvm:x:105:/' /etc/group +fi +msg_ok "Set Up Hardware Acceleration" -NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs -fetch_and_deploy_gh_release "go2rtc" "AlexxIT/go2rtc" "singlefile" "latest" "/usr/local/go2rtc/bin" "go2rtc_linux_amd64" -fetch_and_deploy_gh_release "frigate" "blakeblackshear/frigate" "tarball" "latest" "/opt/frigate" -fetch_and_deploy_gh_release "libusb" "libusb/libusb" "tarball" "v1.0.29" "/opt/frigate/libusb" - -# msg_info "Setting Up Hardware Acceleration" -# $STD apt-get -y install {va-driver-all,ocl-icd-libopencl1,intel-opencl-icd,vainfo,intel-gpu-tools} -# if [[ "$CTTYPE" == "0" ]]; then -# chgrp video /dev/dri -# chmod 755 /dev/dri -# chmod 660 /dev/dri/* -# fi -# msg_ok "Set Up Hardware Acceleration" - -msg_info "Setting up Python" -cd /opt/frigate -mkdir -p /opt/frigate/models -$STD pip3 wheel --wheel-dir=/wheels -r /opt/frigate/docker/main/requirements-wheels.txt -cp -a /opt/frigate/docker/main/rootfs/. / +msg_info "Setting up environment" +#cd ~ && echo "export PATH=$PATH:/usr/local/bin" >> .bashrc +#source .bashrc export TARGETARCH="amd64" -echo 'libc6 libraries/restart-without-asking boolean true' | debconf-set-selections -$STD apt update -$STD ln -svf /usr/lib/btbn-ffmpeg/bin/ffmpeg /usr/local/bin/ffmpeg -$STD ln -svf /usr/lib/btbn-ffmpeg/bin/ffprobe /usr/local/bin/ffprobe +export CCACHE_DIR=/root/.ccache +export CCACHE_MAXSIZE=2G +# http://stackoverflow.com/questions/48162574/ddg#49462622 +export APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn +# https://askubuntu.com/questions/972516/debian-frontend-environment-variable +export DEBIAN_FRONTEND=noninteractive +# Globally set pip break-system-packages option to avoid having to specify it every time +export PIP_BREAK_SYSTEM_PACKAGES=1 +# https://github.com/NVIDIA/nvidia-docker/wiki/Installation-(Native-GPU-Support) +export NVIDIA_VISIBLE_DEVICES=all +export NVIDIA_DRIVER_CAPABILITIES="compute,video,utility" +# Disable tokenizer parallelism warning +# https://stackoverflow.com/questions/62691279/how-to-disable-tokenizers-parallelism-true-false-warning/72926996#729> +export TOKENIZERS_PARALLELISM=true +# https://github.com/huggingface/transformers/issues/27214 +export TRANSFORMERS_NO_ADVISORY_WARNINGS=1 +# Set OpenCV ffmpeg loglevel to fatal: https://ffmpeg.org/doxygen/trunk/log_8h.html +export OPENCV_FFMPEG_LOGLEVEL=8 +# Set HailoRT to disable logging +export HAILORT_LOGGER_PATH=NONE +msg_ok "Setup environment" + +msg_info "Downloading Frigate source" +fetch_and_deploy_gh_release "frigate" "blakeblackshear/frigate" "tarball" "latest" "/opt/frigate" +msg_ok "Downloaded Frigate source" + +msg_info "Building Nginx with Custom Modules" +#Overwrite version check as debian 12 LXC doesn't have the debian.list file for some reason +sed -i 's|if.*"$VERSION_ID" == "12".*|if \[\[ "$VERSION_ID" == "12" \]\] \&\ \[\[ -f /etc/apt/sources.list.d/debian.list \]\]; then|g' /opt/frigate/docker/main/build_nginx.sh +$STD bash /opt/frigate/docker/main/build_nginx.sh +sed -e '/s6-notifyoncheck/ s/^#*/#/' -i /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/run +ln -sf /usr/local/nginx/sbin/nginx /usr/local/bin/nginx +msg_ok "Built Nginx" + +msg_info "Building SQLite with Custom Modules" +sed -i 's|if.*"$VERSION_ID" == "12".*|if \[\[ "$VERSION_ID" == "12" \]\] \&\ \[\[ -f /etc/apt/sources.list.d/debian.list \]\]; then|g' /opt/frigate/docker/main/build_sqlite_vec.sh +$STD bash /opt/frigate/docker/main/build_sqlite_vec.sh +msg_ok "Built SQLite" + +msg_info "Installing go2rtc" +fetch_and_deploy_gh_release "go2rtc" "AlexxIT/go2rtc" "singlefile" "latest" "/usr/local/go2rtc/bin" "go2rtc_linux_amd64" +msg_ok "Installed go2rtc" + +msg_info "Installing Tempio" +sed -i 's|/rootfs/usr/local|/usr/local|g' /opt/frigate/docker/main/install_tempio.sh +$STD bash /opt/frigate/docker/main/install_tempio.sh +ln -sf /usr/local/tempio/bin/tempio /usr/local/bin/tempio +msg_ok "Installed Tempio" + +msg_info "Building libUSB without udev" +cd /opt +wget -q https://github.com/libusb/libusb/archive/v1.0.26.zip -O v1.0.26.zip +$STD unzip v1.0.26.zip +cd libusb-1.0.26 +$STD ./bootstrap.sh +$STD ./configure CC='ccache gcc' CCX='ccache g++' --disable-udev --enable-shared +$STD make -j $(nproc --all) +cd /opt/libusb-1.0.26/libusb +mkdir -p '/usr/local/lib' +$STD bash ../libtool --mode=install /usr/bin/install -c libusb-1.0.la '/usr/local/lib' +mkdir -p '/usr/local/include/libusb-1.0' +$STD install -c -m 644 libusb.h '/usr/local/include/libusb-1.0' +mkdir -p '/usr/local/lib/pkgconfig' +cd /opt/libusb-1.0.26/ +$STD install -c -m 644 libusb-1.0.pc '/usr/local/lib/pkgconfig' +ldconfig +msg_ok "Built libUSB" + +msg_info "Installing Pip" +wget -q https://bootstrap.pypa.io/get-pip.py -O get-pip.py +sed -i 's/args.append("setuptools")/args.append("setuptools==77.0.3")/' get-pip.py +$STD python3 get-pip.py "pip" +msg_ok "Installed Pip" + +msg_info "Installing Frigate Dependencies" +$STD update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.11 1 +$STD pip3 install -r /opt/frigate/docker/main/requirements.txt +msg_ok "Installed Frigate Dependencies" + +msg_info "Building pysqlite3" +sed -i 's|^SQLITE3_VERSION=.*|SQLITE3_VERSION="version-3.46.0"|g' /opt/frigate/docker/main/build_pysqlite3.sh +$STD bash /opt/frigate/docker/main/build_pysqlite3.sh +$STD pip3 wheel --wheel-dir=/wheels -r /opt/frigate/docker/main/requirements-wheels.txt +msg_ok "Built pysqlite3" + +msg_info "Installing NodeJS" +NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs +msg_ok "Installed NodeJS" + +# This should be moved to conditional block, only needed if Coral TPU is detected +msg_info "Downloading Coral TPU Model" +cd / +wget -qO edgetpu_model.tflite https://github.com/google-coral/test_data/raw/release-frogfish/ssdlite_mobiledet_coco_qat_postprocess_edgetpu.tflite +msg_ok "Downloaded Coral TPU Model" + +msg_info "Downloading CPU Model" +mkdir -p /models +cd /models +wget -qO cpu_model.tflite https://github.com/google-coral/test_data/raw/release-frogfish/ssdlite_mobiledet_coco_qat_postprocess.tflite +cp /opt/frigate/labelmap.txt /labelmap.txt +msg_ok "Downloaded CPU Model" + +msg_info "Building Audio Models" +# Get Audio Model and labels +wget -qO yamnet-tflite-classification-tflite-v1.tar.gz https://www.kaggle.com/api/v1/models/google/yamnet/tfLite/classification-tflite/1/download +$STD tar xzf yamnet-tflite-classification-tflite-v1.tar.gz +rm -rf yamnet-tflite-classification-tflite-v1.tar.gz +mv 1.tflite cpu_audio_model.tflite +cp /opt/frigate/audio-labelmap.txt /audio-labelmap.txt +msg_ok "Built Audio Models" + +# This should be moved to conditional block, only needed if Hailo AI module is detected +msg_info "Building HailoRT" +$STD bash /opt/frigate/docker/main/install_hailort.sh +cp -a /opt/frigate/docker/main/rootfs/. / +sed -i '/^.*unset DEBIAN_FRONTEND.*$/d' /opt/frigate/docker/main/install_deps.sh +echo "libedgetpu1-max libedgetpu/accepted-eula boolean true" | debconf-set-selections +echo "libedgetpu1-max libedgetpu/install-confirm-max boolean true" | debconf-set-selections +$STD bash /opt/frigate/docker/main/install_deps.sh $STD pip3 install -U /wheels/*.whl ldconfig -$STD pip3 install -r /opt/frigate/docker/main/requirements-dev.txt -$STD /opt/frigate/.devcontainer/initialize.sh -$STD make version -msg_ok "Python venv ready" +#Run twice to fix dependency conflict +$STD pip3 install -U /wheels/*.whl +msg_ok "Built HailoRT" -msg_info "Building Web UI" +msg_info "Installing OpenVino Runtime and Dev library" +$STD pip3 install -r /opt/frigate/docker/main/requirements-ov.txt +msg_ok "Installed OpenVino Runtime and Dev library" + +msg_info "Downloading OpenVino Model" +mkdir -p /models +cd /models +wget -q http://download.tensorflow.org/models/object_detection/ssdlite_mobilenet_v2_coco_2018_05_09.tar.gz +$STD tar -zxvf ssdlite_mobilenet_v2_coco_2018_05_09.tar.gz --no-same-owner +$STD python3 /opt/frigate/docker/main/build_ov_model.py +mkdir -p /openvino-model +cp -r /models/ssdlite_mobilenet_v2.xml /openvino-model/ +cp -r /models/ssdlite_mobilenet_v2.bin /openvino-model/ +wget -q https://github.com/openvinotoolkit/open_model_zoo/raw/master/data/dataset_classes/coco_91cl_bkgr.txt -O /openvino-model/coco_91cl_bkgr.txt +sed -i 's/truck/car/g' /openvino-model/coco_91cl_bkgr.txt +msg_ok "Downloaded OpenVino Model" + +msg_info "Installing Frigate" +cd /opt/frigate +$STD pip3 install -r /opt/frigate/docker/main/requirements-dev.txt +$STD bash /opt/frigate/.devcontainer/initialize.sh +$STD make version cd /opt/frigate/web -$STD npm ci +$STD npm install $STD npm run build cp -r /opt/frigate/web/dist/* /opt/frigate/web/ -cp -r /opt/frigate/config/. /config -msg_ok "Web UI built" - -msg_info "Writing default config" +cd /opt/frigate/ sed -i '/^s6-svc -O \.$/s/^/#/' /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/frigate/run -mkdir -p /opt/frigate/config -cat </opt/frigate/config/config.yml +cp -r /opt/frigate/config/. /config +mkdir -p /media/frigate +curl -fsSL "https://github.com/intel-iot-devkit/sample-videos/raw/master/person-bicycle-car-detection.mp4" -o "/media/frigate/person-bicycle-car-detection.mp4" +echo "tmpfs /tmp/cache tmpfs defaults 0 0" >>/etc/fstab +cat </etc/frigate.env +DEFAULT_FFMPEG_VERSION="7.0" +INCLUDED_FFMPEG_VERSIONS="7.0:5.0" +EOF +cat </config/config.yml mqtt: enabled: false cameras: @@ -80,125 +211,44 @@ cameras: height: 1080 width: 1920 fps: 5 +# Optional: Authentication configuration +auth: + # Optional: Enable authentication + enabled: false +detect: + enabled: false EOF -mkdir -p /config -ln -sf /config/config.yml /opt/frigate/config/config.yml -if [[ "$CTTYPE" == "0" ]]; then - sed -i -e 's/^kvm:x:104:$/render:x:104:root,frigate/' -e 's/^render:x:105:root$/kvm:x:105:/' /etc/group -else - sed -i -e 's/^kvm:x:104:$/render:x:104:frigate/' -e 's/^render:x:105:$/kvm:x:105:/' /etc/group -fi -echo "tmpfs /tmp/cache tmpfs defaults 0 0" >>/etc/fstab -mkdir -p /media/frigate -wget -qO /media/frigate/person-bicycle-car-detection.mp4 https://github.com/intel-iot-devkit/sample-videos/raw/master/person-bicycle-car-detection.mp4 -cat <<'EOF' >/opt/frigate/frigate/version.py -VERSION = "0.16.0" -EOF -msg_ok "Config ready" +msg_ok "Installed Frigate" -if grep -q -o -m1 -E 'avx[^ ]*' /proc/cpuinfo; then - msg_ok "AVX Support Detected" - msg_info "Installing Openvino Object Detection Model (Resilience)" - $STD pip install -r /opt/frigate/docker/main/requirements-ov.txt - cd /opt/frigate/models - export ENABLE_ANALYTICS=NO - $STD /usr/local/bin/omz_downloader --name ssdlite_mobilenet_v2 --num_attempts 2 - $STD /usr/local/bin/omz_converter --name ssdlite_mobilenet_v2 --precision FP16 --mo /usr/local/bin/mo - cd / - cp -r /opt/frigate/models/public/ssdlite_mobilenet_v2 openvino-model - curl -fsSL "https://github.com/openvinotoolkit/open_model_zoo/raw/master/data/dataset_classes/coco_91cl_bkgr.txt" -o "openvino-model/coco_91cl_bkgr.txt" - sed -i 's/truck/car/g' openvino-model/coco_91cl_bkgr.txt - cat <>/config/config.yml +if grep -q -o -m1 -E 'avx[^ ]* | sse4_2' /proc/cpuinfo; then + msg_ok "AVX or SSE 4.2 Support Detected" + msg_info "Configuring Openvino Object Detection Model" + cat <>/config/config.yml +ffmpeg: + hwaccel_args: auto detectors: - ov: + detector01: type: openvino - device: CPU - model: - path: /openvino-model/FP16/ssdlite_mobilenet_v2.xml model: width: 300 height: 300 input_tensor: nhwc input_pixel_format: bgr + path: /openvino-model/ssdlite_mobilenet_v2.xml labelmap_path: /openvino-model/coco_91cl_bkgr.txt EOF - msg_ok "Installed Openvino Object Detection Model" + msg_ok "Configured Openvino Object Detection Model" else - cat <>/config/config.yml + msg_info "Configuring CPU Object Detection Model" + cat <>/config/config.yml +ffmpeg: + hwaccel_args: auto model: path: /cpu_model.tflite EOF + msg_ok "Configured CPU Object Detection Model" fi -msg_info "Building and Installing libUSB without udev" -wget -qO /tmp/libusb.zip https://github.com/libusb/libusb/archive/v1.0.29.zip -unzip -q /tmp/libusb.zip -d /tmp/ -cd /tmp/libusb-1.0.29 -./bootstrap.sh -./configure --disable-udev --enable-shared -make -j$(nproc --all) -make install -ldconfig -rm -rf /tmp/libusb.zip /tmp/libusb-1.0.29 -msg_ok "Installed libUSB without udev" - -msg_info "Installing Coral Object Detection Model (Patience)" -cd /opt/frigate -export CCACHE_DIR=/root/.ccache -export CCACHE_MAXSIZE=2G -curl -fsSL "https://github.com/libusb/libusb/archive/v1.0.26.zip" -o "v1.0.26.zip" -$STD unzip v1.0.26.zip -rm v1.0.26.zip -cd libusb-1.0.26 -$STD ./bootstrap.sh -$STD ./configure --disable-udev --enable-shared -$STD make -j $(nproc --all) -cd /opt/frigate/libusb-1.0.26/libusb -mkdir -p /usr/local/lib -$STD /bin/bash ../libtool --mode=install /usr/bin/install -c libusb-1.0.la '/usr/local/lib' -mkdir -p /usr/local/include/libusb-1.0 -$STD /usr/bin/install -c -m 644 libusb.h '/usr/local/include/libusb-1.0' -ldconfig -cd / -curl -fsSL "https://github.com/google-coral/test_data/raw/release-frogfish/ssdlite_mobiledet_coco_qat_postprocess_edgetpu.tflite" -o "edgetpu_model.tflite" -curl -fsSL "https://github.com/google-coral/test_data/raw/release-frogfish/ssdlite_mobiledet_coco_qat_postprocess.tflite" -o "cpu_model.tflite" -cp /opt/frigate/labelmap.txt /labelmap.txt -curl -fsSL "https://www.kaggle.com/api/v1/models/google/yamnet/tfLite/classification-tflite/1/download" -o "yamnet-tflite-classification-tflite-v1.tar.gz" -tar xzf yamnet-tflite-classification-tflite-v1.tar.gz -rm -rf yamnet-tflite-classification-tflite-v1.tar.gz -mv 1.tflite cpu_audio_model.tflite -cp /opt/frigate/audio-labelmap.txt /audio-labelmap.txt -mkdir -p /media/frigate -curl -fsSL "https://github.com/intel-iot-devkit/sample-videos/raw/master/person-bicycle-car-detection.mp4" -o "/media/frigate/person-bicycle-car-detection.mp4" -msg_ok "Installed Coral Object Detection Model" - -# ------------------------------------------------------------ -# Tempio installieren -msg_info "Installing Tempio" -sed -i 's|/rootfs/usr/local|/usr/local|g' /opt/frigate/docker/main/install_tempio.sh -export TARGETARCH="amd64" -export DEBIAN_FRONTEND=noninteractive -echo "libedgetpu1-max libedgetpu/accepted-eula select true" | debconf-set-selections -echo "libedgetpu1-max libedgetpu/install-confirm-max select true" | debconf-set-selections -$STD /opt/frigate/docker/main/install_tempio.sh -chmod +x /usr/local/tempio/bin/tempio -ln -sf /usr/local/tempio/bin/tempio /usr/local/bin/tempio -msg_ok "Installed Tempio" - -# msg_info "Copying model files" -# cp /opt/frigate/cpu_model.tflite / -# cp /opt/frigate/edgetpu_model.tflite / -# cp /opt/frigate/audio-labelmap.txt / -# cp /opt/frigate/labelmap.txt / -# msg_ok "Copied model files" - -msg_info "Building Nginx with Custom Modules" -sed -i 's/if \[\[ "$VERSION_ID" == "12" \]\]; then/if [[ -f \/etc\/apt\/sources.list.d\/debian.sources ]]; then/' /opt/frigate/docker/main/build_nginx.sh -$STD /opt/frigate/docker/main/build_nginx.sh -sed -e '/s6-notifyoncheck/ s/^#*/#/' -i /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/run -ln -sf /usr/local/nginx/sbin/nginx /usr/local/bin/nginx -msg_ok "Built Nginx" - msg_info "Creating Services" cat </etc/systemd/system/create_directories.service [Unit] @@ -225,8 +275,7 @@ Type=simple Restart=always RestartSec=1 User=root -Environment=DEFAULT_FFMPEG_VERSION=7.0 -Environment=INCLUDED_FFMPEG_VERSIONS=5.0 +EnvironmentFile=/etc/frigate.env ExecStartPre=+rm /dev/shm/logs/go2rtc/current ExecStart=/bin/bash -c "bash /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/go2rtc/run 2> >(/usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S ' >&2) | /usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S '" StandardOutput=file:/dev/shm/logs/go2rtc/current @@ -249,9 +298,8 @@ Type=simple Restart=always RestartSec=1 User=root +EnvironmentFile=/etc/frigate.env # Environment=PLUS_API_KEY= -Environment=DEFAULT_FFMPEG_VERSION=7.0 -Environment=INCLUDED_FFMPEG_VERSIONS=5.0 ExecStartPre=+rm /dev/shm/logs/frigate/current ExecStart=/bin/bash -c "bash /opt/frigate/docker/main/rootfs/etc/s6-overlay/s6-rc.d/frigate/run 2> >(/usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S ' >&2) | /usr/bin/ts '%%Y-%%m-%%d %%H:%%M:%%.S '" StandardOutput=file:/dev/shm/logs/frigate/current @@ -283,4 +331,14 @@ StandardError=file:/dev/shm/logs/nginx/current WantedBy=multi-user.target EOF systemctl enable -q --now nginx -msg_ok "Configured Services" +msg_ok "Created Services" + +motd_ssh +customize + +msg_info "Cleaning up" +apt-get -y autoremove +apt-get -y autoclean +msg_ok "Cleaned" + +echo -e "Don't forget to edit the Frigate config file (${GN}/config/config.yml${CL}) and reboot. Example configuration at https://docs.frigate.video/configuration/" \ No newline at end of file From dfa6bcf9c71ee750c5d22c88b03683e47d366a67 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 24 Sep 2025 08:42:53 +0200 Subject: [PATCH 1142/1733] Improve storage selection logic in build script Adds checks to ensure the default.vars file exists before storage selection. Updates LXC container creation to skip storage selection if variables are already set, improving efficiency and reliability. --- misc/build.func | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/misc/build.func b/misc/build.func index 0610b13b1..c228ca922 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1632,7 +1632,12 @@ install_script() { METHOD="default" base_settings "$VERBOSE" echo_default + [[ -f /usr/local/community-scripts/default.vars ]] || { + mkdir -p /usr/local/community-scripts + touch /usr/local/community-scripts/default.vars + } ensure_storage_selection_for_vars_file "/usr/local/community-scripts/default.vars" + ;; 2 | advanced | ADVANCED) header_info @@ -1640,7 +1645,12 @@ install_script() { METHOD="advanced" base_settings advanced_settings + [[ -f /usr/local/community-scripts/default.vars ]] || { + mkdir -p /usr/local/community-scripts + touch /usr/local/community-scripts/default.vars + } ensure_storage_selection_for_vars_file "/usr/local/community-scripts/default.vars" + maybe_offer_save_app_defaults ;; 3 | mydefaults | MYDEFAULTS) @@ -2781,11 +2791,13 @@ create_lxc_container() { msg_ok "Storage ${BL}${TEMPLATE_STORAGE}${CL} (${TEMPLATE_STORAGE_INFO}) [Template]" else while true; do - if select_storage template; then - TEMPLATE_STORAGE="$STORAGE_RESULT" - TEMPLATE_STORAGE_INFO="$STORAGE_INFO" - msg_ok "Storage ${BL}${TEMPLATE_STORAGE}${CL} (${TEMPLATE_STORAGE_INFO}) [Template]" - break + if [[ -z "${var_template_storage:-}" ]]; then + if select_storage template; then + TEMPLATE_STORAGE="$STORAGE_RESULT" + TEMPLATE_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${TEMPLATE_STORAGE}${CL} (${TEMPLATE_STORAGE_INFO}) [Template]" + break + fi fi done fi @@ -2796,14 +2808,13 @@ create_lxc_container() { CONTAINER_STORAGE_INFO="$STORAGE_INFO" msg_ok "Storage ${BL}${CONTAINER_STORAGE}${CL} (${CONTAINER_STORAGE_INFO}) [Container]" else - while true; do + if [[ -z "${var_container_storage:-}" ]]; then if select_storage container; then CONTAINER_STORAGE="$STORAGE_RESULT" CONTAINER_STORAGE_INFO="$STORAGE_INFO" msg_ok "Storage ${BL}${CONTAINER_STORAGE}${CL} (${CONTAINER_STORAGE_INFO}) [Container]" - break fi - done + fi fi # Validate content types From fed24086ea730dce58b90a8ab6084891af9ea359 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 24 Sep 2025 08:51:14 +0200 Subject: [PATCH 1143/1733] Update build.func --- misc/build.func | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/misc/build.func b/misc/build.func index c228ca922..6a674cf3b 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1688,10 +1688,18 @@ edit_default_storage() { local vf="/usr/local/community-scripts/default.vars" # make sure file exists - if [ ! -f "$vf" ]; then - msg_info "No default.vars found, creating $vf" - mkdir -p /usr/local/community-scripts + if [[ ! -f "$vf" ]]; then + # still create + mkdir -p "$(dirname "$vf")" touch "$vf" + + if select_storage template; then + echo "var_template_storage=$STORAGE_RESULT" >>"$vf" + fi + + if select_storage container; then + echo "var_container_storage=$STORAGE_RESULT" >>"$vf" + fi fi # reuse the same Whiptail selection we already have From 09c2308b353bb77408c82d7042bd602dbf5d625e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 24 Sep 2025 08:56:59 +0200 Subject: [PATCH 1144/1733] Update build.func --- misc/build.func | 44 ++++++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/misc/build.func b/misc/build.func index 6a674cf3b..a1e515318 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1522,27 +1522,47 @@ maybe_offer_save_app_defaults() { } ensure_storage_selection_for_vars_file() { - vf="$1" + local vf="$1" # Read stored values (if any) + local tpl ct tpl=$(grep -E '^var_template_storage=' "$vf" | cut -d= -f2-) ct=$(grep -E '^var_container_storage=' "$vf" | cut -d= -f2-) - # Template storage - if [ -n "$tpl" ]; then + # Wenn beide Werte schon existieren → übernehmen + if [[ -n "$tpl" && -n "$ct" ]]; then TEMPLATE_STORAGE="$tpl" - else - choose_and_set_storage_for_file "$vf" template - fi - - # Container storage - if [ -n "$ct" ]; then CONTAINER_STORAGE="$ct" - else - choose_and_set_storage_for_file "$vf" container + echo_storage_summary_from_file "$vf" + return 0 fi - echo_storage_summary_from_file "$vf" + # --- Erstmalige Auswahl: beide Abfragen --- + select_storage template + local tpl_sel="$STORAGE_RESULT" + + select_storage container + local ct_sel="$STORAGE_RESULT" + + # --- Zusammenfassung + Nachfrage --- + if whiptail --backtitle "Community Scripts" --title "Default Storage" \ + --yesno "Template-Storage --> $tpl_sel\nContainer-Storage --> $ct_sel\n\nSave as global defaults?" \ + 12 70; then + sed -i '/^[#[:space:]]*var_template_storage=/d' "$vf" + sed -i '/^[#[:space:]]*var_container_storage=/d' "$vf" + echo "var_template_storage=$tpl_sel" >>"$vf" + echo "var_container_storage=$ct_sel" >>"$vf" + else + sed -i '/^[#[:space:]]*var_template_storage=/d' "$vf" + sed -i '/^[#[:space:]]*var_container_storage=/d' "$vf" + echo "# var_template_storage=$tpl_sel" >>"$vf" + echo "# var_container_storage=$ct_sel" >>"$vf" + fi + + TEMPLATE_STORAGE="$tpl_sel" + CONTAINER_STORAGE="$ct_sel" + msg_ok "Using Template-Storage → $tpl_sel" + msg_ok "Using Container-Storage → $ct_sel" } diagnostics_menu() { From bcddaaba41544ed5f32cfdbab055dfccc6175654 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 24 Sep 2025 08:57:11 +0200 Subject: [PATCH 1145/1733] Update build.func --- misc/build.func | 3 --- 1 file changed, 3 deletions(-) diff --git a/misc/build.func b/misc/build.func index a1e515318..6f4ed7617 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1529,7 +1529,6 @@ ensure_storage_selection_for_vars_file() { tpl=$(grep -E '^var_template_storage=' "$vf" | cut -d= -f2-) ct=$(grep -E '^var_container_storage=' "$vf" | cut -d= -f2-) - # Wenn beide Werte schon existieren → übernehmen if [[ -n "$tpl" && -n "$ct" ]]; then TEMPLATE_STORAGE="$tpl" CONTAINER_STORAGE="$ct" @@ -1537,14 +1536,12 @@ ensure_storage_selection_for_vars_file() { return 0 fi - # --- Erstmalige Auswahl: beide Abfragen --- select_storage template local tpl_sel="$STORAGE_RESULT" select_storage container local ct_sel="$STORAGE_RESULT" - # --- Zusammenfassung + Nachfrage --- if whiptail --backtitle "Community Scripts" --title "Default Storage" \ --yesno "Template-Storage --> $tpl_sel\nContainer-Storage --> $ct_sel\n\nSave as global defaults?" \ 12 70; then From 721ba2419162898963b4289a665048090c66ef6f Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 24 Sep 2025 09:29:36 +0200 Subject: [PATCH 1146/1733] Update build.func --- misc/build.func | 144 +++++++++++++++++++++++++----------------------- 1 file changed, 74 insertions(+), 70 deletions(-) diff --git a/misc/build.func b/misc/build.func index 6f4ed7617..b4c215ffa 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2163,16 +2163,20 @@ build_container() { VAAPI_DEVICES=() NVIDIA_DEVICES=() - for device in $(compgen -G "/dev/dri/renderD*" || true); do - VAAPI_DEVICES+=("$device") - done - for device in $(compgen -G "/dev/dri/card*" || true); do + # VAAPI / Intel / AMD + for device in /dev/dri/renderD* /dev/dri/card*; do + [[ -e "$device" ]] || continue VAAPI_DEVICES+=("$device") done - for device in $(compgen -G "/dev/nvidia*" || true); do + # NVIDIA + for device in /dev/nvidia*; do + [[ -e "$device" ]] || continue NVIDIA_DEVICES+=("$device") done + + msg_debug "Detected VAAPI devices: ${VAAPI_DEVICES[*]:-(none)}" + msg_debug "Detected NVIDIA devices: ${NVIDIA_DEVICES[*]:-(none)}" } # Configure USB passthrough for privileged containers @@ -2401,77 +2405,77 @@ EOF fi fi - # Install GPU userland packages - install_gpu_userland() { - local gpu_type="$1" + # # Install GPU userland packages + # install_gpu_userland() { + # local gpu_type="$1" - if [ "$var_os" == "alpine" ]; then - case "$gpu_type" in - VAAPI) - msg_info "Installing VAAPI packages in Alpine container" - pct exec "$CTID" -- ash -c ' - apk add --no-cache \ - mesa-dri-gallium \ - mesa-va-gallium \ - intel-media-driver \ - libva-utils >/dev/null 2>&1 - ' || msg_warn "Some VAAPI packages may not be available in Alpine" - ;; - NVIDIA) - msg_warn "NVIDIA drivers are not readily available in Alpine Linux" - ;; - esac - else - case "$gpu_type" in - VAAPI) - msg_info "Installing VAAPI userland packages" - pct exec "$CTID" -- bash -c ' - . /etc/os-release || true - if [[ "${VERSION_CODENAME:-}" == "trixie" ]]; then - cat >/etc/apt/sources.list.d/non-free.sources </dev/null 2>&1 - DEBIAN_FRONTEND=noninteractive apt-get install -y \ - intel-media-va-driver-non-free \ - mesa-va-drivers \ - libvpl2 \ - vainfo \ - ocl-icd-libopencl1 \ - mesa-opencl-icd \ - intel-gpu-tools >/dev/null 2>&1 - ' && msg_ok "VAAPI userland installed" || msg_warn "Some VAAPI packages failed to install" - ;; - NVIDIA) - msg_info "Installing NVIDIA userland packages" - pct exec "$CTID" -- bash -c ' - apt-get update >/dev/null 2>&1 - DEBIAN_FRONTEND=noninteractive apt-get install -y \ - nvidia-driver \ - nvidia-utils \ - libnvidia-encode1 \ - libcuda1 >/dev/null 2>&1 - ' && msg_ok "NVIDIA userland installed" || msg_warn "Some NVIDIA packages failed to install" - ;; - esac - fi - } + # if [ "$var_os" == "alpine" ]; then + # case "$gpu_type" in + # VAAPI) + # msg_info "Installing VAAPI packages in Alpine container" + # pct exec "$CTID" -- ash -c ' + # apk add --no-cache \ + # mesa-dri-gallium \ + # mesa-va-gallium \ + # intel-media-driver \ + # libva-utils >/dev/null 2>&1 + # ' || msg_warn "Some VAAPI packages may not be available in Alpine" + # ;; + # NVIDIA) + # msg_warn "NVIDIA drivers are not readily available in Alpine Linux" + # ;; + # esac + # else + # case "$gpu_type" in + # VAAPI) + # msg_info "Installing VAAPI userland packages" + # pct exec "$CTID" -- bash -c ' + # . /etc/os-release || true + # if [[ "${VERSION_CODENAME:-}" == "trixie" ]]; then + # cat >/etc/apt/sources.list.d/non-free.sources </dev/null 2>&1 + # DEBIAN_FRONTEND=noninteractive apt-get install -y \ + # intel-media-va-driver-non-free \ + # mesa-va-drivers \ + # libvpl2 \ + # vainfo \ + # ocl-icd-libopencl1 \ + # mesa-opencl-icd \ + # intel-gpu-tools >/dev/null 2>&1 + # ' && msg_ok "VAAPI userland installed" || msg_warn "Some VAAPI packages failed to install" + # ;; + # NVIDIA) + # msg_info "Installing NVIDIA userland packages" + # pct exec "$CTID" -- bash -c ' + # apt-get update >/dev/null 2>&1 + # DEBIAN_FRONTEND=noninteractive apt-get install -y \ + # nvidia-driver \ + # nvidia-utils \ + # libnvidia-encode1 \ + # libcuda1 >/dev/null 2>&1 + # ' && msg_ok "NVIDIA userland installed" || msg_warn "Some NVIDIA packages failed to install" + # ;; + # esac + # fi + # } # Customize container msg_info "Customizing LXC Container" - # Install GPU userland if configured - if [[ "${ENABLE_VAAPI:-0}" == "1" ]]; then - install_gpu_userland "VAAPI" - fi + # # Install GPU userland if configured + # if [[ "${ENABLE_VAAPI:-0}" == "1" ]]; then + # install_gpu_userland "VAAPI" + # fi - if [[ "${ENABLE_NVIDIA:-0}" == "1" ]]; then - install_gpu_userland "NVIDIA" - fi + # if [[ "${ENABLE_NVIDIA:-0}" == "1" ]]; then + # install_gpu_userland "NVIDIA" + # fi # Continue with standard container setup if [ "$var_os" == "alpine" ]; then From 3d83a65813b8678827c22d1575ca768e5fbd6564 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 24 Sep 2025 13:01:17 +0200 Subject: [PATCH 1147/1733] Add Monica test --- ct/monica.sh | 75 +++++++++++++++++++++++++++++++++++ install/monica-install.sh | 82 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 ct/monica.sh create mode 100644 install/monica-install.sh diff --git a/ct/monica.sh b/ct/monica.sh new file mode 100644 index 000000000..2be6d986b --- /dev/null +++ b/ct/monica.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: bvdberg01 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://www.monicahq.com/ + +APP="Monica" +var_tags="${var_tags:-network}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-8}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/monica ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs + + if check_for_gh_release "monica" "monicahq/monica"; then + msg_info "Stopping Service" + systemctl stop apache2 + msg_ok "Stopped Service" + + msg_info "Creating backup" + mv /opt/monica/ /opt/monica-backup + msg_ok "Backup created" + + fetch_and_deploy_gh_release "monica" "monicahq/monica" "prebuild" "latest" "/opt/monica" "monica-v*.tar.bz2" + + msg_info "Configuring monica" + cd /opt/monica/ + cp -r /opt/monica-backup/.env /opt/monica + cp -r /opt/monica-backup/storage/* /opt/monica/storage/ + $STD composer install --no-interaction --no-dev + $STD yarn install + $STD yarn run production + $STD php artisan monica:update --force + chown -R www-data:www-data /opt/monica + chmod -R 775 /opt/monica/storage + msg_ok "Configured monica" + + msg_info "Starting Service" + systemctl start apache2 + msg_ok "Started Service" + + msg_info "Cleaning up" + rm -r /opt/monica-backup + msg_ok "Cleaned" + msg_ok "Updated Successfully" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" diff --git a/install/monica-install.sh b/install/monica-install.sh new file mode 100644 index 000000000..e02824f16 --- /dev/null +++ b/install/monica-install.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: bvdberg01 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://www.monicahq.com/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +PHP_VERSION="8.2" PHP_APACHE="YES" PHP_MODULE="dom,gmp,iconv,mysqli,pdo-mysql,redis,tokenizer" setup_php +setup_composer +setup_mariadb +NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs + +msg_info "Setting up MariaDB" +DB_NAME=monica +DB_USER=monica +DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) +$STD mariadb -u root -e "CREATE DATABASE $DB_NAME;" +$STD mariadb -u root -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';" +$STD mariadb -u root -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" +{ + echo "monica-Credentials" + echo "monica Database User: $DB_USER" + echo "monica Database Password: $DB_PASS" + echo "monica Database Name: $DB_NAME" +} >>~/monica.creds +msg_ok "Set up MariaDB" + +fetch_and_deploy_gh_release "monica" "monicahq/monica" "prebuild" "latest" "/opt/monica" "monica-v*.tar.bz2" + +msg_info "Configuring monica" +cd /opt/monica +cp /opt/monica/.env.example /opt/monica/.env +HASH_SALT=$(openssl rand -base64 32) +sed -i -e "s|^DB_USERNAME=.*|DB_USERNAME=${DB_USER}|" \ + -e "s|^DB_PASSWORD=.*|DB_PASSWORD=${DB_PASS}|" \ + -e "s|^HASH_SALT=.*|HASH_SALT=${HASH_SALT}|" \ + /opt/monica/.env +$STD composer install --no-dev -o --no-interaction +$STD yarn install +$STD yarn run production +$STD php artisan key:generate +$STD php artisan setup:production --email=admin@helper-scripts.com --password=helper-scripts.com --force +chown -R www-data:www-data /opt/monica +chmod -R 775 /opt/monica/storage +msg_ok "Configured monica" + +msg_info "Creating Service" +cat </etc/apache2/sites-available/monica.conf + + ServerName monica + DocumentRoot /opt/monica/public + + Options Indexes FollowSymLinks + AllowOverride All + Require all granted + + + ErrorLog /var/log/apache2/monica_error.log + CustomLog /var/log/apache2/monica_access.log combined + +EOF +$STD a2ensite monica +$STD a2enmod rewrite +$STD a2dissite 000-default.conf +$STD systemctl reload apache2 +msg_ok "Created Service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 476ab98f59ef51543c786b902cef8a743d796141 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 24 Sep 2025 13:01:31 +0200 Subject: [PATCH 1148/1733] Add Monica test --- ct/monica.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/monica.sh b/ct/monica.sh index 2be6d986b..b669fbf81 100644 --- a/ct/monica.sh +++ b/ct/monica.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: bvdberg01 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE From f5b570e7b79b1bfdc8ef0bce89863df27e674dd3 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 24 Sep 2025 13:02:17 +0200 Subject: [PATCH 1149/1733] Add Monica test --- ct/monica.sh | 2 +- install/monica-install.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/monica.sh b/ct/monica.sh index b669fbf81..950de91ec 100644 --- a/ct/monica.sh +++ b/ct/monica.sh @@ -46,7 +46,7 @@ function update_script() { cp -r /opt/monica-backup/.env /opt/monica cp -r /opt/monica-backup/storage/* /opt/monica/storage/ $STD composer install --no-interaction --no-dev - $STD yarn install + $STD yarn install --ignore-engines $STD yarn run production $STD php artisan monica:update --force chown -R www-data:www-data /opt/monica diff --git a/install/monica-install.sh b/install/monica-install.sh index e02824f16..0f7cda1c4 100644 --- a/install/monica-install.sh +++ b/install/monica-install.sh @@ -44,7 +44,7 @@ sed -i -e "s|^DB_USERNAME=.*|DB_USERNAME=${DB_USER}|" \ -e "s|^HASH_SALT=.*|HASH_SALT=${HASH_SALT}|" \ /opt/monica/.env $STD composer install --no-dev -o --no-interaction -$STD yarn install +$STD yarn install --ignore-engines $STD yarn run production $STD php artisan key:generate $STD php artisan setup:production --email=admin@helper-scripts.com --password=helper-scripts.com --force From 1d83f681faf33fd646450661d56b0384502b50d9 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 24 Sep 2025 13:17:58 +0200 Subject: [PATCH 1150/1733] Add Monica test --- ct/monica.sh | 1 + install/monica-install.sh | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ct/monica.sh b/ct/monica.sh index 950de91ec..990a5202f 100644 --- a/ct/monica.sh +++ b/ct/monica.sh @@ -46,6 +46,7 @@ function update_script() { cp -r /opt/monica-backup/.env /opt/monica cp -r /opt/monica-backup/storage/* /opt/monica/storage/ $STD composer install --no-interaction --no-dev + $STD yarn config set ignore-engines true $STD yarn install --ignore-engines $STD yarn run production $STD php artisan monica:update --force diff --git a/install/monica-install.sh b/install/monica-install.sh index 0f7cda1c4..a4e6386c9 100644 --- a/install/monica-install.sh +++ b/install/monica-install.sh @@ -44,7 +44,8 @@ sed -i -e "s|^DB_USERNAME=.*|DB_USERNAME=${DB_USER}|" \ -e "s|^HASH_SALT=.*|HASH_SALT=${HASH_SALT}|" \ /opt/monica/.env $STD composer install --no-dev -o --no-interaction -$STD yarn install --ignore-engines +$STD yarn config set ignore-engines true +$STD yarn install $STD yarn run production $STD php artisan key:generate $STD php artisan setup:production --email=admin@helper-scripts.com --password=helper-scripts.com --force From 82c81e8ad43b3ad36d9508623f28cb5a938213d3 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 24 Sep 2025 14:00:26 +0200 Subject: [PATCH 1151/1733] Update Joplin --- ct/joplin-server.sh | 58 ++++++++++++++++---------------- install/joplin-server-install.sh | 12 +++---- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/ct/joplin-server.sh b/ct/joplin-server.sh index 9f4896fc8..fb6969cf7 100644 --- a/ct/joplin-server.sh +++ b/ct/joplin-server.sh @@ -20,36 +20,36 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/joplin-server ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if check_for_gh_release "joplin-server" "laurent22/joplin"; then - msg_info "Stopping Services" - systemctl stop joplin-server - msg_ok "Stopped Services" - - fetch_and_deploy_gh_release "joplin-server" "laurent22/joplin" "tarball" "latest" - - msg_info "Updating ${APP}" - cd /opt/joplin-server - sed -i "/onenote-converter/d" packages/lib/package.json - $STD yarn config set --home enableTelemetry 0 - export BUILD_SEQUENCIAL=1 - $STD yarn install --inline-builds - msg_ok "Updated $APP" - - msg_info "Starting Services" - systemctl start joplin-server - msg_ok "Started Services" - - msg_ok "Updated Successfully" - fi + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/joplin-server ]]; then + msg_error "No ${APP} Installation Found!" exit + fi + + if check_for_gh_release "joplin-server" "laurent22/joplin"; then + msg_info "Stopping Services" + systemctl stop joplin-server + msg_ok "Stopped Services" + + fetch_and_deploy_gh_release "joplin-server" "laurent22/joplin" "tarball" "latest" + + msg_info "Updating ${APP}" + cd /opt/joplin-server + sed -i "/onenote-converter/d" packages/lib/package.json + $STD yarn config set --home enableTelemetry 0 + export BUILD_SEQUENCIAL=1 + $STD yarn install --inline-builds + msg_ok "Updated $APP" + + msg_info "Starting Services" + systemctl start joplin-server + msg_ok "Started Services" + + msg_ok "Updated Successfully" + fi + exit } start diff --git a/install/joplin-server-install.sh b/install/joplin-server-install.sh index 6187f565b..45f201660 100644 --- a/install/joplin-server-install.sh +++ b/install/joplin-server-install.sh @@ -15,8 +15,8 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - git \ - rsync + git \ + rsync msg_ok "Installed Dependencies" PG_VERSION="17" setup_postgresql @@ -38,10 +38,10 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8' $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC'" { - echo "Joplin-Credentials" - echo "Joplin Database User: $DB_USER" - echo "Joplin Database Password: $DB_PASS" - echo "Joplin Database Name: $DB_NAME" + echo "Joplin-Credentials" + echo "Joplin Database User: $DB_USER" + echo "Joplin Database Password: $DB_PASS" + echo "Joplin Database Name: $DB_NAME" } >>~/joplin.creds msg_ok "Set up PostgreSQL Database" From 96f45dafe73e5ebc64e546508a764050e18421f3 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 24 Sep 2025 14:00:27 +0200 Subject: [PATCH 1152/1733] Update Joplin --- .vscode/settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 8f17c7ff9..47009d646 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -18,7 +18,7 @@ "editor.minimap.enabled": false, "terminal.integrated.scrollback": 10000, "[shellscript]": { - "editor.defaultFormatter": "foxundermoon.shell-format", + "editor.defaultFormatter": "mads-hartmann.bash-ide-vscode", "editor.tabSize": 4, "editor.insertSpaces": true, }, From 7529f5fa90e8158831f9701973803ac5240333fd Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 24 Sep 2025 14:05:39 +0200 Subject: [PATCH 1153/1733] Delete Monica --- ct/monica.sh | 76 ----------------------------------- install/monica-install.sh | 83 --------------------------------------- 2 files changed, 159 deletions(-) delete mode 100644 ct/monica.sh delete mode 100644 install/monica-install.sh diff --git a/ct/monica.sh b/ct/monica.sh deleted file mode 100644 index 990a5202f..000000000 --- a/ct/monica.sh +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: bvdberg01 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://www.monicahq.com/ - -APP="Monica" -var_tags="${var_tags:-network}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-8}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/monica ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs - - if check_for_gh_release "monica" "monicahq/monica"; then - msg_info "Stopping Service" - systemctl stop apache2 - msg_ok "Stopped Service" - - msg_info "Creating backup" - mv /opt/monica/ /opt/monica-backup - msg_ok "Backup created" - - fetch_and_deploy_gh_release "monica" "monicahq/monica" "prebuild" "latest" "/opt/monica" "monica-v*.tar.bz2" - - msg_info "Configuring monica" - cd /opt/monica/ - cp -r /opt/monica-backup/.env /opt/monica - cp -r /opt/monica-backup/storage/* /opt/monica/storage/ - $STD composer install --no-interaction --no-dev - $STD yarn config set ignore-engines true - $STD yarn install --ignore-engines - $STD yarn run production - $STD php artisan monica:update --force - chown -R www-data:www-data /opt/monica - chmod -R 775 /opt/monica/storage - msg_ok "Configured monica" - - msg_info "Starting Service" - systemctl start apache2 - msg_ok "Started Service" - - msg_info "Cleaning up" - rm -r /opt/monica-backup - msg_ok "Cleaned" - msg_ok "Updated Successfully" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" diff --git a/install/monica-install.sh b/install/monica-install.sh deleted file mode 100644 index a4e6386c9..000000000 --- a/install/monica-install.sh +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: bvdberg01 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://www.monicahq.com/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -PHP_VERSION="8.2" PHP_APACHE="YES" PHP_MODULE="dom,gmp,iconv,mysqli,pdo-mysql,redis,tokenizer" setup_php -setup_composer -setup_mariadb -NODE_VERSION="22" NODE_MODULE="yarn@latest" setup_nodejs - -msg_info "Setting up MariaDB" -DB_NAME=monica -DB_USER=monica -DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) -$STD mariadb -u root -e "CREATE DATABASE $DB_NAME;" -$STD mariadb -u root -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';" -$STD mariadb -u root -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" -{ - echo "monica-Credentials" - echo "monica Database User: $DB_USER" - echo "monica Database Password: $DB_PASS" - echo "monica Database Name: $DB_NAME" -} >>~/monica.creds -msg_ok "Set up MariaDB" - -fetch_and_deploy_gh_release "monica" "monicahq/monica" "prebuild" "latest" "/opt/monica" "monica-v*.tar.bz2" - -msg_info "Configuring monica" -cd /opt/monica -cp /opt/monica/.env.example /opt/monica/.env -HASH_SALT=$(openssl rand -base64 32) -sed -i -e "s|^DB_USERNAME=.*|DB_USERNAME=${DB_USER}|" \ - -e "s|^DB_PASSWORD=.*|DB_PASSWORD=${DB_PASS}|" \ - -e "s|^HASH_SALT=.*|HASH_SALT=${HASH_SALT}|" \ - /opt/monica/.env -$STD composer install --no-dev -o --no-interaction -$STD yarn config set ignore-engines true -$STD yarn install -$STD yarn run production -$STD php artisan key:generate -$STD php artisan setup:production --email=admin@helper-scripts.com --password=helper-scripts.com --force -chown -R www-data:www-data /opt/monica -chmod -R 775 /opt/monica/storage -msg_ok "Configured monica" - -msg_info "Creating Service" -cat </etc/apache2/sites-available/monica.conf - - ServerName monica - DocumentRoot /opt/monica/public - - Options Indexes FollowSymLinks - AllowOverride All - Require all granted - - - ErrorLog /var/log/apache2/monica_error.log - CustomLog /var/log/apache2/monica_access.log combined - -EOF -$STD a2ensite monica -$STD a2enmod rewrite -$STD a2dissite 000-default.conf -$STD systemctl reload apache2 -msg_ok "Created Service" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" From 0df793cd369d6c7d6feda7017e730bd2197d241c Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 24 Sep 2025 14:06:37 +0200 Subject: [PATCH 1154/1733] Remove UpSnap --- ct/upsnap.sh | 54 ------------------------------ frontend/public/json/upsnap.json | 40 ----------------------- install/upsnap-install.sh | 56 -------------------------------- 3 files changed, 150 deletions(-) delete mode 100644 ct/upsnap.sh delete mode 100644 frontend/public/json/upsnap.json delete mode 100644 install/upsnap-install.sh diff --git a/ct/upsnap.sh b/ct/upsnap.sh deleted file mode 100644 index 203962e99..000000000 --- a/ct/upsnap.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Slaviša Arežina (tremor021) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/seriousm4x/UpSnap - -APP="UpSnap" -var_tags="${var_tags:-network}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-512}" -var_disk="${var_disk:-2}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/upsnap ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if check_for_gh_release "upsnap" "seriousm4x/UpSnap"; then - msg_info "Stopping Services" - systemctl stop upsnap - msg_ok "Stopped Services" - - fetch_and_deploy_gh_release "upsnap" "seriousm4x/UpSnap" "prebuild" "latest" "/opt/upsnap" "UpSnap_*_linux_amd64.zip" - - msg_info "Starting Services" - systemctl start upsnap - msg_ok "Started Services" - - msg_ok "Updated Successfully" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8090${CL}" diff --git a/frontend/public/json/upsnap.json b/frontend/public/json/upsnap.json deleted file mode 100644 index 82952a8a1..000000000 --- a/frontend/public/json/upsnap.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "UpSnap", - "slug": "upsnap", - "categories": [ - 4 - ], - "date_created": "2025-09-12", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 8090, - "documentation": "https://github.com/seriousm4x/UpSnap/wiki", - "config_path": "", - "website": "https://github.com/seriousm4x/UpSnap", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/upsnap.webp", - "description": "A simple wake on lan web app written with SvelteKit, Go and PocketBase.", - "install_methods": [ - { - "type": "default", - "script": "ct/upsnap.sh", - "resources": { - "cpu": 1, - "ram": 512, - "hdd": 2, - "os": "Debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "The first user you register will be the admin user.", - "type": "info" - } - ] -} diff --git a/install/upsnap-install.sh b/install/upsnap-install.sh deleted file mode 100644 index 3e9c1afd9..000000000 --- a/install/upsnap-install.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Slaviša Arežina (tremor021) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/seriousm4x/UpSnap - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt install -y \ - nmap \ - samba \ - samba-common-bin \ - openssh-client \ - openssh-server \ - sshpass -msg_ok "Installed Dependencies" - -fetch_and_deploy_gh_release "upsnap" "seriousm4x/UpSnap" "prebuild" "latest" "/opt/upsnap" "UpSnap_*_linux_amd64.zip" -setcap 'cap_net_raw=+ep' /opt/upsnap/upsnap - -msg_info "Creating Service" -cat </etc/systemd/system/upsnap.service -[Unit] -Description=UpSnap Service -Documentation=https://github.com/seriousm4x/UpSnap/wiki -After=network.target - -[Service] -Type=simple -User=root -Restart=on-failure -WorkingDirectory=/opt/upsnap -ExecStart=/opt/upsnap/upsnap serve --http=0.0.0.0:8090 - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now upsnap -msg_ok "Service Created" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt -y autoremove -$STD apt -y autoclean -$STD apt -y clean -msg_ok "Cleaned" From 6f4425ae7691dea31c1f9a7d293250703469fc3e Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 24 Sep 2025 12:07:09 +0000 Subject: [PATCH 1155/1733] Update .app files --- ct/headers/upsnap | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 ct/headers/upsnap diff --git a/ct/headers/upsnap b/ct/headers/upsnap deleted file mode 100644 index 0a3180772..000000000 --- a/ct/headers/upsnap +++ /dev/null @@ -1,6 +0,0 @@ - __ __ _____ - / / / /___ / ___/____ ____ _____ - / / / / __ \\__ \/ __ \/ __ `/ __ \ -/ /_/ / /_/ /__/ / / / / /_/ / /_/ / -\____/ .___/____/_/ /_/\__,_/ .___/ - /_/ /_/ From dcca0425600294f212091b98d40b6b77c38bdac7 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 25 Sep 2025 09:03:05 +0200 Subject: [PATCH 1156/1733] Add GoAway script --- ct/goaway.sh | 54 +++++++++++++++++++++++++++++++++++++++ install/goaway-install.sh | 54 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 ct/goaway.sh create mode 100644 install/goaway-install.sh diff --git a/ct/goaway.sh b/ct/goaway.sh new file mode 100644 index 000000000..7b7524b44 --- /dev/null +++ b/ct/goaway.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/pommee/goaway + +APP="GoAway" +var_tags="${var_tags:-network}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-512}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/goaway ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "goaway" "pommee/goaway"; then + msg_info "Stopping Services" + systemctl stop goaway + msg_ok "Stopped Services" + + fetch_and_deploy_gh_release "goaway" "pommee/goaway" "prebuild" "latest" "/opt/goaway" "goaway_*_linux_amd64.tar.gz" + + msg_info "Starting Services" + systemctl start goaway + msg_ok "Started Services" + + msg_ok "Updated Successfully" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" diff --git a/install/goaway-install.sh b/install/goaway-install.sh new file mode 100644 index 000000000..c5ef869f3 --- /dev/null +++ b/install/goaway-install.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://github.com/pommee/goaway + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt-get install -y net-tools +msg_ok "Installed Dependencies" + +fetch_and_deploy_gh_release "goaway" "pommee/goaway" "prebuild" "latest" "/opt/goaway" "goaway_*_linux_amd64.tar.gz" + +msg_info "Creating Service" +cat </etc/systemd/system/goaway.service +[Unit] +Description=GoAway Service +After=network.target + +[Service] +Type=simple +WorkingDirectory=/opt/goaway +ExecStart=/opt/goaway/goaway +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now goaway +sleep 5 +ADMIN_PASS=$(journalctl -u goaway -o cat | grep -oP 'Randomly generated admin password:\s*\K\S+' | tail -n1) +{ + echo "GoAway Credentials" + echo "Admin User: admin" + echo "Admin Password: $ADMIN_PASS" +} >>~/goaway.creds +msg_ok "Service Created" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 9f0d0bcd606f0cb5323d8a18995853e52b99cbd3 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 25 Sep 2025 09:10:03 +0200 Subject: [PATCH 1157/1733] Cleanup --- ct/alpine-caddy.sh | 47 --------- ct/caddy.sh | 1 - ct/globaleaks.sh | 43 -------- ct/joplin-server.sh | 62 ------------ ct/leantime.sh | 62 ------------ ct/npmplus.sh | 58 ----------- ct/outline.sh | 67 ------------- ct/tunarr.sh | 71 -------------- frontend/public/json/caddy.json | 60 ------------ frontend/public/json/execute.json | 48 --------- frontend/public/json/globaleaks.json | 35 ------- frontend/public/json/joplin-server.json | 40 -------- frontend/public/json/leantime.json | 35 ------- frontend/public/json/tunarr.json | 35 ------- install/alpine-caddy-install.sh | 70 ------------- install/globaleaks-install.sh | 30 ------ install/joplin-server-install.sh | 98 ------------------- install/leantime-install.sh | 78 --------------- install/npmplus-install.sh | 124 ------------------------ install/tunarr-install.sh | 101 ------------------- 20 files changed, 1165 deletions(-) delete mode 100644 ct/alpine-caddy.sh delete mode 100644 ct/caddy.sh delete mode 100644 ct/globaleaks.sh delete mode 100644 ct/joplin-server.sh delete mode 100644 ct/leantime.sh delete mode 100644 ct/npmplus.sh delete mode 100644 ct/outline.sh delete mode 100644 ct/tunarr.sh delete mode 100644 frontend/public/json/caddy.json delete mode 100644 frontend/public/json/execute.json delete mode 100644 frontend/public/json/globaleaks.json delete mode 100644 frontend/public/json/joplin-server.json delete mode 100644 frontend/public/json/leantime.json delete mode 100644 frontend/public/json/tunarr.json delete mode 100644 install/alpine-caddy-install.sh delete mode 100644 install/globaleaks-install.sh delete mode 100644 install/joplin-server-install.sh delete mode 100644 install/leantime-install.sh delete mode 100644 install/npmplus-install.sh delete mode 100644 install/tunarr-install.sh diff --git a/ct/alpine-caddy.sh b/ct/alpine-caddy.sh deleted file mode 100644 index 89fe099d3..000000000 --- a/ct/alpine-caddy.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: cobalt (cobaltgit) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://caddyserver.com/ - -APP="Alpine-Caddy" -var_tags="${var_tags:-webserver}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-256}" -var_disk="${var_disk:-3}" -var_os="${var_os:-alpine}" -var_version="${var_version:-3.22}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /etc/caddy ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - msg_info "Updating $APP LXC" - $STD apk -U upgrade - msg_ok "Updated $APP LXC" - - msg_info "Restarting Caddy" - rc-service caddy restart - msg_ok "Restarted Caddy" - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:80${CL}" diff --git a/ct/caddy.sh b/ct/caddy.sh deleted file mode 100644 index 49f72a5ff..000000000 --- a/ct/caddy.sh +++ /dev/null @@ -1 +0,0 @@ -#Placeholder for ALpine-caddy to run teh CI/CD diff --git a/ct/globaleaks.sh b/ct/globaleaks.sh deleted file mode 100644 index 03bc2f081..000000000 --- a/ct/globaleaks.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 communtiy-scripts ORG -# Author: Giovanni `evilaliv3` Pellerano -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/globaleaks/globaleaks-whistleblowing-software - -APP="GlobaLeaks" -var_tags="${var_tags:-whistleblowing-software}" -var_disk="${var_disk:-4}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -f /usr/sbin/globaleaks ]]; then - msg_error "No ${APP} installation found!" - exit - fi - - msg_info "Updating $APP LXC" - $STD apt update - $STD apt -y upgrade - msg_ok "Updated $APP LXC" -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN} ${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}https://${IP}${CL}" diff --git a/ct/joplin-server.sh b/ct/joplin-server.sh deleted file mode 100644 index fb6969cf7..000000000 --- a/ct/joplin-server.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Slaviša Arežina (tremor021) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://joplinapp.org/ - -APP="joplin-server" -var_tags="${var_tags:-notes}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-4096}" -var_disk="${var_disk:-20}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/joplin-server ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if check_for_gh_release "joplin-server" "laurent22/joplin"; then - msg_info "Stopping Services" - systemctl stop joplin-server - msg_ok "Stopped Services" - - fetch_and_deploy_gh_release "joplin-server" "laurent22/joplin" "tarball" "latest" - - msg_info "Updating ${APP}" - cd /opt/joplin-server - sed -i "/onenote-converter/d" packages/lib/package.json - $STD yarn config set --home enableTelemetry 0 - export BUILD_SEQUENCIAL=1 - $STD yarn install --inline-builds - msg_ok "Updated $APP" - - msg_info "Starting Services" - systemctl start joplin-server - msg_ok "Started Services" - - msg_ok "Updated Successfully" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:22300${CL}" diff --git a/ct/leantime.sh b/ct/leantime.sh deleted file mode 100644 index 13b960613..000000000 --- a/ct/leantime.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Stroopwafe1 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://leantime.io - -APP="Leantime" -var_tags="${var_tags:-productivity}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-20}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - - if [[ ! -d /opt/leantime ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if check_for_gh_release "leantime" "Leantime/leantime"; then - msg_info "Creating Backup" - mariadb-dump leantime >"/opt/${APP}_db_backup_$(date +%F).sql" - tar -czf "/opt/${APP}_backup_$(date +%F).tar.gz" "/opt/${APP}" - mv /opt/leantime /opt/leantime_bak - msg_ok "Backup Created" - - fetch_and_deploy_gh_release "leantime" "Leantime/leantime" "prebuild" "latest" "/opt/leantime" Leantime*.tar.gz - - msg_info "Restoring Config & Permissions" - mv /opt/leantime_bak/config/.env /opt/leantime/config/.env - chown -R www-data:www-data "/opt/leantime" - chmod -R 750 "/opt/leantime" - msg_ok "Restored Config & Permissions" - - msg_info "Removing Backup" - rm -rf /opt/leantime_bak - msg_ok "Removed Backup" - msg_ok "Updated Successfully" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}/install${CL}" diff --git a/ct/npmplus.sh b/ct/npmplus.sh deleted file mode 100644 index c3b1755a3..000000000 --- a/ct/npmplus.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: MickLesk (CanbiZ) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/ZoeyVid/NPMplus - -APP="NPMplus" -var_tags="${var_tags:-proxy;nginx}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-512}" -var_disk="${var_disk:-3}" -var_os="${var_os:-alpine}" -var_version="${var_version:-3.22}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - UPD=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "UPDATE MODE" --radiolist --cancel-button Exit-Script "Spacebar = Select" 14 60 2 \ - "1" "Check for Alpine Updates" OFF \ - "2" "Update NPMplus Docker Container" ON \ - 3>&1 1>&2 2>&3) - - header_info "$APP" - - case "$UPD" in - "1") - msg_info "Updating Alpine OS" - $STD apk -U upgrade - msg_ok "System updated" - exit - ;; - "2") - msg_info "Updating NPMplus Container" - cd /opt - msg_info "Pulling latest container image" - $STD docker compose pull - msg_info "Recreating container" - $STD docker compose up -d - msg_ok "NPMplus container updated" - exit - ;; - esac - exit 0 -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:81${CL}" diff --git a/ct/outline.sh b/ct/outline.sh deleted file mode 100644 index 24a42b12c..000000000 --- a/ct/outline.sh +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Slaviša Arežina (tremor021) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/outline/outline - -APP="Outline" -var_tags="${var_tags:-documentation}" -var_disk="${var_disk:-8}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-4096}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/outline ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if check_for_gh_release "outline" "outline/outline"; then - msg_info "Stopping Services" - systemctl stop outline - msg_ok "Services Stopped" - - msg_info "Creating backup" - cp /opt/outline/.env /opt - msg_ok "Backup created" - - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "outline" "outline/outline" "tarball" - - msg_info "Updating ${APP}" - cd /opt/outline - mv /opt/.env /opt/outline - export NODE_ENV=development - export NODE_OPTIONS="--max-old-space-size=3584" - $STD yarn install --frozen-lockfile - export NODE_ENV=production - $STD yarn build - msg_ok "Updated ${APP}" - - msg_info "Starting Services" - systemctl start outline - msg_ok "Started Services" - msg_ok "Updated Successfully" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" diff --git a/ct/tunarr.sh b/ct/tunarr.sh deleted file mode 100644 index f0ed721ab..000000000 --- a/ct/tunarr.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: chrisbenincasa -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://tunarr.com/ - -APP="Tunarr" -var_tags="${var_tags:-iptv}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-1024}" -var_disk="${var_disk:-5}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/tunarr ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - if check_for_gh_release "tunarr" "chrisbenincasa/tunarr"; then - msg_info "Stopping Service" - systemctl stop tunarr - msg_ok "Stopped Service" - - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "tunarr" "chrisbenincasa/tunarr" "singlefile" "latest" "/opt/tunarr" "*linux-x64" - - msg_info "Starting Service" - systemctl start tunarr - msg_ok "Started Service" - msg_ok "Update Successfully" - fi - - if check_for_gh_release "ersatztv-ffmpeg" "ErsatzTV/ErsatzTV-ffmpeg"; then - msg_info "Stopping Service" - systemctl stop tunarr - msg_ok "Stopped Service" - - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "ersatztv-ffmpeg" "ErsatzTV/ErsatzTV-ffmpeg" "prebuild" "latest" "/opt/ErsatzTV-ffmpeg" "*-linux64-gpl-7.1.tar.xz" - - msg_info "Set ErsatzTV-ffmpeg links" - chmod +x /opt/ErsatzTV-ffmpeg/bin/* - ln -sf /opt/ErsatzTV-ffmpeg/bin/ffmpeg /usr/local/bin/ffmpeg - ln -sf /opt/ErsatzTV-ffmpeg/bin/ffplay /usr/local/bin/ffplay - ln -sf /opt/ErsatzTV-ffmpeg/bin/ffprobe /usr/local/bin/ffprobe - msg_ok "ffmpeg links set" - - msg_info "Starting Service" - systemctl start tunarr - msg_ok "Started Service" - msg_ok "Update Successfully" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}" diff --git a/frontend/public/json/caddy.json b/frontend/public/json/caddy.json deleted file mode 100644 index 0f59a88de..000000000 --- a/frontend/public/json/caddy.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "name": "Alpine-Caddy", - "slug": "alpine-caddy", - "categories": [ - 21 - ], - "date_created": "2025-09-17", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 80, - "documentation": "https://caddyserver.com/docs/", - "website": "https://caddyserver.com/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/caddy.webp", - "config_path": "/etc/caddy/Caddyfile", - "description": "Caddy is a powerful, extensible platform to serve your sites, services, and apps, written in Go.", - "install_methods": [ - { - "type": "default", - "script": "ct/caddy.sh", - "resources": { - "cpu": 1, - "ram": 512, - "hdd": 4, - "os": "debian", - "version": "12" - } - }, - { - "type": "alpine", - "script": "ct/alpine-caddy.sh", - "resources": { - "cpu": 1, - "ram": 256, - "hdd": 3, - "os": "alpine", - "version": "3.22" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "xcaddy needs to be updated manually after a caddy update!", - "type": "warning" - }, - { - "text": "if you need an internal module run: `caddy add-package PACKAGENAME`", - "type": "info" - }, - { - "text": "if you need an external module run: `xcaddy build --with github.com/caddy-dns/cloudflare`", - "type": "info" - } - ] -} - diff --git a/frontend/public/json/execute.json b/frontend/public/json/execute.json deleted file mode 100644 index 9014f9c3a..000000000 --- a/frontend/public/json/execute.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "name": "PVE LXC Execute Command", - "slug": "lxc-execute", - "categories": [ - 1 - ], - "date_created": "2025-09-15", - "type": "pve", - "updateable": false, - "privileged": false, - "interface_port": null, - "documentation": null, - "website": null, - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/proxmox.webp", - "config_path": "", - "description": "This script allows administrators to execute a custom command inside one or multiple LXC containers on a Proxmox VE node. Containers can be selectively excluded via an interactive checklist. If a container is stopped, the script will automatically start it, run the command, and then shut it down again. Only Debian and Ubuntu based containers are supported.", - "install_methods": [ - { - "type": "default", - "script": "tools/pve/execute.sh", - "resources": { - "cpu": null, - "ram": null, - "hdd": null, - "os": null, - "version": null - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "Execute within the Proxmox shell.", - "type": "info" - }, - { - "text": "Non-Debian/Ubuntu containers will be skipped automatically.", - "type": "info" - }, - { - "text": "Stopped containers will be started temporarily to run the command, then shut down again.", - "type": "warning" - } - ] -} diff --git a/frontend/public/json/globaleaks.json b/frontend/public/json/globaleaks.json deleted file mode 100644 index f21437376..000000000 --- a/frontend/public/json/globaleaks.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "GlobaLeaks", - "slug": "globaleaks", - "categories": [ - 0 - ], - "date_created": "2025-09-10", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 443, - "documentation": "https://docs.globaleaks.org", - "website": "https://www.globaleaks.org/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/globaleaks.webp", - "config_path": "", - "description": "GlobaLeaks is a free and open-source whistleblowing software enabling anyone to easily set up and maintain a secure reporting platform.", - "install_methods": [ - { - "type": "default", - "script": "ct/globaleaks.sh", - "resources": { - "cpu": 2, - "ram": 1024, - "hdd": 4, - "os": "debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] -} diff --git a/frontend/public/json/joplin-server.json b/frontend/public/json/joplin-server.json deleted file mode 100644 index 49db64487..000000000 --- a/frontend/public/json/joplin-server.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "Joplin Server", - "slug": "joplin-server", - "categories": [ - 12 - ], - "date_created": "2025-09-12", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 22300, - "documentation": "https://joplinapp.org/help/", - "config_path": "/opt/joplin-server/.env", - "website": "https://joplinapp.org/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/joplin.webp", - "description": "Joplin - the privacy-focused note taking app with sync capabilities for Windows, macOS, Linux, Android and iOS.", - "install_methods": [ - { - "type": "default", - "script": "ct/joplin-server.sh", - "resources": { - "cpu": 2, - "ram": 4096, - "hdd": 20, - "os": "Debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": "admin@localhost", - "password": "admin" - }, - "notes": [ - { - "text": "Application can take some time to build, depending on your host speed. Please be patient.", - "type": "info" - } - ] -} diff --git a/frontend/public/json/leantime.json b/frontend/public/json/leantime.json deleted file mode 100644 index e5e0fec81..000000000 --- a/frontend/public/json/leantime.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "Leantime", - "slug": "leantime", - "categories": [ - 12 - ], - "date_created": "2025-06-27", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 80, - "documentation": "https://docs.leantime.io/", - "config_path": "/opt/Leantime/config/.env", - "website": "https://leantime.io", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/leantime.webp", - "description": "Leantime is a goals focused project management system for non-project managers. Building with ADHD, Autism, and dyslexia in mind. ", - "install_methods": [ - { - "type": "default", - "script": "ct/leantime.sh", - "resources": { - "cpu": 2, - "ram": 2048, - "hdd": 20, - "os": "Debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] -} diff --git a/frontend/public/json/tunarr.json b/frontend/public/json/tunarr.json deleted file mode 100644 index 5529ab511..000000000 --- a/frontend/public/json/tunarr.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "Tunarr", - "slug": "tunarr", - "categories": [ - 13 - ], - "date_created": "2025-09-06", - "type": "ct", - "updateable": true, - "privileged": false, - "config_path": "/opt/tunarr/.env", - "interface_port": 8000, - "documentation": "https://tunarr.com/", - "website": "https://tunarr.com/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/tunarr.webp", - "description": "Create a classic TV experience using your own media - IPTV backed by Plex/Jellyfin/Emby.", - "install_methods": [ - { - "type": "default", - "script": "ct/tunarr.sh", - "resources": { - "cpu": 2, - "ram": 1024, - "hdd": 5, - "os": "Debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] -} diff --git a/install/alpine-caddy-install.sh b/install/alpine-caddy-install.sh deleted file mode 100644 index 433808442..000000000 --- a/install/alpine-caddy-install.sh +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: cobalt (cobaltgit) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://caddyserver.com/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Caddy" -$STD apk add --no-cache caddy caddy-openrc -cat </etc/caddy/Caddyfile -:80 { - # Set this path to your site's directory. - root * /var/www/html - - # Enable the static file server. - file_server - - # Another common task is to set up a reverse proxy: - # reverse_proxy localhost:8080 - - # Or serve a PHP site through php-fpm: - # php_fastcgi localhost:9000 -} -EOF -mkdir -p /var/www/html -cat </var/www/html/index.html - - - - Caddy works! - - -

Hello Caddy!

-

For more information, refer to the Caddy documentation

- - -EOF -msg_ok "Installed Caddy" - -read -r -p "${TAB3}Would you like to install xCaddy Addon? " prompt -if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then - GO_VERSION="$(curl -fsSL https://go.dev/VERSION?m=text | head -1 | cut -c3-)" setup_go - msg_info "Setup xCaddy" - cd /opt - RELEASE=$(curl -fsSL https://api.github.com/repos/caddyserver/xcaddy/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - curl -fsSL "https://github.com/caddyserver/xcaddy/releases/download/${RELEASE}/xcaddy_${RELEASE:1}_linux_amd64.tar.gz" -o "xcaddy_${RELEASE:1}_linux_amd64.tar.gz" - $STD tar xzf xcaddy_"${RELEASE:1}"_linux_amd64.tar.gz -C /usr/local/bin xcaddy - rm -rf /opt/xcaddy* - $STD xcaddy build - msg_ok "Setup xCaddy" -fi - -msg_info "Enabling Caddy Service" -$STD rc-update add caddy default -msg_ok "Enabled Caddy Service" - -msg_info "Starting Caddy" -$STD service caddy start -msg_ok "Started Caddy" - -motd_ssh -customize diff --git a/install/globaleaks-install.sh b/install/globaleaks-install.sh deleted file mode 100644 index 650c8cbd3..000000000 --- a/install/globaleaks-install.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Giovanni `evilaliv3` Pellerano -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/globaleaks/globaleaks-whistleblowing-software - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Setup GlobaLeaks" -DISTRO_CODENAME="$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release)" -curl -fsSL https://deb.globaleaks.org/globaleaks.asc | gpg --dearmor -o /etc/apt/trusted.gpg.d/globaleaks.gpg -echo "deb [signed-by=/etc/apt/trusted.gpg.d/globaleaks.gpg] http://deb.globaleaks.org $DISTRO_CODENAME/" >/etc/apt/sources.list.d/globaleaks.list -echo -ne 'APPARMOR_SANDBOXING=0\nNETWORK_SANDBOXING=0' >/etc/default/globaleaks -$STD apt update -$STD apt -y install globaleaks -msg_ok "Setup GlobaLeaks" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" diff --git a/install/joplin-server-install.sh b/install/joplin-server-install.sh deleted file mode 100644 index 45f201660..000000000 --- a/install/joplin-server-install.sh +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Slaviša Arežina (tremor021) -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://joplinapp.org/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt-get install -y \ - git \ - rsync -msg_ok "Installed Dependencies" - -PG_VERSION="17" setup_postgresql -NODE_VERSION=22 NODE_MODULE="yarn@latest,npm@latest,pm2@latest" setup_nodejs -mkdir -p /opt/pm2 -export PM2_HOME=/opt/pm2 -$STD pm2 install pm2-logrotate -$STD pm2 set pm2-logrotate:max_size 100MB -$STD pm2 set pm2-logrotate:retain 5 -$STD pm2 set pm2-logrotate:compress tr - -msg_info "Setting up PostgreSQL Database" -DB_NAME=joplin -DB_USER=joplin -DB_PASS="$(openssl rand -base64 18 | cut -c1-13)" -$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" -$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC'" -{ - echo "Joplin-Credentials" - echo "Joplin Database User: $DB_USER" - echo "Joplin Database Password: $DB_PASS" - echo "Joplin Database Name: $DB_NAME" -} >>~/joplin.creds -msg_ok "Set up PostgreSQL Database" - -fetch_and_deploy_gh_release "joplin-server" "laurent22/joplin" "tarball" "latest" - -msg_info "Setting up Joplin Server (Patience)" -LOCAL_IP=$(hostname -I | awk '{print $1}') -cd /opt/joplin-server -sed -i "/onenote-converter/d" packages/lib/package.json -$STD yarn config set --home enableTelemetry 0 -export BUILD_SEQUENCIAL=1 -$STD yarn install --inline-builds - -cat </opt/joplin-server/.env -PM2_HOME=/opt/pm2 -NODE_ENV=production -APP_BASE_URL=http://$LOCAL_IP:22300 -APP_PORT=22300 -DB_CLIENT=pg -POSTGRES_PASSWORD=$DB_PASS -POSTGRES_DATABASE=$DB_NAME -POSTGRES_USER=$DB_USER -POSTGRES_PORT=5432 -POSTGRES_HOST=localhost -EOF -msg_ok "Setup Joplin Server" - -msg_info "Setting up Service" -cat </etc/systemd/system/joplin-server.service -[Unit] -Description=Joplin Server Service -After=network.target - -[Service] -Type=simple -WorkingDirectory=/opt/joplin-server/packages/server -EnvironmentFile=/opt/joplin-server/.env -ExecStart=/usr/bin/yarn start-prod -Restart=on-failure -RestartSec=5 - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now joplin-server -msg_ok "Service Setup" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" diff --git a/install/leantime-install.sh b/install/leantime-install.sh deleted file mode 100644 index 98aa77a19..000000000 --- a/install/leantime-install.sh +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Stroopwafe1 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://leantime.io - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -PHP_VERSION="8.4" PHP_MODULE="mysql" PHP_APACHE="YES" PHP_FPM="YES" setup_php -setup_mariadb - -msg_info "Setting up Database" -DB_NAME=leantime -DB_USER=leantime -DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) -$STD mysql -u root -e "CREATE DATABASE $DB_NAME;" -$STD mysql -u root -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED WITH mysql_native_password AS PASSWORD('$DB_PASS');" -$STD mysql -u root -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" -{ - echo "Leantime Credentials" - echo "Database User: $DB_USER" - echo "Database Password: $DB_PASS" - echo "Database Name: $DB_NAME" -} >>~/leantime.creds -msg_ok "Set up Database" - -fetch_and_deploy_gh_release "leantime" "Leantime/leantime" "prebuild" "latest" "/opt/leantime" Leantime*.tar.gz - -msg_info "Setup Leantime" -chown -R www-data:www-data "/opt/leantime" -chmod -R 750 "/opt/leantime" -cat </etc/apache2/sites-enabled/000-default.conf - - ServerAdmin webmaster@localhost - DocumentRoot /opt/leantime/public - DirectoryIndex index.php index.html index.cgi index.pl index.xhtml - Options +ExecCGI - - - Options FollowSymLinks - Require all granted - AllowOverride All - - - - Require all granted - - - ErrorLog /var/log/apache2/error.log - CustomLog /var/log/apache2/access.log combined - -EOF -mv "/opt/leantime/config/sample.env" "/opt/leantime/config/.env" -sed -i -e "s|^LEAN_DB_DATABASE.*|LEAN_DB_DATABASE = '$DB_NAME'|" \ - -e "s|^LEAN_DB_USER.*|LEAN_DB_USER = '$DB_USER'|" \ - -e "s|^LEAN_DB_PASSWORD.*|LEAN_DB_PASSWORD = '$DB_PASS'|" \ - -e "s|^LEAN_SESSION_PASSWORD.*|LEAN_SESSION_PASSWORD = '$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13)'|" \ - "/opt/leantime/config/.env" -$STD a2enmod -q proxy_fcgi setenvif rewrite -$STD a2enconf -q "php8.4-fpm" -sed -i -e "s/^;extension.\(curl\|fileinfo\|gd\|intl\|ldap\|mbstring\|exif\|mysqli\|odbc\|openssl\|pdo_mysql\)/extension=\1/g" "/etc/php/8.4/apache2/php.ini" -systemctl restart apache2 -msg_ok "Setup leantime" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" diff --git a/install/npmplus-install.sh b/install/npmplus-install.sh deleted file mode 100644 index f63192c79..000000000 --- a/install/npmplus-install.sh +++ /dev/null @@ -1,124 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: MickLesk (CanbiZ) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/ZoeyVid/NPMplus - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apk add \ - tzdata \ - gawk \ - yq -msg_ok "Installed Dependencies" - -msg_info "Installing Docker & Compose" -$STD apk add docker -$STD rc-service docker start -$STD rc-update add docker default - -get_latest_release() { - curl -fsSL https://api.github.com/repos/$1/releases/latest | grep '"tag_name":' | cut -d'"' -f4 -} -DOCKER_COMPOSE_LATEST_VERSION=$(get_latest_release "docker/compose") -DOCKER_CONFIG=${DOCKER_CONFIG:-$HOME/.docker} -mkdir -p $DOCKER_CONFIG/cli-plugins -curl -fsSL https://github.com/docker/compose/releases/download/$DOCKER_COMPOSE_LATEST_VERSION/docker-compose-linux-x86_64 -o ~/.docker/cli-plugins/docker-compose -chmod +x $DOCKER_CONFIG/cli-plugins/docker-compose -msg_ok "Installed Docker & Compose" - -msg_info "Fetching NPMplus" -cd /opt -curl -fsSL "https://raw.githubusercontent.com/ZoeyVid/NPMplus/refs/heads/develop/compose.yaml" -o compose.yaml -msg_ok "Fetched NPMplus" - -attempts=0 -while true; do - read -r -p "${TAB3}Enter your TZ Identifier (e.g., Europe/Berlin): " TZ_INPUT - if validate_tz "$TZ_INPUT"; then - break - fi - msg_error "Invalid timezone! Please enter a valid TZ identifier." - - attempts=$((attempts + 1)) - if [[ "$attempts" -ge 3 ]]; then - msg_error "Maximum attempts reached. Exiting." - exit 1 - fi -done - -read -r -p "${TAB3}Enter your ACME Email: " ACME_EMAIL_INPUT - -yq -i " - .services.npmplus.environment |= - (map(select(. != \"TZ=*\" and . != \"ACME_EMAIL=*\")) + - [\"TZ=$TZ_INPUT\", \"ACME_EMAIL=$ACME_EMAIL_INPUT\"]) -" /opt/compose.yaml - -msg_info "Building and Starting NPMplus (Patience)" -$STD docker compose up -d -CONTAINER_ID="" -for i in {1..60}; do - CONTAINER_ID=$(docker ps --filter "name=npmplus" --format "{{.ID}}") - if [[ -n "$CONTAINER_ID" ]]; then - STATUS=$(docker inspect --format '{{.State.Health.Status}}' "$CONTAINER_ID" 2>/dev/null || echo "starting") - if [[ "$STATUS" == "healthy" ]]; then - msg_ok "NPMplus is running and healthy" - break - elif [[ "$STATUS" == "unhealthy" ]]; then - msg_error "NPMplus container is unhealthy! Check logs." - docker logs "$CONTAINER_ID" - exit 1 - fi - fi - sleep 2 - [[ $i -eq 60 ]] && msg_error "NPMplus container did not become healthy within 120s." && docker logs "$CONTAINER_ID" && exit 1 -done -msg_ok "Builded and started NPMplus" - -motd_ssh -customize - -msg_info "Retrieving Default Login (Patience)" -PASSWORD_FOUND=0 - -for i in {1..60}; do - PASSWORD_LINE=$( - { awk '/Creating a new user:/{print; exit}' < <(docker logs "$CONTAINER_ID" 2>&1); } || true - ) - - if [[ -n "${PASSWORD_LINE:-}" ]]; then - PASSWORD="${PASSWORD_LINE#*password: }" - printf 'username: admin@example.org\npassword: %s\n' "$PASSWORD" >/opt/.npm_pwd - msg_ok "Saved default login to /opt/.npm_pwd" - PASSWORD_FOUND=1 - break - fi - sleep 2 -done -if [[ $PASSWORD_FOUND -eq 0 ]]; then - PASSWORD_LINE=$( - timeout 30s bash -c ' - docker logs -f --since=0s --tail=0 "$1" 2>&1 | awk "/Creating a new user:/{print; exit}" - ' _ "$CONTAINER_ID" || true - ) - if [[ -n "${PASSWORD_LINE:-}" ]]; then - PASSWORD="${PASSWORD_LINE#*password: }" - printf 'username: admin@example.org\npassword: %s\n' "$PASSWORD" >/opt/.npm_pwd - msg_ok "Saved default login to /opt/.npm_pwd (live)" - PASSWORD_FOUND=1 - fi -fi - -if [[ $PASSWORD_FOUND -eq 0 ]]; then - msg_error "Could not retrieve default login after 120s." - echo -e "\nYou can manually check the container logs with:\n docker logs $CONTAINER_ID | grep 'Creating a new user:'\n" -fi diff --git a/install/tunarr-install.sh b/install/tunarr-install.sh deleted file mode 100644 index ebad1df24..000000000 --- a/install/tunarr-install.sh +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 tteck -# Author: chrisbenincasa -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://tunarr.com/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Setting Up Hardware Acceleration" -if [[ "$CTTYPE" == "0" ]]; then - $STD adduser "$(id -un)" video - $STD adduser "$(id -un)" render -fi -msg_ok "Base Hardware Acceleration Set Up" - -read -r -p "${TAB3}Do you need the intel-media-va-driver-non-free driver for HW encoding (Debian 13 only)? " prompt -if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then - msg_info "Installing Intel Hardware Acceleration (non-free)" - cat <<'EOF' >/etc/apt/sources.list.d/non-free.sources -Types: deb deb-src -URIs: http://deb.debian.org/debian -Suites: trixie -Components: non-free non-free-firmware - -Types: deb deb-src -URIs: http://deb.debian.org/debian-security -Suites: trixie-security -Components: non-free non-free-firmware - -Types: deb deb-src -URIs: http://deb.debian.org/debian -Suites: trixie-updates -Components: non-free non-free-firmware -EOF - - $STD apt update - $STD apt -y install \ - intel-media-va-driver-non-free \ - ocl-icd-libopencl1 \ - mesa-opencl-icd \ - mesa-va-drivers \ - libvpl2 \ - vainfo \ - intel-gpu-tools -else - msg_info "Installing Intel Hardware Acceleration (open packages)" - $STD apt -y install \ - va-driver-all \ - ocl-icd-libopencl1 \ - mesa-opencl-icd \ - mesa-va-drivers \ - vainfo \ - intel-gpu-tools -fi -msg_ok "Installed and Set Up Intel Hardware Acceleration" - -fetch_and_deploy_gh_release "tunarr" "chrisbenincasa/tunarr" "singlefile" "latest" "/opt/tunarr" "*linux-x64" -fetch_and_deploy_gh_release "ersatztv-ffmpeg" "ErsatzTV/ErsatzTV-ffmpeg" "prebuild" "latest" "/opt/ErsatzTV-ffmpeg" "*-linux64-gpl-7.1.tar.xz" - -msg_info "Set ErsatzTV-ffmpeg links" -chmod +x /opt/ErsatzTV-ffmpeg/bin/* -ln -sf /opt/ErsatzTV-ffmpeg/bin/ffmpeg /usr/bin/ffmpeg -ln -sf /opt/ErsatzTV-ffmpeg/bin/ffplay /usr/bin/ffplay -ln -sf /opt/ErsatzTV-ffmpeg/bin/ffprobe /usr/bin/ffprobe -msg_ok "ffmpeg links set" - -msg_info "Creating Service" -cat </etc/systemd/system/tunarr.service -[Unit] -Description=Tunarr Service -After=multi-user.target - -[Service] -Type=simple -User=root -WorkingDirectory=/opt/tunarr -ExecStart=/opt/tunarr/tunarr -Restart=always -RestartSec=30 - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now tunarr -msg_ok "Created Service" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt -y autoremove -$STD apt -y autoclean -$STD apt -y clean -msg_ok "Cleaned" From 9c46401c691a30fbfd6b664f4663a73b73303e81 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 25 Sep 2025 09:10:18 +0200 Subject: [PATCH 1158/1733] Update --- install/goaway-install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install/goaway-install.sh b/install/goaway-install.sh index c5ef869f3..9e0139c3d 100644 --- a/install/goaway-install.sh +++ b/install/goaway-install.sh @@ -27,6 +27,7 @@ After=network.target [Service] Type=simple +User=root WorkingDirectory=/opt/goaway ExecStart=/opt/goaway/goaway Restart=on-failure From 1ff54b289ae59743b203e4fbe9192d8e1a368f37 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 25 Sep 2025 09:18:09 +0200 Subject: [PATCH 1159/1733] Update GoAway --- ct/goaway.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/goaway.sh b/ct/goaway.sh index 7b7524b44..66dcb2f1a 100644 --- a/ct/goaway.sh +++ b/ct/goaway.sh @@ -11,7 +11,7 @@ var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" -var_version="${var_version:-13}" +var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" From 490815e116bd727ad18964a86f1faa9a0c28b1d1 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 25 Sep 2025 07:18:31 +0000 Subject: [PATCH 1160/1733] Update .app files --- ct/headers/alpine-caddy | 6 ------ ct/headers/globaleaks | 6 ------ ct/headers/goaway | 6 ++++++ ct/headers/joplin-server | 6 ------ ct/headers/leantime | 6 ------ ct/headers/npmplus | 6 ------ ct/headers/outline | 6 ------ ct/headers/tunarr | 6 ------ 8 files changed, 6 insertions(+), 42 deletions(-) delete mode 100644 ct/headers/alpine-caddy delete mode 100644 ct/headers/globaleaks create mode 100644 ct/headers/goaway delete mode 100644 ct/headers/joplin-server delete mode 100644 ct/headers/leantime delete mode 100644 ct/headers/npmplus delete mode 100644 ct/headers/outline delete mode 100644 ct/headers/tunarr diff --git a/ct/headers/alpine-caddy b/ct/headers/alpine-caddy deleted file mode 100644 index 75208a457..000000000 --- a/ct/headers/alpine-caddy +++ /dev/null @@ -1,6 +0,0 @@ - ___ __ _ ______ __ __ - / | / /___ (_)___ ___ / ____/___ _____/ /___/ /_ __ - / /| | / / __ \/ / __ \/ _ \______/ / / __ `/ __ / __ / / / / - / ___ |/ / /_/ / / / / / __/_____/ /___/ /_/ / /_/ / /_/ / /_/ / -/_/ |_/_/ .___/_/_/ /_/\___/ \____/\__,_/\__,_/\__,_/\__, / - /_/ /____/ diff --git a/ct/headers/globaleaks b/ct/headers/globaleaks deleted file mode 100644 index b54d1871c..000000000 --- a/ct/headers/globaleaks +++ /dev/null @@ -1,6 +0,0 @@ - ________ __ __ __ - / ____/ /___ / /_ ____ _/ / ___ ____ _/ /_______ - / / __/ / __ \/ __ \/ __ `/ / / _ \/ __ `/ //_/ ___/ -/ /_/ / / /_/ / /_/ / /_/ / /___/ __/ /_/ / ,< (__ ) -\____/_/\____/_.___/\__,_/_____/\___/\__,_/_/|_/____/ - diff --git a/ct/headers/goaway b/ct/headers/goaway new file mode 100644 index 000000000..2e07514b1 --- /dev/null +++ b/ct/headers/goaway @@ -0,0 +1,6 @@ + ______ ___ + / ____/___ / |_ ______ ___ __ + / / __/ __ \/ /| | | /| / / __ `/ / / / +/ /_/ / /_/ / ___ | |/ |/ / /_/ / /_/ / +\____/\____/_/ |_|__/|__/\__,_/\__, / + /____/ diff --git a/ct/headers/joplin-server b/ct/headers/joplin-server deleted file mode 100644 index 3d72492c3..000000000 --- a/ct/headers/joplin-server +++ /dev/null @@ -1,6 +0,0 @@ - _ ___ - (_)___ ____ / (_)___ ________ ______ _____ _____ - / / __ \/ __ \/ / / __ \______/ ___/ _ \/ ___/ | / / _ \/ ___/ - / / /_/ / /_/ / / / / / /_____(__ ) __/ / | |/ / __/ / - __/ /\____/ .___/_/_/_/ /_/ /____/\___/_/ |___/\___/_/ -/___/ /_/ diff --git a/ct/headers/leantime b/ct/headers/leantime deleted file mode 100644 index 048d615df..000000000 --- a/ct/headers/leantime +++ /dev/null @@ -1,6 +0,0 @@ - __ __ _ - / / ___ ____ _____ / /_(_)___ ___ ___ - / / / _ \/ __ `/ __ \/ __/ / __ `__ \/ _ \ - / /___/ __/ /_/ / / / / /_/ / / / / / / __/ -/_____/\___/\__,_/_/ /_/\__/_/_/ /_/ /_/\___/ - diff --git a/ct/headers/npmplus b/ct/headers/npmplus deleted file mode 100644 index 9920f2976..000000000 --- a/ct/headers/npmplus +++ /dev/null @@ -1,6 +0,0 @@ - _ ______ __ ___ __ - / | / / __ \/ |/ /___ / /_ _______ - / |/ / /_/ / /|_/ / __ \/ / / / / ___/ - / /| / ____/ / / / /_/ / / /_/ (__ ) -/_/ |_/_/ /_/ /_/ .___/_/\__,_/____/ - /_/ diff --git a/ct/headers/outline b/ct/headers/outline deleted file mode 100644 index 66e98f4d2..000000000 --- a/ct/headers/outline +++ /dev/null @@ -1,6 +0,0 @@ - ____ __ ___ - / __ \__ __/ /_/ (_)___ ___ - / / / / / / / __/ / / __ \/ _ \ -/ /_/ / /_/ / /_/ / / / / / __/ -\____/\__,_/\__/_/_/_/ /_/\___/ - diff --git a/ct/headers/tunarr b/ct/headers/tunarr deleted file mode 100644 index 27f2a281a..000000000 --- a/ct/headers/tunarr +++ /dev/null @@ -1,6 +0,0 @@ - ______ - /_ __/_ ______ ____ ___________ - / / / / / / __ \/ __ `/ ___/ ___/ - / / / /_/ / / / / /_/ / / / / -/_/ \__,_/_/ /_/\__,_/_/ /_/ - From 1f59e511b563b988c9aa54462ff0eae1bcb113b0 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 25 Sep 2025 09:36:48 +0200 Subject: [PATCH 1161/1733] Update GoAway --- install/goaway-install.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/install/goaway-install.sh b/install/goaway-install.sh index 9e0139c3d..1e021880f 100644 --- a/install/goaway-install.sh +++ b/install/goaway-install.sh @@ -30,6 +30,8 @@ Type=simple User=root WorkingDirectory=/opt/goaway ExecStart=/opt/goaway/goaway +StandardOutput=file:/var/log/goaway.log +StandardError=inherit Restart=on-failure RestartSec=5 @@ -38,7 +40,7 @@ WantedBy=multi-user.target EOF systemctl enable -q --now goaway sleep 5 -ADMIN_PASS=$(journalctl -u goaway -o cat | grep -oP 'Randomly generated admin password:\s*\K\S+' | tail -n1) +ADMIN_PASS=$(grep -oP 'Randomly generated admin password:\s*\K\S+' /var/log/goaway.log | tail -n1) { echo "GoAway Credentials" echo "Admin User: admin" From a60e2d91709ccf2a63fb2da2db9e2e5f6c6cb07c Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 25 Sep 2025 09:43:07 +0200 Subject: [PATCH 1162/1733] Update GoAway --- install/goaway-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/goaway-install.sh b/install/goaway-install.sh index 1e021880f..8119c38e8 100644 --- a/install/goaway-install.sh +++ b/install/goaway-install.sh @@ -40,7 +40,7 @@ WantedBy=multi-user.target EOF systemctl enable -q --now goaway sleep 5 -ADMIN_PASS=$(grep -oP 'Randomly generated admin password:\s*\K\S+' /var/log/goaway.log | tail -n1) +ADMIN_PASS=$(awk -F': ' '/Randomly generated admin password:/ {print $2}' /var/log/goaway.log | tail -n1) { echo "GoAway Credentials" echo "Admin User: admin" From 650b490d27e07fbb10f269fa6ab22894d8b3f66d Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 25 Sep 2025 09:47:56 +0200 Subject: [PATCH 1163/1733] Update GoAway --- install/goaway-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/goaway-install.sh b/install/goaway-install.sh index 8119c38e8..66e495642 100644 --- a/install/goaway-install.sh +++ b/install/goaway-install.sh @@ -39,7 +39,7 @@ RestartSec=5 WantedBy=multi-user.target EOF systemctl enable -q --now goaway -sleep 5 +sleep 10 ADMIN_PASS=$(awk -F': ' '/Randomly generated admin password:/ {print $2}' /var/log/goaway.log | tail -n1) { echo "GoAway Credentials" From 5816d481e27225c0b866669fc6419d684dad642e Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 25 Sep 2025 09:54:17 +0200 Subject: [PATCH 1164/1733] Update GoAway to Deb13 --- ct/goaway.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/goaway.sh b/ct/goaway.sh index 66dcb2f1a..7b7524b44 100644 --- a/ct/goaway.sh +++ b/ct/goaway.sh @@ -11,7 +11,7 @@ var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" -var_version="${var_version:-12}" +var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" From e55f5b53405b95caa51fd98c5649a867a8fa9e69 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 25 Sep 2025 09:57:26 +0200 Subject: [PATCH 1165/1733] Create ntfy.sh for container setup and updates Added a script for managing ntfy container updates and initialization. --- ct/ntfy.sh | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/ct/ntfy.sh b/ct/ntfy.sh index c2c61a728..6c3ffd592 100644 --- a/ct/ntfy.sh +++ b/ct/ntfy.sh @@ -1 +1,44 @@ -# placeholder for CI/CD to run +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://ntfy.sh/ + +APP="ntfy" +var_tags="${var_tags:-notification}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-512}" +var_disk="${var_disk:-2}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /var ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating $APP LXC" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Updated $APP LXC" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" From 9b95278682774bf9427a41e5f5327216612a3f26 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 25 Sep 2025 09:58:20 +0200 Subject: [PATCH 1166/1733] Add ntfy installation script This script installs the ntfy notification service and its dependencies, sets up the necessary repository, and performs cleanup after installation. --- install/ntfy-install.sh | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 install/ntfy-install.sh diff --git a/install/ntfy-install.sh b/install/ntfy-install.sh new file mode 100644 index 000000000..eb662f9b1 --- /dev/null +++ b/install/ntfy-install.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://ntfy.sh/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt-get install -y \ + apt-transport-https +msg_ok "Installed Dependencies" + +msg_info "Installing ntfy" +mkdir -p /etc/apt/keyrings +curl -fsSL https://archive.heckel.io/apt/pubkey.txt | gpg --dearmor -o /etc/apt/keyrings/archive.heckel.io.gpg +echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/archive.heckel.io.gpg] https://archive.heckel.io/apt debian main" >/etc/apt/sources.list.d/archive.heckel.io.list +$STD apt-get update +$STD apt-get install -y ntfy +systemctl enable -q --now ntfy +msg_ok "Installed ntfy" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 0e53991f283baac2ecee708f49a1b3fba44c550e Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 25 Sep 2025 10:18:28 +0200 Subject: [PATCH 1167/1733] Update author information and version in ntfy.sh --- ct/ntfy.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ct/ntfy.sh b/ct/ntfy.sh index 6c3ffd592..6baf046c9 100644 --- a/ct/ntfy.sh +++ b/ct/ntfy.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) +# Author: tteck (tteckster) | Co-Author: CrazyWolf13 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://ntfy.sh/ @@ -11,7 +11,7 @@ var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" var_disk="${var_disk:-2}" var_os="${var_os:-debian}" -var_version="${var_version:-12}" +var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" @@ -28,8 +28,8 @@ function update_script() { exit fi msg_info "Updating $APP LXC" - $STD apt-get update - $STD apt-get -y upgrade + $STD apt update + $STD apt -y upgrade msg_ok "Updated $APP LXC" exit } From 11d8da538bf488f06af1f0100323df8c89e60d22 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 25 Sep 2025 10:23:49 +0200 Subject: [PATCH 1168/1733] Update ntfy installation script with new repository --- install/ntfy-install.sh | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/install/ntfy-install.sh b/install/ntfy-install.sh index eb662f9b1..fb7547c3e 100644 --- a/install/ntfy-install.sh +++ b/install/ntfy-install.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) +# Author: tteck (tteckster) | Co-Author: CrazyWolf13 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://ntfy.sh/ @@ -13,17 +13,20 @@ setting_up_container network_check update_os -msg_info "Installing Dependencies" -$STD apt-get install -y \ - apt-transport-https -msg_ok "Installed Dependencies" - msg_info "Installing ntfy" mkdir -p /etc/apt/keyrings -curl -fsSL https://archive.heckel.io/apt/pubkey.txt | gpg --dearmor -o /etc/apt/keyrings/archive.heckel.io.gpg -echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/archive.heckel.io.gpg] https://archive.heckel.io/apt debian main" >/etc/apt/sources.list.d/archive.heckel.io.list -$STD apt-get update -$STD apt-get install -y ntfy +sudo curl -fsSL -o /etc/apt/keyrings/ntfy.gpg https://archive.ntfy.sh/apt/keyring.gpg + +cat <<'EOF' >/etc/apt/sources.list.d/ntfy.sources +Types: deb +URIs: https://archive.ntfy.sh/apt/ +Suites: stable +Components: main +Signed-By: /etc/apt/keyrings/ntfy.gpg +EOF + +$STD apt update +$STD apt install -y ntfy systemctl enable -q --now ntfy msg_ok "Installed ntfy" @@ -31,6 +34,6 @@ motd_ssh customize msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean +$STD apt -y autoremove +$STD apt -y autoclean msg_ok "Cleaned" From 2e003c295692d0ce5901c05530ce4359872c2c53 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 25 Sep 2025 10:37:11 +0200 Subject: [PATCH 1169/1733] Update ntfy.sh --- ct/ntfy.sh | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/ct/ntfy.sh b/ct/ntfy.sh index 6baf046c9..74f1f702b 100644 --- a/ct/ntfy.sh +++ b/ct/ntfy.sh @@ -27,6 +27,24 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi + + if [ -f /etc/apt/keyrings/archive.heckel.io.gpg ]; then + msg_info "Correcting old Ntfy Repository" + rm -f /etc/apt/keyrings/archive.heckel.io.gpg + rm -f /etc/apt/sources.list.d/archive.heckel.io.list + rm -f /etc/apt/sources.list.d/archive.heckel.io.list.bak + rm -f /etc/apt/sources.list.d/archive.heckel.io.sources + sudo curl -fsSL -o /etc/apt/keyrings/ntfy.gpg https://archive.ntfy.sh/apt/keyring.gpg + cat <<'EOF' >/etc/apt/sources.list.d/ntfy.sources +Types: deb +URIs: https://archive.ntfy.sh/apt/ +Suites: stable +Components: main +Signed-By: /etc/apt/keyrings/ntfy.gpg +EOF + msg_ok "Corrected old Ntfy Repository" + fi + msg_info "Updating $APP LXC" $STD apt update $STD apt -y upgrade From aa643268c977e956777eb7f3f06ae8f61fd7fdc3 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 25 Sep 2025 10:56:08 +0200 Subject: [PATCH 1170/1733] Add GoAway json --- frontend/public/json/goaway.json | 40 ++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 frontend/public/json/goaway.json diff --git a/frontend/public/json/goaway.json b/frontend/public/json/goaway.json new file mode 100644 index 000000000..599c4cd0a --- /dev/null +++ b/frontend/public/json/goaway.json @@ -0,0 +1,40 @@ +{ + "name": "GoAway", + "slug": "goaway", + "categories": [ + 5 + ], + "date_created": "2025-09-12", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 8080, + "documentation": "https://github.com/pommee/goaway#configuration-file", + "config_path": "/opt/goaway/config/settings.yaml", + "website": "https://github.com/pommee/goaway", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/goaway.webp", + "description": "Very good looking new alternative to Pi-Hole and Adguard Home. Been trying it out in Docker and thought it would be a great addition to the community scripts.", + "install_methods": [ + { + "type": "default", + "script": "ct/goaway.sh", + "resources": { + "cpu": 1, + "ram": 512, + "hdd": 4, + "os": "Debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Type `cat ~/goaway.creds` to see login credentials.", + "type": "info" + } + ] +} From f422c1be3ebafda2f8a50e8eb0f6c8c35f835c54 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 25 Sep 2025 11:05:52 +0200 Subject: [PATCH 1171/1733] Update GoAway --- install/goaway-install.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/install/goaway-install.sh b/install/goaway-install.sh index 66e495642..817e57544 100644 --- a/install/goaway-install.sh +++ b/install/goaway-install.sh @@ -52,6 +52,7 @@ motd_ssh customize msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean +$STD apt -y autoremove +$STD apt -y autoclean +$STD apt -y clean msg_ok "Cleaned" From 49ca9926cc6be27bee4d88fbe3d35ec629dc6138 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 25 Sep 2025 14:14:58 +0200 Subject: [PATCH 1172/1733] Create newtemplateavailable.png --- misc/core_ref/newtemplateavailable.png | Bin 0 -> 41282 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 misc/core_ref/newtemplateavailable.png diff --git a/misc/core_ref/newtemplateavailable.png b/misc/core_ref/newtemplateavailable.png new file mode 100644 index 0000000000000000000000000000000000000000..14ca28639d712d1182d57a74f9db22e64ce7853e GIT binary patch literal 41282 zcmd?RcTiL97w-!yAS&=8DoRz6E+7a|x~PD3>AeVq77`#d=^!d99Re7tN|Rob0HKP~ zLQzUW2t}m^2uMjNp`7^ozUTLwxija^+?jjMA15;qo@ChB``OQ0&syKlT6z6gUz6nw z*BLrGIu@-*4~^*Pm{4?d^k+^p(q0J)_;!-^MDJ^)d7rLwkcUVcoN`suQ=_A+Nj!V- zgn>3@_I_mPOGkG}@b?eBmeGx0v=`6&sayCNd%5@pK7j)1G@(Es#MjNs&(8ET?O)zC zYCTjld1^;G4D+)Y%UKGF1xYzd-E)$XYRp;TY@GM;EO-m`ZvzYpy@7dW8mxh0`zrQ%oSg>}=BgLU{q-KJMJf93h%NcjtAj05f zE)!*PLtzIu2PR`&!-o`#mrZgMsLi+%BdsHkiXr|9>Z&gV@0w>U)ix25k(QRmOgr;` zAA>lDuCd{AizN0@W6Dv=HR^bH~yq5avaAkgo=(Cv#^NgK&_ z?j`kjfLP&lm7?ZDAUESb9yc2em6MYQ)g17 zn+cO9)3oqnIQW|&KZB`N$IMdN@SbTdPqG#qRu@wqq<4Pk5KEX%l={-L-(DgA|Z|@O)C|I#p22my?sRF?A+VzwN+Rh2mP~1t7@pT zjgSx`H|;1ZJL%i57Tz+*NJB_?&QHg~dMGlYD0{%?O_4D(#XTZFhZrh&ui!9Vr@6N& zZ}L02rB&?5#dR0ZOC*sn;P&4$@5V9nQp_lE~MOi z8KxASDAmlCoz7eZz8W*A-#3SUpxva#c3J)ukFo0g%cD-pIDavwc*!7qwj5 zUkuaFF-^@>dE+Z-9O!jv@t;K!nZgXIvPG!#H7^bqNX*i=p8ETSLXKsQFz~XFKOMWy}}bk*9CIYwC&Pq zQ@{+-bSOT05M6VmkkxF`-U+_oy8d=*iTd#_6A|jXALScSy3WUP;nb*mN61jP)zecb z0W;!;=QlCpx9Po&rNEaZ%eb^0nGYh3Nw{kd(hhu+k# zAxK6{>sLt$ww)g1#G^;705ZmW=Ln&6*uz#BgfR(KGZ(7ao$&JxhJQ}~xQB?2o6J=y zlH9fCG5&3%W$I=^L)uv>t_!ng*PHCFq)HPj>UyJ96%i#B-32p97Gt&A8oQXQ77F7R zh$1*1J~ai~Qq>^bwZMIsHY$!nx-==p_>91=lUNWCiX3Ou$)B5AFKV&}<>S;f3P#-b zH=q3qU*MRO%q~ISiS-TtbK>nVCY0BqO}^arg(vm_ltTV z+*>TiodlE#06A!sm?6I=H+;*@F2;s%Eatu{l3~lN6ZUe!6=OP-KIA8n=E~8<#$?p3 zm~L?m-L#;sju@!9&Mv!aEm3E%-Lv`QdHF!OYYr^)hg!+qnkg4*xn+fI?NgEDIzGyT z^L?`~iq86=JQMq7k+P7?Is!i_xiI=ctpdEh{Cx$ll;l|}ZkW@@XSwzdmyPlnzxaZO z{<41>xFC5qi}~Cbzjj3+s34J|9V_Q%47b7o>WV6k?uc^a7Z;)hi}=K6pf67z%A}W! zTgtn0J43>!nyW5(t%>W`$>%595?7QNhW#Z&3J zJsE**SwR4vR|=$@P?efJe*2>~ZOPO*h+ zR$!Rm=qn*n1y6{W^>MFFA4^0C~viO9f(I!sZj5sdkWQu zI`Yjccc4#KKCU*l*k6*csDO#TTbAU!%{9{n zx@u!@^qPWuJtN#-uGY!Ebah{C$H|?31=}j$CvWCwYWeI3-*5*BPZgVcXJlSBRtwHc z3}C%^?-wdK&*Yr2o|=oPXM^T~#mNmFt^%dp$4Eq4$%6_QTNA%dU6T~|ITYvjgAXv) z%mxNs<58*RmHJn4Rqu?&8uA%X#vrVIk6T2R?3jCZxdB5!KYdEyC`CqE><}zrl*fAa z^D>*(4pI8sr&IY;fT-%~dEM=MnZzN9s~45mzr9}@Nx{9o#JzK-`Q;*S0mkc$d*b08 zcY2KsVpi;pyzfXmGwZrW>f@q_bW^OBv2|N3rIzbLf4;prU()Vwfo{T=!U|Evfw05( z5P3y_XzHwwd`IM7Uce$eg;;=tikgI5SS#9LQC5TKQ_kzZ<& zTK}cn84A_iCp{a~<~=~fUMkDItSG)Q_@K5<<)TexxemY1Sql2V;uG=8PZ5)liH(SV ze8;_9*z*q!brh{2=N0kIdCemuRuzi*=H#quYL#-h2CM1;_uhWk@r?o0@k3E#6aRTY zloQy*H*IyfQNMI~GP+u=Sa2qtS)+UI`@P1SWqONagURHRMRKhiXVYpMyJLGd{8|!O z3UOz=L^8TQD+GyJ3UVXMfWD)H=`8T}oilic(FeYM@De@@@PTg=OKHVQlJ&B?Rog+~ zoHjn-r$;09G%L;{zcHg&^DW4%x5du?T))>ZT>aFSf^r_W40(i(Q4nIlNaa>GuPqoq zB)8$2)Z|jFvjC|Ymy0|ZRqdV2?4MGZHw!P@ zHyQNIeSYP)R)31kkz8xa<_9{)X`%Simh|GA>I~h{6!19olKPdB<#rJ135jQW?{#-U zo85ro1^x^liBGe_)fe7qCP_ZdtDdbswrJs5Rw=X3==}!iUa`IzKKM=LR+>T^&F8bj zf{*L%(7z7%=Xwfs?^cf3qIf7I3%sx}qs6S${A;Q9lllpz1cc1fl*7N@05{(IW z{kfQ_=sOZGigcq^h>;&2%w(019Aul)PA``P4f65vGD z+OO$L-aSLuYY)*DONr17e(`bD2iecT28!RZ`Ttn?_vnxTGwX;fof?r= z+`N_Pn38DGh8zi$t|MUHZ-H+2tCF0m)X}pxbN_R|6QicQl#!^R}B2`nXT>@H$jm)NApDmt`1V_q>v8pMgiad)6u2q52V+Sk8wg+C2~Nd(kK zPBvhxrUnJPM&8?VQ$ZM$(Ml<@rOHS%3cy$=K7D%h0( z<<;fCvJv5C`a^$NzbB07%l;dN?7q8kit;NbLMQ}v{Gxe$zt!)E_$qWUD?*$NjLbVi zKAF8U_l;_wc9?i{FE?nlYJRUfLek!gFQt<$>}rMepw28<)u>Mw@dg`ID^Xct0JalX z;Qnljd?>+jqoKv;5zA=;{>iC{ffWn!?y~f*dzy1<)s7h}xau4t-X-s*ei4d$d3dC9?RJp> zf6AVuKzY^Lm304fIh5Rl!jw*oFOG;UmrKZJAjGdYV21Y8r7liVPCs0tdF(@`P}&^} z5HpS)3O@A|Q&3JS2lRRrO+Ue;rI>gUj^J5kcKThKf@tT}=W#hIqKXJQ2ajp!dcbMH z#H$X$%RsW1(I7HtUDa&nI{46vO}3&?QJrV#+h>Veh^;yID;Uf5KqH|pjT_-cIWl)W z+onr2mzr^QxPWz&p`TiM-Rl<~kDh?0CJ21Z|30~XE&$av%csCO^pvDwo|Aj?UX^bxv!k(HdV z@vW?s-GaEhL6)B{NjPkKZt6So&C_AZ?inFQ)UlYj9xYI?bLci&bRBnFynz>KPr&sa zk-r49?KXOm`bt)p9r9bP?RP>PSL^-w`*KR&hc8|uj>*_2aLi4go5>9b&HcwiC8-Kt zs}}Y;ZfqXEB^1#d@-Sg_h{b!R?i#u92PI^pfEb&z={(Ce5AP1oKI5rM34}gf^qSur zV8;8eWc7s?>w+6s$27ErGkSxIlNGZzVRffHM{&Oy3= z;@bmUY#{<|0qOmB^rHBPJ*1vu?j4N5ufV{7tRH+?p`8IGmj|bClk@`jcv1Licu>yT z_~SeqSG!^~1p)_Wo@lucO(-d`It)$D8RDF#bTC;z(d3JfFc)Jj)Eu3cJA`^@Pi-9K z0D=sEu@{UKtF!GmG22sjB*~?< znukySnNPeN${N;uzebUhKlIdpEz!|0yp1`T@ead4Juv97{2A>%R57pWzwYGWeXY~0 z(+H`)nt1-+SnJi3lOg;EzWA+n(54A({=^dxEm)O(v|C#j%Xy|Z6ob~LXL=u?_7Bci zT3B(q7A>oc&)@p8Lpg(U@Je#uYi#(&_ZB^Ni+eA zs4E+T=B-(#iMpZ}keI!T>22TFS95>(-I5nIB~kLfyCB$HirDO@>Xg%RExE0&%LuJD z+Bq;bq4}=4cd#@B2I}M-5FG4VW@)PtrhU7?7S7cq;nYG)11m#Ye<9ga#)onA6f4d# zjd7^pC-JX8G+U(Qz;^V|JK@j#h;kt6m?+X0PZn0CCKgp}I-auTCCFcV#Y3Vr6`Pbq znz;fbJlk^hL6CJ_+*l^$D{sH~b&C3}2B zxcYEaxh;*!D@YHIgMq607~Q41=J2~pC8auvkjh-aJe8WqtXwPh_BMh$kU)Z_u9ZS< z#}eMZuCO%hef^DkGTNyYbtookEn^-4q;RH2hvBqdNINzMAV2S$x!}HFPhf&7H*ze| ziZ!x|9_Zk7lbnx9T|Xv?eZECkRhw>dI7Eon*Z^d?fyVy*bHu%(Z}alTsol;ILzAm} z?j74N<_QVN7a>uLe z`t1i{vIxQ}ui43)0_(IWDODg<=0}5>e~TYzTRevyobkPIaBdyf zm6-JDz~QNfGzwT6t!seY+sH7i*mZtGU9^L)cu z2BLSi+2+oF<&uV0yI3;)T z2s?mXZUoySClMq}SZ>}mA#_4fA1#C069>T+4ktKt=3n`93-^98asm(0xdb|Enuq9U zn`%BlhE6eCW-^-)tcw})E21)+@@vQ6R!%;+*;0fGy3nAiWf>|vv=wXOv2U_2s<*ZG ziJ-Fkec>z7C%IQzoMrz0;%RMMAxToN#E z!K&%u@mNu?9&u~TZk)DPY~XiHaJW;;9dW|lE%pMPvVY~MoogkD^9!a} z-lVnUjnRoJiK88R+^DE4!jX?Y`@^HEVnvl<38rNulAR^)2KaEo*6~;%TGtyK`f#6M z1)Wd_rG`r=C_*A2@Av)Z>0c)r^vzk=Ok~!o0m8!8Yqa1zi5T}p_uh)bFT{yCJ+C3= zE2IGmXN9t&L1J&ggRHlfEAk`z{>3Vv!yhW}Zk`pGyFKITZOzeaPZ;~XSpgc9eXJCy zejs9(E$CD9Z~8IIRhwjBj7k?b{{lyKCTU z*pr@>AP&@dS+hX#esL2dnE{S2o41hReyWmNp6oh%wxG15s9YT!n#LoY7AD%LVHJ~( zsmxt*h7SZ!=V3MdtEA(f*@<_bH?6I(wZ$oVdRyP7>9X#!8rrT2b=nllKx(PvR*%0P zprHcPputrS=$)%doejpH!Glh1gRu-!Wvy1&9f=HAvf)b6p)hW#8}eET7B9Te!;_Uv zRGZ^Js7Nm(Pz%J8VTd9U_68&H2^BK`6br?~aP)29gg9zFstx+G^j17*ijXN<(V6J| z$uB$hyv{kyW-BWkWaUfv_`sb3g`sV8fcGUmkBYgHZWp=x?{kv^&Rj1%!I~SfAo^Cq zQKDr(W49|(m*i!1HZo_B@dD|&M6r0^Bh#}Imu07=6f1~t6y$@jp?0Y<(kf!bXlqZT z|JPA&hv%T;g09%6WBCNY6kg~szY(WK&C0%PUa21JP})87JT7rtTgUKZsHCIBu%b*Z zS5(QdUf*Xo8~wHVbtgrt@ruJ8-lg;r0Vf%0_ofu9vFWG2KPvd_yRQ?uqMSNebc@l&-YsBYzTf_AaqmFSu9ibP$& zar}->YOZT|MK7N?H9AJ?iaeYJ0=g|c%D&mCl5hU?mX{NxssCQRn-6`B08m<7?Gx37okS8MFLMIY1CeaSepoS@YP zYH$6^#Vl!rXA-&4;pt)<_~*bdS;1@P2T;w$Oby3=TW!68NV^nHRQL_1^#jCY$krVg zSo*=uMh&i!l_35^`}_qb4ej{pTv#1ATqA&+D| zv8NO78gQ`EiF_C|OzvfuC&~hY)ZEnuCshe0E5*#u2$GpUQxu7c9pFD7aN$MDUnS(W z8$1&V*SqmBO_nF8(36dh z?p6iQT?r2P{FHbaXsL&s{d3rNZ~wOftiHavb|N!7JZXe!ROz1!%SoZ@x6x#}5wP)L z(#>cLQpa0lfi|(MO!cr3%dD6mUQW>d;&319g4F94X+Y-|VVWv8FiZf)ty0S~Ln zNOGj&U6Nn36Pqok02dOo+<$J5Sd8!yis+aV?cyV--5&d#KR19=TMW5JZQpjwa{f(QLHzq!ErxBnXU z-sZxmHy9?crHcdo2F@0eKj^3wF?NvLuP1&Futl2)RuGB=nF=RGAM+w@K8qP7&=i$E z`~$3#aZ)di-`>d7)xO#rf5T<5%C^QRZ8`wCkxTo+@))SkJvA^36=! zs@%@2!`Hersqmm+H9O^9)_Y^-)_)+;uMhtd5(UM_-Y5;XEzbEmXX(DeOXi(axxhb? z_r%E+J^vM}AgFrYswEVw$qq`kQ~p&sCUcn4$gkhmGxt0jq6N;{2{XY9xe6nMJdL-` z=%0m9BX~bf1{wXd%IlSHF?^jjDVo;RI!uyD(@)Uto9odMZ29Wq?5(I-ee?%`-ugnr zZgu}3fRb@}*e#BR8x6K^YH8YRt4mg>u)j=1&*?{Oz0Ve`ln+Z6BrTI!ZK`;eUscRIFTRVUSe2=WuFc> z{InOk%tr28dfqeVh8nfre50qTnRpLaaw@(B_E1@11sAj3zUmg3v+e+0w#rRQRTNKa zezaGfXnNRNC!-=S>8h9o9kDhN7!W2$2G;Kw8ZW<>JS2PAJ;5t;td0dHyy9!(jn{EC zrZ6$5`7;$nc+#kwCUA1G^-RT$wVErv5fOjDvk1NRCzu7=BbKIS<^7#%XYdCZ=LR*K z=R(3CP$uo;B2(i3SN!RuG<1z895dyS%KGcb0JEiu{-J}l8z;Y1R9ls zL|x;zzsE7j+AIIc^r<459}q`gOz@L1@wPUfk#fpCtoU^r!kMY5>iu{|RY-;}Lx>c? z-{A~~?Gzy6M?@Ax_C6+F-?H&rTPIJ*KMdnH4d{CXI5ROHBk|(Nj_zYqE)rCvXzq%( z`zX|X+Ua&w+doU;@pYP7i{7Xs%)gha*THluZ;s}xrbO!{l2JA45+Ht-1${Zu;$uYGfU67PYL|-1vd4|1?I6yZpL)t zK`yjgU%8SdeI<4Jx?Z7*@6%H?XWWYc3_EYPtt&0nq6t7?a9&oXXlmW>s|!?pyfor# z(1%C*wPSC;Esx&>fbHFK$u-)__AR)dgE{F+U(komYXV>*IGLSzQ{%g8=NjC>RBFQO z^5k;mh`n(+Nk&e4i!Y1$V4L%A2N#+0{>Fu|iaZc31{G?4~Ql5#15j zhE%rC29^E@3))5E(qz6QG201qVq85p7we@)a6eRB?2JQtakn;|7YZ-phaR$A*iwliBVLLVAf3wLv1oIBUHm$qtU8Yyls zWLYbduyb4^!9Tk;()+rwmdhJ5-7Okii~}o4AgaA!Jnww1_>+>MuLquu%lJ)rW#Ojh zr`;d-mY;T?7k4(t*_3=l%A2W_24b3{u33VuG=pqqu9Y!{%r)g+Tl^kC4uBIbW|+P% zDA)1IkDstjy`1vJwnB#R(e*)0C)a>h$+Fuca9x#w$IZO}cT?U0qv<;zv2xRbzT7tj zUhBCa0(yVGe>kY{6GVpc$`m6EN~P>zW6DR-oi4a@H++{HT-86rLoWhjkRtyvaifig$yoP4}&A3Nq{||k;>aagKhQ4l8*i*-y(iCrn|k8dZ*~inJ{qb=`vd(c3y5+Go#Nn^dFLD3A@)4_*dEE<>7qiK&xSEeS?oH7zIXDcVR@a z`~Wqy`5E*}l9xBSkXj$7|2BAH?s*(d&)BMu6DPsaHIF@syvaUWMoS#CGAX$yCM$BA z+=r{kAV)jC4)M34(9g=M9(LrK;RvPk0WAURl&z*jF>=CtQvRckh@3>+nJ`;wH7Vsh zh?th_Lq#;vc90@BoMKxxX)S$Zzv-7egWd=+%fhac=Qs||SS}W*ZN2V(Gh zZRA~h4s!Jx>B|0r4EfxKP2VC}IVSO6qg0?7CEjmK1s^tpsrA~~ng5;}<>x@nfIh`} z&Hcg};(bHfZdz4aiJG6oC*$t))x1Bm7bh*zm=*erFy>qEe|Bi_D6Lvu3c>JUM!;d-w#lBFq=&TfS6Ud#Y2pAevCOW7?xuSZP)UB&hI{Ry)h{ zPu5PHSMqmch1esiG|eU$%LiTpf3z+?iYBa`uibvQX7UC2H$Nmr%jFtK2KO%k zK=Y(%LQT|Y&te&Mw5iJfG(_>WXw*`GW$vIa7b!=1&EeGi^>o(oII=>-x7-im^tQ$7 zW;Qq?px~M8M8-@B!d2M0zD+N4x!ws|d_s2No(Iq)1b#W~2zjX_v2fUlOv?uA0SR74 z8t9d=+2<0jDVdCSsEd~@-Xzdg!mWb@F}*q#2w~$czjF1wOb#@8j$;yw@c@Yg^7n1m z)%Gd4d#vDa_mSY@>chIt$j{xvK{Jo>GL1+CEZeNy= zeluFMsa7#`b(AC4n3Z2R#= zBaL=k;ES1mbR!XF?fZt4mec~JTJpucO@RneCVr}T(Aq{`FOE-DAcp@aKJ>6PB4vRP z@c|}U+62vNuK|=cY6p;QgiZa|)W+Y9qWJe_UzM-aLv4ap^cxM z{WABoX}bI6FzCe{n2wWUPBW}Lg@rIyEAQcod8zw7w zJrZQu3i~4?XrZT~55fj?iU>k{o5YL=^vjja*;Oe7yX(hWV55_JpIAP13{j}EazRl< z=!v@Cip7&O_fli+0$eVMU*MBvD2xHB73CHsw{gyTg!NH?MTkK-Wg@hBTO|X@-C+u{ zMZEI~IJHCLb>AEQ;dRO`G+rl7;g)I0ab%SdcHa2LC|b6Xk_NB$yBg8UZ0R!E&2trb zK=-c6D=$~2*!8aBgp$0-7VI0bvD+4#Jnc#jXoOtlzC6UD5n=#@@E5RJOIjV_WKG*5 zDI7ey(ydB)NxifPcG8`>>+>?O>`1+V5|QJVsK2b{D|a_J#j;G!m4#3}f3v9fH13f} zDQ?uXwS)AB=%LnWVa=?+*d^6NoXWd@h+cfKY8}#e_bBLh4G<4buhM{IPBmV3mtooW zC*ZZ@PA{XdtfsDI7@NYfJI22}7BT9; z!naR!8swRf`WiMW7#uqg3Gw&I(0|EnNx}ZdbJaqh-#S2t3@vWC}@0ODK>lE#5L-gC_E>2EMUhLGUmXpNc(?;H&9DcwH z-!4f`P3Go?DXXp>BpHQr5eP3retH~uw&tkg&fwg-5(bosEj8MiBQOahpG zv|LuR8QK00Z}$R7goI|4v{cZqU8`zGx}a}OE^ zsO^&#>pxf&zxO0`k8~;N(TJu%aKyK;N3r8?HREX_{&}8f7B7nQh&;~Q&h6ikvomwF za7e>OpFx!#P#kGSz?GKF!!lS+tWnCgA5{!X)QU|JsLOitPTRhlM&ct^bGOu`UI2 zd^V_t97a4CVc#3Pb8N;p;n_xU=Y}jM z;9h)|UOxUq)vYd10|rP3&j|STD%(sQZ|gcVrrs{l3i@ba9m!Z3)_3r>{Vk)dlX;a5 zMQyoy{zKXs2$ebXqy0T9+hgZq3DYg;UT!6lh8ivZpvISUYVD^B9SM{wIPwG|iZm!} zHj~0-SoY<? zjk_E9=Y};B6_kGS14L2aYWIXM#vm{LC0U7wY7wmM^Ea)$LXb!09aG(2GN7#M4L>^# zuUvVE04Y`lH!h-;&+>e+D%e?l`<9<$-!wP-E29E+GB2d%z^i!S_s!ls-iH`e{0Wus zF&m#h5#l+rGcV58ETx%yP}n?7S<#K89;qH^32KgO>}Gr0Mve6?H=*u*`x_}P)Jeo zSimBvCp*?qbL*Ao%t21HgmL?Lb2{<^ExB|aLm3yH-VwO-)t&Wi)zTvaj_~WbR}dAa z^auP&-prc-SKJbI*-GWDO1RxfJQc{fi_qCR4Y8ikG@jj@J!t@6ab<;s;5u4KpJ>)t zolI!7zZYZaYdwG|ta`sf{RdHUt)7s6k)B!kbgn>dO8ry^>0DSC5X@bbT>N5Ixc!ps zyTYnyWfjrcV&7tRHiGJ5!rD2jp`X9LvB}2Nx8q;=O)%UHV?cF}vCmvt_AN_-t;`!9 z-$V!RyQvOQKgvcEjtF1wuZxEd3i?me)atC^=H>ZZ{$0MZ^-JKc_185c!(k$UFJty| zVhYSL+X&U2;Jq3B0XC{l-~2IoD%VQtPwH~Mh{o;bNU9l}++A~tG&wigDHDOqF)7Q01)Rlt3n!N zj5GJM#J&gI))T0Vn*`|H;d|viMdXeNaSj;V#y^l5n#j=Y0HX{S@b@4_mr&`wlk`q4 zU^KZU--WXWspHG`*N_K)zrH%;!>NPATAEA=N-o~Xxg9+HHS>yX8&)PKWE^VP=Q;pB zJ`uWCq5juF+>N?3)|EGQPdlm)oIa0dxRCSk%A|f(&8k#`@#X!G>WOsLrPlKCX96D6 z3UaqCL-pvplMc7uTyCt;yY_w7tKL|m?6|#)+z1=;6~8M;S6XGxv)26}oG^5#(pT9y z^N)lrSgL|VbF(2!p695OJZXfR}11A-&&6x6AC#70-uiVaTDx$`Ex_q|)Ae^!IToU5mD5H4gQv@@AnhG6T%F%k{muQ_};ev$g~~ z!EONsJ0Uvo?UqKDaGbVW1K3s(Z6&co%r%8Wkst^<+ul!iBw8_{R*oqLjGb(K3o8vM z{${?`pP!mRhGV;sXYCf_F7c-@^i2#~wKrQ{0v4S&V5%F)A5@%NwiKR^)(V?ih7L)+ z%Nb{=B@Jgs6B?sPPb|lC71e{@((U+b+?wF}uF5sUQ&u{)Is39-WWV61qKErR_55`c zIM=?2QwtyH?E;y>FH)+FOpf-&RgeWe8&keZy}BrP9Cb1ERW#xK@?V0=zm_h2zjMOb zZs(=gm`M)4zWQ)W+tQct8h3i8Z&Z%!H)RVBuZruX(X}GZhaSpH8@u|(R;dNydGGX1 z^Dt|B%T-kNiJrEz}T8*JK8xyO?)6^IN@^H@WPeD!PjTIz}(wF8wa+4?(TA6W7 z^4#oH$$*}E8ioQI|6@JFh5y?TY9H$VAB4K&$Xe9(lBpon9zYj5M&YRS?zz0@dFu8E z(D&KzSolBeihugdaGs7{*U|6N0(%a zPmGz%Wt~`lV4E(p2a8Qw|E{TGe~*c}iPnl`bf=fdh_ow|z77Q)%|m&XGNhftMqk%K zhu8StWVc;4WA#-Q)+{9WSh*-Fj?mvh@t2Ihl4&4agx!1-OvEvH5q2>9qhEF8-H8VK zZXT-ixNzU3+o8uw@-m_vuD-?cL)uKo&CduUh4$7#3@F4(f$hVrm4a_4UJZgdS5=0i zYiKo;7mT-LgR6}QF@<0!a_-x?C*_@!R#MQJ<@Sai%cQf$+@Lc>W1I81L{ zSO0bVKF&X}uV5w2M5@Ip6|jRaZ<*X8vu{e)iF{rjSWIq`)E^q(A7l?}@xz4xI$P|H zP<2DUUHcizer0XdKU?i97FPodxN~a(;rH>7L~i3L)8 z!}*(vZ~Nf~7=m>?myj7XT0dQfehJJ;$%6qWKDy_T&eu_WqKYbQOLe2Rcp&-XwD4H> zHx@3@+S)szu$wUxqn{Y7o6w7&2Dtyu?ZsD|K{{F6xxA^gMvj_>rnc(&b0zK*brF?O zg*~+Wz|vv`jz*R_jh6aaU~?N z@I+;?a*-g4!dVRBkk|?T9!ui`LhY*IW(rwGV6HM8Ru=ngfTHFVjj*M8sPSvBLAU^@ zjmAEqKy=DpraOgMK4e(sv30~|d7ME42EX<|{!l%i3}0g>y}X?9G**771@yWyR+47W@u&shpj1tr~l?AfGRhi6Kp(nGq zGS^^-yiehx56klJz?4>{lfy9cmSLk!?#I1y`!&`-&jM#(%gc1TI0s)gCqXh^M0XlF zow2CY5pp(d)>#Mu;wJUA7*OQeF?3QK5KCGIhC9HdU6u+NsW28ON=JL(*SnjZs8iI6ze$F2Y53cIifl{X7lS_i4BxW^RqJ3)=gCk5 zy*SJxuh?8yTAfe`v}qfZaX6l9uF;Ymm)Q5P$Z!Prb*1&R4?oFsC4(lFl6}{uUj9w* zGDD`w!yL1Hh{QP+Z3A;vA^_TQ^#r51_{!Y9b;~OlJEaOkqs4_=+IIOD2I#Gjy7Aj|Y zlq0+!iblh)-L?vI%S~ZA>+4NWI#jH5AWC(22lE!&Fwp7pmqaeS-KL%%*Nk-#eD<#G z@9-|tU0v(d!!3z*BK?}`)PdJ#PDRGtmkR#g4#fzGo#_J>pmqOWwCu$YrAyY{D*F;{x5|S_MsI9JdsY zgoQgiXTo1Q$j0Y4o;FnY2T$Jui!di!9aGsb1EwJ`KVAEaw&Hpqf9r}=T4RLvmA9)a zb+(BU)wN}668|u2+Qr`ce^BbP&j^1{!tY=AF8?2Dm#n1QQY@Vt|H3^lSoZlo&>E(N z|3?4Xwprv(rU`=Jx!B#RQu5lv9tec`+h4ECqd`c)h$eI?1l3PkN-ccA{F-G)bg$H- zvB~M4*^QK1z(|*trSiA{PWEOEo-ay0NcDmd`?6*2VO^s^sl?)%TeYo3Uh#L5O#F)_ z*E%co?-G+?rY0Dv+{K>-o{*aKVqh6?Pa74dy*A^vGXNl@X%+iF_?w`Uipst&;x1%t z+?xM0ZI9h(q;P+y&0Jz<%I6{9{wEZWV5rGAq3NMdBjRkol<1Supuy|}Z z&Gy3NU9f)akvu~O=_^TQih<}#+oFGX8?`XHkARuab!Emu z=5m2Oo6l?V)I_Tg={a0@exY+>K_mBJI}fg@TMfAntG47yu_)Jy<+Qmt#SY-S$)$pfUiXr?K(b;e8CJcpe`D{hBr~pamTS( zLz+&9gg?Rjfwb@FXhgdvVC+)RuUysSF7QmMl=|rKy9LtBq|B@W`)m-vdB1 zV{lpY_w{neEt0V;p0ciSl`TT12C+C?-q(J(&c2KmgQvW0uyJgt;=~- zczPorC#?iEsegvV^BHMMkTfNRx#H~!p?d$ztN4F|vBMRf_HL(8V+S8;=k9yf?v%PW z0kx^duU6X+8|pNA!u?B{A>)mTa(PS1+2Dsa`l#=d&e#kQajM+TQS49kS;yxzJI&Gk zHzW@|*CM4o?9V=8I_J5lB%V_HwA~^w>pR8omOtqV;4(LI(vO{T@G1R|$GAr?!mRGU zvdA_6Lt-XPb%I>PGdUtPL^>=9C2iRE)Uh4j&-Hj}&(})wFVuZM&pTO}E0IUE5*rf2 z7H!Fa<}DmwtS)DYUnG{effyZ&4rpy&U!nLsn~bA24GP;$Sl16O`P@O(T#IXjgu#^B zu-Lg*CD|?!-8_~v=7JSkBhsmIo1URjCUPGa-GG7N0hP*D*W#na3*;KRYF&1Iqu9SY z1p8nTjT!;KK}@Ij3#)0@5RIZ>R6qCSl(8a&9!~IgJ(*_ZiuAKH9=|@62c!E*Z!o0~ zjj)BNZztG6=O5HIl*!#@@OdfUFf}H2b~T|y&zkU#@xDvoIWbW4veL6hbqSQ7VKvfS zEuFoB(R1BEwKCvmw+9*J{dmZuOFX3H-v`U=tK?fvk@sKT_;6k;sb5wmVBlU^COxS#4pq^Q?^fZ#3Cap!XHf z?SV(GiwV*|BLeJAu#K0^t!7rp8qdkgAxGQ!+X>T&^kS}9E}}O-W}kr%(g+Z9XVHwNQWV3ubOD!nWP2L z^{en8Z3pn&tV$O6(?`W?7#XVn6IQ$DticzK_g|p-?~fW+m4#K8P1eM7)QW`hc8=Xv z5G1Jb+Ki>Ke3DcwUC^tdmCGR)+nS#23ke=nO>J$5#&NhiQW&TS%7vcZ2X)k@k-1hITKBKO} zHZA4!zT-W0V-bBEsIyNgPhT>N(n&~Z^kA6wE7;L0gyq{RLA~;F9n58=`_pK zrxcAOT<&n%+89=w?xM)cxN3Q{U97lRtIBb?G94^w|BR}jdIwPld?56zfXQVi6Ar=LA>ENQMW|Iw2yOe( z_A&;K7XH-%PIXkyI2W}Rn6()$pOZn!f3XUxp*Yn}-UIh4EuK8f9Y;2=tPC;v`x^(e z<)jMjXq9@vpW9KB+f9%wIoYspYr$DE?&L!mmpf2``r@lsaoHxrrbmMY7m+-FB7-yc zXwYZpQaOyKYF;-`rMy%^RTIy_j%SqDZ&Xxpxk8(3TMQkzCar$b{?RqR-8=bFx16VzS$ z#H6JEj&FGGMA@)^Nlleb`}crx%DG~*1>gx4nz+37)So0zA7}E*5pCj(t&GR`2h7VZ zwD&xY$y}&s1(9h8muJKWJbC&Vfy=nvIeC6RC`?{eIMs_a9j=)lTr*R3%`eHb7Xnv> zvG7!KBgOe!bK$|Zx;>kfW{pi9E?iEWb|kJx1vshI!|%~L$}EinH&bhCyJe@m5>8-m zM}DFVy@D4tAdjWBcc~p>e&~lC$UI;)0sam_t4^Oht#5@>9jg0ngpN%+5Idn0vcbXJ&O$ym6 zl@p06W|W%AkA*9j4BkG(sXJ5p`dN#;vT%;%l2IrLoRxO z^1G8SxcSSq$p1mxn}{eYd?7x=wxl-+?ru@2NdwH6%jdkIwR6a2hTNYlrYE;d2o6vlVD>_@kJwL-Lf)8^-0&UYaw^VmszIW82 z+Cj;Yw1e6jg=Qn`C(Z}LE4-W7+Q!1HSnD?-JW_UAm|eAsg|DEva~;%3j=jkpN3JOD zT#46r>xeHp#nmU0{>c z7!xzA)EALLVf0M80ijB9>jGzEQBZ{dZC&2!HO|xstE~Z@8i8pY9hqc55L(sI5uRAw z?oKRvulTdf=-`6B%sZc7ZIXR{G+K43WwGDrrIx&y{(N#~6hzKjDN;jQyi4RGVH3d6Ya*&eF|i1eGVIM$Vlm7@)%6&PxqyOPhjR&7~d? zBofTHy>dy=Ob;GEG69>-QNZpYh$x(&tuBpi%m{Ab8@`lfDBXN&PIdZjTSBiv;&-L7 zyI+vr?m6XRnq={#O2;XM;{|mJ(uD(1W82p;1fT;(UW4<7>^7@1hE!G{q8wWIq3NoY zH`{+h^?3daPCH2ht(E*XE}4F-^X1yo!aKkcP2o zjtZ();qk}12UIuReG-k_x4tmyxh!IXSSmd94lua0-#t|6Ud>;ai#q)>kikOt>Bb8a z&EVquJ#X^|k2EyC_~Z@^Z_PE2IiVdw?{w&|Qo2tc`!Me^$oc&l9S7253 zp_a>$@(0k8SM_ffv24#c-cPGinZk$(e`USs7fMm9bYJ;aJ$}F4A_(WR?QUYJaz5UQ z=T&}N@KL@0D;ytN!P^^f^B%(q$m6LQOnU?5;a}J3c!yK;RQ9%8YzwVcBJg&+`NDUF z$12YTmjhuDiXAg-gM6T=h5ivk2&;!&=G?AXl4x){U2HM~8Fs#ee4`B5#f42kA1-@L zA#pWC#UrG`wj?6Ivoozl6Pe3GV>4-|?RDeNDen}#HGD8pF9T}H zIhe6ds2KURreKXcU`>5B3NJxdOT2C`J>knd4ZRxKWO&n+lVyBhRH?6JCIrdtt@mhh zj%s3c&jlfR`SZ5*?o|lxpjP8W=a=Z2KtJ>pG-~L};6+l6D<|;op@~3EZ`arw7&;Cd z$Vsxf(*~ZaAhZn3U6h6p%4h9&H2qFQE2Ru{r1-~wI;g(mVh!$d9I+cl`IF1yY=dRf zfNccq8WR~ePLF{ILwngW53nWPJ4bq##WAB+2erZ`R%H)8Uw6#Ws zLaP!tj-4_#c)corYw1aoAL7;c6I;(4F5a5BGak@uV=wO+DecGCX2<67%ZXLI3q(5M zdxz&#vjYFp&&bBFH}L$~2OzL^?Symfd!frgdm676H#%p`b{RS^m(i4V?rWeRtDKKP z#)9pRjc|XSIqYj>VBN(uTD1=5RVZmUeY--*&P|~REWo;}^8FZh4^`%Wk@4Or?|ZT4 zl6gGI#|V2Bg#Y+_ioa)V3+X+5NUq=^74oZ5<#R_Z1~#mE+GilFY`4knVb5W-hd!OZ zClZ@ny|qMw9|wN2;-s(xK|H#(-`^%)x;jrNV`s)eLL)=!dmt<;%$afhA+UpFZSfj1 zsfPh9|5OWsF#r&594@g`yzD;6!n{-}4GV2I&Z3k_+-3{ z@qA&Somkoob6tp+XACYB`0NVU?%H3^xEn~gV5HIDCWG(HlwN=rf5D$DxnJ=1l3>Dq zLA%hSmCof$=T>uYEH#V%u*JU$^%2YcblG*dv9O&_&Z&ay$J`Vw=I&Zzkg**(|!~%$> zDulFHX5T7a0p=|@^&vsYJvF$1+R31E(A&)E=G=vgr(}oEWHSodhh#c0oA#yz(hqbVe=^kUKMmYLWg3^|C}ArA&MA zu0v(7C6H!H`7aN9-H8Ay7^CvlIu5DDZjwF#NOxOl?V#en0qH2ibL9Fa|9q?96NR;P z?&)7c95(TIH!2l}f}hv(ozHaE!1pLnx@F;1uV;2xymc$Vy}oSU*XS@ZnHrZCh0*bG$7Ep3g3LxG!ZVkMPOD3(F}dzCiq;3Zk;RT5kKr zc7<0OhG5#78&F<&nAtH_Zj#EUb6YHfxbMgLwJ9g`s%4EdDmaUfCmhKAz`@^%6Fn|?e(dls19|ljsJeS1Ng5jOxFtCo#>hHGN?S8ST;(3QiZq#n zD)4=p^a~VrMO$9Uq5kZz4_)E;-?jm!AN-0fk2uH=D=dX<)|xxaEuB6EIwbvxIj=l6 zJ0#Yy&JxMUDPu~_c6TE60?t67K6SwX)v~SEeg~$%ex>(-!8U_ae}mUH|H)SPf8|k> zUmk44)K2RmmTukiiH{iMiXE@~yFYMR9eDr!toT>nUSCyzeJAjr_m+nY(1I01bgb6M z=?yZLmbS6F-cF||ZJ@AEM3>&Daf09Am zo|i>fg@xxQmOOR!Qq znt0XiUC6qUT4RCuIjOj)MpS-4ZP_Sn9##J`e&6jq{iiU*fO;#CcesKIRIMRL?ZHAzcq4?Nj~{wa{znoqtM`eA_rP+$246 zGWu98T#vEtVA&m>A*NO32Ga{ZMW2Nf$;@+#ZXpwU@kaMx*EZW)gN$4`ZeTG!uy_%t z#f}Ve=ON@n#|-j} zC#@1{LB{E`0ert&C|EMF6~pL+udh9RYkbj_;J#~V4q}R`yTB+K<$C9fK;%5RWMYwK z=aC-`ofM%1M$+{qn%SiBTSV-!KkK9)HxDsKqU3IC7@DU=X`(6eg%6`pR@1Cx_LG18 zRLcrB%LRMlLs>MP{R4ftQA04oKh;#TGQCNBee2#QIyYV_XmR=-k~@sWAPa}s@qJ>b z2~m1#o>(+qijGo0pAva(+F$rEJ4X_5D!j>w4tjFx;`DEtVfQeDiR1m+choFgMNDu9 zsXU;#W*E*L?wW4lJbu;Q6uGj5#m8Tv25ZzfovfJM74_P?qAhPdj%oGu&(+0Ml7r^q z4(}+P$}wC8O(`%{A-0*J2({{Pr-q3H$TYMP-}Es>kZoB?#Vs0^g^kLXMMLlW!~fg+ z$FajvR<~&4?N>)Li_t+U@jCcG;Qmio1NUF?>;8|lXk^MIeM>yiu3-#7X=e!MH=>l9 zxh&R`wOg&-L{H09+EF$bb&ihRZP76gUpU5w=N~x{VKCi!EP2B#(Fx2TN^^270AkFe ztKTm2e#u|7NT4V?Gf&t^4}COFV@w;0Z2Yx)!0s8j32e?RDphdqFZj&J4dE`Ylj!CT zP|aVi4$0x-9JSN}zsact+8ML-IH&#Pu$juZPgYH2M{;9vv3Gq>pv5!tXRD^CU|dl? z&RmpZpGK?NXOFcw;to%wtM&$&w}WMh)=#>Fvt@83p}o%7*#WJp`fw8gKW-{k-?lh% zY6#c#3AHbjL@Zp+D8v z>(`K^YtfFLfTnF6A^tV*gN_4ueNGBs?tJG%fZ$`9 zF7PKWXo`k%-%L=>^b1Mv`H7ePC|#5G>MJIBt+LA~rH_WB0R(;c1XQCqNHEXgbf}Lz z+)g_4SyT!52XbYMmu_wmak>}>8W$DGb2tZnYfgcjy&kcv1*YeVm1+zBG?*E@P9PbA zs?wHwR(^eU=1R+#wxf{PSiX1f>%AGtPp3N@(tPJ(b}s|*`pCNB)x#S-fM{5 zW!1(Dkn9Sb$KQ2t7EQA77|KD^LB19Br(f4)nT&lAT02Th`UjLUClN~N$Wo`t;W_C1 zRIeC2rSH;yN32v@z(&1g=6X7ZFQ-jP?~9zv8nf;*P5itLqfWrDn}RvUZFsL<5i|&% zJiEY+F z2oouLAe0HG5qvCt{5IxLiNi7Dh0n#7^2AW15&`R?$wbZ5t;!&C zrXopaM-zOZ-CFLfUlJbP9kpGApaP!M2+f>|MbaOW=wIT(*^BLZ^fisq+8X0P(oaF< zZv9n3c?NGQvv_>~K_N|KqTRcStq^F(8T8Vm17dBdg3dHy*oDMIO6|ie?Zm{bA25cQ zOlqpzE|^4DKZeR=;)#eGa#2qHt$b+q%68g7sosE{rKFmRBPde~X(%Vr)J^(e5S8}= z%@OEf?;8us8Ksx<+u|{fW8uOAe(9E=zI4^8kFRDd1KL=Uh{yt)$&m2mp3+zpvzNSj zv`nLLy2*LvlC|8p48%naJ7|f!cds-T54MY)$aHx>+2lf=$`F}ZZO?g=i6Ot8{@L%V zL4$cG=c?F(G7n=&X-cb~Jm9}=F&4(pxfh#WRn6FslglEf4C|j~J-pj%Dp#J_6j`n{ zJuK4qo+&Uj%IS&AnV#qzB|{3CpBOQ-&qE(vT@p1T=npb_$zjK4{#fF}ijt0In^4r9 z_Q;@bd@38CBE5{Fzm!$Qg?PjInSiOM16>=F`Y*X3FWVfUewQ@T+Q-qfINS)2x)EXW z8b^b<7Dc{f)Acz4w@TG%izK88SnxAFGI13nn0iYybH6r?RXx5?)9PHTca9mTsW1zQ z){N|v#Of6gO+DY&8@^=OX9MP`(?GVwW84$J$gb`Y$Pu~xP&JcQ%ZMz=kcE;(a_h+hA;E4hx9u+}MX71riFJs%||?x+Xj=q00~ zu1uHkKZ&e1o>t}^2%g?m6nSIM@o{F|u@4l;+)(`c^ZQ6^aea;cKNVMY*Kxa{0Hd=N z^(R+8De)D52_Ea4VcLJbmRH7Gh5>%3x#WNiJ2}s#3s%DuJc+y7(QAJ_HymE z3qQ+laaX;$%I`EU6%Am;R8V5iJoh+a0W5BqoUgKSz>Op=dk$Q##DDMm@9|KZPRvX zfX&(~IxZ*|7FD~sVExr)n;pf*1DUV>E8yi?}6~E1b%|mvaY!d1bAyc_&IRf z9QaOKjf)(jZI@6qO%CC!k$8wh|MVOeEWa-RMV_L5I7V2%$1+szmY|OC;a{cYFKfS^ zblZW#7v9sn=gfLVJ;aR4@Mv+-Qfy)ldgKPEqa2T6;zfv(wD8CW-lP!qvTCk`uZm(x z_%RMQSd*(5i&Zhm&w^9Fek?_KzHc*qVS%J&?qz7!rsc!U!Ki9htUy!v$(S~pw3SAz z)X=(DaM7Tmh{@*ZxbTFnuDB!2WZF#?rm0#8oTQ%{u60Q9NPN>`;UBMcwu_<0#OMp# z#Zc=yG9%p7-B;GW{*k>Cf(WP!$D~Hsg@+7kSpJCm+oyo1m>u9IQdUX@2~@ z^M2+P{(L=mDFBxJ_v05l zNHrc<;y@t!Sb^&!9Gj_NO+R)=6})Rue=J;q&wuYk+Ep#tuWUUTUyaVjr)y z*?G4jkrlD^hxUm~5cXoxN-n>SR^xE=HW=GIG;s$hcw&2lp)jrPB95_|g=m0G_>3tW zW?EiWVorJxBwfZ|K#n$|0OazF74|PAs=qG!nDGd{M!mo44ZdS{$@*VQV<=k~k{*V# z>SiSut*kv;**tn8@068vusHcZTQUJ~N;bAS8q1_Xt@Mb8Sbdev$M_Rwa^y;BwB`Uo z@`^gYg!T<79@$J6@r~fcN~_bHcF>dD2{F3z*hLbH@_@=8?r(`sOFP{`1+vGLC1ZL< zqTukVPSH+7tvPEvgQ4eG!f78H4{m8_hyenN%=$}(QJVYbUEch4Z2FrOd(3zpX{WJf zkMISiZOGr*fBCG@re1at%WQ|m>uSqlE7U8&HDihfNJ3G$QOFNm|4Y0^Ri+`?FDVp! zC+pkkH|h(H&++VL(ryGyZ3UE{5e^$IFc$Y|#U7$T!r95LNgvpIg6vAF>zHy$(R_B+FmRaOa7pmPSrk4s-CDnjNJ`wgKd? z$2w@j3!g{cr?nsLLAQ_Te z2_E5yir?1Yg%MfP&e((_wb9o~lNh}$&DsT^k`}e^i&h0I;Zr4?&c5Ckc;vh4%h87D z3r6u+{ZfuORdAE@sOP7Ho3%HU3MYc-O@Q-e>Ln}khTj2(b2sv8qi!ti@uS_F8lKPb zjna@ML;LD;de<ODBOa)s(b;Xl4P(^(zz|4h$1J-z~(+cxwxrtd5;T z=WIFK)YDoleR;nB<3fSI;5c(h2LcpVEBT(NSN(pzV;|#?a)#f5vcf5{7j#?C6msZZ zUZNu`i9#;Hdikl`A_a~ z*H%=XQ&pycF^UOFOI{n$n4BHdV%+T7=N&Hk&`W@*!&LME<3||Efkbo@ya?Hbus-E_ z^RGb+^!$XyDqV6qSia6@BL+tSxN1LC#i*0T)8;~Vok-%SPperBR{KZ{Z^+fB8hQshC|U|VBfG+Y@at^$$_wsZG0_B+>cnG z%(Z-UhC5)@o;Y}9nr;8#_YGo26T?rgA!$8j`3JLs>{}9#Yf9qc!V^8`SEb>JgX)f= zRk2yl9D(tR?8w&*&mDoCdEp9t%rma(Bjo8UeyuKO{u0Dg%^Qd+EO8?6|`gTY0uUj z2OHt`oR&|Os4NW7Bwq1~g}(499yH9+bm0-QgC;MV6e{S-%mnt_#INsc@OrTStTMW^ zWFFr4;Obg#V#*|Q7)FX65P(ic{Bf4*>D>%WlLmEoc$hi6a_Z`2m*Nr*JpWT#pfO)B zD%!bw{Y1g%Dz~s)uz8iPFjsG?A$qgb9fi8+P807F0|NBr)i|UGy}YD@#>ofz+C@Mb z2=Ug4-9z}dT~v3N`D+(x8y5n*=u%VJw4qWM5U1s@x`G(<2M@rMjaew0Sx{%9v!i)L~I%r8yg;kG?1cLK6B`<_l9Ml@C` zsHv+lU7VNZ6^J|s&Ch+G{J1w76~+=! z1Dc-|5OX3}pgH=QZ>*3)qA`$_(=Vcb_v zl0e=#+`cjXoO(h3I{lNh7 z+SoF{RH<*lX>SzYnLeSdGph87Hh;^paCLD!EZScVQ)mLRDarI9QUeGplL@%f24BM( zu0gX}c0)6(X?4Yz%}p9yd_TnG72(>MF2m)&_vlAh^r^<^nXiE_0I3qP>O(5%c}Zi2A$C=E;zns?8G*{wo&c0W$rg7159Gfgv{a;WIt{8lAvSr7j$7g`vsfmrM_n zmU{u;fqZxwHM_#@{<-8=upoNm8DJSmRz)g3bE6a7eFYCj6W6+$pRG^OrlU6&!P8k< zs&9N9Edb0tqkhtn6cEtf7WLCoMcHBE@L2XXM!@XgHp0QNP}HJ5V&!goYCh9dsS7_yB)>ufI>>lS#EewP&rUeFQbjiWP-+JI?02pDm4)WPsR_A z9r>I?633~0Qi?Ljk1jRn_Y5A=oVOc(8Lk=i0^*Vb0*|FdEfVpU!IR-M8$@}7z5hbz z_#gW!*?_mXxD3UlgtzwhRYsUEORG#qq&kJLzq)(a?k;Pq0w)8()B2bMLzETpeODM~ z(Fs8a(z2QifO>nsN+5bZYWHtwanWqD%2$d)yvkGw&2&<&bI}Cc{{cL0;%d8-rmaL* zs7F;E(xnFxj>xr>EAcH}&NAVi>t$!TBTwIu9n8u4l|z3DoIPKW=V)_f9IIu@Tti$u{(Nx4Y|MVSSD(1 zA%ebfc^I@Y1zy*g3|l^nZY=sblj)B}^k|H#``&u~)cx|^c&C2fCOx^c{wbm0>J7ao z1@!fRFwP(}VD^2+{5oh5Qxu4&Xe(2g*`RhNVw?)p=Gc=vYu&sSwqw+vcTpflgx1yE zHt)dRNKa^>roF%FBPEL#oBkEKBv6)c$gl#WAKDAW?h_=fsXYyddGfqnFpvVblY2g^ zPA%})DDma^W_%NP#&26H(9#NbkRhkef|I=Dew7k^9lIjO6nhzUjm8CIieR8F-zC^I zhx+Utc`G?r)mk=F?l@l585_j0OU$gQW>L#ZdZ7q=xmnO8JOy2=7chBUnW0Cy5bJ^h zwTHw;ru9-4JFEJJhzDlYd@%PkNC;`u9VqU=anzKpOAf630&~? z%(~WHt~AsjVkZSOCQ#(Z35rl6iw8y{s+qI%J`V&Lv5t1;>e!gcXv@35JJ6*|!Cax@ z#!bgsTpqpPB=j?L?@QlDP;^E?aS7ZB){dAl;C>`adJn%06QipI5EV(sMuWR6)%$*-{#P?ZIbMig!vByl$iS(avtG)T$dV3L?`OZqTd2>?Yt3k@i_NdHU zmGYU*5vVISaMz&Vn}f`Y#b?rMXBy2iu|TL;eT)O)v24UU^iZ!7uf? zT7z>|je1#Wr<}{{Z4F!dLL~ggz^oGir@mTt0%BIpV*z~tRQ4B&R;ordM`5cGhx2=u zY|EDWvWY43MeAIeH6|)lC^;Uv2ptWPNi&HBzK)*P?zA@0=8UqPKf8_X_BNjHuR2g1 zmD5-K%DbjAV%lRQ_`bLQ>Z zR!LG*-RBF9nW=RJO9Z1XH8RH-xO?Z=jdAptgz!6iGy>u16V|_34(CjZ3xBHw!NE;k%cD)|Wo)9SsS}NTWKQ z(KjMp4{h{R6*3g%2U9JnHe%b3K9sJ13J!=l)C zlaMmdMfj2IAYGH7sIZA0-AWZY4^^=ft;s(Mz}sGVe|!v3aJP7rhSoNgyyiFWsNvDqB;_+>x=t?)iI#b4!L2-1{B&qifwDKVl665RYQJzr{z3gHbg2Yc>-uDIs=WO*9&Y-NVe^PAub zCGP-)v{bvn!bCn=@!Aug2NdVA?)jFT#alc8Ne7P`qbZcZOP+mQxQdMbH<)I;Gy~eF z6wDU@^ZUijcRDc6_f5m-Cun_ZUJ~_0c0k)GA2jP0-&>C{+ASkKzBG)&)S3@K_4gos z^mIdMl$K((WK{V_`UxLrBL$8UH$e=5k0oq;tc8J@UHbyF1#ZuUtHbhYwFprWPAqvc z8C`+;Wh)AmA3s{xc8+7~uW%M9x4Wv9#=`Q11e+yGa&4nS(1{=Gv;pG%ebb7B^V+20 zkE2MaH_nWd#$1UOi31W0Qt!OS=R4mA`(ewxuzxudd3Rti9OmC)0BDTJ(pTL4y8}6^ z(;X2mR*e^0S&iiBFBPW%p|Nmau)E#iX2ezG@M2;unYp#7*J}Cjisx~qZPzhpW-i_u z7V@Z$i+bicUj&almw)XH9wgYlvv!zpt zWZ!3=6@4!Tm00{$Ax;@5zO(?URP0?^c#Rcy*xer>*D762@7>S2L*0{=2>R?U&T>7# zXRpNO)NcJDUd$mFAe2Ngq`*IG@(;22mMww?Iyd==Y)EY=2i*O%f@VmDzvvD{vYY6i zXO!I4jQ=w_HtC)D+KG>L(#sDD(V>X)&^_NriV%Pl>9rp=|AzbUq;S1`9*`>w+Du;L z6bpy$y}Zh+K*DW85#)C#@1Do=58oyzM7$7uezRPkK$YKCtGjIJ)mn%5kJk5Fq!&H) zx!r|SkXCCsrzjI%RchQm_C=QXmE01qiK@TCl}Mx{CHih5iv3a@P^-K_9qJEZ!~0)X zBt7R-6#C)L2<)xQJN4EnpJhH0!sFQAyKv&ihswOzX~DJef9Ae!8c6dz&}f|H(9?HM zW{R7xTex^}f+gQYZHU0cA+69*xQtZ_Chb=Q%^@ zMBTQrQ8_Jw$Dw0?%cM zt33;FFzXdfnkKI?Pj=NBsX<>78x#)L2mHEqulJ1*yDYDU!~5K`bk*F8e<k zhr1A5(z9IFM)OCrR_3j6k1o{~w(DdWtTwwpcdrUrK?7I6;ELUh|07QAm3G9BmvY<3 z5R!g7UK_KsL~<@yx%$01^{mIRXJu?k6myiW8Q?7df}uw>w&y);;ZyoA#$BL(XUb=G z3rh9*)VVRgp-y7BNuhPJKKS)pFJq|ls*uvln5L5yDUYg~`L@UoY%o6n z;pZ6spd7H-RM(oYE2bnnr!B&tkuf2h(0Z`nthk?{RS*|+#YvKaCd#{aQ=3Z z#ZXd&|6EsZF4@6LJzF19{l`nZ2fT#TbZ|92Sj%q6MZEQy+nHmAz3v)8zeC!9OW#z-G$o5&=fS(-?BOBJYk22Y*XiWnHK%ll~2jkr4SiYoG zAoX#PFs0XHX#Yrsew&4oQW|8Nn)IbwC~>IxosEn!kDuC;p7l!K(_t`uYo+6}(t;0> zjZtCu14?UK2ec8-Bm&QoUC*8x2gs5@5(0+L87FtumqXnBsz)tXg(#J&pK?!U^PcZ6 zd;ac{NL>h02$;7=iJ#h|?#dUQbFE2f+Ry*<@H`XEA2AUu+7E)SA6iu%ak4ZO1xgs9 z(fw$rk`}<k{9#uv-zgO3QAG_We7- z*fRbf=*O5Od~K{;^|mKAIpq)Oi{@a;fmqOb;1F0!(6smQ#pZUD+Y8_0cB20V)3=|? zGBtw$LJR|*&lU$OhOl1#MD@}+G%vk*r;yLoa6>5mIt3sA7z)fgSN9@vWi*6;uaoxS@45TJQdSyOKca0rD8M`gEHA_`j+#o>KE=hSv=*(W^C7Y1XmWCBU` z>8#{W%ok^EF&$fhbQ;GA>{qEwa&``sP)bsxM$V`fc~KFe&7T6ChkNc))prY^S3Nkv zUApP}!havILaUXhTE8|*b_XYXhh5JDiT{D}0C};Pgs6WaK!pd8VCL}WWx5%oQpH=f zlKg$OK-h7HAcD;QLdyL>jC6CKr}p0#wq=WAZpwkjGmewE?c>ocY~r+s>zHEHTfvg$F?*WlGRCm)t>uGR%#8st_d@_X7{C~qqHs9!^ zlJ@X=#RfN?H1k%&K$TwYpX}vqUk1eW7+0v><~x4BULwL?^vWP3CAU zk9`E7K=Q`$ZG%PWjIZp-n2ifi{kj2Q5=3}?Yk|rO`D?PvAgKVi9ZN<1d znpC^x37M9seAv9tn_2lD@~q zXx|b1eaTn6%nud!&Jj`03$0TxFHUYi=pS^edh&Oh8sG%L=8dv%q&HOsFDZ4^UAPEV zfgS&->7rTtU`D32p+{fzRJ!}{*V?Mh@6A@t(z?B)HvYDDU`0O6IuIZ*+1-Zc3m!?^ zUL4P>J4^PdHBjScPwk| z@kKwNp~g;D29yyaNV7A_x3OGH!FS#_qLV41(|**O=axyd4U{2wR3oq?dlX2MO^HtJ%vPHKBs1&BK12&#m$pU1Q8n1*5_xB7S`Rp&wpS^yn zF;~e#UaltSbmZt`2}Ic0@3UU$)Y^M+WBZ#YM||2$R{G58dnJ66RDD&h7T$K*_$Mxw z;sq3@Cw`7n>7ND0ib&st!{)vKj7`Nb_S_^YqeO&VaM6F(rV&*9s)GtZ6Kfybe+mc= z!qW{*O=Yv$F=OgQ0hC)RDgA*tAwlsM=xW5|L12M*7|dXzexP?p?JCvHXqEJPHYRLF zN249wYc7PV!3HHoq@o^x9mNhP0aq^cm$^b!^gnwwZI?vB8^8ZhKfTy&YeMc7=HAf} zdHs-)oozTEGiY-^r1*TM(P6mG0ghHDUaBsY_cv6 z0~i+)ZLS-NdB_N+4tMOdaCg=X`>k1FHT?08p2?^W@aL(od%cV+Ym5Y{h5}1{ef!#X z?c&UmoMTkkx$uJU#>iryQuQ3W980X*;B3keyOxjt;t4-a;{IF&^(&jx!h{=veG?UDLqK{C(Pr` zKvgTE%9G;)0HZFguFq#&>5eB(Su^Z2&&lr`QeTCSt_|-5rYCJ!R_WRc%2I8 zWuIN)er%N63REvv?cb2E3RgwDQnF-18+?y{p7WL|X&~K<`w(7RhdnU9>-Wx+kNc06 z7K{pF2xFO}RBP`mM#MzbolrMb+CBAb&}#d1a2udgelx#z{L|96y`>7s%eFBnxs;59 z)UZedYEo__#VwE8(5=->eIE{UcUOo2#|c?&JO4{-!Sen`>C2X(c^5rkS6ywy)x`3V zfX><|7y&3I+_Pa(c&w;n_At(2exKi2j_A4 zZn2*kBSmonj|8N`_9nt?+=Z6E%Z$1r7aZ<;v4U&e>;x|%-y?5|j zk;)dxLSA(_%Te82;+Tr!rUw884qZ85)rFA-DLP@@}C=Gt%stS1s$YvBEj#?{|I$ zU9X_qRyG43lCMy!JS9TV!Yj+i?O6fy+#4ha$Lp=B_u%?aqND950h)Y4u(h z+X^O3(=fY^BsTZ*6^uLnTh$^)JswI>xq|E%3xToEhgRJ^Ur)MRj(>l|Q&ZulR$s(v zQO;BQBr-&k+nbIFeE*fT>ivE=P8oNh?L$42FMdx8{d=z8VMofeFM$Z2>NX|uxd3ICR5$Myj^7_?xA|ix7}!D zNw&`xvBU2GhMtUrwlkNM63==E6jK^ajFI@C7NfL;&+5P;O1`^bS2>;Ke(h5BHc=OF zKRL(00idvWjynP@9CsJsB1@)o^!wSx3y!1y$v^j5l&z)vWW+sQ3E5Nxg0ZIHHGJ(# z&x6iY8cXK^yce+~i_{ZgaKY=C61a7+bym!e5F1%+dgMcueA|s$40dwkztm3dL_uq+ zf7MP>T)YU7T6}!(-%282>0u^E3iD?IOoBhVU(1T{{kvxo zSM=YrtqXl_8vUQy*8QdL@LstbDH>VfMg3wW&v~1BGqg4u6#|_6|wP zIu`sShA);j5J{>L!4#Y|>A|yd(>v8*XR#9;wXh$w?0B`uUd8waRoP>J>|HaOo}F88)N8MUFcA(GT_UtMYXysdfBI^M?V;RNDsp%uHSa z#z6xljd>wwj(L=n_kE=LT1dUEl;v7>iNlLpxV<~(ZJo}M7~E3Zsx83kBcDxIEL6jz z*ZEj5=Q8e(F1t$6+X$%z@v|n>?;lN7@c3g-&F~$Mh~zA5AcuVia5(UtQ*NKU2QTvn zyqd~HnKOVwsFTV+{P7LML*-!3sQdoWQc@me@1NbM_v$~RCbyNo%X`Hx7HG@*VwkI$ zgTw0tyvl>-g+feuVt($$w-tlRR(wbq3S7u$ClFlc<_rcG4I*^pt?~ljX zsyW_@k_7}rugPz_s4tmxFg4?a@8`(Bi6MBPGUG4jp(yl<1Q)0PA^ueXif63GO!}L3 zm900jRk)&(oQN3@g5|z&PjQ`yV}_VJ;SLcw2bUW;=eH5H=RT9>Zm%a@2#@&1`aKSC zK=wRYc|#S9Z}COP-Cpg8mzx+CH0ls~9%kkC17_nW7s044^;w^74b(U2C)&!XDUfW@ z?Yv41UNQL8=*|NTzb6~Dx9ry56~iddolnXw|YU*@={g$r^2wbeG$KN_j7<9 zm+QFfmfhU*??~R{o^_)UFEYj|9*rRz!0sOGXR+o+U!??B1`l(I?jpt9v z$|khjhkB_N^CR`~$3stlX8)lO7n)!aSdI03q3~N~$jKF1m9?8X zNQaFFzMQoi?jn|zu0W|K>H!7cn7bvm;94UUv!Et;Ti@EIW7 zk9fFv2Ug+MUj!YPd#LgwPi?QVuhue3&hHm(gna=iOr6G&lf}^}mC2_%g|R$^y*I;{ z_e3#qh(w(xAoAhEZu?aYba1F%uIbZ-J%2;NzX3$O|C2tk&iyS9?T-LUK0iUKH3m0X3qE%D zZL)bGVbfh%iLfw-T9Va)nCJg#?^@%bOxrlcvWeXytwSW|QKJZ9ji|;tL=Gi~$V!MY zGS0?0Z`-y~qH)%VSz#0z#+cEh&6!F@F%HF;br@&aGWPU2iSNf2l(7ROZmEM*$G zoR(G6e_y1y*k_{VeUV~3U3&Y- zle}HSOi1S*4*FN|(QkH_bIa%Qo*TP+6Dw#fZ%(EDXk2@QX3$?TBX`^V{-Q=GDR~$3 z_0BuqTj?|*qQvzAU!cd>b~Ou*B$b&cq-^J@&MYM+ zDH{FQ(IpX8C5Oql6)z(3Od22MQQVMvpqP`*QjxaIJp5jc2yBh!M>d0Z^m|wdf2#!X zx3xeexCDorD*SIhyjDX|wL6k|Rns3$I7lU5R}r|sMQN%q%=F`N0va1YD|k__m9n*@ zzW`tPK_K{$=%VkyS~5uh5P(52&iuLQ4`omug~QY<`Z6^8T~rkrfi?Dn1KFew(s_Dp z?6XBpAJa^(_N9of!1i%p>=lC+k~Z23(k9(Hp9YZD@Z6DeP9S8JiCozj@U^oYg77J% zFp70&!yZprq$RGm{VLOQG0)vfL^waOzoPI3q%hCr)-7z#xI@>Cm?$!u{U$nUAZL1* zqst+fP5!Qad-Q2jHG57?Z)O!@+n1)+>C{qcymCT~%)Sb$0HPvV7`l^;o1*K>`O&Js z3g^H5e6U}$+EdEeFXgH-Lk}|~dnW~h68m+XfCS)+;zJ?J58zGluUFy9E-dR3#Up_k zj&?pt?Z^1U@JjoKpi#-yijdf0fm z&q@Y{B}rEj9&z1l6X0!ow8&lmQbJ89}wxXv1pj%cjv|NX! zzniDsC@@Qm2b@%d#UAGgWrqO9F6(U^O>);M3v=eOqN1*e5L<>OxXnDD}RuAQza_ww~iyam3RhGe&BTxomXU(hBl zs^J{3O5sC|a4=n9R}*s7b@qs3qS*E!sA$M=bga`~{pU;>Mgzgm%nVI-kKb)@y3=MN z;J@XN2~R|DpvI9s<2h}ZIyZo%yQWiFKw;qkZ2p~ER9odT!39Pg+s!r{%ofY_d8evp zat_8K$~GUxm!`O3qq0Z~ zkqF#qfZ*6DXPSCEHo0^WbD%}J7Ex9;u$;5#$3!P~cqPL;yffXOuH#5By^1;1S#4&q zW^d1fU8YVe?XxhQ+8(Xua2k53yHRnu%sqTO39~xU|Jaz5o9knPN=Kxc5|Tm|r26|@ zsagyU?zAtB)kBYsBi4r%52E5Qi6@RFO!_y<)}vlk=j#n2chg*u%d50P_P5Cep*Q5vI~gtu?NpC(l5UHFeX+M zTFuh$^toXs;7fmI;$9yTOFt3qi%$=d_6c5GYT|^9-N^}hEtxSZIaRsA)y~-Jb!S3v zxD_SsWT5v9!m)CUFu;ji$WINKZ#NH1Jr%q6E-z}8+PdUHOb_BL$RLamxtCuFrkD(d zx}c1ITht(FGoyuH*tU4q?f(T3$UA>53X)4MTr~OIEK|4c3ci&pi=n_fbo~VPefIB0#YgI z!xt3jlshgkB47zb5#6VCQ{m)_vy}SSG3dOCJwrY(+B~;4stWxUPz-EDn!R&js$C6E zs(de?(9cY(+qJIm4^P%{oX3Kb{&DA7%_vU38w04%_#D?7*~Fg;2ca??NK58#t6=4^ z?Nou&pnHMg->v7z*KUY36MG5`+lj?)2auKK>1}A+)G5NZ&yYFVzMCV8@CoDcs|0$> zK-&5aUyoo3xQH}6(vCX_!CskUuo{bDw}mN%k6U}%iX6qBp-@CTg}r{R3q0W_1(4ST zGBZ}tb@&e?2Jz;AE^N&p>FBBu&YaOu*H;#qkmsOL%^_Iyh-2kQ3dFFm_5d_M85ZU? z8p76>!Q6BY-zQAy28eeCx)OH6w#o&n9>B1fSqCH^etivq?S{lqFYEBdQA7wOA^{_B zl9gk-@(0&3TPON^SXG>eLbr=Z2@_!5IVg@{l-`N4fn-!c@m*&*&A?W>omjZ_w*DBT zPIYRyI7}U>SG+FKmfCm?r~;|w&IVr>wk)iJ>B&BSC>!k5KcyBGn;*S|^CWq|_IjQZ zi)gmMNo)0Phj?AYaWcIam|DxhSfze-aeMcOku1z2POUxmmWQ(XleqvEy)&;rtG{5Y ze(QXMG-D~N5kMHh;xd7**;C*@-rp#BG*~Xn{|h%__m#b8x{vWm+&1|V?&rV!J&AAX zDRacII$wdcz$fsOWmE-q%{l_3ma74MX^Qs@a5md$+^!Vh3G)SA!wnSCdO>TLJe?U$nM%MZg8u`yXYFKRJgYxaB9G{+sFQPrCVkr>leUA4LQF+ryi& ZV!B};i*&mn9CrW! literal 0 HcmV?d00001 From a9df079a48301e8013ec0e22642353c8713e4f22 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 25 Sep 2025 14:55:50 +0200 Subject: [PATCH 1173/1733] Update json --- frontend/public/json/goaway.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/public/json/goaway.json b/frontend/public/json/goaway.json index 599c4cd0a..c15cead32 100644 --- a/frontend/public/json/goaway.json +++ b/frontend/public/json/goaway.json @@ -13,14 +13,14 @@ "config_path": "/opt/goaway/config/settings.yaml", "website": "https://github.com/pommee/goaway", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/goaway.webp", - "description": "Very good looking new alternative to Pi-Hole and Adguard Home. Been trying it out in Docker and thought it would be a great addition to the community scripts.", + "description": "Lightweight DNS sinkhole written in Go with a modern dashboard client. Very good looking new alternative to Pi-Hole and Adguard Home.", "install_methods": [ { "type": "default", "script": "ct/goaway.sh", "resources": { "cpu": 1, - "ram": 512, + "ram": 1024, "hdd": 4, "os": "Debian", "version": "13" From dbc7815d86ab23b76a3e793593dbbb36b53fd025 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 25 Sep 2025 14:56:13 +0200 Subject: [PATCH 1174/1733] Update GoAway --- ct/goaway.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/goaway.sh b/ct/goaway.sh index 7b7524b44..6fb59c6db 100644 --- a/ct/goaway.sh +++ b/ct/goaway.sh @@ -8,7 +8,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV APP="GoAway" var_tags="${var_tags:-network}" var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-512}" +var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" From 7190be8af09d24605adff5931bc15971835d8089 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 25 Sep 2025 14:58:19 +0200 Subject: [PATCH 1175/1733] Refactor Verdaccio install and update scripts Updated both install and update scripts to use the setup_nodejs function with NODE_MODULE for Verdaccio installation and upgrade. Improved update process to include apt update/upgrade steps, and standardized apt cleanup commands in the install script. --- ct/verdaccio.sh | 13 ++++++++----- install/verdaccio-install.sh | 13 +++++-------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/ct/verdaccio.sh b/ct/verdaccio.sh index 45bf1a0e4..099755a4a 100644 --- a/ct/verdaccio.sh +++ b/ct/verdaccio.sh @@ -27,10 +27,13 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - NODE_VERSION="22" setup_nodejs - - msg_info "Updating ${APP} LXC" - $STD npm update -g verdaccio + + msg_info "Updating LXC Container" + $STD apt update + $STD apt -y upgrade + msg_ok "Updated LXC Container" + + NODE_VERSION="22" NODE_MODULE="verdaccio" setup_nodejs systemctl restart verdaccio msg_ok "Updated Successfully" exit @@ -43,4 +46,4 @@ description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:4873${CL}" \ No newline at end of file +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:4873${CL}" diff --git a/install/verdaccio-install.sh b/install/verdaccio-install.sh index 965517264..1586217b5 100644 --- a/install/verdaccio-install.sh +++ b/install/verdaccio-install.sh @@ -19,11 +19,7 @@ $STD apt-get install -y \ build-essential msg_ok "Installed Dependencies" -NODE_VERSION="22" setup_nodejs - -msg_info "Installing Verdaccio" -$STD npm install --global verdaccio -msg_ok "Installed Verdaccio" +NODE_VERSION="22" NODE_MODULE="verdaccio" setup_nodejs msg_info "Configuring Verdaccio" mkdir -p /opt/verdaccio/config @@ -94,6 +90,7 @@ motd_ssh customize msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" \ No newline at end of file +$STD apt -y autoremove +$STD apt -y autoclean +$STD apt -y clean +msg_ok "Cleaned" From cc6219ef28246df84ab32dc73aaf45b9f3e9d43a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 25 Sep 2025 15:01:40 +0200 Subject: [PATCH 1176/1733] Refine install and update scripts for Warracker Standardized messaging in update script to use 'Warracker' instead of variable. Updated install script to use 'apt' instead of 'apt-get', improved PostgreSQL setup messaging, and added 'apt clean' to cleanup steps. --- ct/warracker.sh | 6 +++--- install/warracker-install.sh | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ct/warracker.sh b/ct/warracker.sh index c435c60a9..19c412803 100644 --- a/ct/warracker.sh +++ b/ct/warracker.sh @@ -37,13 +37,13 @@ function update_script() { fetch_and_deploy_gh_release "warracker" "sassanix/Warracker" "tarball" "latest" "/opt/warracker" - msg_info "Updating $APP" + msg_info "Updating Warracker" cd /opt/warracker/backend $STD uv venv .venv $STD source .venv/bin/activate $STD uv pip install -r requirements.txt - msg_ok "Updated $APP" - + msg_ok "Updated Warracker" + msg_info "Starting Services" systemctl start warracker systemctl start nginx diff --git a/install/warracker-install.sh b/install/warracker-install.sh index c10fc723e..26d9d524a 100644 --- a/install/warracker-install.sh +++ b/install/warracker-install.sh @@ -14,7 +14,7 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt-get install -y \ +$STD apt install -y \ build-essential \ libpq-dev \ nginx @@ -23,13 +23,12 @@ msg_ok "Installed Dependencies" PYTHON_VERSION="3.12" setup_uv PG_VERSION="17" setup_postgresql -msg_info "Installing Postgresql" +msg_info "Setup PostgreSQL" DB_NAME="warranty_db" DB_USER="warranty_user" DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) DB_ADMIN_USER="warracker_admin" DB_ADMIN_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) -systemctl start postgresql $STD sudo -u postgres psql -c "CREATE USER $DB_USER WITH PASSWORD '$DB_PASS';" $STD sudo -u postgres psql -c "CREATE USER $DB_ADMIN_USER WITH PASSWORD '$DB_ADMIN_PASS' SUPERUSER;" $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME OWNER $DB_ADMIN_USER;" @@ -44,7 +43,7 @@ $STD sudo -u postgres psql -d "$DB_NAME" -c "ALTER DEFAULT PRIVILEGES IN SCHEMA echo "DB_ADMIN_USER: $DB_ADMIN_USER" echo "DB_ADMIN_PASS: $DB_ADMIN_PASS" } >>~/warracker.creds -msg_ok "Installed PostgreSQL" +msg_ok "Setup PostgreSQL" fetch_and_deploy_gh_release "warracker" "sassanix/Warracker" "tarball" "latest" "/opt/warracker" @@ -114,6 +113,7 @@ motd_ssh customize msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean +$STD apt -y autoremove +$STD apt -y autoclean +$STD apt -y clean msg_ok "Cleaned" From ad967c9ae7209fba34b301196c642d59ed5f4b30 Mon Sep 17 00:00:00 2001 From: "app-header-generator[bot]" <194485257+app-header-generator[bot]@users.noreply.github.com> Date: Thu, 25 Sep 2025 21:53:37 +0200 Subject: [PATCH 1177/1733] Update .app files (#945) --- ct/headers/ntfy | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/ntfy diff --git a/ct/headers/ntfy b/ct/headers/ntfy new file mode 100644 index 000000000..e2e078da5 --- /dev/null +++ b/ct/headers/ntfy @@ -0,0 +1,6 @@ + __ ____ + ____ / /_/ __/_ __ + / __ \/ __/ /_/ / / / + / / / / /_/ __/ /_/ / +/_/ /_/\__/_/ \__, / + /____/ From 8d00d2c9db3900b8c4569ae3438c7621cd8a4e42 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 26 Sep 2025 12:47:46 +0200 Subject: [PATCH 1178/1733] Add Description field for Bridges --- misc/build.func | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index 59684d8cb..f25782400 100644 --- a/misc/build.func +++ b/misc/build.func @@ -220,6 +220,22 @@ get_current_ip() { echo "$CURRENT_IP" } +# ------------------------------------------------------------------------------ +# get_bridge_description() +# +# - Reads bridge descriptions from /usr/local/community-scripts/bridge_description +# - Returns description for given bridge name, or empty string if not found +# - File format: bridge_name:description (e.g., vmbr0:LAN, vmbr1:WAN) +# ------------------------------------------------------------------------------ +get_bridge_description() { + local bridge_name="$1" + local desc_file="/usr/local/community-scripts/bridge_description" + + if [[ -f "$desc_file" ]]; then + grep "^${bridge_name}:" "$desc_file" 2>/dev/null | cut -d':' -f2- | head -n1 + fi +} + # ------------------------------------------------------------------------------ # update_motd_ip() # @@ -613,7 +629,20 @@ advanced_settings() { BRG="vmbr0" echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" else - BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge:" 15 40 6 $(echo "$BRIDGES" | awk '{print $0, "Bridge"}') 3>&1 1>&2 2>&3) + # Build bridge menu with descriptions + BRIDGE_MENU_OPTIONS="" + while IFS= read -r bridge; do + if [[ -n "$bridge" ]]; then + description=$(get_bridge_description "$bridge") + if [[ -n "$description" ]]; then + BRIDGE_MENU_OPTIONS="${BRIDGE_MENU_OPTIONS}${bridge} \"${bridge} - ${description}\" " + else + BRIDGE_MENU_OPTIONS="${BRIDGE_MENU_OPTIONS}${bridge} \"${bridge}\" " + fi + fi + done <<< "$BRIDGES" + + BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge:" 15 60 6 $BRIDGE_MENU_OPTIONS 3>&1 1>&2 2>&3) if [[ -z "$BRG" ]]; then exit_script else From 5d68b20a4a4a3b2b4d018783296b873d5c845a8a Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 26 Sep 2025 12:52:44 +0200 Subject: [PATCH 1179/1733] Add Description field for Bridges --- misc/build.func | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/misc/build.func b/misc/build.func index f25782400..690620d5b 100644 --- a/misc/build.func +++ b/misc/build.func @@ -630,19 +630,19 @@ advanced_settings() { echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" else # Build bridge menu with descriptions - BRIDGE_MENU_OPTIONS="" + BRIDGE_MENU_OPTIONS=() while IFS= read -r bridge; do if [[ -n "$bridge" ]]; then description=$(get_bridge_description "$bridge") if [[ -n "$description" ]]; then - BRIDGE_MENU_OPTIONS="${BRIDGE_MENU_OPTIONS}${bridge} \"${bridge} - ${description}\" " + BRIDGE_MENU_OPTIONS+=("$bridge" "${bridge} - ${description}") else - BRIDGE_MENU_OPTIONS="${BRIDGE_MENU_OPTIONS}${bridge} \"${bridge}\" " + BRIDGE_MENU_OPTIONS+=("$bridge" "$bridge") fi fi done <<< "$BRIDGES" - BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge:" 15 60 6 $BRIDGE_MENU_OPTIONS 3>&1 1>&2 2>&3) + BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge:" 15 60 6 "${BRIDGE_MENU_OPTIONS[@]}" 3>&1 1>&2 2>&3) if [[ -z "$BRG" ]]; then exit_script else From 6e05d3afc826b3fd709caba7130b8d27783b99f4 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 26 Sep 2025 12:59:21 +0200 Subject: [PATCH 1180/1733] Add Description field for Bridges --- misc/build.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index 690620d5b..b6e7bdd3a 100644 --- a/misc/build.func +++ b/misc/build.func @@ -635,7 +635,7 @@ advanced_settings() { if [[ -n "$bridge" ]]; then description=$(get_bridge_description "$bridge") if [[ -n "$description" ]]; then - BRIDGE_MENU_OPTIONS+=("$bridge" "${bridge} - ${description}") + BRIDGE_MENU_OPTIONS+=("$bridge" "${description}") else BRIDGE_MENU_OPTIONS+=("$bridge" "$bridge") fi From 055492bb563db9bf204f671bda8aafbded6e0c88 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 26 Sep 2025 13:07:04 +0200 Subject: [PATCH 1181/1733] Add Description field for Bridges --- misc/build.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index b6e7bdd3a..70c604c0a 100644 --- a/misc/build.func +++ b/misc/build.func @@ -637,7 +637,7 @@ advanced_settings() { if [[ -n "$description" ]]; then BRIDGE_MENU_OPTIONS+=("$bridge" "${description}") else - BRIDGE_MENU_OPTIONS+=("$bridge" "$bridge") + BRIDGE_MENU_OPTIONS+=("$bridge" " ") fi fi done <<< "$BRIDGES" From f6997adcdc66b85b0813f30508978bfc9f4344bf Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 26 Sep 2025 13:08:43 +0200 Subject: [PATCH 1182/1733] Add Description field for Bridges --- misc/build.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index 70c604c0a..59af61ad9 100644 --- a/misc/build.func +++ b/misc/build.func @@ -642,7 +642,7 @@ advanced_settings() { fi done <<< "$BRIDGES" - BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge:" 15 60 6 "${BRIDGE_MENU_OPTIONS[@]}" 3>&1 1>&2 2>&3) + BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge:\To add a description, edit /usr/local/community-scripts/bridge_description\nExample: vmbr0:LAN vmbr1:WAN" 15 60 6 "${BRIDGE_MENU_OPTIONS[@]}" 3>&1 1>&2 2>&3) if [[ -z "$BRG" ]]; then exit_script else From 3ea8ed2a430aeba2c5544d280b529867eb31babf Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 26 Sep 2025 13:12:02 +0200 Subject: [PATCH 1183/1733] Add Description field for Bridges --- misc/build.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index 59af61ad9..2c029b8bb 100644 --- a/misc/build.func +++ b/misc/build.func @@ -642,7 +642,7 @@ advanced_settings() { fi done <<< "$BRIDGES" - BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge:\To add a description, edit /usr/local/community-scripts/bridge_description\nExample: vmbr0:LAN vmbr1:WAN" 15 60 6 "${BRIDGE_MENU_OPTIONS[@]}" 3>&1 1>&2 2>&3) + BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge:\nTo add a description, edit /usr/local/community-scripts/bridge_description\nExample: vmbr0:LAN vmbr1:WAN" 15 60 6 "${BRIDGE_MENU_OPTIONS[@]}" 3>&1 1>&2 2>&3) if [[ -z "$BRG" ]]; then exit_script else From 20b9bf8aa2662ed463f95855ef7a4c66120f27fa Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 26 Sep 2025 13:14:30 +0200 Subject: [PATCH 1184/1733] Add Description field for Bridges --- misc/build.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index 2c029b8bb..4dfb0e549 100644 --- a/misc/build.func +++ b/misc/build.func @@ -642,7 +642,7 @@ advanced_settings() { fi done <<< "$BRIDGES" - BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge:\nTo add a description, edit /usr/local/community-scripts/bridge_description\nExample: vmbr0:LAN vmbr1:WAN" 15 60 6 "${BRIDGE_MENU_OPTIONS[@]}" 3>&1 1>&2 2>&3) + BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge:\nTo add a description, edit '/usr/local/community-scripts/bridge_description'\nExample: vmbr0:LAN vmbr1:WAN" 18 80 6 "${BRIDGE_MENU_OPTIONS[@]}" 3>&1 1>&2 2>&3) if [[ -z "$BRG" ]]; then exit_script else From 5a59e2fa6286e14b6cfedcc060e41d848917f3be Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 26 Sep 2025 13:16:32 +0200 Subject: [PATCH 1185/1733] Add Description field for Bridges --- misc/build.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index 4dfb0e549..be56d1b4f 100644 --- a/misc/build.func +++ b/misc/build.func @@ -642,7 +642,7 @@ advanced_settings() { fi done <<< "$BRIDGES" - BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge:\nTo add a description, edit '/usr/local/community-scripts/bridge_description'\nExample: vmbr0:LAN vmbr1:WAN" 18 80 6 "${BRIDGE_MENU_OPTIONS[@]}" 3>&1 1>&2 2>&3) + BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge:\nTo add a description, edit:\n/usr/local/community-scripts/bridge_description\nExample: vmbr0:LAN vmbr1:WAN" 18 80 6 "${BRIDGE_MENU_OPTIONS[@]}" 3>&1 1>&2 2>&3) if [[ -z "$BRG" ]]; then exit_script else From 49fceced696beec4d49a5c378b30f306225c8c5f Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 26 Sep 2025 13:20:38 +0200 Subject: [PATCH 1186/1733] Add Description field for Bridges --- misc/build.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index be56d1b4f..3a68098ca 100644 --- a/misc/build.func +++ b/misc/build.func @@ -642,7 +642,7 @@ advanced_settings() { fi done <<< "$BRIDGES" - BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge:\nTo add a description, edit:\n/usr/local/community-scripts/bridge_description\nExample: vmbr0:LAN vmbr1:WAN" 18 80 6 "${BRIDGE_MENU_OPTIONS[@]}" 3>&1 1>&2 2>&3) + BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge:\n To add a description, edit:\n /usr/local/community-scripts/bridge_description\n Example: vmbr0:LAN vmbr1:WAN" 18 55 6 "${BRIDGE_MENU_OPTIONS[@]}" 3>&1 1>&2 2>&3) if [[ -z "$BRG" ]]; then exit_script else From 27e9d4a63d5c5f5a922b9a35c8364b64b76a96cd Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner <73236783+michelroegl-brunner@users.noreply.github.com> Date: Fri, 26 Sep 2025 15:31:11 +0200 Subject: [PATCH 1187/1733] Update pull request template to address AI scripts Added a note regarding AI-generated scripts in the PR template. --- .github/pull_request_template.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 0f3ff54ac..758405626 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,6 +1,8 @@ 🛑 **New scripts must first be submitted to [ProxmoxVED](https://github.com/community-scripts/ProxmoxVED) for testing.** PRs for new scripts that skip this process will be closed. +# Scripts wich are clearly AI generated and not further revied by the Author of this PR (in terms of Coding Standards and Script Layout) may be closed without review. + --- ## ✍️ Description From 478857603f29947ec0e18bfd591a4c61c06d33ff Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner <73236783+michelroegl-brunner@users.noreply.github.com> Date: Fri, 26 Sep 2025 15:31:41 +0200 Subject: [PATCH 1188/1733] Update pull request template for script submission Removed warning about submitting new scripts for testing and clarified AI-generated script review policy. --- .github/pull_request_template.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 758405626..ecbe54dc8 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,7 +1,4 @@ -🛑 **New scripts must first be submitted to [ProxmoxVED](https://github.com/community-scripts/ProxmoxVED) for testing.** -PRs for new scripts that skip this process will be closed. - -# Scripts wich are clearly AI generated and not further revied by the Author of this PR (in terms of Coding Standards and Script Layout) may be closed without review. +## **Scripts wich are clearly AI generated and not further revied by the Author of this PR (in terms of Coding Standards and Script Layout) may be closed without review.** --- From 8e0606e34a61630dc343847ce2930132d5a6b569 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner <73236783+michelroegl-brunner@users.noreply.github.com> Date: Fri, 26 Sep 2025 15:31:50 +0200 Subject: [PATCH 1189/1733] Update pull request template to remove AI warning Removed unnecessary warning about AI-generated scripts. --- .github/pull_request_template.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index ecbe54dc8..b1a6ee56f 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,7 +1,5 @@ ## **Scripts wich are clearly AI generated and not further revied by the Author of this PR (in terms of Coding Standards and Script Layout) may be closed without review.** ---- - ## ✍️ Description From 4524cb0bf31299cddf8f1c6b9ff3cb1b97d491bf Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sat, 27 Sep 2025 12:49:09 +0200 Subject: [PATCH 1190/1733] Add MyIP --- ct/myip.sh | 56 +++++++++++++++++++++++++++++++++++++++++ install/myip-install.sh | 53 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 ct/myip.sh create mode 100644 install/myip-install.sh diff --git a/ct/myip.sh b/ct/myip.sh new file mode 100644 index 000000000..0ba906f0c --- /dev/null +++ b/ct/myip.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://ipcheck.ing/ + +APP="MyIP" +var_tags="${var_tags:-network}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-512}" +var_disk="${var_disk:-2}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/myip ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "myip" "jason5ng32/MyIP"; then + msg_info "Stopping Services" + systemctl stop myip + msg_ok "Stopped Services" + + cp /opt/myip/.env /opt + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "myip" "jason5ng32/MyIP" "tarball" + mv /opt/.env /opt/myip + + msg_info "Starting Services" + systemctl start myip + msg_ok "Started Services" + + msg_ok "Updated Successfully" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:18966${CL}" diff --git a/install/myip-install.sh b/install/myip-install.sh new file mode 100644 index 000000000..5a32f8446 --- /dev/null +++ b/install/myip-install.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://ipcheck.ing/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +NODE_VERSION="22" setup_nodejs +fetch_and_deploy_gh_release "myip" "jason5ng32/MyIP" "tarball" + +msg_info "Configuring MyIP" +cd /opt/myip +cp .env.example .env +$STD npm install +$STD npm run build +msg_ok "Configured MyIP" + +msg_info "Creating Service" +cat </etc/systemd/system/myip.service +[Unit] +Description=MyIP Service +After=network.target + +[Service] +Type=simple +User=root +WorkingDirectory=/opt/myip +ExecStart=/usr/bin/npm start +EnvironmentFile=/opt/myip/.env +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now myip +msg_ok "Service created" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 5c2793383a0d6d89c92078e182a70f3283308e78 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sat, 27 Sep 2025 13:04:51 +0200 Subject: [PATCH 1191/1733] Update MyIP --- frontend/public/json/myip.json | 35 ++++++++++++++++++++++++++++++++++ install/myip-install.sh | 1 + 2 files changed, 36 insertions(+) create mode 100644 frontend/public/json/myip.json diff --git a/frontend/public/json/myip.json b/frontend/public/json/myip.json new file mode 100644 index 000000000..a8cbda5df --- /dev/null +++ b/frontend/public/json/myip.json @@ -0,0 +1,35 @@ +{ + "name": "MyIP", + "slug": "myip", + "categories": [ + 4 + ], + "date_created": "2025-07-02", + "type": "ct", + "updateable": true, + "privileged": false, + "config_path": "/opt/myip/.env", + "interface_port": 18966, + "documentation": "https://github.com/jason5ng32/MyIP#-environment-variable", + "website": "https://ipcheck.ing/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/MyIP.svg", + "description": "The best IP Toolbox. Easy to check what's your IPs, IP geolocation, check for DNS leaks, examine WebRTC connections, speed test, ping test, MTR test, check website availability, whois search and more!", + "install_methods": [ + { + "type": "default", + "script": "ct/myip.sh", + "resources": { + "cpu": 1, + "ram": 512, + "hdd": 2, + "os": "Debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} diff --git a/install/myip-install.sh b/install/myip-install.sh index 5a32f8446..019e399c5 100644 --- a/install/myip-install.sh +++ b/install/myip-install.sh @@ -50,4 +50,5 @@ customize msg_info "Cleaning up" $STD apt-get -y autoremove $STD apt-get -y autoclean +$STD apt -y clean msg_ok "Cleaned" From f572e666acbb182c2c11897a7b7cdd64a8a0ca50 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sat, 27 Sep 2025 21:52:54 +0200 Subject: [PATCH 1192/1733] GoAway update --- install/goaway-install.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/install/goaway-install.sh b/install/goaway-install.sh index 817e57544..bff3c7e79 100644 --- a/install/goaway-install.sh +++ b/install/goaway-install.sh @@ -40,7 +40,11 @@ WantedBy=multi-user.target EOF systemctl enable -q --now goaway sleep 10 -ADMIN_PASS=$(awk -F': ' '/Randomly generated admin password:/ {print $2}' /var/log/goaway.log | tail -n1) +ADMIN_PASS=$( + tail -F /var/log/goaway.log 2>/dev/null \ + | grep -m1 'Randomly generated admin password:' \ + | awk -F': ' '{print $2}' +) { echo "GoAway Credentials" echo "Admin User: admin" From dfd18d869740792eb47f87f5058b234e6c9fc9e0 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sat, 27 Sep 2025 21:53:42 +0200 Subject: [PATCH 1193/1733] GoAway update --- install/goaway-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/install/goaway-install.sh b/install/goaway-install.sh index bff3c7e79..6f79ab393 100644 --- a/install/goaway-install.sh +++ b/install/goaway-install.sh @@ -39,7 +39,6 @@ RestartSec=5 WantedBy=multi-user.target EOF systemctl enable -q --now goaway -sleep 10 ADMIN_PASS=$( tail -F /var/log/goaway.log 2>/dev/null \ | grep -m1 'Randomly generated admin password:' \ From f4d9317f4dcebb2bfb996ba4a0d63fc747a39370 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sat, 27 Sep 2025 21:58:48 +0200 Subject: [PATCH 1194/1733] GoAway update --- install/goaway-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/goaway-install.sh b/install/goaway-install.sh index 6f79ab393..72589456e 100644 --- a/install/goaway-install.sh +++ b/install/goaway-install.sh @@ -40,7 +40,7 @@ WantedBy=multi-user.target EOF systemctl enable -q --now goaway ADMIN_PASS=$( - tail -F /var/log/goaway.log 2>/dev/null \ + tail -n0 -f /var/log/goaway.log 2>/dev/null \ | grep -m1 'Randomly generated admin password:' \ | awk -F': ' '{print $2}' ) From 2595154233ba99432a82bc864c24e410d5c8ce45 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sat, 27 Sep 2025 22:07:54 +0200 Subject: [PATCH 1195/1733] GoAway update --- install/goaway-install.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/install/goaway-install.sh b/install/goaway-install.sh index 72589456e..9703223f6 100644 --- a/install/goaway-install.sh +++ b/install/goaway-install.sh @@ -39,11 +39,11 @@ RestartSec=5 WantedBy=multi-user.target EOF systemctl enable -q --now goaway -ADMIN_PASS=$( - tail -n0 -f /var/log/goaway.log 2>/dev/null \ - | grep -m1 'Randomly generated admin password:' \ - | awk -F': ' '{print $2}' -) +for i in {1..30}; do + ADMIN_PASS=$(awk -F': ' '/Randomly generated admin password:/ {print $2; exit}' /var/log/goaway.log) + [ -n "$ADMIN_PASS" ] && break + sleep 1 +done { echo "GoAway Credentials" echo "Admin User: admin" From fa7da3bafa254a571a1f9310883245b24917e5fb Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sat, 27 Sep 2025 22:20:28 +0200 Subject: [PATCH 1196/1733] GoAway update --- install/goaway-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/goaway-install.sh b/install/goaway-install.sh index 9703223f6..e3bc6d602 100644 --- a/install/goaway-install.sh +++ b/install/goaway-install.sh @@ -40,7 +40,7 @@ WantedBy=multi-user.target EOF systemctl enable -q --now goaway for i in {1..30}; do - ADMIN_PASS=$(awk -F': ' '/Randomly generated admin password:/ {print $2; exit}' /var/log/goaway.log) + ADMIN_PASS=$(awk -F': ' "/Randomly generated admin password:/ {print \$2; exit}" /var/log/goaway.log) [ -n "$ADMIN_PASS" ] && break sleep 1 done From 23c631ee28d81aabfd25aff89acb48a2e48608da Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sat, 27 Sep 2025 22:24:59 +0200 Subject: [PATCH 1197/1733] Upda --- install/goaway-install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install/goaway-install.sh b/install/goaway-install.sh index e3bc6d602..7d8afcfd5 100644 --- a/install/goaway-install.sh +++ b/install/goaway-install.sh @@ -39,6 +39,7 @@ RestartSec=5 WantedBy=multi-user.target EOF systemctl enable -q --now goaway +sleep 10 for i in {1..30}; do ADMIN_PASS=$(awk -F': ' "/Randomly generated admin password:/ {print \$2; exit}" /var/log/goaway.log) [ -n "$ADMIN_PASS" ] && break From 11e4fcd98d041b8f87d37e2399226cff6fd2d272 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 29 Sep 2025 09:34:53 +0200 Subject: [PATCH 1198/1733] cleanup --- ct/goaway.sh | 54 ---------------- ct/verdaccio.sh | 49 --------------- frontend/public/json/goaway.json | 40 ------------ frontend/public/json/scraparr.json | 40 ------------ frontend/public/json/signoz.json | 40 ------------ frontend/public/json/verdaccio.json | 40 ------------ install/goaway-install.sh | 62 ------------------- install/verdaccio-install.sh | 96 ----------------------------- 8 files changed, 421 deletions(-) delete mode 100644 ct/goaway.sh delete mode 100644 ct/verdaccio.sh delete mode 100644 frontend/public/json/goaway.json delete mode 100644 frontend/public/json/scraparr.json delete mode 100644 frontend/public/json/signoz.json delete mode 100644 frontend/public/json/verdaccio.json delete mode 100644 install/goaway-install.sh delete mode 100644 install/verdaccio-install.sh diff --git a/ct/goaway.sh b/ct/goaway.sh deleted file mode 100644 index 6fb59c6db..000000000 --- a/ct/goaway.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Slaviša Arežina (tremor021) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/pommee/goaway - -APP="GoAway" -var_tags="${var_tags:-network}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-1024}" -var_disk="${var_disk:-4}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/goaway ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if check_for_gh_release "goaway" "pommee/goaway"; then - msg_info "Stopping Services" - systemctl stop goaway - msg_ok "Stopped Services" - - fetch_and_deploy_gh_release "goaway" "pommee/goaway" "prebuild" "latest" "/opt/goaway" "goaway_*_linux_amd64.tar.gz" - - msg_info "Starting Services" - systemctl start goaway - msg_ok "Started Services" - - msg_ok "Updated Successfully" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" diff --git a/ct/verdaccio.sh b/ct/verdaccio.sh deleted file mode 100644 index 099755a4a..000000000 --- a/ct/verdaccio.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: BrynnJKnight -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://verdaccio.org/ - -APP="Verdaccio" -var_tags="${var_tags:-dev-tools;npm;registry}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-8}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -f /etc/systemd/system/verdaccio.service ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - msg_info "Updating LXC Container" - $STD apt update - $STD apt -y upgrade - msg_ok "Updated LXC Container" - - NODE_VERSION="22" NODE_MODULE="verdaccio" setup_nodejs - systemctl restart verdaccio - msg_ok "Updated Successfully" - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:4873${CL}" diff --git a/frontend/public/json/goaway.json b/frontend/public/json/goaway.json deleted file mode 100644 index c15cead32..000000000 --- a/frontend/public/json/goaway.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "GoAway", - "slug": "goaway", - "categories": [ - 5 - ], - "date_created": "2025-09-12", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 8080, - "documentation": "https://github.com/pommee/goaway#configuration-file", - "config_path": "/opt/goaway/config/settings.yaml", - "website": "https://github.com/pommee/goaway", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/goaway.webp", - "description": "Lightweight DNS sinkhole written in Go with a modern dashboard client. Very good looking new alternative to Pi-Hole and Adguard Home.", - "install_methods": [ - { - "type": "default", - "script": "ct/goaway.sh", - "resources": { - "cpu": 1, - "ram": 1024, - "hdd": 4, - "os": "Debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "Type `cat ~/goaway.creds` to see login credentials.", - "type": "info" - } - ] -} diff --git a/frontend/public/json/scraparr.json b/frontend/public/json/scraparr.json deleted file mode 100644 index 9badfbcce..000000000 --- a/frontend/public/json/scraparr.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "Scraparr", - "slug": "scraparr", - "categories": [ - 14 - ], - "date_created": "2025-07-29", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 7100, - "documentation": "https://github.com/thecfu/scraparr/blob/main/README.md", - "website": "https://github.com/thecfu/scraparr", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/scraparr-dark.svg", - "config_path": "/scraparr/config/config.yaml", - "description": "Scraparr is a Prometheus exporter for the *arr suite (Sonarr, Radarr, Lidarr, etc.). It provides metrics that can be scraped by Prometheus to monitor and visualize the health and performance of your *arr applications.", - "install_methods": [ - { - "type": "default", - "script": "ct/scraparr.sh", - "resources": { - "cpu": 2, - "ram": 1024, - "hdd": 4, - "os": "debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "Edit config file then restart the scraparr service: `systemctl restart scraparr`", - "type": "info" - } - ] -} diff --git a/frontend/public/json/signoz.json b/frontend/public/json/signoz.json deleted file mode 100644 index 07b532b0c..000000000 --- a/frontend/public/json/signoz.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "SigNoz", - "slug": "signoz", - "categories": [ - 9 - ], - "date_created": "2025-09-12", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 8080, - "documentation": "https://signoz.io/docs/introduction/", - "config_path": "/opt/signoz/conf/systemd.env", - "website": "https://signoz.io/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/signoz.webp", - "description": "SigNoz is an open-source Datadog or New Relic alternative. Get APM, logs, traces, metrics, exceptions, & alerts in a single tool.", - "install_methods": [ - { - "type": "default", - "script": "ct/signoz.sh", - "resources": { - "cpu": 2, - "ram": 4096, - "hdd": 20, - "os": "Debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "The first user you register will be the admin user.", - "type": "info" - } - ] -} diff --git a/frontend/public/json/verdaccio.json b/frontend/public/json/verdaccio.json deleted file mode 100644 index e1194b785..000000000 --- a/frontend/public/json/verdaccio.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "Verdaccio", - "slug": "verdaccio", - "categories": [ - 20 - ], - "date_created": "2025-09-19", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 4873, - "documentation": "https://verdaccio.org/docs/what-is-verdaccio", - "website": "https://verdaccio.org/", - "logo": "https://verdaccio.org/img/logo/symbol/png/verdaccio-tiny.png", - "config_path": "/opt/verdaccio/config/config.yaml", - "description": "Verdaccio is a lightweight private npm proxy registry built with Node.js. It allows you to host your own npm registry with minimal configuration, providing a private npm repository for your projects. Verdaccio supports npm, yarn, and pnpm, and can cache packages from the public npm registry, allowing for faster installs and protection against npm registry outages. It includes a web interface for browsing packages, authentication and authorization features, and can be easily integrated into your development workflow.", - "install_methods": [ - { - "type": "default", - "script": "ct/verdaccio.sh", - "resources": { - "cpu": 2, - "ram": 2048, - "hdd": 8, - "os": "debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "To create the first user, run: npm adduser --registry http://:4873", - "type": "info" - } - ] -} \ No newline at end of file diff --git a/install/goaway-install.sh b/install/goaway-install.sh deleted file mode 100644 index 7d8afcfd5..000000000 --- a/install/goaway-install.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Slaviša Arežina (tremor021) -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://github.com/pommee/goaway - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt-get install -y net-tools -msg_ok "Installed Dependencies" - -fetch_and_deploy_gh_release "goaway" "pommee/goaway" "prebuild" "latest" "/opt/goaway" "goaway_*_linux_amd64.tar.gz" - -msg_info "Creating Service" -cat </etc/systemd/system/goaway.service -[Unit] -Description=GoAway Service -After=network.target - -[Service] -Type=simple -User=root -WorkingDirectory=/opt/goaway -ExecStart=/opt/goaway/goaway -StandardOutput=file:/var/log/goaway.log -StandardError=inherit -Restart=on-failure -RestartSec=5 - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now goaway -sleep 10 -for i in {1..30}; do - ADMIN_PASS=$(awk -F': ' "/Randomly generated admin password:/ {print \$2; exit}" /var/log/goaway.log) - [ -n "$ADMIN_PASS" ] && break - sleep 1 -done -{ - echo "GoAway Credentials" - echo "Admin User: admin" - echo "Admin Password: $ADMIN_PASS" -} >>~/goaway.creds -msg_ok "Service Created" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt -y autoremove -$STD apt -y autoclean -$STD apt -y clean -msg_ok "Cleaned" diff --git a/install/verdaccio-install.sh b/install/verdaccio-install.sh deleted file mode 100644 index 1586217b5..000000000 --- a/install/verdaccio-install.sh +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: BrynnJKnight -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://verdaccio.org/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt-get install -y \ - ca-certificates \ - build-essential -msg_ok "Installed Dependencies" - -NODE_VERSION="22" NODE_MODULE="verdaccio" setup_nodejs - -msg_info "Configuring Verdaccio" -mkdir -p /opt/verdaccio/config -mkdir -p /opt/verdaccio/storage - -cat </opt/verdaccio/config/config.yaml -# Verdaccio configuration -storage: /opt/verdaccio/storage -auth: - htpasswd: - file: /opt/verdaccio/storage/htpasswd - max_users: 1000 -uplinks: - npmjs: - url: https://registry.npmjs.org/ -packages: - '@*/*': - access: \$all - publish: \$authenticated - proxy: npmjs - '**': - access: \$all - publish: \$authenticated - proxy: npmjs -middlewares: - audit: - enabled: true -logs: - - {type: stdout, format: pretty, level: http} -listen: - - 0.0.0.0:4873 -web: - enable: true - title: Verdaccio - gravatar: true - sort_packages: asc - login: true -EOF - -chown -R root:root /opt/verdaccio -chmod -R 755 /opt/verdaccio -msg_ok "Configured Verdaccio" - -msg_info "Creating Service" -cat </etc/systemd/system/verdaccio.service -[Unit] -Description=Verdaccio lightweight private npm proxy registry -After=network.target - -[Service] -Type=simple -ExecStart=/usr/bin/verdaccio --config /opt/verdaccio/config/config.yaml -Restart=on-failure -StandardOutput=journal -StandardError=journal -SyslogIdentifier=verdaccio -KillMode=control-group - -[Install] -WantedBy=multi-user.target -EOF - -systemctl enable -q --now verdaccio -msg_ok "Created Service" - - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt -y autoremove -$STD apt -y autoclean -$STD apt -y clean -msg_ok "Cleaned" From 4148fffa2d275569e5ceb44abda36b56e071ffa4 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 29 Sep 2025 09:38:20 +0200 Subject: [PATCH 1199/1733] Update livebook-install.sh --- install/livebook-install.sh | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/install/livebook-install.sh b/install/livebook-install.sh index 5d922b721..1d9b98d3d 100644 --- a/install/livebook-install.sh +++ b/install/livebook-install.sh @@ -23,18 +23,25 @@ $STD apt-get install -y \ msg_ok "Installed Dependencies" msg_info "Creating livebook user" +mkdir -p /opt/livebook /data +export HOME=/opt/livebook $STD adduser --system --group --home /opt/livebook --shell /bin/bash livebook msg_ok "Created livebook user" -msg_info "Installing Erlang and Elixir" -mkdir -p /opt/livebook /data -export HOME=/opt/livebook -cd /opt/livebook - -curl -fsSO https://elixir-lang.org/install.sh -$STD sh install.sh elixir@latest otp@latest +msg_warn "WARNING: This script will run an external installer from a third-party source (https://elixir-lang.org)." +msg_warn "The following code is NOT maintained or audited by our repository." +msg_warn "If you have any doubts or concerns, please review the installer code before proceeding:" +msg_custom "${TAB3}${GATEWAY}${BGN}${CL}" "\e[1;34m" "→ https://elixir-lang.org/install.sh" +echo +read -r -p "${TAB3}Do you want to continue? [y/N]: " CONFIRM +if [[ ! "$CONFIRM" =~ ^([yY][eE][sS]|[yY])$ ]]; then + msg_error "Aborted by user. No changes have been made." + exit 10 +fi +bash <(curl -sL https://elixir-lang.org/install.sh) +msg_info "Setup Erlang and Elixir" ERLANG_VERSION=$(ls /opt/livebook/.elixir-install/installs/otp/ | head -n1) ELIXIR_VERSION=$(ls /opt/livebook/.elixir-install/installs/elixir/ | head -n1) LIVEBOOK_PASSWORD=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c16) @@ -60,7 +67,10 @@ export ERLANG_BIN="/opt/livebook/.elixir-install/installs/otp/\${ERLANG_VERSION} export ELIXIR_BIN="/opt/livebook/.elixir-install/installs/elixir/\${ELIXIR_VERSION}/bin" export PATH="\$ESCRIPTS_BIN:\$ERLANG_BIN:\$ELIXIR_BIN:\$PATH" EOF - +cat </opt/livebook/livebook.creds +Livebook-Credentials +Livebook Password: $LIVEBOOK_PASSWORD +EOF msg_ok "Installed Erlang $ERLANG_VERSION and Elixir $ELIXIR_VERSION" msg_info "Installing Livebook" @@ -82,24 +92,14 @@ RestartSec=5 [Install] WantedBy=multi-user.target EOF - chown -R livebook:livebook /opt/livebook /data - systemctl enable -q --now livebook msg_ok "Installed Livebook" -msg_info "Saving Livebook credentials" -cat </opt/livebook/livebook.creds -Livebook-Credentials -Livebook Password: $LIVEBOOK_PASSWORD -EOF -msg_ok "Livebook password stored in /opt/livebook/livebook.creds" - motd_ssh customize msg_info "Cleaning Up" -rm -f /opt/install.sh $STD apt-get autoremove -y $STD apt-get autoclean msg_ok "Cleaned Up" From d0ce943d6c660c858eef059f0ce54c940e9a1994 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 29 Sep 2025 10:06:34 +0200 Subject: [PATCH 1200/1733] Update install scripts for Livebook and Viseron Set default Ubuntu version to 24.04 in ct/livebook.sh. Improve GPU detection and PyTorch installation logic in viseron-install.sh, supporting NVIDIA, Intel, AMD, and CPU-only environments. Update Python version to 3.13 and adjust credential file naming. Minor formatting fix in livebook-install.sh. --- ct/livebook.sh | 2 +- install/livebook-install.sh | 2 +- install/viseron-install.sh | 79 ++++++++++++++++++++++++------------- 3 files changed, 53 insertions(+), 30 deletions(-) diff --git a/ct/livebook.sh b/ct/livebook.sh index 5f801ac75..3afc43531 100755 --- a/ct/livebook.sh +++ b/ct/livebook.sh @@ -11,7 +11,7 @@ var_disk="${var_disk:-4}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_os="${var_os:-ubuntu}" -var_version="${var_version:-24}" +var_version="${var_version:-24.04}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" diff --git a/install/livebook-install.sh b/install/livebook-install.sh index 1d9b98d3d..902d86d2b 100644 --- a/install/livebook-install.sh +++ b/install/livebook-install.sh @@ -99,7 +99,7 @@ msg_ok "Installed Livebook" motd_ssh customize -msg_info "Cleaning Up" +msg_info "Cleaning Up" $STD apt-get autoremove -y $STD apt-get autoclean msg_ok "Cleaned Up" diff --git a/install/viseron-install.sh b/install/viseron-install.sh index e0835a595..78967057b 100644 --- a/install/viseron-install.sh +++ b/install/viseron-install.sh @@ -36,46 +36,69 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8' $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC'" { - echo "Hanko-Credentials" - echo "Hanko Database User: $DB_USER" - echo "Hanko Database Password: $DB_PASS" - echo "Hanko Database Name: $DB_NAME" -} >>~/hanko.creds + echo "Viseron-Credentials" + echo "Viseron Database User: $DB_USER" + echo "Viseron Database Password: $DB_PASS" + echo "Viseron Database Name: $DB_NAME" +} >>~/viseron.creds msg_ok "Set up PostgreSQL Database" -# msg_info "Setting up Hardware Acceleration" -# if [[ "$CTTYPE" == "0" ]]; then -# chgrp video /dev/dri -# chmod 755 /dev/dri -# chmod 660 /dev/dri/* -# fi -# msg_ok "Hardware Acceleration Configured" +msg_info "Setting up Hardware Acceleration" +if [[ "$CTTYPE" == "0" ]]; then + chgrp video /dev/dri + chmod 755 /dev/dri + chmod 660 /dev/dri/* +fi +msg_ok "Hardware Acceleration Configured" -PYTHON_VERSION="3.12" setup_uv +PYTHON_VERSION="3.13" setup_uv fetch_and_deploy_gh_release "viseron" "roflcoopter/viseron" "tarball" "latest" "/opt/viseron" msg_info "Setting up Python Environment" -uv venv --python "python3.12" /opt/viseron/.venv +uv venv --python "python3.13" /opt/viseron/.venv uv pip install --python /opt/viseron/.venv/bin/python --upgrade pip setuptools wheel msg_ok "Python Environment Setup" msg_info "Setup Viseron (Patience)" -if ls /dev/nvidia* >/dev/null 2>&1; then - msg_info "GPU detected → Installing PyTorch with CUDA" - UV_HTTP_TIMEOUT=600 uv pip install --python /opt/viseron/.venv/bin/python \ - torch==2.8.0 torchvision==0.19.0 torchaudio==2.8.0 - msg_ok "Installed Torch with CUDA" -else - msg_info "No GPU detected → Installing CPU-only PyTorch" - UV_HTTP_TIMEOUT=600 uv pip install --python /opt/viseron/.venv/bin/python \ - torch==2.8.0+cpu torchvision==0.19.0+cpu torchaudio==2.8.0+cpu \ - --extra-index-url https://download.pytorch.org/whl/cpu - msg_ok "Installed Torch CPU-only" -fi -UV_HTTP_TIMEOUT=600 uv pip install --python /opt/viseron/.venv/bin/python -e /opt/viseron/. +GPU_VENDOR=$(lspci | grep -E "VGA|3D" | grep -oE "NVIDIA|Intel|AMD" | head -n1) + +case "$GPU_VENDOR" in + NVIDIA) + msg_info "NVIDIA GPU detected → Installing PyTorch with CUDA" + UV_HTTP_TIMEOUT=1200 uv pip install --python /opt/viseron/.venv/bin/python \ + 'torch>=2.2,<3.0' 'torchvision>=0.17,<1.0' 'torchaudio>=2.2,<3.0' + msg_ok "Installed Torch with CUDA" + ;; + Intel) + msg_info "Intel GPU detected → Installing PyTorch with Intel Extension" + UV_HTTP_TIMEOUT=1200 uv pip install --python /opt/viseron/.venv/bin/python \ + 'torch>=2.2,<3.0' 'torchvision>=0.17,<1.0' 'torchaudio>=2.2,<3.0' \ + intel-extension-for-pytorch + msg_ok "Installed Torch with Intel Extension" + ;; + AMD) + msg_info "AMD GPU detected → Installing PyTorch with ROCm" + UV_HTTP_TIMEOUT=1200 uv pip install --python /opt/viseron/.venv/bin/python \ + 'torch>=2.2,<3.0' 'torchvision>=0.17,<1.0' 'torchaudio>=2.2,<3.0' \ + --index-url https://download.pytorch.org/whl/rocm6.0 + msg_ok "Installed Torch with ROCm" + ;; + *) + msg_info "No GPU detected → Installing CPU-only PyTorch" + UV_HTTP_TIMEOUT=1200 uv pip install --python /opt/viseron/.venv/bin/python \ + 'torch>=2.2,<3.0' 'torchvision>=0.17,<1.0' 'torchaudio>=2.2,<3.0' \ + --extra-index-url https://download.pytorch.org/whl/cpu + msg_ok "Installed Torch CPU-only" + ;; +esac + UV_HTTP_TIMEOUT=600 uv pip install --python /opt/viseron/.venv/bin/python -r /opt/viseron/requirements.txt +UV_HTTP_TIMEOUT=600 uv pip install --python /opt/viseron/.venv/bin/python -e /opt/viseron/. + mkdir -p /config/{recordings,snapshots,segments,event_clips,thumbnails} -for d in recordings snapshots segments event_clips thumbnails; do ln -s "/config/$d" "/$d" 2>/dev/null || true; done +for d in recordings snapshots segments event_clips thumbnails; do + ln -sfn "/config/$d" "/$d" +done msg_ok "Setup Viseron" msg_info "Creating Default Configuration" From 74c5a993f416c8c15761f627f9ef0e8c1974ece6 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 29 Sep 2025 08:06:59 +0000 Subject: [PATCH 1201/1733] Update .app files --- ct/headers/goaway | 6 ------ ct/headers/myip | 6 ++++++ ct/headers/verdaccio | 6 ------ 3 files changed, 6 insertions(+), 12 deletions(-) delete mode 100644 ct/headers/goaway create mode 100644 ct/headers/myip delete mode 100644 ct/headers/verdaccio diff --git a/ct/headers/goaway b/ct/headers/goaway deleted file mode 100644 index 2e07514b1..000000000 --- a/ct/headers/goaway +++ /dev/null @@ -1,6 +0,0 @@ - ______ ___ - / ____/___ / |_ ______ ___ __ - / / __/ __ \/ /| | | /| / / __ `/ / / / -/ /_/ / /_/ / ___ | |/ |/ / /_/ / /_/ / -\____/\____/_/ |_|__/|__/\__,_/\__, / - /____/ diff --git a/ct/headers/myip b/ct/headers/myip new file mode 100644 index 000000000..d7a8d566c --- /dev/null +++ b/ct/headers/myip @@ -0,0 +1,6 @@ + __ ___ ________ + / |/ /_ __/ _/ __ \ + / /|_/ / / / // // /_/ / + / / / / /_/ // // ____/ +/_/ /_/\__, /___/_/ + /____/ diff --git a/ct/headers/verdaccio b/ct/headers/verdaccio deleted file mode 100644 index bf03eeff7..000000000 --- a/ct/headers/verdaccio +++ /dev/null @@ -1,6 +0,0 @@ - _ __ __ _ -| | / /__ _________/ /___ ___________(_)___ -| | / / _ \/ ___/ __ / __ `/ ___/ ___/ / __ \ -| |/ / __/ / / /_/ / /_/ / /__/ /__/ / /_/ / -|___/\___/_/ \__,_/\__,_/\___/\___/_/\____/ - From 88fd3a63ed189f1c281b1f90896d41c4a89c3e2e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 29 Sep 2025 10:14:16 +0200 Subject: [PATCH 1202/1733] Update dependencies and Python version in install script Added pciutils to the list of dependencies and updated the Python version used for setup_uv from 3.12 to 3.13. Removed duplicate setup_uv and fetch_and_deploy_gh_release calls to streamline the installation process. --- install/viseron-install.sh | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/install/viseron-install.sh b/install/viseron-install.sh index 78967057b..82b620e5e 100644 --- a/install/viseron-install.sh +++ b/install/viseron-install.sh @@ -16,15 +16,15 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ python3-opencv jq \ - libgl1-mesa-glx libglib2.0-0 \ + libgl1-mesa-glx libglib2.0-0 pciutils \ libgstreamer1.0-0 libgstreamer-plugins-base1.0-0 \ gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-libav \ build-essential python3-dev python3-gi pkg-config libcairo2-dev gir1.2-glib-2.0 \ cmake gfortran libopenblas-dev liblapack-dev libgirepository1.0-dev git msg_ok "Installed Dependencies" -PYTHON_VERSION="3.12" setup_uv PG_VERSION="16" setup_postgresql +PYTHON_VERSION="3.13" setup_uv msg_info "Setting up PostgreSQL Database" DB_NAME=viseron @@ -51,9 +51,6 @@ if [[ "$CTTYPE" == "0" ]]; then fi msg_ok "Hardware Acceleration Configured" -PYTHON_VERSION="3.13" setup_uv -fetch_and_deploy_gh_release "viseron" "roflcoopter/viseron" "tarball" "latest" "/opt/viseron" - msg_info "Setting up Python Environment" uv venv --python "python3.13" /opt/viseron/.venv uv pip install --python /opt/viseron/.venv/bin/python --upgrade pip setuptools wheel From d0a8f3a81bedfe42552693a9aa22c38d117c468e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 29 Sep 2025 10:21:12 +0200 Subject: [PATCH 1203/1733] Update build.func --- misc/build.func | 1 + 1 file changed, 1 insertion(+) diff --git a/misc/build.func b/misc/build.func index 3a68098ca..cd31f9a59 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2160,6 +2160,7 @@ build_container() { "jellyfin" "plex" "scrypted" "tdarr" "unmanic" "ollama" "fileflows" "open-webui" "tunarr" "debian" "handbrake" "sunshine" "moonlight" "kodi" "stremio" + "viseron" ) # ------------------------------------------------------------------------------ From 35b28969beeb885733a0f024e0f8e36bdffd253a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 29 Sep 2025 10:24:25 +0200 Subject: [PATCH 1204/1733] Update PyTorch install commands for GPU detection Simplifies PyTorch, torchvision, and torchaudio installation by removing version constraints and adding appropriate index URLs for Intel and CPU-only setups. Also clarifies log messages and changes the default case to 'CPU' for no GPU detected. --- install/viseron-install.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/install/viseron-install.sh b/install/viseron-install.sh index 82b620e5e..e7509bcd8 100644 --- a/install/viseron-install.sh +++ b/install/viseron-install.sh @@ -63,27 +63,27 @@ case "$GPU_VENDOR" in NVIDIA) msg_info "NVIDIA GPU detected → Installing PyTorch with CUDA" UV_HTTP_TIMEOUT=1200 uv pip install --python /opt/viseron/.venv/bin/python \ - 'torch>=2.2,<3.0' 'torchvision>=0.17,<1.0' 'torchaudio>=2.2,<3.0' + torch torchvision torchaudio msg_ok "Installed Torch with CUDA" ;; Intel) - msg_info "Intel GPU detected → Installing PyTorch with Intel Extension" + msg_info "Intel GPU detected → Installing PyTorch with Intel Extension (CPU wheels)" UV_HTTP_TIMEOUT=1200 uv pip install --python /opt/viseron/.venv/bin/python \ - 'torch>=2.2,<3.0' 'torchvision>=0.17,<1.0' 'torchaudio>=2.2,<3.0' \ - intel-extension-for-pytorch + torch torchvision torchaudio intel-extension-for-pytorch \ + --extra-index-url https://download.pytorch.org/whl/cpu msg_ok "Installed Torch with Intel Extension" ;; AMD) msg_info "AMD GPU detected → Installing PyTorch with ROCm" UV_HTTP_TIMEOUT=1200 uv pip install --python /opt/viseron/.venv/bin/python \ - 'torch>=2.2,<3.0' 'torchvision>=0.17,<1.0' 'torchaudio>=2.2,<3.0' \ + torch torchvision torchaudio \ --index-url https://download.pytorch.org/whl/rocm6.0 msg_ok "Installed Torch with ROCm" ;; - *) + CPU) msg_info "No GPU detected → Installing CPU-only PyTorch" UV_HTTP_TIMEOUT=1200 uv pip install --python /opt/viseron/.venv/bin/python \ - 'torch>=2.2,<3.0' 'torchvision>=0.17,<1.0' 'torchaudio>=2.2,<3.0' \ + torch torchvision torchaudio \ --extra-index-url https://download.pytorch.org/whl/cpu msg_ok "Installed Torch CPU-only" ;; From 6560a7c212c0c93dfc586f0213dd223968c36bf4 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 29 Sep 2025 10:29:35 +0200 Subject: [PATCH 1205/1733] Remove redundant install and environment steps Eliminated duplicate pip install command and unnecessary PATH environment variable from the Viseron install script to streamline setup. --- install/viseron-install.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/install/viseron-install.sh b/install/viseron-install.sh index e7509bcd8..d95d626be 100644 --- a/install/viseron-install.sh +++ b/install/viseron-install.sh @@ -89,7 +89,6 @@ case "$GPU_VENDOR" in ;; esac -UV_HTTP_TIMEOUT=600 uv pip install --python /opt/viseron/.venv/bin/python -r /opt/viseron/requirements.txt UV_HTTP_TIMEOUT=600 uv pip install --python /opt/viseron/.venv/bin/python -e /opt/viseron/. mkdir -p /config/{recordings,snapshots,segments,event_clips,thumbnails} @@ -171,7 +170,6 @@ After=network.target Type=simple User=root WorkingDirectory=/opt/viseron -Environment=PATH=/opt/viseron/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin ExecStart=/opt/viseron/.venv/bin/python -m viseron --config /config/viseron.yaml Restart=always RestartSec=10 From 633ca3edeeffa8e02482e2260eb782f4ee9b7334 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 29 Sep 2025 10:48:25 +0200 Subject: [PATCH 1206/1733] Refactor GPU passthrough logic in build.func Simplifies and streamlines GPU passthrough configuration for LXC containers, consolidating device detection, selection, and userland package installation. Reduces code duplication, improves readability, and adds direct verification steps for VAAPI and NVIDIA support after installation. --- misc/build.func | 270 +++++++++++++----------------------------------- 1 file changed, 73 insertions(+), 197 deletions(-) diff --git a/misc/build.func b/misc/build.func index cd31f9a59..69fe74b55 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2170,16 +2170,13 @@ build_container() { # Get device GID dynamically get_device_gid() { local group="$1" - local gid - gid=$(getent group "$group" 2>/dev/null | cut -d: -f3) - if [[ -z "$gid" ]]; then - case "$group" in - video) gid=44 ;; - render) gid=104 ;; - *) gid=44 ;; - esac - fi - echo "$gid" + gid=$(getent group "$group" | cut -d: -f3) + [[ -n "$gid" ]] && echo "$gid" && return + case "$group" in + video) echo 44 ;; + render) echo 104 ;; + *) echo 44 ;; + esac } # Check if app needs GPU @@ -2192,24 +2189,18 @@ build_container() { } # Detect available GPU devices + is_gpu_app() { + local app="${1,,}" + for gpu in "${GPU_APPS[@]}"; do + [[ "$gpu" == "$app" ]] && return 0 + done + return 1 + } + detect_gpu_devices() { - VAAPI_DEVICES=() - NVIDIA_DEVICES=() - - # VAAPI / Intel / AMD - for device in /dev/dri/renderD* /dev/dri/card*; do - [[ -e "$device" ]] || continue - VAAPI_DEVICES+=("$device") - done - - # NVIDIA - for device in /dev/nvidia*; do - [[ -e "$device" ]] || continue - NVIDIA_DEVICES+=("$device") - done - - msg_debug "Detected VAAPI devices: ${VAAPI_DEVICES[*]:-(none)}" - msg_debug "Detected NVIDIA devices: ${NVIDIA_DEVICES[*]:-(none)}" + VAAPI_DEVICES=(); NVIDIA_DEVICES=() + for d in /dev/dri/renderD* /dev/dri/card*; do [[ -e $d ]] && VAAPI_DEVICES+=("$d"); done + for d in /dev/nvidia*; do [[ -e $d ]] && NVIDIA_DEVICES+=("$d"); done } # Configure USB passthrough for privileged containers @@ -2233,142 +2224,64 @@ lxc.mount.entry: /dev/ttyACM1 dev/ttyACM1 none bind,optional,create= EOF msg_ok "USB passthrough configured" } - - # Configure VAAPI device - configure_vaapi_device() { - local device="$1" - local dev_index="$2" - + configure_vaapi_device() { + local dev="$1" idx="$2" if [[ "$CT_TYPE" == "0" ]]; then - # Privileged container - local major minor - major=$(stat -c '%t' "$device") - minor=$(stat -c '%T' "$device") - major=$((0x$major)) - minor=$((0x$minor)) - echo "lxc.cgroup2.devices.allow: c $major:$minor rwm" >>"$LXC_CONFIG" - echo "lxc.mount.entry: $device dev/$(basename "$device") none bind,optional,create=file" >>"$LXC_CONFIG" + major=$(stat -c '%t' "$dev"); minor=$(stat -c '%T' "$dev") + echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" + echo "lxc.mount.entry: $dev dev/$(basename "$dev") none bind,optional,create=file" >>"$LXC_CONFIG" else - # Unprivileged container - local gid - if [[ "$device" =~ renderD ]]; then - gid=$(get_device_gid "render") - else - gid=$(get_device_gid "video") - fi - echo "dev${dev_index}: $device,gid=$gid" >>"$LXC_CONFIG" + [[ "$dev" =~ renderD ]] && gid=$(get_device_gid "render") || gid=$(get_device_gid "video") + echo "dev$idx: $dev,gid=$gid" >>"$LXC_CONFIG" fi } - # Configure NVIDIA devices configure_nvidia_devices() { - for device in "${NVIDIA_DEVICES[@]}"; do + for dev in "${NVIDIA_DEVICES[@]}"; do if [[ "$CT_TYPE" == "0" ]]; then - local major minor - major=$(stat -c '%t' "$device") - minor=$(stat -c '%T' "$device") - major=$((0x$major)) - minor=$((0x$minor)) - echo "lxc.cgroup2.devices.allow: c $major:$minor rwm" >>"$LXC_CONFIG" - echo "lxc.mount.entry: $device dev/$(basename "$device") none bind,optional,create=file" >>"$LXC_CONFIG" + major=$(stat -c '%t' "$dev"); minor=$(stat -c '%T' "$dev") + echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" + echo "lxc.mount.entry: $dev dev/$(basename "$dev") none bind,optional,create=file" >>"$LXC_CONFIG" else - msg_warn "NVIDIA passthrough to unprivileged container may require additional configuration" + msg_warn "NVIDIA passthrough on unprivileged CT may fail" fi done - - if [[ -d /dev/dri ]] && [[ "$CT_TYPE" == "0" ]]; then - echo "lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" - fi + [[ -d /dev/dri && "$CT_TYPE" == "0" ]] && echo "lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" } - # Main GPU configuration logic configure_gpu_passthrough() { detect_gpu_devices + local should=false + if [[ "$CT_TYPE" == "0" ]] || is_gpu_app "$APP"; then should=true; fi + [[ "$should" == "false" ]] && return - # Check if we should configure GPU - local should_configure=false - if [[ "$CT_TYPE" == "0" ]] || is_gpu_app "$APP"; then - should_configure=true + if [[ ${#VAAPI_DEVICES[@]} -eq 0 && ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then + msg_info "No GPU devices found"; return fi - if [[ "$should_configure" == "false" ]]; then - return 0 - fi + local choices=() selected=() + [[ ${#VAAPI_DEVICES[@]} -gt 0 ]] && choices+=("VAAPI" "Intel/AMD GPU" "OFF") + [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]] && choices+=("NVIDIA" "NVIDIA GPU" "OFF") - # No GPU devices available - if [[ ${#VAAPI_DEVICES[@]} -eq 0 ]] && [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then - msg_info "No GPU devices detected on host" - return 0 - fi - - # Build selection options - local choices=() - local SELECTED_GPUS=() - - if [[ ${#VAAPI_DEVICES[@]} -gt 0 ]]; then - choices+=("VAAPI" "Intel/AMD GPU (${#VAAPI_DEVICES[@]} devices)" "OFF") - fi - - if [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then - choices+=("NVIDIA" "NVIDIA GPU (${#NVIDIA_DEVICES[@]} devices)" "OFF") - fi - - # Auto-select if only one type available if [[ ${#choices[@]} -eq 3 ]]; then - SELECTED_GPUS=("${choices[0]}") - msg_info "Auto-configuring ${choices[0]} GPU passthrough" - elif [[ ${#choices[@]} -gt 3 ]]; then - # Show selection dialog - local selected - selected=$(whiptail --title "GPU Passthrough Selection" \ - --checklist "Multiple GPU types detected. Select which to pass through:" \ - 12 60 $((${#choices[@]} / 3)) \ - "${choices[@]}" 3>&1 1>&2 2>&3) || { - msg_info "GPU passthrough skipped" - return 0 - } - - for item in $selected; do - SELECTED_GPUS+=("${item//\"/}") - done + selected=("VAAPI") + elif [[ ${#choices[@]} -eq 6 ]]; then + read -rp "Multiple GPUs found (VAAPI/NVIDIA). Which passthrough? " sel + [[ "$sel" =~ VAAPI|vaapi ]] && selected=("VAAPI") + [[ "$sel" =~ NVIDIA|nvidia ]] && selected=("NVIDIA") fi - # Apply configuration for selected GPUs - local dev_index=0 - for gpu_type in "${SELECTED_GPUS[@]}"; do - case "$gpu_type" in - VAAPI) - msg_info "Configuring VAAPI passthrough (${#VAAPI_DEVICES[@]} devices)" - for device in "${VAAPI_DEVICES[@]}"; do - configure_vaapi_device "$device" "$dev_index" - : "${dev_index:=0}" - dev_index=$((dev_index + 1)) - done - if [[ "$CT_TYPE" == "0" ]] && [[ -d /dev/dri ]]; then - echo "lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" - fi - export ENABLE_VAAPI=1 - ;; - NVIDIA) - msg_info "Configuring NVIDIA passthrough" - configure_nvidia_devices - export ENABLE_NVIDIA=1 - ;; + local idx=0 + for s in "${selected[@]}"; do + case "$s" in + VAAPI) for d in "${VAAPI_DEVICES[@]}"; do configure_vaapi_device "$d" "$idx"; idx=$((idx+1)); done; export ENABLE_VAAPI=1 ;; + NVIDIA) configure_nvidia_devices; export ENABLE_NVIDIA=1 ;; esac done - - [[ ${#SELECTED_GPUS[@]} -gt 0 ]] && msg_ok "GPU passthrough configured" - + [[ ${#selected[@]} -gt 0 ]] && msg_ok "GPU passthrough configured" } - # ------------------------------------------------------------------------------ - # Apply all hardware passthrough configurations - # ------------------------------------------------------------------------------ - - # USB passthrough (automatic for privileged) configure_usb_passthrough - - # GPU passthrough (based on container type and app) configure_gpu_passthrough # TUN device passthrough @@ -2438,67 +2351,30 @@ EOF fi fi - # # Install GPU userland packages - # install_gpu_userland() { - # local gpu_type="$1" + install_gpu_userland() { + local gpu="$1" + if [[ "$var_os" == "alpine" ]]; then + case "$gpu" in + VAAPI) pct exec "$CTID" -- apk add mesa-dri-gallium mesa-va-gallium intel-media-driver libva-utils ;; + NVIDIA) msg_warn "NVIDIA drivers not in Alpine repos" ;; + esac + else + case "$gpu" in + VAAPI) pct exec "$CTID" -- bash -c "apt-get update && apt-get install -y intel-media-va-driver-non-free mesa-va-drivers vainfo" ;; + NVIDIA) pct exec "$CTID" -- bash -c "apt-get update && apt-get install -y nvidia-driver nvidia-utils libnvidia-encode1" ;; + esac + fi + } - # if [ "$var_os" == "alpine" ]; then - # case "$gpu_type" in - # VAAPI) - # msg_info "Installing VAAPI packages in Alpine container" - # pct exec "$CTID" -- ash -c ' - # apk add --no-cache \ - # mesa-dri-gallium \ - # mesa-va-gallium \ - # intel-media-driver \ - # libva-utils >/dev/null 2>&1 - # ' || msg_warn "Some VAAPI packages may not be available in Alpine" - # ;; - # NVIDIA) - # msg_warn "NVIDIA drivers are not readily available in Alpine Linux" - # ;; - # esac - # else - # case "$gpu_type" in - # VAAPI) - # msg_info "Installing VAAPI userland packages" - # pct exec "$CTID" -- bash -c ' - # . /etc/os-release || true - # if [[ "${VERSION_CODENAME:-}" == "trixie" ]]; then - # cat >/etc/apt/sources.list.d/non-free.sources </dev/null 2>&1 - # DEBIAN_FRONTEND=noninteractive apt-get install -y \ - # intel-media-va-driver-non-free \ - # mesa-va-drivers \ - # libvpl2 \ - # vainfo \ - # ocl-icd-libopencl1 \ - # mesa-opencl-icd \ - # intel-gpu-tools >/dev/null 2>&1 - # ' && msg_ok "VAAPI userland installed" || msg_warn "Some VAAPI packages failed to install" - # ;; - # NVIDIA) - # msg_info "Installing NVIDIA userland packages" - # pct exec "$CTID" -- bash -c ' - # apt-get update >/dev/null 2>&1 - # DEBIAN_FRONTEND=noninteractive apt-get install -y \ - # nvidia-driver \ - # nvidia-utils \ - # libnvidia-encode1 \ - # libcuda1 >/dev/null 2>&1 - # ' && msg_ok "NVIDIA userland installed" || msg_warn "Some NVIDIA packages failed to install" - # ;; - # esac - # fi - # } - - # Customize container + if [[ "${ENABLE_VAAPI:-0}" == "1" ]]; then + install_gpu_userland "VAAPI" + pct exec "$CTID" -- bash -c "chgrp video /dev/dri && chmod 755 /dev/dri && chmod 660 /dev/dri/*" + pct exec "$CTID" -- vainfo >/dev/null 2>&1 && msg_ok "VAAPI verified" || msg_warn "VAAPI failed" + fi + if [[ "${ENABLE_NVIDIA:-0}" == "1" ]]; then + install_gpu_userland "NVIDIA" + pct exec "$CTID" -- nvidia-smi >/dev/null 2>&1 && msg_ok "NVIDIA verified" || msg_warn "NVIDIA failed" + fi msg_info "Customizing LXC Container" # # Install GPU userland if configured From 856440fcd444f3ae11c5453d0611fbabeb24d50a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 29 Sep 2025 10:51:05 +0200 Subject: [PATCH 1207/1733] Update build.func --- misc/build.func | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/misc/build.func b/misc/build.func index 69fe74b55..a41bc8bea 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2198,9 +2198,23 @@ build_container() { } detect_gpu_devices() { - VAAPI_DEVICES=(); NVIDIA_DEVICES=() - for d in /dev/dri/renderD* /dev/dri/card*; do [[ -e $d ]] && VAAPI_DEVICES+=("$d"); done - for d in /dev/nvidia*; do [[ -e $d ]] && NVIDIA_DEVICES+=("$d"); done + VAAPI_DEVICES=() + NVIDIA_DEVICES=() + + # Intel/AMD (VAAPI) + for d in /dev/dri/renderD* /dev/dri/card*; do + [ -e "$d" ] || continue + VAAPI_DEVICES+=("$d") + done + + # NVIDIA + for d in /dev/nvidia*; do + [ -e "$d" ] || continue + NVIDIA_DEVICES+=("$d") + done + + msg_debug "Detected VAAPI devices: ${VAAPI_DEVICES[*]:-(none)}" + msg_debug "Detected NVIDIA devices: ${NVIDIA_DEVICES[*]:-(none)}" } # Configure USB passthrough for privileged containers From 8538a6c10794c877ccf8a504c2b4c834ae930b56 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 29 Sep 2025 11:02:27 +0200 Subject: [PATCH 1208/1733] Refactor GPU passthrough and detection logic Simplifies and improves GPU device detection for Intel, AMD, and NVIDIA, consolidates configuration functions, and enhances permission handling for both privileged and unprivileged containers. Removes redundant helper functions, adds user prompts for multiple GPU types, and improves driver installation and verification steps. Also refactors USB and additional device passthrough setup for clarity and maintainability. --- misc/build.func | 347 +++++++++++++++++++++++++++++++----------------- 1 file changed, 227 insertions(+), 120 deletions(-) diff --git a/misc/build.func b/misc/build.func index a41bc8bea..0dcc26b18 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2155,7 +2155,7 @@ build_container() { # ============================================================================ # List of applications that benefit from GPU acceleration - GPU_APPS=( + GPU_APPS=( "immich" "channels" "emby" "ersatztv" "frigate" "jellyfin" "plex" "scrypted" "tdarr" "unmanic" "ollama" "fileflows" "open-webui" "tunarr" "debian" @@ -2163,22 +2163,6 @@ build_container() { "viseron" ) - # ------------------------------------------------------------------------------ - # Helper Functions for GPU/USB Configuration - # ------------------------------------------------------------------------------ - - # Get device GID dynamically - get_device_gid() { - local group="$1" - gid=$(getent group "$group" | cut -d: -f3) - [[ -n "$gid" ]] && echo "$gid" && return - case "$group" in - video) echo 44 ;; - render) echo 104 ;; - *) echo 44 ;; - esac - } - # Check if app needs GPU is_gpu_app() { local app="${1,,}" @@ -2188,33 +2172,43 @@ build_container() { return 1 } - # Detect available GPU devices - is_gpu_app() { - local app="${1,,}" - for gpu in "${GPU_APPS[@]}"; do - [[ "$gpu" == "$app" ]] && return 0 - done - return 1 - } - + # Detect all available GPU devices detect_gpu_devices() { - VAAPI_DEVICES=() + INTEL_DEVICES=() + AMD_DEVICES=() NVIDIA_DEVICES=() - # Intel/AMD (VAAPI) - for d in /dev/dri/renderD* /dev/dri/card*; do - [ -e "$d" ] || continue - VAAPI_DEVICES+=("$d") - done + # Check for Intel/AMD GPUs via DRI devices + if [[ -d /dev/dri ]]; then + # Intel GPU detection + if lspci | grep -iq "VGA.*Intel\|Display.*Intel"; then + for d in /dev/dri/renderD* /dev/dri/card*; do + [[ -e "$d" ]] && INTEL_DEVICES+=("$d") + done + [[ ${#INTEL_DEVICES[@]} -gt 0 ]] && msg_info "Detected Intel GPU" + fi - # NVIDIA - for d in /dev/nvidia*; do - [ -e "$d" ] || continue - NVIDIA_DEVICES+=("$d") - done + # AMD GPU detection + if lspci | grep -iq "VGA.*AMD\|Display.*AMD\|VGA.*ATI\|Display.*ATI"; then + for d in /dev/dri/renderD* /dev/dri/card*; do + [[ -e "$d" ]] && AMD_DEVICES+=("$d") + done + [[ ${#AMD_DEVICES[@]} -gt 0 ]] && msg_info "Detected AMD GPU" + fi + fi - msg_debug "Detected VAAPI devices: ${VAAPI_DEVICES[*]:-(none)}" - msg_debug "Detected NVIDIA devices: ${NVIDIA_DEVICES[*]:-(none)}" + # NVIDIA GPU detection + if lspci | grep -iq "VGA.*NVIDIA\|3D.*NVIDIA"; then + for d in /dev/nvidia*; do + [[ -e "$d" ]] && NVIDIA_DEVICES+=("$d") + done + if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then + msg_warn "NVIDIA GPU detected but no /dev/nvidia* devices found" + msg_warn "Please install NVIDIA drivers on host: apt install nvidia-driver" + else + msg_info "Detected NVIDIA GPU" + fi + fi } # Configure USB passthrough for privileged containers @@ -2225,7 +2219,7 @@ build_container() { msg_info "Configuring automatic USB passthrough (privileged container)" cat <>"$LXC_CONFIG" -# USB passthrough (privileged container) +# Automatic USB passthrough (privileged container) lxc.cgroup2.devices.allow: a lxc.cap.drop: lxc.cgroup2.devices.allow: c 188:* rwm @@ -2238,79 +2232,135 @@ lxc.mount.entry: /dev/ttyACM1 dev/ttyACM1 none bind,optional,create= EOF msg_ok "USB passthrough configured" } - configure_vaapi_device() { - local dev="$1" idx="$2" - if [[ "$CT_TYPE" == "0" ]]; then - major=$(stat -c '%t' "$dev"); minor=$(stat -c '%T' "$dev") - echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" - echo "lxc.mount.entry: $dev dev/$(basename "$dev") none bind,optional,create=file" >>"$LXC_CONFIG" - else - [[ "$dev" =~ renderD ]] && gid=$(get_device_gid "render") || gid=$(get_device_gid "video") - echo "dev$idx: $dev,gid=$gid" >>"$LXC_CONFIG" - fi - } - - configure_nvidia_devices() { - for dev in "${NVIDIA_DEVICES[@]}"; do - if [[ "$CT_TYPE" == "0" ]]; then - major=$(stat -c '%t' "$dev"); minor=$(stat -c '%T' "$dev") - echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" - echo "lxc.mount.entry: $dev dev/$(basename "$dev") none bind,optional,create=file" >>"$LXC_CONFIG" - else - msg_warn "NVIDIA passthrough on unprivileged CT may fail" - fi - done - [[ -d /dev/dri && "$CT_TYPE" == "0" ]] && echo "lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" - } + # Configure GPU passthrough configure_gpu_passthrough() { + # Skip if not a GPU app and not privileged + if [[ "$CT_TYPE" != "0" ]] && ! is_gpu_app "$APP"; then + return 0 + fi + detect_gpu_devices - local should=false - if [[ "$CT_TYPE" == "0" ]] || is_gpu_app "$APP"; then should=true; fi - [[ "$should" == "false" ]] && return - if [[ ${#VAAPI_DEVICES[@]} -eq 0 && ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then - msg_info "No GPU devices found"; return + # Count available GPU types + local gpu_count=0 + local available_gpus=() + [[ ${#INTEL_DEVICES[@]} -gt 0 ]] && { available_gpus+=("INTEL"); ((gpu_count++)); } + [[ ${#AMD_DEVICES[@]} -gt 0 ]] && { available_gpus+=("AMD"); ((gpu_count++)); } + [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]] && { available_gpus+=("NVIDIA"); ((gpu_count++)); } + + if [[ $gpu_count -eq 0 ]]; then + msg_info "No GPU devices found for passthrough" + return 0 fi - local choices=() selected=() - [[ ${#VAAPI_DEVICES[@]} -gt 0 ]] && choices+=("VAAPI" "Intel/AMD GPU" "OFF") - [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]] && choices+=("NVIDIA" "NVIDIA GPU" "OFF") + local selected_gpu="" - if [[ ${#choices[@]} -eq 3 ]]; then - selected=("VAAPI") - elif [[ ${#choices[@]} -eq 6 ]]; then - read -rp "Multiple GPUs found (VAAPI/NVIDIA). Which passthrough? " sel - [[ "$sel" =~ VAAPI|vaapi ]] && selected=("VAAPI") - [[ "$sel" =~ NVIDIA|nvidia ]] && selected=("NVIDIA") + if [[ $gpu_count -eq 1 ]]; then + # Automatic selection for single GPU + selected_gpu="${available_gpus[0]}" + msg_info "Automatically configuring ${selected_gpu} GPU passthrough" + else + # Multiple GPUs - ask user + echo -e "\n${INFO} Multiple GPU types detected:" + for gpu in "${available_gpus[@]}"; do + echo " - $gpu" + done + read -rp "Which GPU type to passthrough? (${available_gpus[*]}): " selected_gpu + selected_gpu="${selected_gpu^^}" + + # Validate selection + local valid=0 + for gpu in "${available_gpus[@]}"; do + [[ "$selected_gpu" == "$gpu" ]] && valid=1 + done + + if [[ $valid -eq 0 ]]; then + msg_warn "Invalid selection. Skipping GPU passthrough." + return 0 + fi fi - local idx=0 - for s in "${selected[@]}"; do - case "$s" in - VAAPI) for d in "${VAAPI_DEVICES[@]}"; do configure_vaapi_device "$d" "$idx"; idx=$((idx+1)); done; export ENABLE_VAAPI=1 ;; - NVIDIA) configure_nvidia_devices; export ENABLE_NVIDIA=1 ;; - esac - done - [[ ${#selected[@]} -gt 0 ]] && msg_ok "GPU passthrough configured" + # Apply passthrough configuration based on selection + local dev_idx=0 + + case "$selected_gpu" in + INTEL|AMD) + local devices=() + [[ "$selected_gpu" == "INTEL" ]] && devices=("${INTEL_DEVICES[@]}") + [[ "$selected_gpu" == "AMD" ]] && devices=("${AMD_DEVICES[@]}") + + for dev in "${devices[@]}"; do + if [[ "$CT_TYPE" == "0" ]]; then + # Privileged container + local major=$(stat -c '%t' "$dev") + local minor=$(stat -c '%T' "$dev") + echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" + echo "lxc.mount.entry: $dev dev/$(basename "$dev") none bind,optional,create=file" >>"$LXC_CONFIG" + else + # Unprivileged container - use generic GID, will be fixed after start + echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG" + ((dev_idx++)) + fi + done + + # Mount entire /dev/dri for privileged containers + if [[ "$CT_TYPE" == "0" ]]; then + echo "lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" + fi + + export GPU_TYPE="$selected_gpu" + msg_ok "${selected_gpu} GPU passthrough configured" + ;; + + NVIDIA) + if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then + msg_error "NVIDIA drivers not installed on host. Please install: apt install nvidia-driver" + return 1 + fi + + for dev in "${NVIDIA_DEVICES[@]}"; do + if [[ "$CT_TYPE" == "0" ]]; then + local major=$(stat -c '%t' "$dev") + local minor=$(stat -c '%T' "$dev") + echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" + echo "lxc.mount.entry: $dev dev/$(basename "$dev") none bind,optional,create=file" >>"$LXC_CONFIG" + else + msg_warn "NVIDIA passthrough on unprivileged container may not work properly" + fi + done + + if [[ "$CT_TYPE" == "0" && -d /dev/dri ]]; then + echo "lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" + fi + + export GPU_TYPE="NVIDIA" + msg_ok "NVIDIA GPU passthrough configured" + ;; + esac } - configure_usb_passthrough - configure_gpu_passthrough - - # TUN device passthrough - if [ "$ENABLE_TUN" == "yes" ]; then - cat <>"$LXC_CONFIG" + # Additional device passthrough + configure_additional_devices() { + # TUN device passthrough + if [ "$ENABLE_TUN" == "yes" ]; then + cat <>"$LXC_CONFIG" lxc.cgroup2.devices.allow: c 10:200 rwm lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file EOF - fi + fi - # Coral TPU passthrough (if available) - if [[ -e /dev/apex_0 ]]; then - msg_info "Detected Coral TPU - configuring passthrough" - echo "lxc.mount.entry: /dev/apex_0 dev/apex_0 none bind,optional,create=file" >>"$LXC_CONFIG" - fi + # Coral TPU passthrough + if [[ -e /dev/apex_0 ]]; then + msg_info "Detected Coral TPU - configuring passthrough" + echo "lxc.mount.entry: /dev/apex_0 dev/apex_0 none bind,optional,create=file" >>"$LXC_CONFIG" + fi + } + + # Execute pre-start configurations + configure_usb_passthrough + configure_gpu_passthrough + configure_additional_devices # ============================================================================ # START CONTAINER AND INSTALL USERLAND @@ -2364,31 +2414,88 @@ EOF msg_warn "Network reachable but gateway check failed" fi fi - - install_gpu_userland() { - local gpu="$1" - if [[ "$var_os" == "alpine" ]]; then - case "$gpu" in - VAAPI) pct exec "$CTID" -- apk add mesa-dri-gallium mesa-va-gallium intel-media-driver libva-utils ;; - NVIDIA) msg_warn "NVIDIA drivers not in Alpine repos" ;; - esac - else - case "$gpu" in - VAAPI) pct exec "$CTID" -- bash -c "apt-get update && apt-get install -y intel-media-va-driver-non-free mesa-va-drivers vainfo" ;; - NVIDIA) pct exec "$CTID" -- bash -c "apt-get update && apt-get install -y nvidia-driver nvidia-utils libnvidia-encode1" ;; - esac - fi + # Function to get correct GID inside container + get_container_gid() { + local group="$1" + local gid=$(pct exec "$CTID" -- getent group "$group" 2>/dev/null | cut -d: -f3) + echo "${gid:-44}" # Default to 44 if not found } - if [[ "${ENABLE_VAAPI:-0}" == "1" ]]; then - install_gpu_userland "VAAPI" - pct exec "$CTID" -- bash -c "chgrp video /dev/dri && chmod 755 /dev/dri && chmod 660 /dev/dri/*" - pct exec "$CTID" -- vainfo >/dev/null 2>&1 && msg_ok "VAAPI verified" || msg_warn "VAAPI failed" - fi - if [[ "${ENABLE_NVIDIA:-0}" == "1" ]]; then - install_gpu_userland "NVIDIA" - pct exec "$CTID" -- nvidia-smi >/dev/null 2>&1 && msg_ok "NVIDIA verified" || msg_warn "NVIDIA failed" + # Install GPU drivers and fix permissions + if [[ -n "${GPU_TYPE:-}" ]]; then + msg_info "Installing GPU userland drivers for ${GPU_TYPE}" + + case "$GPU_TYPE" in + INTEL|AMD) + if [[ "$var_os" == "alpine" ]]; then + pct exec "$CTID" -- apk add mesa-dri-gallium mesa-va-gallium intel-media-driver libva-utils 2>/dev/null || true + else + pct exec "$CTID" -- bash -c "apt-get update && apt-get install -y vainfo intel-media-va-driver-non-free mesa-va-drivers" 2>/dev/null || true + fi + + # Fix permissions with correct GID + local video_gid=$(get_container_gid "video") + local render_gid=$(get_container_gid "render") + + msg_info "Setting GPU permissions (video:${video_gid}, render:${render_gid})" + + # Fix device permissions inside container + if [[ "$CT_TYPE" == "0" ]]; then + pct exec "$CTID" -- bash -c " + if [ -d /dev/dri ]; then + chgrp ${video_gid} /dev/dri 2>/dev/null || true + chmod 755 /dev/dri + for dev in /dev/dri/*; do + if [[ \"\$dev\" =~ renderD ]]; then + chgrp ${render_gid} \"\$dev\" 2>/dev/null || true + else + chgrp ${video_gid} \"\$dev\" 2>/dev/null || true + fi + chmod 660 \"\$dev\" + done + fi + " + else + # For unprivileged containers, update the LXC config with correct GIDs + msg_info "Updating unprivileged container device GIDs" + + # Stop container to update config + pct stop "$CTID" + + # Update device entries with correct GIDs + sed -i "s/dev\([0-9]\+\):.*renderD.*/dev\1: \/dev\/dri\/renderD*, gid=${render_gid}/" "$LXC_CONFIG" + sed -i "s/dev\([0-9]\+\):.*card.*/dev\1: \/dev\/dri\/card*, gid=${video_gid}/" "$LXC_CONFIG" + + # Restart container + pct start "$CTID" + sleep 5 + fi + + # Verify GPU access + if pct exec "$CTID" -- vainfo >/dev/null 2>&1; then + msg_ok "${GPU_TYPE} GPU verified working" + else + msg_warn "${GPU_TYPE} GPU verification failed - may need additional configuration" + fi + ;; + + NVIDIA) + if [[ "$var_os" != "alpine" ]]; then + pct exec "$CTID" -- bash -c "apt-get update && apt-get install -y nvidia-driver nvidia-utils libnvidia-encode1" 2>/dev/null || true + else + msg_warn "NVIDIA drivers not available in Alpine repos" + fi + + if pct exec "$CTID" -- nvidia-smi >/dev/null 2>&1; then + msg_ok "NVIDIA GPU verified working" + else + msg_warn "NVIDIA GPU verification failed" + fi + ;; + esac fi + + # Continue with standard container setup msg_info "Customizing LXC Container" # # Install GPU userland if configured From 1b207c3c3829f7d3ce6564dd47264e682604a9eb Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 29 Sep 2025 11:12:29 +0200 Subject: [PATCH 1209/1733] Update build.func --- misc/build.func | 74 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 26 deletions(-) diff --git a/misc/build.func b/misc/build.func index 0dcc26b18..d7faa1310 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2178,39 +2178,41 @@ build_container() { AMD_DEVICES=() NVIDIA_DEVICES=() - # Check for Intel/AMD GPUs via DRI devices - if [[ -d /dev/dri ]]; then - # Intel GPU detection - if lspci | grep -iq "VGA.*Intel\|Display.*Intel"; then + # Check for Intel GPU + if lspci 2>/dev/null | grep -iq "VGA.*Intel\|Display.*Intel"; then + msg_info "Detected Intel GPU" + if [[ -d /dev/dri ]]; then for d in /dev/dri/renderD* /dev/dri/card*; do [[ -e "$d" ]] && INTEL_DEVICES+=("$d") done - [[ ${#INTEL_DEVICES[@]} -gt 0 ]] && msg_info "Detected Intel GPU" fi + fi - # AMD GPU detection - if lspci | grep -iq "VGA.*AMD\|Display.*AMD\|VGA.*ATI\|Display.*ATI"; then + # Check for AMD GPU + if lspci 2>/dev/null | grep -iq "VGA.*AMD\|Display.*AMD\|VGA.*ATI"; then + msg_info "Detected AMD GPU" + if [[ -d /dev/dri ]]; then for d in /dev/dri/renderD* /dev/dri/card*; do [[ -e "$d" ]] && AMD_DEVICES+=("$d") done - [[ ${#AMD_DEVICES[@]} -gt 0 ]] && msg_info "Detected AMD GPU" fi fi - # NVIDIA GPU detection - if lspci | grep -iq "VGA.*NVIDIA\|3D.*NVIDIA"; then - for d in /dev/nvidia*; do + # Check for NVIDIA GPU + if lspci 2>/dev/null | grep -iq "VGA.*NVIDIA\|3D.*NVIDIA"; then + msg_info "Detected NVIDIA GPU" + for d in /dev/nvidia* /dev/nvidiactl /dev/nvidia-modeset; do [[ -e "$d" ]] && NVIDIA_DEVICES+=("$d") done + if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then msg_warn "NVIDIA GPU detected but no /dev/nvidia* devices found" msg_warn "Please install NVIDIA drivers on host: apt install nvidia-driver" - else - msg_info "Detected NVIDIA GPU" fi fi } + # Configure USB passthrough for privileged containers configure_usb_passthrough() { if [[ "$CT_TYPE" != "0" ]]; then @@ -2245,9 +2247,21 @@ EOF # Count available GPU types local gpu_count=0 local available_gpus=() - [[ ${#INTEL_DEVICES[@]} -gt 0 ]] && { available_gpus+=("INTEL"); ((gpu_count++)); } - [[ ${#AMD_DEVICES[@]} -gt 0 ]] && { available_gpus+=("AMD"); ((gpu_count++)); } - [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]] && { available_gpus+=("NVIDIA"); ((gpu_count++)); } + + if [[ ${#INTEL_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("INTEL") + gpu_count=$((gpu_count + 1)) + fi + + if [[ ${#AMD_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("AMD") + gpu_count=$((gpu_count + 1)) + fi + + if [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("NVIDIA") + gpu_count=$((gpu_count + 1)) + fi if [[ $gpu_count -eq 0 ]]; then msg_info "No GPU devices found for passthrough" @@ -2293,19 +2307,23 @@ EOF for dev in "${devices[@]}"; do if [[ "$CT_TYPE" == "0" ]]; then # Privileged container - local major=$(stat -c '%t' "$dev") - local minor=$(stat -c '%T' "$dev") - echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" - echo "lxc.mount.entry: $dev dev/$(basename "$dev") none bind,optional,create=file" >>"$LXC_CONFIG" + local major minor + major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") + minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") + + if [[ "$major" != "0" && "$minor" != "0" ]]; then + echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" + echo "lxc.mount.entry: $dev dev/$(basename "$dev") none bind,optional,create=file" >>"$LXC_CONFIG" + fi else # Unprivileged container - use generic GID, will be fixed after start echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG" - ((dev_idx++)) + dev_idx=$((dev_idx + 1)) fi done # Mount entire /dev/dri for privileged containers - if [[ "$CT_TYPE" == "0" ]]; then + if [[ "$CT_TYPE" == "0" && -d /dev/dri ]]; then echo "lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" fi @@ -2321,10 +2339,14 @@ EOF for dev in "${NVIDIA_DEVICES[@]}"; do if [[ "$CT_TYPE" == "0" ]]; then - local major=$(stat -c '%t' "$dev") - local minor=$(stat -c '%T' "$dev") - echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" - echo "lxc.mount.entry: $dev dev/$(basename "$dev") none bind,optional,create=file" >>"$LXC_CONFIG" + local major minor + major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") + minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") + + if [[ "$major" != "0" && "$minor" != "0" ]]; then + echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" + echo "lxc.mount.entry: $dev dev/$(basename "$dev") none bind,optional,create=file" >>"$LXC_CONFIG" + fi else msg_warn "NVIDIA passthrough on unprivileged container may not work properly" fi From 6ca3cb4d775aa8c8b69a76ecaf522449e2439b06 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 29 Sep 2025 11:18:03 +0200 Subject: [PATCH 1210/1733] Update build.func --- misc/build.func | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/misc/build.func b/misc/build.func index d7faa1310..205d7db24 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2178,8 +2178,11 @@ build_container() { AMD_DEVICES=() NVIDIA_DEVICES=() - # Check for Intel GPU - if lspci 2>/dev/null | grep -iq "VGA.*Intel\|Display.*Intel"; then + # Store PCI info to avoid multiple calls + local pci_vga_info=$(lspci -nn 2>/dev/null | grep -E "VGA|Display|3D") + + # Check for Intel GPU - look for Intel vendor ID [8086] + if echo "$pci_vga_info" | grep -q "\[8086:"; then msg_info "Detected Intel GPU" if [[ -d /dev/dri ]]; then for d in /dev/dri/renderD* /dev/dri/card*; do @@ -2188,18 +2191,21 @@ build_container() { fi fi - # Check for AMD GPU - if lspci 2>/dev/null | grep -iq "VGA.*AMD\|Display.*AMD\|VGA.*ATI"; then + # Check for AMD GPU - look for AMD vendor IDs [1002] (AMD/ATI) or [1022] (AMD) + if echo "$pci_vga_info" | grep -qE "\[1002:|\[1022:"; then msg_info "Detected AMD GPU" if [[ -d /dev/dri ]]; then - for d in /dev/dri/renderD* /dev/dri/card*; do - [[ -e "$d" ]] && AMD_DEVICES+=("$d") - done + # Only add if not already claimed by Intel + if [[ ${#INTEL_DEVICES[@]} -eq 0 ]]; then + for d in /dev/dri/renderD* /dev/dri/card*; do + [[ -e "$d" ]] && AMD_DEVICES+=("$d") + done + fi fi fi - # Check for NVIDIA GPU - if lspci 2>/dev/null | grep -iq "VGA.*NVIDIA\|3D.*NVIDIA"; then + # Check for NVIDIA GPU - look for NVIDIA vendor ID [10de] + if echo "$pci_vga_info" | grep -q "\[10de:"; then msg_info "Detected NVIDIA GPU" for d in /dev/nvidia* /dev/nvidiactl /dev/nvidia-modeset; do [[ -e "$d" ]] && NVIDIA_DEVICES+=("$d") @@ -2210,6 +2216,11 @@ build_container() { msg_warn "Please install NVIDIA drivers on host: apt install nvidia-driver" fi fi + + # Debug output + msg_debug "Intel devices: ${INTEL_DEVICES[*]}" + msg_debug "AMD devices: ${AMD_DEVICES[*]}" + msg_debug "NVIDIA devices: ${NVIDIA_DEVICES[*]}" } From de080793ca846ea37671d338168ea374f4ff5632 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 29 Sep 2025 11:27:45 +0200 Subject: [PATCH 1211/1733] Refactor GPU passthrough configuration logic Reworked the configure_gpu_passthrough function for improved clarity and maintainability. Device entries and permissions are now handled more consistently for both privileged and unprivileged containers, with clearer GID assignment and device indexing. Added more robust verification and messaging for GPU setup and access. --- misc/build.func | 441 +++++++++++++++++++++++++++--------------------- 1 file changed, 252 insertions(+), 189 deletions(-) diff --git a/misc/build.func b/misc/build.func index 205d7db24..cc6cc4dbf 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2247,131 +2247,136 @@ EOF } # Configure GPU passthrough - configure_gpu_passthrough() { - # Skip if not a GPU app and not privileged - if [[ "$CT_TYPE" != "0" ]] && ! is_gpu_app "$APP"; then +configure_gpu_passthrough() { + # Skip if not a GPU app and not privileged + if [[ "$CT_TYPE" != "0" ]] && ! is_gpu_app "$APP"; then + return 0 + fi + + detect_gpu_devices + + # Count available GPU types + local gpu_count=0 + local available_gpus=() + + if [[ ${#INTEL_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("INTEL") + gpu_count=$((gpu_count + 1)) + fi + + if [[ ${#AMD_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("AMD") + gpu_count=$((gpu_count + 1)) + fi + + if [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("NVIDIA") + gpu_count=$((gpu_count + 1)) + fi + + if [[ $gpu_count -eq 0 ]]; then + msg_info "No GPU devices found for passthrough" + return 0 + fi + + local selected_gpu="" + + if [[ $gpu_count -eq 1 ]]; then + # Automatic selection for single GPU + selected_gpu="${available_gpus[0]}" + msg_info "Automatically configuring ${selected_gpu} GPU passthrough" + else + # Multiple GPUs - ask user + echo -e "\n${INFO} Multiple GPU types detected:" + for gpu in "${available_gpus[@]}"; do + echo " - $gpu" + done + read -rp "Which GPU type to passthrough? (${available_gpus[*]}): " selected_gpu + selected_gpu="${selected_gpu^^}" + + # Validate selection + local valid=0 + for gpu in "${available_gpus[@]}"; do + [[ "$selected_gpu" == "$gpu" ]] && valid=1 + done + + if [[ $valid -eq 0 ]]; then + msg_warn "Invalid selection. Skipping GPU passthrough." return 0 fi + fi - detect_gpu_devices + # Apply passthrough configuration based on selection + local dev_idx=0 - # Count available GPU types - local gpu_count=0 - local available_gpus=() + case "$selected_gpu" in + INTEL|AMD) + local devices=() + [[ "$selected_gpu" == "INTEL" ]] && devices=("${INTEL_DEVICES[@]}") + [[ "$selected_gpu" == "AMD" ]] && devices=("${AMD_DEVICES[@]}") - if [[ ${#INTEL_DEVICES[@]} -gt 0 ]]; then - available_gpus+=("INTEL") - gpu_count=$((gpu_count + 1)) - fi - - if [[ ${#AMD_DEVICES[@]} -gt 0 ]]; then - available_gpus+=("AMD") - gpu_count=$((gpu_count + 1)) - fi - - if [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then - available_gpus+=("NVIDIA") - gpu_count=$((gpu_count + 1)) - fi - - if [[ $gpu_count -eq 0 ]]; then - msg_info "No GPU devices found for passthrough" - return 0 - fi - - local selected_gpu="" - - if [[ $gpu_count -eq 1 ]]; then - # Automatic selection for single GPU - selected_gpu="${available_gpus[0]}" - msg_info "Automatically configuring ${selected_gpu} GPU passthrough" - else - # Multiple GPUs - ask user - echo -e "\n${INFO} Multiple GPU types detected:" - for gpu in "${available_gpus[@]}"; do - echo " - $gpu" - done - read -rp "Which GPU type to passthrough? (${available_gpus[*]}): " selected_gpu - selected_gpu="${selected_gpu^^}" - - # Validate selection - local valid=0 - for gpu in "${available_gpus[@]}"; do - [[ "$selected_gpu" == "$gpu" ]] && valid=1 - done - - if [[ $valid -eq 0 ]]; then - msg_warn "Invalid selection. Skipping GPU passthrough." - return 0 - fi - fi - - # Apply passthrough configuration based on selection - local dev_idx=0 - - case "$selected_gpu" in - INTEL|AMD) - local devices=() - [[ "$selected_gpu" == "INTEL" ]] && devices=("${INTEL_DEVICES[@]}") - [[ "$selected_gpu" == "AMD" ]] && devices=("${AMD_DEVICES[@]}") - - for dev in "${devices[@]}"; do - if [[ "$CT_TYPE" == "0" ]]; then - # Privileged container - local major minor - major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") - minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") - - if [[ "$major" != "0" && "$minor" != "0" ]]; then - echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" - echo "lxc.mount.entry: $dev dev/$(basename "$dev") none bind,optional,create=file" >>"$LXC_CONFIG" - fi + # For Proxmox WebUI visibility, add as dev0, dev1 etc. + for dev in "${devices[@]}"; do + if [[ "$CT_TYPE" == "0" ]]; then + # Privileged container - use dev entries for WebUI visibility + # Use initial GID 104 (render) for renderD*, 44 (video) for card* + if [[ "$dev" =~ renderD ]]; then + echo "dev${dev_idx}: $dev,uid=0,gid=104" >>"$LXC_CONFIG" else - # Unprivileged container - use generic GID, will be fixed after start echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG" - dev_idx=$((dev_idx + 1)) fi - done + dev_idx=$((dev_idx + 1)) - # Mount entire /dev/dri for privileged containers - if [[ "$CT_TYPE" == "0" && -d /dev/dri ]]; then - echo "lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" - fi + # Also add cgroup allows for privileged containers + local major minor + major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") + minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") - export GPU_TYPE="$selected_gpu" - msg_ok "${selected_gpu} GPU passthrough configured" - ;; - - NVIDIA) - if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then - msg_error "NVIDIA drivers not installed on host. Please install: apt install nvidia-driver" - return 1 - fi - - for dev in "${NVIDIA_DEVICES[@]}"; do - if [[ "$CT_TYPE" == "0" ]]; then - local major minor - major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") - minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") - - if [[ "$major" != "0" && "$minor" != "0" ]]; then - echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" - echo "lxc.mount.entry: $dev dev/$(basename "$dev") none bind,optional,create=file" >>"$LXC_CONFIG" - fi + if [[ "$major" != "0" && "$minor" != "0" ]]; then + echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" + fi + else + # Unprivileged container + if [[ "$dev" =~ renderD ]]; then + echo "dev${dev_idx}: $dev,uid=0,gid=104" >>"$LXC_CONFIG" else - msg_warn "NVIDIA passthrough on unprivileged container may not work properly" + echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG" fi - done - - if [[ "$CT_TYPE" == "0" && -d /dev/dri ]]; then - echo "lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG" + dev_idx=$((dev_idx + 1)) fi + done - export GPU_TYPE="NVIDIA" - msg_ok "NVIDIA GPU passthrough configured" - ;; - esac - } + export GPU_TYPE="$selected_gpu" + msg_ok "${selected_gpu} GPU passthrough configured (${dev_idx} devices)" + ;; + + NVIDIA) + if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then + msg_error "NVIDIA drivers not installed on host. Please install: apt install nvidia-driver" + return 1 + fi + + for dev in "${NVIDIA_DEVICES[@]}"; do + # NVIDIA devices typically need different handling + echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG" + dev_idx=$((dev_idx + 1)) + + if [[ "$CT_TYPE" == "0" ]]; then + local major minor + major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") + minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") + + if [[ "$major" != "0" && "$minor" != "0" ]]; then + echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" + fi + fi + done + + export GPU_TYPE="NVIDIA" + msg_ok "NVIDIA GPU passthrough configured (${dev_idx} devices)" + ;; + esac +} # Additional device passthrough configure_additional_devices() { @@ -2447,87 +2452,145 @@ EOF msg_warn "Network reachable but gateway check failed" fi fi - # Function to get correct GID inside container - get_container_gid() { - local group="$1" - local gid=$(pct exec "$CTID" -- getent group "$group" 2>/dev/null | cut -d: -f3) - echo "${gid:-44}" # Default to 44 if not found - } + Function to get correct GID inside container +get_container_gid() { + local group="$1" + local gid=$(pct exec "$CTID" -- getent group "$group" 2>/dev/null | cut -d: -f3) + echo "${gid:-44}" # Default to 44 if not found +} - # Install GPU drivers and fix permissions - if [[ -n "${GPU_TYPE:-}" ]]; then - msg_info "Installing GPU userland drivers for ${GPU_TYPE}" - - case "$GPU_TYPE" in - INTEL|AMD) - if [[ "$var_os" == "alpine" ]]; then - pct exec "$CTID" -- apk add mesa-dri-gallium mesa-va-gallium intel-media-driver libva-utils 2>/dev/null || true - else - pct exec "$CTID" -- bash -c "apt-get update && apt-get install -y vainfo intel-media-va-driver-non-free mesa-va-drivers" 2>/dev/null || true - fi - - # Fix permissions with correct GID - local video_gid=$(get_container_gid "video") - local render_gid=$(get_container_gid "render") - - msg_info "Setting GPU permissions (video:${video_gid}, render:${render_gid})" - - # Fix device permissions inside container - if [[ "$CT_TYPE" == "0" ]]; then - pct exec "$CTID" -- bash -c " - if [ -d /dev/dri ]; then - chgrp ${video_gid} /dev/dri 2>/dev/null || true - chmod 755 /dev/dri - for dev in /dev/dri/*; do - if [[ \"\$dev\" =~ renderD ]]; then - chgrp ${render_gid} \"\$dev\" 2>/dev/null || true - else - chgrp ${video_gid} \"\$dev\" 2>/dev/null || true - fi - chmod 660 \"\$dev\" - done - fi - " - else - # For unprivileged containers, update the LXC config with correct GIDs - msg_info "Updating unprivileged container device GIDs" - - # Stop container to update config - pct stop "$CTID" - - # Update device entries with correct GIDs - sed -i "s/dev\([0-9]\+\):.*renderD.*/dev\1: \/dev\/dri\/renderD*, gid=${render_gid}/" "$LXC_CONFIG" - sed -i "s/dev\([0-9]\+\):.*card.*/dev\1: \/dev\/dri\/card*, gid=${video_gid}/" "$LXC_CONFIG" - - # Restart container - pct start "$CTID" - sleep 5 - fi - - # Verify GPU access - if pct exec "$CTID" -- vainfo >/dev/null 2>&1; then - msg_ok "${GPU_TYPE} GPU verified working" - else - msg_warn "${GPU_TYPE} GPU verification failed - may need additional configuration" - fi - ;; - - NVIDIA) - if [[ "$var_os" != "alpine" ]]; then - pct exec "$CTID" -- bash -c "apt-get update && apt-get install -y nvidia-driver nvidia-utils libnvidia-encode1" 2>/dev/null || true - else - msg_warn "NVIDIA drivers not available in Alpine repos" - fi - - if pct exec "$CTID" -- nvidia-smi >/dev/null 2>&1; then - msg_ok "NVIDIA GPU verified working" - else - msg_warn "NVIDIA GPU verification failed" - fi - ;; - esac +# Configure GPU passthrough +configure_gpu_passthrough() { + # Skip if not a GPU app and not privileged + if [[ "$CT_TYPE" != "0" ]] && ! is_gpu_app "$APP"; then + return 0 fi + detect_gpu_devices + + # Count available GPU types + local gpu_count=0 + local available_gpus=() + + if [[ ${#INTEL_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("INTEL") + gpu_count=$((gpu_count + 1)) + fi + + if [[ ${#AMD_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("AMD") + gpu_count=$((gpu_count + 1)) + fi + + if [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("NVIDIA") + gpu_count=$((gpu_count + 1)) + fi + + if [[ $gpu_count -eq 0 ]]; then + msg_info "No GPU devices found for passthrough" + return 0 + fi + + local selected_gpu="" + + if [[ $gpu_count -eq 1 ]]; then + # Automatic selection for single GPU + selected_gpu="${available_gpus[0]}" + msg_info "Automatically configuring ${selected_gpu} GPU passthrough" + else + # Multiple GPUs - ask user + echo -e "\n${INFO} Multiple GPU types detected:" + for gpu in "${available_gpus[@]}"; do + echo " - $gpu" + done + read -rp "Which GPU type to passthrough? (${available_gpus[*]}): " selected_gpu + selected_gpu="${selected_gpu^^}" + + # Validate selection + local valid=0 + for gpu in "${available_gpus[@]}"; do + [[ "$selected_gpu" == "$gpu" ]] && valid=1 + done + + if [[ $valid -eq 0 ]]; then + msg_warn "Invalid selection. Skipping GPU passthrough." + return 0 + fi + fi + + # Apply passthrough configuration based on selection + local dev_idx=0 + + case "$selected_gpu" in + INTEL|AMD) + local devices=() + [[ "$selected_gpu" == "INTEL" ]] && devices=("${INTEL_DEVICES[@]}") + [[ "$selected_gpu" == "AMD" ]] && devices=("${AMD_DEVICES[@]}") + + # For Proxmox WebUI visibility, add as dev0, dev1 etc. + for dev in "${devices[@]}"; do + if [[ "$CT_TYPE" == "0" ]]; then + # Privileged container - use dev entries for WebUI visibility + # Use initial GID 104 (render) for renderD*, 44 (video) for card* + if [[ "$dev" =~ renderD ]]; then + echo "dev${dev_idx}: $dev,uid=0,gid=104" >>"$LXC_CONFIG" + else + echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG" + fi + dev_idx=$((dev_idx + 1)) + + # Also add cgroup allows for privileged containers + local major minor + major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") + minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") + + if [[ "$major" != "0" && "$minor" != "0" ]]; then + echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" + fi + else + # Unprivileged container + if [[ "$dev" =~ renderD ]]; then + echo "dev${dev_idx}: $dev,uid=0,gid=104" >>"$LXC_CONFIG" + else + echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG" + fi + dev_idx=$((dev_idx + 1)) + fi + done + + export GPU_TYPE="$selected_gpu" + msg_ok "${selected_gpu} GPU passthrough configured (${dev_idx} devices)" + ;; + + NVIDIA) + if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then + msg_error "NVIDIA drivers not installed on host. Please install: apt install nvidia-driver" + return 1 + fi + + for dev in "${NVIDIA_DEVICES[@]}"; do + # NVIDIA devices typically need different handling + echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG" + dev_idx=$((dev_idx + 1)) + + if [[ "$CT_TYPE" == "0" ]]; then + local major minor + major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") + minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") + + if [[ "$major" != "0" && "$minor" != "0" ]]; then + echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" + fi + fi + done + + export GPU_TYPE="NVIDIA" + msg_ok "NVIDIA GPU passthrough configured (${dev_idx} devices)" + ;; + esac +} + # Continue with standard container setup msg_info "Customizing LXC Container" From 7d6822ecbdcdbb04de98dab4be0d969a55d5520a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 29 Sep 2025 11:35:47 +0200 Subject: [PATCH 1212/1733] Update dependencies and add Viseron release fetch Added gcc, musl-dev, and libpq-dev to the list of installed dependencies. Included a step to fetch and deploy the latest Viseron GitHub release to /opt/viseron before setting up the Python environment. --- install/viseron-install.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/install/viseron-install.sh b/install/viseron-install.sh index d95d626be..dc4c78ec5 100644 --- a/install/viseron-install.sh +++ b/install/viseron-install.sh @@ -16,11 +16,11 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ python3-opencv jq \ - libgl1-mesa-glx libglib2.0-0 pciutils \ + libgl1-mesa-glx libglib2.0-0 pciutils gcc musl-dev \ libgstreamer1.0-0 libgstreamer-plugins-base1.0-0 \ gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-libav \ build-essential python3-dev python3-gi pkg-config libcairo2-dev gir1.2-glib-2.0 \ - cmake gfortran libopenblas-dev liblapack-dev libgirepository1.0-dev git + cmake gfortran libopenblas-dev liblapack-dev libgirepository1.0-dev git libpq-dev msg_ok "Installed Dependencies" PG_VERSION="16" setup_postgresql @@ -51,6 +51,8 @@ if [[ "$CTTYPE" == "0" ]]; then fi msg_ok "Hardware Acceleration Configured" +fetch_and_deploy_gh_release "viseron" "roflcoopter/viseron" "/opt/viseron" + msg_info "Setting up Python Environment" uv venv --python "python3.13" /opt/viseron/.venv uv pip install --python /opt/viseron/.venv/bin/python --upgrade pip setuptools wheel From 42c503d0d257ce25c83226c91cda81c5f4faab32 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 29 Sep 2025 11:47:25 +0200 Subject: [PATCH 1213/1733] Update build.func --- misc/build.func | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/misc/build.func b/misc/build.func index cc6cc4dbf..cf4cc7d8f 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2452,12 +2452,12 @@ EOF msg_warn "Network reachable but gateway check failed" fi fi - Function to get correct GID inside container -get_container_gid() { - local group="$1" - local gid=$(pct exec "$CTID" -- getent group "$group" 2>/dev/null | cut -d: -f3) - echo "${gid:-44}" # Default to 44 if not found -} + # Function to get correct GID inside container + get_container_gid() { + local group="$1" + local gid=$(pct exec "$CTID" -- getent group "$group" 2>/dev/null | cut -d: -f3) + echo "${gid:-44}" # Default to 44 if not found + } # Configure GPU passthrough configure_gpu_passthrough() { From 43db95a38a60cdcf3d6a5217bdb9633b01176ff0 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 29 Sep 2025 11:55:33 +0200 Subject: [PATCH 1214/1733] Remove redundant uid from device config and update install script Eliminates the unnecessary 'uid=0' parameter from device configuration in GPU passthrough setup for privileged containers. Also updates the viseron install script to remove the target directory argument from the fetch_and_deploy_gh_release call, aligning with changes in its usage. --- install/viseron-install.sh | 2 +- misc/build.func | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/install/viseron-install.sh b/install/viseron-install.sh index dc4c78ec5..317a0d256 100644 --- a/install/viseron-install.sh +++ b/install/viseron-install.sh @@ -51,7 +51,7 @@ if [[ "$CTTYPE" == "0" ]]; then fi msg_ok "Hardware Acceleration Configured" -fetch_and_deploy_gh_release "viseron" "roflcoopter/viseron" "/opt/viseron" +fetch_and_deploy_gh_release "viseron" "roflcoopter/viseron" msg_info "Setting up Python Environment" uv venv --python "python3.13" /opt/viseron/.venv diff --git a/misc/build.func b/misc/build.func index cf4cc7d8f..74696ad3a 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2534,9 +2534,9 @@ configure_gpu_passthrough() { # Privileged container - use dev entries for WebUI visibility # Use initial GID 104 (render) for renderD*, 44 (video) for card* if [[ "$dev" =~ renderD ]]; then - echo "dev${dev_idx}: $dev,uid=0,gid=104" >>"$LXC_CONFIG" + echo "dev${dev_idx}: $dev,gid=104" >>"$LXC_CONFIG" else - echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG" + echo "dev${dev_idx}: $dev,gid=44" >>"$LXC_CONFIG" fi dev_idx=$((dev_idx + 1)) From 25c121ccaf44fe29ca781b1c73bdca3264d2ccbf Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 29 Sep 2025 11:58:06 +0200 Subject: [PATCH 1215/1733] Update build.func --- misc/build.func | 101 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 2 deletions(-) diff --git a/misc/build.func b/misc/build.func index 74696ad3a..aa9bd64d8 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2321,9 +2321,9 @@ configure_gpu_passthrough() { # Privileged container - use dev entries for WebUI visibility # Use initial GID 104 (render) for renderD*, 44 (video) for card* if [[ "$dev" =~ renderD ]]; then - echo "dev${dev_idx}: $dev,uid=0,gid=104" >>"$LXC_CONFIG" + echo "dev${dev_idx}: $dev,gid=104" >>"$LXC_CONFIG" else - echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG" + echo "dev${dev_idx}: $dev,gid=44" >>"$LXC_CONFIG" fi dev_idx=$((dev_idx + 1)) @@ -2459,6 +2459,8 @@ EOF echo "${gid:-44}" # Default to 44 if not found } + fix_gpu_gids + # Configure GPU passthrough configure_gpu_passthrough() { # Skip if not a GPU app and not privileged @@ -2732,6 +2734,101 @@ resolve_storage_preselect() { return 0 } +fix_gpu_gids() { + if [[ -z "${GPU_TYPE:-}" ]]; then + return 0 + fi + + msg_info "Detecting and setting correct GPU group IDs" + + # Ermittle die tatsächlichen GIDs aus dem Container + local video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") + local render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") + + # Fallbacks wenn Gruppen nicht existieren + if [[ -z "$video_gid" ]]; then + # Versuche die video Gruppe zu erstellen + pct exec "$CTID" -- sh -c "groupadd -r video 2>/dev/null || true" + video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") + [[ -z "$video_gid" ]] && video_gid="44" # Ultimate fallback + fi + + if [[ -z "$render_gid" ]]; then + # Versuche die render Gruppe zu erstellen + pct exec "$CTID" -- sh -c "groupadd -r render 2>/dev/null || true" + render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") + [[ -z "$render_gid" ]] && render_gid="104" # Ultimate fallback + fi + + msg_info "Container GIDs detected - video:${video_gid}, render:${render_gid}" + + # Prüfe ob die GIDs von den Defaults abweichen + local need_update=0 + if [[ "$video_gid" != "44" ]] || [[ "$render_gid" != "104" ]]; then + need_update=1 + fi + + if [[ $need_update -eq 1 ]]; then + msg_info "Updating device GIDs in container config" + + # Stoppe Container für Config-Update + pct stop "$CTID" >/dev/null 2>&1 + + # Update die dev Einträge mit korrekten GIDs + # Backup der Config + cp "$LXC_CONFIG" "${LXC_CONFIG}.bak" + + # Parse und update jeden dev Eintrag + while IFS= read -r line; do + if [[ "$line" =~ ^dev[0-9]+: ]]; then + # Extract device path + local device_path=$(echo "$line" | sed -E 's/^dev[0-9]+: ([^,]+).*/\1/') + local dev_num=$(echo "$line" | sed -E 's/^(dev[0-9]+):.*/\1/') + + if [[ "$device_path" =~ renderD ]]; then + # RenderD device - use render GID + echo "${dev_num}: ${device_path},gid=${render_gid}" + elif [[ "$device_path" =~ card ]]; then + # Card device - use video GID + echo "${dev_num}: ${device_path},gid=${video_gid}" + else + # Keep original line + echo "$line" + fi + else + # Keep non-dev lines + echo "$line" + fi + done < "$LXC_CONFIG" > "${LXC_CONFIG}.new" + + mv "${LXC_CONFIG}.new" "$LXC_CONFIG" + + # Starte Container wieder + pct start "$CTID" >/dev/null 2>&1 + sleep 3 + + msg_ok "Device GIDs updated successfully" + else + msg_ok "Device GIDs are already correct" + fi + if [[ "$CT_TYPE" == "0" ]]; then + pct exec "$CTID" -- bash -c " + if [ -d /dev/dri ]; then + for dev in /dev/dri/*; do + if [ -e \"\$dev\" ]; then + if [[ \"\$dev\" =~ renderD ]]; then + chgrp ${render_gid} \"\$dev\" 2>/dev/null || true + else + chgrp ${video_gid} \"\$dev\" 2>/dev/null || true + fi + chmod 660 \"\$dev\" 2>/dev/null || true + fi + done + fi + " >/dev/null 2>&1 + fi +} + check_storage_support() { local CONTENT="$1" VALID=0 while IFS= read -r line; do From 5d18a8a7f312a4adfe5c9b7aed85b5aaec9ab419 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 29 Sep 2025 12:17:35 +0200 Subject: [PATCH 1216/1733] Update default Debian version to 13 Changed the default value of var_version from 12 to 13 to use the latest Debian release for new installations. --- ct/viseron.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/viseron.sh b/ct/viseron.sh index f65e15de5..be4377e55 100644 --- a/ct/viseron.sh +++ b/ct/viseron.sh @@ -7,7 +7,7 @@ var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-25}" var_os="${var_os:-debian}" -var_version="${var_version:-12}" +var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-0}" header_info "$APP" From 11f605693d2821f41c121e4db9363a98a102564f Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 29 Sep 2025 12:30:20 +0200 Subject: [PATCH 1217/1733] Update viseron-install.sh --- install/viseron-install.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/install/viseron-install.sh b/install/viseron-install.sh index 317a0d256..1e946b6dd 100644 --- a/install/viseron-install.sh +++ b/install/viseron-install.sh @@ -24,7 +24,7 @@ $STD apt-get install -y \ msg_ok "Installed Dependencies" PG_VERSION="16" setup_postgresql -PYTHON_VERSION="3.13" setup_uv +PYTHON_VERSION="3.11" setup_uv msg_info "Setting up PostgreSQL Database" DB_NAME=viseron @@ -54,7 +54,7 @@ msg_ok "Hardware Acceleration Configured" fetch_and_deploy_gh_release "viseron" "roflcoopter/viseron" msg_info "Setting up Python Environment" -uv venv --python "python3.13" /opt/viseron/.venv +uv venv --python "python3.11" /opt/viseron/.venv uv pip install --python /opt/viseron/.venv/bin/python --upgrade pip setuptools wheel msg_ok "Python Environment Setup" @@ -92,6 +92,7 @@ case "$GPU_VENDOR" in esac UV_HTTP_TIMEOUT=600 uv pip install --python /opt/viseron/.venv/bin/python -e /opt/viseron/. +UV_HTTP_TIMEOUT=600 uv pip install --python /opt/viseron/.venv/bin/python -r /opt/viseron/requirements.txt mkdir -p /config/{recordings,snapshots,segments,event_clips,thumbnails} for d in recordings snapshots segments event_clips thumbnails; do From a500d7f0802885aff85251d43a18a8bb97efc688 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 29 Sep 2025 12:34:20 +0200 Subject: [PATCH 1218/1733] Update MyIP icon --- frontend/public/json/myip.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/myip.json b/frontend/public/json/myip.json index a8cbda5df..8698c12b5 100644 --- a/frontend/public/json/myip.json +++ b/frontend/public/json/myip.json @@ -12,7 +12,7 @@ "interface_port": 18966, "documentation": "https://github.com/jason5ng32/MyIP#-environment-variable", "website": "https://ipcheck.ing/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/MyIP.svg", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/myip.webp", "description": "The best IP Toolbox. Easy to check what's your IPs, IP geolocation, check for DNS leaks, examine WebRTC connections, speed test, ping test, MTR test, check website availability, whois search and more!", "install_methods": [ { From 0f5f482de359248d2ff15e27349a916f8dfda9a6 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 29 Sep 2025 13:31:44 +0200 Subject: [PATCH 1219/1733] Update viseron-install.sh --- install/viseron-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/viseron-install.sh b/install/viseron-install.sh index 1e946b6dd..d282f284f 100644 --- a/install/viseron-install.sh +++ b/install/viseron-install.sh @@ -14,9 +14,9 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt-get install -y \ +$STD apt install -y \ python3-opencv jq \ - libgl1-mesa-glx libglib2.0-0 pciutils gcc musl-dev \ + libglib2.0-0 pciutils gcc musl-dev \ libgstreamer1.0-0 libgstreamer-plugins-base1.0-0 \ gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-libav \ build-essential python3-dev python3-gi pkg-config libcairo2-dev gir1.2-glib-2.0 \ From 16046fb9b9adc7c82bf6d9057c87f1c8e0651d08 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 29 Sep 2025 13:32:49 +0200 Subject: [PATCH 1220/1733] Update myip.sh --- ct/myip.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/myip.sh b/ct/myip.sh index 0ba906f0c..84a4e289c 100644 --- a/ct/myip.sh +++ b/ct/myip.sh @@ -9,7 +9,7 @@ APP="MyIP" var_tags="${var_tags:-network}" var_cpu="${var_cpu:-1}" var_ram="${var_ram:-512}" -var_disk="${var_disk:-2}" +var_disk="${var_disk:-4}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" From cbd18f91d1b6ca6d090f2f78abc098db6f5cf432 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 29 Sep 2025 13:36:00 +0200 Subject: [PATCH 1221/1733] Remove extra newline and update apt commands Removed an unnecessary newline in ct/myip.sh for cleaner output. Updated apt-get to apt in install/myip-install.sh for consistency with modern usage. --- ct/myip.sh | 1 - install/myip-install.sh | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/ct/myip.sh b/ct/myip.sh index 84a4e289c..10c3de823 100644 --- a/ct/myip.sh +++ b/ct/myip.sh @@ -40,7 +40,6 @@ function update_script() { msg_info "Starting Services" systemctl start myip msg_ok "Started Services" - msg_ok "Updated Successfully" fi exit diff --git a/install/myip-install.sh b/install/myip-install.sh index 019e399c5..725536d11 100644 --- a/install/myip-install.sh +++ b/install/myip-install.sh @@ -48,7 +48,7 @@ motd_ssh customize msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean +$STD apt -y autoremove +$STD apt -y autoclean $STD apt -y clean msg_ok "Cleaned" From a1930fd88c81990650c828bbd53b2b666f80081e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 29 Sep 2025 13:38:19 +0200 Subject: [PATCH 1222/1733] - --- ct/{ => deferred}/docspell.sh | 0 ct/frigate.sh | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename ct/{ => deferred}/docspell.sh (100%) diff --git a/ct/docspell.sh b/ct/deferred/docspell.sh similarity index 100% rename from ct/docspell.sh rename to ct/deferred/docspell.sh diff --git a/ct/frigate.sh b/ct/frigate.sh index 9dd49dc19..68f529c6d 100644 --- a/ct/frigate.sh +++ b/ct/frigate.sh @@ -11,7 +11,7 @@ var_cpu="${var_cpu:-4}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-20}" var_os="${var_os:-debian}" -var_version="${var_version:-12}" +var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-0}" header_info "$APP" From 01bccd81454a5cfe73557e33664867767b97954f Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 29 Sep 2025 13:40:40 +0200 Subject: [PATCH 1223/1733] Move install scripts to deferred directory and remove Scraparr/Signoz/Outline scripts Renamed several install scripts to the 'deferred' directory for organization. Deleted install and container scripts for Scraparr, Signoz, and Outline, removing their setup and service definitions. --- ct/{ => deferred}/maxun.sh | 0 ct/scraparr.sh | 63 ----- ct/signoz.sh | 64 ----- install/{ => deferred}/docspell-install.sh | 0 install/{ => deferred}/hanko-install.sh | 0 install/{ => deferred}/kanba-install.sh | 0 install/{ => deferred}/maxun-install.sh | 0 install/outline-install.sh | 90 ------- install/scraparr-install.sh | 56 ----- install/signoz-install.sh | 264 --------------------- 10 files changed, 537 deletions(-) rename ct/{ => deferred}/maxun.sh (100%) delete mode 100644 ct/scraparr.sh delete mode 100644 ct/signoz.sh rename install/{ => deferred}/docspell-install.sh (100%) rename install/{ => deferred}/hanko-install.sh (100%) rename install/{ => deferred}/kanba-install.sh (100%) rename install/{ => deferred}/maxun-install.sh (100%) delete mode 100644 install/outline-install.sh delete mode 100644 install/scraparr-install.sh delete mode 100644 install/signoz-install.sh diff --git a/ct/maxun.sh b/ct/deferred/maxun.sh similarity index 100% rename from ct/maxun.sh rename to ct/deferred/maxun.sh diff --git a/ct/scraparr.sh b/ct/scraparr.sh deleted file mode 100644 index efe595ddc..000000000 --- a/ct/scraparr.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: JasonGreenC -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/thecfu/scraparr - -APP="Scraparr" -var_tags="${var_tags:-arr;monitoring}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-1024}" -var_disk="${var_disk:-4}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - - if [[ ! -d /opt/scraparr/ ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - if check_for_gh_release "scraparr" "thecfu/scraparr"; then - msg_info "Stopping Services" - systemctl stop scraparr - msg_ok "Services Stopped" - - PYTHON_VERSION="3.12" setup_uv - fetch_and_deploy_gh_release "scrappar" "thecfu/scraparr" "tarball" "latest" "/opt/scraparr" - - msg_info "Updating Scraparr" - cd /opt/scraparr - $STD uv venv /opt/scraparr/.venv - $STD /opt/scraparr/.venv/bin/python -m ensurepip --upgrade - $STD /opt/scraparr/.venv/bin/python -m pip install --upgrade pip - $STD /opt/scraparr/.venv/bin/python -m pip install -r /opt/scraparr/src/scraparr/requirements.txt - chmod -R 755 /opt/scraparr - msg_ok "Updated Scraparr" - - msg_info "Starting Services" - systemctl start scraparr - msg_ok "Services Started" - msg_ok "Updated Successfully" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:7100${CL}" diff --git a/ct/signoz.sh b/ct/signoz.sh deleted file mode 100644 index 6efeec713..000000000 --- a/ct/signoz.sh +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Slaviša Arežina (tremor021) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://signoz.io/ - -APP="signoz" -var_tags="${var_tags:-notes}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-4096}" -var_disk="${var_disk:-20}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/signoz ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if check_for_gh_release "signoz" "SigNoz/signoz"; then - msg_info "Stopping Services" - systemctl stop signoz - systemctl stop signoz-otel-collector - msg_ok "Stopped Services" - - fetch_and_deploy_gh_release "signoz" "SigNoz/signoz" "prebuild" "latest" "/opt/signoz" "signoz-community_linux_amd64.tar.gz" - fetch_and_deploy_gh_release "signoz-otel-collector" "SigNoz/signoz-otel-collector" "prebuild" "latest" "/opt/signoz-otel-collector" "signoz-otel-collector_linux_amd64.tar.gz" - fetch_and_deploy_gh_release "signoz-schema-migrator" "SigNoz/signoz-otel-collector" "prebuild" "latest" "/opt/signoz-schema-migrator" "signoz-schema-migrator_linux_amd64.tar.gz" - - msg_info "Updating ${APP}" - cd /opt/signoz-schema-migrator/bin - $STD ./signoz-schema-migrator sync --dsn="tcp://localhost:9000?password=" --replication=true --up= - $STD ./signoz-schema-migrator async --dsn="tcp://localhost:9000?password=" --replication=true --up= - msg_ok "Updated $APP" - - msg_info "Starting Services" - systemctl start signoz-otel-collector - systemctl start signoz - msg_ok "Started Services" - - msg_ok "Updated Successfully" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" diff --git a/install/docspell-install.sh b/install/deferred/docspell-install.sh similarity index 100% rename from install/docspell-install.sh rename to install/deferred/docspell-install.sh diff --git a/install/hanko-install.sh b/install/deferred/hanko-install.sh similarity index 100% rename from install/hanko-install.sh rename to install/deferred/hanko-install.sh diff --git a/install/kanba-install.sh b/install/deferred/kanba-install.sh similarity index 100% rename from install/kanba-install.sh rename to install/deferred/kanba-install.sh diff --git a/install/maxun-install.sh b/install/deferred/maxun-install.sh similarity index 100% rename from install/maxun-install.sh rename to install/deferred/maxun-install.sh diff --git a/install/outline-install.sh b/install/outline-install.sh deleted file mode 100644 index ed496101a..000000000 --- a/install/outline-install.sh +++ /dev/null @@ -1,90 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Slaviša Arežina (tremor021) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/outline/outline - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt-get install -y \ - mkcert \ - git \ - redis -msg_ok "Installed Dependencies" - -NODE_VERSION="20" NODE_MODULE="yarn@latest" setup_nodejs -PG_VERSION="16" setup_postgresql - -msg_info "Set up PostgreSQL Database" -DB_NAME="outline" -DB_USER="outline" -DB_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" -$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" -$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" -{ - echo "Outline-Credentials" - echo "Outline Database User: $DB_USER" - echo "Outline Database Password: $DB_PASS" - echo "Outline Database Name: $DB_NAME" -} >>~/outline.creds -msg_ok "Set up PostgreSQL Database" - -fetch_and_deploy_gh_release "outline" "outline/outline" "tarball" "v0.87.3" - -msg_info "Configuring Outline (Patience)" -SECRET_KEY="$(openssl rand -hex 32)" -LOCAL_IP="$(hostname -I | awk '{print $1}')" -cd /opt/outline -cp .env.sample .env -export NODE_ENV=development -sed -i 's/NODE_ENV=production/NODE_ENV=development/g' /opt/outline/.env -sed -i "s/generate_a_new_key/${SECRET_KEY}/g" /opt/outline/.env -sed -i "s/user:pass@postgres/${DB_USER}:${DB_PASS}@localhost/g" /opt/outline/.env -sed -i 's/redis:6379/localhost:6379/g' /opt/outline/.env -sed -i "5s#URL=#URL=http://${LOCAL_IP}#g" /opt/outline/.env -sed -i 's/FORCE_HTTPS=true/FORCE_HTTPS=false/g' /opt/outline/.env -export NODE_OPTIONS="--max-old-space-size=3584" -$STD yarn install --frozen-lockfile -export NODE_ENV=production -sed -i 's/NODE_ENV=development/NODE_ENV=production/g' /opt/outline/.env -$STD yarn build -msg_ok "Configured Outline" - -msg_info "Creating Service" -cat </etc/systemd/system/outline.service -[Unit] -Description=Outline Service -After=network.target - -[Service] -Type=simple -User=root -WorkingDirectory=/opt/outline -ExecStart=/usr/bin/yarn start -Restart=always -EnvironmentFile=/opt/outline/.env - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now outline -msg_ok "Created Service" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" diff --git a/install/scraparr-install.sh b/install/scraparr-install.sh deleted file mode 100644 index 8c0de782c..000000000 --- a/install/scraparr-install.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: JasonGreenC -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/thecfu/scraparr - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -PYTHON_VERSION="3.13" setup_uv -fetch_and_deploy_gh_release "scrappar" "thecfu/scraparr" "tarball" "latest" "/opt/scraparr" - -msg_info "Installing Scraparr" -cd /opt/scraparr -$STD uv venv /opt/scraparr/.venv -$STD /opt/scraparr/.venv/bin/python -m ensurepip --upgrade -$STD /opt/scraparr/.venv/bin/python -m pip install --upgrade pip -$STD /opt/scraparr/.venv/bin/python -m pip install -r /opt/scraparr/src/scraparr/requirements.txt -chmod -R 755 /opt/scraparr -mkdir -p /scraparr/config -mv /opt/scraparr/config.yaml /scraparr/config/config.yaml -chmod -R 755 /scraparr -msg_ok "Installed Scraparr" - -msg_info "Creating Service" -cat </etc/systemd/system/scraparr.service -[Unit] -Description=Scraparr -Wants=network-online.target -After=network.target - -[Service] -Type=simple -WorkingDirectory=/opt/scraparr/src -ExecStart=/opt/scraparr/.venv/bin/python -m scraparr.scraparr -Restart=always - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now scraparr -msg_ok "Configured Service" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" diff --git a/install/signoz-install.sh b/install/signoz-install.sh deleted file mode 100644 index fb52d12bc..000000000 --- a/install/signoz-install.sh +++ /dev/null @@ -1,264 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Slaviša Arežina (tremor021) -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://signoz.io/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt-get install -y \ - apt-transport-https \ - ca-certificates -msg_ok "Installed Dependencies" - -JAVA_VERSION="21" setup_java - -msg_info "Setting up ClickHouse" -curl -fsSL "https://packages.clickhouse.com/rpm/lts/repodata/repomd.xml.key" | gpg --dearmor -o /usr/share/keyrings/clickhouse-keyring.gpg -echo "deb [signed-by=/usr/share/keyrings/clickhouse-keyring.gpg arch=amd64] https://packages.clickhouse.com/deb stable main" >/etc/apt/sources.list.d/clickhouse.list -$STD apt-get update -export DEBIAN_FRONTEND=noninteractive -$STD apt-get install -y clickhouse-server clickhouse-client -msg_ok "Setup ClickHouse" - -msg_info "Setting up Zookeeper" -curl -fsSL "https://dlcdn.apache.org/zookeeper/current/$(curl -fsSL https://dlcdn.apache.org/zookeeper/current/ | grep -o 'apache-zookeeper-[0-9.]\+-bin\.tar\.gz' | head -n1)" -o ~/zookeeper.tar.gz -tar -xzf "$HOME/zookeeper.tar.gz" -C "$HOME" -mkdir -p /opt/zookeeper -mkdir -p /var/lib/zookeeper -mkdir -p /var/log/zookeeper -cp -r ~/apache-zookeeper-*-bin/* /opt/zookeeper - -cat </opt/zookeeper/conf/zoo.cfg -tickTime=2000 -dataDir=/var/lib/zookeeper -clientPort=2181 -admin.serverPort=3181 -EOF - -cat </opt/zookeeper/conf/zoo.env -ZOO_LOG_DIR=/var/log/zookeeper -EOF - -cat </etc/systemd/system/zookeeper.service -[Unit] -Description=Zookeeper -Documentation=http://zookeeper.apache.org - -[Service] -EnvironmentFile=/opt/zookeeper/conf/zoo.env -Type=forking -WorkingDirectory=/opt/zookeeper -ExecStart=/opt/zookeeper/bin/zkServer.sh start /opt/zookeeper/conf/zoo.cfg -ExecStop=/opt/zookeeper/bin/zkServer.sh stop /opt/zookeeper/conf/zoo.cfg -ExecReload=/opt/zookeeper/bin/zkServer.sh restart /opt/zookeeper/conf/zoo.cfg -TimeoutSec=30 -Restart=on-failure - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now zookeeper -msg_ok "Setup Zookeeper" - -msg_info "Configuring ClickHouse" -cat </etc/clickhouse-server/config.d/cluster.xml - - - /clickhouse/task_queue/ddl - - - - - - 127.0.0.1 - 9000 - - - - - - - 127.0.0.1 - 2181 - - - - 01 - 01 - - -EOF -systemctl enable -q --now clickhouse-server -msg_ok "Configured ClickHouse" - -fetch_and_deploy_gh_release "signoz-schema-migrator" "SigNoz/signoz-otel-collector" "prebuild" "latest" "/opt/signoz-schema-migrator" "signoz-schema-migrator_linux_amd64.tar.gz" - -msg_info "Running ClickHouse migrations" -cd /opt/signoz-schema-migrator/bin -$STD ./signoz-schema-migrator sync --dsn="tcp://localhost:9000?password=" --replication=true --up= -$STD ./signoz-schema-migrator async --dsn="tcp://localhost:9000?password=" --replication=true --up= -msg_ok "ClickHouse Migrations Completed" - -fetch_and_deploy_gh_release "signoz" "SigNoz/signoz" "prebuild" "latest" "/opt/signoz" "signoz-community_linux_amd64.tar.gz" - -msg_info "Setting up SigNoz" -mkdir -p /var/lib/signoz - -cat </opt/signoz/conf/systemd.env -SIGNOZ_INSTRUMENTATION_LOGS_LEVEL=info -INVITE_EMAIL_TEMPLATE=/opt/signoz/templates/invitation_email_template.html -SIGNOZ_SQLSTORE_SQLITE_PATH=/var/lib/signoz/signoz.db -SIGNOZ_WEB_ENABLED=true -SIGNOZ_WEB_DIRECTORY=/opt/signoz/web -SIGNOZ_JWT_SECRET=secret -SIGNOZ_ALERTMANAGER_PROVIDER=signoz -SIGNOZ_TELEMETRYSTORE_PROVIDER=clickhouse -SIGNOZ_TELEMETRYSTORE_CLICKHOUSE_DSN=tcp://localhost:9000?password= -DOT_METRICS_ENABLED=true -EOF - -cat </etc/systemd/system/signoz.service -[Unit] -Description=SigNoz -Documentation=https://signoz.io/docs -After=clickhouse-server.service - -[Service] -Type=simple -KillMode=mixed -Restart=on-failure -WorkingDirectory=/opt/signoz -EnvironmentFile=/opt/signoz/conf/systemd.env -ExecStart=/opt/signoz/bin/signoz server - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now signoz -msg_ok "Setup Signoz" - -fetch_and_deploy_gh_release "signoz-otel-collector" "SigNoz/signoz-otel-collector" "prebuild" "latest" "/opt/signoz-otel-collector" "signoz-otel-collector_linux_amd64.tar.gz" - -msg_info "Setting up SigNoz OTel Collector" -mkdir -p /var/lib/signoz-otel-collector - -cat </opt/signoz-otel-collector/conf/config.yaml -receivers: - otlp: - protocols: - grpc: - endpoint: 0.0.0.0:4317 - max_recv_msg_size_mib: 16 - http: - endpoint: 0.0.0.0:4318 - jaeger: - protocols: - grpc: - endpoint: 0.0.0.0:14250 - thrift_http: - endpoint: 0.0.0.0:14268 - httplogreceiver/heroku: - endpoint: 0.0.0.0:8081 - source: heroku - httplogreceiver/json: - endpoint: 0.0.0.0:8082 - source: json -processors: - batch: - send_batch_size: 50000 - timeout: 1s - signozspanmetrics/delta: - metrics_exporter: signozclickhousemetrics - latency_histogram_buckets: [100us, 1ms, 2ms, 6ms, 10ms, 50ms, 100ms, 250ms, 500ms, 1000ms, 1400ms, 2000ms, 5s, 10s, 20s, 40s, 60s] - dimensions_cache_size: 100000 - dimensions: - - name: service.namespace - default: default - - name: deployment.environment - default: default - - name: signoz.collector.id - aggregation_temporality: AGGREGATION_TEMPORALITY_DELTA -extensions: - health_check: - endpoint: 0.0.0.0:13133 - zpages: - endpoint: localhost:55679 - pprof: - endpoint: localhost:1777 -exporters: - clickhousetraces: - datasource: tcp://localhost:9000/signoz_traces?password= - use_new_schema: true - signozclickhousemetrics: - dsn: tcp://localhost:9000/signoz_metrics?password= - timeout: 45s - clickhouselogsexporter: - dsn: tcp://localhost:9000/signoz_logs?password= - timeout: 10s - use_new_schema: true - metadataexporter: - dsn: tcp://localhost:9000/signoz_metadata?password= - timeout: 10s - tenant_id: default - cache: - provider: in_memory -service: - telemetry: - logs: - encoding: json - extensions: [health_check, zpages, pprof] - pipelines: - traces: - receivers: [otlp, jaeger] - processors: [signozspanmetrics/delta, batch] - exporters: [clickhousetraces, metadataexporter] - metrics: - receivers: [otlp] - processors: [batch] - exporters: [metadataexporter, signozclickhousemetrics] - logs: - receivers: [otlp, httplogreceiver/heroku, httplogreceiver/json] - processors: [batch] - exporters: [clickhouselogsexporter, metadataexporter] -EOF - -cat </opt/signoz-otel-collector/conf/opamp.yaml -server_endpoint: ws://127.0.0.1:4320/v1/opamp -EOF - -cat </etc/systemd/system/signoz-otel-collector.service -[Unit] -Description=SigNoz OTel Collector -Documentation=https://signoz.io/docs -After=clickhouse-server.service - -[Service] -Type=simple -KillMode=mixed -Restart=on-failure -WorkingDirectory=/opt/signoz-otel-collector -ExecStart=/opt/signoz-otel-collector/bin/signoz-otel-collector --config=/opt/signoz-otel-collector/conf/config.yaml --manager-config=/opt/signoz-otel-collector/conf/opamp.yaml --copy-path=/var/lib/signoz-otel-collector/config.yaml - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now signoz-otel-collector - -motd_ssh -customize - -msg_info "Cleaning up" -rm -rf ~/zookeeper.tar.gz -rm -rf ~/apache-zookeeper-*-bin -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" From cc4ceee5ab99c1c729738a3dca508c62120bd7c8 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 29 Sep 2025 14:08:07 +0200 Subject: [PATCH 1224/1733] Update Ghostfolio and Warracker install scripts Bump default Debian and dependency versions for Ghostfolio and Warracker. Streamline Ghostfolio update and install logic, improve backup handling, and update commands to use latest Node.js, PostgreSQL, and Python versions. Clean up redundant steps and standardize output handling. --- ct/ghostfolio.sh | 52 +++++++++++++++-------------------- install/ghostfolio-install.sh | 50 +++++++++++---------------------- install/warracker-install.sh | 2 +- 3 files changed, 39 insertions(+), 65 deletions(-) diff --git a/ct/ghostfolio.sh b/ct/ghostfolio.sh index 5acd7f6a0..63c9be00d 100644 --- a/ct/ghostfolio.sh +++ b/ct/ghostfolio.sh @@ -11,7 +11,7 @@ var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" -var_version="${var_version:-12}" +var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" @@ -29,43 +29,35 @@ function update_script() { exit fi - msg_info "Stopping $APP" - systemctl stop ghostfolio - msg_ok "Stopped $APP" + if check_for_gh_release "ghostfolio" "ghostfolio/ghostfolio"; then + msg_info "Stopping Service" + systemctl stop ghostfolio + msg_ok "Stopped Service" - msg_info "Creating Backup" - tar -czf "/opt/ghostfolio_backup_$(date +%F).tar.gz" /opt/ghostfolio - msg_ok "Backup Created" + msg_info "Creating Backup" + tar -czf "/opt/ghostfolio_backup_$(date +%F).tar.gz" /opt/ghostfolio + mv /opt/ghostfolio/.env /opt/env.backup + msg_ok "Backup Created" - msg_info "Updating $APP" - systemctl stop ghostfolio + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "ghostfolio" "ghostfolio/ghostfolio" "tarball" "latest" "/opt/ghostfolio" - if [[ -d /opt/ghostfolio ]]; then - rm -rf /opt/ghostfolio_backup - mv /opt/ghostfolio /opt/ghostfolio_backup - fi + msg_info "Updating Ghostfolio" + mv /opt/env.backup /opt/ghostfolio/.env + cd /opt/ghostfolio + $STD npm ci + $STD npm run build:production + $STD prisma migrate deploy + msg_ok "Updated Ghostfolio" - if fetch_and_deploy_gh_release "ghostfolio" "ghostfolio/ghostfolio" "tarball" "latest" "/opt/ghostfolio"; then - cd /opt/ghostfolio - npm ci - npm run build:production - npx prisma migrate deploy - msg_ok "Updated $APP" - else - if [[ -d /opt/ghostfolio_backup ]]; then - rm -rf /opt/ghostfolio - mv /opt/ghostfolio_backup /opt/ghostfolio - fi - msg_ok "No update required or update failed. ${APP} is up to date" - fi - - msg_info "Starting $APP" + msg_info "Starting Service" systemctl start ghostfolio - msg_ok "Started $APP" + msg_ok "Started Service" msg_info "Cleaning Up" - npm cache clean --force + $STD npm cache clean --force msg_ok "Cleanup Completed" + msg_ok "Updated Successfully" + fi exit } diff --git a/install/ghostfolio-install.sh b/install/ghostfolio-install.sh index 090b209b6..9c02175bb 100644 --- a/install/ghostfolio-install.sh +++ b/install/ghostfolio-install.sh @@ -14,20 +14,15 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt-get install -y \ +$STD apt install -y \ build-essential \ - python3 \ openssl \ - curl \ - ca-certificates + ca-certificates \ + redis-server msg_ok "Installed Dependencies" -PG_VERSION="15" setup_postgresql -NODE_VERSION="22" setup_nodejs - -msg_info "Installing Redis" -$STD apt-get install -y redis-server -msg_ok "Installed Redis" +PG_VERSION="17" setup_postgresql +NODE_VERSION="24" setup_nodejs msg_info "Setting up Database" DB_NAME=ghostfolio @@ -52,31 +47,17 @@ $STD sudo -u postgres psql -d $DB_NAME -c "ALTER DEFAULT PRIVILEGES IN SCHEMA pu echo "Redis Password: $REDIS_PASS" echo "Access Token Salt: $ACCESS_TOKEN_SALT" echo "JWT Secret Key: $JWT_SECRET_KEY" - if [[ -n "${COINGECKO_DEMO_KEY:-}" ]]; then - echo "CoinGecko Demo API Key: $COINGECKO_DEMO_KEY" - fi - if [[ -n "${COINGECKO_PRO_KEY:-}" ]]; then - echo "CoinGecko Pro API Key: $COINGECKO_PRO_KEY" - fi - echo "" - echo "To add CoinGecko API keys later, edit: /opt/ghostfolio/.env" } >>~/ghostfolio.creds msg_ok "Set up Database" -msg_info "Configuring Redis" -sed -i "s/# requirepass foobared/requirepass $REDIS_PASS/" /etc/redis/redis.conf -systemctl restart redis-server -msg_ok "Configured Redis" - fetch_and_deploy_gh_release "ghostfolio" "ghostfolio/ghostfolio" "tarball" "latest" "/opt/ghostfolio" -msg_info "Installing Ghostfolio Dependencies" +msg_info "Setup Ghostfolio" +sed -i "s/# requirepass foobared/requirepass $REDIS_PASS/" /etc/redis/redis.conf +systemctl restart redis-server cd /opt/ghostfolio -npm ci -msg_ok "Installed Dependencies" - -msg_info "Building Ghostfolio (This may take several minutes)" -npm run build:production +$STD npm ci +$STD npm run build:production msg_ok "Built Ghostfolio" msg_ok "Optional CoinGecko API Configuration" @@ -111,8 +92,8 @@ msg_ok "Set up Environment" msg_info "Running Database Migrations" cd /opt/ghostfolio -npx prisma migrate deploy -npx prisma db seed +$STD npx prisma migrate deploy +$STD npx prisma db seed msg_ok "Database Migrations Complete" msg_info "Creating Service" @@ -143,7 +124,8 @@ motd_ssh customize msg_info "Cleaning up" -npm cache clean --force -$STD apt-get -y autoremove -$STD apt-get -y autoclean +$STD npm cache clean --force +$STD apt -y autoremove +$STD apt -y autoclean +$STD apt -y clean msg_ok "Cleaned" diff --git a/install/warracker-install.sh b/install/warracker-install.sh index 26d9d524a..a3f4d13c3 100644 --- a/install/warracker-install.sh +++ b/install/warracker-install.sh @@ -20,7 +20,7 @@ $STD apt install -y \ nginx msg_ok "Installed Dependencies" -PYTHON_VERSION="3.12" setup_uv +PYTHON_VERSION="3.13" setup_uv PG_VERSION="17" setup_postgresql msg_info "Setup PostgreSQL" From dcca0803f0de1875279c06756c2863da571e4457 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 29 Sep 2025 12:08:27 +0000 Subject: [PATCH 1225/1733] Update .app files --- ct/headers/docspell | 6 ------ ct/headers/maxun | 6 ------ ct/headers/scraparr | 6 ------ ct/headers/signoz | 6 ------ 4 files changed, 24 deletions(-) delete mode 100644 ct/headers/docspell delete mode 100644 ct/headers/maxun delete mode 100644 ct/headers/scraparr delete mode 100644 ct/headers/signoz diff --git a/ct/headers/docspell b/ct/headers/docspell deleted file mode 100644 index 51277d47b..000000000 --- a/ct/headers/docspell +++ /dev/null @@ -1,6 +0,0 @@ - ____ ____ - / __ \____ ______________ ___ / / / - / / / / __ \/ ___/ ___/ __ \/ _ \/ / / - / /_/ / /_/ / /__(__ ) /_/ / __/ / / -/_____/\____/\___/____/ .___/\___/_/_/ - /_/ diff --git a/ct/headers/maxun b/ct/headers/maxun deleted file mode 100644 index 056b0ab51..000000000 --- a/ct/headers/maxun +++ /dev/null @@ -1,6 +0,0 @@ - __ ___ - / |/ /___ __ ____ ______ - / /|_/ / __ `/ |/_/ / / / __ \ - / / / / /_/ /> Date: Mon, 29 Sep 2025 14:09:53 +0200 Subject: [PATCH 1226/1733] Refactor Bridge description logic --- misc/build.func | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/misc/build.func b/misc/build.func index aa9bd64d8..acd4a96cc 100644 --- a/misc/build.func +++ b/misc/build.func @@ -220,21 +220,6 @@ get_current_ip() { echo "$CURRENT_IP" } -# ------------------------------------------------------------------------------ -# get_bridge_description() -# -# - Reads bridge descriptions from /usr/local/community-scripts/bridge_description -# - Returns description for given bridge name, or empty string if not found -# - File format: bridge_name:description (e.g., vmbr0:LAN, vmbr1:WAN) -# ------------------------------------------------------------------------------ -get_bridge_description() { - local bridge_name="$1" - local desc_file="/usr/local/community-scripts/bridge_description" - - if [[ -f "$desc_file" ]]; then - grep "^${bridge_name}:" "$desc_file" 2>/dev/null | cut -d':' -f2- | head -n1 - fi -} # ------------------------------------------------------------------------------ # update_motd_ip() @@ -633,7 +618,8 @@ advanced_settings() { BRIDGE_MENU_OPTIONS=() while IFS= read -r bridge; do if [[ -n "$bridge" ]]; then - description=$(get_bridge_description "$bridge") + # Get description from Proxmox built-in method + description=$(grep '^#' /etc/network/interfaces | sed 's/^#\s*//' | tail -n1) if [[ -n "$description" ]]; then BRIDGE_MENU_OPTIONS+=("$bridge" "${description}") else @@ -642,7 +628,7 @@ advanced_settings() { fi done <<< "$BRIDGES" - BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge:\n To add a description, edit:\n /usr/local/community-scripts/bridge_description\n Example: vmbr0:LAN vmbr1:WAN" 18 55 6 "${BRIDGE_MENU_OPTIONS[@]}" 3>&1 1>&2 2>&3) + BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge: " 18 55 6 "${BRIDGE_MENU_OPTIONS[@]}" 3>&1 1>&2 2>&3) if [[ -z "$BRG" ]]; then exit_script else From 592f98dfb70c184bcd88872c948b18c6c73cf9fc Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Mon, 29 Sep 2025 14:12:56 +0200 Subject: [PATCH 1227/1733] Refactor Bridge description logic --- misc/build.func | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/misc/build.func b/misc/build.func index acd4a96cc..b84e146d8 100644 --- a/misc/build.func +++ b/misc/build.func @@ -618,8 +618,8 @@ advanced_settings() { BRIDGE_MENU_OPTIONS=() while IFS= read -r bridge; do if [[ -n "$bridge" ]]; then - # Get description from Proxmox built-in method - description=$(grep '^#' /etc/network/interfaces | sed 's/^#\s*//' | tail -n1) + # Get description from Proxmox built-in method - find comment for this specific bridge + description=$(grep -A 10 "iface $bridge" /etc/network/interfaces | grep '^#' | head -n1 | sed 's/^#\s*//') if [[ -n "$description" ]]; then BRIDGE_MENU_OPTIONS+=("$bridge" "${description}") else From 6ed204cbd1e58fae02e1729728609ccf6e21c271 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 29 Sep 2025 14:28:35 +0200 Subject: [PATCH 1228/1733] Update ghostfolio.sh --- ct/ghostfolio.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ct/ghostfolio.sh b/ct/ghostfolio.sh index 63c9be00d..2d676212c 100644 --- a/ct/ghostfolio.sh +++ b/ct/ghostfolio.sh @@ -35,7 +35,11 @@ function update_script() { msg_ok "Stopped Service" msg_info "Creating Backup" - tar -czf "/opt/ghostfolio_backup_$(date +%F).tar.gz" /opt/ghostfolio + tar -czf "/opt/ghostfolio_backup_$(date +%F).tar.gz" \ + -C /opt \ + --exclude="ghostfolio/node_modules" \ + --exclude="ghostfolio/dist" \ + ghostfolio mv /opt/ghostfolio/.env /opt/env.backup msg_ok "Backup Created" From 79bd7b753e332d0c190e4b65112a11e6f8caa41b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 29 Sep 2025 14:36:18 +0200 Subject: [PATCH 1229/1733] Update ghostfolio.sh --- ct/ghostfolio.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/ghostfolio.sh b/ct/ghostfolio.sh index 2d676212c..9619a27f4 100644 --- a/ct/ghostfolio.sh +++ b/ct/ghostfolio.sh @@ -50,7 +50,7 @@ function update_script() { cd /opt/ghostfolio $STD npm ci $STD npm run build:production - $STD prisma migrate deploy + $STD npx prisma migrate deploy msg_ok "Updated Ghostfolio" msg_info "Starting Service" From b0c436dd4239016d22cac9b650674ac63eb5955c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 29 Sep 2025 14:48:11 +0200 Subject: [PATCH 1230/1733] Update build.func --- misc/build.func | 50 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index b84e146d8..3d5a62dbc 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2193,6 +2193,11 @@ build_container() { # Check for NVIDIA GPU - look for NVIDIA vendor ID [10de] if echo "$pci_vga_info" | grep -q "\[10de:"; then msg_info "Detected NVIDIA GPU" + if ! check_nvidia_host_setup; then + msg_error "NVIDIA host setup incomplete. Skipping GPU passthrough." + msg_info "Fix NVIDIA drivers on host, then recreate container or passthrough manually." + return 0 + fi for d in /dev/nvidia* /dev/nvidiactl /dev/nvidia-modeset; do [[ -e "$d" ]] && NVIDIA_DEVICES+=("$d") done @@ -2201,7 +2206,30 @@ build_container() { msg_warn "NVIDIA GPU detected but no /dev/nvidia* devices found" msg_warn "Please install NVIDIA drivers on host: apt install nvidia-driver" fi - fi + if [[ "$CT_TYPE" == "0" ]]; then + cat <>"$LXC_CONFIG" + # NVIDIA GPU Passthrough (privileged) + lxc.cgroup2.devices.allow: c 195:* rwm + lxc.cgroup2.devices.allow: c 243:* rwm + lxc.mount.entry: /dev/nvidia0 dev/nvidia0 none bind,optional,create=file + lxc.mount.entry: /dev/nvidiactl dev/nvidiactl none bind,optional,create=file + lxc.mount.entry: /dev/nvidia-uvm dev/nvidia-uvm none bind,optional,create=file + lxc.mount.entry: /dev/nvidia-uvm-tools dev/nvidia-uvm-tools none bind,optional,create=file + EOF + fi + if [[ -e /dev/dri/renderD128 ]]; then + echo "lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file" >>"$LXC_CONFIG" + fi + + export GPU_TYPE="NVIDIA" + export NVIDIA_DRIVER_VERSION=$(nvidia-smi --query-gpu=driver_version --format=csv,noheader 2>/dev/null | head -n1) + msg_ok "NVIDIA GPU passthrough configured (driver: ${NVIDIA_DRIVER_VERSION})" + else + msg_warn "NVIDIA passthrough only supported for privileged containers" + return 0 + fi + fi + } # Debug output msg_debug "Intel devices: ${INTEL_DEVICES[*]}" @@ -2815,6 +2843,26 @@ fix_gpu_gids() { fi } +# NVIDIA-spezific check on host +check_nvidia_host_setup() { + if ! command -v nvidia-smi >/dev/null 2>&1; then + msg_warn "NVIDIA GPU detected but nvidia-smi not found on host" + msg_warn "Please install NVIDIA drivers on host first." + #echo " 1. Download driver: wget https://us.download.nvidia.com/XFree86/Linux-x86_64/550.127.05/NVIDIA-Linux-x86_64-550.127.05.run" + #echo " 2. Install: ./NVIDIA-Linux-x86_64-550.127.05.run --dkms" + #echo " 3. Verify: nvidia-smi" + return 1 + fi + + # check if nvidia-smi works + if ! nvidia-smi >/dev/null 2>&1; then + msg_warn "nvidia-smi installed but not working. Driver issue?" + return 1 + fi + + return 0 +} + check_storage_support() { local CONTENT="$1" VALID=0 while IFS= read -r line; do From 2d0b42ae45483a85ad6af24bfe3060ea72b7d81c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 30 Sep 2025 09:06:39 +0200 Subject: [PATCH 1231/1733] cleanup --- ct/ghostfolio.sh | 75 --------------- ct/myip.sh | 55 ----------- ct/warracker.sh | 63 ------------- frontend/public/json/ghostfolio.json | 56 ------------ frontend/public/json/myip.json | 35 ------- frontend/public/json/warracker.json | 40 -------- install/ghostfolio-install.sh | 131 --------------------------- install/myip-install.sh | 54 ----------- install/warracker-install.sh | 119 ------------------------ 9 files changed, 628 deletions(-) delete mode 100644 ct/ghostfolio.sh delete mode 100644 ct/myip.sh delete mode 100644 ct/warracker.sh delete mode 100644 frontend/public/json/ghostfolio.json delete mode 100644 frontend/public/json/myip.json delete mode 100644 frontend/public/json/warracker.json delete mode 100644 install/ghostfolio-install.sh delete mode 100644 install/myip-install.sh delete mode 100644 install/warracker-install.sh diff --git a/ct/ghostfolio.sh b/ct/ghostfolio.sh deleted file mode 100644 index 9619a27f4..000000000 --- a/ct/ghostfolio.sh +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: lucasfell -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://ghostfol.io/ - -APP="Ghostfolio" -var_tags="${var_tags:-finance;investment}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-4096}" -var_disk="${var_disk:-8}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - - if [[ ! -f /opt/ghostfolio/dist/apps/api/main.js ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if check_for_gh_release "ghostfolio" "ghostfolio/ghostfolio"; then - msg_info "Stopping Service" - systemctl stop ghostfolio - msg_ok "Stopped Service" - - msg_info "Creating Backup" - tar -czf "/opt/ghostfolio_backup_$(date +%F).tar.gz" \ - -C /opt \ - --exclude="ghostfolio/node_modules" \ - --exclude="ghostfolio/dist" \ - ghostfolio - mv /opt/ghostfolio/.env /opt/env.backup - msg_ok "Backup Created" - - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "ghostfolio" "ghostfolio/ghostfolio" "tarball" "latest" "/opt/ghostfolio" - - msg_info "Updating Ghostfolio" - mv /opt/env.backup /opt/ghostfolio/.env - cd /opt/ghostfolio - $STD npm ci - $STD npm run build:production - $STD npx prisma migrate deploy - msg_ok "Updated Ghostfolio" - - msg_info "Starting Service" - systemctl start ghostfolio - msg_ok "Started Service" - - msg_info "Cleaning Up" - $STD npm cache clean --force - msg_ok "Cleanup Completed" - msg_ok "Updated Successfully" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3333${CL}" diff --git a/ct/myip.sh b/ct/myip.sh deleted file mode 100644 index 10c3de823..000000000 --- a/ct/myip.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Slaviša Arežina (tremor021) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://ipcheck.ing/ - -APP="MyIP" -var_tags="${var_tags:-network}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-512}" -var_disk="${var_disk:-4}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/myip ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if check_for_gh_release "myip" "jason5ng32/MyIP"; then - msg_info "Stopping Services" - systemctl stop myip - msg_ok "Stopped Services" - - cp /opt/myip/.env /opt - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "myip" "jason5ng32/MyIP" "tarball" - mv /opt/.env /opt/myip - - msg_info "Starting Services" - systemctl start myip - msg_ok "Started Services" - msg_ok "Updated Successfully" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:18966${CL}" diff --git a/ct/warracker.sh b/ct/warracker.sh deleted file mode 100644 index 19c412803..000000000 --- a/ct/warracker.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: BvdBerg01 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/sassanix/Warracker/ - -APP="warracker" -var_tags="${var_tags:-warranty}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-512}" -var_disk="${var_disk:-4}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/warracker ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if check_for_gh_release "warracker" "sassanix/Warracker"; then - msg_info "Stopping Services" - systemctl stop warrackermigration - systemctl stop warracker - systemctl stop nginx - msg_ok "Stopped Services" - - fetch_and_deploy_gh_release "warracker" "sassanix/Warracker" "tarball" "latest" "/opt/warracker" - - msg_info "Updating Warracker" - cd /opt/warracker/backend - $STD uv venv .venv - $STD source .venv/bin/activate - $STD uv pip install -r requirements.txt - msg_ok "Updated Warracker" - - msg_info "Starting Services" - systemctl start warracker - systemctl start nginx - msg_ok "Started Services" - msg_ok "Updated Successfully" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" diff --git a/frontend/public/json/ghostfolio.json b/frontend/public/json/ghostfolio.json deleted file mode 100644 index 84a2868e0..000000000 --- a/frontend/public/json/ghostfolio.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "name": "Ghostfolio", - "slug": "ghostfolio", - "categories": [ - 23 - ], - "date_created": "2025-08-14", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 3333, - "documentation": "https://github.com/ghostfolio/ghostfolio?tab=readme-ov-file#self-hosting", - "website": "https://ghostfol.io/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/ghostfolio.webp", - "config_path": "/opt/ghostfolio/.env", - "description": "Ghostfolio is an open source wealth management software built with web technology. The application empowers busy people to keep track of stocks, ETFs or cryptocurrencies and make solid, data-driven investment decisions.", - "install_methods": [ - { - "type": "default", - "script": "ct/ghostfolio.sh", - "resources": { - "cpu": 2, - "ram": 4096, - "hdd": 8, - "os": "debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "Create your first user account by visiting the web interface and clicking 'Get Started'. The first user will automatically get admin privileges.", - "type": "info" - }, - { - "text": "Database and Redis credentials: `cat ~/ghostfolio.creds`", - "type": "info" - }, - { - "text": "Configuration file: `/opt/ghostfolio/.env`", - "type": "info" - }, - { - "text": "Optional: CoinGecko API keys can be added during installation or later in the .env file for enhanced cryptocurrency data.", - "type": "info" - }, - { - "text": "Build process requires 4GB RAM (runtime: ~2GB). A temporary swap file will be created automatically if insufficient memory is detected.", - "type": "warning" - } - ] -} diff --git a/frontend/public/json/myip.json b/frontend/public/json/myip.json deleted file mode 100644 index 8698c12b5..000000000 --- a/frontend/public/json/myip.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "MyIP", - "slug": "myip", - "categories": [ - 4 - ], - "date_created": "2025-07-02", - "type": "ct", - "updateable": true, - "privileged": false, - "config_path": "/opt/myip/.env", - "interface_port": 18966, - "documentation": "https://github.com/jason5ng32/MyIP#-environment-variable", - "website": "https://ipcheck.ing/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/myip.webp", - "description": "The best IP Toolbox. Easy to check what's your IPs, IP geolocation, check for DNS leaks, examine WebRTC connections, speed test, ping test, MTR test, check website availability, whois search and more!", - "install_methods": [ - { - "type": "default", - "script": "ct/myip.sh", - "resources": { - "cpu": 1, - "ram": 512, - "hdd": 2, - "os": "Debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] -} diff --git a/frontend/public/json/warracker.json b/frontend/public/json/warracker.json deleted file mode 100644 index a276b04dc..000000000 --- a/frontend/public/json/warracker.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "Warracker", - "slug": "warracker", - "categories": [ - 12 - ], - "date_created": "2025-09-19", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 80, - "documentation": null, - "config_path": "/opt/.env", - "website": "https://warracker.com/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/warracker.webp", - "description": "Warracker is an open source, self-hostable warranty tracker to monitor expirations, store receipts, files. You own the data, your rules!", - "install_methods": [ - { - "type": "default", - "script": "ct/warracker.sh", - "resources": { - "cpu": 1, - "ram": 512, - "hdd": 4, - "os": "Debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "The first user you register will be the admin user.", - "type": "info" - } - ] -} diff --git a/install/ghostfolio-install.sh b/install/ghostfolio-install.sh deleted file mode 100644 index 9c02175bb..000000000 --- a/install/ghostfolio-install.sh +++ /dev/null @@ -1,131 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: lucasfell -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://ghostfol.io/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt install -y \ - build-essential \ - openssl \ - ca-certificates \ - redis-server -msg_ok "Installed Dependencies" - -PG_VERSION="17" setup_postgresql -NODE_VERSION="24" setup_nodejs - -msg_info "Setting up Database" -DB_NAME=ghostfolio -DB_USER=ghostfolio -DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) -REDIS_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) -ACCESS_TOKEN_SALT=$(openssl rand -base64 32) -JWT_SECRET_KEY=$(openssl rand -base64 32) -$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME;" -$STD sudo -u postgres psql -c "CREATE USER $DB_USER WITH ENCRYPTED PASSWORD '$DB_PASS';" -$STD sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER;" -$STD sudo -u postgres psql -c "ALTER USER $DB_USER CREATEDB;" -$STD sudo -u postgres psql -d $DB_NAME -c "GRANT ALL ON SCHEMA public TO $DB_USER;" -$STD sudo -u postgres psql -d $DB_NAME -c "GRANT CREATE ON SCHEMA public TO $DB_USER;" -$STD sudo -u postgres psql -d $DB_NAME -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO $DB_USER;" -$STD sudo -u postgres psql -d $DB_NAME -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO $DB_USER;" -{ - echo "Ghostfolio Credentials" - echo "Database User: $DB_USER" - echo "Database Password: $DB_PASS" - echo "Database Name: $DB_NAME" - echo "Redis Password: $REDIS_PASS" - echo "Access Token Salt: $ACCESS_TOKEN_SALT" - echo "JWT Secret Key: $JWT_SECRET_KEY" -} >>~/ghostfolio.creds -msg_ok "Set up Database" - -fetch_and_deploy_gh_release "ghostfolio" "ghostfolio/ghostfolio" "tarball" "latest" "/opt/ghostfolio" - -msg_info "Setup Ghostfolio" -sed -i "s/# requirepass foobared/requirepass $REDIS_PASS/" /etc/redis/redis.conf -systemctl restart redis-server -cd /opt/ghostfolio -$STD npm ci -$STD npm run build:production -msg_ok "Built Ghostfolio" - -msg_ok "Optional CoinGecko API Configuration" -echo -echo -e "${YW}CoinGecko API keys are optional but provide better cryptocurrency data.${CL}" -echo -e "${YW}You can skip this and add them later by editing /opt/ghostfolio/.env${CL}" -echo -read -rp "${TAB3}CoinGecko Demo API key (press Enter to skip): " COINGECKO_DEMO_KEY -read -rp "${TAB3}CoinGecko Pro API key (press Enter to skip): " COINGECKO_PRO_KEY - -msg_info "Setting up Environment" -cat </opt/ghostfolio/.env -DATABASE_URL=postgresql://$DB_USER:$DB_PASS@localhost:5432/$DB_NAME?connect_timeout=300&sslmode=prefer -REDIS_HOST=localhost -REDIS_PORT=6379 -REDIS_PASSWORD=$REDIS_PASS -ACCESS_TOKEN_SALT=$ACCESS_TOKEN_SALT -JWT_SECRET_KEY=$JWT_SECRET_KEY -NODE_ENV=production -PORT=3333 -HOST=0.0.0.0 -EOF - -if [[ -n "${COINGECKO_DEMO_KEY:-}" ]]; then - echo "API_KEY_COINGECKO_DEMO=$COINGECKO_DEMO_KEY" >>/opt/ghostfolio/.env -fi - -if [[ -n "${COINGECKO_PRO_KEY:-}" ]]; then - echo "API_KEY_COINGECKO_PRO=$COINGECKO_PRO_KEY" >>/opt/ghostfolio/.env -fi -msg_ok "Set up Environment" - -msg_info "Running Database Migrations" -cd /opt/ghostfolio -$STD npx prisma migrate deploy -$STD npx prisma db seed -msg_ok "Database Migrations Complete" - -msg_info "Creating Service" -cat </etc/systemd/system/ghostfolio.service -[Unit] -Description=Ghostfolio Investment Tracker -After=network.target postgresql.service redis-server.service -Wants=postgresql.service redis-server.service - -[Service] -Type=simple -User=root -WorkingDirectory=/opt/ghostfolio/dist/apps/api -Environment=NODE_ENV=production -EnvironmentFile=/opt/ghostfolio/.env -ExecStart=/usr/bin/node main.js -Restart=always -RestartSec=10 - -[Install] -WantedBy=multi-user.target -EOF - -systemctl enable -q --now ghostfolio -msg_ok "Created Service" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD npm cache clean --force -$STD apt -y autoremove -$STD apt -y autoclean -$STD apt -y clean -msg_ok "Cleaned" diff --git a/install/myip-install.sh b/install/myip-install.sh deleted file mode 100644 index 725536d11..000000000 --- a/install/myip-install.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Slaviša Arežina (tremor021) -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://ipcheck.ing/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -NODE_VERSION="22" setup_nodejs -fetch_and_deploy_gh_release "myip" "jason5ng32/MyIP" "tarball" - -msg_info "Configuring MyIP" -cd /opt/myip -cp .env.example .env -$STD npm install -$STD npm run build -msg_ok "Configured MyIP" - -msg_info "Creating Service" -cat </etc/systemd/system/myip.service -[Unit] -Description=MyIP Service -After=network.target - -[Service] -Type=simple -User=root -WorkingDirectory=/opt/myip -ExecStart=/usr/bin/npm start -EnvironmentFile=/opt/myip/.env -Restart=on-failure -RestartSec=5 - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now myip -msg_ok "Service created" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt -y autoremove -$STD apt -y autoclean -$STD apt -y clean -msg_ok "Cleaned" diff --git a/install/warracker-install.sh b/install/warracker-install.sh deleted file mode 100644 index a3f4d13c3..000000000 --- a/install/warracker-install.sh +++ /dev/null @@ -1,119 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: bvdberg01 -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://github.com/sassanix/Warracker/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt install -y \ - build-essential \ - libpq-dev \ - nginx -msg_ok "Installed Dependencies" - -PYTHON_VERSION="3.13" setup_uv -PG_VERSION="17" setup_postgresql - -msg_info "Setup PostgreSQL" -DB_NAME="warranty_db" -DB_USER="warranty_user" -DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) -DB_ADMIN_USER="warracker_admin" -DB_ADMIN_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) -$STD sudo -u postgres psql -c "CREATE USER $DB_USER WITH PASSWORD '$DB_PASS';" -$STD sudo -u postgres psql -c "CREATE USER $DB_ADMIN_USER WITH PASSWORD '$DB_ADMIN_PASS' SUPERUSER;" -$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME OWNER $DB_ADMIN_USER;" -$STD sudo -u postgres psql -d "$DB_NAME" -c "GRANT USAGE ON SCHEMA public TO $DB_USER;" -$STD sudo -u postgres psql -d "$DB_NAME" -c "GRANT CREATE ON SCHEMA public TO $DB_USER;" -$STD sudo -u postgres psql -d "$DB_NAME" -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO $DB_USER;" -{ - echo "Application Credentials" - echo "DB_NAME: $DB_NAME" - echo "DB_USER: $DB_USER" - echo "DB_PASS: $DB_PASS" - echo "DB_ADMIN_USER: $DB_ADMIN_USER" - echo "DB_ADMIN_PASS: $DB_ADMIN_PASS" -} >>~/warracker.creds -msg_ok "Setup PostgreSQL" - -fetch_and_deploy_gh_release "warracker" "sassanix/Warracker" "tarball" "latest" "/opt/warracker" - -msg_info "Installing Warracker" -cd /opt/warracker/backend -$STD uv venv .venv -$STD source .venv/bin/activate -$STD uv pip install -r requirements.txt -mv /opt/warracker/env.example /opt/.env -sed -i \ - -e "s/your_secure_database_password/$DB_PASS/" \ - -e "s/your_secure_admin_password/$DB_ADMIN_PASS/" \ - -e "s|^# DB_PORT=5432$|DB_HOST=127.0.0.1|" \ - -e "s|your_very_secure_flask_secret_key_change_this_in_production|$(openssl rand -base64 32 | tr -d '\n')|" \ - /opt/.env -mkdir -p /data/uploads -msg_ok "Installed Warracker" - -msg_info "Configuring Nginx" -mv /opt/warracker/nginx.conf /etc/nginx/sites-available/warracker.conf -sed -i \ - -e "s|alias /var/www/html/locales/;|alias /opt/warracker/locales/;|" \ - -e "s|/var/www/html|/opt/warracker/frontend|g" \ - -e "s/client_max_body_size __NGINX_MAX_BODY_SIZE_CONFIG_VALUE__/client_max_body_size 32M/" \ - /etc/nginx/sites-available/warracker.conf -ln -s /etc/nginx/sites-available/warracker.conf /etc/nginx/sites-enabled/warracker.conf -rm /etc/nginx/sites-enabled/default -systemctl restart nginx - -msg_ok "Configured Nginx" - -msg_info "Creating systemd services" -cat </etc/systemd/system/warrackermigration.service -[Unit] -Description=Warracker Migration Service -After=network.target - -[Service] -Type=oneshot -WorkingDirectory=/opt/warracker/backend/migrations -EnvironmentFile=/opt/.env -ExecStart=/opt/warracker/backend/.venv/bin/python apply_migrations.py - -[Install] -WantedBy=multi-user.target -EOF - -cat </etc/systemd/system/warracker.service -[Unit] -Description=Warracker Service -After=network.target warrackermigration.service -Requires=warrackermigration.service - -[Service] -WorkingDirectory=/opt/warracker -EnvironmentFile=/opt/.env -ExecStart=/opt/warracker/backend/.venv/bin/gunicorn --config /opt/warracker/backend/gunicorn_config.py backend:create_app() --bind 127.0.0.1:5000 -Restart=always - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now warracker -msg_ok "Started Warracker Services" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt -y autoremove -$STD apt -y autoclean -$STD apt -y clean -msg_ok "Cleaned" From e7936a686a9ca940f5c6450f719de74765e8cc92 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 30 Sep 2025 09:13:44 +0200 Subject: [PATCH 1232/1733] Fix NVIDIA passthrough logic in container build Refactored the NVIDIA GPU passthrough configuration to ensure device entries and related settings are only added when /dev/nvidia* devices are present. This prevents passthrough configuration attempts when no NVIDIA devices are detected, improving reliability and error messaging. --- misc/build.func | 55 +++++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/misc/build.func b/misc/build.func index 3d5a62dbc..c53632a9e 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2198,6 +2198,7 @@ build_container() { msg_info "Fix NVIDIA drivers on host, then recreate container or passthrough manually." return 0 fi + for d in /dev/nvidia* /dev/nvidiactl /dev/nvidia-modeset; do [[ -e "$d" ]] && NVIDIA_DEVICES+=("$d") done @@ -2205,37 +2206,37 @@ build_container() { if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then msg_warn "NVIDIA GPU detected but no /dev/nvidia* devices found" msg_warn "Please install NVIDIA drivers on host: apt install nvidia-driver" - fi - if [[ "$CT_TYPE" == "0" ]]; then + else + if [[ "$CT_TYPE" == "0" ]]; then cat <>"$LXC_CONFIG" - # NVIDIA GPU Passthrough (privileged) - lxc.cgroup2.devices.allow: c 195:* rwm - lxc.cgroup2.devices.allow: c 243:* rwm - lxc.mount.entry: /dev/nvidia0 dev/nvidia0 none bind,optional,create=file - lxc.mount.entry: /dev/nvidiactl dev/nvidiactl none bind,optional,create=file - lxc.mount.entry: /dev/nvidia-uvm dev/nvidia-uvm none bind,optional,create=file - lxc.mount.entry: /dev/nvidia-uvm-tools dev/nvidia-uvm-tools none bind,optional,create=file - EOF - fi + # NVIDIA GPU Passthrough (privileged) + lxc.cgroup2.devices.allow: c 195:* rwm + lxc.cgroup2.devices.allow: c 243:* rwm + lxc.mount.entry: /dev/nvidia0 dev/nvidia0 none bind,optional,create=file + lxc.mount.entry: /dev/nvidiactl dev/nvidiactl none bind,optional,create=file + lxc.mount.entry: /dev/nvidia-uvm dev/nvidia-uvm none bind,optional,create=file + lxc.mount.entry: /dev/nvidia-uvm-tools dev/nvidia-uvm-tools none bind,optional,create=file +EOF + if [[ -e /dev/dri/renderD128 ]]; then - echo "lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file" >>"$LXC_CONFIG" - fi - - export GPU_TYPE="NVIDIA" - export NVIDIA_DRIVER_VERSION=$(nvidia-smi --query-gpu=driver_version --format=csv,noheader 2>/dev/null | head -n1) - msg_ok "NVIDIA GPU passthrough configured (driver: ${NVIDIA_DRIVER_VERSION})" - else - msg_warn "NVIDIA passthrough only supported for privileged containers" - return 0 + echo "lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file" >>"$LXC_CONFIG" fi - fi - } - # Debug output - msg_debug "Intel devices: ${INTEL_DEVICES[*]}" - msg_debug "AMD devices: ${AMD_DEVICES[*]}" - msg_debug "NVIDIA devices: ${NVIDIA_DEVICES[*]}" - } + export GPU_TYPE="NVIDIA" + export NVIDIA_DRIVER_VERSION=$(nvidia-smi --query-gpu=driver_version --format=csv,noheader 2>/dev/null | head -n1) + msg_ok "NVIDIA GPU passthrough configured (driver: ${NVIDIA_DRIVER_VERSION})" + else + msg_warn "NVIDIA passthrough only supported for privileged containers" + return 0 + fi + fi + fi + + # Debug output + msg_debug "Intel devices: ${INTEL_DEVICES[*]}" + msg_debug "AMD devices: ${AMD_DEVICES[*]}" + msg_debug "NVIDIA devices: ${NVIDIA_DEVICES[*]}" + } # Configure USB passthrough for privileged containers From 45f9098fea92d8de74ad32dbeadeb8fbc228c040 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 30 Sep 2025 09:44:42 +0200 Subject: [PATCH 1233/1733] Create pve-container-upgrader.png --- misc/core_ref/pve-container-upgrader.png | Bin 0 -> 70668 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 misc/core_ref/pve-container-upgrader.png diff --git a/misc/core_ref/pve-container-upgrader.png b/misc/core_ref/pve-container-upgrader.png new file mode 100644 index 0000000000000000000000000000000000000000..efabf8a8159c013757e0ae8fb6342ee0fd758fb2 GIT binary patch literal 70668 zcmd432UL??x9>|85dj4el_r89Aieh{RjM=rDN>|2X;MO{A|ge43%&Q=n*t&=KHs>|Zw)WndW-xFeA6T2!K zy5r!G_1t{n_PZ2YVSh>Cq43s2$K`{E_xq35I7%O_t)1O%T|7+nZe!na9j@|9R`-+X z?iH|_whzFKCJB|TwPH-9C%zJy_?oATM_l{pi#FcjvUy!iNcnB6XxWyid4^r(av30RG2j2)Y`YpJuChuks0-=O&uON|Au z{mYPGaFcJq?&wKO>i%5TM#fpIDc2-a914N(+g#ScB!`kXjTN!O{;TomhZ6kLf5gd% z`%ho-wemmRz;OzSfBNS2|A$>3c>mL9p^*+v?=A)p!#J0BQKe{~(z#b7&$7r03(FqX zB%2J8AUv&0v>ddd11gh^G`hn_K5^{Y5GdF6&qKQJay{bHLQ_X^XW`0K0N)8wcHx(` zSD#g#?32j9UeuRB3y~|?qfHCR9M#@_vztc1V42WX^JvUX$$E*#K2CX&Jfv@ z$ukqHEp#V?D8H>SUf%0ki1F6i86tmWqlyxakDqtv)5(zSU=~}`mwC^$KNDA@CJSYT z6iqG6)^Ee>)hm+d<`_q3Ue?<4gMc=Xq_q`K)^sdP-OKN)vlDOGZ@o)>lBB-Z2-m48 zG2|T6Em7H1{4pYI^4!zGHXim;r;>YZ#+}@uF2^8p@T~f*nT(plq{Db7uO~Q{iEP%k zQIjRHDsIYIr_3}r)zRpabn4?$=uu;8NqqunMYaiS5z-2M%>1xgdpD0kAjYqcuBnEi zrE6f#zWi!E^rVwU>t;3-UnxII1yQ25{t$u3C}YqDzL4KVE$iSP25cbI=j?bWg= zw-!^fco}9mB65ZVA^XP0?xQkp%#TO<7cjC)Jy246|0{0+n4b|ZfNfB`CqY$U$K%BV zy0TQUh6;9d_pO&Ag;KXzhVg_9r?(ombyHM5C*GvmrBw)k$i(6A-=lsbjM?LhRLj&S zOSSZm=t3wPs>b)c&K1q*E06@u8U`_qwAuj!@nCXzjb-kS0; z&OIm*&?#{b`ZjU_zWQ089G_hp2dDeXZDJ-jROL%`z_#&80TWQgOzMc~a!@CE^8$Po z8!ai2FWrionW)n+XHf@dn#ppoES!+q2u&RN5IBBzc#9h&`&i>bXLrL|NQ{xGbAv*h zLbl*T`V&H4z`%fh@tV5(mu;=;Go!?Bv7Il?n4p!~(VD^!sd|^H?^EwROr)aIvzf#f zK9Xs1DZvw*7n=IG>~)LP-YgHgcM#Pqk+v7lCgfNOCgXV9GfyTS9oPEU2$jJil;XO? zRPziU#{wnOt>|of%9DMBkAw5=ODvCvHyn9e5;@kiHHRI3=f_>(LcjJY%1VnlOI-3Wzz&1iqjS`X&S>Fcu+c$!)D+;!Qt+= zxhcv!Tepv{97Cc5$5z8Yi)V`(VAHqvFNZrxpqu9ldpRFV8WAG3NztY-rK->)=L0+g z)A=<)7}~F@2vzF0S#TlVvnJgYH|>L4i5VmPv|F$P*0TdINWiw#m;PKTkNd5LQElkv#H&$4_j7!&UV?m+-ACj@$h?w96x-5KB595nF zd-qQ1nE?~DbRr=(#!XymaS3q7KZmE6j(H1=+zxX*=P#j66i2jG386fti&8$nPlLz5 z@0t{E;VeC(>ydgPFb}e$(x&_RMrVT8^A#b3+NO(5sl9fa6i2?2_P%kcGO2!(&a%zT zJ?SH><~DGSowAd2s;#)Cr|Ma2h06~?CZ*-dH9R&t>r0J}*G~?AoQ&Ke(A<5pc5x!h zUM;HPnYK0&J|_hlSBkUC0Z={0Ny#mxB71R&OC#htwOoW}*8sM#*?ZciUGO=%1I7;V zVAs?Wy8NxOS-A!QkwNp{*nj=%8QBWAh@UcdN`n(=tYTXE!k!3%wwy7Ia~E$&ql%9X zhgJ@_w{9y`TxyTKxLMSA&zSE{budV&#xR__UmlG|I#;hpN*CxTVInl>&ke-TGzyH@ z-D>4I8I+;Qki$rZ+>um=UJ2Y^xakp8Cj(`6DpK4$A3Y_V$O$BS14r0Z4bF(O{E+XO zGL$;L%kXi(TbiCd#Mk8&!%er`5oe0ZuXMhf*d_b~HzvZe9u?*CST_~xXB6Hw zmJY7qA9vPbOo;jItZ(ZzPvaRkICkO~mgbe7_MEHPGNX&*Qyur-*y&G0={)fQxjDNN zaq9^(AKil=1kh-OQ&k>YbDc8%dxaqfbaHgHfFU)o7^u^$?!o zPpI5sjd7`5Z$>_95LB9^H#U`1#@5*@q>VPyY1mw9G$=Tgsc@?J_^*shC6Cw6wYt+9 zW;aaV@fbJOON-daW)&FXl8Y0x`aCZ+7f|=K&VxWB>(MQos16M+iKJo8jvI)sLF4 z!uXeOkI$tc5#g9HvOQ`SJR+ylxq(qz${D(oqXi9LG+K4O+F%&GsS%OZ?$OpZa$(}J zIqW&XX25M?#O6N=$8a3KoONgI@bKYuf&)n_#QO)+3iFEk5O+Qv*8~n;I$!pdYPfDB za_rcl0%~5uKLO*AM2*MA@trZ55?y=`hK<}lJ%<73NSOx)v()^%E!UoNzvTxe3S}M! zvtoTmD?hbeGPP~X)dW8XJ(JO~8&#T`N4Jrgv-V@fr*ONY2qOhQayM9Lmy>z%q7yAS zRBcD$3kn{4y*Ycd0IqH@$`$k3rM5(Ja5E^oS`AIw3l~4yyqB_04w&gye0)CUa|24u?8cz4FHAn*xL+! z(N(1A8kZ3*fOGfzdz`0S=9cqPhA;wZ0}W9UBEsp@`@x61=r=_7>Zqm{nF_x@-gmjj zk!BBPg1Q9SUETKm>p^`)LE-Cl9^^8?uhgjt7rElUL;_po&H~Qea}dH?qlF%MCZy?*|#5ajFWJL9CYw4RN1$@Gb8 z@kU-29389eU7(4tdv)dmF|8?*4R+vxMcEMher@_M0(b3~LL1i?nL|Eag zno9|F8ZK~H(W{F*=ks<(w>1R5R|x%hgvWD+_mmwbUGmS!0B1FJv_I0lAZYnNlkekI zgN}`0IgftbrlFeV#qJB35%=oE*3EvD+?oKqwy`VhWyz?%Oa{?}Q0Uog3#C}*;p6G> z%r`1T)rt%bmv<=88GoJvPNMKFCZ%Ak2O``9cpa&#@lO;y0nE$EnwuizZGhGv5kaQX zckyxDaB1q0Y$Y_E5vs2A|SnOi)aRvJyEUIWs*V3bQo84nqG-_t>r@z1(I6s&oO) zX2SNOh8NfB_frKuPD*LoeF|Dtf9FwEZIa5>ohh&xgzA)nMHtq+yOUTUL~EFh}4 zI>?ljbwLrRvV=^p>CM|QGJH-hZgA=|%+r)6O`FNE<+Sca9RL?wqehfHX~myLaM1VeBb*a*8~%E(nWFYh6iI;vA`fX4o-F8ZDL8k%RK2q zY0KKgW3)|zR4)AzS*12U`zrNYhwQPuv-~OExt+kVtr0!2vatG|tGnyHo7+24QO-3jL(0U8y~A1XX|a&FWQjH~B%kCWho+!X8uM`HMD5Dp;{^KNHKzBHl;yR1$MCZ{g&e`k)OX% zKe7!|3o^Q-qU-h{vPCAvNRwG|LdfO)-b4l*9P{`0DSVM-;`BMx(w7Liu@A^exX%6| z0&)zw4l(_7YC^s4-Q{3il(8Zrc7C>h;huplZK zvN{))kbYd~MTjp{=rF5Rp5K3kHV}Su>H74kht+eKx7(YG&tR*{DC9x+kr25NZq93QM+n%LwhOZved1XI7+x?&8W8R z4;zFw*bDR_Z0iNRO89dx_!m?(E{D+Uv(n||oKf35WvY-IkE$Lnn3)%sD@dmYDU|6j z`{h|oPSfq`-7U3?wC9EnzY1ogL`R<(-Ny}jk6rl9o7L}854JyZ#|W6`(T7d!JQ0%f z!K0{nA3vzY()=Oq{nc%Yzt(*BZDN2MxXS+r>Ab`$k<<1`7PwuVdkrYZEpgH0aWr5y zR-yz{0RxvAHZzT-x%~rX#c*k!yS2ux9Qe9;d{YxP@5!WCiwXXo231Ajb-ifQ$)7ZB zsTIkrISWTavle&zk( z+vLJ?BPw)Q=2{o~05zW`G$U{%z{p*+Q)0OCm?$o>i~JkIPKClXe>tUX)3$D!=%$p) z*McHG57)w1q&AJcKQfO{w=N&hN7u@a`*bN(*f-_Hr&fHYI_{-{9c^T=md{SU1ZyRE zn$GHIe-*5qKPt+#nkyzQ)9ABvmYw#9X5V$|T6l4HsP_V9$psH#0 zkk1{pL?b7|rS84JI9UE*`PnMp2EUGOF?6Enc4|WtFF^HylILCV3^6C2y4@+ZbUuAc z&6#CkJ9kX`b^8n>dFNDl3XQjeg{|qsa&ej+I>a@hsjIhAfsKxJ3 zsu0vI7t?gL;GXI%x&Yhz{qS7!>p`ZU8;UZmXe{vp1KK`LE zzJB@n-A`(?*@+uv^{n@rTmFiXfO1E4YM=E*E;c%fDsp(-#GWOR6lZ}ra5KwV^azy@N2B2&UJ4ir$r~ZF+gZ%l8|l;_u&e-Vo4|_KGhhyr_?N#yEQ+ z?V;+WiFiy^8RNv-+z*+*J}D6tDJgk2RnbjW9XR}LH$vv+z47cfov#``n8Us`qGpfT z6gD8m9W_gug6+k;JG<)>L0c+uUc9DzkSfT&RKMgjG#_v|n~=B!Dbu|Lmnw=d+4dNm zpakIr+DktRwm#ge+q}homCTUve$CC4lO4XIsr}JxzhOR0E~zk8weFa{>~)!TAuX`8 zc#!^6*!3+dEFdCmd~Ib+x?yJu%4I`}Gq7tyFWy<}2Si0yF%W`QuqdzE75e~F-JNgu zUxjbRF!&{F06s(?%{6UKPqK!hc`PX76vE%Djfg;U6J#IZ1ok<8Ru8w=e$HY$Uq?ud z^X2Wa4clASei9sdC!^u>37=_6aUlaG5XIh|JKc4Z?_LePJD7Jp{Tw~5qu)bbV&>BSRzLgxzvw)J_R35y#SHU8Q_ zaT-|8q-Vq~nvn>aQ)c`4sk^4$c%gF=2#f zJ9ahCkWBtw8!eS~UtT`_=@&a`nD%ZAH$pZUI0j{S*wkaMSM(ggJ5Hx`Mz5!4w#}EA zG(OU%fobQzTl%U_gUDV0m!_!4X-b~y**`>2s*AjQ*xyhmRYK3mDOW$NHV*M9qZkn7 zywM~4^XPAyw~s2O_Nw?E^U@73HBM$uYaXfw3X?6#z5VDkN-;chuga;Z*fh0ZOU*mW z-TDGU7E-PuE4OlvhtnO*!u+jwudP<#w8UjRIU4;OaPd24lS9)acSHJ2UJN-Fi@CjL z_8Ke}lqw+U7eH8%;tCPYm{acGxL^IaGK@a$9|a4kIh2`YJP;)25Ou2d2Y^Ouwa_4vTw9OZ2K5L zw_N9Tq^gG`!)Ii(mWh`OZY?>0L0$OZWvV#c(<)ODjE!HZ|7)qU z{{I5|v~h{Sy}U+1&rD-T0SDj|?=1B$pjeD`m4qY!Za2S9$YF(iC-O3>L_QBK88P4a zC}Es@wl}4V`Jyxb11NoI^OU$b+Okv8%Q1ZPO0DBv!9!!M8MCVY8T^T@6xIlueBjU^ z$~RngiB>=MCPWdch$$R?4eImbj8X_CJrdt>_b76FAzlzs{q*yHAb&B;sTdXxhY!9< zr4@~J^x|$*^ziQax-JdXMwtfvraXtHQi1rSRYbUBs!?3Y8+j)e$1X!w6EVCZO$)J;0p!dP zo_~y{#KeHZv3}ERCZaAC?}ek-gG@xzzR%|X#FF>s zkP`3}&~>+Ji%Lf6&O6s=RjwgOg zjdz=2>?A?B;gARiE#7LaVmr*3H>v$Ayl~Iv$cCi4YWwF!=X!H5fWNkW6Bao<^31mI zA$d&nFj?Kkrg(Bm++WmAhAEo=qb$b&6BUzk?dY=s z3m>s8I$ggHQ5AZ}Vy)JLbutt?9?9F2_I;o;;_B0n^R=VLu~2A2o2}akHva!k=j(%t z>nGBA$;C~1=%KWVfmqb72(YHTCmOwV_ecp9Ce5pZbZv0Vc!ltzO@#SZRY zM6L3?^R^!& zcC(AQUL+kP6#KYb>baKAZ0$MUvkBnXTUYAKI9w#T<%mYR%_7hFo>NfAil{?7mWEj7 z1VEI@n@Fu{3+als%~y;11=%b7oXx? z(trtX!_#30L*hEmtOGV4h0QcwEo;Ektm* z8k&y!gu-IPXW}PES5ZHaQ>7ZjTT-lFa@u}rzM4zO;>{NHeUW=t%G!`QOHiC4WO620 zIgSs@>T{nlpX`{*{Q#YXqgbzK)9bXKc z*VE9*fF?^c?_S@{Pk;C>>sQl^nZM}0>civyJ28VQ(d3U@(dOZNwjYPnFIR{jQ4r8B ztAEn6IikA8WzGSej$*A~tSB*<3LP{h47XEKk; zO;Qu(b;$Y)Fq(^i{QCrhI*P=Semx5jto+m1q)p@~fSeKNT16$CG7Zq69!HEa&4xsE z?p4}R8tSwBhxw$)^!xYe+={`ZU{+-!O(ia4aO?wp%Vy+J--A3Gp%y0d1QLpV0$s74l?6E>F^Co(*?)^K z5-{*AshuN;icSg6gT9_|L;mKAJ^#iRwf+~rX#4^<-5O;#V(l&Ml?jD|Eg#7R*GZx&n{Xj$@Jxh~}}jCBWOZ^Jt28_0nRQ*8@0CP5opq*5hpg z-Ag519P4`v+VWHbFGj3~?o68R5U9Z8T{FcVnD$PW1EM!>r(5e#*)XdD^pEKMR_l-6 zZogJfESYO8(bgq$NXcmwu5frC!@NfNu@KHy_^2R8DDZ=I(#ORURhC3i<=r6D z8c!PSTUoU)~O0;_IUqu8L;2btc-$u;Kk& zOOK@I?`lBw+7lrx+ji{WB&Dq;bfzKXmCl=4eA{M0(lkG=2|XmO>xlZ1+cWgWr_^x! z@-_Gqgz2jOx1eLKu3bZc!>sZSYp&xUIG-&izW;!&!Jx97G;yVJXq#2b6QGE|19IOqsWiXRAA{(3M*k+Zwm@pXil;$6&BV-cvj?gCr)_k}CZOLe_+oe+u zn=|Pw5B2Pd0iPOPM4i-Lso7l76Lr0&DV==$XD5w8+J#uP#cWj@MGy| z<4BB6Mo~un-#9ZOxIVDJ7$^ey>xfpPL%8IRD78<8I?R@c=!O6~Xc>v4+Pg$LEcx3v{wR7EX>?;xT@A-`iTwG$K7LFSr6 zi5HZz3!QHJT|2|lrN9q98Bd>*@Eo3&XWEOFBY#N=Z}^R?rkIzJ#Y})MU zI{I?ZqU~DrYA*+$bs@~JEpoSTt9OmG9uau{7`_dmc_F;Pxxe%B#E*oot3GG~W1Ac} za17lHGhb|x^f*DQ8uhCS@{!K+pt$x=9&#gxI==6VwXxl^B_?o8`8!+$0j$>ojusT% zNNM0fiCCOv9P5Me^4;2ImJIkKUvGHGT=9t*pMsLecI|?FkC?!DKc!6-I+_*hPZXa+ zrGfE5lxQv5tZ&$Y*COkGF{rON`Azk4?4Pi5Yq|XcIS!=}fntWZ8a2P%&EInW%G zrCu(tOr`pV5hh+4q&t?$CK_U%d@qozp!(hab;7mM352+P>&ha=@W4g!=!oPrPmCdq z#PPh~5;c|McSCw|C@3!3AJzL@{O%fOkVbNWE{AE?>RiSvN;Gd@yzgaUjwHjs8|ueY ze)lKVZhYW(6w{4lU&zZvk=44 zl@X{=e;!{Hmru<7u2$Jk&Z~QXEetz= z_jsoM&#&~bv-bqiPFm%!Dc|}LnP)F-QP;?K;^I7v|68#~>gz#DnbyXn0SgNIl3p4X zNQ%>K!NT1AhLL!-oljVWNQ0aD_+Lb`o9Dj=F8>8L|0~X!Mh_y9ra;{UKLILIJayN# z23hVjVQ2q(Ytn7T_xUW&tx;qF=$OqjDL_&*V4c=!vHJCIer1|b$NYr_I#Zl=4PR?# zRywhcI4Pe~4aPv8B;54>C=oP%dawF%j>1^L-=F%BnDM=pm&R^UID>?|ro@ri)&(m@ zT+;o7dow~FPIW{&h#9iu!s*c=viiu2ARj_T`hjXQn?H3N(96))W3z_X7FfghUqdi0 zmEYpGX}ER|gZb=yk?1={9W&}?H?A3RBt_t8t|Ei_TSEGXM(*br3;<<1nU#kZ+F$|N zKm-FII{IwySJ$MCoG?U>QJz#+Pm-IleVexoSIOP$^apyI$0lbrA3 zeEI(9jpHOaY@hxTvb~MV$DEnWD)w(UlQ)n|A&FrBn(ywESRP%h?&5`H zz!G?CNQt}kM;bRKL4)psRm^9}Z0o?~K^Pj}&58+RPB(f{QJ)UfggnI)`U5^hPUOfC z%{TV|93|gXDb1#GrnBC}h5bC&oTNA2(+%ZVARGS~4 z#kUHE9PCgS2U}Q+itpN$0G%XffB{8mE5&ijR7|C&=_ww$yD=MJwk+$4V-v#xdrD%~ zU&=QWjaV&!!uEy{x$-uZc~qvZA)LN@L~^&ZubrhHY8i63Q!hw$xwPl+5&gZ`>xqf~ z_d?t6hFIbWg8lIv?dLFu5I?Cd5i#%@i=T?}xSmU_XMA=qXX=ks2W+pt|NA$Qqb9J} zjuQBHQOs#bX=QNSWXwb9!2V8YrcPit%Cs|D&pL??o~NE~U@W3yQJ{P;VSSOm z^cufxvwO|>r%o<<%kpcZ)Fu+Usk&+Np`n?Qr`_aRSb1f_2Te3SRo9eju+SNBwPX}U zB!zrNCwXGx>wGDA8NyoTaYW)WQ34j)EXzVaze2*ys2hb{``g*H-P3!|1WLqq#C)vi ze3d>>(Y4lUPKb8Ko0bMB&r0QC{{wM0ugZenT6?E1a$QfVxa@%X^HX=9sB_a-bj5=& zY0aXL>l);gMV=*{#!J9(Rc_pL`gv^X$7}^|`m27evGpH*S$W~o zhy4Oq2f&<-qU*1w=0DPk0yK)GP+eObU4TUT^8=Efjh`)+DU}>}X0zA1`m;`Z6uQ%iymSW6{UJRG?&91>dG5fkHH7|ysslUxl*_*jPhjW0 z|NGJhPllbn%AxU8{kk);dBFQ3;jfn5bYjl}lC(Xi`y~9f#%&hJPPT2P$MOu?8`RDN zN9??fq9K0*s-b^6x$1(YN^U>m*+E}5*JcV4(5nahn?zz)u-?()x#T{fplM5MO`9SK z2jqLA2fRFe+ZE2=nF^-Rk7GHGFPRICTKGX2@wa9e7o?(|fz@k?lt4P=(%6_m}Dt}MDS$RIH>_$zzpI6ky z3l#70@QkDHI$)gNjjA?R!@pJdZKp2$a=a0nnuwXww1IkyKr#7eB2H4~J=kajyn5ku zB6sk%2ApB0veR6msIm=&_kn_H>wM>wy+N=Cx9gShgz)XLTFaYeuBNurNc|iov zHK9BszQB2icT<;g)68#&h6XhQP>h$@r2nPGdX-&IlrRYb|C{(leg2IK+rsqk3j^t~ zo0q3S*!|64?e6#gN4eR*KLqDyi&HHV5d+H1zl!sZoDOmq5WSXyU3EGj`YbInMf$lW zOcBQ_O*QMaXBRDzKmAtr(}wRG`5^I}I{XunBW4d5$x}sS%DCPY8sm8e^e^bmd+mQo zp}vGjq3m$h9ycK^bu$TWbzJ4}*^}A+rhwG!_X>HJ`Ez zmZ1JhSL@teXUzSeGR!~vZAe=Fv{B{88$bU~w^I=BexZp7t!#N~>;uhmflhg(+P!|F zkEICWTjv)$y=RIe**!V+y7)r|Tv(uOvH$8hH5Kjoo zz3;ib+}pPkcJ{09lzQWtIIY-aO%!2YTTPc`K(v>B)hF4q;^-;6`9U#n_hh?0{>Q>j zSbnZ9Au zPd`cTu$7kc^j`9k{$I!XUuHt@VSZoWrnf|a1f-VSen0LA4kX1BdL1MUU{vWJWDw<5 zqET|Kd>OhT+UpF(0%-hSEs-fe5rthX%nGhp{O!bYv ziAc|Y7EqBfms$3yUY~ktR+bt6N_#Uh7?{^q@AE=S9ZS^(R zGnh>rZ9eHm3PRW(2dyq?-4p^9OsNzGVVbF18 zQg9lMGSI}AgvV!^DF!=S{#MUilji@u^FBrmpCav|=#HNKd{CIVW8{;yj974T8oO47 zwayu(Q*F96`{c`7hd*HJJA(E`LJi`nhKuY}g>Qa(5vHwaW?-GxQ#IvBrGC=>3G~tj z8z*ad)G+fAYM^RSG@245aZu%PF8oyv6C_fdlnLreH*IMzjQ>R>vEQ0e=rXK10U&Az z(;92`Z(L+6bWcUqgFHp#aMK?h8;1o(O`y4iyL%9kxoAEmroro{^U>d+z;A7;*qL#%d7shr zT8np?rr+dI#C#kvEwYYgb&{L)>Tq%Dm;<`Fh@J_rm%mmpQ*CRD;+v6&Ol*O*El{ zmBSfZO&!T$bg{WmXs)l_=h{V^zeEy^8931+Ko)Gm(A8}fTO~l1)TFRh{xZ(T_W4?Xkm@&xmr_z`uvW1b+2f%KB~DBHc~p)hvoF*J0g0X)EL&8p*|nyhV#Yxf)<)7FT1k=? zeNHI=zs;21U-KIIwfO14_FN@aJZaZ$`!6Ux03TdzjIl04c841*D!kETwvDlPa(6br zDx-*M(PkE+Eo6G?oDo8o9lJ^wzqV01Mz>q%kMqBI7gav*liYsQk36+BsAi zO_+Sm)p2wTqM4pcSuIj~l-$qVL+f-jBXYP6+|f(oGF!=?VZ*s90YZ=^g8%UudqX@-k$?pS&My66?EM|3K_l4Nk9&)6m&{G%$ zWZGffU?2}xjvmf%&LZi)iaLpcNawj+7jbwX!lhYP2NqFOmkWqaRj}xfGzQ23H|Cp$ zBocfQ-o>v(B*n+E%Fy^bupgVT(_$J$>(5ur(qFp-4C=9(^Eau`M)1|d(R<@P^Bx-? zt8z_SJNx&V0HBf;k1~gOe0IRMjOJCc*9Z@S;8q6~59c?U2C(!aksus3R&%Dd@8sx@ z?IuRoX>{@UMJ-Azwb4Om@Sl{0y3k#(d^zjK3Pc{)?=Q#toQo`X_=C>A4Y{XGi__ir zHg0;?q9$HSZ3mm9^ne9;vJMo7@D*PQJ7q--u590WxM7QW+!t>im&D4eXVUk@{@?Eg z#r$5-bpQsw&?*x#FE3*5K2!kFXr<&im3nqcXub3d54I@d!R8AU6)VA z-uB%6<2y?N34Em%@X)-bR7b9ryDyhLsyoKlG|)yhZYIH4fLD^a5RC`9k%qpPlLKe^ zE`^^=^#`cdv0{S0!tF(5(^1k#x*J#c41V9E=`Xn?g|D7m{fO5Sy9g8BnzW%gnbO3> z1)%n;NT4)f*g2n5;XQv^KsI)8txxEv8}uEZ=baF>4G2I5HqdE&6f?39#O{?kd5OQ-CM#f@^4>Dk^W6Cby2aR8 z#p#|VWSSa(3yRbOf4LBhHtv7A1Fr|Luxs`-C5=Phi&QuL+tN$vxpnvA?M~%HnB53rr5=5 zanyfH{TzIJJsmrmSn^{UTgBkrlT_n2Qy+nL{&j*H_i3PLZPJZUUm zJAx!HD9D77h1#db1+}YTbHw zUAV`u?eoqE<$(1ACEKc+*HBHDwTKzSUV0-D26X}Cz!Uns_k;WmLIMo(4&bqJ#OmWu z?OLx^>q7)HFs9_FZh@!wdYp>QcM0E>S-dO1*-H31(l zrnU8URxvg_p&cyt!&BL@3pumOpdzo#ZsXd^O@?bi;%%x_bfpzpG$39GmMIgRZj!WJ zQjL<^3;DMSc?zmbcPr<*oo8b*vKy$ml{`e&?PmJ;VedB_=YJKHEvC^NdE)nyQG##E zF(j5f>5;jr#>(AYX> z;WjOts14e}a(uTUS!2%Cb9$utV`k;?{DvU8l)M91O%eGBB)ZPg7{3y0%~OjdxGKm} z-F$otO3NxU=fO239+3uSz9dDzl8c0|7CY_ymb$X8g{iAm=IJvJi{8|*YIx3c7y|o@ zAsj;8zmi}7SUM-$_xH8~nMsTs5rqiGZ?r-x76?PGYW*&Vd?gD_(CHF|KaR))$o{X%%}@O!3bE(NS5A@ zU@&PLWr&p^{*c*Nd-?_&pwVHT+@+sWZ+rMh{L`X0Hn1}LllXw{sZ$MP_NS)8mWY24 z^0p5kfXMpb)MY{{YKRH&%CyzOd;mQp*WA!})q)Y?z@WlT+wvsstqmo;HI^Q8MV#cMhnV-CcOm z<@&juvg@Ud`tt+M#RT0}?>e>9WRDy!!n8|ZWo$V8fS zs2Ou-`TzV(Z~JQEE%#NRx^Qqw8d&aJ%l$SA&2+eUo;I`F&1o0YF+^vK%~1 zz}#HzoQ!@3*LtnXHPty4A+?x%Q#P>I++E@JJ+xM0pV!n=W?adkCV z{ut5h*`?D?|M4!qkWs&IHe$nX;&><@U4#2*-{={wjagP@c*j=~Wi^9=JC#&S*zDW# zM&TXU)33+1oc0M*ntn$!1zVl-R>EbvDa%TWH)4Hlo+2l~;*8hgI~yWZOSiK^*AQRI zup8V!TPy@83=!gHxDdWv>0d0+$x6-hM{>3bo)R-kH`h;py}^W!o)a+V=3uu>*}`&i z@T*_@Hd}o$jhn_q81k#FqU(@M-~s2npj%cfdFwswn!u(-!UJCkOk%_M6Gi1mU!YA4 zx!f?b0`3o(G@*r0bq7+PbRD{eUDVqbZ`AVLG9K&qYm)yuB;t8EXDDq3|4rTrhZ&o9 zdhwUY9(xfEP8{~&jkh1kAeUw6+`_|&nizM}Qmw3;zl`zU9K?}COYrED7^N*xC2^Io7kaqT1;g=(BGRoFTJ z6b_G-g1zrXz6th1M3KbQY(s@%TszVHsm@8v+YJl$brthciwm0kxK%&_ZsJWc!d%uN z>5av5ZojiFin4lO%CXi#lo60$OJ)CE{dBqhd@+T;1pLPR8=la`ldxsWAIVPIdF4%- zv3DIvmWOh;>r=evJQC4d&;0}=VP|pxMQjDyf(@jFPq4iEyE7Ck_kV#^;UjO0rLb%a z@vMT5o|^L;5pVZ*qovE%W$dIl$Xq@T{lvRYtfxXC;!lY;N#ZZn|Q}Ih10>8>Nc%e zLy76wS_EG>)TS0Gq`IO{KbG&eXe-}+b4*S)_e*lRN{LSAN(U0IJIbsTjQO!E9o(|-l?IeBH^*+r{Gqx4$`Y_Ysy6IAGHrAi6F9&_+Qhl}$fbMgUIsd}xrwM#tA%4ZEXT`fmZeTW5V=_3qG zyrZ*Rb%6Hp!&vJ*hkSp7J?Eu|(q`T(ez2uJML1}aPq5_0#jtLU>WDL;f*L%vaP38DCmrHrZ7@TFi z_t_R$DF`nl6Z{|Ky?Hp4@BcnrsU%6Vg^;pDwz8Eqlr2j_NR|?^XU`0#C|Yb;vW%VV zjC~tp2npGDV+`4iF$`uHhUcDozdzsa_xa=bJ-_34{`eiobN@9RnYr)lbzk>=ov-tC zzRv6OY_%q*iPLLqj9a{;e0*LZl(e8O+-nEBiuBK0h?X#o# zQ%yqg8Ts|tv*SHyd-TCR2~=x__LU8yPeXXs;XVJ+70(l9fE0SZ(Bg$KsguEXq}#-4 zLadNAPq=k8_0;TSL>jxyD9oUiQiZ}jG)EIVS7Y7NTMHm|djJ)8TBKncJGA|JK}4*g zq;S6RWvq$ByWE2^&&u;=|JW&%)EL$JBJLU$NcRl8X6i9iD1LOTp@@_AaZd*5GPI*C zhpP*t#-Eq`5qj5(*;9qylf^c12>}`t(=X1!+b<_7T=H|2~#aj92UdN|?7 zjWITM<_=}k+;mV4g$5}n$ngB5jk5kR=^nx2r%#}@VcEl}rOo@42{uKz~$ zacGJKse*~Gf@rT36lj|*x{w`li3HBp9KU_M;juuF^!Zrary*>@34*Ci@Wcmr?6W zH)>5%FAfIkhbi^H#dEqWnbazCEO>lbJ1Sena2H$*y3aT?R@67&(QT2?UTDZa9;q8BMuDhg#Tl>2P%f}W+Q+@2Eoe-%&e#wQLlEmkW%#ju+2Dg6F|J2ha zXnJ%TWjt>cgd4{BbOs4eS&{i9{FNp_~KB4%c!TK!0__JehvQ=nR~-|vP0zUZ=Z&j!xcMkzkM*VJlcjL zC5;+94Vojo3@`imMDu#CVrxShwX7e0oCuk&%GcTbVK~9@p+0Hw+v=LT25 z9Ce79TR5p{X7kM=t(cj=!00WM4BRT1dbW%UNaD}n+TKJ|`kp~DiuPZzaQ{hn*Q`s% za!{&Ry_@!(?(2ovQh3XWUxZ^JuciW~_OgEEH8%Mh`PeV)V|A~^D*B6WD=`_MSCa_0 z_hUx##J9+6$tAVu9gCDafv&M9D|Y*lqw@=U3bWDu*}OMNq@Fm5$$1t zM~SkzYgrbkg-qhuOd9iE-oY4_Di{n_h|Dsb={53s;v>>?**pE@eEiWBi?~s`L~fRv z^bd=h9^FHM!L+em^Qn2S^YhNVJbmfntpbDWH$Bm-*8aWMSH|)NHlIh$*SIVd7##Rk zoFbadoo1y`y_h)_z)d&~J)R(RvuTQZ_DuxbE#i3(C)>+10fQ!wL9r!ms~}G2^mb10 zBYjx5_SAXt3BAH!AcM2EE4@awE_rj^2Ih9&eF^+ywSSUFkvim>u*$av5ahk@{IGd! z*Px~f>8^iDVCSllX$*PI<};&Fh?D+|#$mRa0m|VywnG%@??1&PyH*f+V3eh^_8hfk zlm)!)H{-4Y!89`q_LJx>TA(y=>S7OLy4JHBU?2GUo>cVyj!R9bz4D@AOP~d}&s{3r zW1KupiPL+WEeg3v2fqUgWl#0yRbNw1>hPDLj!jcJ>&m~4%gUB7 z3i}bAxOw*xg(y9PNEQ332Zp|xiiP*@y!X8r1msvXv7br|mJ(K)Z60?5uyl-&pPSU~d*$!R8S>B@godO~Az=o;zFmua7b}reeCPzx*qTF=@QXdo|WQC250*spu8aMgCv^gLRi>kwM(K}NYhx|48m>+ORg z%J!=!XIBW5S^m-kfTsAyc_%OY$7T;}(Qw(CzW+}*!;)xL0yU>S7ae`fmfy6pvvlFz z_Tg$~NCkhm0p(ntl4!Qwx7>-tV#NBnEQ8E^E8q>cw;8rF~~t+$hooZM2@vLrCJaF(i%=7cf=@lry7w zN=*xH{YgqBjn#s066q87pQpYP78w;=EiJV9lWuVDo2nykmW70m!Uyz6DHFrmP zm&6KhjGq4)mpr{$kv|!x|EMDa8>Z)fUGC?q@fU>`$VY{-?LH58vo>H&Rbf|#&PV76 z0pvcx;7`wKY)fzZ(*^_X-z;2W53?21r-i=d7KRHrRXBf(b`zAh8OkM#rYpY|95bL0 zlQs@+xvCs!v!T*@YG6oYXRqJfR`>7;6>Bx`Qqzm;arrH$N~jML|GRhZ#`R1-C+m@N ztEFm{;xhy4g;T?!RHIr*3GGs`-^Y*j{>E{1vytn^E?`dkx$aNlD@$^ z>?MNLYTUoCwr{&5Oywlk5=)Gr@prJNISvv4WUsrCqW|Jk;piKM441PXU+5T%mzLf)Tfe<$1!#)=Z!J zwq8G4_=WH|f1vhDUMOadC|+|ZUrbc@XN@XOFuo4DmCrXymRoaLUu@sL5W!^@OAu1A za_a~wvCBZwT^c_F%}^d=oCAE#eBz7@j$g_n+QeY0l*$fjytjYe>Z;&g?l5|7mSiCV z%eKf>#J;Z7_b$*c`KoA>YoOfqeFS#?Iz49*LaE?^R!i;9lE051Tf zKJ9tnTdYoW*{7!*B_p11EQX5QE6HTup+216^xLAau zwJb)2`Or(=S2<73N)1BsAE25G4(I-MM|RB^eZ5aGpPL(D@|jasiwT?u99|?kG}2Gy zYE^wRT9s~Uy^EX-(GIF4Uf6L~kiwp#-Sw7Qqf_iHGGUT$0Nldh;z^$-!<2pRev8`C z$8ygF-D(WdFpRgHAil3*Hz9><>@(GH42R)p^N9(D>y_SItHi>l=~WAsrOy(CT@Bmg zcP4)Sq&_G#SNZu_{EP%3>6BYqCAaJvA$e2&kG}U@fFnPFSEj~AIHsHLqhA!&>pr#16@-y^`x8^UCjy2}v0rZtcd|eF>1=+l=w^ z)zGhqQj8^kjH6<4m4rA<3zr&LveO+P`GZ^?erZTv*E9Kf^}bE^@Nm)6nJ(wHD`_C+ zI6G{^&oMCPrB91D%&QQzgE1$VHOm&K{>ur%UGn zi?)-_}C&qh$l7yhoXJYY({NoA+nseM_@F7Aag7MIsRqlMlYjO1}n%fa?hy;_L{@z5LMzZGBF zkM;56jP^LRQP_HlRt?9ChCsWCYeYGM*VLXHC`h3CWp6^_9G5_MlR*WMhk7Rf<21T@ zgO7#&2Ks~kI>xjX0g0T(MpNg=DS5X516-mk{#|t7 zTVK+@_Vzw3SQnq4xE`vY>gHc4t-yWIE{!i1=7SDqHSR^$`RlTBu3rtCHZn{R`Vxoj zjWsORA&qG{I3)pC-ukQeSNl(B&B!A8lk#>ONnceaHg7SGhmRO#y}(FNin(U^|MoVS ziK6|x5^;(V@9}k#bHhBkThQvTlLoWeS1q#a;d(t4e!U9xq>4Phg5Yv{oS3!qdme>U zs)3Y`nDj)D`~xFecs5h2Pv{6g{=h4oTgq2L{dKV%5Uz9a(b3T=eTsTgtiDD=_3y6&u0K!P z7!r`Xk`~_a)-H*1CdA*x}N`$(fz_o%>pp2u7d?i$x8qB{MxIkCo&?}d1hT2DFGXo#$U)3q2Dxr zShiNtn{Mo=3JN5~p{O|r1kRH364$QRw0@M9MR3yYgd&s(D{Er6z*s%}%|J$xV ze@+FQ3{aN-^YIv95~SwYZofM*-4l~YYcmg^=!Cob(n`E~TO{>NRoA8NKL$TSf3k}& zIJcA!lf$CCEL$AiK>P_^y8m(YX7W2J53~Q$2YvVlDa$0N?c-@nh!d?l_nh}XAM4e; zlM)4-^n!uba?~Scxhs;npk5n z+}aiQ_P|w+VYR1-I}A9`^J7cdmwPpVbN2J|Z+ARY2slP>G`(W8@P4~V>d*LcAl##N z6BlKGRXXp{Z!jXcfC{{&6O=8he-`S0-iu(U3AuD@cn;pbQy!?Z)Uh^6X;Ha*;6dbh=kjMpfxgF5)$wN4A{=~y=1m$kmf4rn-Oh2h zZ4M)e!hfJHD2r|>TkuajG5`&^<}Zdb`>e%V6fjR4VvQuvm1c+CYUQ6mEo0v*|6q-ED$F>ttM|pu zdQneM;IAFO#9g%nVHw1nGr&2gCZ0d66GkloUc;w<$g51KMLbD8?O;?i$sOF+9nbZ` z=X|-sJmL>{y#jz&i3y3eB(;cSz{$K>{(^6%&Z$&um~F4-Tm7IRV#;3|<&NM~c)fSJ ze6cXmIVge<07{QbF1$r7vK@orUiRN$4srcBZ!hq*YxYg`(%HbW<6k zHybc4C-^6p;R1Y_D$;IuJ>%IX9>+u7)4n~b)O(D$Yc3A?MDZN0`HN2PYX3#2^5ZCp z&ufdt_j_`|%;+TN^sa|ZQ*phYC1hFTz9-$P&cDB)D~YyTYX0Lzyl)~IpSdz|Rt6_4 z^T6Cg;em14)&S~eM8<$}s&$ctUWJ*EAfBgEx9PX{VY0wx0{TCeBW8b@Xcx^KZuV^J zHYxuu@LN?{XDI6^(ZV>=;-!%-q=x{laqzW5y#R1(s)s^Ks@Is)qZ`A|#6;Nl&mkM$ zKI#C(B*nkX<<86?*B-~)2Ol51mz(dzkR9^U5OHyB7)Z@Oj(tyS=fSi{HKlRYKq|M! zasqYQMje82Txr-%8Osqh9PnWKnxM{0k0WjKeJC7#|hG}`^_4!(E*vmDTHgMN}5l}nkHzgEm z$Ioxi3EwIHRgyf$BxaQ66K*b4Yn=D^0gvR65bvqkRR`LejklfU71Y0-wlJ%%mA(4m zPE*Jj5?fzLfuhCRN&{J~yCZ(UJ_wVIK#dZn<8Jgc$BVU4+cv%_yj7q-M?jI;K4qub~4 zap8y{3cE<`Ev{nhc;1xhC-a>-bKo;r+0{os*ejp@Jxt&W3cqlN%rW_(C_C9aj)D3C`U7VmQ z`fWY}O(W{0)BN$-I-m7Ix`wDEFYC2bOz&bVX@%Xqo*$r&_dX9VPvzUEG#eHDfTPQD=PE z%5nr*1@J4{WuXxcHPgAWVD|Jbv%_Xw`D`*E?!YgIbAGeKLw!bxT>hN_)xoBZ3nFIiB1|vdDSgpjwgC0dM6R z`*`n7k8E5*{)C&MfKU@&ycd-byphDy#*KL^1y2)lJnHJxA|jTE!V~t{XpCoV|d}Rr`8R zV&xf|RHZwuxf)*}w<739XWa}Vl=L5O2^(^|Y(8^(sORCeYAaXZTcrOjDI{*QHomL> zFFGAL%GgNy_OdwbqQHL8iUax$FO`GUr;Wt3u&&l@Rk1YLw&YOFIRUXK3)fQT&4q?7 zey*ZH%FcKa$YHGDHmTW$8y>b|Uc4QUS6--t`$uPDwQ$+e$=Ym^B) z$`H53J(9Efau5s7CH=OUQk60lS)~l8Q4R(@wqxb?6JV!{U?+oe4K@_-Y=MQ+;O(Xm zdru|6xu~ZX!-LB6hIZDbzksB=I$0T9s@#&}ONjzKM^XFbL;(%xt={FBWz)*#rKQC- z+FZ`^{jckQ%;%+z6@=8z(`pHHW$!*n>f1B63V~2zDvmd zf{J*%Hz+JniQ3fn$0N#aI;F3T9rY`uT9Zd`u|B$-#$5^dqvwhh27GyG6j%p>2${rD zYN^=nEAMLhpI&ZF{U%Rww3tyD^NT7Z9RVCZDMfE(()#&U<;+WQobvN=f0mUA-6}>&9(1e1aq5gj&v`#&2Ph& z1aT^zd(Pv^u_+S&U`y=B?&Sl$GfUZ1x6`PQO7iQwcLGlR^sN7yLjp~NU$6h$#B|KY zR-zOe;uY|5)8SoWcTkkY?}WM+gb*=&_Boj$-0(*ukrtJj~=#9K-Hzq z>d!Ve&m?v9@J=2o) z!)lQ@-2B3cTP`H5RE+ESN~&P`*s*A!6gIRVSdHUcV5&0TAn4x}kof0^uh`7lsDAK` z6{#GG+bp#kYUea}y;>wMwuoq0=L{;f29e;rpUjdY16*4^QxLh}0Ul2(pzq zK6;Oa(4lVQlGDT#Wc_fD@ry#>~4Mx%3PnmJoeY&3X1qgQPz zDmm6gibEy@xdG8rgP-5}2!WQ1Ps*vsMA|F|Iz1YA(8uC5T;<~A_X#5iH~;pX<%r(j z&xwXU@&&z&laI2kQIUwg*BMCOZj2v!TL9fOx_Qg)bpYH*E7@2_&#>I>3%c{=zk(L; z$SysU#kEjw@!GY5Ua~+-^@CvCC|1;g-q@9ld0~j?PaXZfTuW=<^y%z~pifckf`gr@ zcfXO_UI?rPo#QDk=7##op-w1eHe1yl?R zEYlgjC4KF{#eP7oYdXA-m+;Kd29gctD`#@-2yu+!`pEz)9?X)h&QD>joej5ll<9n) zy4Hx|>z==%?{IxebxxVtq?9Mg1+)D+>6SuMaR@5Qeu%;I*9%0(DTUJuf)i|V0+R80 z$s@Us1u#igKpC)Lu%ffVrIQ%w=1^}{`luVIe=O_uJyR8wtrkce%U62wSC8T-KX=;u zzbR6@`_1osShIg`pNFXVo~r_KZj^p7J7ah;7dMon#4nFz$?YnR6pQ3MSly~ zw3+T;ok01D7}b}|A)mTql*@KB zAAfS;Cbl1{T|RAAB$~5-%LQ;|EF95(0gZ`|^?5U%d|9A$YdxSg>p<{$JbzVf`piG0 z#zr2yK#;J|n9%vW*8GxI_Wr|ZbE1aU85F7h?L}EVI}KSU&5&ZH7hS~xg!4h89KB5I z8I=dP$9KxExZbVNEvcDrlzK6&%z#o?>4j~3e{m|2tp;3a-W@ZG%5e?1^$eW5b6mL7 zK_NIhboc#7P4=<@x4(BeBYQu}hBgr$01&6jUSVF$;^q?H!w*bfF8x3w8E6G8LfMJ@#!nfXq?a;t6bw9L4fCIsm_-d z=P*%R#hz}J?l&Wl4u15>ObOHaNzgvVV&}b-KqA?AEEK`Jl{PT}w9j+@xTVa{+l$VZk1rfWDP z-E?AHyyh~+B+Ya@Liu6MDQybSfuylCR;Eof)W?(A+BC-QiEFIQ(8#tp8gsfngddP~ zC;yrgy!1EY=N#SA#FfXvrT~XAYfms-jpA+@b5rUhUU3a2!bRlD{snG+zi`9iAaw=t>&@$RLxUc20kkh`!}jwQ7#L*c0xl{iih8e zhJvo};ns^5dhak1xV+c~ZJ1>a@{a202WtdS4t9KS{rkwZzuJV`iweuSKvcKdF3HNb zfptE61$6r?X^TC+uk>9tp_8Z=l^|R>hgB9RTvRI10ycS&mFY(3-2=qr`Zg_&dEbeF zpyOOyL>qG4b>txyAYsQRXlszPfUL@fMoEPBy=Ri7Yx)md%gn*qPCwrL=~9yHrw7{Q zQJ$bNIl;pfLuLQw9}K%y1w9dU7ca$IM>+m8+)HZ-dsL?IQ{ER{)`nCqzghY}EtfEd zUReKc*qUM`;YGo?O)KAb|Dj@<*~aIiF(2l1d(KD%t?K&QSg_?n{%EB$qyb}dk)=WO zj)wfT3-%v+4wsvfjkjxtIx+9d>FC2(M2kDF+7t@g791n(pl7~lY zQYE0J^6pN4_jEP?tjUmpJyuEI<6J#hi?2BM&muPssv0^t3|RvJyJ`bcpu_4VmEWHn zo*Zg*qW(_Bdw{+4)zG1oQt`3J^&#YMG3UGFZDWm`4Iw(Ck`Wm~CxumG8@ z7O)@NjK0sS(Si%pG|OR!r(9C*hlP!lhi<-L+32rtVnyFC50g33p8t25m%IvC(|ntX zzl9LmK6XwGazb`uLd3k6e8n);-E3RUD=>>jY|bliE?4{?_EU2G-Mh?(VtWIk=xg(* zmfs`SwKoJHC*rhT<^ft3hlbCKCw`v6{Q%FBsiFj`e>kA*{(Jo2u;Zl$FkW6~DE&~J za=3K#i6?NmY9lBG?yjOGp?qXZUVMPeK=}neeN9azsfW#5ri-C&tIVfBzN?zsc|y}! zN5oXfH%-Q->+^5U+Aj zyZt*se8|uKsFQ9|kIIqx>9BWPHlHY6VTQ+FgJx@8zHsSmC`^MPjLY^|G0mA>?d zT5uGc_t(1R3*5LGX8%p%SR7zmadTw>JFW)PA?yq(z#A~6=?2C#c!?L;`F%l8@)saJ zTczb9TZkgrSl0ZGTGNmIS<7k2erx(_3NS&!j{~+yg0lF}43h_wFrJvx&c1HDV$Z;> z8*Q{>11JSwu-p~!&*YShG1IBLFjtgRGS_!juAdD)jEGA6`dzE^nNYvu+WkCiETC;sPe_dMC=gg|3Q%_ZZP%r^b@;a`1 z^s3M;h_Yt--u44;JUVI!&%6KSLh(eP(PLoSm3Qth>`uQ@zu1Z|7O&~$eq*DbO4w#vWA zm){vCsB~grh6zc)zBs#wYB%$1s?1h_!oZtr{Ne8{{Op&9Ga) pXcsdFC05Lf*GX z%<^BrA^HKE_~rMSL8^Xhi5HdSMBUe3$X$l7q>hZ8#ovTg&LU&@#te#^K2E2eJFkk- zcP~9WhGzS-iN%dsrI1C8^^xuv0Y_2Z-;97nrj%jtSdEfOLlfiARpgPJ!R8m+; ztsZ+oYHe%1F*=A+IDx!}pH_8;S*kjWN6gNH9CpNn7If1<3ylVv1_a`8sEZv5fr+rD|WJVmeXM4R(pM><&@8`mv*hhE; zTfwI(x*PKQ(`1w1GB1ZBybqp~i>k`wF!KkTW>iKnoR;sIOTgM#dKOIY=oj5+%Iq^!_nVnXYLcou4YYy?Wif4BI}PU=f(B5u<8t4SF1L=REqJunY1F550>*Cqp;% z?T+|s{PgwS;|9L1Ma}UL)Q;RBoN1xl@0|kU^EWR}0Y)ix=ETNId0_LL3fy}i5~fNq zcfULFGK?Rmy1xrLJ&_h6a0c+Y60&`wqUyoTGDIwM-AnEV$=pJmcKNZo>-l>%c0e4d z>Dv_g9@$q#Ik^%CiHz zk;IX2gJ#e_w{}{+*%Hz1@($n>YnAk-MNS8mVhg_k=#4hdl{&w8Q^sQ5BDm4%L|5La zd4$k9(#=0+T|#30wSX(5M~9yo?#{=$3eAs22I(t)ZB*q746`=+4Pi@@YPnuIQi)5R z@n+ky<&o~~NC5t5h#;hd@egrQ35=h?vok9;8Gv`THfRx%gmpb{!RWCnC7}X{ip^H< z26E44k-lImp4qz7k_+*VfM`Bd)Y1pk=@ZN z4_%yw2hs;WTa&@^`@mi6L!H;+AnzgSJnDqcuSh>UVy2(^xMJ9n zC@t^yQ}M+g6K^#$3^-JE#lC{XUY3X@UdWO6ZF*L{#BYpgeeftf7n2zeXkd1C;5VKk zm65n)o~;ZI<)-jectGxgfoBtAC1u+r7-=0|_)D$95v*@^l$xisCnz^CkyvnBIrG=W zLPa$&kz`6BX|uZM=wLx<8Ham;3rD#1N`4!!Es!G@A#bXn*FR|gmUb^pbp&{QH-Dg` zyS8-N`9Ra}C{90!=t}N-+pw{nPhIr6%=>?Euqg^7&A)u?#!p3iA}0&noDQtV>^_S* zq04To2iu0ey5aUAps-?Po~aA7=OYtYCx)iTv|}<>2Sp5mDSDvBs;+C9{~086T$Qq3 zlez-Z5xlYGZZ6Lx?wLv`lZ*lQ7hwQ9df2HZ|b((dARIQ(e%bk(rG(dM9tG*-HkWu zRUe{<&&Y)zkba=cPS!A8Uhh#XdmaML&Z*-g6m;drYF>x5NyK@0lg5mfOotgi;qMRp zj{(n;XfbKA4+9Mz4w9w!&f^YpaV@-j<+df>mWfhw^+RuUqx}1C^IB`{Sk97my4^ud zjBv^}bOj@!VPemR1|Eid2w)b{Si6wBKAa7e>*t6tlvPU-F!p~(yj+gF#{KM?aMW2O zjM|jW1%4jk-Qt)T`MfC#Xx-&B{`0B@tO9S5rg!S3&6oqcNxc!g38!6^j}(rg$`Gon z%4{R4v=pW5<5knS5RzNxQWK(bUdh4+A!|ul`AHu zyjumgDK73f61WBT^iUy4dyQ90Og~%`rolH|CA=el9}1h8gy;r z*|S9|MS<8sM^%`gdcQ_h7p>rW}bewb}-$Nt6f zxAbb$svw_AvM~QIFt(J@=j8#HQo)Qw$oE(--vO>5%}g(vWk}e#uopz>>&kRlTRgft zY}$NIIbs%U@q5d{;xU~5xZrgat`oSa9IYBuIX1VKA!D~Ks!ZP;v_U?9;~e|A|U25TI5X-YFI(yA0cy={nV73GO-X z06i>q`AWyk1n_$A)1CE=j2%taol^Eb3@V6(i72Up?Y*%_(`r$0s&%$&L(CCksD#(N zxpO<MvOz}_LJ;tLa=pV3D64ZdmB;|M-x#vJ!5}vXMG1AT9&BRt zMkB?xC(rH$S6U-C1wnp;7Pq64ru5yXU#stGSb~@rn(`W+odemlKLLR?XB2#K8Gf?G z-zukJCQVXU!71w)voth(86k#U9x-X@xWHrGYlgsZ8o712-yj%O+t1~! zqGfP0$VY}Dn%`&Yj@48XJ~_uv4wh|7vy+sgDEJ2wTuCL`cn8pg4y5@co_4tuOO`I- z9Xbei1dsm+>aqXqNghj6g0CRNoL6Y&PVq*iDB84!cW>%FN)La$M*}De?W1vFa{1O% z)qjJF-cNA^!nIi)k-%yrM#IR9p#4_Z3T9Vb52L7kJ9%jam!AVo_NOat^KS28D5m7<&OUm7W95o1VbH?Rk^T`0e{i&Hz#Ej_bg=BK zMx`R(Jd>|%flgpupbqpf%>DPN zF^!yhl;Ea^mT072holz4$8h*FT;kqfHOSXBx|Bq%#02vFQS`hy&S&FQ3Fo_m-}c5Vh+x-}mV!uCG?6vw`-GdpDNHR^zz0Cp|Y2?`<*@cte+JoK@~x zTUURb9q6dy6}ja$_QN#& zfJG7Rqg|6pnJk+gTRAt9Te0tef}E8B<7@UAFbt8_RO>$ocHd=hF5e3|ZTk!ujz{C( zCHthW-*58T2cG|47Y7lpiuc&jpE+#77b_84e6~wTHxAV3$EV;XTeIQhb^OQqZC{wqx$#PEvtn$I6f*VEX%lk z*h#9E8&FX2jbXhE621|o2|i;NOxSPoV}=%C0mbY{UxZwL>(i#NF&Fyy_9L300NhD) z@HlBL$8Xe10?o+G2s3-&jz1HgGl>9R+VJ4sGOn=B9q+~t!t%O=t!HCQ6)kR#OERD6 zvKG2C4SOg8UrrsFeKst()5%}Jo4(HQMH{@lNb0HtvmFTJ?6{{nv=#Xj3P6?lr!aof8Mi#|7^{SEf}|MI@zCmCg#Lh=d!}>e|47is;!~No zCwJCsLdGbhQP@S;N2puQT#T@gqRAe3qG^5S9z2L2^GvN)QM=drM-Cz>1+R_m$WcUm zK%~JY*Sk=35{!ZSq^N@;Hu#8aC zZYWo=KUrwI`e{UkU+P5yW9*h@z|v`Q!@N@bSWQ9RLvxhd+H~D}w`g-WkDkT?-Cc!GJ2wm85Xt{h-N*(z{RX6@i zD(u-TUdmNPRenK^Rq4K5qo7U&L zQYEGWdrLTKWe@r%pt(D-e*?OoU0YLpae8a4jf?aqqH_ncP5uR8Kc+=TXFF%ayMLB4 z%O13L+R1bMI#fm8DqCfL(qenK1y8>hws;z!t46>kc5O0DOWcRo3}AVmigTZEb*`(%3;Btp3LYBX-nsd+}`fQ+fH;Z1beYetC^IQ=tl- zhuE(|D~E*Nk*0&h3w{A%BWbZ8Zh+(J$lFOlnoj}6+{_{8FE>TG{)vV4CrYSNI{gl5 zl||LLfPkbrva}dva4S_VpP1c>UOW$?ggPrAUr)miLbiWIDt$l0mA<~yQl-3y>#$@r zG2U76BaVYE7=ZU#Md__;bC<>>70W)2ytTISlTNO-4W6#sJPit0cuciC#T1SCmf@K- z^ESLu7E4bm(jtDFq4!LtDWtQYp$X)=%(tQ5l-@b{rR1N3?f@GGLgQa*Ou^dYG2Z*y zFrkJW+rlwITPl*`8uV`ZnqR1hXY2L8$Jrg}3r>1|Kb9p

YD07q;Vq%06^1}u0dkRrwr}2@<89PF*JPB~1;-=WdkMA)KB9s$Hg^-k= z*i0lhn!Me|-ie`cTp9v$$}E&}*(WQ^<&>J)iR$|O`Ab0hwTL(L^X}pV-6aG?yMgEW z7!NV)GWRc*)%zp18@KYxx=X1K7&<5htGxmKz1m?6TllC5VXl$GZCO4_`$^U|OS9U* z8I?8D$ZCv1hOkn{mVp{fFT?0=)cTs{RP_CJUfuLU7kU481iv84$7^m6{2+Rpa_<@d#>EB_ z%Og=j(8~W$q)rNy7{ucHd)+5_uDTs2=KS{{>GhGG)$&@x3_c98rC9%PCudYi6`~&N zVu!r~Yqj zfs8-f<^>)b2h6m9J@58FgGV%C2Ejyo?r zz+*UQuD9bvOY(X)oaE}C=!On>$XRuVTrf-PI`yFvq*c2W_MW1%NNHHHy5QIE+}+;z z3m)@1arG+a$nimP2-~>EEAol0=1&ak?dF@bLwO=leV)t%CZ@gH4g1c8QX(@Uzrlw8 zBRrP7l$Kg5BlX|O8uvrr;`mZ}uSG$>-HMf)jGRkz-T6&74w{1Y{)WnkWCc&*Ic#Rw zUQK35t&V(w^Ybj|csxvC|JzCgyv* zB9mzv9PhvUS+IJw3v>CT(pmzQXG|K;{$Y(S>q+uDAc~NpvLVNVC^YNHT1zi6ZtLpE z>9p-2w9!FJ_zfVA=6h(qcJ$tCkdorYTdrj=wx-U&E;>#d%lP`gxQ@QPCfp=r|AdcQ zAVF^Gf5~;IXr`by$X;z6wU!`!dsWMI;GM}a;F-9CwFcyFu+$a;5V$snXmNN$MHZFyA4!YjX0u7*Z=7&bi5Xk|hvAr*HxhTe42&;BiU_EmO1)e@82Ba$oi?Bd_ z&f4)!4S;y?UJ%NA1o zKz&sMZr1?m|J}7dsg5u9Fa>yvue%4MUq{x}Jm|XmLVwCbUht!E<32`9p;`gY4b1XH zm0qKHlS^65y!*-k+TR(}80-zygEx1u4q7;K+9P9n!`HX#90QX_qa1;?YNMG}?mnY1 zJ~90ADz9BiB+N1J*5m?diz@{m;->QBdEW8-JoV+Lyi{fY!IM1duoic=RNV#?bn27> zRvRS&V=gm13uAxwBxvYBdpS~n_I|b8UiFLku}tqH3bByugzG>c*?Qwr=SQr2Vw?(4 z+AkGSHQb2>c3)47>)1Y!adppK0!Qmp62EpN_Urqjk#AKGFzU6JEgG;UJ9U8?OQ869 z{Fgr<;oPw&|F3Bl4;upzP{AyTmgpz$aJgss2_fE(BzEGq7bE1}xevepTy0Lf{Fc>u z1;_msKMv3m6J=j}K}h@E-eTx0h@*}FxLUl$=s;0XaFnM0_^g)Mq3hW6U6s0z7H>RA z`XKjxHjpijRspFanJdSl&FvS-UW@;Vr>`m@CYiXH^c|xyQMF z;|0xYMK!)L;JM<8u|1OU#j~ zWlx>o&lpGd?zH6nfy4mD2Eg6q9*Rqpgen>n9@COkLI&$X$|L9WKThhy&@*CW`^5}m{`)-t zzu0^4pr+ct-52#yR6r0!svt@S=|yT3q)YG8doO~3v;?IJBGNkn>Am-+ARxW>5~-m_ zYDhxhtnfU~`+Mii>@$1L+5eo``~Jg;%$k+8zPZcyF4yOZx}TMPxOR=)b8tJspIBc^ zXcTEV{+ZMnM&Z?}h(^jI(Zfl`&})wqfPHT{VsNO6I0|_@M&=>v(XjlNgEREK)~}vs zryVmE+oRfH>ZRTE9qK*~%{fz*J>%i-)uQ;Pl#y zyVEuSKmXq@)aU`eP=E4n*n0kCU=X@)8s(=r%u0!`McD7s;*Ur28%;Pu1}r7v%z)@H zKSg|YE#RBeF4&L2xqzspQ(6h+JSZ<848P1Z&_n{^Q1-G$n6grQ(h+W%*dlzr;R5d`YYZe|rnUuG zv)Dd5Vbsi;7WWnlBXRCymMJwRsj0?CgPP3_Wdw#-e`?Pz^nGjjEj zq{sOA>lL9#FU&>&gPApZq1oEBulIUC=Sj}qtcZfR9tyf$P948ZoAKvLl(;_3Lmmet?Yk_B39{($~4eQk7*&cuu!iN4tQ(`d3bM;rpxhD zij(J{{2fH410_M5W#h49p+eo_Hy^8nJpRSu^*&_)WYLP)1Blr!lij7_fpIy3&l}KG z!vTZ8PrU}Vj6-wKexT02G4_Zpf&c}Kf}7+Qnpo^32Z>rpt&Aop8$f;jg`d+ds|oeL z2&^u7L6qMq02XE>se3+0^Ma%dL=f^L(mXset;!)z)ni;wEpM@G-0|^XG@CkbOrGtj zbI+_!Wfry-E0D5NI@b_y^EKuj3BE4q_RMGUz@4uJ!$^LKc&nP%b3i}#b^-VPG-{~^ z0w?_Jj!6wy&^qtf#b2*+D!W$id*Eo2Xis!_%`}OkE(t-mjxp`q!*+qc1@Xrxajz`kbMIxU>mTD z4LzW~wfTwov(`tn{Uyu=Cwc{Rc~;Y`iNHX<*CPNSnVWyYs0H?7zl~|8@&WLL!&H-i z`r?mWI(HFvg9;u57TvXsUCVA6h zB1h!m{DKD~aO8c6$59^sq6S;t@4)yaF-$d)uZ^ z0f6LPywF=VDWZf^l>CE^yOGxSdfc&g{aih65$^!<2Q%DCIrSgXUEiRy)O_UkCx-hi zvd#Kgvs<526Rtpcjlh&j`hD{*u{Dma@l7oDgj(hmc5PNU%KgK=oa%`8i%*pF@tf9> z?b7v<%1B|Q+&pES(itkcC6PLv*f;)Xfn-{?KZ!+jWoO7Ki1VG_C|AuWqBQ8!7jPQY@;jku6G@@aY6` zGgSnwP;6Ypy5Sw1B39NlF4!CEwN;Nle#G?3=gVJ= zw!^?y{>PrV8wy{t$-b}3iZK?bR_~;$wqqGdQHysAsmzhZj_LSWrJ@Tey_x0Q3PTi& zJ5CSkQAb;?u=e#CSf-AQWK$=}ymPbq=J~vc4FCfyJKu7O85T94ySY>GD=>9Q{(7&? zEVGpdy@$kB&q0hk0^ibsn^y&Ub00s(8; zcvbi06DQc=OX&-TpMU0Gc+L}4UP{Yn|Awym?=Z5ticv(GR~YN}6f2)3yBb%ciTNCe znc+%d$Fy6DV;S*I{2R@;vcz_uuaKwo#Rd7r5f$T01|;TORdV1aoQKWp3(<_b6mq;t zaC6O-TY4LK<3}>Hc|`J27OF^>o9sf4RWvdvBKPYZ;KLsMO;^{mbB0}nTm*a3$)^X8 zB<)(e{1PkZ{Z|)>3-Z#GEAJC~q?}x&pQBx5fyI+@+y?VW3pPxM=NOaxtEwRefiNwH zH|>Q20~__af~&t!ZgYDcyu88)3<=)J*T0nd?g9%qeBO5|{i6n)KynE)fmeLc9?;|yU(tq+u31p1WhgaMUqmd;3FuDZ%e@sNx9 zoHbP|nmXo-Q4CrOEE`i_+;d7Mhgx)e5V__^cdY(T@rqv|4{c3yXUSi0t8EaKd@>y#M{yw=)Zv@ zBgg0Db^VBPSFAvD#_h8`B}%Ns;cZNo=^Kzz7!ojLbE?~4IA_EJTYn`l^!o7jsq6Ob z^k1A$dQaUc?>jA`1?FK4mr|L%#~({eXk@^-S|6yL4rWs0drAOqnVhe|6RAgTUO_U& zoI^07;>*X?r;l@=n@Hur<#l6DOB_>qVYL;(a+c91gU%3KHqTqk?{jS0J#y5z%pz*pX{Dk^d8jXhmfNXWEpH^XzVBqy)GNcrglPKuwF`-fb z{UwsEUsN-9-(6O)4>4M%+KGL%O>3xJL{MihUtQ&#LqUTGF%d;XV?U5{J6o^wIs(;x zbg9N_hI>zia7m+VnP=cUZ{enuO-nwCbz_@)3Xd&KL!GZ_ciONV1j{Wb#dF%Lr1@Yqw8ODH(4OHgWCGyA?g#Smy=2 zTD3Pc%cw>|G4)~j&dH>G!~aCleZpa;;xyB^Q?{gj6G3{~*>mYHu(3qES!NcCYoNaW zHqa)Zfqo}E0yCF!e))+@8yY<^3hyPi;R>EW18r>&`L70QcKNZ#1^P6O<;4Y(b;Dg^ zN_8X?F)YVnKDmj54q642((dP1YVRm{)RESlPbX>tFpN`&P~-z*oAqP2MemqUZFRYT zlPwd|H0^N%96@ncTqIx*fs@zH5XZ*{emF1rP+zc7T2oeCRM(8%55FMv<-2AuX(I7c zrTu*9lyDnp>!A&Ot;~?DLc7bK%Btgzxe5tl-73Gz#ZkEwG|20+I6>Z$;u+_nz6{LM zIquA)6zBxzz^{+8Tet2?N~z6BhNQugreBsTWc>BOQU|byJ2eHj1<4Qp;2A%4^lam1 z-e;wmubX|Tz4S9_Du4E5Fn=CQszKN9`n}tz9uSTY5eUr0uxLNuTED*1z1~x@G0CooAIIf?<=1ITI(fm@y?UFgoLM2_WkkebM_>Nig`P+xM4goYwbAN)< z3rgB`8tZa%=;w0nC!KB@P#-ofn`q5j4 zoz2is+z}kJxN7e_298OKLVL#;L6wtt%K_ah7%PA<3Hmb+XGHn_BrF1dyak3xDyGA$ zIPPVDR7)`$Acw`oa)BGe{>Tgu(0o6AwN(x0#>@RdAZ>JHeU~*KwxBz2_t%95@U%1? zI8_C`5vb!-J3^0%6J((Q2qfY#J4Pjd>vO|G&vl=6#Y){_0-Kayq7jcgjg5`+X1hAQ zQPLr^<)J{gd6rudJL!Ehl(O1{faY&m@IbTU61eGs@^{v7|mczv?eveAMAqs zM^J;_szMB1w&8etXklmK5O@SXH7lA49GY`JcAB>4XJVQ5IAdFPjH?xWOl1iBJRg<( z=dD(2O8^mU>iRSxnAbMT@QPaKMHJo!9kqDh_SdG7OSM)(?RC`5vb~x};{H_n8igko zQ~zx~YuVWiz&Wm+Z#z$Wqte4wvH)ge54|~}EzP2v-lA8@$5Gk?Q3rnjL;bFQR3Mzb z^J(c|Y`Sz+a)G#@>{5bf)+9?jdYb;|Jg=sHxc*MBlC8^)R_OBO##`>*#vxk-BA^`p zvT`r9+&BFw`ttJ{`BLcGw2fvZhUPIX_wu>&d#u2o3FWj+Rl6LvoPP8-(3}+I^}cnq z|Gt*Q>jd;9*Gvn|g&B%y9^-|5#|KOn9||6G zNiIPd;5#k5X~L-?)j=A3>s~9*-#1}%quZmaZNtZtx3Vd=Jh5B0#0v<%hK-N`zRm5- z8!@yiyhVnO_X;;r0^xB!D`L8plIt7Pg7=$rDAhBfh<{N=4{)jG2fkeJpJf+do#X%G z8i%PCpq3XSUa2ljNNfQS=I&Rkrvgm-itp=%)?)a&hfCKhlIGx#hrAZi%g_%kYV(~9 zqookamX~QJc+!H*5a1lRanc5UfbIFy`S}wnUNYA8zgH64)Hsh1xm9+Fk8iv190{5u z@f&&{wJoPbS-9Oun)v1b^_PUjP7o9DaZa4_ILAzY2-Tp&<=3c(P9V!~Hcw(p&MnT6 zbH|8az^3XepfWR(ca=)+7o+&y^7WrvKW+Q*I=*oI-E&|$0$A|)@B>v~Hd;F@2}LMu z;|otY7B9gMp(T6@*;CZ}~E?RkjO*}q8_&YWKD)Raz^H04^ir>M;7hQG} z!bvynpFjG-93O-<{pu>Yt>?H`4TJlBME@Z2pl2$8P-cRmCV_!FkeXm)5JBttqdW*V zU{Vyox{AdYg9Z^Ou*8`217a7qfYbcCqB;g;1^&aPQL#>_R}3zE zP`^lJe!y~Sj){9VQ80=6tXyi37;ih<(I>#vefC`#i%pZ_1y}IKIX(eZ!;GpId!|AzK)K&Z z5dJH@)v=1&sxOxb7VA0hUXKFMdmdA?6$PSuaA7gy&tS}PPTtjYUv#k!s>I>U6Y472 z!6COxWqFY=OfohZ&5 z^+>j-|NMMA+!?B{HElD7<`svLDXD>Yg(nu>Go0#G_>Fxw&xx?rZ2<5BmTh4MHMvu!duvb5_S=F|%SCFu%?L8~^XY+L3(R6*r-vMN z5wYen=d4w@tZmY)owucxQ3URE5H5^UZfIBNABM25pm+HjmjE#!$q`bDBo;!sM`rqi zBXIYa^^Zj!kq$e*a`j|_`;YtA z<&(wN#U9z$i=*!L&wu0d;13&T;^j0jlxfgjG*LW%E@t{VJ`F#k=o<*MaIczRRZ<*yIS)i zwhklwKc!3nvSw_cK5;a-5V>NQu#@{h6^F;zc4b*N=GYpJh3e$68)(GD}9 zQD_&d%`x__8+&8c4g<{LJBT)Z5BFqBb&4ni9dmV}ZG@i@$@mUcu>JLn-R9#~Ak1WZ z2biy`x?y=m=m+O$eV9=u^A@~iTpm+PpS=Tt`@ZYr z55g#Pt~z+O!}$v7V^4?c`maAMRs1qm$i`kjH{o30TSj5a9-o)ov_?1T3c(^qpF!kb zrJsE+ufNj=%TlMSK9{zI;t?46Ct!OxC+D5qF6ZOf>eE>HA!HlB{w7-{f|iELS$Crf z#8Y;RXO6)iELfU$I}nCJOwk8%m}?9W(hUkzB(XQZ%lhp2Kz`(P`W$1S4g41Wb81GB zy~AqIEuHv0)H}KEMt%DSYdso?v-GZ>om{A=YrR!NGLNZ;FMju3Qog_MXNHjsT+My# z0Np*WB8LQ24Gj}5(+vE8M&%7`!+y-UH)D2MhEZKD_b)uae=rX3u?iy>v4)VE;hx)7 zr_5h7G|iaTRRMAhsQs3*HgFQy0uTqJ%lew;rJ07&*BpEl6r3&32!SV727|G@He-N@ zL3c>#jg^A2Zr;jqo~`1carwh3R4%ZIF$W$fzw}vuOSlZykoE8z+(#PDomk9V%zDm$ zU}8uUves_uiE>iqL%)`-ZNEU;#zQI8S;{VqT(bLM_YE@sLdi`?UVBK0T>0S)?tqLe z94Ubh`FAODW@1Idy^l(#(JHWU{CG|s(PnOBI7mvkPU&Xx@{13B;dhY$6y z2%6^~cHR;(PQHz!=bo~D`p{zl@b*C6=)WHGtSAWlaxZ2P&oh~pTjXs!z>&hKxxIeD zvw36D$i^R)vb+uSyRzB91o{s=5}D}#c!Rvp1qj*LMLayAMxw{BJ78vs5r^Q9&9UeiKDv@+~Ropi_5f6L3y; z8-}+CMm^!CeOx?!5f&d2Isj%frk7q%VM0VZN0vT&uxUN2H4K@h5)Wfo6lyk$dNaX$ z27Yb4b?T4qQEK<^cGC2N{Dx;&CW&gd41>Kb+t<(zHn|JlcRRSvZopu_EVwl42E+E) zhMy@c%?!(L)(e074=s~)9$f$=&0y@$J&Og5^y4^@kNj2vk48iXi@=k@v&K&jca#<| z7VcPu7wxS~jmDM1F|Bx%1fJ^iC@9S@H@Ua3Za#j;`Q=E-#Xw;`$_*NS+&9kq9iuS5 zTI`tJaj-X5O21y`zuzyGUmA$YEGOa6%x-^i#AQ`P?W2#txi-+K1ae|tI#g_3+Hl}xT7QdBb| zP#~^MPBZj|cdU>SXnBn{!GA}n*|yGx=DS)72YCU{M zvaUj?XbKe5?qcIe9{fQrlXBcNpwvd}E{hj=+OU#@QIYaOFb0~aVQ@a6oaqIMc z^_=>KsgJ)AO0IkT zl1x2&zXd3-@)oAUaH}vHpl(3hqtM6c3Q(#<=N925WHp7FFZFW90Mv8cNI7WZ^_)S> zj`q+jxEJNS`I_bkAvSZ`NPqmAt-tN}5clAYb_XtC7vK`7Ui-;~i zfb|u!XEMVD>uv~${0e$e+a3*F?H^w4f6CLm#NK~j^Ww|Q;qg~kg}?R810$RFUvmvR z@lDlD;n7DiX|IRacS=R$t?zkelyq`&Ug_sQ4EMcU=4a*$4yi5@F_>LaRkE=u(6YJV z=4Ah%nPW>#I(bi&Jipb}6+k`$9&Q**Al5RyYb*DkYTK%%MlSuyF#lcBXAF1^@Kvx^ z-G+PwRTxjq);Z~Hbqilpf8B?u3uPZf_k8b#deIs6mN!9z+SOn+m!TXI%GL4A`umj1 zT%cw%7xh73(igj$Qfc|GX8)dO&QW7J_a=$~>cY|ip9^$DwU+W2W!qhG5!eFyRhtxl ziQn4JJWbzSgJo&F*vyF9*i&vN1cWjyV#Yr3=}e|V)oL^^2o|dNW&`gu%%I;h1DK^G zKAY&RqS1Ol=j-4JPS!u0rq{II8$9=^9ndcbQ4TtUa;=r2Ix`ZU&bNK)atCjjl*iQ2 zFYZ7R!QM@12q<|C_bW8M-tNi45;#S=SQvh1?!6F&xu{)`w26~4PtWJ9BSu(N56`Xl z_|J~rw@$8`R=VZ|KHur@buk1i*J5Yd!x`|b&KMGbl!)cJG|#K{UL;Y{ z1G2m=v#6vJp+bZ7{ipWsb}kddzJi?*rFTwX@FR7Tk7&f7jDbo0r!SpGFvSq;TL9Wx zcezvs@_}cY`MS%Jy=rfA67@y}qtzdAuO2y!yx!(F!!kaH;dd8EJV+nqOxMj^C2ed6 zoCN(NR?*okX1=lUCO)l)&o!YP$d5yDf8}o-0Y3u4`MKizR#HH0gS9ofBumK8Mj&IE zd+IS%SWGKdDf-~{33m;otF}}#yWTl=xBMk4vTWE0J#El=2%L`<01apQ zn+FkbGDuzj%;2f6fxV`evKFDp+_9ZNJoDnSyMjAr!Vmh#lpZRN4@z@xyb${7? zo1#Vo$@K~qGMfGL8ugp1the(JS zF!g(X{OjU*(v~V9f=}u^!GC;bLAB$#H0CcgQc_dGf;?7XCFE`IL~4k!|KBu&g6Y5K zrCX1Uxlf4kMhJih8Muv+HcVk~<&CkfDcVRxC+ zV9c7tNE!4SysP4a%&<9J2{2T{Ql2ZG>DbsQ-fg@mW{JTyT0AJFa7_)@h9 z$0CaSE@XF_f`3Aa#e<68Y1Rky1Z{w$yH%_IM@u~e5|fA8`CTpjdBtm9Y_kB=xQ$|) z|BZIahGZ&`7Mb1r@@_uQy7t8^faQ`K`5_=;62gFp!@rg~s;CCq@6vj$TJlPHJV03u z){ld9RJ?dLnEDMMbA*h)fI)HU>(#v`x{^bp}8pVFwU-e^?|&* zrQ$?Qs+NW!hh|sEDe^S7sx|UgLp;6!RhqER-~J(+5k+0MEG!#@tqbvy>Sl$)1e=QG8@gOFh)s?lsT$=`E z?^hf?O0qmD^lfQ^6ZUK7Q*lcOcogUpmP?EkXmn!Gp2e@n3&pjuWbKPN8VRucmvM6E z_W<6QgihIzZAzHBFemi9;Z(P5A#?w8vCyneAbYN;zGtM{2R~qwFm@-ETTnamP5 z_7J3*Clxsv`aTVHO3#&V9-hJl&KLYr?d;E-Wd!wWym3Nef4NKk9KWW%98DLA-WqxC zGSIhbf*2dREwGFbq$gRoi(}b35%2t1rQ#e>IX%hZ zyaGj&#c9~S@8_O9juKhFedo#?%~8F2%ahW&YDrn2UE14I+BO)%W>Uxb&o+2++t;gY zcNwQ*g=Y%it@W8zQKq3EotgnO8BH+bRtcXUzRaMJ?%%B5;%2RoJR1&`(E9RmtDF;- zzG{_3vhR0usvqkDyX=js&K1S~fXALIQ|TZ$Z%;cPw-i)@{hRjk7o9`KbzYjlKb8xYaDvVgE>|2ObUO z_xpM;`0nY8f0%olMgpQ~IT)Y$pg;BDrG8A$L>8CQpl0QP%6P}U(%=w}U}i^xPWoWG z+FF}>2b;RCMZ2RBm6O{i3AgQnEbT5{ra+!xi`@777krKPCE!Ep$vvlD-&BK!(|q9z z?!&Fa67UR)?OKj#1`y|B$@L8Z#kk=?jYXUm%1UsuhYZu^l z?R6sCK6eVQ@;;K(5|0r8<}AP&5!}dL>vJQ4hn=HtG(niWA47xqAh4<4ETk%fpv6b3 z4X$;V`y3uAyQMuGET*22Gjq)tU&|-&KetS;&?2(iXWS0YC`-UZ@%*CSIn&vCdui+? z>QDV#`Aw4hr7=9>$oXl+NVRfIQpPdz$hA44h!EGAaB^3d)JLqCjh6ppTPlHXt8{e6 zQ8_Wmqbj=T@J@*JWOS$;iG42~Z12JdG{or=b7AK3tVq{&SJ{%x&5Gj+V}d?KyGoUz zOzFu6d_9`JbFzxQBy$aI-4DW^9_&TJ`TK!~nbQMpq}I_;Lb2D{hT5n!t&8F@leBVm zIfG9;1H*A}hXi43bR?tks=U%ZL(iDRrq)Nuk@&7)2x^^}K1Iz(gP#raZ9%;9ly%Cr zl#`z7Ue@PM8cHoY)<*C0?fc)@UFs%~!c+Yki}PV`A;N8;L@t@`)V-~2V?57??GE_~ zw$BY;a%{}|HKfnj_WMFT27D9kko4AHK~KAg)$@C>zNIMy;mgR>5txI5l#gs9G&>Y$ zlV@mcjY_rh*3)`z^?2E=FU~(uIVz<({ERU5DPw_Jl|^{5>9hCo_jwJKQ->PFHTI>A zP-g6pbg~jVHPtsv9w>R0i*N*E-Umx)eW0gyS)UXDQSEuF(_ZEr!uASDu|K`Wh5Lt( z;r*$Jxzl+M5enbR7!^=l8(AZ!{QJi_%z5(x^RYoEtT8nCMoii6aW^IX7zNfWGpWos zVuuXw_U-&~_?oKyGio(2UuseXicT+Tl1jhXdE}T1)mPhKf!_<<)#^ktOz{Y1p0-fx zsB>mGWWs0O1a7CN*X1YzgF;sn7Mj04f37NnnQa zo-n@4Z#CY~24asTt1jG2 z^0JOe7M|BDwD)s@YJ0XdY25>e-@3uS=#Lz)M2|h&SGBF6^77Fbzu?-D$!>rIZIr&H ziJ52(A&v4fJaqRP;+iDkmlbv4E!lUO^ZN`J(rfD6nvm$@i{bNEK-qP&gCt69o_F7R-^BJwq2?pa?8sAbny_9~i#pU+ zzjh(nu{hhG5G8HI)C2}OyP>M#w{uK+T=6MYj`hF2+wNtHnR?`AV0Inf*FQW~in_}> zgD?5^MI38-iXLS{D~!rs1iQ+-Ya4{SQ_hoo;E6pV6T?@C)-BR@|C%JklVg1!dU>!%2s^wf&5yUMAQypO*dH5iUA@|(-IZUdsmd#857C*{l^YzAG zh@T}*f_!`4r@8&Anx`m{ni0B&qxwISq?7KYpQ|8TypUZ$EMn8*KJ(cP)I*Civ2{&; zdzt;+IQCBGw;EzQ1!B#R^G`S6=ixHoFAu4Wwj2FRg3orZjbtU%^C?dizd9(}#tLb- zPIBt3MF^NY;?m~?eQk1!zejZIo?wf1&6&%@ydP7k@zc2*AIghXYYK(hhmG$1Qo58B zVteMoGtaUIBJVs}&9i9$Eb=(wQ0#PQh8rP7glw~G`PzYlL5C*3gSN@={7IC#k{c53 zH_7rA)OFWjszG{E?dhjDXI|p2TdBPHEP}j?A8Z37#atq-hp4AUMs!R3Iy-&+n7Bco z_C?4i$TluQpee9#)4&l%e^@}g~b5NxXws*oF zD6WUW^%J-Ia-njW62oopP-cYbY;VWQ&vbh8i*DsK6IM3!5nsN;PKtzU*@>^Xgpz*yZ@F!>(UcVgSXeE%b)!8*j7F)Gf3- zUCeaX%MbraGdORpQ;AY)x};nf9__>>Ph9*O-}#t`?SPM0r9n#JTK@xTV9)ca{4Qd+ z$*F0Jvp2_;fZ~t`6}w?aihoAogUhdy{{AZ-o-QTZzaR1PlyGitS0!L8=D((BrcF8j z{uwWc>wkFp?;_1S9cWmSV3(Kc7Sn#+uxwhX zP5!9%;ek^3@AHHrOSTCqml3ho_0M?b*pmoJ>m2NyF2+c*T>?b9|xtqoiD zb>4lWzBBiz-03{J;waiE__?mLh6~T*e4}iXxiK32U|lE5rxf)Y)@2`xZhaW$lL@1GvM4XOq@&Rg7d>7Ovb`>{$0{)e^h0jTD_I8PcrG z@N0i#TkTM+T~tBOf<#r*BLKW80o zrE}z~f9>#=R%(}F;$0vuetvI`K%*fQWK{HtNaS~K*?ECg{pBYkLoJ)n6z$Ow*=TQSmTTr~NY+F?N8UUs5P^exGP0jQoi~*$c}#9# z8r+2}r=N)l8)PMrQCF+n*!J4dvO9@08n@w4-oA%_NOs~oQ5bQsQMe~#Mhxpxj7VF~ zeD9Xh$@N>3Eg~ptJJKR+>>C%23w?@iCd^4g1}UUo(mF}dM(+TnyFy8D=2RZ(dZk29*9I?QKiF}jlBL$d!hoAN4hzHH;*uGvd zh{t~jNnjszw#*>(e3o!Xr5)y8QvV{N&-}fty^#^M!-N%5k!?Z6FK{qmj==ovY<2-O z*l*Lf(_eQUMxk%+T{qv3I>>8YDlO+WNJO2Gnz^-WxQ4!pEFZ-+oEI_FEspLN6PKutx4DD?_W9C6Svp;wai5E5S{?gr=9c8zfzoI5Z*el%l zdp49e)nvlyOBX2N@udStHa7=_d;~UY3*Jy~$e5KWmM#T63ac`&N$U$V@|!?73I_LaRJF^o*R!E}DmIdDN4`Y>lFfo$LKc?3mo?9zp3{lSD0O+xVYX8j@0QP~6K8jSqugmFy%iJ@0bG|j z_8w0DlA~~qtumCoOS&j;zrX(Hb|Ai9 z>Ty2E#JMYQ(8~199ZCb@)QZnXq02-h&D(;|OJ3A3{E%*?RGvPIL%L#~^ zk=6#1dh>?-k;ih|g1_$tZfSKAgLZ#FUhJVZPxp`FFBl5P%v|nEn>hvssWP@rq=k#>K*%-P+uq_DO7Gx=;6X*|MGoayFYg0-)5%Jem7U>7V_oaetwdA zeh{yRs2(Bt^ubL&{fCR*uOji8pY-pF#T_L1dY$Nfo&l zA(hUH_l%ta1sagyCBhiZ{EtXPnwW~B52g9At^$25zQMD8{Dy{5 z$Hj{pVpw>?r+L;jC6Db25mLZ)#})oMOqqi$_7+s=S-8_Uw7j_Zhir$Bf^EnI?;K2x z(#yNzlZ!50Okcp6e-i^;i@NqW*++kylwnb{ZxGrzDgC-&+MQvuSl0_}$Nv1fU8VMC z3B>AS$Rhta$MTD%)AztUfnc|c+mQPJF8pcwu`X#RZe;0}x+~A7OiXhc(x90B`$xt1 z=1?(@x02T{iI!Pi{*YE2pdPw}B^u!_NJURLajNw~L1A92OLF_{IpIzn=3~Fd^JFTW z&_}styRXw%C}o6DWE{F#LofWF*+HZQKHw@_nfT9EC!1X#9@{~o0=bls$T!oRPzzhc z{Z+ja=8|6<9_qtL0igusu)w;e!D(();!9NYDOG~`CEJ(8QM&5y`)I{2#U^9(VHZ+_ z%il?WDIZXmGzQF8^=*l5zqeVC^~YG3VEt0j;bk+k2O;KmI*{LF_8N&rV7*;hA(@L1 z1HsTpK8QITUG4nZU9H=L2j3zSdE}%XGZRVfxfIkX`?u}VI3|qK3U#Aa+XZ#vJ&c#D z9V?Qhyeq<8^y#Y7o-%&%MMOhFm06bxe;p*ddXEpzc2r`L$8Y-p)_Al=eZ5mo$nrIn z5DwK9ac9q~cWI9Nwi{^(et;pYUThcIaNwg-XvMT$xY)qtTjk&#llow_1s z9Aqdn)Qb4z-2SVKbo(08wY+=<0{3O%JS)lkAba_U(`B)ZWnSH_hFDsk;%|TA<+q*M z_!P@|{b+ha6Hex}PR3)n$NW4R)*8yBOk-|AozUay+?ZRbR<(+8+sOF&CZeB_ga-IsbM%5)BF!V40{}IkZBbk7^%*p-ze-5w z;;bp4@ZzqSg|x|6V?tW*qwZR$K+{6N_j2A=HA3>(fNpa-Y+bH#_!y(=-r@%hnHIC~ z^3Q!IV#00q37&Z3wyRmMo$Y-|Z2|u*(NB_a0_27PpJJ|S9QGu`?b?3O*?*K{1=44& zaPQxTEcDi~W#O?z5!M`?p@vBQ-^7I%eC64PaH_D~_W7K?67)4?#VEHiz0*^Xn*Nbg z>(hnj=ePR{eEOo#Y1P`Nv${Xdc(edH8(DZy%-9{~$Lr8P(t?P=2bWtqa;oidpr#{o z@V1U$h+TAKZs_35QK+_`s20a`!6eb31Mg<+c+rUCzzHAr@avPdo(6HAl7k|u^tfcf zTZxDJist9zk!8$@vd(piPIZ8XiNc$_nf*v`bW!@qtlJx{QO;$@)=RfD+T+4$r+YMi z<^4Rru+x+rH#A9DXHoX9n{A&wkWDs1a24-xc2lBuo81ZKudw% zS{U0vi6nIoxC~v(H=2*@5J{RJAAF-RPSb`k|3Kn&E!x3y7iu5@sU;u zu-$09AJYdfK*r`^Q&VTKKd+a`2^vNL-)Sm;_gD8T4nMrae7W)JJv^jz0`){Ir%1)JZV z&StoJwoI0rmQRU&+kO@3Km4XH^h&dyWbF&& z_C|>F(}LueOw5lngq=`n`E|PA_15$C?4rpYG)RgJno;&?X8i5*D_{x={C?poXP=s02J{p4bh;a;s}=lS$Vufm}|Zn&T$#lq#tK)_KVj}=E&La9=Pug&SuK(&54t@-ik9c`|>blKPND|Ro{}> z=AK9MzVthuDM{T-x7K1>3X?rsx*!4%w_jO447B@0?jr~xTeBfwjwFte1*yO$>@^Pu z8E=qoq3gOujXQN`rr%-xq)MVs38|*%uzuFU@A!6SWpK6F{N`xp^f!CE_gn<-+V&^6 zc3;GiQxprY1oOqWhP?a|KX{7+b8j-1c~0QDar0T-da}{9|8{MWfUYY?+K<|KTy|Qa zQyc3q3+fEfaL!V`k+AV?{`PMOS|xU)lc=n4K%-jt(_QnZxk%UCHdPg+Hi|Et2j^d) zC)kCm^Le9op><6LUXbQ4?C?F+5$4n2NyN^9eFGiPYnaZ)T(2@a!{h6xkv^BDDX(M} z`1G5^*P)MYWUW5pGbS^oEHtv;=)M%P!oV_8$%(KnmwT+llxk;4n=cRl?{ORA3LFfCV!6eU&wc8BMx z4YVq=13V2&@?+!FHzi&XRh8W|iAxS+C6r~IV9+v||E32lPtb-OV}6&&EytMDeXVXV zh~rVo6TZw-zjwaEViWOXc|c++xb8|x7I;`#dj8YVz?|r776V}*I7-yRze^KC^YH4e zcL9jHfQ^B8YJ zBh<99!B}NaxKxEp;v6d+;d87JE+qaT7VN1CI zC?R7StWxO!Q@3v7>!i@09@nk_D?YWWZbkdf;~to^q8a>FLbh;tTHgFW51S+zu%S@IH>e}#S4O#h<|#xcnJrvpZ|5^pajaWNCSLIHeP^sD1zheAOz+E~?0r5fHOX4s zGe31B-zJ=}Qo_(YnR@!+pH$T3({NyA6g96j-H{-SZ3(~4%+J`SO_OSrvGrUxXL;fR5pq|WZ@5xS88f8psII=k+R z;rhbzP3n79>jI&XRpPAmCrjH!LlWg*^@V0|$)}*9{VLOd-F>w@5m^nt{=>9&TDrFj z4(b=_ZpCux;k3{@iNcF%?6l2o>9QlKLp>WM8Y+^`wU^W$`1b@A50U$8Esu)i=RZH* zI{%Wsh!s~xO*>~lKaK9sH;0LnFcU&XY@sh#e|-!sw9(S9WP`9J{=a3T3D=~1-OCm^I3JfHQ@KxN7` zz5p-PFG+M_Ogs%eBW(|}({uvUlb`#q->q?JlTD|i*=|>z+pw=G(fOo-ZF4iI{}F{Y zvgaHK9zt+x&-u5#3D>JM#jOqNb$vJJ3Mr@6bUBK5^od-aw~#z8gXL|f>2e*(q?8Mx z!nt;{i@)EDv-@I30wkFEdQ2>lPl|1c3Jj3@_Z{WmM3gQu5o;dL#?=GLR89gTJzI6| z{Uy^%NS!@ha}bnH0&ALYe)fjQ;8tb5zJl`~af%UGY@1nVOU}+;{u2I? z3id}?lE>bg>;rtXq>oex7pzQeEc#SMG?s2nAUp~xAA6o~Yvis6t=x~&*x`|8|@R@E`S4r`YMf0-grDVu&f zHlG`LFi>;5%waVw*II^?U@M5D=+Gu^>C;v+Y}ly}lCm>h<(JoJ?0D+p(V}jRBukt; zNhK|lt6hID0VIvzs~B$@=5|Y{GXNC^R|0t1ezON(EtcW_t0$BbQ2d`|Sku|B*ULm5 zweK8|)%X2{y8tNPlN4~N=vCn{()jly-s{8%|MhvC>E^$mzrXuGm;X;j#*hn0ok=jq zh1xoP#H;E%hlXAt3Hu?o6-&mRp||A${{?!u+R^#W@sArHvx{;;Yo zjtUA@-b&>XoZ(gK%q8sWd^Ls}8u9R+9$sXRJPsArB41h?(n%R*xP_(j;G;DE&i(|z z{t)>K%PrGgnkRy@j|T*MVKVg(@0N#bq*G>9kuDLjv44z46e?+Rx2V~v*H7oa{Ca-v zq#1Dc40rhzDovxeDqlJFgk>gARYj9-g9NLL$}T%jR%(W1A6fMup5F`o`mQh`HP^mb z4*S^rs)slXhxH~FEbaNa|Ew5a&y%1SFd@xoW;Aqe?Q^S?8Z5h1lCake>3l zP$PFX*UYo(jB_vLuFsG%;Vtzbl;hUt(n}TmD z+|>{o?JR_M2I31RX}*rL;&(c@1@j4}NuK!lcU|~9TQwTF#Eg3tXrF|@D`c64YM5xJ zi^LxkPVH%yYv(q8oX?ECS~kvlS+4X&>!r=s<83dR8XkB&xzPD&>T0e*gb-b>nSrEJ zpLEfVl11InVZqDlaD;|tLM0CdliDDGxwCk>EX&%6Ke|TaC<`*aii;ZM#Z!%I*5pqL zi(8|#nbF~mWSm<|k14j-!Z^xv&kW{koV{1$ivS*28Pqr*$%|27NB7`Jl&pP%cvNIujwjn!{-Pi^*jA54N zqweo>e&_to^PJ~*p83Nc&iMGe*Xz1o*Xwm%@q%f^%0HGD^UucnQm2v$Q_}f4HjOmq z|BSju{Vg-1ok0+MbuDa`TFLv*S`Tu}_U~Xk_U&t3NT+ z@pM`{kKEqi>nh%M1^H9$J`UZ5K~ECGgiFiF=J8MZU(z*!ALY=MUli#5^lh1XYo5s< z!u8agrON#G@54Z1f5-3lalC7H5aWLO)1!`nK2r(LmnQklc-3)zRQwW4i*Bu1a6MuC z(W*%NlM$XOr&qKo$a#(`r`e-sv<=P3q+GS$`#GojQ*_zPExtSeh-r4g?&_pLi~pI* zUM{|_)E4?TT@y2FKEyp=|KayxnFoI}3ih5|TzJ}qncwe5b5dq!!kX_Dqivd87Tq}~ zzkKdOtnZ4i?X{!7@n0LIEk9uHye)-7g%@%KFJuWR{OCTcBS$XA_;BHm zEx1X8W~pQ?YGz+6qKEI4xRh#gwNH&pV~5ow^!9R1_ggIWt3NJMp3V>ePPY`DxZ}2oXFcMNi0noQkmV&_ zr;J^qa5wBZY8DEd-1v{3?B1fc8M88GH`Qjk?qIYLP4Fe`#7n5 zVJYLKmgcaisgPTF_xMg7&nZoMsb%djS-M@KFlh$U`WTnI*zFIbz_-N(LnIwj;b!}t z=YDVNCtHZOUVh&PtnTLz(@PCH+ICtEIsl#FqgUZ;b{w>w^u^8>UYXq1u{pyFG~)_Zwv!j- zky&baah}td^+;C*5~ib8)%jU4L!c+5Z%@Xf&~`+k4n=wwFeV;BmtuXJrML36q+=Z1 z4E*m41_b3Mg+7hHYRS8hX?V^{ zyIU3e(-Qm!LC!(fnZOU*tSFsn?_Yc#hiaYs4Pd7K1~9)@Jig~pEcl(f`6=T%*L=xj z+W@So&twZLLx5b>p4hvdQl;UhKUS_R48Wa_5Q13FHptrtj?Gdc{F|4;4G?~3Ok49h3PM)Ad zNm%J<%vdjg4;US<5o$j?&c!5fU?q6QttejvG5;aFD>53l9RBMeeChl@Snu}4{3k<{HGi{&72*tV{x&p$g z-+S;z>|c3V-vlo_J;R2bh!3cpKgULy=-mb;byn~-9~gdFT;E5 zr0fEO2q)gDfSJJUZ*J`!kgw&xv;2A87B$cLBw>J}bEOBIN0+kX^~V$@LIkX@dmF;N zor#(rC)MT#0xwbXOFPK$gOc8J^;}$`RCoNmH(dz0>6h1{P@3s zqc8Ov{pf@Du}X(~NDz7gkc|fRi*9CHWuWdc(7#;5s=zI9*;=^b*k(>-rB>i-TjG*? zZO|OGY0FFpbClpHcCAWi9!~ zZx<0}T|I;!5dF!Fu^N@^;;F4SwTM{&FYVqjb26yG<-J626@FL-0 zSsmjQlYWHi4J~4GwbbF~J*;j3-i_FR=Lu^g4mseg_+1e<6&k$X$c*1T_ZLGoh@ zer%=H5Q9<6(8+96+UXaqC-mh<;=C3ic)E;xftHhS^XC>o-ol_rk7BIBFjf(TBC{0C zU67B;npb3*I?zsfLeUp;mszt6X9AnEl1`Z*rNf=WDug-AOgfLRnir-M0dEoV9_Jl)z9l3G$HaDN1ZrdUsV;?kE%#GKO@VCnE%?a z8W+POaWwO;pl3>2M*&jY0+bnpY0Zgq$0?yc+XQ5U`034PW(3#A%|>t98D<$nn>}93 zxIC=6^(MwJnJz^V_EG|Iw=kwQV|9C4-G_|@9^^=mT71KoCTvsfg`YsQ%F-#bYo62uy0r#;=7-06-^snjta5)mB!Y8`vTrhq4qV|Ov^L*gv3j?E zMxe1aUdcS@z0%P*4|w;Cg#uIs*_`aHdE(KJJR$^kJ zgTw0lha(D5gDyuUWo$5z$FB{c)T|{xinxkp54TfYen`=3o0XPma(jGd#=j4JxoqZM zhTqCdE3ru5!6|4%ypm~hCcpB|b(I=sqb6OnIjRm;oL$LtN3PLcNo3dgU_?#ZZZ`Wd$-zB*xr5}ct3n* z3*r_Meb`d-t-iFuwje)gPNiwe+Av46Hxn*?t2fhFbxT*2Y5kJYyvi4yAcW}ON-LVj z>4aHv$OzIBshiuF3ePjbJ%oMoJLVJRe=Qh;>1$lf&`8U`yK+1XE6jtz;^tTE3(y;A zcZP5v*4lV1!%_<*9A<3#w?z&u3KMsCn?vYQ?!>QI$U}XcBwJ_u0|TAo3yxC_S5-1n z^QfDRat?IIE2SuW>Zr&YAU|n~R;TU=bQ&j7F-vw)-Td zlsxRk1}-H9Pu~I3AfBf@BFGhX@!Z+u;tv_l6x}!l%Uyciv}I4b=ZB{Uo?VIA4`}r4 z@nSuo$Sp73%a!{4iWqFn1a3o}6b4O6nKfQY)EaT|^gmwkcGZRgDO+&v_Pt;P|@)I!PIVWWCHnK84E zbx3_E9Ti*dGdm z#93KXfnn)V-}fMXqV$cEx41fFpthOcQF(gshK)DNX~4Sc-oZ<8J3Z+}Ui_MPzC{ zwhy|G6;%j)u#?t_Q|_l(xcRdK#~+_qa9erj!sa6SC!n{7eEm_1t2#-PG`> zX;=Fm3Pj7QDU57$bpF8Mx;2>q%3+?n)l(Aqvt@&_YCBj(V`WT^*3L9s2`e9h-q^GU z*=D*mLg6&ty|yS?p7`=yw9fXy#Ju*J=Fb$Ku6R>0yQ5sNO@mbfzeoJ-IC=wO->1eN zm1sbt`{Es0@^F-ooX$<7GRgnI9=r^9wFwiF1hQdW+lGY+R zIE>NJmi=pM{SO`vgn{00Fm+xg4DxA80z#LD&pg=c|=9la=UJ2%(;5|Ki4#wz0O!_n?i{3Y<{;j5C^Ct z-t|#N3mbFGUCKIP(_Q?TF+y?jqb5@Ycv`w0>@wFV&F1dr>3zt$#{#WJ*eB-w-}RP| zJ0;0IF`K9A{ewooebxmTJ_F%f8yOl~H%FG}Nd}RJ$=sK;3(1R7E)p=9>e-!hJ}>T^ zGmtZUMiMLLXqy*`iJyxX-l>@;U-fqGaUJ*9NJd^iTXtV40DQX_)C=0|5h?!i6RB%g ztI&6V@BBi3oR^`2u~rF+Px03&lVn?GY2Cfslob;=!xR{h)6Jv!0$b6iXKAU@q~yoG zE)&|>wvTjt7D=}JvA0U=STavnH!jvslpS5?2$@!k-41ReQ{C(nWc!bzz?QZ;y}y*= z-Qgm;R?+Vhf%Q+Qs;4w`o z&NdlPdXU&(YH0HL%Qq9dNGLy;sD}xRj2Tq53E#%^K<}?)M1%_9g%?LmM|=60Ss5Ef zBEQ%Cph=HqS8sebSDADbzOlr`_Ak3D4sNJm#T!?@UvfKIzjGDtY&u)qi}!5cK3yy! zuoY;jvA_~xF5V9xw{8_2o97(e(K5`e3cf2FGqC$vU1#R~m~zNf)i>`z#IrsgJl~l# zuH2K6%n>mSBAY(npYNA-{uT+xrC;)~`()F`oV9}P)tgD#H669h);agI_Vi}mqmMkS zHDjsq3juz)a{2G|t|QFzmO4c#rP5=GJ3+TBLt22WXe=1<&NQzR49)#=YuQFQM2EbW zPh!eAFdM~YnEUIBR8(}pn6U;W;*7(~6K4+A9yNi#K89(UI8=A;nEnVXIp!H0n&56B-syB_Fl5;fc$PmSw z8X1mC{7WWiVt3UFP>iF>PNyy^h^)U$+$Uvc3P4|A^~TjM`aVzs=Z*(=N%r!jF=O?Y zqwEm|rk4?7r7gF10!GN8dSsDx0)Ltz>8K*3OgyfGqwjU|k_JiE*Nc9j>VdVj?E z!I#KUBulbnQ>`U8+XVVUt1lRqGrw=)XX~)h_G#!8c0tM;I~=y!COeA0oVl}7cVvv7 ziV)pOGz6vO09UJyA!8g_4^KDKxq|g~VB+#40r87p(HCdbvGPJ{!^ZYQ`p9?huzbP4 zx5zSW3#_!wg|_(bqDPm#cWn2X?kd0(08^eZ*0LPc5Qbc#tlzHjX98^fd(q}Qd1{;? zHjH56z_(=|?^xHBCIL7;1VR{c_QciZ$nNu1{?}u3zj{fB`QKxsE^l> zQ;9eC_ebpy9(o29$u_4RsaeoEEbIr{I;5d8zt^K#!pS~IYgSL+b-I#k8lwR45OTjZKg8=CiT#- zzvcbbE;T+s)-E*&!yP|&lfL#G$B*MT0(hEuO{_i1HX3l}XHv_GZqck@I_TD1|Kcm7 zBlqH@IBNs1@!P{Wf2`b}rAW@6-vmXVud=ww6?Fj?GzD6PQA57Ygc{Vh->FJ%vmZH@ zuqobmDWCJ=y&W);`tygZ_Y3`|f0*MCYsU3Cju*bP3?q{q=7IoFPC5T6E1RAVMKWl$ z#X~-I?(#=?09%vbYjrrk{m&BN8+r;Za2lf`2|nbSYJ2%jrL9rgC<~jmqmVPvUEs6E=Z}8e0zC zDCoTv#KcXV`I)7_=^x6ww34z@;=W_Tc7qCf^fpp?R4p|)?fPT9Nw)fM;o*VR zQn97f+b7!q5Xpftg{T<}KluCGNNypIeHv87eyK$gl0XuOm#KJ%<#1svQ(uA|e8)T$ z9)?nPUBpc6+pZt={Or_r!V7;dCLct}6!W^&qOQZL0F747U!UekU%L?U%Fi4B!e6i; zPr&FGxKT!K6D&4%wu10koEwu*d?xKE;0L{cdTu zR?Scmm4R`}1RZ0=Ey`OIa3Z5iP`{A=ZeO(%zB3f8BcZ*UF<100Nc^(t>5vld_5{gC zOi4CPspExuPY<@(VA*;JTc1r-K%2)a&&=s^?8yKp@inK*G>g8p*J$~=HnDX+!Kn!VYYf~U>GJI+D(sG^4(WI89(^IODuE&}^HWVz%+ zTUo`J+fwgU#BN9VE6Kh+Gryhl&Cj(D{3c}ZhewAC@jVv@D!B-GSrtx3^?=^KnmBMG zWrHx>u)1*sYTqyP?4^|u4h@C)R@T>DKJ2tZdOG|$t4Xjxh~`QUn-^15Wn)rai$lsW zeV;(xXjo-x(ck;K``z-DHWyq?^-|xcXA`%nyr!EuK4JG3nBjTt{DZ+gi{cBfbIg9; zPAXL{C|RRn*P zH(SkaaT(_}Pclh^4h5HYqD(~2Za;EL1^-+ozKO26d$jC4VuODyyZ>gs0pc=VXf`jX zFT*NtAAft8JY+m7z0|2g6Bp4geYjj?9Io>IRvh%jvDIt~w_PEJc9XA)NY{^WZrg?B z9Ec9nrLY*~dmj{PyWA9jzRSRX&Ehq-WNZ?lDtSx176CtY4F#Q_vJXp-f3k{+z-))3 zSiZ~Z(GGemZzfn3>l^%h*%}bSv7dlmHhrs4veBvn1aK_kI}J~Bb(#QXWP}_X1C9U{X_{FJS?5r~%=PA#NXms^&waYR9-%3d;gl6$rWMA6 z@w9SjWI(O0Z41jnqebAVicJ=Dc|=iB1v{>i$cp0^QkHAHT%&S8EJFEm z)x98j^@xBDgW2!S1|M1|R`CG8H-4A8u|Il61;^1#)(Av>6@h^h*7v&hxyCLm?lyc> z$4uk>T^{Sb{S>BK#Lau_z^r?apO7c#c-Is4@O*)Q>|-lln$%}_nM%~@x{40bW++lK zeVK3PXk>k);D(H7xj)+kSU7jcvH)UVaj3Yb>v(qRe)W#&!d*ZGSkj>F z?s)J@e8jpMz1Vwr9(u~M0H4TfzdSDZY^#HnB>$1Dmb#aB=0z8x6DJ>Q1kWKj-y$t_ ze1w*IFR4!=%nwa~tistSzMc8y5N(Dfy;|d<1Mi~wW($%*nnKg|W0nMUAfa8@pK_ft zNoLm+1+nL;_Y4$Hm9qxnWP1MgUwufiMEDgPo!#v@2xz?2Q<=N^s+&i3!Q_*XxoznE zn~6Mq^13~U1^l{17wre9i_2RTIfLG;d-Iy1pH668qW9E4Y zOM6{`xQ(2e_SuitP6Q42O7Ot!ECc?-_lhXciG`5$9RR7v3!T>1HU`NB8CdlFUH%ey zl=)7kZ1OCl=?wIw_D=e>+}>b`?@oy>T7P4h0N4`k}#gt$^IcbSW= zBFf;~_h&mVD~}EsgR_$O=-)$6BsNrPt?>$)PD4++1RV&Nm7HmEHcFV;I{*;%a5rwL z@~Y`$ap5mPzSHk4yl3eXOQ%q)k32+UVu1e7$?~41YF#`pg{E!;BkE!;?G&1F?~k8D zj$PF?2gFh0C;vCg<>gJA*4gg^k3%n7X#9WW4Xm?_)9Pb&scVshFGz3A=u_J^qTodGh{Sa2)&O=pJ(<}zbzcmSBDq%4%E=b@=M)6}o6fdI zDZuUamU7uAO|NuVA(rjS^l#~@>y>56Oigb;YA_i(wNjnkLdOKYEVbpOv;|ZXI{uj7 z1X4#w^RAdI+y==|;czBn7zCoxEA0D4!U<`pOe8l+YtjpM&VcM8=~ixY9`@PR68Fg# zR+9AoCd6Yt5Zjs^P$@0%^OJ_nR>wa;7kgZ<1;AtXHj*;a6vK9m z(mIG_Ys0FBp_{9Mu)WX zB}Cn~0~EYH0^N(f!T;I}2hY|JKlr8SOCLvb?V;lH-3?_nS-<9cM{y3Bg?s!B{gaJ8qu+qb$3OthNoD zaD=<-GbQ%;2O&LkJ_K!ZawMmtR_##73)=5hJYlWH(DgmN?2Z;ak#cs%zn2@UhzTJo zX`oOzt0B;N%4y2b9C1~C^f7)MGgJZLK~vLM1gJO0!Qi(0;yFTWdf;Be&IiLhqkpga&%ly0{A!^eCy(-tYr+w(bG~b zm3GDmNHNRLN4UdMW~3htgl)k1#i!3l$>4^)^vE(?w0_J`3~lcEW|PCc2&lj)6F9|~ zNvO>7rV{${D|sK=s(+xj+Rnz{y*FB;ymRVay-0hav(CaKqf|tf(ljs50bH0+`BZA2w2)W7b{i;egi9U(x*KhLyFnyyOb#)ks!RpT#Po?>3VceCTC zw5|pUU$p7Hdeg`9v^E|CyJkMA>@NYy^tznt1*K?^xt~`5JF1t`IWJ>_X+=;ZPw+~a zsLU=PVP>sYy-j-$umC_Q?sgH&iXa+%6S8H&9HAs!$y7XUF24}`3BILSr}uR>{{ zWyWi@^j5H41&%NfVhI_?21Z{jn-^o6*$L|vp1nq~%wrfEq@HV&HwxX#TS*z~*>UA? z`4CDz!vwAmddCMIPi?H#brWX4mrTcL(d+`NUr9qCo_FX}`pqV}js<#+ccGkj6;Gy+ zW3*z{WGM&Xjke`8{~n|q-qY_kK+R+&N@de|%Hkk>tXj$p5vqy_T8(kqN+}x}_M)Yy z+eOJ>Xq*bvKw1d=;AWYnR4ccWnwp*- zn34|J<-ykBGLQ?Ew77Hus%*foYUulYDW(o4Fnr-%c@WY4(){XTTbo_ejKA_fHkwn$ zXtw_8Mc2xxeV&iQ&lX=RYQHgA^#utLgw7SM&YjyTXMc1$pd_R8+`411W5Es=&MlWs z84~di$>aPXKAz0lmpXYjR~3e~%xtlzB$-Wb=azW?GV(s368~mEpDN-@o1-gU71SRa zj(~oI7}waeSbf~;U*tOSXCx;Cu1U42 zrO-M#T`iLV!?3uD$U_7j{KZAv7sD1{D5q!k6fqyFQ)&U@FYX*ulC8qlhW|0GAtu~E zID7f*<^Eek+$!nOX=Zv;wgLIY@)hZtk+X|mgRW1RttR=78P4MR<|e44I+>aZQ#iUw zkG0T;s#b0fljMPpp%y>XIV^QWU6BqG)w9&EOkjTSxy~Cn%xJ&YR3oN7MOSoIJIu09 z)KdxIMcLvk)j`rQ)@ZZ14EnN|RI`K_b<2s*scmY)D%UGoLM!@dA!BGNaEbMBXdSi1 zF14vJ2?m-Bi@qq<$_%Z}ox+kLr3m9m&RG0hd^36j6UKd9Bn9}~%6X~%1{l8F0SX&~ z!Xs7>YS;?Q$){(M10O_y%CCMp|46e+0FJi#^Ys%<0@u8V!E?7Mi@oP5-ng5GD^|3R z>9dq`ll9n>0^Hv`V}Xlu-?wQFntV3k-&qDOy^7l4Uie{~g-$@$Hg^dvkaqWw+uC%n zZ|O^w#3z)h(3Lg54~ZfFUi}ZQojzgS1b8d*N$uqOfBvz%%>TH|#lLU--&icAl8OWV zy${9Q-ZzI7jnA%QSJCd_ySA`k-RM)jt@~0^-Y(d!!4JFs0X80?+;YO5GCtP&=Ckn7 z7lQM-QK-}?KB!Gn3+1@VlPJjGo003`QO!^@hJqvIK^0?U#LKqfd{+yYGw~gkLYnko?EYo~C&r%r7+xcR8welP!al|yf#Yw)F$ptx_^*>|)TWLu_r;jsF&^Jxz-a&bA&{ArcI zaK;z;OQU*u`gwuz2zvqJnQs32pfZO1k-GgWg%A>gxCbRu8wdnngl-sdB&T+vocg1$ z#cU)SX)=`T>UK~#Q?&ZZYih_Em^|qopl#pfA39kc-}hx*7R30$PMu#pNNw@}pZ=wt zF2ko7e-ll2?FC$qaK;!#-j3!4q=W z%CUEe3O@EOHK|Nme;nF$-9yw@?{o3}<|Whj{f>Zt4qB7| zw_-XVpcV^$Y_Ff0B;VH)jcBSx?U1jpPk>bved|45+z+T%?OiNA*XFOPiCm_y~2-bFqExHM$Q^DER-kAqu<0}Rw-fJ-hka~sYe z7)|5D+BjDq%W>f>k+tf@*-8J96@|`Q>V=8Afl5VWabv?ngH+x0hUiWmuSR2_*m)Dm zH1(Lt_EO@*|BDwq|C#u(zACo)8XQ^a!~zM@hrV80Z1<^ZN90FJ`#GAMvrQn98Co(2 z{eq6Y;0V2XI!-2DiqQG8Y=@_4{BAo_8(Cke+)BkCcTZtsRj9j-+1uzSl7k!N6CfOO zJZ+;(Eno=!nA)<}!&pJ-gzpgnBj8C~E~ZeK{ZQ8jo|SdFDHml}1-*uL5@DFb z&AJ?dss{F(*C+VYf=mq0jBY=FtZ2F*RJGpK=fiwwysa$6GsKwnL#fy;flV8a&Wj0o zaRZ))N0FJuUEX+HE9cg60IvlEIo-%2~G!zO>0SXz7o>r5`WBt7N`6FapH}UZts-bIo$d4rq$?;;v;N1n`*wA-R5D4LELRNB#A$N z!uxCAQYCO;=F;Z%El}A5Ik3f~_WT1Zbk4-@^6da=CLy{h?4Z`#oRiZ={V(dW6!Pbh zcUWV$iv2ru+`gY-s#x}}bqY7C#nb8B5(_Mf(iln6IR&yJAQbgFMW-LACv!TE75uE4 z{lcsKD6G11c4 zx=z|TE*jvFug+Pa$C?lHawt)0nBc%2I+7ww$ibF-_Nv*_6#8cw`fpxpto_4nc+bY$ zNh+K#mp1Z_e5ASC{4Qq9V6tXk4XfeRC@?Vg7`@OrX7nKnK74E&>4rpbDfiBx*Zwlf>H$QL5C z>3kh*p{0Is>Qi&C3?kpGKK4a<%}ffprmbdhR(+{fh}%MM;1J}UDH{L=b;V+2{9f)6 zFLIZDFP5L%+Yc}JEM8xg^gR~MLkyL9h25Fl4Lr%^uR#@!)(scVT5gTofkIU-l?yifkk6uKM=J!OZ4Qk6o2A36=C+?UMzM{?RyH$~&B0OcMV zX*G!cM{-CJ_En92Be$k!vNq4SD+z|5ASt*DDmZlaw65HyC8T6L4N3cVKJARSU4}0w zd$p!XwH`IO3i&r5K$M>1uZ7Ec$dp-ca_2eN*#K6kg{$KlKo`n(1^jtgYBGOW_9?c; zy6o5M?fc{>?KOP^(w)wnRz3WrdS;;JUKauIK8}bpaVu`yJ)iR~P8&?FJJ-5;P9^~- zl2?tG@zJG6WzZU5B!)i6HeUE}3mhx&LgrOx|6vp74H(U_eJZ$>G!Mi*0kp1jzk#-# z|7p}STrTw@o^}5&+Y@8&;lj_E%1$19uEZ7l_r|6@!?wlOYw-#Sm+pK!MDgCqSE}z{ zFUgj4DZgl9sowygl$)jLJKx?c9`K8qXDCrLEJl-Z`>JLXb^Y55^RJ0GRp8tCVev!= zbMpOhFeIVVIk5S!&||CWY&rR%Tr(P`$mM4ci_*9h#@^Ltt-b5`xEqiQjP8iwhY$Tk z79xw*y3~60ijGtw4qiR9OYXeLQ8z3pKf1jae+ybCVG%P*%wX2u?Ggjc(Q(QE{as>Y zC#`{d@0JrN!)pD=?CIf~b;ylW6Ew!__^<{V+_evl0@HwRHA>UY9d-}Vv}p(1rxmE1 zR%k=i(Yg9{-{zaHvO50>p&tAvgjzd$!9f?6=fKt!etsfkX}-^UmGJxt=ayz#UJKu| zzVJOG23iMNgz%$bT>yu&Ah;mb^9EI%fRJnIuA=VDg@El;>ky_&UzyG4v)Th}2>ZO!+b}kCYU9d3 zRr$UAzT1ag7=r|{KLOmWzMaWbD_KYaQ8p6K^6Gb3_FhEbn&F&U3fp-q>~H}`<^d*B zRgevFuV$J1!~Cfe-+uC4VU5?vadf3)ALiaqndj#^E;D!{ocVp-QM3C;_^w?TyqfxxTU1ta{nE`~o^g4j;uI|p3H$MAmT}iQk)xNj?%li0F-#<8M>v5DoA%ry~ z6YkGJp3sQYR|E(EFOvvuF@&~gYfW#jI$>9%e$1trQ67#P41JrwkHzoAz{`reK!L6s z3v?-Q68M(msPE;6|Fb^Ugn25a7I^gW=Nt2blPCV?zv!JzzRbD0I-fQxnK^}BH5+^2 z@k=tXNWTV?M`97mAHBIY%IqOL4*$OSZwQ)qm*G?KWlroHt7BNfQUJlP(D*8vBKR9$ zf0SZ8*uxJ1ykiZUWv)Z7KW+S+o{Kl-iOb!5FP4()zY95ckKd<~!005Jp_nJ-l z!IF-H#zWmI;yfGn8GiD?INNMfnx*CB1FMJM!+JkXm4q4>-!?45AdO8gcK)-eKF$5- z5!MU@S-!PL!==n%4pm*?a`7gt`<+l7o zhwxVhX!(UgfPFmk9KOsY&mlzK_IH3eoM}Rc63W{#Nt>Lfl=h2M5#1d*XT`lPZ%zu3 z^Tw^)!gQF3{~d1UtC*UO;1(U;57%JBBJV5 e&i^uT8b`F1g^DeHn$7QPLqk>fK2WFWpZ^6a9zF^H literal 0 HcmV?d00001 From 2af9549bfa3e8e400549729d11fbdbd7ebd9eecd Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 30 Sep 2025 13:36:28 +0200 Subject: [PATCH 1234/1733] Update docker-vm.sh --- vm/docker-vm.sh | 814 ++++++++++++++++++++---------------------------- 1 file changed, 331 insertions(+), 483 deletions(-) diff --git a/vm/docker-vm.sh b/vm/docker-vm.sh index 65a59ed75..dd880f505 100644 --- a/vm/docker-vm.sh +++ b/vm/docker-vm.sh @@ -1,14 +1,36 @@ #!/usr/bin/env bash - +# Docker VM (Debian/Ubuntu Cloud-Image) für Proxmox VE 8/9 +# +# PVE 8: direct inject via virt-customize +# PVE 9: Cloud-Init (user-data via local:snippets) +# # Copyright (c) 2021-2025 community-scripts ORG # Author: thost96 (thost96) | Co-Author: michelroegl-brunner -# Refactor (q35 + PVE9 virt-customize network fix + robustness): MickLesk +# Refactor (q35 + PVE9 cloud-init + Robustheit): MickLesk # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -set -e -source /dev/stdin <<<$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func) +set -euo pipefail -function header_info() { +# ---- API-Funktionen laden ---------------------------------------------------- +source /dev/stdin <<<"$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func)" + +# ---- UI / Farben ------------------------------------------------------------- +YW=$'\033[33m'; BL=$'\033[36m'; RD=$'\033[01;31m'; GN=$'\033[1;92m'; DGN=$'\033[32m'; CL=$'\033[m' +BOLD=$'\033[1m'; BFR=$'\\r\\033[K'; TAB=" " +CM="${TAB}✔️${TAB}${CL}"; CROSS="${TAB}✖️${TAB}${CL}"; INFO="${TAB}💡${TAB}${CL}" +OSI="${TAB}🖥️${TAB}${CL}"; DISKSIZE="${TAB}💾${TAB}${CL}"; CPUCORE="${TAB}🧠${TAB}${CL}" +RAMSIZE="${TAB}🛠️${TAB}${CL}"; CONTAINERID="${TAB}🆔${TAB}${CL}"; HOSTNAME="${TAB}🏠${TAB}${CL}" +BRIDGE="${TAB}🌉${TAB}${CL}"; GATEWAY="${TAB}🌐${TAB}${CL}"; DEFAULT="${TAB}⚙️${TAB}${CL}" +MACADDRESS="${TAB}🔗${TAB}${CL}"; VLANTAG="${TAB}🏷️${TAB}${CL}"; CREATING="${TAB}🚀${TAB}${CL}" +ADVANCED="${TAB}🧩${TAB}${CL}" + +# ---- Spinner-/Msg-Funktionen (kompakt) --------------------------------------- +msg_info() { echo -ne "${TAB}${YW}$1${CL}"; } +msg_ok() { echo -e "${BFR}${CM}${GN}$1${CL}"; } +msg_error() { echo -e "${BFR}${CROSS}${RD}$1${CL}"; } + +# ---- Header ------------------------------------------------------------------ +header_info() { clear cat <<"EOF" ____ __ _ ____ ___ @@ -19,398 +41,290 @@ function header_info() { EOF } -header_info -echo -e "\n Loading..." - -# ---------- Globals ---------- -GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') -RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" -METHOD="" -NSAPP="docker-vm" -var_os="debian" -var_version="12" -DISK_SIZE="10G" - -YW=$(echo "\033[33m") -BL=$(echo "\033[36m") -RD=$(echo "\033[01;31m") -BGN=$(echo "\033[4;92m") -GN=$(echo "\033[1;92m") -DGN=$(echo "\033[32m") -CL=$(echo "\033[m") -BOLD=$(echo "\033[1m") -BFR="\\r\\033[K" -HOLD=" " -TAB=" " - -CM="${TAB}✔️${TAB}${CL}" -CROSS="${TAB}✖️${TAB}${CL}" -INFO="${TAB}💡${TAB}${CL}" -OS="${TAB}🖥️${TAB}${CL}" -CONTAINERTYPE="${TAB}📦${TAB}${CL}" -DISKSIZE="${TAB}💾${TAB}${CL}" -CPUCORE="${TAB}🧠${TAB}${CL}" -RAMSIZE="${TAB}🛠️${TAB}${CL}" -CONTAINERID="${TAB}🆔${TAB}${CL}" -HOSTNAME="${TAB}🏠${TAB}${CL}" -BRIDGE="${TAB}🌉${TAB}${CL}" -GATEWAY="${TAB}🌐${TAB}${CL}" -DEFAULT="${TAB}⚙️${TAB}${CL}" -MACADDRESS="${TAB}🔗${TAB}${CL}" -VLANTAG="${TAB}🏷️${TAB}${CL}" -CREATING="${TAB}🚀${TAB}${CL}" -ADVANCED="${TAB}🧩${TAB}${CL}" -CLOUD="${TAB}☁️${TAB}${CL}" - -THIN="discard=on,ssd=1," +header_info; echo -e "\n Loading..." +# ---- Fehler-/Aufräum-Handling ------------------------------------------------ trap 'error_handler $LINENO "$BASH_COMMAND"' ERR -trap cleanup EXIT +trap 'cleanup' EXIT trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM -function error_handler() { - local exit_code="$?" - local line_number="$1" - local command="$2" - local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}" - post_update_to_api "failed" "${command}" - echo -e "\n$error_message\n" - cleanup_vmid +error_handler() { + local ec=$? ln="$1" cmd="$2" + msg_error "in line ${ln}: exit code ${ec}: while executing: ${YW}${cmd}${CL}" + post_update_to_api "failed" "${cmd}" + cleanup_vmid || true + exit "$ec" } -function get_valid_nextid() { - local try_id - try_id=$(pvesh get /cluster/nextid) - while true; do - if [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; then - try_id=$((try_id + 1)) - continue - fi - if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${try_id}($|[-_])"; then - try_id=$((try_id + 1)) - continue - fi - break - done - echo "$try_id" -} - -function cleanup_vmid() { - if qm status $VMID &>/dev/null; then - qm stop $VMID &>/dev/null || true - qm destroy $VMID &>/dev/null || true +cleanup_vmid() { + if [[ -n "${VMID:-}" ]] && qm status "$VMID" &>/dev/null; then + qm stop "$VMID" &>/dev/null || true + qm destroy "$VMID" &>/dev/null || true fi } -function cleanup() { - popd >/dev/null || true - post_update_to_api "done" "none" +TEMP_DIR="$(mktemp -d)" +cleanup() { + popd >/dev/null 2>&1 || true rm -rf "$TEMP_DIR" + post_update_to_api "done" "none" } -TEMP_DIR=$(mktemp -d) pushd "$TEMP_DIR" >/dev/null -if ! whiptail --backtitle "Proxmox VE Helper Scripts" --title "Docker VM" --yesno "This will create a New Docker VM. Proceed?" 10 58; then - header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit -fi - -function msg_info() { echo -ne "${TAB}${YW}${HOLD}$1${HOLD}"; } -function msg_ok() { echo -e "${BFR}${CM}${GN}$1${CL}"; } -function msg_error() { echo -e "${BFR}${CROSS}${RD}$1${CL}"; } - -function check_root() { - if [[ "$(id -u)" -ne 0 || $(ps -o comm= -p $PPID) == "sudo" ]]; then - clear - msg_error "Please run this script as root." - echo -e "\nExiting..." - sleep 2 - exit - fi -} - -# Supported: Proxmox VE 8.0.x – 8.9.x and 9.0 (NOT 9.1+) -pve_check() { - local PVE_VER - PVE_VER="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" - if [[ "$PVE_VER" =~ ^8\.([0-9]+) ]]; then - local MINOR="${BASH_REMATCH[1]}" - ((MINOR >= 0 && MINOR <= 9)) && return 0 - msg_error "This version of Proxmox VE is not supported." - exit 1 - fi - if [[ "$PVE_VER" =~ ^9\.([0-9]+) ]]; then - local MINOR="${BASH_REMATCH[1]}" - ((MINOR == 0)) && return 0 - msg_error "This version of Proxmox VE is not yet supported (9.1+)." - exit 1 - fi - msg_error "This version of Proxmox VE is not supported (need 8.x or 9.0)." - exit 1 -} - -function arch_check() { - if [ "$(dpkg --print-architecture)" != "amd64" ]; then - echo -e "\n ${INFO}This script will not work with PiMox! \n" - echo -e "\n Visit https://github.com/asylumexp/Proxmox for ARM64 support. \n" - echo -e "Exiting..." - sleep 2 - exit - fi -} - -function ssh_check() { - if command -v pveversion >/dev/null 2>&1 && [ -n "${SSH_CLIENT:+x}" ]; then - if whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's suggested to use the Proxmox shell instead of SSH, since SSH can create issues while gathering variables. Proceed anyway?" 10 62; then :; else - clear - exit +# ---- Sanity Checks ----------------------------------------------------------- +check_root() { if [[ "$(id -u)" -ne 0 ]]; then msg_error "Run as root."; exit 1; fi; } +arch_check() { [[ "$(dpkg --print-architecture)" = "amd64" ]] || { msg_error "ARM/PiMox nicht unterstützt."; exit 1; }; } +ssh_check() { + if command -v pveversion >/dev/null 2>&1 && [[ -n "${SSH_CLIENT:+x}" ]]; then + if ! whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" \ + --yesno "Nutze besser die Proxmox Shell (Konsole) – SSH kann Variablen-Ermittlung stören. Trotzdem fortfahren?" 10 70; then + clear; exit 1 fi fi } - -function exit-script() { - clear - echo -e "\n${CROSS}${RD}User exited script${CL}\n" - exit +pve_check() { + local ver; ver="$(pveversion | awk -F'/' '{print $2}' | cut -d'-' -f1)" + case "$ver" in + 8.*|9.*) : ;; + *) msg_error "Unsupported Proxmox VE: ${ver} (need 8.x or 9.x)"; exit 1 ;; + esac } -function default_settings() { - VMID=$(get_valid_nextid) - FORMAT=",efitype=4m" - DISK_CACHE="" - DISK_SIZE="10G" - HN="docker" - CPU_TYPE="" - CORE_COUNT="2" - RAM_SIZE="4096" - BRG="vmbr0" - MAC="$GEN_MAC" - VLAN="" - MTU="" - START_VM="yes" - METHOD="default" - echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}q35${CL}" - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}" - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" - echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}" - echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}" - echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}" - echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}" - echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}" - echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}" - echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" +check_root; arch_check; pve_check; ssh_check + +# ---- Defaults / UI Vorbelegung ---------------------------------------------- +GEN_MAC="02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/:$//')" +RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" +NSAPP="docker-vm" +THIN="discard=on,ssd=1," +FORMAT=",efitype=4m" +DISK_CACHE="" +DISK_SIZE="10G" +HN="docker" +CPU_TYPE="" +CORE_COUNT="2" +RAM_SIZE="4096" +BRG="vmbr0" +MAC="$GEN_MAC" +VLAN="" +MTU="" +START_VM="yes" +METHOD="default" +var_os="debian" +var_version="12" + +# ---- Startdialog ------------------------------------------------------------- +if ! whiptail --backtitle "Proxmox VE Helper Scripts" --title "Docker VM" \ + --yesno "This will create a NEW Docker VM. Proceed?" 10 58; then + header_info; echo -e "${CROSS}${RD}User exited script${CL}\n"; exit 1 +fi + +# ---- Helper: VMID-Find ------------------------------------------------------- +get_valid_nextid() { + local id; id=$(pvesh get /cluster/nextid) + while :; do + if [[ -f "/etc/pve/qemu-server/${id}.conf" || -f "/etc/pve/lxc/${id}.conf" ]]; then id=$((id+1)); continue; fi + if lvs --noheadings -o lv_name | grep -qE "(^|[-_])${id}($|[-_])"; then id=$((id+1)); continue; fi + break + done + echo "$id" +} + +# ---- Msg Wrapper ------------------------------------------------------------- +exit-script() { clear; echo -e "\n${CROSS}${RD}User exited script${CL}\n"; exit 1; } + +default_settings() { + VMID="$(get_valid_nextid)" + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${GN}${VMID}${CL}" + echo -e "${OSI}${BOLD}${DGN}CPU Model: ${GN}KVM64${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${GN}${CORE_COUNT}${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${GN}${RAM_SIZE}${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${GN}${DISK_SIZE}${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${GN}None${CL}" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${GN}${HN}${CL}" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${GN}${BRG}${CL}" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${GN}${MAC}${CL}" + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${GN}Default${CL}" + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${GN}Default${CL}" + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${GN}yes${CL}" echo -e "${CREATING}${BOLD}${DGN}Creating a Docker VM using the above default settings${CL}" } -function advanced_settings() { +advanced_settings() { METHOD="advanced" - [ -z "${VMID:-}" ] && VMID=$(get_valid_nextid) + [[ -z "${VMID:-}" ]] && VMID="$(get_valid_nextid)" while true; do - if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - [ -z "$VMID" ] && VMID=$(get_valid_nextid) + if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 "$VMID" \ + --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + [[ -z "$VMID" ]] && VMID="$(get_valid_nextid)" if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then - echo -e "${CROSS}${RD} ID $VMID is already in use${CL}" - sleep 2 - continue + echo -e "${CROSS}${RD} ID $VMID is already in use${CL}"; sleep 1.5; continue fi - echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" + echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${GN}$VMID${CL}" break else exit-script; fi done - FORMAT=",efitype=4m" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}q35${CL}" + echo -e "${OSI}${BOLD}${DGN}Machine Type: ${GN}q35${CL}" - if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ') - if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then DISK_SIZE="${DISK_SIZE}G"; fi - [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]] || { - echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size.${CL}" - exit-script - } - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}" + if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" \ + --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + DISK_SIZE="$(echo "$DISK_SIZE" | tr -d ' ')"; [[ "$DISK_SIZE" =~ ^[0-9]+$ ]] && DISK_SIZE="${DISK_SIZE}G" + [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]] || { msg_error "Invalid Disk Size"; exit-script; } + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${GN}$DISK_SIZE${CL}" else exit-script; fi - if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ - "0" "None (Default)" ON "1" "Write Through" OFF 3>&1 1>&2 2>&3); then - if [ "$DISK_CACHE" = "1" ]; then - DISK_CACHE="cache=writethrough," - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}" - else - DISK_CACHE="" - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}" + if DISK_CACHE_SEL=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" \ + --radiolist "Choose" --cancel-button Exit-Script 10 58 2 "0" "None (Default)" ON "1" "Write Through" OFF \ + 3>&1 1>&2 2>&3); then + if [[ "$DISK_CACHE_SEL" = "1" ]]; then DISK_CACHE="cache=writethrough,"; echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${GN}Write Through${CL}" + else DISK_CACHE=""; echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${GN}None${CL}" fi else exit-script; fi - if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 docker --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - if [ -z "$VM_NAME" ]; then HN="docker"; else HN=$(echo ${VM_NAME,,} | tr -d ' '); fi - echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" + if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 "$HN" \ + --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + [[ -z "$VM_NAME" ]] && VM_NAME="docker"; HN="$(echo "${VM_NAME,,}" | tr -d ' ')" + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${GN}$HN${CL}" else exit-script; fi - if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ - "0" "KVM64 (Default)" ON "1" "Host" OFF 3>&1 1>&2 2>&3); then - if [ "$CPU_TYPE1" = "1" ]; then - CPU_TYPE=" -cpu host" - echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}" - else - CPU_TYPE="" - echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}" + if CPU_TYPE_SEL=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" \ + --radiolist "Choose" --cancel-button Exit-Script 10 58 2 "0" "KVM64 (Default)" ON "1" "Host" OFF \ + 3>&1 1>&2 2>&3); then + if [[ "$CPU_TYPE_SEL" = "1" ]]; then CPU_TYPE=" -cpu host"; echo -e "${OSI}${BOLD}${DGN}CPU Model: ${GN}Host${CL}" + else CPU_TYPE=""; echo -e "${OSI}${BOLD}${DGN}CPU Model: ${GN}KVM64${CL}" fi else exit-script; fi - if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - [ -z "$CORE_COUNT" ] && CORE_COUNT="2" - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate CPU Cores" 8 58 "$CORE_COUNT" \ + --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + [[ -z "$CORE_COUNT" ]] && CORE_COUNT="2" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${GN}$CORE_COUNT${CL}" else exit-script; fi - if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 2048 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - [ -z "$RAM_SIZE" ] && RAM_SIZE="2048" - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}" + if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Allocate RAM in MiB" 8 58 "$RAM_SIZE" \ + --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + [[ -z "$RAM_SIZE" ]] && RAM_SIZE="2048" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${GN}$RAM_SIZE${CL}" else exit-script; fi - if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - [ -z "$BRG" ] && BRG="vmbr0" - echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Bridge" 8 58 "$BRG" \ + --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + [[ -z "$BRG" ]] && BRG="vmbr0" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${GN}$BRG${CL}" else exit-script; fi - if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - if [ -z "$MAC1" ]; then MAC="$GEN_MAC"; else MAC="$MAC1"; fi - echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}" + if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a MAC Address" 8 58 "$MAC" \ + --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + [[ -z "$MAC1" ]] && MAC1="$GEN_MAC"; MAC="$MAC1" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${GN}$MAC${CL}" else exit-script; fi - if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - if [ -z "$VLAN1" ]; then - VLAN1="Default" - VLAN="" - else VLAN=",tag=$VLAN1"; fi - echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}" + if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set VLAN (blank = default)" 8 58 "" \ + --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [[ -z "$VLAN1" ]]; then VLAN1="Default"; VLAN=""; else VLAN=",tag=$VLAN1"; fi + echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${GN}$VLAN1${CL}" else exit-script; fi - if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then - if [ -z "$MTU1" ]; then - MTU1="Default" - MTU="" - else MTU=",mtu=$MTU1"; fi - echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" + if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Interface MTU Size (blank = default)" 8 58 "" \ + --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [[ -z "$MTU1" ]]; then MTU1="Default"; MTU=""; else MTU=",mtu=$MTU1"; fi + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${GN}$MTU1${CL}" else exit-script; fi - if whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58; then - echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}" - START_VM="yes" + if whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" \ + --yesno "Start VM when completed?" 10 58; then START_VM="yes"; else START_VM="no"; fi + echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${GN}${START_VM}${CL}" + + if ! whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" \ + --yesno "Ready to create a Docker VM?" --no-button Do-Over 10 58; then + header_info; echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}"; advanced_settings else - echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}" - START_VM="no" - fi - - if whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Docker VM?" --no-button Do-Over 10 58; then echo -e "${CREATING}${BOLD}${DGN}Creating a Docker VM using the above advanced settings${CL}" - else - header_info - echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" - advanced_settings fi } -function start_script() { - if whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58; then - header_info - echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}" - default_settings +start_script() { + if whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" \ + --yesno "Use Default Settings?" --no-button Advanced 10 58; then + header_info; echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}"; default_settings else - header_info - echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}" - advanced_settings + header_info; echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}"; advanced_settings fi } -check_root -arch_check -pve_check -ssh_check -start_script -post_to_api_vm +start_script; post_to_api_vm -function choose_os() { - if OS_CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ - --title "Choose Base OS" \ - --radiolist "Select the OS for the Docker VM:" 12 60 3 \ - "debian12" "Debian 12 (Bookworm, stable & best for scripts)" ON \ - "debian13" "Debian 13 (Trixie, newer, but repos lag)" OFF \ - "ubuntu24" "Ubuntu 24.04 LTS (modern kernel, GPU/AI friendly)" OFF \ - 3>&1 1>&2 2>&3); then +# ---- OS Auswahl -------------------------------------------------------------- +choose_os() { + local OS_CHOICE + if OS_CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Choose Base OS" --radiolist \ + "Select the OS for the Docker VM:" 12 60 3 \ + "debian12" "Debian 12 (Bookworm, stable & best for scripts)" ON \ + "debian13" "Debian 13 (Trixie, newer, but repos lag)" OFF \ + "ubuntu24" "Ubuntu 24.04 LTS (modern kernel, GPU/AI friendly)" OFF \ + 3>&1 1>&2 2>&3); then case "$OS_CHOICE" in - debian12) - var_os="debian" - var_version="12" - URL="https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-nocloud-$(dpkg --print-architecture).qcow2" - ;; - debian13) - var_os="debian" - var_version="13" - URL="https://cloud.debian.org/images/cloud/trixie/latest/debian-13-nocloud-$(dpkg --print-architecture).qcow2" - ;; - ubuntu24) - var_os="ubuntu" - var_version="24.04" - URL="https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-$(dpkg --print-architecture).img" - ;; + debian12) var_os="debian"; var_version="12"; URL="https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-nocloud-$(dpkg --print-architecture).qcow2" ;; + debian13) var_os="debian"; var_version="13"; URL="https://cloud.debian.org/images/cloud/trixie/latest/debian-13-nocloud-$(dpkg --print-architecture).qcow2" ;; + ubuntu24) var_os="ubuntu"; var_version="24.04"; URL="https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-$(dpkg --print-architecture).img" ;; esac - echo -e "${OS}${BOLD}${DGN}Selected OS: ${BGN}${OS_CHOICE}${CL}" + echo -e "${OSI}${BOLD}${DGN}Selected OS: ${GN}${OS_CHOICE}${CL}" else exit-script fi } -PVE_VER=$(pveversion | awk -F'/' '{print $2}' | cut -d'-' -f1 | cut -d'.' -f1) -if [ "$PVE_VER" -eq 8 ]; then - INSTALL_MODE="direct" -elif [ "$PVE_VER" -eq 9 ]; then - INSTALL_MODE="firstboot" -else - msg_error "Unsupported Proxmox VE version: $PVE_VER" - exit 1 +# ---- PVE Version + Install-Mode (einmalig) ----------------------------------- +PVE_MAJ="$(pveversion | awk -F'/' '{print $2}' | cut -d'-' -f1 | cut -d'.' -f1)" +case "$PVE_MAJ" in + 8) INSTALL_MODE="direct" ;; + 9) INSTALL_MODE="cloudinit" ;; + *) msg_error "Unsupported Proxmox VE major: $PVE_MAJ (need 8 or 9)"; exit 1 ;; +esac + +# Optionaler Override (einmalig) +if ! whiptail --backtitle "Proxmox VE Helper Scripts" --title "Docker Installation Mode" --yesno \ + "Detected PVE ${PVE_MAJ}. Use ${INSTALL_MODE^^} mode?\n\nYes = ${INSTALL_MODE^^}\nNo = Switch to the other mode" 11 70; then + INSTALL_MODE=$([ "$INSTALL_MODE" = "direct" ] && echo cloudinit || echo direct) fi -# ---------- Storage selection ---------- +# ---- Storage Auswahl --------------------------------------------------------- msg_info "Validating Storage" +STORAGE_MENU=(); MSG_MAX_LENGTH=0 while read -r line; do - TAG=$(echo $line | awk '{print $1}') - TYPE=$(echo $line | awk '{printf "%-10s", $2}') - FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') + TAG=$(echo "$line" | awk '{print $1}') + TYPE=$(echo "$line" | awk '{printf "%-10s", $2}') + FREE=$(echo "$line" | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') ITEM=" Type: $TYPE Free: $FREE " - OFFSET=2 - if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then - MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) - fi + (( ${#ITEM} + 2 > MSG_MAX_LENGTH )) && MSG_MAX_LENGTH=${#ITEM}+2 STORAGE_MENU+=("$TAG" "$ITEM" "OFF") done < <(pvesm status -content images | awk 'NR>1') + VALID=$(pvesm status -content images | awk 'NR>1') -if [ -z "$VALID" ]; then - msg_error "Unable to detect a valid storage location." - exit -elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then +if [[ -z "$VALID" ]]; then + msg_error "No valid storage with content=images found." + exit 1 +elif (( ${#STORAGE_MENU[@]} / 3 == 1 )); then STORAGE=${STORAGE_MENU[0]} else - while [ -z "${STORAGE:+x}" ]; do + while [[ -z "${STORAGE:+x}" ]]; do STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ - "Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \ - 16 $(($MSG_MAX_LENGTH + 23)) 6 \ - "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) + "Which storage pool for ${HN}?\n(Use Spacebar to select)" \ + 16 $((MSG_MAX_LENGTH + 23)) 6 "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) done fi -msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." -msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." +msg_ok "Using ${BL}${STORAGE}${CL} for Storage" +msg_ok "Virtual Machine ID is ${BL}${VMID}${CL}" -# ---------- Download Cloud Image ---------- +# ---- Cloud Image Download ---------------------------------------------------- choose_os msg_info "Retrieving Cloud Image for $var_os $var_version" curl --retry 30 --retry-delay 3 --retry-connrefused -fSL -o "$(basename "$URL")" "$URL" -FILE=$(basename "$URL") -msg_ok "Downloaded ${CL}${BL}${FILE}${CL}" +FILE="$(basename "$URL")" +msg_ok "Downloaded ${BL}${FILE}${CL}" # Ubuntu RAW → qcow2 if [[ "$FILE" == *.img ]]; then @@ -418,241 +332,168 @@ if [[ "$FILE" == *.img ]]; then qemu-img convert -O qcow2 "$FILE" "${FILE%.img}.qcow2" rm -f "$FILE" FILE="${FILE%.img}.qcow2" - msg_ok "Converted to ${CL}${BL}${FILE}${CL}" + msg_ok "Converted to ${BL}${FILE}${CL}" fi -# ---------- Ensure libguestfs-tools ---------- -INSTALL_MODE="direct" -if ! command -v virt-customize >/dev/null 2>&1; then - msg_info "Installing libguestfs-tools" - apt-get -qq update >/dev/null - apt-get -qq install -y libguestfs-tools >/dev/null - msg_ok "Installed libguestfs-tools" -fi +# ---- Codename & Docker-Repo (einmalig) --------------------------------------- +detect_codename_and_repo() { + if [[ "$URL" == *"/bookworm/"* || "$FILE" == *"debian-12-"* ]]; then + CODENAME="bookworm"; DOCKER_BASE="https://download.docker.com/linux/debian" + elif [[ "$URL" == *"/trixie/"* || "$FILE" == *"debian-13-"* ]]; then + CODENAME="trixie"; DOCKER_BASE="https://download.docker.com/linux/debian" + elif [[ "$URL" == *"/noble/"* || "$FILE" == *"noble-"* ]]; then + CODENAME="noble"; DOCKER_BASE="https://download.docker.com/linux/ubuntu" + else + CODENAME="bookworm"; DOCKER_BASE="https://download.docker.com/linux/debian" + fi + REPO_CODENAME="$CODENAME" + if [[ "$DOCKER_BASE" == *"linux/debian"* && "$CODENAME" == "trixie" ]]; then + REPO_CODENAME="bookworm" + fi +} +detect_codename_and_repo -# Some PVE9 nodes need this for guestfs -export LIBGUESTFS_BACKEND=direct - -# ---------- Decide distro codename & Docker repo base ---------- -if [[ "$URL" == *"/bookworm/"* || "$FILE" == *"debian-12-"* ]]; then - CODENAME="bookworm" - DOCKER_BASE="https://download.docker.com/linux/debian" -elif [[ "$URL" == *"/trixie/"* || "$FILE" == *"debian-13-"* ]]; then - CODENAME="trixie" - DOCKER_BASE="https://download.docker.com/linux/debian" -elif [[ "$URL" == *"/noble/"* || "$FILE" == *"noble-"* ]]; then - CODENAME="noble" - DOCKER_BASE="https://download.docker.com/linux/ubuntu" -else - CODENAME="bookworm" - DOCKER_BASE="https://download.docker.com/linux/debian" -fi -# Map Debian trixie → bookworm (Docker-Repo oft später) -REPO_CODENAME="$CODENAME" -if [[ "$DOCKER_BASE" == *"linux/debian"* && "$CODENAME" == "trixie" ]]; then - REPO_CODENAME="bookworm" -fi - -# ---------- Detect PVE major version (again; independent var) ---------- -PVE_MAJ=$(pveversion | awk -F'/' '{print $2}' | cut -d'-' -f1 | cut -d'.' -f1) -if [ "$PVE_MAJ" -eq 8 ]; then INSTALL_MODE="direct"; else INSTALL_MODE="firstboot"; fi - -# ---------- Optional: allow manual override ---------- -if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Docker Installation Mode" \ - --yesno "Detected PVE ${PVE_MAJ}. Use ${INSTALL_MODE^^} mode?\n\nYes = ${INSTALL_MODE^^}\nNo = Switch to the other mode" 11 70; then :; else - if [ "$INSTALL_MODE" = "direct" ]; then INSTALL_MODE="firstboot"; else INSTALL_MODE="direct"; fi -fi - -# ---------- PVE8: Direct install into image via virt-customize ---------- -if [ "$INSTALL_MODE" = "direct" ]; then - msg_info "Injecting Docker & QGA into image (${CODENAME}, repo base: $(basename "$DOCKER_BASE"))" - # robust retry wrapper +# ---- PVE8: direct inject via virt-customize ---------------------------------- +if [[ "$INSTALL_MODE" = "direct" ]]; then + msg_info "Injecting Docker & QGA into image (${CODENAME}, repo: $(basename "$DOCKER_BASE"))" + export LIBGUESTFS_BACKEND=direct + if ! command -v virt-customize >/dev/null 2>&1; then + apt-get -qq update >/dev/null + apt-get -qq install -y libguestfs-tools >/dev/null + fi vrun() { virt-customize -q -a "${FILE}" "$@" >/dev/null; } - vrun \ - --install qemu-guest-agent,apt-transport-https,ca-certificates,curl,gnupg,lsb-release \ + --install qemu-guest-agent,ca-certificates,curl,gnupg,lsb-release,apt-transport-https \ --run-command "install -m 0755 -d /etc/apt/keyrings" \ --run-command "curl -fsSL ${DOCKER_BASE}/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg" \ --run-command "chmod a+r /etc/apt/keyrings/docker.gpg" \ --run-command "echo 'deb [arch=\$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] ${DOCKER_BASE} ${REPO_CODENAME} stable' > /etc/apt/sources.list.d/docker.list" \ --run-command "apt-get update -qq" \ --run-command "apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin" \ - --run-command "systemctl enable docker qemu-guest-agent" - - # PATH / login.defs Korrekturen - vrun \ + --run-command "systemctl enable docker qemu-guest-agent" \ --run-command "sed -i 's#^ENV_SUPATH.*#ENV_SUPATH PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin#' /etc/login.defs || true" \ --run-command "sed -i 's#^ENV_PATH.*#ENV_PATH PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin#' /etc/login.defs || true" \ --run-command "printf 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\n' >/etc/environment" \ --run-command "grep -q 'export PATH=' /root/.bashrc || echo 'export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' >> /root/.bashrc" - msg_ok "Docker & QGA injected" fi -# ---------- PVE9: First-boot installer inside guest ---------- -if [ "$INSTALL_MODE" = "firstboot" ]; then - msg_info "Preparing first-boot Docker installer (${CODENAME}, $(basename "$DOCKER_BASE"))" - mkdir -p firstboot - cat >firstboot/firstboot-docker.sh <<'EOSH' -#!/usr/bin/env bash -set -euxo pipefail +# ---- PVE9: Cloud-Init Snippet (NoCloud) -------------------------------------- +if [[ "$INSTALL_MODE" = "cloudinit" ]]; then + msg_info "Preparing Cloud-Init user-data for Docker (${CODENAME})" -LOG=/var/log/firstboot-docker.log -exec >>"$LOG" 2>&1 + # Prüfen, ob local Snippets erlaubt + if ! pvesm status | awk '$1=="local" && $2 ~ /snippets/ {ok=1} END{exit !ok}'; then + msg_error "Storage 'local' must allow content 'Snippets'. Enable it in Datacenter → Storage → local." + exit 1 + fi -mark_done() { mkdir -p /var/lib/firstboot; date > /var/lib/firstboot/docker.done; } -retry() { local t=$1; shift; local n=0; until "$@"; do n=$((n+1)); [ "$n" -ge "$t" ] && return 1; sleep 5; done; } + SNIPPET="/var/lib/vz/snippets/docker-${VMID}-user-data.yaml" + # Docker GPG in base64 + DOCKER_GPG_B64="$(curl -fsSL "${DOCKER_BASE}/gpg" | gpg --dearmor | base64 -w0)" -wait_network() { - retry 60 getent hosts deb.debian.org || retry 60 getent hosts archive.ubuntu.com - retry 60 bash -lc 'curl -fsS https://download.docker.com/ >/dev/null' -} + cat >"$SNIPPET" </etc/environment - grep -q 'export PATH=' /root/.bashrc || echo 'export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' >> /root/.bashrc - export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin -} +package_update: true +package_upgrade: false +packages: + - ca-certificates + - curl + - gnupg + - qemu-guest-agent + - cloud-guest-utils # growpart/resizefs -main() { - export DEBIAN_FRONTEND=noninteractive - mkdir -p /etc/apt/apt.conf.d - printf 'Acquire::Retries "10";\nAcquire::http::Timeout "60";\nAcquire::https::Timeout "60";\n' >/etc/apt/apt.conf.d/80-retries-timeouts +write_files: + - path: /etc/apt/keyrings/docker.gpg + permissions: '0644' + encoding: b64 + content: ${DOCKER_GPG_B64} - wait_network +runcmd: + - sed -i 's#^ENV_SUPATH.*#ENV_SUPATH PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin#' /etc/login.defs || true + - sed -i 's#^ENV_PATH.*#ENV_PATH PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin#' /etc/login.defs || true + - printf 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\n' > /etc/environment + - "grep -q 'export PATH=' /root/.bashrc || echo 'export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' >> /root/.bashrc" - . /etc/os-release - CODENAME="${VERSION_CODENAME:-bookworm}" - case "$ID" in - ubuntu) DOCKER_BASE="https://download.docker.com/linux/ubuntu" ;; - debian|*) DOCKER_BASE="https://download.docker.com/linux/debian" ;; - esac - REPO_CODENAME="$CODENAME" - if [ "$ID" = "debian" ] && [ "$CODENAME" = "trixie" ]; then REPO_CODENAME="bookworm"; fi + - install -m 0755 -d /etc/apt/keyrings + - echo "deb [arch=\$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] ${DOCKER_BASE} ${REPO_CODENAME} stable" > /etc/apt/sources.list.d/docker.list - retry 20 apt-get update -qq - retry 10 apt-get install -y ca-certificates curl gnupg qemu-guest-agent apt-transport-https lsb-release software-properties-common + - apt-get update -qq + - apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin - install -m 0755 -d /etc/apt/keyrings - curl -fsSL "${DOCKER_BASE}/gpg" | gpg --dearmor -o /etc/apt/keyrings/docker.gpg - chmod a+r /etc/apt/keyrings/docker.gpg - echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] ${DOCKER_BASE} ${REPO_CODENAME} stable" > /etc/apt/sources.list.d/docker.list + - systemctl enable --now qemu-guest-agent + - systemctl enable --now docker - retry 20 apt-get update -qq - retry 10 apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin +growpart: + mode: auto + devices: ['/'] + ignore_growroot_disabled: false - systemctl enable --now qemu-guest-agent || true - systemctl enable --now docker +fs_resize: true - fix_path +power_state: + mode: reboot + condition: true +EOYAML - command -v docker >/dev/null - systemctl is-active --quiet docker - - mark_done -} -main -EOSH - chmod +x firstboot/firstboot-docker.sh - - cat >firstboot/firstboot-docker.service <<'EOUNIT' -[Unit] -Description=First boot: install Docker & QGA -After=network-online.target cloud-init.service -Wants=network-online.target -ConditionPathExists=!/var/lib/firstboot/docker.done -StartLimitIntervalSec=0 - -[Service] -Type=oneshot -ExecStart=/usr/local/sbin/firstboot-docker.sh -Restart=on-failure -RestartSec=10s -TimeoutStartSec=0 -RemainAfterExit=no - -[Install] -WantedBy=multi-user.target -EOUNIT - - echo "$HN" >firstboot/hostname - - virt-customize -q -a "${FILE}" \ - --copy-in firstboot/firstboot-docker.sh:/usr/local/sbin \ - --copy-in firstboot/firstboot-docker.service:/etc/systemd/system \ - --copy-in firstboot/hostname:/etc \ - --run-command "chmod +x /usr/local/sbin/firstboot-docker.sh" \ - --run-command "systemctl enable firstboot-docker.service" \ - --run-command "echo -n > /etc/machine-id" \ - --run-command "truncate -s 0 /etc/hostname && mv /etc/hostname /etc/hostname.orig && echo '${HN}' >/etc/hostname" >/dev/null - - msg_ok "First-boot Docker installer injected" + chmod 0644 "$SNIPPET" + msg_ok "Cloud-Init user-data written: ${SNIPPET}" fi -# ---------- Expand partition offline ---------- -msg_info "Expanding root partition to use full disk space" -qemu-img create -f qcow2 expanded.qcow2 ${DISK_SIZE} >/dev/null 2>&1 -virt-resize --expand /dev/sda1 ${FILE} expanded.qcow2 >/dev/null 2>&1 -mv expanded.qcow2 ${FILE} >/dev/null 2>&1 -msg_ok "Expanded image to full size" - -# ---------- Create VM shell (q35) ---------- +# ---- VM erstellen (q35) ------------------------------------------------------ msg_info "Creating a Docker VM shell" qm create "$VMID" -machine q35 -bios ovmf -agent 1 -tablet 0 -localtime 1 ${CPU_TYPE} \ -cores "$CORE_COUNT" -memory "$RAM_SIZE" -name "$HN" -tags community-script \ -net0 "virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU" -onboot 1 -ostype l26 -scsihw virtio-scsi-pci >/dev/null msg_ok "Created VM shell" -# ---------- Import disk ---------- +# ---- Disk importieren -------------------------------------------------------- msg_info "Importing disk into storage ($STORAGE)" if qm disk import --help >/dev/null 2>&1; then IMPORT_CMD=(qm disk import); else IMPORT_CMD=(qm importdisk); fi IMPORT_OUT="$("${IMPORT_CMD[@]}" "$VMID" "${FILE}" "$STORAGE" --format qcow2 2>&1 || true)" DISK_REF="$(printf '%s\n' "$IMPORT_OUT" | sed -n "s/.*successfully imported disk '\([^']\+\)'.*/\1/p" | tr -d "\r\"'")" [[ -z "$DISK_REF" ]] && DISK_REF="$(pvesm list "$STORAGE" | awk -v id="$VMID" '$5 ~ ("vm-"id"-disk-") {print $1":"$5}' | sort | tail -n1)" -[[ -z "$DISK_REF" ]] && { - msg_error "Unable to determine imported disk reference." - echo "$IMPORT_OUT" - exit 1 -} -msg_ok "Imported disk (${CL}${BL}${DISK_REF}${CL})" +[[ -z "$DISK_REF" ]] && { msg_error "Unable to determine imported disk reference."; echo "$IMPORT_OUT"; exit 1; } +msg_ok "Imported disk (${BL}${DISK_REF}${CL})" -# ---------- Attach EFI + root disk ---------- -msg_info "Attaching EFI and root disk" -qm set "$VMID" \ - --efidisk0 "${STORAGE}:0${FORMAT}" \ - --scsi0 "${DISK_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE}" \ - --boot order=scsi0 \ - --serial0 socket >/dev/null +# ---- EFI + Root + Cloud-Init anhängen --------------------------------------- +msg_info "Attaching EFI/root disk and Cloud-Init" +qm set "$VMID" --efidisk0 "${STORAGE}:0${FORMAT}" >/dev/null +qm set "$VMID" --scsi0 "${DISK_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE}" >/dev/null +qm set "$VMID" --boot order=scsi0 >/dev/null +qm set "$VMID" --serial0 socket >/dev/null qm set "$VMID" --agent enabled=1,fstrim_cloned_disks=1 >/dev/null qm set "$VMID" --ide2 "${STORAGE}:cloudinit" >/dev/null -qm set "$VMID" --ciuser root --cipassword '' --sshkeys "/root/.ssh/authorized_keys" >/dev/null || true qm set "$VMID" --ipconfig0 "ip=dhcp" >/dev/null qm set "$VMID" --nameserver "1.1.1.1 9.9.9.9" --searchdomain "lan" >/dev/null -msg_ok "Attached EFI and root disk" +qm set "$VMID" --ciuser root --cipassword '' --sshkeys "/root/.ssh/authorized_keys" >/dev/null || true -# ---------- Ensure final size (PVE layer) ---------- +if [[ "$INSTALL_MODE" = "cloudinit" ]]; then + qm set "$VMID" --cicustom "user=local:snippets/$(basename "$SNIPPET")" >/dev/null +fi +msg_ok "Attached EFI/root and Cloud-Init" + +# ---- Disk auf Zielgröße im PVE-Layer (Cloud-Init wächst FS) ------------------ msg_info "Resizing disk to $DISK_SIZE (PVE layer)" qm resize "$VMID" scsi0 "${DISK_SIZE}" >/dev/null || true msg_ok "Resized disk" -# ---------- Description ---------- +# ---- Beschreibung ------------------------------------------------------------ DESCRIPTION=$( - cat < Logo -

Docker VM

-

spend Coffee

- GitHub @@ -669,14 +510,21 @@ DESCRIPTION=$( EOF ) qm set "$VMID" -description "$DESCRIPTION" >/dev/null +msg_ok "Created a Docker VM ${BL}(${HN})${CL}" -msg_ok "Created a Docker VM ${CL}${BL}(${HN})" - -if [ "$START_VM" == "yes" ]; then +# ---- Start ------------------------------------------------------------------- +if [[ "$START_VM" == "yes" ]]; then msg_info "Starting Docker VM" - qm start $VMID + qm start "$VMID" msg_ok "Started Docker VM" fi post_update_to_api "done" "none" msg_ok "Completed Successfully!\n" + +# ---- Hinweise/Debug (Cloud-Init) -------------------------------------------- +# In der VM prüfen: +# journalctl -u cloud-init -b +# cat /var/log/cloud-init.log +# cat /var/log/cloud-init-output.log +# cloud-init status --long From 0850cf512079cbae05d204a8116014fb1abcaf91 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 30 Sep 2025 13:44:35 +0200 Subject: [PATCH 1235/1733] Update docker-vm.sh --- vm/docker-vm.sh | 56 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/vm/docker-vm.sh b/vm/docker-vm.sh index dd880f505..8f317d0fe 100644 --- a/vm/docker-vm.sh +++ b/vm/docker-vm.sh @@ -255,6 +255,42 @@ start_script() { fi } +# ---------- Cloud-Init Snippet-Storage ermitteln ---------- +pick_snippet_storage() { + # Liefert in SNIPPET_STORE und SNIPPET_DIR zurück + mapfile -t SNIPPET_STORES < <(pvesm status -content snippets | awk 'NR>1 {print $1}') + + _store_snippets_dir() { + local store="$1" + local p; p="$(pvesm path "$store" 2>/dev/null || true)" + [[ -n "$p" ]] || return 1 + echo "$p/snippets" + } + + # 1) Gewählter Storage selbst + if printf '%s\n' "${SNIPPET_STORES[@]}" | grep -qx -- "$STORAGE"; then + SNIPPET_STORE="$STORAGE" + SNIPPET_DIR="$(_store_snippets_dir "$STORAGE")" || return 1 + return 0 + fi + + # 2) Fallback: "local" + if printf '%s\n' "${SNIPPET_STORES[@]}" | grep -qx -- "local"; then + SNIPPET_STORE="local" + SNIPPET_DIR="$(_store_snippets_dir local)" || true + [[ -n "$SNIPPET_DIR" ]] && return 0 + fi + + # 3) Irgendein anderer + for s in "${SNIPPET_STORES[@]}"; do + SNIPPET_DIR="$(_store_snippets_dir "$s")" || continue + SNIPPET_STORE="$s" + return 0 + done + + return 1 +} + start_script; post_to_api_vm # ---- OS Auswahl -------------------------------------------------------------- @@ -382,17 +418,19 @@ fi if [[ "$INSTALL_MODE" = "cloudinit" ]]; then msg_info "Preparing Cloud-Init user-data for Docker (${CODENAME})" - # Prüfen, ob local Snippets erlaubt - if ! pvesm status | awk '$1=="local" && $2 ~ /snippets/ {ok=1} END{exit !ok}'; then - msg_error "Storage 'local' must allow content 'Snippets'. Enable it in Datacenter → Storage → local." + if ! pick_snippet_storage; then + msg_error "No storage with snippets support available. Please enable 'Snippets' on at least one dir storage (e.g. local)." exit 1 fi - SNIPPET="/var/lib/vz/snippets/docker-${VMID}-user-data.yaml" - # Docker GPG in base64 + mkdir -p "$SNIPPET_DIR" + SNIPPET_FILE="docker-${VMID}-user-data.yaml" + SNIPPET_PATH="${SNIPPET_DIR}/${SNIPPET_FILE}" + DOCKER_GPG_B64="$(curl -fsSL "${DOCKER_BASE}/gpg" | gpg --dearmor | base64 -w0)" - cat >"$SNIPPET" <"$SNIPPET_PATH" </dev/null qm set "$VMID" --ciuser root --cipassword '' --sshkeys "/root/.ssh/authorized_keys" >/dev/null || true if [[ "$INSTALL_MODE" = "cloudinit" ]]; then - qm set "$VMID" --cicustom "user=local:snippets/$(basename "$SNIPPET")" >/dev/null + qm set "$VMID" --cicustom "user=${SNIPPET_STORE}:snippets/${SNIPPET_FILE}" >/dev/null fi msg_ok "Attached EFI/root and Cloud-Init" From d844d1ff577e7739dcd5a16a420c44fdc41ce27b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 30 Sep 2025 13:45:49 +0200 Subject: [PATCH 1236/1733] Update docker-vm.sh --- vm/docker-vm.sh | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/vm/docker-vm.sh b/vm/docker-vm.sh index 8f317d0fe..98db814ed 100644 --- a/vm/docker-vm.sh +++ b/vm/docker-vm.sh @@ -76,14 +76,6 @@ pushd "$TEMP_DIR" >/dev/null # ---- Sanity Checks ----------------------------------------------------------- check_root() { if [[ "$(id -u)" -ne 0 ]]; then msg_error "Run as root."; exit 1; fi; } arch_check() { [[ "$(dpkg --print-architecture)" = "amd64" ]] || { msg_error "ARM/PiMox nicht unterstützt."; exit 1; }; } -ssh_check() { - if command -v pveversion >/dev/null 2>&1 && [[ -n "${SSH_CLIENT:+x}" ]]; then - if ! whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" \ - --yesno "Nutze besser die Proxmox Shell (Konsole) – SSH kann Variablen-Ermittlung stören. Trotzdem fortfahren?" 10 70; then - clear; exit 1 - fi - fi -} pve_check() { local ver; ver="$(pveversion | awk -F'/' '{print $2}' | cut -d'-' -f1)" case "$ver" in @@ -92,7 +84,7 @@ pve_check() { esac } -check_root; arch_check; pve_check; ssh_check +check_root; arch_check; pve_check; # ---- Defaults / UI Vorbelegung ---------------------------------------------- GEN_MAC="02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/:$//')" From 97de56bf0454b8c32eec2175edad368941d33c57 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 30 Sep 2025 13:46:07 +0200 Subject: [PATCH 1237/1733] Update docker-vm.sh --- vm/docker-vm.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/docker-vm.sh b/vm/docker-vm.sh index 98db814ed..d06a3e208 100644 --- a/vm/docker-vm.sh +++ b/vm/docker-vm.sh @@ -12,7 +12,7 @@ set -euo pipefail # ---- API-Funktionen laden ---------------------------------------------------- -source /dev/stdin <<<"$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/api.func)" +source /dev/stdin <<<"$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/api.func)" # ---- UI / Farben ------------------------------------------------------------- YW=$'\033[33m'; BL=$'\033[36m'; RD=$'\033[01;31m'; GN=$'\033[1;92m'; DGN=$'\033[32m'; CL=$'\033[m' From 5b393a9a0cfe9a8600db05784b62303aa9547efa Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 30 Sep 2025 13:49:11 +0200 Subject: [PATCH 1238/1733] Update viseron-install.sh --- install/viseron-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/viseron-install.sh b/install/viseron-install.sh index d282f284f..2aa3f21f0 100644 --- a/install/viseron-install.sh +++ b/install/viseron-install.sh @@ -187,6 +187,6 @@ motd_ssh customize msg_info "Cleaning up" -$STD apt-get autoremove -$STD apt-get autoclean +$STD apt -y autoremove +$STD apt -y autoclean msg_ok "Cleaned" From 0810f49aac6b327cd8d6d126630d041eb2acc257 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 30 Sep 2025 13:56:23 +0200 Subject: [PATCH 1239/1733] Add snippet storage selection for Cloud-Init on PVE 9 Introduces a storage selection dialog for Cloud-Init snippets when running on Proxmox VE 9 in cloudinit install mode. Refactors storage selection for VM disks and improves error messages for missing storage types. --- vm/docker-vm.sh | 51 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/vm/docker-vm.sh b/vm/docker-vm.sh index d06a3e208..cc66fac84 100644 --- a/vm/docker-vm.sh +++ b/vm/docker-vm.sh @@ -43,7 +43,6 @@ EOF } header_info; echo -e "\n Loading..." -# ---- Fehler-/Aufräum-Handling ------------------------------------------------ trap 'error_handler $LINENO "$BASH_COMMAND"' ERR trap 'cleanup' EXIT trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT @@ -321,31 +320,59 @@ fi # ---- Storage Auswahl --------------------------------------------------------- msg_info "Validating Storage" -STORAGE_MENU=(); MSG_MAX_LENGTH=0 +DISK_MENU=(); MSG_MAX_LENGTH=0 while read -r line; do TAG=$(echo "$line" | awk '{print $1}') TYPE=$(echo "$line" | awk '{printf "%-10s", $2}') - FREE=$(echo "$line" | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}') + FREE=$(echo "$line" | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf("%9sB", $6)}') ITEM=" Type: $TYPE Free: $FREE " (( ${#ITEM} + 2 > MSG_MAX_LENGTH )) && MSG_MAX_LENGTH=${#ITEM}+2 - STORAGE_MENU+=("$TAG" "$ITEM" "OFF") + DISK_MENU+=("$TAG" "$ITEM" "OFF") done < <(pvesm status -content images | awk 'NR>1') VALID=$(pvesm status -content images | awk 'NR>1') if [[ -z "$VALID" ]]; then - msg_error "No valid storage with content=images found." + msg_error "No storage with content=images available. You need at least one images-capable storage." exit 1 -elif (( ${#STORAGE_MENU[@]} / 3 == 1 )); then - STORAGE=${STORAGE_MENU[0]} +elif (( ${#DISK_MENU[@]} / 3 == 1 )); then + STORAGE=${DISK_MENU[0]} else while [[ -z "${STORAGE:+x}" ]]; do - STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \ - "Which storage pool for ${HN}?\n(Use Spacebar to select)" \ - 16 $((MSG_MAX_LENGTH + 23)) 6 "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) + STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Disk Storage" --radiolist \ + "Which storage pool should be used for the VM disk?\n(Use Spacebar to select)" \ + 16 $((MSG_MAX_LENGTH + 23)) 6 "${DISK_MENU[@]}" 3>&1 1>&2 2>&3) done fi -msg_ok "Using ${BL}${STORAGE}${CL} for Storage" -msg_ok "Virtual Machine ID is ${BL}${VMID}${CL}" +msg_ok "Using ${BL}${STORAGE}${CL} for VM disk" + +if [[ "$PVE_MAJ" -eq 9 && "$INSTALL_MODE" = "cloudinit" ]]; then + msg_info "Validating Snippet Storage" + SNIP_MENU=(); MSG_MAX_LENGTH=0 + while read -r line; do + TAG=$(echo "$line" | awk '{print $1}') + TYPE=$(echo "$line" | awk '{printf "%-10s", $2}') + FREE=$(echo "$line" | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf("%9sB", $6)}') + ITEM=" Type: $TYPE Free: $FREE " + (( ${#ITEM} + 2 > MSG_MAX_LENGTH )) && MSG_MAX_LENGTH=${#ITEM}+2 + SNIP_MENU+=("$TAG" "$ITEM" "OFF") + done < <(pvesm status -content snippets | awk 'NR>1') + + VALID=$(pvesm status -content snippets | awk 'NR>1') + if [[ -z "$VALID" ]]; then + msg_error "No storage with content=snippets available. Please enable 'Snippets' on at least one directory storage (e.g. local)." + exit 1 + elif (( ${#SNIP_MENU[@]} / 3 == 1 )); then + SNIPPET_STORE=${SNIP_MENU[0]} + else + while [[ -z "${SNIPPET_STORE:+x}" ]]; do + SNIPPET_STORE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Snippet Storage" --radiolist \ + "Which storage should be used for the Cloud-Init snippet?\n(Use Spacebar to select)" \ + 16 $((MSG_MAX_LENGTH + 23)) 6 "${SNIP_MENU[@]}" 3>&1 1>&2 2>&3) + done + fi + msg_ok "Using ${BL}${SNIPPET_STORE}${CL} for Cloud-Init snippets" +fi + # ---- Cloud Image Download ---------------------------------------------------- choose_os From 1dca949dfc78a1e8861c95dc30841bba2454b486 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 30 Sep 2025 14:16:47 +0200 Subject: [PATCH 1240/1733] Update docker-vm.sh --- vm/docker-vm.sh | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/vm/docker-vm.sh b/vm/docker-vm.sh index cc66fac84..c7c644d09 100644 --- a/vm/docker-vm.sh +++ b/vm/docker-vm.sh @@ -437,19 +437,16 @@ fi if [[ "$INSTALL_MODE" = "cloudinit" ]]; then msg_info "Preparing Cloud-Init user-data for Docker (${CODENAME})" - if ! pick_snippet_storage; then - msg_error "No storage with snippets support available. Please enable 'Snippets' on at least one dir storage (e.g. local)." - exit 1 - fi - + # Use SNIPPET_STORE selected earlier + SNIPPET_DIR="$(pvesm path "$SNIPPET_STORE")/snippets" mkdir -p "$SNIPPET_DIR" + SNIPPET_FILE="docker-${VMID}-user-data.yaml" - SNIPPET_PATH="${SNIPPET_DIR}/${SNIPPET_FILE}" + SNIPPET_PATH="${SNIPPET_DIR}/${SNIPPET_FILE}"" DOCKER_GPG_B64="$(curl -fsSL "${DOCKER_BASE}/gpg" | gpg --dearmor | base64 -w0)" - - cat >"$SNIPPET_PATH" <"$SNIPPET_PATH" < Date: Tue, 30 Sep 2025 14:18:23 +0200 Subject: [PATCH 1241/1733] Update docker-vm.sh --- vm/docker-vm.sh | 6 ------ 1 file changed, 6 deletions(-) diff --git a/vm/docker-vm.sh b/vm/docker-vm.sh index c7c644d09..1483b979c 100644 --- a/vm/docker-vm.sh +++ b/vm/docker-vm.sh @@ -106,12 +106,6 @@ METHOD="default" var_os="debian" var_version="12" -# ---- Startdialog ------------------------------------------------------------- -if ! whiptail --backtitle "Proxmox VE Helper Scripts" --title "Docker VM" \ - --yesno "This will create a NEW Docker VM. Proceed?" 10 58; then - header_info; echo -e "${CROSS}${RD}User exited script${CL}\n"; exit 1 -fi - # ---- Helper: VMID-Find ------------------------------------------------------- get_valid_nextid() { local id; id=$(pvesh get /cluster/nextid) From 564d33e60c273d891ae4d99e7973ce6ea8f462cf Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 30 Sep 2025 14:23:20 +0200 Subject: [PATCH 1242/1733] Fix typo and adjust whiptail dialog width Corrected an extra quote in SNIPPET_PATH assignment and increased the whiptail radiolist width from 60 to 70 for better display in the Docker VM setup script. --- vm/docker-vm.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vm/docker-vm.sh b/vm/docker-vm.sh index 1483b979c..7ada54f23 100644 --- a/vm/docker-vm.sh +++ b/vm/docker-vm.sh @@ -282,7 +282,7 @@ start_script; post_to_api_vm choose_os() { local OS_CHOICE if OS_CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Choose Base OS" --radiolist \ - "Select the OS for the Docker VM:" 12 60 3 \ + "Select the OS for the Docker VM:" 12 70 3 \ "debian12" "Debian 12 (Bookworm, stable & best for scripts)" ON \ "debian13" "Debian 13 (Trixie, newer, but repos lag)" OFF \ "ubuntu24" "Ubuntu 24.04 LTS (modern kernel, GPU/AI friendly)" OFF \ @@ -436,7 +436,7 @@ if [[ "$INSTALL_MODE" = "cloudinit" ]]; then mkdir -p "$SNIPPET_DIR" SNIPPET_FILE="docker-${VMID}-user-data.yaml" - SNIPPET_PATH="${SNIPPET_DIR}/${SNIPPET_FILE}"" + SNIPPET_PATH="${SNIPPET_DIR}/${SNIPPET_FILE}" DOCKER_GPG_B64="$(curl -fsSL "${DOCKER_BASE}/gpg" | gpg --dearmor | base64 -w0)" From 2221d9026ff6a24af89d2644f69ef78cd85f3b3d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 30 Sep 2025 14:28:16 +0200 Subject: [PATCH 1243/1733] Update docker-vm.sh --- vm/docker-vm.sh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/vm/docker-vm.sh b/vm/docker-vm.sh index 7ada54f23..0b9c036d8 100644 --- a/vm/docker-vm.sh +++ b/vm/docker-vm.sh @@ -402,6 +402,13 @@ detect_codename_and_repo() { } detect_codename_and_repo +get_snippet_dir() { + local store="$1" + awk -v s="$store" ' + $1 == "dir:" && $2 == s {getline; print $2 "/snippets"} + ' /etc/pve/storage.cfg +} + # ---- PVE8: direct inject via virt-customize ---------------------------------- if [[ "$INSTALL_MODE" = "direct" ]]; then msg_info "Injecting Docker & QGA into image (${CODENAME}, repo: $(basename "$DOCKER_BASE"))" @@ -432,7 +439,7 @@ if [[ "$INSTALL_MODE" = "cloudinit" ]]; then msg_info "Preparing Cloud-Init user-data for Docker (${CODENAME})" # Use SNIPPET_STORE selected earlier - SNIPPET_DIR="$(pvesm path "$SNIPPET_STORE")/snippets" + SNIPPET_DIR="$(get_snippet_dir "$SNIPPET_STORE")" mkdir -p "$SNIPPET_DIR" SNIPPET_FILE="docker-${VMID}-user-data.yaml" From 6a22b5ca0ec4519a4b91a3458bf3ef81da45c786 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 30 Sep 2025 15:56:54 +0200 Subject: [PATCH 1244/1733] Update docker-vm.sh --- vm/docker-vm.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/vm/docker-vm.sh b/vm/docker-vm.sh index 0b9c036d8..f9634119c 100644 --- a/vm/docker-vm.sh +++ b/vm/docker-vm.sh @@ -460,6 +460,7 @@ packages: - gnupg - qemu-guest-agent - cloud-guest-utils # growpart/resizefs + - cloud-init write_files: - path: /etc/apt/keyrings/docker.gpg From a636cb66f23895e453cabe92f1c71a2114bac41d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 30 Sep 2025 15:58:06 +0200 Subject: [PATCH 1245/1733] Update docker-vm.sh --- vm/docker-vm.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vm/docker-vm.sh b/vm/docker-vm.sh index f9634119c..c095e3b2c 100644 --- a/vm/docker-vm.sh +++ b/vm/docker-vm.sh @@ -288,8 +288,8 @@ choose_os() { "ubuntu24" "Ubuntu 24.04 LTS (modern kernel, GPU/AI friendly)" OFF \ 3>&1 1>&2 2>&3); then case "$OS_CHOICE" in - debian12) var_os="debian"; var_version="12"; URL="https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-nocloud-$(dpkg --print-architecture).qcow2" ;; - debian13) var_os="debian"; var_version="13"; URL="https://cloud.debian.org/images/cloud/trixie/latest/debian-13-nocloud-$(dpkg --print-architecture).qcow2" ;; + debian12) var_os="debian"; var_version="12"; URL="https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-genericcloud-$(dpkg --print-architecture).qcow2" ;; + debian13) var_os="debian"; var_version="13"; URL="https://cloud.debian.org/images/cloud/trixie/latest/debian-13-genericcloud-$(dpkg --print-architecture).qcow2" ;; ubuntu24) var_os="ubuntu"; var_version="24.04"; URL="https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-$(dpkg --print-architecture).img" ;; esac echo -e "${OSI}${BOLD}${DGN}Selected OS: ${GN}${OS_CHOICE}${CL}" From 6d517e382d025a7743e153a01ca180729f9fc7cd Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 30 Sep 2025 16:01:53 +0200 Subject: [PATCH 1246/1733] cleanup --- {frontend/public/json => ct/deferred}/docspell.json | 0 {frontend/public/json => ct/deferred}/maxun.json | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {frontend/public/json => ct/deferred}/docspell.json (100%) rename {frontend/public/json => ct/deferred}/maxun.json (100%) diff --git a/frontend/public/json/docspell.json b/ct/deferred/docspell.json similarity index 100% rename from frontend/public/json/docspell.json rename to ct/deferred/docspell.json diff --git a/frontend/public/json/maxun.json b/ct/deferred/maxun.json similarity index 100% rename from frontend/public/json/maxun.json rename to ct/deferred/maxun.json From d0729aa82aea728ce569a66a57b2918b7789577a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 30 Sep 2025 16:03:43 +0200 Subject: [PATCH 1247/1733] Update docker-vm.sh --- vm/docker-vm.sh | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/vm/docker-vm.sh b/vm/docker-vm.sh index c095e3b2c..cb48174c8 100644 --- a/vm/docker-vm.sh +++ b/vm/docker-vm.sh @@ -459,27 +459,15 @@ packages: - curl - gnupg - qemu-guest-agent - - cloud-guest-utils # growpart/resizefs - - cloud-init - -write_files: - - path: /etc/apt/keyrings/docker.gpg - permissions: '0644' - encoding: b64 - content: ${DOCKER_GPG_B64} + - cloud-guest-utils runcmd: - - sed -i 's#^ENV_SUPATH.*#ENV_SUPATH PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin#' /etc/login.defs || true - - sed -i 's#^ENV_PATH.*#ENV_PATH PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin#' /etc/login.defs || true - - printf 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\n' > /etc/environment - - "grep -q 'export PATH=' /root/.bashrc || echo 'export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' >> /root/.bashrc" - - install -m 0755 -d /etc/apt/keyrings + - curl -fsSL ${DOCKER_BASE}/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg + - chmod a+r /etc/apt/keyrings/docker.gpg - echo "deb [arch=\$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] ${DOCKER_BASE} ${REPO_CODENAME} stable" > /etc/apt/sources.list.d/docker.list - - apt-get update -qq - apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin - - systemctl enable --now qemu-guest-agent - systemctl enable --now docker From 843fe1802d3765c39ec185469eb120ec7bd75d02 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 30 Sep 2025 16:12:18 +0200 Subject: [PATCH 1248/1733] Update docker-vm.sh --- vm/docker-vm.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/docker-vm.sh b/vm/docker-vm.sh index cb48174c8..f674c3730 100644 --- a/vm/docker-vm.sh +++ b/vm/docker-vm.sh @@ -504,7 +504,7 @@ DISK_REF="$(printf '%s\n' "$IMPORT_OUT" | sed -n "s/.*successfully imported disk msg_ok "Imported disk (${BL}${DISK_REF}${CL})" # ---- EFI + Root + Cloud-Init anhängen --------------------------------------- -msg_info "Attaching EFI/root disk and Cloud-Init" +msg_info "Attaching EFI/root disk and Cloud-Init (Patience)" qm set "$VMID" --efidisk0 "${STORAGE}:0${FORMAT}" >/dev/null qm set "$VMID" --scsi0 "${DISK_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE}" >/dev/null qm set "$VMID" --boot order=scsi0 >/dev/null From 6512d645bbbe207b9bb4f2c0a07104908c30213a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 30 Sep 2025 16:15:20 +0200 Subject: [PATCH 1249/1733] Update docker-vm.sh --- vm/docker-vm.sh | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/vm/docker-vm.sh b/vm/docker-vm.sh index f674c3730..3b2fe264c 100644 --- a/vm/docker-vm.sh +++ b/vm/docker-vm.sh @@ -503,17 +503,24 @@ DISK_REF="$(printf '%s\n' "$IMPORT_OUT" | sed -n "s/.*successfully imported disk [[ -z "$DISK_REF" ]] && { msg_error "Unable to determine imported disk reference."; echo "$IMPORT_OUT"; exit 1; } msg_ok "Imported disk (${BL}${DISK_REF}${CL})" +SSHKEYS_ARG="" +if [[ -s /root/.ssh/authorized_keys ]]; then + SSHKEYS_ARG="--sshkeys /root/.ssh/authorized_keys" +fi + # ---- EFI + Root + Cloud-Init anhängen --------------------------------------- msg_info "Attaching EFI/root disk and Cloud-Init (Patience)" -qm set "$VMID" --efidisk0 "${STORAGE}:0${FORMAT}" >/dev/null -qm set "$VMID" --scsi0 "${DISK_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE}" >/dev/null -qm set "$VMID" --boot order=scsi0 >/dev/null -qm set "$VMID" --serial0 socket >/dev/null -qm set "$VMID" --agent enabled=1,fstrim_cloned_disks=1 >/dev/null -qm set "$VMID" --ide2 "${STORAGE}:cloudinit" >/dev/null -qm set "$VMID" --ipconfig0 "ip=dhcp" >/dev/null -qm set "$VMID" --nameserver "1.1.1.1 9.9.9.9" --searchdomain "lan" >/dev/null -qm set "$VMID" --ciuser root --cipassword '' --sshkeys "/root/.ssh/authorized_keys" >/dev/null || true +qm set "$VMID" \ + --efidisk0 "${STORAGE}:0${FORMAT}" \ + --scsi0 "${DISK_REF},${DISK_CACHE}${THIN}size=${DISK_SIZE}" \ + --boot order=scsi0 \ + --serial0 socket \ + --agent enabled=1,fstrim_cloned_disks=1 \ + --ide2 "${STORAGE}:cloudinit" \ + --ipconfig0 "ip=dhcp" \ + --nameserver "1.1.1.1 9.9.9.9" --searchdomain "lan" \ + --ciuser root --cipassword '' \ + $SSHKEYS_ARG >/dev/null || true if [[ "$INSTALL_MODE" = "cloudinit" ]]; then qm set "$VMID" --cicustom "user=${SNIPPET_STORE}:snippets/${SNIPPET_FILE}" >/dev/null From 3304f973fcba67236c1dc40c2f1ef3e3947fe589 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 30 Sep 2025 16:37:01 +0200 Subject: [PATCH 1250/1733] Add SSH key or password authentication for VM creation Introduces logic to detect SSH public keys on the host and prompt the user to use them for root login in new VMs. If no keys are found or declined, prompts for a root password with confirmation. Refactors authentication setup into a dedicated function and updates VM creation flow to configure authentication accordingly. --- vm/docker-vm.sh | 64 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/vm/docker-vm.sh b/vm/docker-vm.sh index 3b2fe264c..313bb4ed9 100644 --- a/vm/docker-vm.sh +++ b/vm/docker-vm.sh @@ -298,6 +298,20 @@ choose_os() { fi } +SSH_PUB_KEYS=() +while IFS= read -r -d '' key; do + SSH_PUB_KEYS+=("$key") +done < <(find /root/.ssh -maxdepth 1 -type f -name "*.pub" -print0 2>/dev/null) + +USE_KEYS="no" +if [[ ${#SSH_PUB_KEYS[@]} -gt 0 ]]; then + if whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "SSH Key Authentication" \ + --yesno "Found SSH public keys on the host:\n\n${SSH_PUB_KEYS[*]}\n\nUse them for root login in the new VM?" 15 70; then + USE_KEYS="yes" + fi +fi + # ---- PVE Version + Install-Mode (einmalig) ----------------------------------- PVE_MAJ="$(pveversion | awk -F'/' '{print $2}' | cut -d'-' -f1 | cut -d'.' -f1)" case "$PVE_MAJ" in @@ -367,6 +381,47 @@ if [[ "$PVE_MAJ" -eq 9 && "$INSTALL_MODE" = "cloudinit" ]]; then msg_ok "Using ${BL}${SNIPPET_STORE}${CL} for Cloud-Init snippets" fi +configure_authentication() { + local SSH_PUB_KEYS=() + while IFS= read -r -d '' key; do + SSH_PUB_KEYS+=("$key") + done < <(find /root/.ssh -maxdepth 1 -type f -name "*.pub" -print0 2>/dev/null) + + if [[ ${#SSH_PUB_KEYS[@]} -gt 0 ]]; then + # Found keys → ask user + if whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "SSH Key Authentication" \ + --yesno "Found SSH public keys:\n\n${SSH_PUB_KEYS[*]}\n\nDo you want to use them for root login in the new VM?" \ + 15 70; then + echo -e "${CM}${GN}Using SSH keys for root login${CL}" + qm set "$VMID" --ciuser root --sshkeys "${SSH_PUB_KEYS[0]}" >/dev/null + return + fi + fi + + # No key or user said No → ask for password twice + local PASS1 PASS2 + while true; do + PASS1=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "Root Password" \ + --passwordbox "Enter a password for root user" 10 70 3>&1 1>&2 2>&3) || exit-script + + PASS2=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "Confirm Root Password" \ + --passwordbox "Re-enter password for confirmation" 10 70 3>&1 1>&2 2>&3) || exit-script + + if [[ "$PASS1" == "$PASS2" && -n "$PASS1" ]]; then + echo -e "${CM}${GN}Root password confirmed and set${CL}" + qm set "$VMID" --ciuser root --cipassword "$PASS1" >/dev/null + break + else + whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "Password Mismatch" \ + --msgbox "Passwords did not match or were empty. Please try again." 10 70 + fi + done +} + # ---- Cloud Image Download ---------------------------------------------------- choose_os @@ -494,6 +549,10 @@ qm create "$VMID" -machine q35 -bios ovmf -agent 1 -tablet 0 -localtime 1 ${CPU_ -net0 "virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU" -onboot 1 -ostype l26 -scsihw virtio-scsi-pci >/dev/null msg_ok "Created VM shell" +msg_info "Configuring authentication" +configure_authentication +msg_ok "Authentication configured" + # ---- Disk importieren -------------------------------------------------------- msg_info "Importing disk into storage ($STORAGE)" if qm disk import --help >/dev/null 2>&1; then IMPORT_CMD=(qm disk import); else IMPORT_CMD=(qm importdisk); fi @@ -517,10 +576,7 @@ qm set "$VMID" \ --serial0 socket \ --agent enabled=1,fstrim_cloned_disks=1 \ --ide2 "${STORAGE}:cloudinit" \ - --ipconfig0 "ip=dhcp" \ - --nameserver "1.1.1.1 9.9.9.9" --searchdomain "lan" \ - --ciuser root --cipassword '' \ - $SSHKEYS_ARG >/dev/null || true + --ipconfig0 "ip=dhcp" >/dev/null if [[ "$INSTALL_MODE" = "cloudinit" ]]; then qm set "$VMID" --cicustom "user=${SNIPPET_STORE}:snippets/${SNIPPET_FILE}" >/dev/null From eba99e7d458a1b0ba732e96b652145c8f2fedc74 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 30 Sep 2025 16:40:03 +0200 Subject: [PATCH 1251/1733] Update docker-vm.sh --- vm/docker-vm.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vm/docker-vm.sh b/vm/docker-vm.sh index 313bb4ed9..36f7177ff 100644 --- a/vm/docker-vm.sh +++ b/vm/docker-vm.sh @@ -426,6 +426,8 @@ configure_authentication() { # ---- Cloud Image Download ---------------------------------------------------- choose_os msg_info "Retrieving Cloud Image for $var_os $var_version" +echo -e "" +echo -e "" curl --retry 30 --retry-delay 3 --retry-connrefused -fSL -o "$(basename "$URL")" "$URL" FILE="$(basename "$URL")" msg_ok "Downloaded ${BL}${FILE}${CL}" From a250b7a7154a3cf8c2277eac4e120f5e28deab55 Mon Sep 17 00:00:00 2001 From: Enzo Legrand Date: Tue, 30 Sep 2025 16:46:46 +0200 Subject: [PATCH 1252/1733] chore: change paths for development --- misc/build.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index c53632a9e..60b9b61ca 100644 --- a/misc/build.func +++ b/misc/build.func @@ -80,7 +80,7 @@ variables() { # [Dd]*) # for file in core.func error_handler.func tools.func; do # local_path="$FUNC_DIR/$file" -# url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/$file" +# url="https://raw.githubusercontent.com/prop4n/ProxmoxVED/refs/heads/add-script-sonarqube/misc/$file" # remote_tmp="$(mktemp)" # curl -fsSL "$url" -o "$remote_tmp" || continue From 5925552ae15b6094af5a2a6cecc26cdfb9b791cb Mon Sep 17 00:00:00 2001 From: Enzo Legrand Date: Tue, 30 Sep 2025 16:50:25 +0200 Subject: [PATCH 1253/1733] add: script to configure the LXC container for sonarqube --- ct/sonarqube.sh | 76 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 ct/sonarqube.sh diff --git a/ct/sonarqube.sh b/ct/sonarqube.sh new file mode 100644 index 000000000..da560aa03 --- /dev/null +++ b/ct/sonarqube.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +source <(curl -s https://raw.githubusercontent.com/prop4n/ProxmoxVED/refs/heads/add-script-sonarqube/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: prop4n +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://docs.sonarsource.com/sonarqube-server + +APP="SonarQube" +var_tags="${var_tags:-asset-automation}" +var_cpu="${var_cpu:-4}" +var_ram="${var_ram:-6144}" +var_disk="${var_disk:-15}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/sonarqube ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + RELEASE=$(curl -s https://api.github.com/repos/SonarSource/sonarqube/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') + if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then + msg_info "Updating ${APP} to v${RELEASE}" + + systemctl stop sonarqube + + BACKUP_DIR="/opt/sonarqube-backup" + mv /opt/sonarqube ${BACKUP_DIR} + + curl -fsSL -o /tmp/sonarqube.zip "https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-${RELEASE}.zip" + + mkdir -p /opt/sonarqube + + unzip -q /tmp/sonarqube.zip -d /tmp + cp -r /tmp/sonarqube-${RELEASE}/* /opt/sonarqube/ + rm -rf /tmp/sonarqube* + + cp -rp ${BACKUP_DIR}/data/ /opt/sonarqube/data/ + cp -rp ${BACKUP_DIR}/extensions/ /opt/sonarqube/extensions/ + cp -p ${BACKUP_DIR}/conf/sonar.properties /opt/sonarqube/conf/sonar.properties + rm -rf ${BACKUP_DIR} + + chown -R sonarqube:sonarqube /opt/sonarqube + + echo "${RELEASE}" > /opt/${APP}_version.txt + + systemctl start sonarqube + + msg_ok "Updated to v${RELEASE}" + else + msg_ok "No update required. ${APP} is already at v${RELEASE}." + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9000${CL}" +echo -e "${YW}Default credentials:${CL}" +echo -e "${TAB}Username: admin" +echo -e "${TAB}Password: admin" From 93809e578466af79bb0844238c1666e546a6bf91 Mon Sep 17 00:00:00 2001 From: Enzo Legrand Date: Tue, 30 Sep 2025 16:51:05 +0200 Subject: [PATCH 1254/1733] add: sonarqube installation script --- install/sonarqube-install.sh | 93 ++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 install/sonarqube-install.sh diff --git a/install/sonarqube-install.sh b/install/sonarqube-install.sh new file mode 100644 index 000000000..eb9c5de29 --- /dev/null +++ b/install/sonarqube-install.sh @@ -0,0 +1,93 @@ +#!/usr/bin/env bash +# Copyright (c) 2021-2025 community-scripts ORG +# Author: prop4n +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://docs.sonarsource.com/sonarqube-server + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies (Patience)" +$STD apt-get install -y openjdk-17-jdk curl unzip +msg_ok "Installed Dependencies" + +RELEASE=$(curl -s https://api.github.com/repos/SonarSource/sonarqube/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') +PG_VERSION="17" setup_postgresql + +msg_info "Installing Postgresql" +DB_NAME="sonarqube" +DB_USER="sonarqube" +DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) +systemctl start postgresql +$STD sudo -u postgres psql -c "CREATE USER $DB_USER WITH PASSWORD '$DB_PASS';" +$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME OWNER $DB_USER;" +{ + echo "Application Credentials" + echo "DB_NAME: $DB_NAME" + echo "DB_USER: $DB_USER" + echo "DB_PASS: $DB_PASS" +} >>~/sonarqube.creds +msg_ok "Installed PostgreSQL" + +msg_info "Creating SonarQube User" +useradd -r -m -U -d /opt/sonarqube -s /bin/bash sonarqube 2>/dev/null || true + +msg_info "SonarQube Installation" +curl -fsSL -o /tmp/sonarqube.zip "https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-${RELEASE}.zip" +unzip -q /tmp/sonarqube.zip -d /tmp +cp -r /tmp/sonarqube-*/* /opt/sonarqube/ +rm -rf /tmp/sonarqube* +chown -R sonarqube:sonarqube /opt/sonarqube +chmod -R 755 /opt/sonarqube +msg_ok "Installed SonarQube" + +msg_info "Configuring SonarQube" +mkdir -p /opt/sonarqube/conf +cat >/opt/sonarqube/conf/sonar.properties </etc/systemd/system/sonarqube.service < Date: Tue, 30 Sep 2025 17:06:17 +0200 Subject: [PATCH 1255/1733] fix: path to my personnal fork --- misc/build.func | 6 +++--- misc/install.func | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/misc/build.func b/misc/build.func index 60b9b61ca..b311f3b36 100644 --- a/misc/build.func +++ b/misc/build.func @@ -36,7 +36,7 @@ variables() { # FUNC_DIR="/usr/local/community-scripts/core" # mkdir -p "$FUNC_DIR" -# BUILD_URL="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func" +# BUILD_URL="https://raw.githubusercontent.com/prop4n/ProxmoxVED/refs/heads/add-script-sonarqube/misc/build.func" # BUILD_REV="$FUNC_DIR/build.rev" # DEVMODE="${DEVMODE:-no}" @@ -61,7 +61,7 @@ variables() { # update_func_file() { # local file="$1" -# local url="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/$file" +# local url="https://raw.githubusercontent.com/prop4n/ProxmoxVED/refs/heads/add-script-sonarqube/misc/$file" # local local_path="$FUNC_DIR/$file" # echo "⬇️ Downloading $file ..." @@ -2671,7 +2671,7 @@ EOF' install_ssh_keys_into_ct # Run application installer - if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)"; then + if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/prop4n/ProxmoxVED/refs/heads/add-script-sonarqube/install/${var_install}.sh)"; then exit $? fi } diff --git a/misc/install.func b/misc/install.func index f741b921d..3ea617ad3 100644 --- a/misc/install.func +++ b/misc/install.func @@ -32,7 +32,7 @@ verb_ip6() { # # This function handles errors # error_handler() { -# source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/api.func) +# source <(curl -fsSL https://raw.githubusercontent.com/prop4n/ProxmoxVED/refs/heads/add-script-sonarqube/misc/api.func) # local exit_code="$1" # local line_number="$2" # local command="${3:-}" From 065e738981ddb1762ea28dea55c78bddeac73938 Mon Sep 17 00:00:00 2001 From: Enzo Legrand Date: Tue, 30 Sep 2025 17:18:42 +0200 Subject: [PATCH 1256/1733] fix: ct script and remove misc scripts --- ct/sonarqube.sh | 4 +- misc/build.func | 3473 --------------------------------------------- misc/install.func | 206 --- 3 files changed, 2 insertions(+), 3681 deletions(-) delete mode 100644 misc/build.func delete mode 100644 misc/install.func diff --git a/ct/sonarqube.sh b/ct/sonarqube.sh index da560aa03..74761e50a 100644 --- a/ct/sonarqube.sh +++ b/ct/sonarqube.sh @@ -6,10 +6,10 @@ source <(curl -s https://raw.githubusercontent.com/prop4n/ProxmoxVED/refs/heads/ # Source: https://docs.sonarsource.com/sonarqube-server APP="SonarQube" -var_tags="${var_tags:-asset-automation}" +var_tags="${var_tags:-automation}" var_cpu="${var_cpu:-4}" var_ram="${var_ram:-6144}" -var_disk="${var_disk:-15}" +var_disk="${var_disk:-25}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" diff --git a/misc/build.func b/misc/build.func deleted file mode 100644 index b311f3b36..000000000 --- a/misc/build.func +++ /dev/null @@ -1,3473 +0,0 @@ -#!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG -# Author: tteck (tteckster) | MickLesk | michelroegl-brunner -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Revision: 1 - -# ------------------------------------------------------------------------------ -# variables() -# -# - Normalize application name (NSAPP = lowercase, no spaces) -# - Build installer filename (var_install) -# - Define regex for integer validation -# - Fetch hostname of Proxmox node -# - Set default values for diagnostics/method -# - Generate random UUID for tracking -# ------------------------------------------------------------------------------ -variables() { - NSAPP=$(echo "${APP,,}" | tr -d ' ') # This function sets the NSAPP variable by converting the value of the APP variable to lowercase and removing any spaces. - var_install="${NSAPP}-install" # sets the var_install variable by appending "-install" to the value of NSAPP. - INTEGER='^[0-9]+([.][0-9]+)?$' # it defines the INTEGER regular expression pattern. - PVEHOST_NAME=$(hostname) # gets the Proxmox Hostname and sets it to Uppercase - DIAGNOSTICS="yes" # sets the DIAGNOSTICS variable to "yes", used for the API call. - METHOD="default" # sets the METHOD variable to "default", used for the API call. - RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" # generates a random UUID and sets it to the RANDOM_UUID variable. - CTTYPE="${CTTYPE:-${CT_TYPE:-1}}" - #CT_TYPE=${var_unprivileged:-$CT_TYPE} -} - -# ----------------------------------------------------------------------------- -# Community-Scripts bootstrap loader -# - Always sources build.func from remote -# - Updates local core files only if build.func changed -# - Local cache: /usr/local/community-scripts/core -# ----------------------------------------------------------------------------- - -# FUNC_DIR="/usr/local/community-scripts/core" -# mkdir -p "$FUNC_DIR" - -# BUILD_URL="https://raw.githubusercontent.com/prop4n/ProxmoxVED/refs/heads/add-script-sonarqube/misc/build.func" -# BUILD_REV="$FUNC_DIR/build.rev" -# DEVMODE="${DEVMODE:-no}" - -# # --- Step 1: fetch build.func content once, compute hash --- -# build_content="$(curl -fsSL "$BUILD_URL")" || { -# echo "❌ Failed to fetch build.func" -# exit 1 -# } - -# newhash=$(printf "%s" "$build_content" | sha256sum | awk '{print $1}') -# oldhash=$(cat "$BUILD_REV" 2>/dev/null || echo "") - -# # --- Step 2: if build.func changed, offer update for core files --- -# if [ "$newhash" != "$oldhash" ]; then -# echo "⚠️ build.func changed!" - -# while true; do -# read -rp "Refresh local core files? [y/N/diff]: " ans -# case "$ans" in -# [Yy]*) -# echo "$newhash" >"$BUILD_REV" - -# update_func_file() { -# local file="$1" -# local url="https://raw.githubusercontent.com/prop4n/ProxmoxVED/refs/heads/add-script-sonarqube/misc/$file" -# local local_path="$FUNC_DIR/$file" - -# echo "⬇️ Downloading $file ..." -# curl -fsSL "$url" -o "$local_path" || { -# echo "❌ Failed to fetch $file" -# exit 1 -# } -# echo "✔️ Updated $file" -# } - -# update_func_file core.func -# update_func_file error_handler.func -# update_func_file tools.func -# break -# ;; -# [Dd]*) -# for file in core.func error_handler.func tools.func; do -# local_path="$FUNC_DIR/$file" -# url="https://raw.githubusercontent.com/prop4n/ProxmoxVED/refs/heads/add-script-sonarqube/misc/$file" -# remote_tmp="$(mktemp)" - -# curl -fsSL "$url" -o "$remote_tmp" || continue - -# if [ -f "$local_path" ]; then -# echo "🔍 Diff for $file:" -# diff -u "$local_path" "$remote_tmp" || echo "(no differences)" -# else -# echo "📦 New file $file will be installed" -# fi - -# rm -f "$remote_tmp" -# done -# ;; -# *) -# echo "❌ Skipped updating local core files" -# break -# ;; -# esac -# done -# else -# if [ "$DEVMODE" != "yes" ]; then -# echo "✔️ build.func unchanged → using existing local core files" -# fi -# fi - -# if [ -n "${_COMMUNITY_SCRIPTS_LOADER:-}" ]; then -# return 0 2>/dev/null || exit 0 -# fi -# _COMMUNITY_SCRIPTS_LOADER=1 - -# # --- Step 3: always source local versions of the core files --- -# source "$FUNC_DIR/core.func" -# source "$FUNC_DIR/error_handler.func" -# source "$FUNC_DIR/tools.func" - -# # --- Step 4: finally, source build.func directly from memory --- -# # (no tmp file needed) -# source <(printf "%s" "$build_content") - -# ------------------------------------------------------------------------------ -# Load core + error handler functions from community-scripts repo -# -# - Prefer curl if available, fallback to wget -# - Load: core.func, error_handler.func, api.func -# - Initialize error traps after loading -# ------------------------------------------------------------------------------ - -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) - -if command -v curl >/dev/null 2>&1; then - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) - load_functions - catch_errors - #echo "(build.func) Loaded core.func via curl" -elif command -v wget >/dev/null 2>&1; then - source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) - source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) - load_functions - catch_errors - #echo "(build.func) Loaded core.func via wget" -fi - -# ------------------------------------------------------------------------------ -# maxkeys_check() -# -# - Reads kernel keyring limits (maxkeys, maxbytes) -# - Checks current usage for LXC user (UID 100000) -# - Warns if usage is close to limits and suggests sysctl tuning -# - Exits if thresholds are exceeded -# - https://cleveruptime.com/docs/files/proc-key-users | https://docs.kernel.org/security/keys/core.html -# ------------------------------------------------------------------------------ - -maxkeys_check() { - # Read kernel parameters - per_user_maxkeys=$(cat /proc/sys/kernel/keys/maxkeys 2>/dev/null || echo 0) - per_user_maxbytes=$(cat /proc/sys/kernel/keys/maxbytes 2>/dev/null || echo 0) - - # Exit if kernel parameters are unavailable - if [[ "$per_user_maxkeys" -eq 0 || "$per_user_maxbytes" -eq 0 ]]; then - echo -e "${CROSS}${RD} Error: Unable to read kernel parameters. Ensure proper permissions.${CL}" - exit 1 - fi - - # Fetch key usage for user ID 100000 (typical for containers) - used_lxc_keys=$(awk '/100000:/ {print $2}' /proc/key-users 2>/dev/null || echo 0) - used_lxc_bytes=$(awk '/100000:/ {split($5, a, "/"); print a[1]}' /proc/key-users 2>/dev/null || echo 0) - - # Calculate thresholds and suggested new limits - threshold_keys=$((per_user_maxkeys - 100)) - threshold_bytes=$((per_user_maxbytes - 1000)) - new_limit_keys=$((per_user_maxkeys * 2)) - new_limit_bytes=$((per_user_maxbytes * 2)) - - # Check if key or byte usage is near limits - failure=0 - if [[ "$used_lxc_keys" -gt "$threshold_keys" ]]; then - echo -e "${CROSS}${RD} Warning: Key usage is near the limit (${used_lxc_keys}/${per_user_maxkeys}).${CL}" - echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxkeys=${new_limit_keys}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." - failure=1 - fi - if [[ "$used_lxc_bytes" -gt "$threshold_bytes" ]]; then - echo -e "${CROSS}${RD} Warning: Key byte usage is near the limit (${used_lxc_bytes}/${per_user_maxbytes}).${CL}" - echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxbytes=${new_limit_bytes}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." - failure=1 - fi - - # Provide next steps if issues are detected - if [[ "$failure" -eq 1 ]]; then - echo -e "${INFO} To apply changes, run: ${BOLD}service procps force-reload${CL}" - exit 1 - fi - - echo -e "${CM}${GN} All kernel key limits are within safe thresholds.${CL}" -} - -# ------------------------------------------------------------------------------ -# get_current_ip() -# -# - Returns current container IP depending on OS type -# - Debian/Ubuntu: uses `hostname -I` -# - Alpine: parses eth0 via `ip -4 addr` -# ------------------------------------------------------------------------------ -get_current_ip() { - if [ -f /etc/os-release ]; then - # Check for Debian/Ubuntu (uses hostname -I) - if grep -qE 'ID=debian|ID=ubuntu' /etc/os-release; then - CURRENT_IP=$(hostname -I | awk '{print $1}') - # Check for Alpine (uses ip command) - elif grep -q 'ID=alpine' /etc/os-release; then - CURRENT_IP=$(ip -4 addr show eth0 | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) - else - CURRENT_IP="Unknown" - fi - fi - echo "$CURRENT_IP" -} - - -# ------------------------------------------------------------------------------ -# update_motd_ip() -# -# - Updates /etc/motd with current container IP -# - Removes old IP entries to avoid duplicates -# ------------------------------------------------------------------------------ -update_motd_ip() { - MOTD_FILE="/etc/motd" - - if [ -f "$MOTD_FILE" ]; then - # Remove existing IP Address lines to prevent duplication - sed -i '/IP Address:/d' "$MOTD_FILE" - - IP=$(get_current_ip) - # Add the new IP address - echo -e "${TAB}${NETWORK}${YW} IP Address: ${GN}${IP}${CL}" >>"$MOTD_FILE" - fi -} - -# ------------------------------------------------------------------------------ -# install_ssh_keys_into_ct() -# -# - Installs SSH keys into container root account if SSH is enabled -# - Uses pct push or direct input to authorized_keys -# - Falls back to warning if no keys provided -# ------------------------------------------------------------------------------ -install_ssh_keys_into_ct() { - [[ "$SSH" != "yes" ]] && return 0 - - if [[ -n "$SSH_KEYS_FILE" && -s "$SSH_KEYS_FILE" ]]; then - msg_info "Installing selected SSH keys into CT ${CTID}" - pct exec "$CTID" -- sh -c 'mkdir -p /root/.ssh && chmod 700 /root/.ssh' || { - msg_error "prepare /root/.ssh failed" - return 1 - } - pct push "$CTID" "$SSH_KEYS_FILE" /root/.ssh/authorized_keys >/dev/null 2>&1 || - pct exec "$CTID" -- sh -c "cat > /root/.ssh/authorized_keys" <"$SSH_KEYS_FILE" || { - msg_error "write authorized_keys failed" - return 1 - } - pct exec "$CTID" -- sh -c 'chmod 600 /root/.ssh/authorized_keys' || true - msg_ok "Installed SSH keys into CT ${CTID}" - return 0 - fi - - # Fallback: nichts ausgewählt - msg_warn "No SSH keys to install (skipping)." - return 0 -} - -# ------------------------------------------------------------------------------ -# base_settings() -# -# - Defines all base/default variables for container creation -# - Reads from environment variables (var_*) -# - Provides fallback defaults for OS type/version -# ------------------------------------------------------------------------------ -base_settings() { - # Default Settings - CT_TYPE=${var_unprivileged:-"1"} - DISK_SIZE=${var_disk:-"4"} - CORE_COUNT=${var_cpu:-"1"} - RAM_SIZE=${var_ram:-"1024"} - VERBOSE=${var_verbose:-"${1:-no}"} - PW=${var_pw:-""} - CT_ID=${var_ctid:-$NEXTID} - HN=${var_hostname:-$NSAPP} - BRG=${var_brg:-"vmbr0"} - NET=${var_net:-"dhcp"} - IPV6_METHOD=${var_ipv6_method:-"none"} - IPV6_STATIC=${var_ipv6_static:-""} - GATE=${var_gateway:-""} - APT_CACHER=${var_apt_cacher:-""} - APT_CACHER_IP=${var_apt_cacher_ip:-""} - MTU=${var_mtu:-""} - SD=${var_storage:-""} - NS=${var_ns:-""} - MAC=${var_mac:-""} - VLAN=${var_vlan:-""} - SSH=${var_ssh:-"no"} - SSH_AUTHORIZED_KEY=${var_ssh_authorized_key:-""} - UDHCPC_FIX=${var_udhcpc_fix:-""} - TAGS="community-script,${var_tags:-}" - ENABLE_FUSE=${var_fuse:-"${1:-no}"} - ENABLE_TUN=${var_tun:-"${1:-no}"} - - # Since these 2 are only defined outside of default_settings function, we add a temporary fallback. TODO: To align everything, we should add these as constant variables (e.g. OSTYPE and OSVERSION), but that would currently require updating the default_settings function for all existing scripts - if [ -z "$var_os" ]; then - var_os="debian" - fi - if [ -z "$var_version" ]; then - var_version="12" - fi -} - -# ------------------------------------------------------------------------------ -# echo_default() -# -# - Prints summary of default values (ID, OS, type, disk, RAM, CPU, etc.) -# - Uses icons and formatting for readability -# - Convert CT_TYPE to description -# ------------------------------------------------------------------------------ -echo_default() { - CT_TYPE_DESC="Unprivileged" - if [ "$CT_TYPE" -eq 0 ]; then - CT_TYPE_DESC="Privileged" - fi - - echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}${CT_ID}${CL}" - echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os ($var_version)${CL}" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" - if [ "$VERBOSE" == "yes" ]; then - echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}Enabled${CL}" - fi - echo -e "${CREATING}${BOLD}${BL}Creating a ${APP} LXC using the above default settings${CL}" - echo -e " " -} - -# ------------------------------------------------------------------------------ -# exit_script() -# -# - Called when user cancels an action -# - Clears screen and exits gracefully -# ------------------------------------------------------------------------------ -exit_script() { - clear - echo -e "\n${CROSS}${RD}User exited script${CL}\n" - exit -} - -# ------------------------------------------------------------------------------ -# find_host_ssh_keys() -# -# - Scans system for available SSH keys -# - Supports defaults (~/.ssh, /etc/ssh/authorized_keys) -# - Returns list of files containing valid SSH public keys -# - Sets FOUND_HOST_KEY_COUNT to number of keys found -# ------------------------------------------------------------------------------ -find_host_ssh_keys() { - local re='(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))' - local -a files=() cand=() - local g="${var_ssh_import_glob:-}" - local total=0 f base c - - shopt -s nullglob - if [[ -n "$g" ]]; then - for pat in $g; do cand+=($pat); done - else - cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2) - cand+=(/root/.ssh/*.pub) - cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*) - fi - shopt -u nullglob - - for f in "${cand[@]}"; do - [[ -f "$f" && -r "$f" ]] || continue - base="$(basename -- "$f")" - case "$base" in - known_hosts | known_hosts.* | config) continue ;; - id_*) [[ "$f" != *.pub ]] && continue ;; - esac - - # CRLF safe check for host keys - c=$(tr -d '\r' <"$f" | awk ' - /^[[:space:]]*#/ {next} - /^[[:space:]]*$/ {next} - {print} - ' | grep -E -c '"$re"' || true) - - if ((c > 0)); then - files+=("$f") - total=$((total + c)) - fi - done - - # Fallback to /root/.ssh/authorized_keys - if ((${#files[@]} == 0)) && [[ -r /root/.ssh/authorized_keys ]]; then - if grep -E -q "$re" /root/.ssh/authorized_keys; then - files+=(/root/.ssh/authorized_keys) - total=$((total + $(grep -E -c "$re" /root/.ssh/authorized_keys || echo 0))) - fi - fi - - FOUND_HOST_KEY_COUNT="$total" - ( - IFS=: - echo "${files[*]}" - ) -} - -# ------------------------------------------------------------------------------ -# advanced_settings() -# -# - Interactive whiptail menu for advanced configuration -# - Lets user set container type, password, CT ID, hostname, disk, CPU, RAM -# - Supports IPv4/IPv6, DNS, MAC, VLAN, tags, SSH keys, FUSE, verbose mode -# - Ends with confirmation or re-entry if cancelled -# ------------------------------------------------------------------------------ -advanced_settings() { - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox --title "Here is an instructional tip:" "To make a selection, use the Spacebar." 8 58 - # Setting Default Tag for Advanced Settings - TAGS="community-script;${var_tags:-}" - CT_DEFAULT_TYPE="${CT_TYPE}" - CT_TYPE="" - while [ -z "$CT_TYPE" ]; do - if [ "$CT_DEFAULT_TYPE" == "1" ]; then - if CT_TYPE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ - "1" "Unprivileged" ON \ - "0" "Privileged" OFF \ - 3>&1 1>&2 2>&3); then - if [ -n "$CT_TYPE" ]; then - CT_TYPE_DESC="Unprivileged" - if [ "$CT_TYPE" -eq 0 ]; then - CT_TYPE_DESC="Privileged" - fi - echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os | ${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" - fi - else - exit_script - fi - fi - if [ "$CT_DEFAULT_TYPE" == "0" ]; then - if CT_TYPE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ - "1" "Unprivileged" OFF \ - "0" "Privileged" ON \ - 3>&1 1>&2 2>&3); then - if [ -n "$CT_TYPE" ]; then - CT_TYPE_DESC="Unprivileged" - if [ "$CT_TYPE" -eq 0 ]; then - CT_TYPE_DESC="Privileged" - fi - echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os${CL}" - echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" - fi - else - exit_script - fi - fi - done - - while true; do - if PW1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --passwordbox "\nSet Root Password (needed for root ssh access)" 9 58 --title "PASSWORD (leave blank for automatic login)" 3>&1 1>&2 2>&3); then - # Empty = Autologin - if [[ -z "$PW1" ]]; then - PW="" - PW1="Automatic Login" - echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}$PW1${CL}" - break - fi - - # Invalid: contains spaces - if [[ "$PW1" == *" "* ]]; then - whiptail --msgbox "Password cannot contain spaces." 8 58 - continue - fi - - # Invalid: too short - if ((${#PW1} < 5)); then - whiptail --msgbox "Password must be at least 5 characters." 8 58 - continue - fi - - # Confirm password - if PW2=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then - if [[ "$PW1" == "$PW2" ]]; then - PW="-password $PW1" - echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}" - break - else - whiptail --msgbox "Passwords do not match. Please try again." 8 58 - fi - else - exit_script - fi - else - exit_script - fi - done - - if CT_ID=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Container ID" 8 58 "$NEXTID" --title "CONTAINER ID" 3>&1 1>&2 2>&3); then - if [ -z "$CT_ID" ]; then - CT_ID="$NEXTID" - echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" - else - echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" - fi - else - exit_script - fi - - while true; do - if CT_NAME=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 "$NSAPP" --title "HOSTNAME" 3>&1 1>&2 2>&3); then - if [ -z "$CT_NAME" ]; then - HN="$NSAPP" - else - HN=$(echo "${CT_NAME,,}" | tr -d ' ') - fi - # Hostname validate (RFC 1123) - if [[ "$HN" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ ]]; then - echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" - break - else - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --msgbox "❌ Invalid hostname: '$HN'\n\nOnly lowercase letters, digits and hyphens (-) are allowed.\nUnderscores (_) or other characters are not permitted!" 10 70 - fi - else - exit_script - fi - done - - while true; do - DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GB" 8 58 "$var_disk" --title "DISK SIZE" 3>&1 1>&2 2>&3) || exit_script - - if [ -z "$DISK_SIZE" ]; then - DISK_SIZE="$var_disk" - fi - - if [[ "$DISK_SIZE" =~ ^[1-9][0-9]*$ ]]; then - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" - break - else - whiptail --msgbox "Disk size must be a positive integer!" 8 58 - fi - done - - while true; do - CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ - --inputbox "Allocate CPU Cores" 8 58 "$var_cpu" --title "CORE COUNT" 3>&1 1>&2 2>&3) || exit_script - - if [ -z "$CORE_COUNT" ]; then - CORE_COUNT="$var_cpu" - fi - - if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" - break - else - whiptail --msgbox "CPU core count must be a positive integer!" 8 58 - fi - done - - while true; do - RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ - --inputbox "Allocate RAM in MiB" 8 58 "$var_ram" --title "RAM" 3>&1 1>&2 2>&3) || exit_script - - if [ -z "$RAM_SIZE" ]; then - RAM_SIZE="$var_ram" - fi - - if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" - break - else - whiptail --msgbox "RAM size must be a positive integer!" 8 58 - fi - done - - IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f) - BRIDGES="" - OLD_IFS=$IFS - IFS=$'\n' - for iface_filepath in ${IFACE_FILEPATH_LIST}; do - - iface_indexes_tmpfile=$(mktemp -q -u '.iface-XXXX') - (grep -Pn '^\s*iface' "${iface_filepath}" | cut -d':' -f1 && wc -l "${iface_filepath}" | cut -d' ' -f1) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' >"${iface_indexes_tmpfile}" || true - - if [ -f "${iface_indexes_tmpfile}" ]; then - - while read -r pair; do - start=$(echo "${pair}" | cut -d':' -f1) - end=$(echo "${pair}" | cut -d':' -f2) - - if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then - iface_name=$(sed "${start}q;d" "${iface_filepath}" | awk '{print $2}') - BRIDGES="${iface_name}"$'\n'"${BRIDGES}" - fi - - done <"${iface_indexes_tmpfile}" - rm -f "${iface_indexes_tmpfile}" - fi - - done - IFS=$OLD_IFS - BRIDGES=$(echo "$BRIDGES" | grep -v '^\s*$' | sort | uniq) - if [[ -z "$BRIDGES" ]]; then - BRG="vmbr0" - echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" - else - # Build bridge menu with descriptions - BRIDGE_MENU_OPTIONS=() - while IFS= read -r bridge; do - if [[ -n "$bridge" ]]; then - # Get description from Proxmox built-in method - find comment for this specific bridge - description=$(grep -A 10 "iface $bridge" /etc/network/interfaces | grep '^#' | head -n1 | sed 's/^#\s*//') - if [[ -n "$description" ]]; then - BRIDGE_MENU_OPTIONS+=("$bridge" "${description}") - else - BRIDGE_MENU_OPTIONS+=("$bridge" " ") - fi - fi - done <<< "$BRIDGES" - - BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge: " 18 55 6 "${BRIDGE_MENU_OPTIONS[@]}" 3>&1 1>&2 2>&3) - if [[ -z "$BRG" ]]; then - exit_script - else - echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" - fi - fi - - # IPv4 methods: dhcp, static, none - while true; do - IPV4_METHOD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "IPv4 Address Management" \ - --menu "Select IPv4 Address Assignment Method:" 12 60 2 \ - "dhcp" "Automatic (DHCP, recommended)" \ - "static" "Static (manual entry)" \ - 3>&1 1>&2 2>&3) - - exit_status=$? - if [ $exit_status -ne 0 ]; then - exit_script - fi - - case "$IPV4_METHOD" in - dhcp) - NET="dhcp" - GATE="" - echo -e "${NETWORK}${BOLD}${DGN}IPv4: DHCP${CL}" - break - ;; - static) - # Static: call and validate CIDR address - while true; do - NET=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --inputbox "Enter Static IPv4 CIDR Address (e.g. 192.168.100.50/24)" 8 58 "" \ - --title "IPv4 ADDRESS" 3>&1 1>&2 2>&3) - if [ -z "$NET" ]; then - whiptail --msgbox "IPv4 address must not be empty." 8 58 - continue - elif [[ "$NET" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then - echo -e "${NETWORK}${BOLD}${DGN}IPv4 Address: ${BGN}$NET${CL}" - break - else - whiptail --msgbox "$NET is not a valid IPv4 CIDR address. Please enter a correct value!" 8 58 - fi - done - - # call and validate Gateway - while true; do - GATE1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --inputbox "Enter Gateway IP address for static IPv4" 8 58 "" \ - --title "Gateway IP" 3>&1 1>&2 2>&3) - if [ -z "$GATE1" ]; then - whiptail --msgbox "Gateway IP address cannot be empty." 8 58 - elif [[ ! "$GATE1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then - whiptail --msgbox "Invalid Gateway IP address format." 8 58 - else - GATE=",gw=$GATE1" - echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE1${CL}" - break - fi - done - break - ;; - esac - done - - # IPv6 Address Management selection - while true; do - IPV6_METHOD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --menu \ - "Select IPv6 Address Management Type:" 15 58 4 \ - "auto" "SLAAC/AUTO (recommended, default)" \ - "dhcp" "DHCPv6" \ - "static" "Static (manual entry)" \ - "none" "Disabled" \ - --default-item "auto" 3>&1 1>&2 2>&3) - [ $? -ne 0 ] && exit_script - - case "$IPV6_METHOD" in - auto) - echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}SLAAC/AUTO${CL}" - IPV6_ADDR="" - IPV6_GATE="" - break - ;; - dhcp) - echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}DHCPv6${CL}" - IPV6_ADDR="dhcp" - IPV6_GATE="" - break - ;; - static) - # Ask for static IPv6 address (CIDR notation, e.g., 2001:db8::1234/64) - while true; do - IPV6_ADDR=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox \ - "Set a static IPv6 CIDR address (e.g., 2001:db8::1234/64)" 8 58 "" \ - --title "IPv6 STATIC ADDRESS" 3>&1 1>&2 2>&3) || exit_script - if [[ "$IPV6_ADDR" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+(/[0-9]{1,3})$ ]]; then - echo -e "${NETWORK}${BOLD}${DGN}IPv6 Address: ${BGN}$IPV6_ADDR${CL}" - break - else - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox \ - "$IPV6_ADDR is an invalid IPv6 CIDR address. Please enter a valid IPv6 CIDR address (e.g., 2001:db8::1234/64)" 8 58 - fi - done - # Optional: ask for IPv6 gateway for static config - while true; do - IPV6_GATE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox \ - "Enter IPv6 gateway address (optional, leave blank for none)" 8 58 "" --title "IPv6 GATEWAY" 3>&1 1>&2 2>&3) - if [ -z "$IPV6_GATE" ]; then - IPV6_GATE="" - break - elif [[ "$IPV6_GATE" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+$ ]]; then - break - else - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox \ - "Invalid IPv6 gateway format." 8 58 - fi - done - break - ;; - none) - echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}Disabled${CL}" - IPV6_ADDR="none" - IPV6_GATE="" - break - ;; - *) - exit_script - ;; - esac - done - - if [ "$var_os" == "alpine" ]; then - APT_CACHER="" - APT_CACHER_IP="" - else - if APT_CACHER_IP=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set APT-Cacher IP (leave blank for none)" 8 58 --title "APT-Cacher IP" 3>&1 1>&2 2>&3); then - APT_CACHER="${APT_CACHER_IP:+yes}" - echo -e "${NETWORK}${BOLD}${DGN}APT-Cacher IP Address: ${BGN}${APT_CACHER_IP:-Default}${CL}" - else - exit_script - fi - fi - - # if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "IPv6" --yesno "Disable IPv6?" 10 58); then - # DISABLEIP6="yes" - # else - # DISABLEIP6="no" - # fi - # echo -e "${DISABLEIPV6}${BOLD}${DGN}Disable IPv6: ${BGN}$DISABLEIP6${CL}" - - if MTU1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default [The MTU of your selected vmbr, default is 1500])" 8 58 --title "MTU SIZE" 3>&1 1>&2 2>&3); then - if [ -z "$MTU1" ]; then - MTU1="Default" - MTU="" - else - MTU=",mtu=$MTU1" - fi - echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" - else - exit_script - fi - - if SD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a DNS Search Domain (leave blank for HOST)" 8 58 --title "DNS Search Domain" 3>&1 1>&2 2>&3); then - if [ -z "$SD" ]; then - SX=Host - SD="" - else - SX=$SD - SD="-searchdomain=$SD" - fi - echo -e "${SEARCH}${BOLD}${DGN}DNS Search Domain: ${BGN}$SX${CL}" - else - exit_script - fi - - if NX=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a DNS Server IP (leave blank for HOST)" 8 58 --title "DNS SERVER IP" 3>&1 1>&2 2>&3); then - if [ -z "$NX" ]; then - NX=Host - NS="" - else - NS="-nameserver=$NX" - fi - echo -e "${NETWORK}${BOLD}${DGN}DNS Server IP Address: ${BGN}$NX${CL}" - else - exit_script - fi - - if [ "$var_os" == "alpine" ] && [ "$NET" == "dhcp" ] && [ "$NX" != "Host" ]; then - UDHCPC_FIX="yes" - else - UDHCPC_FIX="no" - fi - export UDHCPC_FIX - - if MAC1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a MAC Address(leave blank for generated MAC)" 8 58 --title "MAC ADDRESS" 3>&1 1>&2 2>&3); then - if [ -z "$MAC1" ]; then - MAC1="Default" - MAC="" - else - MAC=",hwaddr=$MAC1" - echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" - fi - else - exit_script - fi - - if VLAN1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for no VLAN)" 8 58 --title "VLAN" 3>&1 1>&2 2>&3); then - if [ -z "$VLAN1" ]; then - VLAN1="Default" - VLAN="" - else - VLAN=",tag=$VLAN1" - fi - echo -e "${VLANTAG}${BOLD}${DGN}Vlan: ${BGN}$VLAN1${CL}" - else - exit_script - fi - - if ADV_TAGS=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Custom Tags?[If you remove all, there will be no tags!]" 8 58 "${TAGS}" --title "Advanced Tags" 3>&1 1>&2 2>&3); then - if [ -n "${ADV_TAGS}" ]; then - ADV_TAGS=$(echo "$ADV_TAGS" | tr -d '[:space:]') - TAGS="${ADV_TAGS}" - else - TAGS=";" - fi - echo -e "${NETWORK}${BOLD}${DGN}Tags: ${BGN}$TAGS${CL}" - else - exit_script - fi - - # --- SSH key provisioning (one dialog) --- - SSH_KEYS_FILE="$(mktemp)" - : >"$SSH_KEYS_FILE" - - IFS=$'\0' read -r -d '' -a _def_files < <(ssh_discover_default_files && printf '\0') - ssh_build_choices_from_files "${_def_files[@]}" - DEF_KEYS_COUNT="$COUNT" - - if [[ "$DEF_KEYS_COUNT" -gt 0 ]]; then - SSH_KEY_MODE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ - "Provision SSH keys for root:" 14 72 4 \ - "found" "Select from detected keys (${DEF_KEYS_COUNT})" \ - "manual" "Paste a single public key" \ - "folder" "Scan another folder (path or glob)" \ - "none" "No keys" 3>&1 1>&2 2>&3) || exit_script - else - SSH_KEY_MODE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ - "No host keys detected; choose manual/none:" 12 72 2 \ - "manual" "Paste a single public key" \ - "none" "No keys" 3>&1 1>&2 2>&3) || exit_script - fi - - case "$SSH_KEY_MODE" in - found) - SEL=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT HOST KEYS" \ - --checklist "Select one or more keys to import:" 20 140 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script - for tag in $SEL; do - tag="${tag%\"}" - tag="${tag#\"}" - line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) - [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" - done - ;; - manual) - SSH_AUTHORIZED_KEY="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --inputbox "Paste one SSH public key line (ssh-ed25519/ssh-rsa/...)" 10 72 --title "SSH Public Key" 3>&1 1>&2 2>&3)" - [[ -n "$SSH_AUTHORIZED_KEY" ]] && printf '%s\n' "$SSH_AUTHORIZED_KEY" >>"$SSH_KEYS_FILE" - ;; - folder) - GLOB_PATH="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --inputbox "Enter a folder or glob to scan (e.g. /root/.ssh/*.pub)" 10 72 --title "Scan Folder/Glob" 3>&1 1>&2 2>&3)" - if [[ -n "$GLOB_PATH" ]]; then - shopt -s nullglob - read -r -a _scan_files <<<"$GLOB_PATH" - shopt -u nullglob - if [[ "${#_scan_files[@]}" -gt 0 ]]; then - ssh_build_choices_from_files "${_scan_files[@]}" - if [[ "$COUNT" -gt 0 ]]; then - SEL=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT FOLDER KEYS" \ - --checklist "Select key(s) to import:" 20 78 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script - for tag in $SEL; do - tag="${tag%\"}" - tag="${tag#\"}" - line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) - [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" - done - else - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "No keys found in: $GLOB_PATH" 8 60 - fi - else - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "Path/glob returned no files." 8 60 - fi - fi - ;; - none) : ;; - esac - - # Dedupe + clean EOF - if [[ -s "$SSH_KEYS_FILE" ]]; then - sort -u -o "$SSH_KEYS_FILE" "$SSH_KEYS_FILE" - printf '\n' >>"$SSH_KEYS_FILE" - fi - - # SSH activate, if keys found or password set - if [[ -s "$SSH_KEYS_FILE" || "$PW" == -password* ]]; then - if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable root SSH access?" 10 58); then - SSH="yes" - else - SSH="no" - fi - else - SSH="no" - fi - echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}" - - export SSH_KEYS_FILE - if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "FUSE Support" --yesno "Enable FUSE support?\nRequired for tools like rclone, mergerfs, AppImage, etc." 10 58); then - ENABLE_FUSE="yes" - else - ENABLE_FUSE="no" - fi - echo -e "${FUSE}${BOLD}${DGN}Enable FUSE Support: ${BGN}$ENABLE_FUSE${CL}" - - if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then - VERBOSE="yes" - else - VERBOSE="no" - fi - echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}" - - if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create ${APP} LXC?" 10 58); then - echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}" - else - clear - header_info - echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings on node $PVEHOST_NAME${CL}" - advanced_settings - fi -} - -# ------------------------------------------------------------------------------ -# diagnostics_check() -# -# - Ensures diagnostics config file exists at /usr/local/community-scripts/diagnostics -# - Asks user whether to send anonymous diagnostic data -# - Saves DIAGNOSTICS=yes/no in the config file -# ------------------------------------------------------------------------------ -diagnostics_check() { - if ! [ -d "/usr/local/community-scripts" ]; then - mkdir -p /usr/local/community-scripts - fi - - if ! [ -f "/usr/local/community-scripts/diagnostics" ]; then - if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS" --yesno "Send Diagnostics of LXC Installation?\n\n(This only transmits data without user data, just RAM, CPU, LXC name, ...)" 10 58); then - cat </usr/local/community-scripts/diagnostics -DIAGNOSTICS=yes - -#This file is used to store the diagnostics settings for the Community-Scripts API. -#https://github.com/community-scripts/ProxmoxVED/discussions/1836 -#Your diagnostics will be sent to the Community-Scripts API for troubleshooting/statistical purposes. -#You can review the data at https://community-scripts.github.io/ProxmoxVE/data -#If you do not wish to send diagnostics, please set the variable 'DIAGNOSTICS' to "no" in /usr/local/community-scripts/diagnostics, or use the menue. -#This will disable the diagnostics feature. -#To send diagnostics, set the variable 'DIAGNOSTICS' to "yes" in /usr/local/community-scripts/diagnostics, or use the menue. -#This will enable the diagnostics feature. -#The following information will be sent: -#"disk_size" -#"core_count" -#"ram_size" -#"os_type" -#"os_version" -#"nsapp" -#"method" -#"pve_version" -#"status" -#If you have any concerns, please review the source code at /misc/build.func -EOF - DIAGNOSTICS="yes" - else - cat </usr/local/community-scripts/diagnostics -DIAGNOSTICS=no - -#This file is used to store the diagnostics settings for the Community-Scripts API. -#https://github.com/community-scripts/ProxmoxVED/discussions/1836 -#Your diagnostics will be sent to the Community-Scripts API for troubleshooting/statistical purposes. -#You can review the data at https://community-scripts.github.io/ProxmoxVE/data -#If you do not wish to send diagnostics, please set the variable 'DIAGNOSTICS' to "no" in /usr/local/community-scripts/diagnostics, or use the menue. -#This will disable the diagnostics feature. -#To send diagnostics, set the variable 'DIAGNOSTICS' to "yes" in /usr/local/community-scripts/diagnostics, or use the menue. -#This will enable the diagnostics feature. -#The following information will be sent: -#"disk_size" -#"core_count" -#"ram_size" -#"os_type" -#"os_version" -#"nsapp" -#"method" -#"pve_version" -#"status" -#If you have any concerns, please review the source code at /misc/build.func -EOF - DIAGNOSTICS="no" - fi - else - DIAGNOSTICS=$(awk -F '=' '/^DIAGNOSTICS/ {print $2}' /usr/local/community-scripts/diagnostics) - - fi - -} - -# ------------------------------------------------------------------------------ -# default_var_settings -# -# - Ensures /usr/local/community-scripts/default.vars exists (creates if missing) -# - Loads var_* values from default.vars (safe parser, no source/eval) -# - Precedence: ENV var_* > default.vars > built-in defaults -# - Maps var_verbose → VERBOSE -# - Calls base_settings "$VERBOSE" and echo_default -# ------------------------------------------------------------------------------ -default_var_settings() { - # Allowed var_* keys (alphabetically sorted) - local VAR_WHITELIST=( - var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_ctid var_disk var_fuse - var_gateway var_hostname var_ipv6_method var_ipv6_static var_mac var_mtu - var_net var_ns var_pw var_ram var_tags var_tun var_unprivileged - var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage - ) - - # Snapshot: environment variables (highest precedence) - declare -A _HARD_ENV=() - local _k - for _k in "${VAR_WHITELIST[@]}"; do - if printenv "$_k" >/dev/null 2>&1; then _HARD_ENV["$_k"]=1; fi - done - - # Find default.vars location - local _find_default_vars - _find_default_vars() { - local f - for f in \ - /usr/local/community-scripts/default.vars \ - "$HOME/.config/community-scripts/default.vars" \ - "./default.vars"; do - [ -f "$f" ] && { - echo "$f" - return 0 - } - done - return 1 - } - # Allow override of storages via env (for non-interactive use cases) - [ -n "${var_template_storage:-}" ] && TEMPLATE_STORAGE="$var_template_storage" - [ -n "${var_container_storage:-}" ] && CONTAINER_STORAGE="$var_container_storage" - - # Create once, with storages already selected, no var_ctid/var_hostname lines - local _ensure_default_vars - _ensure_default_vars() { - _find_default_vars >/dev/null 2>&1 && return 0 - - local canonical="/usr/local/community-scripts/default.vars" - msg_info "No default.vars found. Creating ${canonical}" - mkdir -p /usr/local/community-scripts - - # Pick storages before writing the file (always ask unless only one) - # Create a minimal temp file to write into - : >"$canonical" - - # Base content (no var_ctid / var_hostname here) - cat >"$canonical" <<'EOF' -# Community-Scripts defaults (var_* only). Lines starting with # are comments. -# Precedence: ENV var_* > default.vars > built-ins. -# Keep keys alphabetically sorted. - -# Container type -var_unprivileged=1 - -# Resources -var_cpu=1 -var_disk=4 -var_ram=1024 - -# Network -var_brg=vmbr0 -var_net=dhcp -var_ipv6_method=none -# var_gateway= -# var_ipv6_static= -# var_vlan= -# var_mtu= -# var_mac= -# var_ns= - -# SSH -var_ssh=no -# var_ssh_authorized_key= - -# APT cacher (optional) -# var_apt_cacher=yes -# var_apt_cacher_ip=192.168.1.10 - -# Features/Tags/verbosity -var_fuse=no -var_tun=no -var_tags=community-script -var_verbose=no - -# Security (root PW) – empty => autologin -# var_pw= -EOF - - # Now choose storages (always prompt unless just one exists) - choose_and_set_storage_for_file "$canonical" template - choose_and_set_storage_for_file "$canonical" container - - chmod 0644 "$canonical" - msg_ok "Created ${canonical}" - } - - # Whitelist check - local _is_whitelisted_key - _is_whitelisted_key() { - local k="$1" - local w - for w in "${VAR_WHITELIST[@]}"; do [ "$k" = "$w" ] && return 0; done - return 1 - } - - # Safe parser for KEY=VALUE lines - local _load_vars_file - _load_vars_file() { - local file="$1" - [ -f "$file" ] || return 0 - msg_info "Loading defaults from ${file}" - local line key val - while IFS= read -r line || [ -n "$line" ]; do - line="${line#"${line%%[![:space:]]*}"}" - line="${line%"${line##*[![:space:]]}"}" - [[ -z "$line" || "$line" == \#* ]] && continue - if [[ "$line" =~ ^([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then - local var_key="${BASH_REMATCH[1]}" - local var_val="${BASH_REMATCH[2]}" - - [[ "$var_key" != var_* ]] && continue - _is_whitelisted_key "$var_key" || { - msg_debug "Ignore non-whitelisted ${var_key}" - continue - } - - # Strip quotes - if [[ "$var_val" =~ ^\"(.*)\"$ ]]; then - var_val="${BASH_REMATCH[1]}" - elif [[ "$var_val" =~ ^\'(.*)\'$ ]]; then - var_val="${BASH_REMATCH[1]}" - fi - - # Unsafe characters - case $var_val in - \"*\") - var_val=${var_val#\"} - var_val=${var_val%\"} - ;; - \'*\') - var_val=${var_val#\'} - var_val=${var_val%\'} - ;; - esac # Hard env wins - [[ -n "${_HARD_ENV[$var_key]:-}" ]] && continue - # Set only if not already exported - [[ -z "${!var_key+x}" ]] && export "${var_key}=${var_val}" - else - msg_warn "Malformed line in ${file}: ${line}" - fi - done <"$file" - msg_ok "Loaded ${file}" - } - - # 1) Ensure file exists - _ensure_default_vars - - # 2) Load file - local dv - dv="$(_find_default_vars)" || { - msg_error "default.vars not found after ensure step" - return 1 - } - _load_vars_file "$dv" - - # 3) Map var_verbose → VERBOSE - if [[ -n "${var_verbose:-}" ]]; then - case "${var_verbose,,}" in 1 | yes | true | on) VERBOSE="yes" ;; 0 | no | false | off) VERBOSE="no" ;; *) VERBOSE="${var_verbose}" ;; esac - else - VERBOSE="no" - fi - - # 4) Apply base settings and show summary - METHOD="mydefaults-global" - base_settings "$VERBOSE" - header_info - echo -e "${DEFAULT}${BOLD}${BL}Using My Defaults (default.vars) on node $PVEHOST_NAME${CL}" - echo_default -} - -# ------------------------------------------------------------------------------ -# get_app_defaults_path() -# -# - Returns full path for app-specific defaults file -# - Example: /usr/local/community-scripts/defaults/.vars -# ------------------------------------------------------------------------------ - -get_app_defaults_path() { - local n="${NSAPP:-${APP,,}}" - echo "/usr/local/community-scripts/defaults/${n}.vars" -} - -# ------------------------------------------------------------------------------ -# maybe_offer_save_app_defaults -# -# - Called after advanced_settings returned with fully chosen values. -# - If no .vars exists, offers to persist current advanced settings -# into /usr/local/community-scripts/defaults/.vars -# - Only writes whitelisted var_* keys. -# - Extracts raw values from flags like ",gw=..." ",mtu=..." etc. -# ------------------------------------------------------------------------------ -if ! declare -p VAR_WHITELIST >/dev/null 2>&1; then - declare -ag VAR_WHITELIST=( - var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_ctid var_disk var_fuse - var_gateway var_hostname var_ipv6_method var_ipv6_static var_mac var_mtu - var_net var_ns var_pw var_ram var_tags var_tun var_unprivileged - var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage - ) -fi - -_is_whitelisted_key() { - local k="$1" - local w - for w in "${VAR_WHITELIST[@]}"; do [[ "$k" == "$w" ]] && return 0; done - return 1 -} - -_sanitize_value() { - # Disallow Command-Substitution / Shell-Meta - case "$1" in - *'$('* | *'`'* | *';'* | *'&'* | *'<('*) - echo "" - return 0 - ;; - esac - echo "$1" -} - -# Map-Parser: read var_* from file into _VARS_IN associative array -declare -A _VARS_IN -_load_vars_file() { - local file="$1" - [ -f "$file" ] || return 0 - msg_info "Loading defaults from ${file}" - local line key val - while IFS= read -r line || [ -n "$line" ]; do - line="${line#"${line%%[![:space:]]*}"}" - line="${line%"${line##*[![:space:]]}"}" - [ -z "$line" ] && continue - case "$line" in - \#*) continue ;; - esac - key=$(printf "%s" "$line" | cut -d= -f1) - val=$(printf "%s" "$line" | cut -d= -f2-) - case "$key" in - var_*) - if _is_whitelisted_key "$key"; then - [ -z "${!key+x}" ] && export "$key=$val" - fi - ;; - esac - done <"$file" - msg_ok "Loaded ${file}" -} - -# Diff function for two var_* files -> produces human-readable diff list for $1 (old) vs $2 (new) -_build_vars_diff() { - local oldf="$1" newf="$2" - local k - local -A OLD=() NEW=() - _load_vars_file_to_map "$oldf" - for k in "${!_VARS_IN[@]}"; do OLD["$k"]="${_VARS_IN[$k]}"; done - _load_vars_file_to_map "$newf" - for k in "${!_VARS_IN[@]}"; do NEW["$k"]="${_VARS_IN[$k]}"; done - - local out - out+="# Diff for ${APP} (${NSAPP})\n" - out+="# Old: ${oldf}\n# New: ${newf}\n\n" - - local found_change=0 - - # Changed & Removed - for k in "${!OLD[@]}"; do - if [[ -v NEW["$k"] ]]; then - if [[ "${OLD[$k]}" != "${NEW[$k]}" ]]; then - out+="~ ${k}\n - old: ${OLD[$k]}\n + new: ${NEW[$k]}\n" - found_change=1 - fi - else - out+="- ${k}\n - old: ${OLD[$k]}\n" - found_change=1 - fi - done - - # Added - for k in "${!NEW[@]}"; do - if [[ ! -v OLD["$k"] ]]; then - out+="+ ${k}\n + new: ${NEW[$k]}\n" - found_change=1 - fi - done - - if [[ $found_change -eq 0 ]]; then - out+="(No differences)\n" - fi - - printf "%b" "$out" -} - -# Build a temporary .vars file from current advanced settings -_build_current_app_vars_tmp() { - tmpf="$(mktemp /tmp/${NSAPP:-app}.vars.new.XXXXXX)" - - # NET/GW - _net="${NET:-}" - _gate="" - case "${GATE:-}" in - ,gw=*) _gate=$(echo "$GATE" | sed 's/^,gw=//') ;; - esac - - # IPv6 - _ipv6_method="${IPV6_METHOD:-auto}" - _ipv6_static="" - _ipv6_gateway="" - if [ "$_ipv6_method" = "static" ]; then - _ipv6_static="${IPV6_ADDR:-}" - _ipv6_gateway="${IPV6_GATE:-}" - fi - - # MTU/VLAN/MAC - _mtu="" - _vlan="" - _mac="" - case "${MTU:-}" in - ,mtu=*) _mtu=$(echo "$MTU" | sed 's/^,mtu=//') ;; - esac - case "${VLAN:-}" in - ,tag=*) _vlan=$(echo "$VLAN" | sed 's/^,tag=//') ;; - esac - case "${MAC:-}" in - ,hwaddr=*) _mac=$(echo "$MAC" | sed 's/^,hwaddr=//') ;; - esac - - # DNS / Searchdomain - _ns="" - _searchdomain="" - case "${NS:-}" in - -nameserver=*) _ns=$(echo "$NS" | sed 's/^-nameserver=//') ;; - esac - case "${SD:-}" in - -searchdomain=*) _searchdomain=$(echo "$SD" | sed 's/^-searchdomain=//') ;; - esac - - # SSH / APT / Features - _ssh="${SSH:-no}" - _ssh_auth="${SSH_AUTHORIZED_KEY:-}" - _apt_cacher="${APT_CACHER:-}" - _apt_cacher_ip="${APT_CACHER_IP:-}" - _fuse="${ENABLE_FUSE:-no}" - _tun="${ENABLE_TUN:-no}" - _tags="${TAGS:-}" - _verbose="${VERBOSE:-no}" - - # Type / Resources / Identity - _unpriv="${CT_TYPE:-1}" - _cpu="${CORE_COUNT:-1}" - _ram="${RAM_SIZE:-1024}" - _disk="${DISK_SIZE:-4}" - _hostname="${HN:-$NSAPP}" - - # Storage - _tpl_storage="${TEMPLATE_STORAGE:-${var_template_storage:-}}" - _ct_storage="${CONTAINER_STORAGE:-${var_container_storage:-}}" - - { - echo "# App-specific defaults for ${APP} (${NSAPP})" - echo "# Generated on $(date -u '+%Y-%m-%dT%H:%M:%SZ')" - echo - - echo "var_unprivileged=$(_sanitize_value "$_unpriv")" - echo "var_cpu=$(_sanitize_value "$_cpu")" - echo "var_ram=$(_sanitize_value "$_ram")" - echo "var_disk=$(_sanitize_value "$_disk")" - - [ -n "${BRG:-}" ] && echo "var_brg=$(_sanitize_value "$BRG")" - [ -n "$_net" ] && echo "var_net=$(_sanitize_value "$_net")" - [ -n "$_gate" ] && echo "var_gateway=$(_sanitize_value "$_gate")" - [ -n "$_mtu" ] && echo "var_mtu=$(_sanitize_value "$_mtu")" - [ -n "$_vlan" ] && echo "var_vlan=$(_sanitize_value "$_vlan")" - [ -n "$_mac" ] && echo "var_mac=$(_sanitize_value "$_mac")" - [ -n "$_ns" ] && echo "var_ns=$(_sanitize_value "$_ns")" - - [ -n "$_ipv6_method" ] && echo "var_ipv6_method=$(_sanitize_value "$_ipv6_method")" - [ -n "$_ipv6_static" ] && echo "var_ipv6_static=$(_sanitize_value "$_ipv6_static")" - - [ -n "$_ssh" ] && echo "var_ssh=$(_sanitize_value "$_ssh")" - [ -n "$_ssh_auth" ] && echo "var_ssh_authorized_key=$(_sanitize_value "$_ssh_auth")" - - [ -n "$_apt_cacher" ] && echo "var_apt_cacher=$(_sanitize_value "$_apt_cacher")" - [ -n "$_apt_cacher_ip" ] && echo "var_apt_cacher_ip=$(_sanitize_value "$_apt_cacher_ip")" - - [ -n "$_fuse" ] && echo "var_fuse=$(_sanitize_value "$_fuse")" - [ -n "$_tun" ] && echo "var_tun=$(_sanitize_value "$_tun")" - [ -n "$_tags" ] && echo "var_tags=$(_sanitize_value "$_tags")" - [ -n "$_verbose" ] && echo "var_verbose=$(_sanitize_value "$_verbose")" - - [ -n "$_hostname" ] && echo "var_hostname=$(_sanitize_value "$_hostname")" - [ -n "$_searchdomain" ] && echo "var_searchdomain=$(_sanitize_value "$_searchdomain")" - - [ -n "$_tpl_storage" ] && echo "var_template_storage=$(_sanitize_value "$_tpl_storage")" - [ -n "$_ct_storage" ] && echo "var_container_storage=$(_sanitize_value "$_ct_storage")" - } >"$tmpf" - - echo "$tmpf" -} - -# ------------------------------------------------------------------------------ -# maybe_offer_save_app_defaults() -# -# - Called after advanced_settings() -# - Offers to save current values as app defaults if not existing -# - If file exists: shows diff and allows Update, Keep, View Diff, or Cancel -# ------------------------------------------------------------------------------ -maybe_offer_save_app_defaults() { - local app_vars_path - app_vars_path="$(get_app_defaults_path)" - - # always build from current settings - local new_tmp diff_tmp - new_tmp="$(_build_current_app_vars_tmp)" - diff_tmp="$(mktemp -p /tmp "${NSAPP:-app}.vars.diff.XXXXXX")" - - # 1) if no file → offer to create - if [[ ! -f "$app_vars_path" ]]; then - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --yesno "Save these advanced settings as defaults for ${APP}?\n\nThis will create:\n${app_vars_path}" 12 72; then - mkdir -p "$(dirname "$app_vars_path")" - install -m 0644 "$new_tmp" "$app_vars_path" - msg_ok "Saved app defaults: ${app_vars_path}" - fi - rm -f "$new_tmp" "$diff_tmp" - return 0 - fi - - # 2) if file exists → build diff - _build_vars_diff "$app_vars_path" "$new_tmp" >"$diff_tmp" - - # if no differences → do nothing - if grep -q "^(No differences)$" "$diff_tmp"; then - rm -f "$new_tmp" "$diff_tmp" - return 0 - fi - - # 3) if file exists → show menu with default selection "Update Defaults" - local app_vars_file - app_vars_file="$(basename "$app_vars_path")" - - while true; do - local sel - sel="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "APP DEFAULTS – ${APP}" \ - --menu "Differences detected. What do you want to do?" 20 78 10 \ - "Update Defaults" "Write new values to ${app_vars_file}" \ - "Keep Current" "Keep existing defaults (no changes)" \ - "View Diff" "Show a detailed diff" \ - "Cancel" "Abort without changes" \ - --default-item "Update Defaults" \ - 3>&1 1>&2 2>&3)" || { sel="Cancel"; } - - case "$sel" in - "Update Defaults") - install -m 0644 "$new_tmp" "$app_vars_path" - msg_ok "Updated app defaults: ${app_vars_path}" - break - ;; - "Keep Current") - msg_info "Keeping current app defaults: ${app_vars_path}" - break - ;; - "View Diff") - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "Diff – ${APP}" \ - --scrolltext --textbox "$diff_tmp" 25 100 - ;; - "Cancel" | *) - msg_info "Canceled. No changes to app defaults." - break - ;; - esac - done - - rm -f "$new_tmp" "$diff_tmp" -} - -ensure_storage_selection_for_vars_file() { - local vf="$1" - - # Read stored values (if any) - local tpl ct - tpl=$(grep -E '^var_template_storage=' "$vf" | cut -d= -f2-) - ct=$(grep -E '^var_container_storage=' "$vf" | cut -d= -f2-) - - # Wenn beide Werte schon existieren → übernehmen - if [[ -n "$tpl" && -n "$ct" ]]; then - TEMPLATE_STORAGE="$tpl" - CONTAINER_STORAGE="$ct" - echo_storage_summary_from_file "$vf" - return 0 - fi - - # --- Erstmalige Auswahl: beide Abfragen --- - select_storage template - local tpl_sel="$STORAGE_RESULT" - - select_storage container - local ct_sel="$STORAGE_RESULT" - - # --- Zusammenfassung + Nachfrage --- - if whiptail --backtitle "Community Scripts" --title "Default Storage" \ - --yesno "Template-Storage --> $tpl_sel\nContainer-Storage --> $ct_sel\n\nSave as global defaults?" \ - 12 70; then - sed -i '/^[#[:space:]]*var_template_storage=/d' "$vf" - sed -i '/^[#[:space:]]*var_container_storage=/d' "$vf" - echo "var_template_storage=$tpl_sel" >>"$vf" - echo "var_container_storage=$ct_sel" >>"$vf" - else - sed -i '/^[#[:space:]]*var_template_storage=/d' "$vf" - sed -i '/^[#[:space:]]*var_container_storage=/d' "$vf" - echo "# var_template_storage=$tpl_sel" >>"$vf" - echo "# var_container_storage=$ct_sel" >>"$vf" - fi - - TEMPLATE_STORAGE="$tpl_sel" - CONTAINER_STORAGE="$ct_sel" - msg_ok "Using Template-Storage → $tpl_sel" - msg_ok "Using Container-Storage → $ct_sel" -} - -diagnostics_menu() { - if [ "${DIAGNOSTICS:-no}" = "yes" ]; then - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "DIAGNOSTIC SETTINGS" \ - --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ - --yes-button "No" --no-button "Back"; then - DIAGNOSTICS="no" - sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=no/' /usr/local/community-scripts/diagnostics - whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 - fi - else - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "DIAGNOSTIC SETTINGS" \ - --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ - --yes-button "Yes" --no-button "Back"; then - DIAGNOSTICS="yes" - sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=yes/' /usr/local/community-scripts/diagnostics - whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 - fi - fi -} - -# ------------------------------------------------------------------------------ -# install_script() -# -# - Main entrypoint for installation mode -# - Runs safety checks (pve_check, root_check, maxkeys_check, diagnostics_check) -# - Builds interactive menu (Default, Verbose, Advanced, My Defaults, App Defaults, Diagnostics, Storage, Exit) -# - Applies chosen settings and triggers container build -# ------------------------------------------------------------------------------ -install_script() { - pve_check - shell_check - root_check - arch_check - ssh_check - maxkeys_check - diagnostics_check - - if systemctl is-active -q ping-instances.service; then - systemctl -q stop ping-instances.service - fi - - NEXTID=$(pvesh get /cluster/nextid) - timezone=$(cat /etc/timezone) - header_info - - # --- Support CLI argument as direct preset (default, advanced, …) --- - CHOICE="${mode:-${1:-}}" - - # If no CLI argument → show whiptail menu - if [ -z "$CHOICE" ]; then - local menu_items=( - "1" "Default Install" - "2" "Advanced Install" - "3" "My Defaults" - ) - - if [ -f "$(get_app_defaults_path)" ]; then - menu_items+=("4" "App Defaults for ${APP}") - menu_items+=("5" "Settings") - else - menu_items+=("4" "Settings") - fi - - TMP_CHOICE=$(whiptail \ - --backtitle "Proxmox VE Helper Scripts" \ - --title "Community-Scripts Options" \ - --ok-button "Select" --cancel-button "Exit Script" \ - --notags \ - --menu "\nChoose an option:\n Use TAB or Arrow keys to navigate, ENTER to select.\n" \ - 20 60 9 \ - "${menu_items[@]}" \ - --default-item "1" \ - 3>&1 1>&2 2>&3) || exit_script - CHOICE="$TMP_CHOICE" - fi - - # --- Main case --- - case "$CHOICE" in - 1 | default | DEFAULT) - header_info - echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME${CL}" - VERBOSE="no" - METHOD="default" - base_settings "$VERBOSE" - echo_default - [[ -f /usr/local/community-scripts/default.vars ]] || { - mkdir -p /usr/local/community-scripts - touch /usr/local/community-scripts/default.vars - } - ensure_storage_selection_for_vars_file "/usr/local/community-scripts/default.vars" - - ;; - 2 | advanced | ADVANCED) - header_info - echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Install on node $PVEHOST_NAME${CL}" - METHOD="advanced" - base_settings - advanced_settings - [[ -f /usr/local/community-scripts/default.vars ]] || { - mkdir -p /usr/local/community-scripts - touch /usr/local/community-scripts/default.vars - } - ensure_storage_selection_for_vars_file "/usr/local/community-scripts/default.vars" - - maybe_offer_save_app_defaults - ;; - 3 | mydefaults | MYDEFAULTS) - default_var_settings || { - msg_error "Failed to apply default.vars" - exit 1 - } - ensure_storage_selection_for_vars_file "/usr/local/community-scripts/default.vars" - ;; - 4 | appdefaults | APPDEFAULTS) - if [ -f "$(get_app_defaults_path)" ]; then - header_info - echo -e "${DEFAULT}${BOLD}${BL}Using App Defaults for ${APP} on node $PVEHOST_NAME${CL}" - METHOD="appdefaults" - base_settings - _load_vars_file "$(get_app_defaults_path)" - echo_default - ensure_storage_selection_for_vars_file "$(get_app_defaults_path)" - else - msg_error "No App Defaults available for ${APP}" - exit 1 - fi - ;; - 5 | settings | SETTINGS) - settings_menu - ;; - *) - echo -e "${CROSS}${RD}Invalid option: $CHOICE${CL}" - exit 1 - ;; - esac -} - -edit_default_storage() { - local vf="/usr/local/community-scripts/default.vars" - - # make sure file exists - if [[ ! -f "$vf" ]]; then - # still create - mkdir -p "$(dirname "$vf")" - touch "$vf" - - if select_storage template; then - echo "var_template_storage=$STORAGE_RESULT" >>"$vf" - fi - - if select_storage container; then - echo "var_container_storage=$STORAGE_RESULT" >>"$vf" - fi - fi - - # reuse the same Whiptail selection we already have - ensure_storage_selection_for_vars_file "$vf" -} - -settings_menu() { - while true; do - local settings_items=( - "1" "Manage API-Diagnostic Setting" - "2" "Edit Default.vars" - "3" "Edit Default Storage" - ) - if [ -f "$(get_app_defaults_path)" ]; then - settings_items+=("4" "Edit App.vars for ${APP}") - settings_items+=("5" "Exit") - else - settings_items+=("4" "Exit") - fi - - local choice - choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ - --title "Community-Scripts SETTINGS Menu" \ - --ok-button "OK" --cancel-button "Back" \ - --menu "\n\nChoose a settings option:\n\nUse TAB or Arrow keys to navigate, ENTER to select." 20 60 9 \ - "${settings_items[@]}" \ - 3>&1 1>&2 2>&3) || break - - case "$choice" in - 1) diagnostics_menu ;; - 2) ${EDITOR:-nano} /usr/local/community-scripts/default.vars ;; - 3) edit_default_storage ;; - 4) - if [ -f "$(get_app_defaults_path)" ]; then - ${EDITOR:-nano} "$(get_app_defaults_path)" - else - exit_script - fi - ;; - 5) exit_script ;; - esac - done -} - -# ===== Unified storage selection & writing to vars files ===== -_write_storage_to_vars() { - # $1 = vars_file, $2 = key (var_container_storage / var_template_storage), $3 = value - local vf="$1" key="$2" val="$3" - # remove uncommented and commented versions to avoid duplicates - sed -i "/^[#[:space:]]*${key}=/d" "$vf" - echo "${key}=${val}" >>"$vf" -} - -choose_and_set_storage_for_file() { - # $1 = vars_file, $2 = class ('container'|'template') - local vf="$1" class="$2" key="" current="" - case "$class" in - container) key="var_container_storage" ;; - template) key="var_template_storage" ;; - *) - msg_error "Unknown storage class: $class" - return 1 - ;; - esac - - current=$(awk -F= -v k="^${key}=" '$0 ~ k {print $2; exit}' "$vf") - - # If only one storage exists for the content type, auto-pick. Else always ask (your wish #4). - local content="rootdir" - [[ "$class" == "template" ]] && content="vztmpl" - local count - count=$(pvesm status -content "$content" | awk 'NR>1{print $1}' | wc -l) - - if [[ "$count" -eq 1 ]]; then - STORAGE_RESULT=$(pvesm status -content "$content" | awk 'NR>1{print $1; exit}') - STORAGE_INFO="" - else - # If the current value is preselectable, we could show it, but per your requirement we always offer selection - select_storage "$class" || return 1 - fi - - _write_storage_to_vars "$vf" "$key" "$STORAGE_RESULT" - - # Keep environment in sync for later steps (e.g. app-default save) - if [[ "$class" == "container" ]]; then - export var_container_storage="$STORAGE_RESULT" - export CONTAINER_STORAGE="$STORAGE_RESULT" - else - export var_template_storage="$STORAGE_RESULT" - export TEMPLATE_STORAGE="$STORAGE_RESULT" - fi - - msg_ok "Updated ${key} → ${STORAGE_RESULT}" -} - -echo_storage_summary_from_file() { - local vars_file="$1" - local ct_store tpl_store - ct_store=$(awk -F= '/^var_container_storage=/ {print $2; exit}' "$vars_file") - tpl_store=$(awk -F= '/^var_template_storage=/ {print $2; exit}' "$vars_file") - if [ -n "$tpl" ]; then - TEMPLATE_STORAGE="$tpl" - else - choose_and_set_storage_for_file "$vf" template - fi - - if [ -n "$ct" ]; then - CONTAINER_STORAGE="$ct" - else - choose_and_set_storage_for_file "$vf" container - fi -} - -# ------------------------------------------------------------------------------ -# check_container_resources() -# -# - Compares host RAM/CPU with required values -# - Warns if under-provisioned and asks user to continue or abort -# ------------------------------------------------------------------------------ -check_container_resources() { - current_ram=$(free -m | awk 'NR==2{print $2}') - current_cpu=$(nproc) - - if [[ "$current_ram" -lt "$var_ram" ]] || [[ "$current_cpu" -lt "$var_cpu" ]]; then - echo -e "\n${INFO}${HOLD} ${GN}Required: ${var_cpu} CPU, ${var_ram}MB RAM ${CL}| ${RD}Current: ${current_cpu} CPU, ${current_ram}MB RAM${CL}" - echo -e "${YWB}Please ensure that the ${APP} LXC is configured with at least ${var_cpu} vCPU and ${var_ram} MB RAM for the build process.${CL}\n" - echo -ne "${INFO}${HOLD} May cause data loss! ${INFO} Continue update with under-provisioned LXC? " - read -r prompt - if [[ ! ${prompt,,} =~ ^(yes)$ ]]; then - echo -e "${CROSS}${HOLD} ${YWB}Exiting based on user input.${CL}" - exit 1 - fi - else - echo -e "" - fi -} - -# ------------------------------------------------------------------------------ -# check_container_storage() -# -# - Checks /boot partition usage -# - Warns if usage >80% and asks user confirmation before proceeding -# ------------------------------------------------------------------------------ -check_container_storage() { - total_size=$(df /boot --output=size | tail -n 1) - local used_size=$(df /boot --output=used | tail -n 1) - usage=$((100 * used_size / total_size)) - if ((usage > 80)); then - echo -e "${INFO}${HOLD} ${YWB}Warning: Storage is dangerously low (${usage}%).${CL}" - echo -ne "Continue anyway? " - read -r prompt - if [[ ! ${prompt,,} =~ ^(y|yes)$ ]]; then - echo -e "${CROSS}${HOLD}${YWB}Exiting based on user input.${CL}" - exit 1 - fi - fi -} - -# ------------------------------------------------------------------------------ -# ssh_extract_keys_from_file() -# -# - Extracts valid SSH public keys from given file -# - Supports RSA, Ed25519, ECDSA and filters out comments/invalid lines -# ------------------------------------------------------------------------------ -ssh_extract_keys_from_file() { - local f="$1" - [[ -r "$f" ]] || return 0 - tr -d '\r' <"$f" | awk ' - /^[[:space:]]*#/ {next} - /^[[:space:]]*$/ {next} - # nackt: typ base64 [comment] - /^(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/ {print; next} - # mit Optionen: finde ab erstem Key-Typ - { - match($0, /(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/) - if (RSTART>0) { print substr($0, RSTART) } - } - ' -} - -# ------------------------------------------------------------------------------ -# ssh_build_choices_from_files() -# -# - Builds interactive whiptail checklist of available SSH keys -# - Generates fingerprint, type and comment for each key -# ------------------------------------------------------------------------------ -ssh_build_choices_from_files() { - local -a files=("$@") - CHOICES=() - COUNT=0 - MAPFILE="$(mktemp)" - local id key typ fp cmt base ln=0 - - for f in "${files[@]}"; do - [[ -f "$f" && -r "$f" ]] || continue - base="$(basename -- "$f")" - case "$base" in - known_hosts | known_hosts.* | config) continue ;; - id_*) [[ "$f" != *.pub ]] && continue ;; - esac - - # map every key in file - while IFS= read -r key; do - [[ -n "$key" ]] || continue - - typ="" - fp="" - cmt="" - # Only the pure key part (without options) is already included in ‘key’. - read -r _typ _b64 _cmt <<<"$key" - typ="${_typ:-key}" - cmt="${_cmt:-}" - # Fingerprint via ssh-keygen (if available) - if command -v ssh-keygen >/dev/null 2>&1; then - fp="$(printf '%s\n' "$key" | ssh-keygen -lf - 2>/dev/null | awk '{print $2}')" - fi - # Label shorten - [[ ${#cmt} -gt 40 ]] && cmt="${cmt:0:37}..." - - ln=$((ln + 1)) - COUNT=$((COUNT + 1)) - id="K${COUNT}" - echo "${id}|${key}" >>"$MAPFILE" - CHOICES+=("$id" "[$typ] ${fp:+$fp }${cmt:+$cmt }— ${base}" "OFF") - done < <(ssh_extract_keys_from_file "$f") - done -} - -# ------------------------------------------------------------------------------ -# ssh_discover_default_files() -# -# - Scans standard paths for SSH keys -# - Includes ~/.ssh/*.pub, /etc/ssh/authorized_keys, etc. -# ------------------------------------------------------------------------------ -ssh_discover_default_files() { - local -a cand=() - shopt -s nullglob - cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2) - cand+=(/root/.ssh/*.pub) - cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*) - shopt -u nullglob - printf '%s\0' "${cand[@]}" -} - -# ------------------------------------------------------------------------------ -# start() -# -# - Entry point of script -# - On Proxmox host: calls install_script -# - In silent mode: runs update_script -# - Otherwise: shows update/setting menu -# ------------------------------------------------------------------------------ -start() { - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) - if command -v pveversion >/dev/null 2>&1; then - install_script || return 0 - return 0 - elif [ ! -z ${PHS_SILENT+x} ] && [[ "${PHS_SILENT}" == "1" ]]; then - VERBOSE="no" - set_std_mode - update_script - else - CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "${APP} LXC Update/Setting" --menu \ - "Support/Update functions for ${APP} LXC. Choose an option:" \ - 12 60 3 \ - "1" "YES (Silent Mode)" \ - "2" "YES (Verbose Mode)" \ - "3" "NO (Cancel Update)" --nocancel --default-item "1" 3>&1 1>&2 2>&3) - - case "$CHOICE" in - 1) - VERBOSE="no" - set_std_mode - ;; - 2) - VERBOSE="yes" - set_std_mode - ;; - 3) - clear - exit_script - exit - ;; - esac - update_script - fi -} - -# ------------------------------------------------------------------------------ -# build_container() -# -# - Creates and configures the LXC container -# - Builds network string and applies features (FUSE, TUN, VAAPI passthrough) -# - Starts container and waits for network connectivity -# - Installs base packages, SSH keys, and runs -install.sh -# ------------------------------------------------------------------------------ -build_container() { - # if [ "$VERBOSE" == "yes" ]; then set -x; fi - - NET_STRING="-net0 name=eth0,bridge=${BRG:-vmbr0}" - - # MAC - if [[ -n "$MAC" ]]; then - case "$MAC" in - ,hwaddr=*) NET_STRING+="$MAC" ;; - *) NET_STRING+=",hwaddr=$MAC" ;; - esac - fi - - # IP (immer zwingend, Standard dhcp) - NET_STRING+=",ip=${NET:-dhcp}" - - # Gateway - if [[ -n "$GATE" ]]; then - case "$GATE" in - ,gw=*) NET_STRING+="$GATE" ;; - *) NET_STRING+=",gw=$GATE" ;; - esac - fi - - # VLAN - if [[ -n "$VLAN" ]]; then - case "$VLAN" in - ,tag=*) NET_STRING+="$VLAN" ;; - *) NET_STRING+=",tag=$VLAN" ;; - esac - fi - - # MTU - if [[ -n "$MTU" ]]; then - case "$MTU" in - ,mtu=*) NET_STRING+="$MTU" ;; - *) NET_STRING+=",mtu=$MTU" ;; - esac - fi - - # IPv6 Handling - case "$IPV6_METHOD" in - auto) NET_STRING="$NET_STRING,ip6=auto" ;; - dhcp) NET_STRING="$NET_STRING,ip6=dhcp" ;; - static) - NET_STRING="$NET_STRING,ip6=$IPV6_ADDR" - [ -n "$IPV6_GATE" ] && NET_STRING="$NET_STRING,gw6=$IPV6_GATE" - ;; - none) ;; - esac - - if [ "$CT_TYPE" == "1" ]; then - FEATURES="keyctl=1,nesting=1" - else - FEATURES="nesting=1" - fi - - if [ "$ENABLE_FUSE" == "yes" ]; then - FEATURES="$FEATURES,fuse=1" - fi - - TEMP_DIR=$(mktemp -d) - pushd "$TEMP_DIR" >/dev/null - if [ "$var_os" == "alpine" ]; then - export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-install.func)" - else - export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" - fi - export DIAGNOSTICS="$DIAGNOSTICS" - export RANDOM_UUID="$RANDOM_UUID" - export CACHER="$APT_CACHER" - export CACHER_IP="$APT_CACHER_IP" - export tz="$timezone" - export APPLICATION="$APP" - export app="$NSAPP" - export PASSWORD="$PW" - export VERBOSE="$VERBOSE" - export SSH_ROOT="${SSH}" - export SSH_AUTHORIZED_KEY - export CTID="$CT_ID" - export CTTYPE="$CT_TYPE" - export ENABLE_FUSE="$ENABLE_FUSE" - export ENABLE_TUN="$ENABLE_TUN" - export PCT_OSTYPE="$var_os" - export PCT_OSVERSION="${var_version%%.*}" - export PCT_DISK_SIZE="$DISK_SIZE" - export PCT_OPTIONS=" - -features $FEATURES - -hostname $HN - -tags $TAGS - $SD - $NS - $NET_STRING - -onboot 1 - -cores $CORE_COUNT - -memory $RAM_SIZE - -unprivileged $CT_TYPE - $PW -" - export TEMPLATE_STORAGE="${var_template_storage:-}" - export CONTAINER_STORAGE="${var_container_storage:-}" - create_lxc_container || exit $? - - LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" - - # ============================================================================ - # GPU/USB PASSTHROUGH CONFIGURATION - # ============================================================================ - - # List of applications that benefit from GPU acceleration - GPU_APPS=( - "immich" "channels" "emby" "ersatztv" "frigate" - "jellyfin" "plex" "scrypted" "tdarr" "unmanic" - "ollama" "fileflows" "open-webui" "tunarr" "debian" - "handbrake" "sunshine" "moonlight" "kodi" "stremio" - "viseron" - ) - - # Check if app needs GPU - is_gpu_app() { - local app="${1,,}" - for gpu_app in "${GPU_APPS[@]}"; do - [[ "$app" == "${gpu_app,,}" ]] && return 0 - done - return 1 - } - - # Detect all available GPU devices - detect_gpu_devices() { - INTEL_DEVICES=() - AMD_DEVICES=() - NVIDIA_DEVICES=() - - # Store PCI info to avoid multiple calls - local pci_vga_info=$(lspci -nn 2>/dev/null | grep -E "VGA|Display|3D") - - # Check for Intel GPU - look for Intel vendor ID [8086] - if echo "$pci_vga_info" | grep -q "\[8086:"; then - msg_info "Detected Intel GPU" - if [[ -d /dev/dri ]]; then - for d in /dev/dri/renderD* /dev/dri/card*; do - [[ -e "$d" ]] && INTEL_DEVICES+=("$d") - done - fi - fi - - # Check for AMD GPU - look for AMD vendor IDs [1002] (AMD/ATI) or [1022] (AMD) - if echo "$pci_vga_info" | grep -qE "\[1002:|\[1022:"; then - msg_info "Detected AMD GPU" - if [[ -d /dev/dri ]]; then - # Only add if not already claimed by Intel - if [[ ${#INTEL_DEVICES[@]} -eq 0 ]]; then - for d in /dev/dri/renderD* /dev/dri/card*; do - [[ -e "$d" ]] && AMD_DEVICES+=("$d") - done - fi - fi - fi - - # Check for NVIDIA GPU - look for NVIDIA vendor ID [10de] - if echo "$pci_vga_info" | grep -q "\[10de:"; then - msg_info "Detected NVIDIA GPU" - if ! check_nvidia_host_setup; then - msg_error "NVIDIA host setup incomplete. Skipping GPU passthrough." - msg_info "Fix NVIDIA drivers on host, then recreate container or passthrough manually." - return 0 - fi - - for d in /dev/nvidia* /dev/nvidiactl /dev/nvidia-modeset; do - [[ -e "$d" ]] && NVIDIA_DEVICES+=("$d") - done - - if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then - msg_warn "NVIDIA GPU detected but no /dev/nvidia* devices found" - msg_warn "Please install NVIDIA drivers on host: apt install nvidia-driver" - else - if [[ "$CT_TYPE" == "0" ]]; then - cat <>"$LXC_CONFIG" - # NVIDIA GPU Passthrough (privileged) - lxc.cgroup2.devices.allow: c 195:* rwm - lxc.cgroup2.devices.allow: c 243:* rwm - lxc.mount.entry: /dev/nvidia0 dev/nvidia0 none bind,optional,create=file - lxc.mount.entry: /dev/nvidiactl dev/nvidiactl none bind,optional,create=file - lxc.mount.entry: /dev/nvidia-uvm dev/nvidia-uvm none bind,optional,create=file - lxc.mount.entry: /dev/nvidia-uvm-tools dev/nvidia-uvm-tools none bind,optional,create=file -EOF - - if [[ -e /dev/dri/renderD128 ]]; then - echo "lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file" >>"$LXC_CONFIG" - fi - - export GPU_TYPE="NVIDIA" - export NVIDIA_DRIVER_VERSION=$(nvidia-smi --query-gpu=driver_version --format=csv,noheader 2>/dev/null | head -n1) - msg_ok "NVIDIA GPU passthrough configured (driver: ${NVIDIA_DRIVER_VERSION})" - else - msg_warn "NVIDIA passthrough only supported for privileged containers" - return 0 - fi - fi - fi - - # Debug output - msg_debug "Intel devices: ${INTEL_DEVICES[*]}" - msg_debug "AMD devices: ${AMD_DEVICES[*]}" - msg_debug "NVIDIA devices: ${NVIDIA_DEVICES[*]}" - } - - - # Configure USB passthrough for privileged containers - configure_usb_passthrough() { - if [[ "$CT_TYPE" != "0" ]]; then - return 0 - fi - - msg_info "Configuring automatic USB passthrough (privileged container)" - cat <>"$LXC_CONFIG" -# Automatic USB passthrough (privileged container) -lxc.cgroup2.devices.allow: a -lxc.cap.drop: -lxc.cgroup2.devices.allow: c 188:* rwm -lxc.cgroup2.devices.allow: c 189:* rwm -lxc.mount.entry: /dev/serial/by-id dev/serial/by-id none bind,optional,create=dir -lxc.mount.entry: /dev/ttyUSB0 dev/ttyUSB0 none bind,optional,create=file -lxc.mount.entry: /dev/ttyUSB1 dev/ttyUSB1 none bind,optional,create=file -lxc.mount.entry: /dev/ttyACM0 dev/ttyACM0 none bind,optional,create=file -lxc.mount.entry: /dev/ttyACM1 dev/ttyACM1 none bind,optional,create=file -EOF - msg_ok "USB passthrough configured" - } - - # Configure GPU passthrough -configure_gpu_passthrough() { - # Skip if not a GPU app and not privileged - if [[ "$CT_TYPE" != "0" ]] && ! is_gpu_app "$APP"; then - return 0 - fi - - detect_gpu_devices - - # Count available GPU types - local gpu_count=0 - local available_gpus=() - - if [[ ${#INTEL_DEVICES[@]} -gt 0 ]]; then - available_gpus+=("INTEL") - gpu_count=$((gpu_count + 1)) - fi - - if [[ ${#AMD_DEVICES[@]} -gt 0 ]]; then - available_gpus+=("AMD") - gpu_count=$((gpu_count + 1)) - fi - - if [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then - available_gpus+=("NVIDIA") - gpu_count=$((gpu_count + 1)) - fi - - if [[ $gpu_count -eq 0 ]]; then - msg_info "No GPU devices found for passthrough" - return 0 - fi - - local selected_gpu="" - - if [[ $gpu_count -eq 1 ]]; then - # Automatic selection for single GPU - selected_gpu="${available_gpus[0]}" - msg_info "Automatically configuring ${selected_gpu} GPU passthrough" - else - # Multiple GPUs - ask user - echo -e "\n${INFO} Multiple GPU types detected:" - for gpu in "${available_gpus[@]}"; do - echo " - $gpu" - done - read -rp "Which GPU type to passthrough? (${available_gpus[*]}): " selected_gpu - selected_gpu="${selected_gpu^^}" - - # Validate selection - local valid=0 - for gpu in "${available_gpus[@]}"; do - [[ "$selected_gpu" == "$gpu" ]] && valid=1 - done - - if [[ $valid -eq 0 ]]; then - msg_warn "Invalid selection. Skipping GPU passthrough." - return 0 - fi - fi - - # Apply passthrough configuration based on selection - local dev_idx=0 - - case "$selected_gpu" in - INTEL|AMD) - local devices=() - [[ "$selected_gpu" == "INTEL" ]] && devices=("${INTEL_DEVICES[@]}") - [[ "$selected_gpu" == "AMD" ]] && devices=("${AMD_DEVICES[@]}") - - # For Proxmox WebUI visibility, add as dev0, dev1 etc. - for dev in "${devices[@]}"; do - if [[ "$CT_TYPE" == "0" ]]; then - # Privileged container - use dev entries for WebUI visibility - # Use initial GID 104 (render) for renderD*, 44 (video) for card* - if [[ "$dev" =~ renderD ]]; then - echo "dev${dev_idx}: $dev,gid=104" >>"$LXC_CONFIG" - else - echo "dev${dev_idx}: $dev,gid=44" >>"$LXC_CONFIG" - fi - dev_idx=$((dev_idx + 1)) - - # Also add cgroup allows for privileged containers - local major minor - major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") - minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") - - if [[ "$major" != "0" && "$minor" != "0" ]]; then - echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" - fi - else - # Unprivileged container - if [[ "$dev" =~ renderD ]]; then - echo "dev${dev_idx}: $dev,uid=0,gid=104" >>"$LXC_CONFIG" - else - echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG" - fi - dev_idx=$((dev_idx + 1)) - fi - done - - export GPU_TYPE="$selected_gpu" - msg_ok "${selected_gpu} GPU passthrough configured (${dev_idx} devices)" - ;; - - NVIDIA) - if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then - msg_error "NVIDIA drivers not installed on host. Please install: apt install nvidia-driver" - return 1 - fi - - for dev in "${NVIDIA_DEVICES[@]}"; do - # NVIDIA devices typically need different handling - echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG" - dev_idx=$((dev_idx + 1)) - - if [[ "$CT_TYPE" == "0" ]]; then - local major minor - major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") - minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") - - if [[ "$major" != "0" && "$minor" != "0" ]]; then - echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" - fi - fi - done - - export GPU_TYPE="NVIDIA" - msg_ok "NVIDIA GPU passthrough configured (${dev_idx} devices)" - ;; - esac -} - - # Additional device passthrough - configure_additional_devices() { - # TUN device passthrough - if [ "$ENABLE_TUN" == "yes" ]; then - cat <>"$LXC_CONFIG" -lxc.cgroup2.devices.allow: c 10:200 rwm -lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file -EOF - fi - - # Coral TPU passthrough - if [[ -e /dev/apex_0 ]]; then - msg_info "Detected Coral TPU - configuring passthrough" - echo "lxc.mount.entry: /dev/apex_0 dev/apex_0 none bind,optional,create=file" >>"$LXC_CONFIG" - fi - } - - # Execute pre-start configurations - configure_usb_passthrough - configure_gpu_passthrough - configure_additional_devices - - # ============================================================================ - # START CONTAINER AND INSTALL USERLAND - # ============================================================================ - - msg_info "Starting LXC Container" - pct start "$CTID" - - # Wait for container to be running - for i in {1..10}; do - if pct status "$CTID" | grep -q "status: running"; then - msg_ok "Started LXC Container" - break - fi - sleep 1 - if [ "$i" -eq 10 ]; then - msg_error "LXC Container did not reach running state" - exit 1 - fi - done - - # Wait for network (skip for Alpine initially) - if [ "$var_os" != "alpine" ]; then - msg_info "Waiting for network in LXC container" - - # Wait for IP - for i in {1..20}; do - ip_in_lxc=$(pct exec "$CTID" -- ip -4 addr show dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) - [ -n "$ip_in_lxc" ] && break - sleep 1 - done - - if [ -z "$ip_in_lxc" ]; then - msg_error "No IP assigned to CT $CTID after 20s" - exit 1 - fi - - # Try to reach gateway - gw_ok=0 - for i in {1..10}; do - if pct exec "$CTID" -- ping -c1 -W1 "${GATEWAY:-8.8.8.8}" >/dev/null 2>&1; then - gw_ok=1 - break - fi - sleep 1 - done - - if [ "$gw_ok" -eq 1 ]; then - msg_ok "Network in LXC is reachable (IP $ip_in_lxc)" - else - msg_warn "Network reachable but gateway check failed" - fi - fi - # Function to get correct GID inside container - get_container_gid() { - local group="$1" - local gid=$(pct exec "$CTID" -- getent group "$group" 2>/dev/null | cut -d: -f3) - echo "${gid:-44}" # Default to 44 if not found - } - - fix_gpu_gids - -# Configure GPU passthrough -configure_gpu_passthrough() { - # Skip if not a GPU app and not privileged - if [[ "$CT_TYPE" != "0" ]] && ! is_gpu_app "$APP"; then - return 0 - fi - - detect_gpu_devices - - # Count available GPU types - local gpu_count=0 - local available_gpus=() - - if [[ ${#INTEL_DEVICES[@]} -gt 0 ]]; then - available_gpus+=("INTEL") - gpu_count=$((gpu_count + 1)) - fi - - if [[ ${#AMD_DEVICES[@]} -gt 0 ]]; then - available_gpus+=("AMD") - gpu_count=$((gpu_count + 1)) - fi - - if [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then - available_gpus+=("NVIDIA") - gpu_count=$((gpu_count + 1)) - fi - - if [[ $gpu_count -eq 0 ]]; then - msg_info "No GPU devices found for passthrough" - return 0 - fi - - local selected_gpu="" - - if [[ $gpu_count -eq 1 ]]; then - # Automatic selection for single GPU - selected_gpu="${available_gpus[0]}" - msg_info "Automatically configuring ${selected_gpu} GPU passthrough" - else - # Multiple GPUs - ask user - echo -e "\n${INFO} Multiple GPU types detected:" - for gpu in "${available_gpus[@]}"; do - echo " - $gpu" - done - read -rp "Which GPU type to passthrough? (${available_gpus[*]}): " selected_gpu - selected_gpu="${selected_gpu^^}" - - # Validate selection - local valid=0 - for gpu in "${available_gpus[@]}"; do - [[ "$selected_gpu" == "$gpu" ]] && valid=1 - done - - if [[ $valid -eq 0 ]]; then - msg_warn "Invalid selection. Skipping GPU passthrough." - return 0 - fi - fi - - # Apply passthrough configuration based on selection - local dev_idx=0 - - case "$selected_gpu" in - INTEL|AMD) - local devices=() - [[ "$selected_gpu" == "INTEL" ]] && devices=("${INTEL_DEVICES[@]}") - [[ "$selected_gpu" == "AMD" ]] && devices=("${AMD_DEVICES[@]}") - - # For Proxmox WebUI visibility, add as dev0, dev1 etc. - for dev in "${devices[@]}"; do - if [[ "$CT_TYPE" == "0" ]]; then - # Privileged container - use dev entries for WebUI visibility - # Use initial GID 104 (render) for renderD*, 44 (video) for card* - if [[ "$dev" =~ renderD ]]; then - echo "dev${dev_idx}: $dev,gid=104" >>"$LXC_CONFIG" - else - echo "dev${dev_idx}: $dev,gid=44" >>"$LXC_CONFIG" - fi - dev_idx=$((dev_idx + 1)) - - # Also add cgroup allows for privileged containers - local major minor - major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") - minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") - - if [[ "$major" != "0" && "$minor" != "0" ]]; then - echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" - fi - else - # Unprivileged container - if [[ "$dev" =~ renderD ]]; then - echo "dev${dev_idx}: $dev,uid=0,gid=104" >>"$LXC_CONFIG" - else - echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG" - fi - dev_idx=$((dev_idx + 1)) - fi - done - - export GPU_TYPE="$selected_gpu" - msg_ok "${selected_gpu} GPU passthrough configured (${dev_idx} devices)" - ;; - - NVIDIA) - if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then - msg_error "NVIDIA drivers not installed on host. Please install: apt install nvidia-driver" - return 1 - fi - - for dev in "${NVIDIA_DEVICES[@]}"; do - # NVIDIA devices typically need different handling - echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG" - dev_idx=$((dev_idx + 1)) - - if [[ "$CT_TYPE" == "0" ]]; then - local major minor - major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") - minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") - - if [[ "$major" != "0" && "$minor" != "0" ]]; then - echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" - fi - fi - done - - export GPU_TYPE="NVIDIA" - msg_ok "NVIDIA GPU passthrough configured (${dev_idx} devices)" - ;; - esac -} - - # Continue with standard container setup - msg_info "Customizing LXC Container" - - # # Install GPU userland if configured - # if [[ "${ENABLE_VAAPI:-0}" == "1" ]]; then - # install_gpu_userland "VAAPI" - # fi - - # if [[ "${ENABLE_NVIDIA:-0}" == "1" ]]; then - # install_gpu_userland "NVIDIA" - # fi - - # Continue with standard container setup - if [ "$var_os" == "alpine" ]; then - sleep 3 - pct exec "$CTID" -- /bin/sh -c 'cat </etc/apk/repositories -http://dl-cdn.alpinelinux.org/alpine/latest-stable/main -http://dl-cdn.alpinelinux.org/alpine/latest-stable/community -EOF' - pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses jq >/dev/null" - else - sleep 3 - pct exec "$CTID" -- bash -c "sed -i '/$LANG/ s/^# //' /etc/locale.gen" - pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \ - echo LANG=\$locale_line >/etc/default/locale && \ - locale-gen >/dev/null && \ - export LANG=\$locale_line" - - if [[ -z "${tz:-}" ]]; then - tz=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "Etc/UTC") - fi - - if pct exec "$CTID" -- test -e "/usr/share/zoneinfo/$tz"; then - pct exec "$CTID" -- bash -c "tz='$tz'; echo \"\$tz\" >/etc/timezone && ln -sf \"/usr/share/zoneinfo/\$tz\" /etc/localtime" - else - msg_warn "Skipping timezone setup – zone '$tz' not found in container" - fi - - pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y sudo curl mc gnupg2 jq >/dev/null" || { - msg_error "apt-get base packages installation failed" - exit 1 - } - fi - - msg_ok "Customized LXC Container" - - # Verify GPU access if enabled - if [[ "${ENABLE_VAAPI:-0}" == "1" ]] && [ "$var_os" != "alpine" ]; then - pct exec "$CTID" -- bash -c "vainfo >/dev/null 2>&1" && - msg_ok "VAAPI verified working" || - msg_warn "VAAPI verification failed - may need additional configuration" - fi - - if [[ "${ENABLE_NVIDIA:-0}" == "1" ]] && [ "$var_os" != "alpine" ]; then - pct exec "$CTID" -- bash -c "nvidia-smi >/dev/null 2>&1" && - msg_ok "NVIDIA verified working" || - msg_warn "NVIDIA verification failed - may need additional configuration" - fi - - # Install SSH keys - install_ssh_keys_into_ct - - # Run application installer - if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/prop4n/ProxmoxVED/refs/heads/add-script-sonarqube/install/${var_install}.sh)"; then - exit $? - fi -} - -destroy_lxc() { - if [[ -z "$CT_ID" ]]; then - msg_error "No CT_ID found. Nothing to remove." - return 1 - fi - - # Abbruch bei Ctrl-C / Ctrl-D / ESC - trap 'echo; msg_error "Aborted by user (SIGINT/SIGQUIT)"; return 130' INT QUIT - - local prompt - if ! read -rp "Remove this Container? " prompt; then - # read gibt != 0 zurück bei Ctrl-D/ESC - msg_error "Aborted input (Ctrl-D/ESC)" - return 130 - fi - - case "${prompt,,}" in - y | yes) - if pct stop "$CT_ID" &>/dev/null && pct destroy "$CT_ID" &>/dev/null; then - msg_ok "Removed Container $CT_ID" - else - msg_error "Failed to remove Container $CT_ID" - return 1 - fi - ;; - "" | n | no) - msg_info "Container was not removed." - ;; - *) - msg_warn "Invalid response. Container was not removed." - ;; - esac -} - -# ------------------------------------------------------------------------------ -# Storage discovery / selection helpers -# ------------------------------------------------------------------------------ -# ===== Storage discovery / selection helpers (ported from create_lxc.sh) ===== -resolve_storage_preselect() { - local class="$1" preselect="$2" required_content="" - case "$class" in - template) required_content="vztmpl" ;; - container) required_content="rootdir" ;; - *) return 1 ;; - esac - [[ -z "$preselect" ]] && return 1 - if ! pvesm status -content "$required_content" | awk 'NR>1{print $1}' | grep -qx -- "$preselect"; then - msg_warn "Preselected storage '${preselect}' does not support content '${required_content}' (or not found)" - return 1 - fi - - local line total used free - line="$(pvesm status | awk -v s="$preselect" 'NR>1 && $1==s {print $0}')" - if [[ -z "$line" ]]; then - STORAGE_INFO="n/a" - else - total="$(awk '{print $4}' <<<"$line")" - used="$(awk '{print $5}' <<<"$line")" - free="$(awk '{print $6}' <<<"$line")" - local total_h used_h free_h - if command -v numfmt >/dev/null 2>&1; then - total_h="$(numfmt --to=iec --suffix=B --format %.1f "$total" 2>/dev/null || echo "$total")" - used_h="$(numfmt --to=iec --suffix=B --format %.1f "$used" 2>/dev/null || echo "$used")" - free_h="$(numfmt --to=iec --suffix=B --format %.1f "$free" 2>/dev/null || echo "$free")" - STORAGE_INFO="Free: ${free_h} Used: ${used_h}" - else - STORAGE_INFO="Free: ${free} Used: ${used}" - fi - fi - STORAGE_RESULT="$preselect" - return 0 -} - -fix_gpu_gids() { - if [[ -z "${GPU_TYPE:-}" ]]; then - return 0 - fi - - msg_info "Detecting and setting correct GPU group IDs" - - # Ermittle die tatsächlichen GIDs aus dem Container - local video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") - local render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") - - # Fallbacks wenn Gruppen nicht existieren - if [[ -z "$video_gid" ]]; then - # Versuche die video Gruppe zu erstellen - pct exec "$CTID" -- sh -c "groupadd -r video 2>/dev/null || true" - video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") - [[ -z "$video_gid" ]] && video_gid="44" # Ultimate fallback - fi - - if [[ -z "$render_gid" ]]; then - # Versuche die render Gruppe zu erstellen - pct exec "$CTID" -- sh -c "groupadd -r render 2>/dev/null || true" - render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") - [[ -z "$render_gid" ]] && render_gid="104" # Ultimate fallback - fi - - msg_info "Container GIDs detected - video:${video_gid}, render:${render_gid}" - - # Prüfe ob die GIDs von den Defaults abweichen - local need_update=0 - if [[ "$video_gid" != "44" ]] || [[ "$render_gid" != "104" ]]; then - need_update=1 - fi - - if [[ $need_update -eq 1 ]]; then - msg_info "Updating device GIDs in container config" - - # Stoppe Container für Config-Update - pct stop "$CTID" >/dev/null 2>&1 - - # Update die dev Einträge mit korrekten GIDs - # Backup der Config - cp "$LXC_CONFIG" "${LXC_CONFIG}.bak" - - # Parse und update jeden dev Eintrag - while IFS= read -r line; do - if [[ "$line" =~ ^dev[0-9]+: ]]; then - # Extract device path - local device_path=$(echo "$line" | sed -E 's/^dev[0-9]+: ([^,]+).*/\1/') - local dev_num=$(echo "$line" | sed -E 's/^(dev[0-9]+):.*/\1/') - - if [[ "$device_path" =~ renderD ]]; then - # RenderD device - use render GID - echo "${dev_num}: ${device_path},gid=${render_gid}" - elif [[ "$device_path" =~ card ]]; then - # Card device - use video GID - echo "${dev_num}: ${device_path},gid=${video_gid}" - else - # Keep original line - echo "$line" - fi - else - # Keep non-dev lines - echo "$line" - fi - done < "$LXC_CONFIG" > "${LXC_CONFIG}.new" - - mv "${LXC_CONFIG}.new" "$LXC_CONFIG" - - # Starte Container wieder - pct start "$CTID" >/dev/null 2>&1 - sleep 3 - - msg_ok "Device GIDs updated successfully" - else - msg_ok "Device GIDs are already correct" - fi - if [[ "$CT_TYPE" == "0" ]]; then - pct exec "$CTID" -- bash -c " - if [ -d /dev/dri ]; then - for dev in /dev/dri/*; do - if [ -e \"\$dev\" ]; then - if [[ \"\$dev\" =~ renderD ]]; then - chgrp ${render_gid} \"\$dev\" 2>/dev/null || true - else - chgrp ${video_gid} \"\$dev\" 2>/dev/null || true - fi - chmod 660 \"\$dev\" 2>/dev/null || true - fi - done - fi - " >/dev/null 2>&1 - fi -} - -# NVIDIA-spezific check on host -check_nvidia_host_setup() { - if ! command -v nvidia-smi >/dev/null 2>&1; then - msg_warn "NVIDIA GPU detected but nvidia-smi not found on host" - msg_warn "Please install NVIDIA drivers on host first." - #echo " 1. Download driver: wget https://us.download.nvidia.com/XFree86/Linux-x86_64/550.127.05/NVIDIA-Linux-x86_64-550.127.05.run" - #echo " 2. Install: ./NVIDIA-Linux-x86_64-550.127.05.run --dkms" - #echo " 3. Verify: nvidia-smi" - return 1 - fi - - # check if nvidia-smi works - if ! nvidia-smi >/dev/null 2>&1; then - msg_warn "nvidia-smi installed but not working. Driver issue?" - return 1 - fi - - return 0 -} - -check_storage_support() { - local CONTENT="$1" VALID=0 - while IFS= read -r line; do - local STORAGE_NAME - STORAGE_NAME=$(awk '{print $1}' <<<"$line") - [[ -n "$STORAGE_NAME" ]] && VALID=1 - done < <(pvesm status -content "$CONTENT" 2>/dev/null | awk 'NR>1') - [[ $VALID -eq 1 ]] -} - -select_storage() { - local CLASS=$1 CONTENT CONTENT_LABEL - case $CLASS in - container) - CONTENT='rootdir' - CONTENT_LABEL='Container' - ;; - template) - CONTENT='vztmpl' - CONTENT_LABEL='Container template' - ;; - iso) - CONTENT='iso' - CONTENT_LABEL='ISO image' - ;; - images) - CONTENT='images' - CONTENT_LABEL='VM Disk image' - ;; - backup) - CONTENT='backup' - CONTENT_LABEL='Backup' - ;; - snippets) - CONTENT='snippets' - CONTENT_LABEL='Snippets' - ;; - *) - msg_error "Invalid storage class '$CLASS'" - return 1 - ;; - esac - - declare -A STORAGE_MAP - local -a MENU=() - local COL_WIDTH=0 - - while read -r TAG TYPE _ TOTAL USED FREE _; do - [[ -n "$TAG" && -n "$TYPE" ]] || continue - local DISPLAY="${TAG} (${TYPE})" - local USED_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$USED") - local FREE_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$FREE") - local INFO="Free: ${FREE_FMT}B Used: ${USED_FMT}B" - STORAGE_MAP["$DISPLAY"]="$TAG" - MENU+=("$DISPLAY" "$INFO" "OFF") - ((${#DISPLAY} > COL_WIDTH)) && COL_WIDTH=${#DISPLAY} - done < <(pvesm status -content "$CONTENT" | awk 'NR>1') - - if [[ ${#MENU[@]} -eq 0 ]]; then - msg_error "No storage found for content type '$CONTENT'." - return 2 - fi - - if [[ $((${#MENU[@]} / 3)) -eq 1 ]]; then - STORAGE_RESULT="${STORAGE_MAP[${MENU[0]}]}" - STORAGE_INFO="${MENU[1]}" - return 0 - fi - - local WIDTH=$((COL_WIDTH + 42)) - while true; do - local DISPLAY_SELECTED - DISPLAY_SELECTED=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "Storage Pools" \ - --radiolist "Which storage pool for ${CONTENT_LABEL,,}?\n(Spacebar to select)" \ - 16 "$WIDTH" 6 "${MENU[@]}" 3>&1 1>&2 2>&3) || { exit_script; } - - DISPLAY_SELECTED=$(sed 's/[[:space:]]*$//' <<<"$DISPLAY_SELECTED") - if [[ -z "$DISPLAY_SELECTED" || -z "${STORAGE_MAP[$DISPLAY_SELECTED]+_}" ]]; then - whiptail --msgbox "No valid storage selected. Please try again." 8 58 - continue - fi - STORAGE_RESULT="${STORAGE_MAP[$DISPLAY_SELECTED]}" - for ((i = 0; i < ${#MENU[@]}; i += 3)); do - if [[ "${MENU[$i]}" == "$DISPLAY_SELECTED" ]]; then - STORAGE_INFO="${MENU[$i + 1]}" - break - fi - done - return 0 - done -} - -create_lxc_container() { - # ------------------------------------------------------------------------------ - # Optional verbose mode (debug tracing) - # ------------------------------------------------------------------------------ - if [[ "${CREATE_LXC_VERBOSE:-no}" == "yes" ]]; then set -x; fi - - # ------------------------------------------------------------------------------ - # Helpers (dynamic versioning / template parsing) - # ------------------------------------------------------------------------------ - pkg_ver() { dpkg-query -W -f='${Version}\n' "$1" 2>/dev/null || echo ""; } - pkg_cand() { apt-cache policy "$1" 2>/dev/null | awk '/Candidate:/ {print $2}'; } - - ver_ge() { dpkg --compare-versions "$1" ge "$2"; } - ver_gt() { dpkg --compare-versions "$1" gt "$2"; } - ver_lt() { dpkg --compare-versions "$1" lt "$2"; } - - # Extract Debian OS minor from template name: debian-13-standard_13.1-1_amd64.tar.zst => "13.1" - parse_template_osver() { sed -n 's/.*_\([0-9][0-9]*\(\.[0-9]\+\)\?\)-.*/\1/p' <<<"$1"; } - - # Offer upgrade for pve-container/lxc-pve if candidate > installed; optional auto-retry pct create - # Returns: - # 0 = no upgrade needed - # 1 = upgraded (and if do_retry=yes and retry succeeded, creation done) - # 2 = user declined - # 3 = upgrade attempted but failed OR retry failed - offer_lxc_stack_upgrade_and_maybe_retry() { - local do_retry="${1:-no}" # yes|no - local _pvec_i _pvec_c _lxcp_i _lxcp_c need=0 - - _pvec_i="$(pkg_ver pve-container)" - _lxcp_i="$(pkg_ver lxc-pve)" - _pvec_c="$(pkg_cand pve-container)" - _lxcp_c="$(pkg_cand lxc-pve)" - - if [[ -n "$_pvec_c" && "$_pvec_c" != "none" ]]; then - ver_gt "$_pvec_c" "${_pvec_i:-0}" && need=1 - fi - if [[ -n "$_lxcp_c" && "$_lxcp_c" != "none" ]]; then - ver_gt "$_lxcp_c" "${_lxcp_i:-0}" && need=1 - fi - if [[ $need -eq 0 ]]; then - msg_debug "No newer candidate for pve-container/lxc-pve (installed=$_pvec_i/$_lxcp_i, cand=$_pvec_c/$_lxcp_c)" - return 0 - fi - - echo - echo "An update for the Proxmox LXC stack is available:" - echo " pve-container: installed=${_pvec_i:-n/a} candidate=${_pvec_c:-n/a}" - echo " lxc-pve : installed=${_lxcp_i:-n/a} candidate=${_lxcp_c:-n/a}" - echo - read -rp "Do you want to upgrade now? [y/N] " _ans - case "${_ans,,}" in - y | yes) - msg_info "Upgrading Proxmox LXC stack (pve-container, lxc-pve)" - if apt-get update -qq >/dev/null && apt-get install -y --only-upgrade pve-container lxc-pve >/dev/null; then - msg_ok "LXC stack upgraded." - if [[ "$do_retry" == "yes" ]]; then - msg_info "Retrying container creation after upgrade" - if pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then - msg_ok "Container created successfully after upgrade." - return 1 - else - msg_error "pct create still failed after upgrade. See $LOGFILE" - return 3 - fi - fi - return 1 - else - msg_error "Upgrade failed. Please check APT output." - return 3 - fi - ;; - *) return 2 ;; - esac - } - - # ------------------------------------------------------------------------------ - # Required input variables - # ------------------------------------------------------------------------------ - [[ "${CTID:-}" ]] || { - msg_error "You need to set 'CTID' variable." - exit 203 - } - [[ "${PCT_OSTYPE:-}" ]] || { - msg_error "You need to set 'PCT_OSTYPE' variable." - exit 204 - } - - msg_debug "CTID=$CTID" - msg_debug "PCT_OSTYPE=$PCT_OSTYPE" - msg_debug "PCT_OSVERSION=${PCT_OSVERSION:-default}" - - # ID checks - [[ "$CTID" -ge 100 ]] || { - msg_error "ID cannot be less than 100." - exit 205 - } - if qm status "$CTID" &>/dev/null || pct status "$CTID" &>/dev/null; then - echo -e "ID '$CTID' is already in use." - unset CTID - msg_error "Cannot use ID that is already in use." - exit 206 - fi - - # Storage capability check - check_storage_support "rootdir" || { - msg_error "No valid storage found for 'rootdir' [Container]" - exit 1 - } - check_storage_support "vztmpl" || { - msg_error "No valid storage found for 'vztmpl' [Template]" - exit 1 - } - - # Template storage selection - if resolve_storage_preselect template "${TEMPLATE_STORAGE:-}"; then - TEMPLATE_STORAGE="$STORAGE_RESULT" - TEMPLATE_STORAGE_INFO="$STORAGE_INFO" - msg_ok "Storage ${BL}${TEMPLATE_STORAGE}${CL} (${TEMPLATE_STORAGE_INFO}) [Template]" - else - while true; do - if [[ -z "${var_template_storage:-}" ]]; then - if select_storage template; then - TEMPLATE_STORAGE="$STORAGE_RESULT" - TEMPLATE_STORAGE_INFO="$STORAGE_INFO" - msg_ok "Storage ${BL}${TEMPLATE_STORAGE}${CL} (${TEMPLATE_STORAGE_INFO}) [Template]" - break - fi - fi - done - fi - - # Container storage selection - if resolve_storage_preselect container "${CONTAINER_STORAGE:-}"; then - CONTAINER_STORAGE="$STORAGE_RESULT" - CONTAINER_STORAGE_INFO="$STORAGE_INFO" - msg_ok "Storage ${BL}${CONTAINER_STORAGE}${CL} (${CONTAINER_STORAGE_INFO}) [Container]" - else - if [[ -z "${var_container_storage:-}" ]]; then - if select_storage container; then - CONTAINER_STORAGE="$STORAGE_RESULT" - CONTAINER_STORAGE_INFO="$STORAGE_INFO" - msg_ok "Storage ${BL}${CONTAINER_STORAGE}${CL} (${CONTAINER_STORAGE_INFO}) [Container]" - fi - fi - fi - - # Validate content types - msg_info "Validating content types of storage '$CONTAINER_STORAGE'" - STORAGE_CONTENT=$(grep -A4 -E "^(zfspool|dir|lvmthin|lvm): $CONTAINER_STORAGE" /etc/pve/storage.cfg | grep content | awk '{$1=""; print $0}' | xargs) - msg_debug "Storage '$CONTAINER_STORAGE' has content types: $STORAGE_CONTENT" - grep -qw "rootdir" <<<"$STORAGE_CONTENT" || { - msg_error "Storage '$CONTAINER_STORAGE' does not support 'rootdir'. Cannot create LXC." - exit 217 - } - $STD msg_ok "Storage '$CONTAINER_STORAGE' supports 'rootdir'" - - msg_info "Validating content types of template storage '$TEMPLATE_STORAGE'" - TEMPLATE_CONTENT=$(grep -A4 -E "^[^:]+: $TEMPLATE_STORAGE" /etc/pve/storage.cfg | grep content | awk '{$1=""; print $0}' | xargs) - msg_debug "Template storage '$TEMPLATE_STORAGE' has content types: $TEMPLATE_CONTENT" - if ! grep -qw "vztmpl" <<<"$TEMPLATE_CONTENT"; then - msg_warn "Template storage '$TEMPLATE_STORAGE' does not declare 'vztmpl'. This may cause pct create to fail." - else - $STD msg_ok "Template storage '$TEMPLATE_STORAGE' supports 'vztmpl'" - fi - - # Free space check - STORAGE_FREE=$(pvesm status | awk -v s="$CONTAINER_STORAGE" '$1 == s { print $6 }') - REQUIRED_KB=$((${PCT_DISK_SIZE:-8} * 1024 * 1024)) - [[ "$STORAGE_FREE" -ge "$REQUIRED_KB" ]] || { - msg_error "Not enough space on '$CONTAINER_STORAGE'. Needed: ${PCT_DISK_SIZE:-8}G." - exit 214 - } - - # Cluster quorum (if cluster) - if [[ -f /etc/pve/corosync.conf ]]; then - msg_info "Checking cluster quorum" - if ! pvecm status | awk -F':' '/^Quorate/ { exit ($2 ~ /Yes/) ? 0 : 1 }'; then - msg_error "Cluster is not quorate. Start all nodes or configure quorum device (QDevice)." - exit 210 - fi - msg_ok "Cluster is quorate" - fi - - # ------------------------------------------------------------------------------ - # Template discovery & validation - # ------------------------------------------------------------------------------ - TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" - case "$PCT_OSTYPE" in - debian | ubuntu) TEMPLATE_PATTERN="-standard_" ;; - alpine | fedora | rocky | centos) TEMPLATE_PATTERN="-default_" ;; - *) TEMPLATE_PATTERN="" ;; - esac - - msg_info "Searching for template '$TEMPLATE_SEARCH'" - - mapfile -t LOCAL_TEMPLATES < <( - pveam list "$TEMPLATE_STORAGE" 2>/dev/null | - awk -v s="$TEMPLATE_SEARCH" -v p="$TEMPLATE_PATTERN" '$1 ~ s && $1 ~ p {print $1}' | - sed 's|.*/||' | sort -t - -k 2 -V - ) - - pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)." - mapfile -t ONLINE_TEMPLATES < <( - pveam available -section system 2>/dev/null | - sed -n "s/.*\($TEMPLATE_SEARCH.*$TEMPLATE_PATTERN.*\)/\1/p" | - sort -t - -k 2 -V - ) - ONLINE_TEMPLATE="" - [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" - - if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then - TEMPLATE="${LOCAL_TEMPLATES[-1]}" - TEMPLATE_SOURCE="local" - else - TEMPLATE="$ONLINE_TEMPLATE" - TEMPLATE_SOURCE="online" - fi - - TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" - if [[ -z "$TEMPLATE_PATH" ]]; then - TEMPLATE_BASE=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) - [[ -n "$TEMPLATE_BASE" ]] && TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE" - fi - [[ -n "$TEMPLATE_PATH" ]] || { - msg_error "Unable to resolve template path for $TEMPLATE_STORAGE. Check storage type and permissions." - exit 220 - } - - msg_ok "Template ${BL}$TEMPLATE${CL} [$TEMPLATE_SOURCE]" - msg_debug "Resolved TEMPLATE_PATH=$TEMPLATE_PATH" - - NEED_DOWNLOAD=0 - if [[ ! -f "$TEMPLATE_PATH" ]]; then - msg_info "Template not present locally – will download." - NEED_DOWNLOAD=1 - elif [[ ! -r "$TEMPLATE_PATH" ]]; then - msg_error "Template file exists but is not readable – check permissions." - exit 221 - elif [[ "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then - if [[ -n "$ONLINE_TEMPLATE" ]]; then - msg_warn "Template file too small (<1MB) – re-downloading." - NEED_DOWNLOAD=1 - else - msg_warn "Template looks too small, but no online version exists. Keeping local file." - fi - elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then - if [[ -n "$ONLINE_TEMPLATE" ]]; then - msg_warn "Template appears corrupted – re-downloading." - NEED_DOWNLOAD=1 - else - msg_warn "Template appears corrupted, but no online version exists. Keeping local file." - fi - else - $STD msg_ok "Template $TEMPLATE is present and valid." - fi - - if [[ "$TEMPLATE_SOURCE" == "local" && -n "$ONLINE_TEMPLATE" && "$TEMPLATE" != "$ONLINE_TEMPLATE" ]]; then - msg_warn "Local template is outdated: $TEMPLATE (latest available: $ONLINE_TEMPLATE)" - if whiptail --yesno "A newer template is available:\n$ONLINE_TEMPLATE\n\nDo you want to download and use it instead?" 12 70; then - TEMPLATE="$ONLINE_TEMPLATE" - NEED_DOWNLOAD=1 - else - msg_info "Continuing with local template $TEMPLATE" - fi - fi - - if [[ "$NEED_DOWNLOAD" -eq 1 ]]; then - [[ -f "$TEMPLATE_PATH" ]] && rm -f "$TEMPLATE_PATH" - for attempt in {1..3}; do - msg_info "Attempt $attempt: Downloading template $TEMPLATE to $TEMPLATE_STORAGE" - if pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null 2>&1; then - msg_ok "Template download successful." - break - fi - if [[ $attempt -eq 3 ]]; then - msg_error "Failed after 3 attempts. Please check network access, permissions, or manually run:\n pveam download $TEMPLATE_STORAGE $TEMPLATE" - exit 222 - fi - sleep $((attempt * 5)) - done - fi - - if ! pveam list "$TEMPLATE_STORAGE" 2>/dev/null | grep -q "$TEMPLATE"; then - msg_error "Template $TEMPLATE not available in storage $TEMPLATE_STORAGE after download." - exit 223 - fi - - # ------------------------------------------------------------------------------ - # Dynamic preflight for Debian 13.x: offer upgrade if available (no hard mins) - # ------------------------------------------------------------------------------ - if [[ "$PCT_OSTYPE" == "debian" ]]; then - OSVER="$(parse_template_osver "$TEMPLATE")" - if [[ -n "$OSVER" ]]; then - # Proactive, aber ohne Abbruch – nur Angebot - offer_lxc_stack_upgrade_and_maybe_retry "no" || true - fi - fi - - # ------------------------------------------------------------------------------ - # Create LXC Container - # ------------------------------------------------------------------------------ - msg_info "Creating LXC container" - - # Ensure subuid/subgid entries exist - grep -q "root:100000:65536" /etc/subuid || echo "root:100000:65536" >>/etc/subuid - grep -q "root:100000:65536" /etc/subgid || echo "root:100000:65536" >>/etc/subgid - - # Assemble pct options - PCT_OPTIONS=(${PCT_OPTIONS[@]:-${DEFAULT_PCT_OPTIONS[@]}}) - [[ " ${PCT_OPTIONS[*]} " =~ " -rootfs " ]] || PCT_OPTIONS+=(-rootfs "$CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}") - - # Lock by template file (avoid concurrent downloads/creates) - lockfile="/tmp/template.${TEMPLATE}.lock" - exec 9>"$lockfile" || { - msg_error "Failed to create lock file '$lockfile'." - exit 200 - } - flock -w 60 9 || { - msg_error "Timeout while waiting for template lock." - exit 211 - } - - LOGFILE="/tmp/pct_create_${CTID}.log" - msg_debug "pct create command: pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" - msg_debug "Logfile: $LOGFILE" - - # First attempt - if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >"$LOGFILE" 2>&1; then - msg_error "Container creation failed on ${TEMPLATE_STORAGE}. Checking template..." - - # Validate template file - if [[ ! -s "$TEMPLATE_PATH" || "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then - msg_warn "Template file too small or missing – re-downloading." - rm -f "$TEMPLATE_PATH" - pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" - elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then - if [[ -n "$ONLINE_TEMPLATE" ]]; then - msg_warn "Template appears corrupted – re-downloading." - rm -f "$TEMPLATE_PATH" - pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" - else - msg_warn "Template appears corrupted, but no online version exists. Skipping re-download." - fi - fi - - # Retry after repair - if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then - # Fallback to local storage - if [[ "$TEMPLATE_STORAGE" != "local" ]]; then - msg_warn "Retrying container creation with fallback to local storage..." - LOCAL_TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" - if [[ ! -f "$LOCAL_TEMPLATE_PATH" ]]; then - msg_info "Downloading template to local..." - pveam download local "$TEMPLATE" >/dev/null 2>&1 - fi - if pct create "$CTID" "local:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then - msg_ok "Container successfully created using local fallback." - else - # --- Dynamic stack upgrade + auto-retry on the well-known error pattern --- - if grep -qiE 'unsupported .* version' "$LOGFILE"; then - echo - echo "pct reported 'unsupported ... version' – your LXC stack might be too old for this template." - echo "We can try to upgrade 'pve-container' and 'lxc-pve' now and retry automatically." - if offer_lxc_stack_upgrade_and_maybe_retry "yes"; then - : # success after retry - else - rc=$? - case $rc in - 2) echo "Upgrade was declined. Please update and re-run: - apt update && apt install --only-upgrade pve-container lxc-pve" ;; - 3) echo "Upgrade and/or retry failed. Please inspect: $LOGFILE" ;; - esac - exit 231 - fi - else - msg_error "Container creation failed even with local fallback. See $LOGFILE" - if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then - set -x - bash -x -c "pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" 2>&1 | tee -a "$LOGFILE" - set +x - fi - exit 209 - fi - fi - else - msg_error "Container creation failed on local storage. See $LOGFILE" - # --- Dynamic stack upgrade + auto-retry on the well-known error pattern --- - if grep -qiE 'unsupported .* version' "$LOGFILE"; then - echo - echo "pct reported 'unsupported ... version' – your LXC stack might be too old for this template." - echo "We can try to upgrade 'pve-container' and 'lxc-pve' now and retry automatically." - if offer_lxc_stack_upgrade_and_maybe_retry "yes"; then - : # success after retry - else - rc=$? - case $rc in - 2) echo "Upgrade was declined. Please update and re-run: - apt update && apt install --only-upgrade pve-container lxc-pve" ;; - 3) echo "Upgrade and/or retry failed. Please inspect: $LOGFILE" ;; - esac - exit 231 - fi - else - if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then - set -x - bash -x -c "pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" 2>&1 | tee -a "$LOGFILE" - set +x - fi - exit 209 - fi - fi - fi - fi - - # Verify container exists - pct list | awk '{print $1}' | grep -qx "$CTID" || { - msg_error "Container ID $CTID not listed in 'pct list'. See $LOGFILE" - exit 215 - } - - # Verify config rootfs - grep -q '^rootfs:' "/etc/pve/lxc/$CTID.conf" || { - msg_error "RootFS entry missing in container config. See $LOGFILE" - exit 216 - } - - msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created." -} - -# ------------------------------------------------------------------------------ -# description() -# -# - Sets container description with HTML content (logo, links, badges) -# - Restarts ping-instances.service if present -# - Posts status "done" to API -# ------------------------------------------------------------------------------ -description() { - IP=$(pct exec "$CTID" ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) - - # Generate LXC Description - DESCRIPTION=$( - cat < - - Logo - - -

${APP} LXC

- -

- - spend Coffee - -

- - - - GitHub - - - - Discussions - - - - Issues - - -EOF - ) - pct set "$CTID" -description "$DESCRIPTION" - - if [[ -f /etc/systemd/system/ping-instances.service ]]; then - systemctl start ping-instances.service - fi - - post_update_to_api "done" "none" -} - -# ------------------------------------------------------------------------------ -# api_exit_script() -# -# - Exit trap handler -# - Reports exit codes to API with detailed reason -# - Handles known codes (100–209) and maps them to errors -# ------------------------------------------------------------------------------ -api_exit_script() { - exit_code=$? - if [ $exit_code -ne 0 ]; then - case $exit_code in - 100) post_update_to_api "failed" "100: Unexpected error in create_lxc.sh" ;; - 101) post_update_to_api "failed" "101: No network connection detected in create_lxc.sh" ;; - 200) post_update_to_api "failed" "200: LXC creation failed in create_lxc.sh" ;; - 201) post_update_to_api "failed" "201: Invalid Storage class in create_lxc.sh" ;; - 202) post_update_to_api "failed" "202: User aborted menu in create_lxc.sh" ;; - 203) post_update_to_api "failed" "203: CTID not set in create_lxc.sh" ;; - 204) post_update_to_api "failed" "204: PCT_OSTYPE not set in create_lxc.sh" ;; - 205) post_update_to_api "failed" "205: CTID cannot be less than 100 in create_lxc.sh" ;; - 206) post_update_to_api "failed" "206: CTID already in use in create_lxc.sh" ;; - 207) post_update_to_api "failed" "207: Template not found in create_lxc.sh" ;; - 208) post_update_to_api "failed" "208: Error downloading template in create_lxc.sh" ;; - 209) post_update_to_api "failed" "209: Container creation failed, but template is intact in create_lxc.sh" ;; - *) post_update_to_api "failed" "Unknown error, exit code: $exit_code in create_lxc.sh" ;; - esac - fi -} - -if command -v pveversion >/dev/null 2>&1; then - trap 'api_exit_script' EXIT -fi -trap 'post_update_to_api "failed" "$BASH_COMMAND"' ERR -trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT -trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM diff --git a/misc/install.func b/misc/install.func deleted file mode 100644 index 3ea617ad3..000000000 --- a/misc/install.func +++ /dev/null @@ -1,206 +0,0 @@ -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) -# Co-Author: MickLesk -# Co-Author: michelroegl-brunner -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE - -if ! command -v curl >/dev/null 2>&1; then - printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 - apt-get update >/dev/null 2>&1 - apt-get install -y curl >/dev/null 2>&1 -fi -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) -load_functions -catch_errors - -# This function enables IPv6 if it's not disabled and sets verbose mode -verb_ip6() { - set_std_mode # Set STD mode based on VERBOSE - - if [ "$DISABLEIPV6" == "yes" ]; then - echo "net.ipv6.conf.all.disable_ipv6 = 1" >>/etc/sysctl.conf - $STD sysctl -p - fi -} - -# # This function sets error handling options and defines the error_handler function to handle errors -# catch_errors() { -# set -Eeuo pipefail -# trap 'error_handler $LINENO "$BASH_COMMAND"' ERR -# } - -# # This function handles errors -# error_handler() { -# source <(curl -fsSL https://raw.githubusercontent.com/prop4n/ProxmoxVED/refs/heads/add-script-sonarqube/misc/api.func) -# local exit_code="$1" -# local line_number="$2" -# local command="${3:-}" - -# if [[ "$exit_code" -eq 0 ]]; then -# return 0 -# fi - -# printf "\e[?25h" -# echo -e "\n${RD}[ERROR]${CL} in line ${RD}${line_number}${CL}: exit code ${RD}${exit_code}${CL}: while executing command ${YW}${command}${CL}\n" -# exit "$exit_code" -#} - -# This function sets up the Container OS by generating the locale, setting the timezone, and checking the network connection -setting_up_container() { - msg_info "Setting up Container OS" - for ((i = RETRY_NUM; i > 0; i--)); do - if [ "$(hostname -I)" != "" ]; then - break - fi - echo 1>&2 -en "${CROSS}${RD} No Network! " - sleep $RETRY_EVERY - done - if [ "$(hostname -I)" = "" ]; then - echo 1>&2 -e "\n${CROSS}${RD} No Network After $RETRY_NUM Tries${CL}" - echo -e "${NETWORK}Check Network Settings" - exit 1 - fi - rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED - systemctl disable -q --now systemd-networkd-wait-online.service - msg_ok "Set up Container OS" - #msg_custom "${CM}" "${GN}" "Network Connected: ${BL}$(hostname -I)" - msg_ok "Network Connected: ${BL}$(hostname -I)" -} - -# This function checks the network connection by pinging a known IP address and prompts the user to continue if the internet is not connected -network_check() { - set +e - trap - ERR - ipv4_connected=false - ipv6_connected=false - sleep 1 - - # Check IPv4 connectivity to Google, Cloudflare & Quad9 DNS servers. - if ping -c 1 -W 1 1.1.1.1 &>/dev/null || ping -c 1 -W 1 8.8.8.8 &>/dev/null || ping -c 1 -W 1 9.9.9.9 &>/dev/null; then - msg_ok "IPv4 Internet Connected" - ipv4_connected=true - else - msg_error "IPv4 Internet Not Connected" - fi - - # Check IPv6 connectivity to Google, Cloudflare & Quad9 DNS servers. - if ping6 -c 1 -W 1 2606:4700:4700::1111 &>/dev/null || ping6 -c 1 -W 1 2001:4860:4860::8888 &>/dev/null || ping6 -c 1 -W 1 2620:fe::fe &>/dev/null; then - msg_ok "IPv6 Internet Connected" - ipv6_connected=true - else - msg_error "IPv6 Internet Not Connected" - fi - - # If both IPv4 and IPv6 checks fail, prompt the user - if [[ $ipv4_connected == false && $ipv6_connected == false ]]; then - read -r -p "No Internet detected, would you like to continue anyway? " prompt - if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then - echo -e "${INFO}${RD}Expect Issues Without Internet${CL}" - else - echo -e "${NETWORK}Check Network Settings" - exit 1 - fi - fi - - # DNS resolution checks for GitHub-related domains (IPv4 and/or IPv6) - GIT_HOSTS=("github.com" "raw.githubusercontent.com" "api.github.com" "git.community-scripts.org") - GIT_STATUS="Git DNS:" - DNS_FAILED=false - - for HOST in "${GIT_HOSTS[@]}"; do - RESOLVEDIP=$(getent hosts "$HOST" | awk '{ print $1 }' | grep -E '(^([0-9]{1,3}\.){3}[0-9]{1,3}$)|(^[a-fA-F0-9:]+$)' | head -n1) - if [[ -z "$RESOLVEDIP" ]]; then - GIT_STATUS+="$HOST:($DNSFAIL)" - DNS_FAILED=true - else - GIT_STATUS+=" $HOST:($DNSOK)" - fi - done - - if [[ "$DNS_FAILED" == true ]]; then - fatal "$GIT_STATUS" - else - msg_ok "$GIT_STATUS" - fi - - set -e - trap 'error_handler $LINENO "$BASH_COMMAND"' ERR -} - -# This function updates the Container OS by running apt-get update and upgrade -update_os() { - msg_info "Updating Container OS" - if [[ "$CACHER" == "yes" ]]; then - echo "Acquire::http::Proxy-Auto-Detect \"/usr/local/bin/apt-proxy-detect.sh\";" >/etc/apt/apt.conf.d/00aptproxy - cat <<'EOF' >/usr/local/bin/apt-proxy-detect.sh -#!/bin/bash -if nc -w1 -z "${CACHER_IP}" 3142; then - echo -n "http://${CACHER_IP}:3142" -else - echo -n "DIRECT" -fi -EOF - chmod +x /usr/local/bin/apt-proxy-detect.sh - fi - $STD apt-get update - $STD apt-get -o Dpkg::Options::="--force-confold" -y dist-upgrade - rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED - msg_ok "Updated Container OS" - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) -} - -# This function modifies the message of the day (motd) and SSH settings -motd_ssh() { - grep -qxF "export TERM='xterm-256color'" /root/.bashrc || echo "export TERM='xterm-256color'" >>/root/.bashrc - - if [ -f "/etc/os-release" ]; then - OS_NAME=$(grep ^NAME /etc/os-release | cut -d= -f2 | tr -d '"') - OS_VERSION=$(grep ^VERSION_ID /etc/os-release | cut -d= -f2 | tr -d '"') - elif [ -f "/etc/debian_version" ]; then - OS_NAME="Debian" - OS_VERSION=$(cat /etc/debian_version) - fi - - PROFILE_FILE="/etc/profile.d/00_lxc-details.sh" - echo "echo -e \"\"" >"$PROFILE_FILE" - echo -e "echo -e \"${BOLD}${YW}${APPLICATION} LXC Container - DEV Repository${CL}\"" >>"$PROFILE_FILE" - echo -e "echo -e \"${RD}WARNING: This is a DEVELOPMENT version (ProxmoxVED). Do NOT use in production!${CL}\"" >>"$PROFILE_FILE" - echo -e "echo -e \"${YW} OS: ${GN}${OS_NAME} - Version: ${OS_VERSION}${CL}\"" >>"$PROFILE_FILE" - echo -e "echo -e \"${YW} Hostname: ${GN}\$(hostname)${CL}\"" >>"$PROFILE_FILE" - echo -e "echo -e \"${YW} IP Address: ${GN}\$(hostname -I | awk '{print \$1}')${CL}\"" >>"$PROFILE_FILE" - echo -e "echo -e \"${YW} Repository: ${GN}https://github.com/community-scripts/ProxmoxVED${CL}\"" >>"$PROFILE_FILE" - echo "echo \"\"" >>"$PROFILE_FILE" - - chmod -x /etc/update-motd.d/* - - if [[ "${SSH_ROOT}" == "yes" ]]; then - sed -i "s/#PermitRootLogin prohibit-password/PermitRootLogin yes/g" /etc/ssh/sshd_config - systemctl restart sshd - fi -} - -# This function customizes the container by modifying the getty service and enabling auto-login for the root user -customize() { - if [[ "$PASSWORD" == "" ]]; then - msg_info "Customizing Container" - GETTY_OVERRIDE="/etc/systemd/system/container-getty@1.service.d/override.conf" - mkdir -p $(dirname $GETTY_OVERRIDE) - cat <$GETTY_OVERRIDE - [Service] - ExecStart= - ExecStart=-/sbin/agetty --autologin root --noclear --keep-baud tty%I 115200,38400,9600 \$TERM -EOF - systemctl daemon-reload - systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//') - msg_ok "Customized Container" - fi - echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update - chmod +x /usr/bin/update - if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then - mkdir -p /root/.ssh - echo "${SSH_AUTHORIZED_KEY}" >/root/.ssh/authorized_keys - chmod 700 /root/.ssh - chmod 600 /root/.ssh/authorized_keys - fi -} From f41c353c07c2c3a4221662f2f7645bd8dbd1aaa6 Mon Sep 17 00:00:00 2001 From: Enzo Legrand Date: Tue, 30 Sep 2025 17:22:21 +0200 Subject: [PATCH 1257/1733] fix: set default build.fun and install.func --- misc/build.func | 3473 +++++++++++++++++++++++++++++++++++++++++++++ misc/install.func | 206 +++ 2 files changed, 3679 insertions(+) create mode 100644 misc/build.func create mode 100644 misc/install.func diff --git a/misc/build.func b/misc/build.func new file mode 100644 index 000000000..c53632a9e --- /dev/null +++ b/misc/build.func @@ -0,0 +1,3473 @@ +#!/usr/bin/env bash +# Copyright (c) 2021-2025 community-scripts ORG +# Author: tteck (tteckster) | MickLesk | michelroegl-brunner +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Revision: 1 + +# ------------------------------------------------------------------------------ +# variables() +# +# - Normalize application name (NSAPP = lowercase, no spaces) +# - Build installer filename (var_install) +# - Define regex for integer validation +# - Fetch hostname of Proxmox node +# - Set default values for diagnostics/method +# - Generate random UUID for tracking +# ------------------------------------------------------------------------------ +variables() { + NSAPP=$(echo "${APP,,}" | tr -d ' ') # This function sets the NSAPP variable by converting the value of the APP variable to lowercase and removing any spaces. + var_install="${NSAPP}-install" # sets the var_install variable by appending "-install" to the value of NSAPP. + INTEGER='^[0-9]+([.][0-9]+)?$' # it defines the INTEGER regular expression pattern. + PVEHOST_NAME=$(hostname) # gets the Proxmox Hostname and sets it to Uppercase + DIAGNOSTICS="yes" # sets the DIAGNOSTICS variable to "yes", used for the API call. + METHOD="default" # sets the METHOD variable to "default", used for the API call. + RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" # generates a random UUID and sets it to the RANDOM_UUID variable. + CTTYPE="${CTTYPE:-${CT_TYPE:-1}}" + #CT_TYPE=${var_unprivileged:-$CT_TYPE} +} + +# ----------------------------------------------------------------------------- +# Community-Scripts bootstrap loader +# - Always sources build.func from remote +# - Updates local core files only if build.func changed +# - Local cache: /usr/local/community-scripts/core +# ----------------------------------------------------------------------------- + +# FUNC_DIR="/usr/local/community-scripts/core" +# mkdir -p "$FUNC_DIR" + +# BUILD_URL="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func" +# BUILD_REV="$FUNC_DIR/build.rev" +# DEVMODE="${DEVMODE:-no}" + +# # --- Step 1: fetch build.func content once, compute hash --- +# build_content="$(curl -fsSL "$BUILD_URL")" || { +# echo "❌ Failed to fetch build.func" +# exit 1 +# } + +# newhash=$(printf "%s" "$build_content" | sha256sum | awk '{print $1}') +# oldhash=$(cat "$BUILD_REV" 2>/dev/null || echo "") + +# # --- Step 2: if build.func changed, offer update for core files --- +# if [ "$newhash" != "$oldhash" ]; then +# echo "⚠️ build.func changed!" + +# while true; do +# read -rp "Refresh local core files? [y/N/diff]: " ans +# case "$ans" in +# [Yy]*) +# echo "$newhash" >"$BUILD_REV" + +# update_func_file() { +# local file="$1" +# local url="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/$file" +# local local_path="$FUNC_DIR/$file" + +# echo "⬇️ Downloading $file ..." +# curl -fsSL "$url" -o "$local_path" || { +# echo "❌ Failed to fetch $file" +# exit 1 +# } +# echo "✔️ Updated $file" +# } + +# update_func_file core.func +# update_func_file error_handler.func +# update_func_file tools.func +# break +# ;; +# [Dd]*) +# for file in core.func error_handler.func tools.func; do +# local_path="$FUNC_DIR/$file" +# url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/$file" +# remote_tmp="$(mktemp)" + +# curl -fsSL "$url" -o "$remote_tmp" || continue + +# if [ -f "$local_path" ]; then +# echo "🔍 Diff for $file:" +# diff -u "$local_path" "$remote_tmp" || echo "(no differences)" +# else +# echo "📦 New file $file will be installed" +# fi + +# rm -f "$remote_tmp" +# done +# ;; +# *) +# echo "❌ Skipped updating local core files" +# break +# ;; +# esac +# done +# else +# if [ "$DEVMODE" != "yes" ]; then +# echo "✔️ build.func unchanged → using existing local core files" +# fi +# fi + +# if [ -n "${_COMMUNITY_SCRIPTS_LOADER:-}" ]; then +# return 0 2>/dev/null || exit 0 +# fi +# _COMMUNITY_SCRIPTS_LOADER=1 + +# # --- Step 3: always source local versions of the core files --- +# source "$FUNC_DIR/core.func" +# source "$FUNC_DIR/error_handler.func" +# source "$FUNC_DIR/tools.func" + +# # --- Step 4: finally, source build.func directly from memory --- +# # (no tmp file needed) +# source <(printf "%s" "$build_content") + +# ------------------------------------------------------------------------------ +# Load core + error handler functions from community-scripts repo +# +# - Prefer curl if available, fallback to wget +# - Load: core.func, error_handler.func, api.func +# - Initialize error traps after loading +# ------------------------------------------------------------------------------ + +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) + +if command -v curl >/dev/null 2>&1; then + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) + load_functions + catch_errors + #echo "(build.func) Loaded core.func via curl" +elif command -v wget >/dev/null 2>&1; then + source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) + load_functions + catch_errors + #echo "(build.func) Loaded core.func via wget" +fi + +# ------------------------------------------------------------------------------ +# maxkeys_check() +# +# - Reads kernel keyring limits (maxkeys, maxbytes) +# - Checks current usage for LXC user (UID 100000) +# - Warns if usage is close to limits and suggests sysctl tuning +# - Exits if thresholds are exceeded +# - https://cleveruptime.com/docs/files/proc-key-users | https://docs.kernel.org/security/keys/core.html +# ------------------------------------------------------------------------------ + +maxkeys_check() { + # Read kernel parameters + per_user_maxkeys=$(cat /proc/sys/kernel/keys/maxkeys 2>/dev/null || echo 0) + per_user_maxbytes=$(cat /proc/sys/kernel/keys/maxbytes 2>/dev/null || echo 0) + + # Exit if kernel parameters are unavailable + if [[ "$per_user_maxkeys" -eq 0 || "$per_user_maxbytes" -eq 0 ]]; then + echo -e "${CROSS}${RD} Error: Unable to read kernel parameters. Ensure proper permissions.${CL}" + exit 1 + fi + + # Fetch key usage for user ID 100000 (typical for containers) + used_lxc_keys=$(awk '/100000:/ {print $2}' /proc/key-users 2>/dev/null || echo 0) + used_lxc_bytes=$(awk '/100000:/ {split($5, a, "/"); print a[1]}' /proc/key-users 2>/dev/null || echo 0) + + # Calculate thresholds and suggested new limits + threshold_keys=$((per_user_maxkeys - 100)) + threshold_bytes=$((per_user_maxbytes - 1000)) + new_limit_keys=$((per_user_maxkeys * 2)) + new_limit_bytes=$((per_user_maxbytes * 2)) + + # Check if key or byte usage is near limits + failure=0 + if [[ "$used_lxc_keys" -gt "$threshold_keys" ]]; then + echo -e "${CROSS}${RD} Warning: Key usage is near the limit (${used_lxc_keys}/${per_user_maxkeys}).${CL}" + echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxkeys=${new_limit_keys}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." + failure=1 + fi + if [[ "$used_lxc_bytes" -gt "$threshold_bytes" ]]; then + echo -e "${CROSS}${RD} Warning: Key byte usage is near the limit (${used_lxc_bytes}/${per_user_maxbytes}).${CL}" + echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxbytes=${new_limit_bytes}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." + failure=1 + fi + + # Provide next steps if issues are detected + if [[ "$failure" -eq 1 ]]; then + echo -e "${INFO} To apply changes, run: ${BOLD}service procps force-reload${CL}" + exit 1 + fi + + echo -e "${CM}${GN} All kernel key limits are within safe thresholds.${CL}" +} + +# ------------------------------------------------------------------------------ +# get_current_ip() +# +# - Returns current container IP depending on OS type +# - Debian/Ubuntu: uses `hostname -I` +# - Alpine: parses eth0 via `ip -4 addr` +# ------------------------------------------------------------------------------ +get_current_ip() { + if [ -f /etc/os-release ]; then + # Check for Debian/Ubuntu (uses hostname -I) + if grep -qE 'ID=debian|ID=ubuntu' /etc/os-release; then + CURRENT_IP=$(hostname -I | awk '{print $1}') + # Check for Alpine (uses ip command) + elif grep -q 'ID=alpine' /etc/os-release; then + CURRENT_IP=$(ip -4 addr show eth0 | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) + else + CURRENT_IP="Unknown" + fi + fi + echo "$CURRENT_IP" +} + + +# ------------------------------------------------------------------------------ +# update_motd_ip() +# +# - Updates /etc/motd with current container IP +# - Removes old IP entries to avoid duplicates +# ------------------------------------------------------------------------------ +update_motd_ip() { + MOTD_FILE="/etc/motd" + + if [ -f "$MOTD_FILE" ]; then + # Remove existing IP Address lines to prevent duplication + sed -i '/IP Address:/d' "$MOTD_FILE" + + IP=$(get_current_ip) + # Add the new IP address + echo -e "${TAB}${NETWORK}${YW} IP Address: ${GN}${IP}${CL}" >>"$MOTD_FILE" + fi +} + +# ------------------------------------------------------------------------------ +# install_ssh_keys_into_ct() +# +# - Installs SSH keys into container root account if SSH is enabled +# - Uses pct push or direct input to authorized_keys +# - Falls back to warning if no keys provided +# ------------------------------------------------------------------------------ +install_ssh_keys_into_ct() { + [[ "$SSH" != "yes" ]] && return 0 + + if [[ -n "$SSH_KEYS_FILE" && -s "$SSH_KEYS_FILE" ]]; then + msg_info "Installing selected SSH keys into CT ${CTID}" + pct exec "$CTID" -- sh -c 'mkdir -p /root/.ssh && chmod 700 /root/.ssh' || { + msg_error "prepare /root/.ssh failed" + return 1 + } + pct push "$CTID" "$SSH_KEYS_FILE" /root/.ssh/authorized_keys >/dev/null 2>&1 || + pct exec "$CTID" -- sh -c "cat > /root/.ssh/authorized_keys" <"$SSH_KEYS_FILE" || { + msg_error "write authorized_keys failed" + return 1 + } + pct exec "$CTID" -- sh -c 'chmod 600 /root/.ssh/authorized_keys' || true + msg_ok "Installed SSH keys into CT ${CTID}" + return 0 + fi + + # Fallback: nichts ausgewählt + msg_warn "No SSH keys to install (skipping)." + return 0 +} + +# ------------------------------------------------------------------------------ +# base_settings() +# +# - Defines all base/default variables for container creation +# - Reads from environment variables (var_*) +# - Provides fallback defaults for OS type/version +# ------------------------------------------------------------------------------ +base_settings() { + # Default Settings + CT_TYPE=${var_unprivileged:-"1"} + DISK_SIZE=${var_disk:-"4"} + CORE_COUNT=${var_cpu:-"1"} + RAM_SIZE=${var_ram:-"1024"} + VERBOSE=${var_verbose:-"${1:-no}"} + PW=${var_pw:-""} + CT_ID=${var_ctid:-$NEXTID} + HN=${var_hostname:-$NSAPP} + BRG=${var_brg:-"vmbr0"} + NET=${var_net:-"dhcp"} + IPV6_METHOD=${var_ipv6_method:-"none"} + IPV6_STATIC=${var_ipv6_static:-""} + GATE=${var_gateway:-""} + APT_CACHER=${var_apt_cacher:-""} + APT_CACHER_IP=${var_apt_cacher_ip:-""} + MTU=${var_mtu:-""} + SD=${var_storage:-""} + NS=${var_ns:-""} + MAC=${var_mac:-""} + VLAN=${var_vlan:-""} + SSH=${var_ssh:-"no"} + SSH_AUTHORIZED_KEY=${var_ssh_authorized_key:-""} + UDHCPC_FIX=${var_udhcpc_fix:-""} + TAGS="community-script,${var_tags:-}" + ENABLE_FUSE=${var_fuse:-"${1:-no}"} + ENABLE_TUN=${var_tun:-"${1:-no}"} + + # Since these 2 are only defined outside of default_settings function, we add a temporary fallback. TODO: To align everything, we should add these as constant variables (e.g. OSTYPE and OSVERSION), but that would currently require updating the default_settings function for all existing scripts + if [ -z "$var_os" ]; then + var_os="debian" + fi + if [ -z "$var_version" ]; then + var_version="12" + fi +} + +# ------------------------------------------------------------------------------ +# echo_default() +# +# - Prints summary of default values (ID, OS, type, disk, RAM, CPU, etc.) +# - Uses icons and formatting for readability +# - Convert CT_TYPE to description +# ------------------------------------------------------------------------------ +echo_default() { + CT_TYPE_DESC="Unprivileged" + if [ "$CT_TYPE" -eq 0 ]; then + CT_TYPE_DESC="Privileged" + fi + + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}${CT_ID}${CL}" + echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os ($var_version)${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + if [ "$VERBOSE" == "yes" ]; then + echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}Enabled${CL}" + fi + echo -e "${CREATING}${BOLD}${BL}Creating a ${APP} LXC using the above default settings${CL}" + echo -e " " +} + +# ------------------------------------------------------------------------------ +# exit_script() +# +# - Called when user cancels an action +# - Clears screen and exits gracefully +# ------------------------------------------------------------------------------ +exit_script() { + clear + echo -e "\n${CROSS}${RD}User exited script${CL}\n" + exit +} + +# ------------------------------------------------------------------------------ +# find_host_ssh_keys() +# +# - Scans system for available SSH keys +# - Supports defaults (~/.ssh, /etc/ssh/authorized_keys) +# - Returns list of files containing valid SSH public keys +# - Sets FOUND_HOST_KEY_COUNT to number of keys found +# ------------------------------------------------------------------------------ +find_host_ssh_keys() { + local re='(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))' + local -a files=() cand=() + local g="${var_ssh_import_glob:-}" + local total=0 f base c + + shopt -s nullglob + if [[ -n "$g" ]]; then + for pat in $g; do cand+=($pat); done + else + cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2) + cand+=(/root/.ssh/*.pub) + cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*) + fi + shopt -u nullglob + + for f in "${cand[@]}"; do + [[ -f "$f" && -r "$f" ]] || continue + base="$(basename -- "$f")" + case "$base" in + known_hosts | known_hosts.* | config) continue ;; + id_*) [[ "$f" != *.pub ]] && continue ;; + esac + + # CRLF safe check for host keys + c=$(tr -d '\r' <"$f" | awk ' + /^[[:space:]]*#/ {next} + /^[[:space:]]*$/ {next} + {print} + ' | grep -E -c '"$re"' || true) + + if ((c > 0)); then + files+=("$f") + total=$((total + c)) + fi + done + + # Fallback to /root/.ssh/authorized_keys + if ((${#files[@]} == 0)) && [[ -r /root/.ssh/authorized_keys ]]; then + if grep -E -q "$re" /root/.ssh/authorized_keys; then + files+=(/root/.ssh/authorized_keys) + total=$((total + $(grep -E -c "$re" /root/.ssh/authorized_keys || echo 0))) + fi + fi + + FOUND_HOST_KEY_COUNT="$total" + ( + IFS=: + echo "${files[*]}" + ) +} + +# ------------------------------------------------------------------------------ +# advanced_settings() +# +# - Interactive whiptail menu for advanced configuration +# - Lets user set container type, password, CT ID, hostname, disk, CPU, RAM +# - Supports IPv4/IPv6, DNS, MAC, VLAN, tags, SSH keys, FUSE, verbose mode +# - Ends with confirmation or re-entry if cancelled +# ------------------------------------------------------------------------------ +advanced_settings() { + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox --title "Here is an instructional tip:" "To make a selection, use the Spacebar." 8 58 + # Setting Default Tag for Advanced Settings + TAGS="community-script;${var_tags:-}" + CT_DEFAULT_TYPE="${CT_TYPE}" + CT_TYPE="" + while [ -z "$CT_TYPE" ]; do + if [ "$CT_DEFAULT_TYPE" == "1" ]; then + if CT_TYPE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ + "1" "Unprivileged" ON \ + "0" "Privileged" OFF \ + 3>&1 1>&2 2>&3); then + if [ -n "$CT_TYPE" ]; then + CT_TYPE_DESC="Unprivileged" + if [ "$CT_TYPE" -eq 0 ]; then + CT_TYPE_DESC="Privileged" + fi + echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os | ${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" + fi + else + exit_script + fi + fi + if [ "$CT_DEFAULT_TYPE" == "0" ]; then + if CT_TYPE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ + "1" "Unprivileged" OFF \ + "0" "Privileged" ON \ + 3>&1 1>&2 2>&3); then + if [ -n "$CT_TYPE" ]; then + CT_TYPE_DESC="Unprivileged" + if [ "$CT_TYPE" -eq 0 ]; then + CT_TYPE_DESC="Privileged" + fi + echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os${CL}" + echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" + fi + else + exit_script + fi + fi + done + + while true; do + if PW1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --passwordbox "\nSet Root Password (needed for root ssh access)" 9 58 --title "PASSWORD (leave blank for automatic login)" 3>&1 1>&2 2>&3); then + # Empty = Autologin + if [[ -z "$PW1" ]]; then + PW="" + PW1="Automatic Login" + echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}$PW1${CL}" + break + fi + + # Invalid: contains spaces + if [[ "$PW1" == *" "* ]]; then + whiptail --msgbox "Password cannot contain spaces." 8 58 + continue + fi + + # Invalid: too short + if ((${#PW1} < 5)); then + whiptail --msgbox "Password must be at least 5 characters." 8 58 + continue + fi + + # Confirm password + if PW2=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then + if [[ "$PW1" == "$PW2" ]]; then + PW="-password $PW1" + echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}" + break + else + whiptail --msgbox "Passwords do not match. Please try again." 8 58 + fi + else + exit_script + fi + else + exit_script + fi + done + + if CT_ID=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Container ID" 8 58 "$NEXTID" --title "CONTAINER ID" 3>&1 1>&2 2>&3); then + if [ -z "$CT_ID" ]; then + CT_ID="$NEXTID" + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" + else + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" + fi + else + exit_script + fi + + while true; do + if CT_NAME=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 "$NSAPP" --title "HOSTNAME" 3>&1 1>&2 2>&3); then + if [ -z "$CT_NAME" ]; then + HN="$NSAPP" + else + HN=$(echo "${CT_NAME,,}" | tr -d ' ') + fi + # Hostname validate (RFC 1123) + if [[ "$HN" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ ]]; then + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" + break + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --msgbox "❌ Invalid hostname: '$HN'\n\nOnly lowercase letters, digits and hyphens (-) are allowed.\nUnderscores (_) or other characters are not permitted!" 10 70 + fi + else + exit_script + fi + done + + while true; do + DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GB" 8 58 "$var_disk" --title "DISK SIZE" 3>&1 1>&2 2>&3) || exit_script + + if [ -z "$DISK_SIZE" ]; then + DISK_SIZE="$var_disk" + fi + + if [[ "$DISK_SIZE" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" + break + else + whiptail --msgbox "Disk size must be a positive integer!" 8 58 + fi + done + + while true; do + CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --inputbox "Allocate CPU Cores" 8 58 "$var_cpu" --title "CORE COUNT" 3>&1 1>&2 2>&3) || exit_script + + if [ -z "$CORE_COUNT" ]; then + CORE_COUNT="$var_cpu" + fi + + if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + break + else + whiptail --msgbox "CPU core count must be a positive integer!" 8 58 + fi + done + + while true; do + RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --inputbox "Allocate RAM in MiB" 8 58 "$var_ram" --title "RAM" 3>&1 1>&2 2>&3) || exit_script + + if [ -z "$RAM_SIZE" ]; then + RAM_SIZE="$var_ram" + fi + + if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + break + else + whiptail --msgbox "RAM size must be a positive integer!" 8 58 + fi + done + + IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f) + BRIDGES="" + OLD_IFS=$IFS + IFS=$'\n' + for iface_filepath in ${IFACE_FILEPATH_LIST}; do + + iface_indexes_tmpfile=$(mktemp -q -u '.iface-XXXX') + (grep -Pn '^\s*iface' "${iface_filepath}" | cut -d':' -f1 && wc -l "${iface_filepath}" | cut -d' ' -f1) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' >"${iface_indexes_tmpfile}" || true + + if [ -f "${iface_indexes_tmpfile}" ]; then + + while read -r pair; do + start=$(echo "${pair}" | cut -d':' -f1) + end=$(echo "${pair}" | cut -d':' -f2) + + if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then + iface_name=$(sed "${start}q;d" "${iface_filepath}" | awk '{print $2}') + BRIDGES="${iface_name}"$'\n'"${BRIDGES}" + fi + + done <"${iface_indexes_tmpfile}" + rm -f "${iface_indexes_tmpfile}" + fi + + done + IFS=$OLD_IFS + BRIDGES=$(echo "$BRIDGES" | grep -v '^\s*$' | sort | uniq) + if [[ -z "$BRIDGES" ]]; then + BRG="vmbr0" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + else + # Build bridge menu with descriptions + BRIDGE_MENU_OPTIONS=() + while IFS= read -r bridge; do + if [[ -n "$bridge" ]]; then + # Get description from Proxmox built-in method - find comment for this specific bridge + description=$(grep -A 10 "iface $bridge" /etc/network/interfaces | grep '^#' | head -n1 | sed 's/^#\s*//') + if [[ -n "$description" ]]; then + BRIDGE_MENU_OPTIONS+=("$bridge" "${description}") + else + BRIDGE_MENU_OPTIONS+=("$bridge" " ") + fi + fi + done <<< "$BRIDGES" + + BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge: " 18 55 6 "${BRIDGE_MENU_OPTIONS[@]}" 3>&1 1>&2 2>&3) + if [[ -z "$BRG" ]]; then + exit_script + else + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + fi + fi + + # IPv4 methods: dhcp, static, none + while true; do + IPV4_METHOD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "IPv4 Address Management" \ + --menu "Select IPv4 Address Assignment Method:" 12 60 2 \ + "dhcp" "Automatic (DHCP, recommended)" \ + "static" "Static (manual entry)" \ + 3>&1 1>&2 2>&3) + + exit_status=$? + if [ $exit_status -ne 0 ]; then + exit_script + fi + + case "$IPV4_METHOD" in + dhcp) + NET="dhcp" + GATE="" + echo -e "${NETWORK}${BOLD}${DGN}IPv4: DHCP${CL}" + break + ;; + static) + # Static: call and validate CIDR address + while true; do + NET=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Enter Static IPv4 CIDR Address (e.g. 192.168.100.50/24)" 8 58 "" \ + --title "IPv4 ADDRESS" 3>&1 1>&2 2>&3) + if [ -z "$NET" ]; then + whiptail --msgbox "IPv4 address must not be empty." 8 58 + continue + elif [[ "$NET" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then + echo -e "${NETWORK}${BOLD}${DGN}IPv4 Address: ${BGN}$NET${CL}" + break + else + whiptail --msgbox "$NET is not a valid IPv4 CIDR address. Please enter a correct value!" 8 58 + fi + done + + # call and validate Gateway + while true; do + GATE1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Enter Gateway IP address for static IPv4" 8 58 "" \ + --title "Gateway IP" 3>&1 1>&2 2>&3) + if [ -z "$GATE1" ]; then + whiptail --msgbox "Gateway IP address cannot be empty." 8 58 + elif [[ ! "$GATE1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then + whiptail --msgbox "Invalid Gateway IP address format." 8 58 + else + GATE=",gw=$GATE1" + echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE1${CL}" + break + fi + done + break + ;; + esac + done + + # IPv6 Address Management selection + while true; do + IPV6_METHOD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --menu \ + "Select IPv6 Address Management Type:" 15 58 4 \ + "auto" "SLAAC/AUTO (recommended, default)" \ + "dhcp" "DHCPv6" \ + "static" "Static (manual entry)" \ + "none" "Disabled" \ + --default-item "auto" 3>&1 1>&2 2>&3) + [ $? -ne 0 ] && exit_script + + case "$IPV6_METHOD" in + auto) + echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}SLAAC/AUTO${CL}" + IPV6_ADDR="" + IPV6_GATE="" + break + ;; + dhcp) + echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}DHCPv6${CL}" + IPV6_ADDR="dhcp" + IPV6_GATE="" + break + ;; + static) + # Ask for static IPv6 address (CIDR notation, e.g., 2001:db8::1234/64) + while true; do + IPV6_ADDR=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox \ + "Set a static IPv6 CIDR address (e.g., 2001:db8::1234/64)" 8 58 "" \ + --title "IPv6 STATIC ADDRESS" 3>&1 1>&2 2>&3) || exit_script + if [[ "$IPV6_ADDR" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+(/[0-9]{1,3})$ ]]; then + echo -e "${NETWORK}${BOLD}${DGN}IPv6 Address: ${BGN}$IPV6_ADDR${CL}" + break + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox \ + "$IPV6_ADDR is an invalid IPv6 CIDR address. Please enter a valid IPv6 CIDR address (e.g., 2001:db8::1234/64)" 8 58 + fi + done + # Optional: ask for IPv6 gateway for static config + while true; do + IPV6_GATE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox \ + "Enter IPv6 gateway address (optional, leave blank for none)" 8 58 "" --title "IPv6 GATEWAY" 3>&1 1>&2 2>&3) + if [ -z "$IPV6_GATE" ]; then + IPV6_GATE="" + break + elif [[ "$IPV6_GATE" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+$ ]]; then + break + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox \ + "Invalid IPv6 gateway format." 8 58 + fi + done + break + ;; + none) + echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}Disabled${CL}" + IPV6_ADDR="none" + IPV6_GATE="" + break + ;; + *) + exit_script + ;; + esac + done + + if [ "$var_os" == "alpine" ]; then + APT_CACHER="" + APT_CACHER_IP="" + else + if APT_CACHER_IP=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set APT-Cacher IP (leave blank for none)" 8 58 --title "APT-Cacher IP" 3>&1 1>&2 2>&3); then + APT_CACHER="${APT_CACHER_IP:+yes}" + echo -e "${NETWORK}${BOLD}${DGN}APT-Cacher IP Address: ${BGN}${APT_CACHER_IP:-Default}${CL}" + else + exit_script + fi + fi + + # if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "IPv6" --yesno "Disable IPv6?" 10 58); then + # DISABLEIP6="yes" + # else + # DISABLEIP6="no" + # fi + # echo -e "${DISABLEIPV6}${BOLD}${DGN}Disable IPv6: ${BGN}$DISABLEIP6${CL}" + + if MTU1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default [The MTU of your selected vmbr, default is 1500])" 8 58 --title "MTU SIZE" 3>&1 1>&2 2>&3); then + if [ -z "$MTU1" ]; then + MTU1="Default" + MTU="" + else + MTU=",mtu=$MTU1" + fi + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" + else + exit_script + fi + + if SD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a DNS Search Domain (leave blank for HOST)" 8 58 --title "DNS Search Domain" 3>&1 1>&2 2>&3); then + if [ -z "$SD" ]; then + SX=Host + SD="" + else + SX=$SD + SD="-searchdomain=$SD" + fi + echo -e "${SEARCH}${BOLD}${DGN}DNS Search Domain: ${BGN}$SX${CL}" + else + exit_script + fi + + if NX=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a DNS Server IP (leave blank for HOST)" 8 58 --title "DNS SERVER IP" 3>&1 1>&2 2>&3); then + if [ -z "$NX" ]; then + NX=Host + NS="" + else + NS="-nameserver=$NX" + fi + echo -e "${NETWORK}${BOLD}${DGN}DNS Server IP Address: ${BGN}$NX${CL}" + else + exit_script + fi + + if [ "$var_os" == "alpine" ] && [ "$NET" == "dhcp" ] && [ "$NX" != "Host" ]; then + UDHCPC_FIX="yes" + else + UDHCPC_FIX="no" + fi + export UDHCPC_FIX + + if MAC1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a MAC Address(leave blank for generated MAC)" 8 58 --title "MAC ADDRESS" 3>&1 1>&2 2>&3); then + if [ -z "$MAC1" ]; then + MAC1="Default" + MAC="" + else + MAC=",hwaddr=$MAC1" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" + fi + else + exit_script + fi + + if VLAN1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for no VLAN)" 8 58 --title "VLAN" 3>&1 1>&2 2>&3); then + if [ -z "$VLAN1" ]; then + VLAN1="Default" + VLAN="" + else + VLAN=",tag=$VLAN1" + fi + echo -e "${VLANTAG}${BOLD}${DGN}Vlan: ${BGN}$VLAN1${CL}" + else + exit_script + fi + + if ADV_TAGS=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Custom Tags?[If you remove all, there will be no tags!]" 8 58 "${TAGS}" --title "Advanced Tags" 3>&1 1>&2 2>&3); then + if [ -n "${ADV_TAGS}" ]; then + ADV_TAGS=$(echo "$ADV_TAGS" | tr -d '[:space:]') + TAGS="${ADV_TAGS}" + else + TAGS=";" + fi + echo -e "${NETWORK}${BOLD}${DGN}Tags: ${BGN}$TAGS${CL}" + else + exit_script + fi + + # --- SSH key provisioning (one dialog) --- + SSH_KEYS_FILE="$(mktemp)" + : >"$SSH_KEYS_FILE" + + IFS=$'\0' read -r -d '' -a _def_files < <(ssh_discover_default_files && printf '\0') + ssh_build_choices_from_files "${_def_files[@]}" + DEF_KEYS_COUNT="$COUNT" + + if [[ "$DEF_KEYS_COUNT" -gt 0 ]]; then + SSH_KEY_MODE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ + "Provision SSH keys for root:" 14 72 4 \ + "found" "Select from detected keys (${DEF_KEYS_COUNT})" \ + "manual" "Paste a single public key" \ + "folder" "Scan another folder (path or glob)" \ + "none" "No keys" 3>&1 1>&2 2>&3) || exit_script + else + SSH_KEY_MODE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ + "No host keys detected; choose manual/none:" 12 72 2 \ + "manual" "Paste a single public key" \ + "none" "No keys" 3>&1 1>&2 2>&3) || exit_script + fi + + case "$SSH_KEY_MODE" in + found) + SEL=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT HOST KEYS" \ + --checklist "Select one or more keys to import:" 20 140 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script + for tag in $SEL; do + tag="${tag%\"}" + tag="${tag#\"}" + line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) + [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" + done + ;; + manual) + SSH_AUTHORIZED_KEY="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Paste one SSH public key line (ssh-ed25519/ssh-rsa/...)" 10 72 --title "SSH Public Key" 3>&1 1>&2 2>&3)" + [[ -n "$SSH_AUTHORIZED_KEY" ]] && printf '%s\n' "$SSH_AUTHORIZED_KEY" >>"$SSH_KEYS_FILE" + ;; + folder) + GLOB_PATH="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Enter a folder or glob to scan (e.g. /root/.ssh/*.pub)" 10 72 --title "Scan Folder/Glob" 3>&1 1>&2 2>&3)" + if [[ -n "$GLOB_PATH" ]]; then + shopt -s nullglob + read -r -a _scan_files <<<"$GLOB_PATH" + shopt -u nullglob + if [[ "${#_scan_files[@]}" -gt 0 ]]; then + ssh_build_choices_from_files "${_scan_files[@]}" + if [[ "$COUNT" -gt 0 ]]; then + SEL=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT FOLDER KEYS" \ + --checklist "Select key(s) to import:" 20 78 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script + for tag in $SEL; do + tag="${tag%\"}" + tag="${tag#\"}" + line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) + [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" + done + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "No keys found in: $GLOB_PATH" 8 60 + fi + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "Path/glob returned no files." 8 60 + fi + fi + ;; + none) : ;; + esac + + # Dedupe + clean EOF + if [[ -s "$SSH_KEYS_FILE" ]]; then + sort -u -o "$SSH_KEYS_FILE" "$SSH_KEYS_FILE" + printf '\n' >>"$SSH_KEYS_FILE" + fi + + # SSH activate, if keys found or password set + if [[ -s "$SSH_KEYS_FILE" || "$PW" == -password* ]]; then + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable root SSH access?" 10 58); then + SSH="yes" + else + SSH="no" + fi + else + SSH="no" + fi + echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}" + + export SSH_KEYS_FILE + if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "FUSE Support" --yesno "Enable FUSE support?\nRequired for tools like rclone, mergerfs, AppImage, etc." 10 58); then + ENABLE_FUSE="yes" + else + ENABLE_FUSE="no" + fi + echo -e "${FUSE}${BOLD}${DGN}Enable FUSE Support: ${BGN}$ENABLE_FUSE${CL}" + + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then + VERBOSE="yes" + else + VERBOSE="no" + fi + echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}" + + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create ${APP} LXC?" 10 58); then + echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}" + else + clear + header_info + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings on node $PVEHOST_NAME${CL}" + advanced_settings + fi +} + +# ------------------------------------------------------------------------------ +# diagnostics_check() +# +# - Ensures diagnostics config file exists at /usr/local/community-scripts/diagnostics +# - Asks user whether to send anonymous diagnostic data +# - Saves DIAGNOSTICS=yes/no in the config file +# ------------------------------------------------------------------------------ +diagnostics_check() { + if ! [ -d "/usr/local/community-scripts" ]; then + mkdir -p /usr/local/community-scripts + fi + + if ! [ -f "/usr/local/community-scripts/diagnostics" ]; then + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS" --yesno "Send Diagnostics of LXC Installation?\n\n(This only transmits data without user data, just RAM, CPU, LXC name, ...)" 10 58); then + cat </usr/local/community-scripts/diagnostics +DIAGNOSTICS=yes + +#This file is used to store the diagnostics settings for the Community-Scripts API. +#https://github.com/community-scripts/ProxmoxVED/discussions/1836 +#Your diagnostics will be sent to the Community-Scripts API for troubleshooting/statistical purposes. +#You can review the data at https://community-scripts.github.io/ProxmoxVE/data +#If you do not wish to send diagnostics, please set the variable 'DIAGNOSTICS' to "no" in /usr/local/community-scripts/diagnostics, or use the menue. +#This will disable the diagnostics feature. +#To send diagnostics, set the variable 'DIAGNOSTICS' to "yes" in /usr/local/community-scripts/diagnostics, or use the menue. +#This will enable the diagnostics feature. +#The following information will be sent: +#"disk_size" +#"core_count" +#"ram_size" +#"os_type" +#"os_version" +#"nsapp" +#"method" +#"pve_version" +#"status" +#If you have any concerns, please review the source code at /misc/build.func +EOF + DIAGNOSTICS="yes" + else + cat </usr/local/community-scripts/diagnostics +DIAGNOSTICS=no + +#This file is used to store the diagnostics settings for the Community-Scripts API. +#https://github.com/community-scripts/ProxmoxVED/discussions/1836 +#Your diagnostics will be sent to the Community-Scripts API for troubleshooting/statistical purposes. +#You can review the data at https://community-scripts.github.io/ProxmoxVE/data +#If you do not wish to send diagnostics, please set the variable 'DIAGNOSTICS' to "no" in /usr/local/community-scripts/diagnostics, or use the menue. +#This will disable the diagnostics feature. +#To send diagnostics, set the variable 'DIAGNOSTICS' to "yes" in /usr/local/community-scripts/diagnostics, or use the menue. +#This will enable the diagnostics feature. +#The following information will be sent: +#"disk_size" +#"core_count" +#"ram_size" +#"os_type" +#"os_version" +#"nsapp" +#"method" +#"pve_version" +#"status" +#If you have any concerns, please review the source code at /misc/build.func +EOF + DIAGNOSTICS="no" + fi + else + DIAGNOSTICS=$(awk -F '=' '/^DIAGNOSTICS/ {print $2}' /usr/local/community-scripts/diagnostics) + + fi + +} + +# ------------------------------------------------------------------------------ +# default_var_settings +# +# - Ensures /usr/local/community-scripts/default.vars exists (creates if missing) +# - Loads var_* values from default.vars (safe parser, no source/eval) +# - Precedence: ENV var_* > default.vars > built-in defaults +# - Maps var_verbose → VERBOSE +# - Calls base_settings "$VERBOSE" and echo_default +# ------------------------------------------------------------------------------ +default_var_settings() { + # Allowed var_* keys (alphabetically sorted) + local VAR_WHITELIST=( + var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_ctid var_disk var_fuse + var_gateway var_hostname var_ipv6_method var_ipv6_static var_mac var_mtu + var_net var_ns var_pw var_ram var_tags var_tun var_unprivileged + var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage + ) + + # Snapshot: environment variables (highest precedence) + declare -A _HARD_ENV=() + local _k + for _k in "${VAR_WHITELIST[@]}"; do + if printenv "$_k" >/dev/null 2>&1; then _HARD_ENV["$_k"]=1; fi + done + + # Find default.vars location + local _find_default_vars + _find_default_vars() { + local f + for f in \ + /usr/local/community-scripts/default.vars \ + "$HOME/.config/community-scripts/default.vars" \ + "./default.vars"; do + [ -f "$f" ] && { + echo "$f" + return 0 + } + done + return 1 + } + # Allow override of storages via env (for non-interactive use cases) + [ -n "${var_template_storage:-}" ] && TEMPLATE_STORAGE="$var_template_storage" + [ -n "${var_container_storage:-}" ] && CONTAINER_STORAGE="$var_container_storage" + + # Create once, with storages already selected, no var_ctid/var_hostname lines + local _ensure_default_vars + _ensure_default_vars() { + _find_default_vars >/dev/null 2>&1 && return 0 + + local canonical="/usr/local/community-scripts/default.vars" + msg_info "No default.vars found. Creating ${canonical}" + mkdir -p /usr/local/community-scripts + + # Pick storages before writing the file (always ask unless only one) + # Create a minimal temp file to write into + : >"$canonical" + + # Base content (no var_ctid / var_hostname here) + cat >"$canonical" <<'EOF' +# Community-Scripts defaults (var_* only). Lines starting with # are comments. +# Precedence: ENV var_* > default.vars > built-ins. +# Keep keys alphabetically sorted. + +# Container type +var_unprivileged=1 + +# Resources +var_cpu=1 +var_disk=4 +var_ram=1024 + +# Network +var_brg=vmbr0 +var_net=dhcp +var_ipv6_method=none +# var_gateway= +# var_ipv6_static= +# var_vlan= +# var_mtu= +# var_mac= +# var_ns= + +# SSH +var_ssh=no +# var_ssh_authorized_key= + +# APT cacher (optional) +# var_apt_cacher=yes +# var_apt_cacher_ip=192.168.1.10 + +# Features/Tags/verbosity +var_fuse=no +var_tun=no +var_tags=community-script +var_verbose=no + +# Security (root PW) – empty => autologin +# var_pw= +EOF + + # Now choose storages (always prompt unless just one exists) + choose_and_set_storage_for_file "$canonical" template + choose_and_set_storage_for_file "$canonical" container + + chmod 0644 "$canonical" + msg_ok "Created ${canonical}" + } + + # Whitelist check + local _is_whitelisted_key + _is_whitelisted_key() { + local k="$1" + local w + for w in "${VAR_WHITELIST[@]}"; do [ "$k" = "$w" ] && return 0; done + return 1 + } + + # Safe parser for KEY=VALUE lines + local _load_vars_file + _load_vars_file() { + local file="$1" + [ -f "$file" ] || return 0 + msg_info "Loading defaults from ${file}" + local line key val + while IFS= read -r line || [ -n "$line" ]; do + line="${line#"${line%%[![:space:]]*}"}" + line="${line%"${line##*[![:space:]]}"}" + [[ -z "$line" || "$line" == \#* ]] && continue + if [[ "$line" =~ ^([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then + local var_key="${BASH_REMATCH[1]}" + local var_val="${BASH_REMATCH[2]}" + + [[ "$var_key" != var_* ]] && continue + _is_whitelisted_key "$var_key" || { + msg_debug "Ignore non-whitelisted ${var_key}" + continue + } + + # Strip quotes + if [[ "$var_val" =~ ^\"(.*)\"$ ]]; then + var_val="${BASH_REMATCH[1]}" + elif [[ "$var_val" =~ ^\'(.*)\'$ ]]; then + var_val="${BASH_REMATCH[1]}" + fi + + # Unsafe characters + case $var_val in + \"*\") + var_val=${var_val#\"} + var_val=${var_val%\"} + ;; + \'*\') + var_val=${var_val#\'} + var_val=${var_val%\'} + ;; + esac # Hard env wins + [[ -n "${_HARD_ENV[$var_key]:-}" ]] && continue + # Set only if not already exported + [[ -z "${!var_key+x}" ]] && export "${var_key}=${var_val}" + else + msg_warn "Malformed line in ${file}: ${line}" + fi + done <"$file" + msg_ok "Loaded ${file}" + } + + # 1) Ensure file exists + _ensure_default_vars + + # 2) Load file + local dv + dv="$(_find_default_vars)" || { + msg_error "default.vars not found after ensure step" + return 1 + } + _load_vars_file "$dv" + + # 3) Map var_verbose → VERBOSE + if [[ -n "${var_verbose:-}" ]]; then + case "${var_verbose,,}" in 1 | yes | true | on) VERBOSE="yes" ;; 0 | no | false | off) VERBOSE="no" ;; *) VERBOSE="${var_verbose}" ;; esac + else + VERBOSE="no" + fi + + # 4) Apply base settings and show summary + METHOD="mydefaults-global" + base_settings "$VERBOSE" + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using My Defaults (default.vars) on node $PVEHOST_NAME${CL}" + echo_default +} + +# ------------------------------------------------------------------------------ +# get_app_defaults_path() +# +# - Returns full path for app-specific defaults file +# - Example: /usr/local/community-scripts/defaults/.vars +# ------------------------------------------------------------------------------ + +get_app_defaults_path() { + local n="${NSAPP:-${APP,,}}" + echo "/usr/local/community-scripts/defaults/${n}.vars" +} + +# ------------------------------------------------------------------------------ +# maybe_offer_save_app_defaults +# +# - Called after advanced_settings returned with fully chosen values. +# - If no .vars exists, offers to persist current advanced settings +# into /usr/local/community-scripts/defaults/.vars +# - Only writes whitelisted var_* keys. +# - Extracts raw values from flags like ",gw=..." ",mtu=..." etc. +# ------------------------------------------------------------------------------ +if ! declare -p VAR_WHITELIST >/dev/null 2>&1; then + declare -ag VAR_WHITELIST=( + var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_ctid var_disk var_fuse + var_gateway var_hostname var_ipv6_method var_ipv6_static var_mac var_mtu + var_net var_ns var_pw var_ram var_tags var_tun var_unprivileged + var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage + ) +fi + +_is_whitelisted_key() { + local k="$1" + local w + for w in "${VAR_WHITELIST[@]}"; do [[ "$k" == "$w" ]] && return 0; done + return 1 +} + +_sanitize_value() { + # Disallow Command-Substitution / Shell-Meta + case "$1" in + *'$('* | *'`'* | *';'* | *'&'* | *'<('*) + echo "" + return 0 + ;; + esac + echo "$1" +} + +# Map-Parser: read var_* from file into _VARS_IN associative array +declare -A _VARS_IN +_load_vars_file() { + local file="$1" + [ -f "$file" ] || return 0 + msg_info "Loading defaults from ${file}" + local line key val + while IFS= read -r line || [ -n "$line" ]; do + line="${line#"${line%%[![:space:]]*}"}" + line="${line%"${line##*[![:space:]]}"}" + [ -z "$line" ] && continue + case "$line" in + \#*) continue ;; + esac + key=$(printf "%s" "$line" | cut -d= -f1) + val=$(printf "%s" "$line" | cut -d= -f2-) + case "$key" in + var_*) + if _is_whitelisted_key "$key"; then + [ -z "${!key+x}" ] && export "$key=$val" + fi + ;; + esac + done <"$file" + msg_ok "Loaded ${file}" +} + +# Diff function for two var_* files -> produces human-readable diff list for $1 (old) vs $2 (new) +_build_vars_diff() { + local oldf="$1" newf="$2" + local k + local -A OLD=() NEW=() + _load_vars_file_to_map "$oldf" + for k in "${!_VARS_IN[@]}"; do OLD["$k"]="${_VARS_IN[$k]}"; done + _load_vars_file_to_map "$newf" + for k in "${!_VARS_IN[@]}"; do NEW["$k"]="${_VARS_IN[$k]}"; done + + local out + out+="# Diff for ${APP} (${NSAPP})\n" + out+="# Old: ${oldf}\n# New: ${newf}\n\n" + + local found_change=0 + + # Changed & Removed + for k in "${!OLD[@]}"; do + if [[ -v NEW["$k"] ]]; then + if [[ "${OLD[$k]}" != "${NEW[$k]}" ]]; then + out+="~ ${k}\n - old: ${OLD[$k]}\n + new: ${NEW[$k]}\n" + found_change=1 + fi + else + out+="- ${k}\n - old: ${OLD[$k]}\n" + found_change=1 + fi + done + + # Added + for k in "${!NEW[@]}"; do + if [[ ! -v OLD["$k"] ]]; then + out+="+ ${k}\n + new: ${NEW[$k]}\n" + found_change=1 + fi + done + + if [[ $found_change -eq 0 ]]; then + out+="(No differences)\n" + fi + + printf "%b" "$out" +} + +# Build a temporary .vars file from current advanced settings +_build_current_app_vars_tmp() { + tmpf="$(mktemp /tmp/${NSAPP:-app}.vars.new.XXXXXX)" + + # NET/GW + _net="${NET:-}" + _gate="" + case "${GATE:-}" in + ,gw=*) _gate=$(echo "$GATE" | sed 's/^,gw=//') ;; + esac + + # IPv6 + _ipv6_method="${IPV6_METHOD:-auto}" + _ipv6_static="" + _ipv6_gateway="" + if [ "$_ipv6_method" = "static" ]; then + _ipv6_static="${IPV6_ADDR:-}" + _ipv6_gateway="${IPV6_GATE:-}" + fi + + # MTU/VLAN/MAC + _mtu="" + _vlan="" + _mac="" + case "${MTU:-}" in + ,mtu=*) _mtu=$(echo "$MTU" | sed 's/^,mtu=//') ;; + esac + case "${VLAN:-}" in + ,tag=*) _vlan=$(echo "$VLAN" | sed 's/^,tag=//') ;; + esac + case "${MAC:-}" in + ,hwaddr=*) _mac=$(echo "$MAC" | sed 's/^,hwaddr=//') ;; + esac + + # DNS / Searchdomain + _ns="" + _searchdomain="" + case "${NS:-}" in + -nameserver=*) _ns=$(echo "$NS" | sed 's/^-nameserver=//') ;; + esac + case "${SD:-}" in + -searchdomain=*) _searchdomain=$(echo "$SD" | sed 's/^-searchdomain=//') ;; + esac + + # SSH / APT / Features + _ssh="${SSH:-no}" + _ssh_auth="${SSH_AUTHORIZED_KEY:-}" + _apt_cacher="${APT_CACHER:-}" + _apt_cacher_ip="${APT_CACHER_IP:-}" + _fuse="${ENABLE_FUSE:-no}" + _tun="${ENABLE_TUN:-no}" + _tags="${TAGS:-}" + _verbose="${VERBOSE:-no}" + + # Type / Resources / Identity + _unpriv="${CT_TYPE:-1}" + _cpu="${CORE_COUNT:-1}" + _ram="${RAM_SIZE:-1024}" + _disk="${DISK_SIZE:-4}" + _hostname="${HN:-$NSAPP}" + + # Storage + _tpl_storage="${TEMPLATE_STORAGE:-${var_template_storage:-}}" + _ct_storage="${CONTAINER_STORAGE:-${var_container_storage:-}}" + + { + echo "# App-specific defaults for ${APP} (${NSAPP})" + echo "# Generated on $(date -u '+%Y-%m-%dT%H:%M:%SZ')" + echo + + echo "var_unprivileged=$(_sanitize_value "$_unpriv")" + echo "var_cpu=$(_sanitize_value "$_cpu")" + echo "var_ram=$(_sanitize_value "$_ram")" + echo "var_disk=$(_sanitize_value "$_disk")" + + [ -n "${BRG:-}" ] && echo "var_brg=$(_sanitize_value "$BRG")" + [ -n "$_net" ] && echo "var_net=$(_sanitize_value "$_net")" + [ -n "$_gate" ] && echo "var_gateway=$(_sanitize_value "$_gate")" + [ -n "$_mtu" ] && echo "var_mtu=$(_sanitize_value "$_mtu")" + [ -n "$_vlan" ] && echo "var_vlan=$(_sanitize_value "$_vlan")" + [ -n "$_mac" ] && echo "var_mac=$(_sanitize_value "$_mac")" + [ -n "$_ns" ] && echo "var_ns=$(_sanitize_value "$_ns")" + + [ -n "$_ipv6_method" ] && echo "var_ipv6_method=$(_sanitize_value "$_ipv6_method")" + [ -n "$_ipv6_static" ] && echo "var_ipv6_static=$(_sanitize_value "$_ipv6_static")" + + [ -n "$_ssh" ] && echo "var_ssh=$(_sanitize_value "$_ssh")" + [ -n "$_ssh_auth" ] && echo "var_ssh_authorized_key=$(_sanitize_value "$_ssh_auth")" + + [ -n "$_apt_cacher" ] && echo "var_apt_cacher=$(_sanitize_value "$_apt_cacher")" + [ -n "$_apt_cacher_ip" ] && echo "var_apt_cacher_ip=$(_sanitize_value "$_apt_cacher_ip")" + + [ -n "$_fuse" ] && echo "var_fuse=$(_sanitize_value "$_fuse")" + [ -n "$_tun" ] && echo "var_tun=$(_sanitize_value "$_tun")" + [ -n "$_tags" ] && echo "var_tags=$(_sanitize_value "$_tags")" + [ -n "$_verbose" ] && echo "var_verbose=$(_sanitize_value "$_verbose")" + + [ -n "$_hostname" ] && echo "var_hostname=$(_sanitize_value "$_hostname")" + [ -n "$_searchdomain" ] && echo "var_searchdomain=$(_sanitize_value "$_searchdomain")" + + [ -n "$_tpl_storage" ] && echo "var_template_storage=$(_sanitize_value "$_tpl_storage")" + [ -n "$_ct_storage" ] && echo "var_container_storage=$(_sanitize_value "$_ct_storage")" + } >"$tmpf" + + echo "$tmpf" +} + +# ------------------------------------------------------------------------------ +# maybe_offer_save_app_defaults() +# +# - Called after advanced_settings() +# - Offers to save current values as app defaults if not existing +# - If file exists: shows diff and allows Update, Keep, View Diff, or Cancel +# ------------------------------------------------------------------------------ +maybe_offer_save_app_defaults() { + local app_vars_path + app_vars_path="$(get_app_defaults_path)" + + # always build from current settings + local new_tmp diff_tmp + new_tmp="$(_build_current_app_vars_tmp)" + diff_tmp="$(mktemp -p /tmp "${NSAPP:-app}.vars.diff.XXXXXX")" + + # 1) if no file → offer to create + if [[ ! -f "$app_vars_path" ]]; then + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --yesno "Save these advanced settings as defaults for ${APP}?\n\nThis will create:\n${app_vars_path}" 12 72; then + mkdir -p "$(dirname "$app_vars_path")" + install -m 0644 "$new_tmp" "$app_vars_path" + msg_ok "Saved app defaults: ${app_vars_path}" + fi + rm -f "$new_tmp" "$diff_tmp" + return 0 + fi + + # 2) if file exists → build diff + _build_vars_diff "$app_vars_path" "$new_tmp" >"$diff_tmp" + + # if no differences → do nothing + if grep -q "^(No differences)$" "$diff_tmp"; then + rm -f "$new_tmp" "$diff_tmp" + return 0 + fi + + # 3) if file exists → show menu with default selection "Update Defaults" + local app_vars_file + app_vars_file="$(basename "$app_vars_path")" + + while true; do + local sel + sel="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "APP DEFAULTS – ${APP}" \ + --menu "Differences detected. What do you want to do?" 20 78 10 \ + "Update Defaults" "Write new values to ${app_vars_file}" \ + "Keep Current" "Keep existing defaults (no changes)" \ + "View Diff" "Show a detailed diff" \ + "Cancel" "Abort without changes" \ + --default-item "Update Defaults" \ + 3>&1 1>&2 2>&3)" || { sel="Cancel"; } + + case "$sel" in + "Update Defaults") + install -m 0644 "$new_tmp" "$app_vars_path" + msg_ok "Updated app defaults: ${app_vars_path}" + break + ;; + "Keep Current") + msg_info "Keeping current app defaults: ${app_vars_path}" + break + ;; + "View Diff") + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "Diff – ${APP}" \ + --scrolltext --textbox "$diff_tmp" 25 100 + ;; + "Cancel" | *) + msg_info "Canceled. No changes to app defaults." + break + ;; + esac + done + + rm -f "$new_tmp" "$diff_tmp" +} + +ensure_storage_selection_for_vars_file() { + local vf="$1" + + # Read stored values (if any) + local tpl ct + tpl=$(grep -E '^var_template_storage=' "$vf" | cut -d= -f2-) + ct=$(grep -E '^var_container_storage=' "$vf" | cut -d= -f2-) + + # Wenn beide Werte schon existieren → übernehmen + if [[ -n "$tpl" && -n "$ct" ]]; then + TEMPLATE_STORAGE="$tpl" + CONTAINER_STORAGE="$ct" + echo_storage_summary_from_file "$vf" + return 0 + fi + + # --- Erstmalige Auswahl: beide Abfragen --- + select_storage template + local tpl_sel="$STORAGE_RESULT" + + select_storage container + local ct_sel="$STORAGE_RESULT" + + # --- Zusammenfassung + Nachfrage --- + if whiptail --backtitle "Community Scripts" --title "Default Storage" \ + --yesno "Template-Storage --> $tpl_sel\nContainer-Storage --> $ct_sel\n\nSave as global defaults?" \ + 12 70; then + sed -i '/^[#[:space:]]*var_template_storage=/d' "$vf" + sed -i '/^[#[:space:]]*var_container_storage=/d' "$vf" + echo "var_template_storage=$tpl_sel" >>"$vf" + echo "var_container_storage=$ct_sel" >>"$vf" + else + sed -i '/^[#[:space:]]*var_template_storage=/d' "$vf" + sed -i '/^[#[:space:]]*var_container_storage=/d' "$vf" + echo "# var_template_storage=$tpl_sel" >>"$vf" + echo "# var_container_storage=$ct_sel" >>"$vf" + fi + + TEMPLATE_STORAGE="$tpl_sel" + CONTAINER_STORAGE="$ct_sel" + msg_ok "Using Template-Storage → $tpl_sel" + msg_ok "Using Container-Storage → $ct_sel" +} + +diagnostics_menu() { + if [ "${DIAGNOSTICS:-no}" = "yes" ]; then + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "DIAGNOSTIC SETTINGS" \ + --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ + --yes-button "No" --no-button "Back"; then + DIAGNOSTICS="no" + sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=no/' /usr/local/community-scripts/diagnostics + whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 + fi + else + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "DIAGNOSTIC SETTINGS" \ + --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ + --yes-button "Yes" --no-button "Back"; then + DIAGNOSTICS="yes" + sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=yes/' /usr/local/community-scripts/diagnostics + whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 + fi + fi +} + +# ------------------------------------------------------------------------------ +# install_script() +# +# - Main entrypoint for installation mode +# - Runs safety checks (pve_check, root_check, maxkeys_check, diagnostics_check) +# - Builds interactive menu (Default, Verbose, Advanced, My Defaults, App Defaults, Diagnostics, Storage, Exit) +# - Applies chosen settings and triggers container build +# ------------------------------------------------------------------------------ +install_script() { + pve_check + shell_check + root_check + arch_check + ssh_check + maxkeys_check + diagnostics_check + + if systemctl is-active -q ping-instances.service; then + systemctl -q stop ping-instances.service + fi + + NEXTID=$(pvesh get /cluster/nextid) + timezone=$(cat /etc/timezone) + header_info + + # --- Support CLI argument as direct preset (default, advanced, …) --- + CHOICE="${mode:-${1:-}}" + + # If no CLI argument → show whiptail menu + if [ -z "$CHOICE" ]; then + local menu_items=( + "1" "Default Install" + "2" "Advanced Install" + "3" "My Defaults" + ) + + if [ -f "$(get_app_defaults_path)" ]; then + menu_items+=("4" "App Defaults for ${APP}") + menu_items+=("5" "Settings") + else + menu_items+=("4" "Settings") + fi + + TMP_CHOICE=$(whiptail \ + --backtitle "Proxmox VE Helper Scripts" \ + --title "Community-Scripts Options" \ + --ok-button "Select" --cancel-button "Exit Script" \ + --notags \ + --menu "\nChoose an option:\n Use TAB or Arrow keys to navigate, ENTER to select.\n" \ + 20 60 9 \ + "${menu_items[@]}" \ + --default-item "1" \ + 3>&1 1>&2 2>&3) || exit_script + CHOICE="$TMP_CHOICE" + fi + + # --- Main case --- + case "$CHOICE" in + 1 | default | DEFAULT) + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME${CL}" + VERBOSE="no" + METHOD="default" + base_settings "$VERBOSE" + echo_default + [[ -f /usr/local/community-scripts/default.vars ]] || { + mkdir -p /usr/local/community-scripts + touch /usr/local/community-scripts/default.vars + } + ensure_storage_selection_for_vars_file "/usr/local/community-scripts/default.vars" + + ;; + 2 | advanced | ADVANCED) + header_info + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Install on node $PVEHOST_NAME${CL}" + METHOD="advanced" + base_settings + advanced_settings + [[ -f /usr/local/community-scripts/default.vars ]] || { + mkdir -p /usr/local/community-scripts + touch /usr/local/community-scripts/default.vars + } + ensure_storage_selection_for_vars_file "/usr/local/community-scripts/default.vars" + + maybe_offer_save_app_defaults + ;; + 3 | mydefaults | MYDEFAULTS) + default_var_settings || { + msg_error "Failed to apply default.vars" + exit 1 + } + ensure_storage_selection_for_vars_file "/usr/local/community-scripts/default.vars" + ;; + 4 | appdefaults | APPDEFAULTS) + if [ -f "$(get_app_defaults_path)" ]; then + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using App Defaults for ${APP} on node $PVEHOST_NAME${CL}" + METHOD="appdefaults" + base_settings + _load_vars_file "$(get_app_defaults_path)" + echo_default + ensure_storage_selection_for_vars_file "$(get_app_defaults_path)" + else + msg_error "No App Defaults available for ${APP}" + exit 1 + fi + ;; + 5 | settings | SETTINGS) + settings_menu + ;; + *) + echo -e "${CROSS}${RD}Invalid option: $CHOICE${CL}" + exit 1 + ;; + esac +} + +edit_default_storage() { + local vf="/usr/local/community-scripts/default.vars" + + # make sure file exists + if [[ ! -f "$vf" ]]; then + # still create + mkdir -p "$(dirname "$vf")" + touch "$vf" + + if select_storage template; then + echo "var_template_storage=$STORAGE_RESULT" >>"$vf" + fi + + if select_storage container; then + echo "var_container_storage=$STORAGE_RESULT" >>"$vf" + fi + fi + + # reuse the same Whiptail selection we already have + ensure_storage_selection_for_vars_file "$vf" +} + +settings_menu() { + while true; do + local settings_items=( + "1" "Manage API-Diagnostic Setting" + "2" "Edit Default.vars" + "3" "Edit Default Storage" + ) + if [ -f "$(get_app_defaults_path)" ]; then + settings_items+=("4" "Edit App.vars for ${APP}") + settings_items+=("5" "Exit") + else + settings_items+=("4" "Exit") + fi + + local choice + choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "Community-Scripts SETTINGS Menu" \ + --ok-button "OK" --cancel-button "Back" \ + --menu "\n\nChoose a settings option:\n\nUse TAB or Arrow keys to navigate, ENTER to select." 20 60 9 \ + "${settings_items[@]}" \ + 3>&1 1>&2 2>&3) || break + + case "$choice" in + 1) diagnostics_menu ;; + 2) ${EDITOR:-nano} /usr/local/community-scripts/default.vars ;; + 3) edit_default_storage ;; + 4) + if [ -f "$(get_app_defaults_path)" ]; then + ${EDITOR:-nano} "$(get_app_defaults_path)" + else + exit_script + fi + ;; + 5) exit_script ;; + esac + done +} + +# ===== Unified storage selection & writing to vars files ===== +_write_storage_to_vars() { + # $1 = vars_file, $2 = key (var_container_storage / var_template_storage), $3 = value + local vf="$1" key="$2" val="$3" + # remove uncommented and commented versions to avoid duplicates + sed -i "/^[#[:space:]]*${key}=/d" "$vf" + echo "${key}=${val}" >>"$vf" +} + +choose_and_set_storage_for_file() { + # $1 = vars_file, $2 = class ('container'|'template') + local vf="$1" class="$2" key="" current="" + case "$class" in + container) key="var_container_storage" ;; + template) key="var_template_storage" ;; + *) + msg_error "Unknown storage class: $class" + return 1 + ;; + esac + + current=$(awk -F= -v k="^${key}=" '$0 ~ k {print $2; exit}' "$vf") + + # If only one storage exists for the content type, auto-pick. Else always ask (your wish #4). + local content="rootdir" + [[ "$class" == "template" ]] && content="vztmpl" + local count + count=$(pvesm status -content "$content" | awk 'NR>1{print $1}' | wc -l) + + if [[ "$count" -eq 1 ]]; then + STORAGE_RESULT=$(pvesm status -content "$content" | awk 'NR>1{print $1; exit}') + STORAGE_INFO="" + else + # If the current value is preselectable, we could show it, but per your requirement we always offer selection + select_storage "$class" || return 1 + fi + + _write_storage_to_vars "$vf" "$key" "$STORAGE_RESULT" + + # Keep environment in sync for later steps (e.g. app-default save) + if [[ "$class" == "container" ]]; then + export var_container_storage="$STORAGE_RESULT" + export CONTAINER_STORAGE="$STORAGE_RESULT" + else + export var_template_storage="$STORAGE_RESULT" + export TEMPLATE_STORAGE="$STORAGE_RESULT" + fi + + msg_ok "Updated ${key} → ${STORAGE_RESULT}" +} + +echo_storage_summary_from_file() { + local vars_file="$1" + local ct_store tpl_store + ct_store=$(awk -F= '/^var_container_storage=/ {print $2; exit}' "$vars_file") + tpl_store=$(awk -F= '/^var_template_storage=/ {print $2; exit}' "$vars_file") + if [ -n "$tpl" ]; then + TEMPLATE_STORAGE="$tpl" + else + choose_and_set_storage_for_file "$vf" template + fi + + if [ -n "$ct" ]; then + CONTAINER_STORAGE="$ct" + else + choose_and_set_storage_for_file "$vf" container + fi +} + +# ------------------------------------------------------------------------------ +# check_container_resources() +# +# - Compares host RAM/CPU with required values +# - Warns if under-provisioned and asks user to continue or abort +# ------------------------------------------------------------------------------ +check_container_resources() { + current_ram=$(free -m | awk 'NR==2{print $2}') + current_cpu=$(nproc) + + if [[ "$current_ram" -lt "$var_ram" ]] || [[ "$current_cpu" -lt "$var_cpu" ]]; then + echo -e "\n${INFO}${HOLD} ${GN}Required: ${var_cpu} CPU, ${var_ram}MB RAM ${CL}| ${RD}Current: ${current_cpu} CPU, ${current_ram}MB RAM${CL}" + echo -e "${YWB}Please ensure that the ${APP} LXC is configured with at least ${var_cpu} vCPU and ${var_ram} MB RAM for the build process.${CL}\n" + echo -ne "${INFO}${HOLD} May cause data loss! ${INFO} Continue update with under-provisioned LXC? " + read -r prompt + if [[ ! ${prompt,,} =~ ^(yes)$ ]]; then + echo -e "${CROSS}${HOLD} ${YWB}Exiting based on user input.${CL}" + exit 1 + fi + else + echo -e "" + fi +} + +# ------------------------------------------------------------------------------ +# check_container_storage() +# +# - Checks /boot partition usage +# - Warns if usage >80% and asks user confirmation before proceeding +# ------------------------------------------------------------------------------ +check_container_storage() { + total_size=$(df /boot --output=size | tail -n 1) + local used_size=$(df /boot --output=used | tail -n 1) + usage=$((100 * used_size / total_size)) + if ((usage > 80)); then + echo -e "${INFO}${HOLD} ${YWB}Warning: Storage is dangerously low (${usage}%).${CL}" + echo -ne "Continue anyway? " + read -r prompt + if [[ ! ${prompt,,} =~ ^(y|yes)$ ]]; then + echo -e "${CROSS}${HOLD}${YWB}Exiting based on user input.${CL}" + exit 1 + fi + fi +} + +# ------------------------------------------------------------------------------ +# ssh_extract_keys_from_file() +# +# - Extracts valid SSH public keys from given file +# - Supports RSA, Ed25519, ECDSA and filters out comments/invalid lines +# ------------------------------------------------------------------------------ +ssh_extract_keys_from_file() { + local f="$1" + [[ -r "$f" ]] || return 0 + tr -d '\r' <"$f" | awk ' + /^[[:space:]]*#/ {next} + /^[[:space:]]*$/ {next} + # nackt: typ base64 [comment] + /^(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/ {print; next} + # mit Optionen: finde ab erstem Key-Typ + { + match($0, /(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/) + if (RSTART>0) { print substr($0, RSTART) } + } + ' +} + +# ------------------------------------------------------------------------------ +# ssh_build_choices_from_files() +# +# - Builds interactive whiptail checklist of available SSH keys +# - Generates fingerprint, type and comment for each key +# ------------------------------------------------------------------------------ +ssh_build_choices_from_files() { + local -a files=("$@") + CHOICES=() + COUNT=0 + MAPFILE="$(mktemp)" + local id key typ fp cmt base ln=0 + + for f in "${files[@]}"; do + [[ -f "$f" && -r "$f" ]] || continue + base="$(basename -- "$f")" + case "$base" in + known_hosts | known_hosts.* | config) continue ;; + id_*) [[ "$f" != *.pub ]] && continue ;; + esac + + # map every key in file + while IFS= read -r key; do + [[ -n "$key" ]] || continue + + typ="" + fp="" + cmt="" + # Only the pure key part (without options) is already included in ‘key’. + read -r _typ _b64 _cmt <<<"$key" + typ="${_typ:-key}" + cmt="${_cmt:-}" + # Fingerprint via ssh-keygen (if available) + if command -v ssh-keygen >/dev/null 2>&1; then + fp="$(printf '%s\n' "$key" | ssh-keygen -lf - 2>/dev/null | awk '{print $2}')" + fi + # Label shorten + [[ ${#cmt} -gt 40 ]] && cmt="${cmt:0:37}..." + + ln=$((ln + 1)) + COUNT=$((COUNT + 1)) + id="K${COUNT}" + echo "${id}|${key}" >>"$MAPFILE" + CHOICES+=("$id" "[$typ] ${fp:+$fp }${cmt:+$cmt }— ${base}" "OFF") + done < <(ssh_extract_keys_from_file "$f") + done +} + +# ------------------------------------------------------------------------------ +# ssh_discover_default_files() +# +# - Scans standard paths for SSH keys +# - Includes ~/.ssh/*.pub, /etc/ssh/authorized_keys, etc. +# ------------------------------------------------------------------------------ +ssh_discover_default_files() { + local -a cand=() + shopt -s nullglob + cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2) + cand+=(/root/.ssh/*.pub) + cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*) + shopt -u nullglob + printf '%s\0' "${cand[@]}" +} + +# ------------------------------------------------------------------------------ +# start() +# +# - Entry point of script +# - On Proxmox host: calls install_script +# - In silent mode: runs update_script +# - Otherwise: shows update/setting menu +# ------------------------------------------------------------------------------ +start() { + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) + if command -v pveversion >/dev/null 2>&1; then + install_script || return 0 + return 0 + elif [ ! -z ${PHS_SILENT+x} ] && [[ "${PHS_SILENT}" == "1" ]]; then + VERBOSE="no" + set_std_mode + update_script + else + CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "${APP} LXC Update/Setting" --menu \ + "Support/Update functions for ${APP} LXC. Choose an option:" \ + 12 60 3 \ + "1" "YES (Silent Mode)" \ + "2" "YES (Verbose Mode)" \ + "3" "NO (Cancel Update)" --nocancel --default-item "1" 3>&1 1>&2 2>&3) + + case "$CHOICE" in + 1) + VERBOSE="no" + set_std_mode + ;; + 2) + VERBOSE="yes" + set_std_mode + ;; + 3) + clear + exit_script + exit + ;; + esac + update_script + fi +} + +# ------------------------------------------------------------------------------ +# build_container() +# +# - Creates and configures the LXC container +# - Builds network string and applies features (FUSE, TUN, VAAPI passthrough) +# - Starts container and waits for network connectivity +# - Installs base packages, SSH keys, and runs -install.sh +# ------------------------------------------------------------------------------ +build_container() { + # if [ "$VERBOSE" == "yes" ]; then set -x; fi + + NET_STRING="-net0 name=eth0,bridge=${BRG:-vmbr0}" + + # MAC + if [[ -n "$MAC" ]]; then + case "$MAC" in + ,hwaddr=*) NET_STRING+="$MAC" ;; + *) NET_STRING+=",hwaddr=$MAC" ;; + esac + fi + + # IP (immer zwingend, Standard dhcp) + NET_STRING+=",ip=${NET:-dhcp}" + + # Gateway + if [[ -n "$GATE" ]]; then + case "$GATE" in + ,gw=*) NET_STRING+="$GATE" ;; + *) NET_STRING+=",gw=$GATE" ;; + esac + fi + + # VLAN + if [[ -n "$VLAN" ]]; then + case "$VLAN" in + ,tag=*) NET_STRING+="$VLAN" ;; + *) NET_STRING+=",tag=$VLAN" ;; + esac + fi + + # MTU + if [[ -n "$MTU" ]]; then + case "$MTU" in + ,mtu=*) NET_STRING+="$MTU" ;; + *) NET_STRING+=",mtu=$MTU" ;; + esac + fi + + # IPv6 Handling + case "$IPV6_METHOD" in + auto) NET_STRING="$NET_STRING,ip6=auto" ;; + dhcp) NET_STRING="$NET_STRING,ip6=dhcp" ;; + static) + NET_STRING="$NET_STRING,ip6=$IPV6_ADDR" + [ -n "$IPV6_GATE" ] && NET_STRING="$NET_STRING,gw6=$IPV6_GATE" + ;; + none) ;; + esac + + if [ "$CT_TYPE" == "1" ]; then + FEATURES="keyctl=1,nesting=1" + else + FEATURES="nesting=1" + fi + + if [ "$ENABLE_FUSE" == "yes" ]; then + FEATURES="$FEATURES,fuse=1" + fi + + TEMP_DIR=$(mktemp -d) + pushd "$TEMP_DIR" >/dev/null + if [ "$var_os" == "alpine" ]; then + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-install.func)" + else + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" + fi + export DIAGNOSTICS="$DIAGNOSTICS" + export RANDOM_UUID="$RANDOM_UUID" + export CACHER="$APT_CACHER" + export CACHER_IP="$APT_CACHER_IP" + export tz="$timezone" + export APPLICATION="$APP" + export app="$NSAPP" + export PASSWORD="$PW" + export VERBOSE="$VERBOSE" + export SSH_ROOT="${SSH}" + export SSH_AUTHORIZED_KEY + export CTID="$CT_ID" + export CTTYPE="$CT_TYPE" + export ENABLE_FUSE="$ENABLE_FUSE" + export ENABLE_TUN="$ENABLE_TUN" + export PCT_OSTYPE="$var_os" + export PCT_OSVERSION="${var_version%%.*}" + export PCT_DISK_SIZE="$DISK_SIZE" + export PCT_OPTIONS=" + -features $FEATURES + -hostname $HN + -tags $TAGS + $SD + $NS + $NET_STRING + -onboot 1 + -cores $CORE_COUNT + -memory $RAM_SIZE + -unprivileged $CT_TYPE + $PW +" + export TEMPLATE_STORAGE="${var_template_storage:-}" + export CONTAINER_STORAGE="${var_container_storage:-}" + create_lxc_container || exit $? + + LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" + + # ============================================================================ + # GPU/USB PASSTHROUGH CONFIGURATION + # ============================================================================ + + # List of applications that benefit from GPU acceleration + GPU_APPS=( + "immich" "channels" "emby" "ersatztv" "frigate" + "jellyfin" "plex" "scrypted" "tdarr" "unmanic" + "ollama" "fileflows" "open-webui" "tunarr" "debian" + "handbrake" "sunshine" "moonlight" "kodi" "stremio" + "viseron" + ) + + # Check if app needs GPU + is_gpu_app() { + local app="${1,,}" + for gpu_app in "${GPU_APPS[@]}"; do + [[ "$app" == "${gpu_app,,}" ]] && return 0 + done + return 1 + } + + # Detect all available GPU devices + detect_gpu_devices() { + INTEL_DEVICES=() + AMD_DEVICES=() + NVIDIA_DEVICES=() + + # Store PCI info to avoid multiple calls + local pci_vga_info=$(lspci -nn 2>/dev/null | grep -E "VGA|Display|3D") + + # Check for Intel GPU - look for Intel vendor ID [8086] + if echo "$pci_vga_info" | grep -q "\[8086:"; then + msg_info "Detected Intel GPU" + if [[ -d /dev/dri ]]; then + for d in /dev/dri/renderD* /dev/dri/card*; do + [[ -e "$d" ]] && INTEL_DEVICES+=("$d") + done + fi + fi + + # Check for AMD GPU - look for AMD vendor IDs [1002] (AMD/ATI) or [1022] (AMD) + if echo "$pci_vga_info" | grep -qE "\[1002:|\[1022:"; then + msg_info "Detected AMD GPU" + if [[ -d /dev/dri ]]; then + # Only add if not already claimed by Intel + if [[ ${#INTEL_DEVICES[@]} -eq 0 ]]; then + for d in /dev/dri/renderD* /dev/dri/card*; do + [[ -e "$d" ]] && AMD_DEVICES+=("$d") + done + fi + fi + fi + + # Check for NVIDIA GPU - look for NVIDIA vendor ID [10de] + if echo "$pci_vga_info" | grep -q "\[10de:"; then + msg_info "Detected NVIDIA GPU" + if ! check_nvidia_host_setup; then + msg_error "NVIDIA host setup incomplete. Skipping GPU passthrough." + msg_info "Fix NVIDIA drivers on host, then recreate container or passthrough manually." + return 0 + fi + + for d in /dev/nvidia* /dev/nvidiactl /dev/nvidia-modeset; do + [[ -e "$d" ]] && NVIDIA_DEVICES+=("$d") + done + + if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then + msg_warn "NVIDIA GPU detected but no /dev/nvidia* devices found" + msg_warn "Please install NVIDIA drivers on host: apt install nvidia-driver" + else + if [[ "$CT_TYPE" == "0" ]]; then + cat <>"$LXC_CONFIG" + # NVIDIA GPU Passthrough (privileged) + lxc.cgroup2.devices.allow: c 195:* rwm + lxc.cgroup2.devices.allow: c 243:* rwm + lxc.mount.entry: /dev/nvidia0 dev/nvidia0 none bind,optional,create=file + lxc.mount.entry: /dev/nvidiactl dev/nvidiactl none bind,optional,create=file + lxc.mount.entry: /dev/nvidia-uvm dev/nvidia-uvm none bind,optional,create=file + lxc.mount.entry: /dev/nvidia-uvm-tools dev/nvidia-uvm-tools none bind,optional,create=file +EOF + + if [[ -e /dev/dri/renderD128 ]]; then + echo "lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file" >>"$LXC_CONFIG" + fi + + export GPU_TYPE="NVIDIA" + export NVIDIA_DRIVER_VERSION=$(nvidia-smi --query-gpu=driver_version --format=csv,noheader 2>/dev/null | head -n1) + msg_ok "NVIDIA GPU passthrough configured (driver: ${NVIDIA_DRIVER_VERSION})" + else + msg_warn "NVIDIA passthrough only supported for privileged containers" + return 0 + fi + fi + fi + + # Debug output + msg_debug "Intel devices: ${INTEL_DEVICES[*]}" + msg_debug "AMD devices: ${AMD_DEVICES[*]}" + msg_debug "NVIDIA devices: ${NVIDIA_DEVICES[*]}" + } + + + # Configure USB passthrough for privileged containers + configure_usb_passthrough() { + if [[ "$CT_TYPE" != "0" ]]; then + return 0 + fi + + msg_info "Configuring automatic USB passthrough (privileged container)" + cat <>"$LXC_CONFIG" +# Automatic USB passthrough (privileged container) +lxc.cgroup2.devices.allow: a +lxc.cap.drop: +lxc.cgroup2.devices.allow: c 188:* rwm +lxc.cgroup2.devices.allow: c 189:* rwm +lxc.mount.entry: /dev/serial/by-id dev/serial/by-id none bind,optional,create=dir +lxc.mount.entry: /dev/ttyUSB0 dev/ttyUSB0 none bind,optional,create=file +lxc.mount.entry: /dev/ttyUSB1 dev/ttyUSB1 none bind,optional,create=file +lxc.mount.entry: /dev/ttyACM0 dev/ttyACM0 none bind,optional,create=file +lxc.mount.entry: /dev/ttyACM1 dev/ttyACM1 none bind,optional,create=file +EOF + msg_ok "USB passthrough configured" + } + + # Configure GPU passthrough +configure_gpu_passthrough() { + # Skip if not a GPU app and not privileged + if [[ "$CT_TYPE" != "0" ]] && ! is_gpu_app "$APP"; then + return 0 + fi + + detect_gpu_devices + + # Count available GPU types + local gpu_count=0 + local available_gpus=() + + if [[ ${#INTEL_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("INTEL") + gpu_count=$((gpu_count + 1)) + fi + + if [[ ${#AMD_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("AMD") + gpu_count=$((gpu_count + 1)) + fi + + if [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("NVIDIA") + gpu_count=$((gpu_count + 1)) + fi + + if [[ $gpu_count -eq 0 ]]; then + msg_info "No GPU devices found for passthrough" + return 0 + fi + + local selected_gpu="" + + if [[ $gpu_count -eq 1 ]]; then + # Automatic selection for single GPU + selected_gpu="${available_gpus[0]}" + msg_info "Automatically configuring ${selected_gpu} GPU passthrough" + else + # Multiple GPUs - ask user + echo -e "\n${INFO} Multiple GPU types detected:" + for gpu in "${available_gpus[@]}"; do + echo " - $gpu" + done + read -rp "Which GPU type to passthrough? (${available_gpus[*]}): " selected_gpu + selected_gpu="${selected_gpu^^}" + + # Validate selection + local valid=0 + for gpu in "${available_gpus[@]}"; do + [[ "$selected_gpu" == "$gpu" ]] && valid=1 + done + + if [[ $valid -eq 0 ]]; then + msg_warn "Invalid selection. Skipping GPU passthrough." + return 0 + fi + fi + + # Apply passthrough configuration based on selection + local dev_idx=0 + + case "$selected_gpu" in + INTEL|AMD) + local devices=() + [[ "$selected_gpu" == "INTEL" ]] && devices=("${INTEL_DEVICES[@]}") + [[ "$selected_gpu" == "AMD" ]] && devices=("${AMD_DEVICES[@]}") + + # For Proxmox WebUI visibility, add as dev0, dev1 etc. + for dev in "${devices[@]}"; do + if [[ "$CT_TYPE" == "0" ]]; then + # Privileged container - use dev entries for WebUI visibility + # Use initial GID 104 (render) for renderD*, 44 (video) for card* + if [[ "$dev" =~ renderD ]]; then + echo "dev${dev_idx}: $dev,gid=104" >>"$LXC_CONFIG" + else + echo "dev${dev_idx}: $dev,gid=44" >>"$LXC_CONFIG" + fi + dev_idx=$((dev_idx + 1)) + + # Also add cgroup allows for privileged containers + local major minor + major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") + minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") + + if [[ "$major" != "0" && "$minor" != "0" ]]; then + echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" + fi + else + # Unprivileged container + if [[ "$dev" =~ renderD ]]; then + echo "dev${dev_idx}: $dev,uid=0,gid=104" >>"$LXC_CONFIG" + else + echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG" + fi + dev_idx=$((dev_idx + 1)) + fi + done + + export GPU_TYPE="$selected_gpu" + msg_ok "${selected_gpu} GPU passthrough configured (${dev_idx} devices)" + ;; + + NVIDIA) + if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then + msg_error "NVIDIA drivers not installed on host. Please install: apt install nvidia-driver" + return 1 + fi + + for dev in "${NVIDIA_DEVICES[@]}"; do + # NVIDIA devices typically need different handling + echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG" + dev_idx=$((dev_idx + 1)) + + if [[ "$CT_TYPE" == "0" ]]; then + local major minor + major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") + minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") + + if [[ "$major" != "0" && "$minor" != "0" ]]; then + echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" + fi + fi + done + + export GPU_TYPE="NVIDIA" + msg_ok "NVIDIA GPU passthrough configured (${dev_idx} devices)" + ;; + esac +} + + # Additional device passthrough + configure_additional_devices() { + # TUN device passthrough + if [ "$ENABLE_TUN" == "yes" ]; then + cat <>"$LXC_CONFIG" +lxc.cgroup2.devices.allow: c 10:200 rwm +lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file +EOF + fi + + # Coral TPU passthrough + if [[ -e /dev/apex_0 ]]; then + msg_info "Detected Coral TPU - configuring passthrough" + echo "lxc.mount.entry: /dev/apex_0 dev/apex_0 none bind,optional,create=file" >>"$LXC_CONFIG" + fi + } + + # Execute pre-start configurations + configure_usb_passthrough + configure_gpu_passthrough + configure_additional_devices + + # ============================================================================ + # START CONTAINER AND INSTALL USERLAND + # ============================================================================ + + msg_info "Starting LXC Container" + pct start "$CTID" + + # Wait for container to be running + for i in {1..10}; do + if pct status "$CTID" | grep -q "status: running"; then + msg_ok "Started LXC Container" + break + fi + sleep 1 + if [ "$i" -eq 10 ]; then + msg_error "LXC Container did not reach running state" + exit 1 + fi + done + + # Wait for network (skip for Alpine initially) + if [ "$var_os" != "alpine" ]; then + msg_info "Waiting for network in LXC container" + + # Wait for IP + for i in {1..20}; do + ip_in_lxc=$(pct exec "$CTID" -- ip -4 addr show dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) + [ -n "$ip_in_lxc" ] && break + sleep 1 + done + + if [ -z "$ip_in_lxc" ]; then + msg_error "No IP assigned to CT $CTID after 20s" + exit 1 + fi + + # Try to reach gateway + gw_ok=0 + for i in {1..10}; do + if pct exec "$CTID" -- ping -c1 -W1 "${GATEWAY:-8.8.8.8}" >/dev/null 2>&1; then + gw_ok=1 + break + fi + sleep 1 + done + + if [ "$gw_ok" -eq 1 ]; then + msg_ok "Network in LXC is reachable (IP $ip_in_lxc)" + else + msg_warn "Network reachable but gateway check failed" + fi + fi + # Function to get correct GID inside container + get_container_gid() { + local group="$1" + local gid=$(pct exec "$CTID" -- getent group "$group" 2>/dev/null | cut -d: -f3) + echo "${gid:-44}" # Default to 44 if not found + } + + fix_gpu_gids + +# Configure GPU passthrough +configure_gpu_passthrough() { + # Skip if not a GPU app and not privileged + if [[ "$CT_TYPE" != "0" ]] && ! is_gpu_app "$APP"; then + return 0 + fi + + detect_gpu_devices + + # Count available GPU types + local gpu_count=0 + local available_gpus=() + + if [[ ${#INTEL_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("INTEL") + gpu_count=$((gpu_count + 1)) + fi + + if [[ ${#AMD_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("AMD") + gpu_count=$((gpu_count + 1)) + fi + + if [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("NVIDIA") + gpu_count=$((gpu_count + 1)) + fi + + if [[ $gpu_count -eq 0 ]]; then + msg_info "No GPU devices found for passthrough" + return 0 + fi + + local selected_gpu="" + + if [[ $gpu_count -eq 1 ]]; then + # Automatic selection for single GPU + selected_gpu="${available_gpus[0]}" + msg_info "Automatically configuring ${selected_gpu} GPU passthrough" + else + # Multiple GPUs - ask user + echo -e "\n${INFO} Multiple GPU types detected:" + for gpu in "${available_gpus[@]}"; do + echo " - $gpu" + done + read -rp "Which GPU type to passthrough? (${available_gpus[*]}): " selected_gpu + selected_gpu="${selected_gpu^^}" + + # Validate selection + local valid=0 + for gpu in "${available_gpus[@]}"; do + [[ "$selected_gpu" == "$gpu" ]] && valid=1 + done + + if [[ $valid -eq 0 ]]; then + msg_warn "Invalid selection. Skipping GPU passthrough." + return 0 + fi + fi + + # Apply passthrough configuration based on selection + local dev_idx=0 + + case "$selected_gpu" in + INTEL|AMD) + local devices=() + [[ "$selected_gpu" == "INTEL" ]] && devices=("${INTEL_DEVICES[@]}") + [[ "$selected_gpu" == "AMD" ]] && devices=("${AMD_DEVICES[@]}") + + # For Proxmox WebUI visibility, add as dev0, dev1 etc. + for dev in "${devices[@]}"; do + if [[ "$CT_TYPE" == "0" ]]; then + # Privileged container - use dev entries for WebUI visibility + # Use initial GID 104 (render) for renderD*, 44 (video) for card* + if [[ "$dev" =~ renderD ]]; then + echo "dev${dev_idx}: $dev,gid=104" >>"$LXC_CONFIG" + else + echo "dev${dev_idx}: $dev,gid=44" >>"$LXC_CONFIG" + fi + dev_idx=$((dev_idx + 1)) + + # Also add cgroup allows for privileged containers + local major minor + major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") + minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") + + if [[ "$major" != "0" && "$minor" != "0" ]]; then + echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" + fi + else + # Unprivileged container + if [[ "$dev" =~ renderD ]]; then + echo "dev${dev_idx}: $dev,uid=0,gid=104" >>"$LXC_CONFIG" + else + echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG" + fi + dev_idx=$((dev_idx + 1)) + fi + done + + export GPU_TYPE="$selected_gpu" + msg_ok "${selected_gpu} GPU passthrough configured (${dev_idx} devices)" + ;; + + NVIDIA) + if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then + msg_error "NVIDIA drivers not installed on host. Please install: apt install nvidia-driver" + return 1 + fi + + for dev in "${NVIDIA_DEVICES[@]}"; do + # NVIDIA devices typically need different handling + echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG" + dev_idx=$((dev_idx + 1)) + + if [[ "$CT_TYPE" == "0" ]]; then + local major minor + major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") + minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") + + if [[ "$major" != "0" && "$minor" != "0" ]]; then + echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" + fi + fi + done + + export GPU_TYPE="NVIDIA" + msg_ok "NVIDIA GPU passthrough configured (${dev_idx} devices)" + ;; + esac +} + + # Continue with standard container setup + msg_info "Customizing LXC Container" + + # # Install GPU userland if configured + # if [[ "${ENABLE_VAAPI:-0}" == "1" ]]; then + # install_gpu_userland "VAAPI" + # fi + + # if [[ "${ENABLE_NVIDIA:-0}" == "1" ]]; then + # install_gpu_userland "NVIDIA" + # fi + + # Continue with standard container setup + if [ "$var_os" == "alpine" ]; then + sleep 3 + pct exec "$CTID" -- /bin/sh -c 'cat </etc/apk/repositories +http://dl-cdn.alpinelinux.org/alpine/latest-stable/main +http://dl-cdn.alpinelinux.org/alpine/latest-stable/community +EOF' + pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses jq >/dev/null" + else + sleep 3 + pct exec "$CTID" -- bash -c "sed -i '/$LANG/ s/^# //' /etc/locale.gen" + pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \ + echo LANG=\$locale_line >/etc/default/locale && \ + locale-gen >/dev/null && \ + export LANG=\$locale_line" + + if [[ -z "${tz:-}" ]]; then + tz=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "Etc/UTC") + fi + + if pct exec "$CTID" -- test -e "/usr/share/zoneinfo/$tz"; then + pct exec "$CTID" -- bash -c "tz='$tz'; echo \"\$tz\" >/etc/timezone && ln -sf \"/usr/share/zoneinfo/\$tz\" /etc/localtime" + else + msg_warn "Skipping timezone setup – zone '$tz' not found in container" + fi + + pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y sudo curl mc gnupg2 jq >/dev/null" || { + msg_error "apt-get base packages installation failed" + exit 1 + } + fi + + msg_ok "Customized LXC Container" + + # Verify GPU access if enabled + if [[ "${ENABLE_VAAPI:-0}" == "1" ]] && [ "$var_os" != "alpine" ]; then + pct exec "$CTID" -- bash -c "vainfo >/dev/null 2>&1" && + msg_ok "VAAPI verified working" || + msg_warn "VAAPI verification failed - may need additional configuration" + fi + + if [[ "${ENABLE_NVIDIA:-0}" == "1" ]] && [ "$var_os" != "alpine" ]; then + pct exec "$CTID" -- bash -c "nvidia-smi >/dev/null 2>&1" && + msg_ok "NVIDIA verified working" || + msg_warn "NVIDIA verification failed - may need additional configuration" + fi + + # Install SSH keys + install_ssh_keys_into_ct + + # Run application installer + if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)"; then + exit $? + fi +} + +destroy_lxc() { + if [[ -z "$CT_ID" ]]; then + msg_error "No CT_ID found. Nothing to remove." + return 1 + fi + + # Abbruch bei Ctrl-C / Ctrl-D / ESC + trap 'echo; msg_error "Aborted by user (SIGINT/SIGQUIT)"; return 130' INT QUIT + + local prompt + if ! read -rp "Remove this Container? " prompt; then + # read gibt != 0 zurück bei Ctrl-D/ESC + msg_error "Aborted input (Ctrl-D/ESC)" + return 130 + fi + + case "${prompt,,}" in + y | yes) + if pct stop "$CT_ID" &>/dev/null && pct destroy "$CT_ID" &>/dev/null; then + msg_ok "Removed Container $CT_ID" + else + msg_error "Failed to remove Container $CT_ID" + return 1 + fi + ;; + "" | n | no) + msg_info "Container was not removed." + ;; + *) + msg_warn "Invalid response. Container was not removed." + ;; + esac +} + +# ------------------------------------------------------------------------------ +# Storage discovery / selection helpers +# ------------------------------------------------------------------------------ +# ===== Storage discovery / selection helpers (ported from create_lxc.sh) ===== +resolve_storage_preselect() { + local class="$1" preselect="$2" required_content="" + case "$class" in + template) required_content="vztmpl" ;; + container) required_content="rootdir" ;; + *) return 1 ;; + esac + [[ -z "$preselect" ]] && return 1 + if ! pvesm status -content "$required_content" | awk 'NR>1{print $1}' | grep -qx -- "$preselect"; then + msg_warn "Preselected storage '${preselect}' does not support content '${required_content}' (or not found)" + return 1 + fi + + local line total used free + line="$(pvesm status | awk -v s="$preselect" 'NR>1 && $1==s {print $0}')" + if [[ -z "$line" ]]; then + STORAGE_INFO="n/a" + else + total="$(awk '{print $4}' <<<"$line")" + used="$(awk '{print $5}' <<<"$line")" + free="$(awk '{print $6}' <<<"$line")" + local total_h used_h free_h + if command -v numfmt >/dev/null 2>&1; then + total_h="$(numfmt --to=iec --suffix=B --format %.1f "$total" 2>/dev/null || echo "$total")" + used_h="$(numfmt --to=iec --suffix=B --format %.1f "$used" 2>/dev/null || echo "$used")" + free_h="$(numfmt --to=iec --suffix=B --format %.1f "$free" 2>/dev/null || echo "$free")" + STORAGE_INFO="Free: ${free_h} Used: ${used_h}" + else + STORAGE_INFO="Free: ${free} Used: ${used}" + fi + fi + STORAGE_RESULT="$preselect" + return 0 +} + +fix_gpu_gids() { + if [[ -z "${GPU_TYPE:-}" ]]; then + return 0 + fi + + msg_info "Detecting and setting correct GPU group IDs" + + # Ermittle die tatsächlichen GIDs aus dem Container + local video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") + local render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") + + # Fallbacks wenn Gruppen nicht existieren + if [[ -z "$video_gid" ]]; then + # Versuche die video Gruppe zu erstellen + pct exec "$CTID" -- sh -c "groupadd -r video 2>/dev/null || true" + video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") + [[ -z "$video_gid" ]] && video_gid="44" # Ultimate fallback + fi + + if [[ -z "$render_gid" ]]; then + # Versuche die render Gruppe zu erstellen + pct exec "$CTID" -- sh -c "groupadd -r render 2>/dev/null || true" + render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") + [[ -z "$render_gid" ]] && render_gid="104" # Ultimate fallback + fi + + msg_info "Container GIDs detected - video:${video_gid}, render:${render_gid}" + + # Prüfe ob die GIDs von den Defaults abweichen + local need_update=0 + if [[ "$video_gid" != "44" ]] || [[ "$render_gid" != "104" ]]; then + need_update=1 + fi + + if [[ $need_update -eq 1 ]]; then + msg_info "Updating device GIDs in container config" + + # Stoppe Container für Config-Update + pct stop "$CTID" >/dev/null 2>&1 + + # Update die dev Einträge mit korrekten GIDs + # Backup der Config + cp "$LXC_CONFIG" "${LXC_CONFIG}.bak" + + # Parse und update jeden dev Eintrag + while IFS= read -r line; do + if [[ "$line" =~ ^dev[0-9]+: ]]; then + # Extract device path + local device_path=$(echo "$line" | sed -E 's/^dev[0-9]+: ([^,]+).*/\1/') + local dev_num=$(echo "$line" | sed -E 's/^(dev[0-9]+):.*/\1/') + + if [[ "$device_path" =~ renderD ]]; then + # RenderD device - use render GID + echo "${dev_num}: ${device_path},gid=${render_gid}" + elif [[ "$device_path" =~ card ]]; then + # Card device - use video GID + echo "${dev_num}: ${device_path},gid=${video_gid}" + else + # Keep original line + echo "$line" + fi + else + # Keep non-dev lines + echo "$line" + fi + done < "$LXC_CONFIG" > "${LXC_CONFIG}.new" + + mv "${LXC_CONFIG}.new" "$LXC_CONFIG" + + # Starte Container wieder + pct start "$CTID" >/dev/null 2>&1 + sleep 3 + + msg_ok "Device GIDs updated successfully" + else + msg_ok "Device GIDs are already correct" + fi + if [[ "$CT_TYPE" == "0" ]]; then + pct exec "$CTID" -- bash -c " + if [ -d /dev/dri ]; then + for dev in /dev/dri/*; do + if [ -e \"\$dev\" ]; then + if [[ \"\$dev\" =~ renderD ]]; then + chgrp ${render_gid} \"\$dev\" 2>/dev/null || true + else + chgrp ${video_gid} \"\$dev\" 2>/dev/null || true + fi + chmod 660 \"\$dev\" 2>/dev/null || true + fi + done + fi + " >/dev/null 2>&1 + fi +} + +# NVIDIA-spezific check on host +check_nvidia_host_setup() { + if ! command -v nvidia-smi >/dev/null 2>&1; then + msg_warn "NVIDIA GPU detected but nvidia-smi not found on host" + msg_warn "Please install NVIDIA drivers on host first." + #echo " 1. Download driver: wget https://us.download.nvidia.com/XFree86/Linux-x86_64/550.127.05/NVIDIA-Linux-x86_64-550.127.05.run" + #echo " 2. Install: ./NVIDIA-Linux-x86_64-550.127.05.run --dkms" + #echo " 3. Verify: nvidia-smi" + return 1 + fi + + # check if nvidia-smi works + if ! nvidia-smi >/dev/null 2>&1; then + msg_warn "nvidia-smi installed but not working. Driver issue?" + return 1 + fi + + return 0 +} + +check_storage_support() { + local CONTENT="$1" VALID=0 + while IFS= read -r line; do + local STORAGE_NAME + STORAGE_NAME=$(awk '{print $1}' <<<"$line") + [[ -n "$STORAGE_NAME" ]] && VALID=1 + done < <(pvesm status -content "$CONTENT" 2>/dev/null | awk 'NR>1') + [[ $VALID -eq 1 ]] +} + +select_storage() { + local CLASS=$1 CONTENT CONTENT_LABEL + case $CLASS in + container) + CONTENT='rootdir' + CONTENT_LABEL='Container' + ;; + template) + CONTENT='vztmpl' + CONTENT_LABEL='Container template' + ;; + iso) + CONTENT='iso' + CONTENT_LABEL='ISO image' + ;; + images) + CONTENT='images' + CONTENT_LABEL='VM Disk image' + ;; + backup) + CONTENT='backup' + CONTENT_LABEL='Backup' + ;; + snippets) + CONTENT='snippets' + CONTENT_LABEL='Snippets' + ;; + *) + msg_error "Invalid storage class '$CLASS'" + return 1 + ;; + esac + + declare -A STORAGE_MAP + local -a MENU=() + local COL_WIDTH=0 + + while read -r TAG TYPE _ TOTAL USED FREE _; do + [[ -n "$TAG" && -n "$TYPE" ]] || continue + local DISPLAY="${TAG} (${TYPE})" + local USED_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$USED") + local FREE_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$FREE") + local INFO="Free: ${FREE_FMT}B Used: ${USED_FMT}B" + STORAGE_MAP["$DISPLAY"]="$TAG" + MENU+=("$DISPLAY" "$INFO" "OFF") + ((${#DISPLAY} > COL_WIDTH)) && COL_WIDTH=${#DISPLAY} + done < <(pvesm status -content "$CONTENT" | awk 'NR>1') + + if [[ ${#MENU[@]} -eq 0 ]]; then + msg_error "No storage found for content type '$CONTENT'." + return 2 + fi + + if [[ $((${#MENU[@]} / 3)) -eq 1 ]]; then + STORAGE_RESULT="${STORAGE_MAP[${MENU[0]}]}" + STORAGE_INFO="${MENU[1]}" + return 0 + fi + + local WIDTH=$((COL_WIDTH + 42)) + while true; do + local DISPLAY_SELECTED + DISPLAY_SELECTED=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "Storage Pools" \ + --radiolist "Which storage pool for ${CONTENT_LABEL,,}?\n(Spacebar to select)" \ + 16 "$WIDTH" 6 "${MENU[@]}" 3>&1 1>&2 2>&3) || { exit_script; } + + DISPLAY_SELECTED=$(sed 's/[[:space:]]*$//' <<<"$DISPLAY_SELECTED") + if [[ -z "$DISPLAY_SELECTED" || -z "${STORAGE_MAP[$DISPLAY_SELECTED]+_}" ]]; then + whiptail --msgbox "No valid storage selected. Please try again." 8 58 + continue + fi + STORAGE_RESULT="${STORAGE_MAP[$DISPLAY_SELECTED]}" + for ((i = 0; i < ${#MENU[@]}; i += 3)); do + if [[ "${MENU[$i]}" == "$DISPLAY_SELECTED" ]]; then + STORAGE_INFO="${MENU[$i + 1]}" + break + fi + done + return 0 + done +} + +create_lxc_container() { + # ------------------------------------------------------------------------------ + # Optional verbose mode (debug tracing) + # ------------------------------------------------------------------------------ + if [[ "${CREATE_LXC_VERBOSE:-no}" == "yes" ]]; then set -x; fi + + # ------------------------------------------------------------------------------ + # Helpers (dynamic versioning / template parsing) + # ------------------------------------------------------------------------------ + pkg_ver() { dpkg-query -W -f='${Version}\n' "$1" 2>/dev/null || echo ""; } + pkg_cand() { apt-cache policy "$1" 2>/dev/null | awk '/Candidate:/ {print $2}'; } + + ver_ge() { dpkg --compare-versions "$1" ge "$2"; } + ver_gt() { dpkg --compare-versions "$1" gt "$2"; } + ver_lt() { dpkg --compare-versions "$1" lt "$2"; } + + # Extract Debian OS minor from template name: debian-13-standard_13.1-1_amd64.tar.zst => "13.1" + parse_template_osver() { sed -n 's/.*_\([0-9][0-9]*\(\.[0-9]\+\)\?\)-.*/\1/p' <<<"$1"; } + + # Offer upgrade for pve-container/lxc-pve if candidate > installed; optional auto-retry pct create + # Returns: + # 0 = no upgrade needed + # 1 = upgraded (and if do_retry=yes and retry succeeded, creation done) + # 2 = user declined + # 3 = upgrade attempted but failed OR retry failed + offer_lxc_stack_upgrade_and_maybe_retry() { + local do_retry="${1:-no}" # yes|no + local _pvec_i _pvec_c _lxcp_i _lxcp_c need=0 + + _pvec_i="$(pkg_ver pve-container)" + _lxcp_i="$(pkg_ver lxc-pve)" + _pvec_c="$(pkg_cand pve-container)" + _lxcp_c="$(pkg_cand lxc-pve)" + + if [[ -n "$_pvec_c" && "$_pvec_c" != "none" ]]; then + ver_gt "$_pvec_c" "${_pvec_i:-0}" && need=1 + fi + if [[ -n "$_lxcp_c" && "$_lxcp_c" != "none" ]]; then + ver_gt "$_lxcp_c" "${_lxcp_i:-0}" && need=1 + fi + if [[ $need -eq 0 ]]; then + msg_debug "No newer candidate for pve-container/lxc-pve (installed=$_pvec_i/$_lxcp_i, cand=$_pvec_c/$_lxcp_c)" + return 0 + fi + + echo + echo "An update for the Proxmox LXC stack is available:" + echo " pve-container: installed=${_pvec_i:-n/a} candidate=${_pvec_c:-n/a}" + echo " lxc-pve : installed=${_lxcp_i:-n/a} candidate=${_lxcp_c:-n/a}" + echo + read -rp "Do you want to upgrade now? [y/N] " _ans + case "${_ans,,}" in + y | yes) + msg_info "Upgrading Proxmox LXC stack (pve-container, lxc-pve)" + if apt-get update -qq >/dev/null && apt-get install -y --only-upgrade pve-container lxc-pve >/dev/null; then + msg_ok "LXC stack upgraded." + if [[ "$do_retry" == "yes" ]]; then + msg_info "Retrying container creation after upgrade" + if pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then + msg_ok "Container created successfully after upgrade." + return 1 + else + msg_error "pct create still failed after upgrade. See $LOGFILE" + return 3 + fi + fi + return 1 + else + msg_error "Upgrade failed. Please check APT output." + return 3 + fi + ;; + *) return 2 ;; + esac + } + + # ------------------------------------------------------------------------------ + # Required input variables + # ------------------------------------------------------------------------------ + [[ "${CTID:-}" ]] || { + msg_error "You need to set 'CTID' variable." + exit 203 + } + [[ "${PCT_OSTYPE:-}" ]] || { + msg_error "You need to set 'PCT_OSTYPE' variable." + exit 204 + } + + msg_debug "CTID=$CTID" + msg_debug "PCT_OSTYPE=$PCT_OSTYPE" + msg_debug "PCT_OSVERSION=${PCT_OSVERSION:-default}" + + # ID checks + [[ "$CTID" -ge 100 ]] || { + msg_error "ID cannot be less than 100." + exit 205 + } + if qm status "$CTID" &>/dev/null || pct status "$CTID" &>/dev/null; then + echo -e "ID '$CTID' is already in use." + unset CTID + msg_error "Cannot use ID that is already in use." + exit 206 + fi + + # Storage capability check + check_storage_support "rootdir" || { + msg_error "No valid storage found for 'rootdir' [Container]" + exit 1 + } + check_storage_support "vztmpl" || { + msg_error "No valid storage found for 'vztmpl' [Template]" + exit 1 + } + + # Template storage selection + if resolve_storage_preselect template "${TEMPLATE_STORAGE:-}"; then + TEMPLATE_STORAGE="$STORAGE_RESULT" + TEMPLATE_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${TEMPLATE_STORAGE}${CL} (${TEMPLATE_STORAGE_INFO}) [Template]" + else + while true; do + if [[ -z "${var_template_storage:-}" ]]; then + if select_storage template; then + TEMPLATE_STORAGE="$STORAGE_RESULT" + TEMPLATE_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${TEMPLATE_STORAGE}${CL} (${TEMPLATE_STORAGE_INFO}) [Template]" + break + fi + fi + done + fi + + # Container storage selection + if resolve_storage_preselect container "${CONTAINER_STORAGE:-}"; then + CONTAINER_STORAGE="$STORAGE_RESULT" + CONTAINER_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${CONTAINER_STORAGE}${CL} (${CONTAINER_STORAGE_INFO}) [Container]" + else + if [[ -z "${var_container_storage:-}" ]]; then + if select_storage container; then + CONTAINER_STORAGE="$STORAGE_RESULT" + CONTAINER_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${CONTAINER_STORAGE}${CL} (${CONTAINER_STORAGE_INFO}) [Container]" + fi + fi + fi + + # Validate content types + msg_info "Validating content types of storage '$CONTAINER_STORAGE'" + STORAGE_CONTENT=$(grep -A4 -E "^(zfspool|dir|lvmthin|lvm): $CONTAINER_STORAGE" /etc/pve/storage.cfg | grep content | awk '{$1=""; print $0}' | xargs) + msg_debug "Storage '$CONTAINER_STORAGE' has content types: $STORAGE_CONTENT" + grep -qw "rootdir" <<<"$STORAGE_CONTENT" || { + msg_error "Storage '$CONTAINER_STORAGE' does not support 'rootdir'. Cannot create LXC." + exit 217 + } + $STD msg_ok "Storage '$CONTAINER_STORAGE' supports 'rootdir'" + + msg_info "Validating content types of template storage '$TEMPLATE_STORAGE'" + TEMPLATE_CONTENT=$(grep -A4 -E "^[^:]+: $TEMPLATE_STORAGE" /etc/pve/storage.cfg | grep content | awk '{$1=""; print $0}' | xargs) + msg_debug "Template storage '$TEMPLATE_STORAGE' has content types: $TEMPLATE_CONTENT" + if ! grep -qw "vztmpl" <<<"$TEMPLATE_CONTENT"; then + msg_warn "Template storage '$TEMPLATE_STORAGE' does not declare 'vztmpl'. This may cause pct create to fail." + else + $STD msg_ok "Template storage '$TEMPLATE_STORAGE' supports 'vztmpl'" + fi + + # Free space check + STORAGE_FREE=$(pvesm status | awk -v s="$CONTAINER_STORAGE" '$1 == s { print $6 }') + REQUIRED_KB=$((${PCT_DISK_SIZE:-8} * 1024 * 1024)) + [[ "$STORAGE_FREE" -ge "$REQUIRED_KB" ]] || { + msg_error "Not enough space on '$CONTAINER_STORAGE'. Needed: ${PCT_DISK_SIZE:-8}G." + exit 214 + } + + # Cluster quorum (if cluster) + if [[ -f /etc/pve/corosync.conf ]]; then + msg_info "Checking cluster quorum" + if ! pvecm status | awk -F':' '/^Quorate/ { exit ($2 ~ /Yes/) ? 0 : 1 }'; then + msg_error "Cluster is not quorate. Start all nodes or configure quorum device (QDevice)." + exit 210 + fi + msg_ok "Cluster is quorate" + fi + + # ------------------------------------------------------------------------------ + # Template discovery & validation + # ------------------------------------------------------------------------------ + TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" + case "$PCT_OSTYPE" in + debian | ubuntu) TEMPLATE_PATTERN="-standard_" ;; + alpine | fedora | rocky | centos) TEMPLATE_PATTERN="-default_" ;; + *) TEMPLATE_PATTERN="" ;; + esac + + msg_info "Searching for template '$TEMPLATE_SEARCH'" + + mapfile -t LOCAL_TEMPLATES < <( + pveam list "$TEMPLATE_STORAGE" 2>/dev/null | + awk -v s="$TEMPLATE_SEARCH" -v p="$TEMPLATE_PATTERN" '$1 ~ s && $1 ~ p {print $1}' | + sed 's|.*/||' | sort -t - -k 2 -V + ) + + pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)." + mapfile -t ONLINE_TEMPLATES < <( + pveam available -section system 2>/dev/null | + sed -n "s/.*\($TEMPLATE_SEARCH.*$TEMPLATE_PATTERN.*\)/\1/p" | + sort -t - -k 2 -V + ) + ONLINE_TEMPLATE="" + [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" + + if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then + TEMPLATE="${LOCAL_TEMPLATES[-1]}" + TEMPLATE_SOURCE="local" + else + TEMPLATE="$ONLINE_TEMPLATE" + TEMPLATE_SOURCE="online" + fi + + TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" + if [[ -z "$TEMPLATE_PATH" ]]; then + TEMPLATE_BASE=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) + [[ -n "$TEMPLATE_BASE" ]] && TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE" + fi + [[ -n "$TEMPLATE_PATH" ]] || { + msg_error "Unable to resolve template path for $TEMPLATE_STORAGE. Check storage type and permissions." + exit 220 + } + + msg_ok "Template ${BL}$TEMPLATE${CL} [$TEMPLATE_SOURCE]" + msg_debug "Resolved TEMPLATE_PATH=$TEMPLATE_PATH" + + NEED_DOWNLOAD=0 + if [[ ! -f "$TEMPLATE_PATH" ]]; then + msg_info "Template not present locally – will download." + NEED_DOWNLOAD=1 + elif [[ ! -r "$TEMPLATE_PATH" ]]; then + msg_error "Template file exists but is not readable – check permissions." + exit 221 + elif [[ "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then + if [[ -n "$ONLINE_TEMPLATE" ]]; then + msg_warn "Template file too small (<1MB) – re-downloading." + NEED_DOWNLOAD=1 + else + msg_warn "Template looks too small, but no online version exists. Keeping local file." + fi + elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then + if [[ -n "$ONLINE_TEMPLATE" ]]; then + msg_warn "Template appears corrupted – re-downloading." + NEED_DOWNLOAD=1 + else + msg_warn "Template appears corrupted, but no online version exists. Keeping local file." + fi + else + $STD msg_ok "Template $TEMPLATE is present and valid." + fi + + if [[ "$TEMPLATE_SOURCE" == "local" && -n "$ONLINE_TEMPLATE" && "$TEMPLATE" != "$ONLINE_TEMPLATE" ]]; then + msg_warn "Local template is outdated: $TEMPLATE (latest available: $ONLINE_TEMPLATE)" + if whiptail --yesno "A newer template is available:\n$ONLINE_TEMPLATE\n\nDo you want to download and use it instead?" 12 70; then + TEMPLATE="$ONLINE_TEMPLATE" + NEED_DOWNLOAD=1 + else + msg_info "Continuing with local template $TEMPLATE" + fi + fi + + if [[ "$NEED_DOWNLOAD" -eq 1 ]]; then + [[ -f "$TEMPLATE_PATH" ]] && rm -f "$TEMPLATE_PATH" + for attempt in {1..3}; do + msg_info "Attempt $attempt: Downloading template $TEMPLATE to $TEMPLATE_STORAGE" + if pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null 2>&1; then + msg_ok "Template download successful." + break + fi + if [[ $attempt -eq 3 ]]; then + msg_error "Failed after 3 attempts. Please check network access, permissions, or manually run:\n pveam download $TEMPLATE_STORAGE $TEMPLATE" + exit 222 + fi + sleep $((attempt * 5)) + done + fi + + if ! pveam list "$TEMPLATE_STORAGE" 2>/dev/null | grep -q "$TEMPLATE"; then + msg_error "Template $TEMPLATE not available in storage $TEMPLATE_STORAGE after download." + exit 223 + fi + + # ------------------------------------------------------------------------------ + # Dynamic preflight for Debian 13.x: offer upgrade if available (no hard mins) + # ------------------------------------------------------------------------------ + if [[ "$PCT_OSTYPE" == "debian" ]]; then + OSVER="$(parse_template_osver "$TEMPLATE")" + if [[ -n "$OSVER" ]]; then + # Proactive, aber ohne Abbruch – nur Angebot + offer_lxc_stack_upgrade_and_maybe_retry "no" || true + fi + fi + + # ------------------------------------------------------------------------------ + # Create LXC Container + # ------------------------------------------------------------------------------ + msg_info "Creating LXC container" + + # Ensure subuid/subgid entries exist + grep -q "root:100000:65536" /etc/subuid || echo "root:100000:65536" >>/etc/subuid + grep -q "root:100000:65536" /etc/subgid || echo "root:100000:65536" >>/etc/subgid + + # Assemble pct options + PCT_OPTIONS=(${PCT_OPTIONS[@]:-${DEFAULT_PCT_OPTIONS[@]}}) + [[ " ${PCT_OPTIONS[*]} " =~ " -rootfs " ]] || PCT_OPTIONS+=(-rootfs "$CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}") + + # Lock by template file (avoid concurrent downloads/creates) + lockfile="/tmp/template.${TEMPLATE}.lock" + exec 9>"$lockfile" || { + msg_error "Failed to create lock file '$lockfile'." + exit 200 + } + flock -w 60 9 || { + msg_error "Timeout while waiting for template lock." + exit 211 + } + + LOGFILE="/tmp/pct_create_${CTID}.log" + msg_debug "pct create command: pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" + msg_debug "Logfile: $LOGFILE" + + # First attempt + if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >"$LOGFILE" 2>&1; then + msg_error "Container creation failed on ${TEMPLATE_STORAGE}. Checking template..." + + # Validate template file + if [[ ! -s "$TEMPLATE_PATH" || "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then + msg_warn "Template file too small or missing – re-downloading." + rm -f "$TEMPLATE_PATH" + pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" + elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then + if [[ -n "$ONLINE_TEMPLATE" ]]; then + msg_warn "Template appears corrupted – re-downloading." + rm -f "$TEMPLATE_PATH" + pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" + else + msg_warn "Template appears corrupted, but no online version exists. Skipping re-download." + fi + fi + + # Retry after repair + if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then + # Fallback to local storage + if [[ "$TEMPLATE_STORAGE" != "local" ]]; then + msg_warn "Retrying container creation with fallback to local storage..." + LOCAL_TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" + if [[ ! -f "$LOCAL_TEMPLATE_PATH" ]]; then + msg_info "Downloading template to local..." + pveam download local "$TEMPLATE" >/dev/null 2>&1 + fi + if pct create "$CTID" "local:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then + msg_ok "Container successfully created using local fallback." + else + # --- Dynamic stack upgrade + auto-retry on the well-known error pattern --- + if grep -qiE 'unsupported .* version' "$LOGFILE"; then + echo + echo "pct reported 'unsupported ... version' – your LXC stack might be too old for this template." + echo "We can try to upgrade 'pve-container' and 'lxc-pve' now and retry automatically." + if offer_lxc_stack_upgrade_and_maybe_retry "yes"; then + : # success after retry + else + rc=$? + case $rc in + 2) echo "Upgrade was declined. Please update and re-run: + apt update && apt install --only-upgrade pve-container lxc-pve" ;; + 3) echo "Upgrade and/or retry failed. Please inspect: $LOGFILE" ;; + esac + exit 231 + fi + else + msg_error "Container creation failed even with local fallback. See $LOGFILE" + if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then + set -x + bash -x -c "pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" 2>&1 | tee -a "$LOGFILE" + set +x + fi + exit 209 + fi + fi + else + msg_error "Container creation failed on local storage. See $LOGFILE" + # --- Dynamic stack upgrade + auto-retry on the well-known error pattern --- + if grep -qiE 'unsupported .* version' "$LOGFILE"; then + echo + echo "pct reported 'unsupported ... version' – your LXC stack might be too old for this template." + echo "We can try to upgrade 'pve-container' and 'lxc-pve' now and retry automatically." + if offer_lxc_stack_upgrade_and_maybe_retry "yes"; then + : # success after retry + else + rc=$? + case $rc in + 2) echo "Upgrade was declined. Please update and re-run: + apt update && apt install --only-upgrade pve-container lxc-pve" ;; + 3) echo "Upgrade and/or retry failed. Please inspect: $LOGFILE" ;; + esac + exit 231 + fi + else + if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then + set -x + bash -x -c "pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" 2>&1 | tee -a "$LOGFILE" + set +x + fi + exit 209 + fi + fi + fi + fi + + # Verify container exists + pct list | awk '{print $1}' | grep -qx "$CTID" || { + msg_error "Container ID $CTID not listed in 'pct list'. See $LOGFILE" + exit 215 + } + + # Verify config rootfs + grep -q '^rootfs:' "/etc/pve/lxc/$CTID.conf" || { + msg_error "RootFS entry missing in container config. See $LOGFILE" + exit 216 + } + + msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created." +} + +# ------------------------------------------------------------------------------ +# description() +# +# - Sets container description with HTML content (logo, links, badges) +# - Restarts ping-instances.service if present +# - Posts status "done" to API +# ------------------------------------------------------------------------------ +description() { + IP=$(pct exec "$CTID" ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) + + # Generate LXC Description + DESCRIPTION=$( + cat < + + Logo + + +

${APP} LXC

+ +

+ + spend Coffee + +

+ + + + GitHub + + + + Discussions + + + + Issues + + +EOF + ) + pct set "$CTID" -description "$DESCRIPTION" + + if [[ -f /etc/systemd/system/ping-instances.service ]]; then + systemctl start ping-instances.service + fi + + post_update_to_api "done" "none" +} + +# ------------------------------------------------------------------------------ +# api_exit_script() +# +# - Exit trap handler +# - Reports exit codes to API with detailed reason +# - Handles known codes (100–209) and maps them to errors +# ------------------------------------------------------------------------------ +api_exit_script() { + exit_code=$? + if [ $exit_code -ne 0 ]; then + case $exit_code in + 100) post_update_to_api "failed" "100: Unexpected error in create_lxc.sh" ;; + 101) post_update_to_api "failed" "101: No network connection detected in create_lxc.sh" ;; + 200) post_update_to_api "failed" "200: LXC creation failed in create_lxc.sh" ;; + 201) post_update_to_api "failed" "201: Invalid Storage class in create_lxc.sh" ;; + 202) post_update_to_api "failed" "202: User aborted menu in create_lxc.sh" ;; + 203) post_update_to_api "failed" "203: CTID not set in create_lxc.sh" ;; + 204) post_update_to_api "failed" "204: PCT_OSTYPE not set in create_lxc.sh" ;; + 205) post_update_to_api "failed" "205: CTID cannot be less than 100 in create_lxc.sh" ;; + 206) post_update_to_api "failed" "206: CTID already in use in create_lxc.sh" ;; + 207) post_update_to_api "failed" "207: Template not found in create_lxc.sh" ;; + 208) post_update_to_api "failed" "208: Error downloading template in create_lxc.sh" ;; + 209) post_update_to_api "failed" "209: Container creation failed, but template is intact in create_lxc.sh" ;; + *) post_update_to_api "failed" "Unknown error, exit code: $exit_code in create_lxc.sh" ;; + esac + fi +} + +if command -v pveversion >/dev/null 2>&1; then + trap 'api_exit_script' EXIT +fi +trap 'post_update_to_api "failed" "$BASH_COMMAND"' ERR +trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT +trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM diff --git a/misc/install.func b/misc/install.func new file mode 100644 index 000000000..f741b921d --- /dev/null +++ b/misc/install.func @@ -0,0 +1,206 @@ +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# Co-Author: MickLesk +# Co-Author: michelroegl-brunner +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE + +if ! command -v curl >/dev/null 2>&1; then + printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 + apt-get update >/dev/null 2>&1 + apt-get install -y curl >/dev/null 2>&1 +fi +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) +load_functions +catch_errors + +# This function enables IPv6 if it's not disabled and sets verbose mode +verb_ip6() { + set_std_mode # Set STD mode based on VERBOSE + + if [ "$DISABLEIPV6" == "yes" ]; then + echo "net.ipv6.conf.all.disable_ipv6 = 1" >>/etc/sysctl.conf + $STD sysctl -p + fi +} + +# # This function sets error handling options and defines the error_handler function to handle errors +# catch_errors() { +# set -Eeuo pipefail +# trap 'error_handler $LINENO "$BASH_COMMAND"' ERR +# } + +# # This function handles errors +# error_handler() { +# source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/api.func) +# local exit_code="$1" +# local line_number="$2" +# local command="${3:-}" + +# if [[ "$exit_code" -eq 0 ]]; then +# return 0 +# fi + +# printf "\e[?25h" +# echo -e "\n${RD}[ERROR]${CL} in line ${RD}${line_number}${CL}: exit code ${RD}${exit_code}${CL}: while executing command ${YW}${command}${CL}\n" +# exit "$exit_code" +#} + +# This function sets up the Container OS by generating the locale, setting the timezone, and checking the network connection +setting_up_container() { + msg_info "Setting up Container OS" + for ((i = RETRY_NUM; i > 0; i--)); do + if [ "$(hostname -I)" != "" ]; then + break + fi + echo 1>&2 -en "${CROSS}${RD} No Network! " + sleep $RETRY_EVERY + done + if [ "$(hostname -I)" = "" ]; then + echo 1>&2 -e "\n${CROSS}${RD} No Network After $RETRY_NUM Tries${CL}" + echo -e "${NETWORK}Check Network Settings" + exit 1 + fi + rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED + systemctl disable -q --now systemd-networkd-wait-online.service + msg_ok "Set up Container OS" + #msg_custom "${CM}" "${GN}" "Network Connected: ${BL}$(hostname -I)" + msg_ok "Network Connected: ${BL}$(hostname -I)" +} + +# This function checks the network connection by pinging a known IP address and prompts the user to continue if the internet is not connected +network_check() { + set +e + trap - ERR + ipv4_connected=false + ipv6_connected=false + sleep 1 + + # Check IPv4 connectivity to Google, Cloudflare & Quad9 DNS servers. + if ping -c 1 -W 1 1.1.1.1 &>/dev/null || ping -c 1 -W 1 8.8.8.8 &>/dev/null || ping -c 1 -W 1 9.9.9.9 &>/dev/null; then + msg_ok "IPv4 Internet Connected" + ipv4_connected=true + else + msg_error "IPv4 Internet Not Connected" + fi + + # Check IPv6 connectivity to Google, Cloudflare & Quad9 DNS servers. + if ping6 -c 1 -W 1 2606:4700:4700::1111 &>/dev/null || ping6 -c 1 -W 1 2001:4860:4860::8888 &>/dev/null || ping6 -c 1 -W 1 2620:fe::fe &>/dev/null; then + msg_ok "IPv6 Internet Connected" + ipv6_connected=true + else + msg_error "IPv6 Internet Not Connected" + fi + + # If both IPv4 and IPv6 checks fail, prompt the user + if [[ $ipv4_connected == false && $ipv6_connected == false ]]; then + read -r -p "No Internet detected, would you like to continue anyway? " prompt + if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then + echo -e "${INFO}${RD}Expect Issues Without Internet${CL}" + else + echo -e "${NETWORK}Check Network Settings" + exit 1 + fi + fi + + # DNS resolution checks for GitHub-related domains (IPv4 and/or IPv6) + GIT_HOSTS=("github.com" "raw.githubusercontent.com" "api.github.com" "git.community-scripts.org") + GIT_STATUS="Git DNS:" + DNS_FAILED=false + + for HOST in "${GIT_HOSTS[@]}"; do + RESOLVEDIP=$(getent hosts "$HOST" | awk '{ print $1 }' | grep -E '(^([0-9]{1,3}\.){3}[0-9]{1,3}$)|(^[a-fA-F0-9:]+$)' | head -n1) + if [[ -z "$RESOLVEDIP" ]]; then + GIT_STATUS+="$HOST:($DNSFAIL)" + DNS_FAILED=true + else + GIT_STATUS+=" $HOST:($DNSOK)" + fi + done + + if [[ "$DNS_FAILED" == true ]]; then + fatal "$GIT_STATUS" + else + msg_ok "$GIT_STATUS" + fi + + set -e + trap 'error_handler $LINENO "$BASH_COMMAND"' ERR +} + +# This function updates the Container OS by running apt-get update and upgrade +update_os() { + msg_info "Updating Container OS" + if [[ "$CACHER" == "yes" ]]; then + echo "Acquire::http::Proxy-Auto-Detect \"/usr/local/bin/apt-proxy-detect.sh\";" >/etc/apt/apt.conf.d/00aptproxy + cat <<'EOF' >/usr/local/bin/apt-proxy-detect.sh +#!/bin/bash +if nc -w1 -z "${CACHER_IP}" 3142; then + echo -n "http://${CACHER_IP}:3142" +else + echo -n "DIRECT" +fi +EOF + chmod +x /usr/local/bin/apt-proxy-detect.sh + fi + $STD apt-get update + $STD apt-get -o Dpkg::Options::="--force-confold" -y dist-upgrade + rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED + msg_ok "Updated Container OS" + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) +} + +# This function modifies the message of the day (motd) and SSH settings +motd_ssh() { + grep -qxF "export TERM='xterm-256color'" /root/.bashrc || echo "export TERM='xterm-256color'" >>/root/.bashrc + + if [ -f "/etc/os-release" ]; then + OS_NAME=$(grep ^NAME /etc/os-release | cut -d= -f2 | tr -d '"') + OS_VERSION=$(grep ^VERSION_ID /etc/os-release | cut -d= -f2 | tr -d '"') + elif [ -f "/etc/debian_version" ]; then + OS_NAME="Debian" + OS_VERSION=$(cat /etc/debian_version) + fi + + PROFILE_FILE="/etc/profile.d/00_lxc-details.sh" + echo "echo -e \"\"" >"$PROFILE_FILE" + echo -e "echo -e \"${BOLD}${YW}${APPLICATION} LXC Container - DEV Repository${CL}\"" >>"$PROFILE_FILE" + echo -e "echo -e \"${RD}WARNING: This is a DEVELOPMENT version (ProxmoxVED). Do NOT use in production!${CL}\"" >>"$PROFILE_FILE" + echo -e "echo -e \"${YW} OS: ${GN}${OS_NAME} - Version: ${OS_VERSION}${CL}\"" >>"$PROFILE_FILE" + echo -e "echo -e \"${YW} Hostname: ${GN}\$(hostname)${CL}\"" >>"$PROFILE_FILE" + echo -e "echo -e \"${YW} IP Address: ${GN}\$(hostname -I | awk '{print \$1}')${CL}\"" >>"$PROFILE_FILE" + echo -e "echo -e \"${YW} Repository: ${GN}https://github.com/community-scripts/ProxmoxVED${CL}\"" >>"$PROFILE_FILE" + echo "echo \"\"" >>"$PROFILE_FILE" + + chmod -x /etc/update-motd.d/* + + if [[ "${SSH_ROOT}" == "yes" ]]; then + sed -i "s/#PermitRootLogin prohibit-password/PermitRootLogin yes/g" /etc/ssh/sshd_config + systemctl restart sshd + fi +} + +# This function customizes the container by modifying the getty service and enabling auto-login for the root user +customize() { + if [[ "$PASSWORD" == "" ]]; then + msg_info "Customizing Container" + GETTY_OVERRIDE="/etc/systemd/system/container-getty@1.service.d/override.conf" + mkdir -p $(dirname $GETTY_OVERRIDE) + cat <$GETTY_OVERRIDE + [Service] + ExecStart= + ExecStart=-/sbin/agetty --autologin root --noclear --keep-baud tty%I 115200,38400,9600 \$TERM +EOF + systemctl daemon-reload + systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//') + msg_ok "Customized Container" + fi + echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update + chmod +x /usr/bin/update + if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then + mkdir -p /root/.ssh + echo "${SSH_AUTHORIZED_KEY}" >/root/.ssh/authorized_keys + chmod 700 /root/.ssh + chmod 600 /root/.ssh/authorized_keys + fi +} From e2e24f223a70bbcbd5a091ebc617c7f1bdf2f7e1 Mon Sep 17 00:00:00 2001 From: Enzo Legrand Date: Tue, 30 Sep 2025 17:26:13 +0200 Subject: [PATCH 1258/1733] add: sonarqube json config --- frontend/public/json/sonarqube.json | 36 +++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 frontend/public/json/sonarqube.json diff --git a/frontend/public/json/sonarqube.json b/frontend/public/json/sonarqube.json new file mode 100644 index 000000000..c86f2240e --- /dev/null +++ b/frontend/public/json/sonarqube.json @@ -0,0 +1,36 @@ +{ + "name": "sonarqube", + "slug": "sonarqube", + "categories": [ + 20, + 19 + ], + "date_created": "2025-09-30", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 9000, + "documentation": "https://docs.sonarsource.com/sonarqube-server", + "config_path": "/opt/sonarqube/conf/sonar.properties", + "website": null, + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/sonarqube.webp", + "description": "SonarQube Server automates code quality and security reviews and provides actionable code intelligence so developers can focus on building better, faster.", + "install_methods": [ + { + "type": "default", + "script": "ct/sonarqube.sh", + "resources": { + "cpu": 4, + "ram": 6144, + "hdd": 25, + "os": "Debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": "admin", + "password": "admin" + }, + "notes": [] +} From 823bbca9afed7714271defabfc286e4fbe304342 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Tue, 30 Sep 2025 13:35:58 -0400 Subject: [PATCH 1259/1733] Add rwMarkable --- ct/rwmarkable.sh | 75 ++++++++++++++++++++++++++++ frontend/public/json/rwmarkable.json | 36 +++++++++++++ install/rwmarkable-install.sh | 67 +++++++++++++++++++++++++ 3 files changed, 178 insertions(+) create mode 100644 ct/rwmarkable.sh create mode 100644 frontend/public/json/rwmarkable.json create mode 100644 install/rwmarkable-install.sh diff --git a/ct/rwmarkable.sh b/ct/rwmarkable.sh new file mode 100644 index 000000000..c7579db98 --- /dev/null +++ b/ct/rwmarkable.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: vhsdream +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://github.com/fccview/rwMarkable + +APP="rwMarkable" +var_tags="${var_tags:-tasks;notes}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -d /opt/rwmarkable ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "rwMarkable" "fccview/rwMarkable"; then + msg_info "Stopping ${APP}" + systemctl stop rwmarkable + msg_ok "Stopped ${APP}" + + msg_info "Backing up configuration & data" + cd /opt/rwmarkable + cp ./.env /opt/app.env + $STD tar -cf /opt/data.tar ./data + msg_ok "Backed up configuration & data" + + NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs + cd /opt + export CLEAN_INSTALL=1 + fetch_and_deploy_gh_release "rwMarkable" "fccview/rwMarkable" "tarball" "latest" "/opt/rwmarkable" + + msg_info "Updating app" + export NEXT_TELEMETRY_DISABLE=1 + export NODE_ENV=production + cd /opt/rwmarkable + $STD yarn --frozen-lockfile + $STD yarn build + msg_ok "Updated app" + + msg_info "Restoring configuration & data" + mv /opt/app.env /opt/rwmarkable/.env + $STD tar xf /opt/data.tar + msg_ok "Restored configuration & data" + + msg_info "Restarting ${APP} service" + systemctl start rwmarkable + msg_ok "Restarted ${APP} service" + fi + +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" diff --git a/frontend/public/json/rwmarkable.json b/frontend/public/json/rwmarkable.json new file mode 100644 index 000000000..8eaed9acc --- /dev/null +++ b/frontend/public/json/rwmarkable.json @@ -0,0 +1,36 @@ +{ + "name": "rwMarkable", + "slug": "rwmarkable", + "categories": [ + 12 + ], + "date_created": "2024-10-02", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 3000, + "documentation": "https://github.com/fccview/rwMarkable/blob/main/README.md", + "website": "https://github.com/fccview/rwMarkable", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/rwmarkable.webp", + "config_path": "/opt/rwmarkable/.env", + "description": "A simple, self-hosted app for your checklists and notes. Tired of bloated, cloud-based to-do apps? rwMarkable is a lightweight alternative for managing your personal checklists and notes. It's built with Next.js 14, is easy to deploy, and keeps all your data on your own server.", + "install_methods": [ + { + "type": "default", + "script": "ct/rwmarkable.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 4, + "os": "debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} + diff --git a/install/rwmarkable-install.sh b/install/rwmarkable-install.sh new file mode 100644 index 000000000..e74c21eb4 --- /dev/null +++ b/install/rwmarkable-install.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: vhsdream +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/fccview/rwMarkable + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs +fetch_and_deploy_gh_release "rwMarkable" "fccview/rwMarkable" "tarball" "latest" "/opt/rwmarkable" + +msg_info "Building app" +cd /opt/rwmarkable +export NEXT_TELEMETRY_DISABLE=1 +export NODE_ENV=production +$STD yarn --frozen-lockfile +$STD yarn build +# maybe move some stuff around so the app is in a sensible spot? +msg_ok "Successfully built app" + +msg_info "Creating .env file" +cat </opt/rwmarkable/.env +NODE_ENV=production +# HTTPS=true + +# --- SSO with OIDC (optional) +# SSO_MODE=oidc +# OIDC_ISSUER= +# OIDC_CLIENT_ID= +# APP_URL= +# SSO_FALLBACK_LOCAL=true # Allow both SSO and normal login +# OIDC_CLIENT_SECRET=your_client_secret # Enable confidential client mode with client authentication +# OIDC_ADMIN_GROUPS=admins # Map provider groups to admin role +EOF +msg_ok "Created .env file" + +msg_info "Creating rwMarkable Service" +cat </etc/systemd/system/rwmarkable.service +[Unit] +Description=rwMarkable server +After=network.target + +[Service] +WorkingDirectory=/opt/rwmarkable +EnvironmentFile=/opt/rwmarkable/.env +ExecStart=yarn start +Restart=unless-stopped + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now rwmarkable +msg_ok "Created rwMarkable Service" +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 6b756c95f1d69621591ee4a140f1464633c3f774 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Tue, 30 Sep 2025 13:52:56 -0400 Subject: [PATCH 1260/1733] rwMarkable: create the data folders during installation --- install/rwmarkable-install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install/rwmarkable-install.sh b/install/rwmarkable-install.sh index e74c21eb4..b52b0163f 100644 --- a/install/rwmarkable-install.sh +++ b/install/rwmarkable-install.sh @@ -22,6 +22,7 @@ export NEXT_TELEMETRY_DISABLE=1 export NODE_ENV=production $STD yarn --frozen-lockfile $STD yarn build +mkdir -p data/{users,checklists,notes} # maybe move some stuff around so the app is in a sensible spot? msg_ok "Successfully built app" From 12e86edb7ad2b0606bb50984c23fbd5a565c1495 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Tue, 30 Sep 2025 14:13:56 -0400 Subject: [PATCH 1261/1733] rwMarkable: use yarn command for disabling telemetry; remove NODE_ENV during build --- ct/rwmarkable.sh | 3 +-- install/rwmarkable-install.sh | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/ct/rwmarkable.sh b/ct/rwmarkable.sh index c7579db98..a7d2664e2 100644 --- a/ct/rwmarkable.sh +++ b/ct/rwmarkable.sh @@ -46,10 +46,9 @@ function update_script() { fetch_and_deploy_gh_release "rwMarkable" "fccview/rwMarkable" "tarball" "latest" "/opt/rwmarkable" msg_info "Updating app" - export NEXT_TELEMETRY_DISABLE=1 - export NODE_ENV=production cd /opt/rwmarkable $STD yarn --frozen-lockfile + $STD yarn next telemetry disable $STD yarn build msg_ok "Updated app" diff --git a/install/rwmarkable-install.sh b/install/rwmarkable-install.sh index b52b0163f..9dfedc439 100644 --- a/install/rwmarkable-install.sh +++ b/install/rwmarkable-install.sh @@ -18,9 +18,8 @@ fetch_and_deploy_gh_release "rwMarkable" "fccview/rwMarkable" "tarball" "latest" msg_info "Building app" cd /opt/rwmarkable -export NEXT_TELEMETRY_DISABLE=1 -export NODE_ENV=production $STD yarn --frozen-lockfile +$STD yarn next telemetry disable $STD yarn build mkdir -p data/{users,checklists,notes} # maybe move some stuff around so the app is in a sensible spot? From 2f9d2d975601792f4023166a6145939ab54a523e Mon Sep 17 00:00:00 2001 From: vhsdream Date: Tue, 30 Sep 2025 14:18:57 -0400 Subject: [PATCH 1262/1733] rwMarkable: fix service file restart --- install/rwmarkable-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/rwmarkable-install.sh b/install/rwmarkable-install.sh index 9dfedc439..602f65487 100644 --- a/install/rwmarkable-install.sh +++ b/install/rwmarkable-install.sh @@ -51,7 +51,7 @@ After=network.target WorkingDirectory=/opt/rwmarkable EnvironmentFile=/opt/rwmarkable/.env ExecStart=yarn start -Restart=unless-stopped +Restart=on-abnormal [Install] WantedBy=multi-user.target From 552959a39bdc793b30a14746809f40f6f2f1164e Mon Sep 17 00:00:00 2001 From: Vincent <114195376+HydroshieldMKII@users.noreply.github.com> Date: Tue, 30 Sep 2025 14:36:17 -0400 Subject: [PATCH 1263/1733] Update Guardian installation and update scripts to use new repository paths and improve dependency management --- ct/guardian.sh | 34 ++++++++++++++++++++++++---------- install/guardian-install.sh | 19 +++++++++---------- misc/build.func | 2 +- misc/install.func | 10 +++++----- 4 files changed, 39 insertions(+), 26 deletions(-) diff --git a/ct/guardian.sh b/ct/guardian.sh index 42aaa0bc1..09c774ccb 100644 --- a/ct/guardian.sh +++ b/ct/guardian.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: HydroshieldMKII # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -31,35 +31,49 @@ function update_script() { fi # Crawling the new version and checking whether an update is required - RELEASE=$(curl -fsSL [RELEASE_URL] | [PARSE_RELEASE_COMMAND]) + RELEASE=$(curl -fsSL https://api.github.com/repos/HydroshieldMKII/Guardian/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then # Stopping Services msg_info "Stopping $APP" - systemctl stop [SERVICE_NAME] + systemctl stop guardian-backend guardian-frontend msg_ok "Stopped $APP" # Creating Backup msg_info "Creating Backup" - tar -czf "/opt/${APP}_backup_$(date +%F).tar.gz" [IMPORTANT_PATHS] + tar -czf "/opt/${APP}_backup_$(date +%F).tar.gz" /opt/Guardian msg_ok "Backup Created" # Execute Update msg_info "Updating $APP to v${RELEASE}" - [UPDATE_COMMANDS] + cd /tmp + curl -fsSL -o "${RELEASE}.zip" "https://github.com/HydroshieldMKII/Guardian/archive/refs/tags/${RELEASE}.zip" + unzip -q "${RELEASE}.zip" + rm -rf /opt/Guardian + mv "Guardian-${RELEASE}/" "/opt/Guardian" + + # Build Backend + cd /opt/Guardian/backend + npm ci + npm run build + + # Build Frontend + cd /opt/Guardian/frontend + npm ci + npm run build + + echo "${RELEASE}" >/opt/${APP}_version.txt msg_ok "Updated $APP to v${RELEASE}" # Starting Services msg_info "Starting $APP" - systemctl start [SERVICE_NAME] + systemctl start guardian-backend guardian-frontend msg_ok "Started $APP" # Cleaning up msg_info "Cleaning Up" - rm -rf [TEMP_FILES] + rm -rf /tmp/"${RELEASE}.zip" /tmp/"Guardian-${RELEASE}" msg_ok "Cleanup Completed" - # Last Action - echo "${RELEASE}" >/opt/${APP}_version.txt msg_ok "Update Successful" else msg_ok "No update required. ${APP} is already at v${RELEASE}" @@ -74,4 +88,4 @@ description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:[PORT]${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" diff --git a/install/guardian-install.sh b/install/guardian-install.sh index 77d73eed0..69eeecf37 100644 --- a/install/guardian-install.sh +++ b/install/guardian-install.sh @@ -20,31 +20,30 @@ $STD apt-get install -y \ git \ nodejs \ npm \ - sqlite + sqlite3 \ + unzip \ + curl msg_ok "Installed Dependencies" # Setup App -msg_info "Setup ${APPLICATION}" +msg_info "Setup Guardian" RELEASE=$(curl -fsSL https://api.github.com/repos/HydroshieldMKII/Guardian/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') curl -fsSL -o "${RELEASE}.zip" "https://github.com/HydroshieldMKII/Guardian/archive/refs/tags/${RELEASE}.zip" unzip -q "${RELEASE}.zip" -mv "${APPLICATION}-${RELEASE}/" "/opt/${APPLICATION}" -# -# -# -echo "${RELEASE}" >/opt/"${APPLICATION}"_version.txt -msg_ok "Setup ${APPLICATION}" +mv "Guardian-${RELEASE}/" "/opt/Guardian" +echo "${RELEASE}" >/opt/Guardian_version.txt +msg_ok "Setup Guardian" # ===== Build Backend ===== msg_info "Building backend" -cd /opt/${APPLICATION}/backend +cd /opt/Guardian/backend npm ci npm run build msg_ok "Built backend" # ===== Build Frontend ===== msg_info "Building frontend" -cd /opt/${APPLICATION}/frontend +cd /opt/Guardian/frontend npm ci npm run build msg_ok "Built frontend" diff --git a/misc/build.func b/misc/build.func index d6b0af725..92e9c7198 100644 --- a/misc/build.func +++ b/misc/build.func @@ -79,7 +79,7 @@ variables() { # [Dd]*) # for file in core.func error_handler.func tools.func; do # local_path="$FUNC_DIR/$file" -# url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/$file" +# url="https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/$file" # remote_tmp="$(mktemp)" # curl -fsSL "$url" -o "$remote_tmp" || continue diff --git a/misc/install.func b/misc/install.func index f741b921d..2ebc8e2e8 100644 --- a/misc/install.func +++ b/misc/install.func @@ -9,8 +9,8 @@ if ! command -v curl >/dev/null 2>&1; then apt-get update >/dev/null 2>&1 apt-get install -y curl >/dev/null 2>&1 fi -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) +source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/core.func) +source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/error_handler.func) load_functions catch_errors @@ -32,7 +32,7 @@ verb_ip6() { # # This function handles errors # error_handler() { -# source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/api.func) +# source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/api.func) # local exit_code="$1" # local line_number="$2" # local command="${3:-}" @@ -147,7 +147,7 @@ EOF $STD apt-get -o Dpkg::Options::="--force-confold" -y dist-upgrade rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED msg_ok "Updated Container OS" - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) + source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/tools.func) } # This function modifies the message of the day (motd) and SSH settings @@ -195,7 +195,7 @@ EOF systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//') msg_ok "Customized Container" fi - echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update + echo "bash -c \"\$(curl -fsSL https://github.com/HydroshieldMKII/ProxmoxVED/raw/add-guardian-app/ct/${app}.sh)\"" >/usr/bin/update chmod +x /usr/bin/update if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then mkdir -p /root/.ssh From e3718d5c63bcb7a320d1243c59611bcc1a5559f9 Mon Sep 17 00:00:00 2001 From: Vincent <114195376+HydroshieldMKII@users.noreply.github.com> Date: Tue, 30 Sep 2025 14:36:26 -0400 Subject: [PATCH 1264/1733] Refactor update_script function to streamline update process and ensure version tracking --- ct/guardian.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ct/guardian.sh b/ct/guardian.sh index 09c774ccb..b3c4634b1 100644 --- a/ct/guardian.sh +++ b/ct/guardian.sh @@ -50,17 +50,17 @@ function update_script() { unzip -q "${RELEASE}.zip" rm -rf /opt/Guardian mv "Guardian-${RELEASE}/" "/opt/Guardian" - + # Build Backend cd /opt/Guardian/backend npm ci npm run build - + # Build Frontend cd /opt/Guardian/frontend npm ci npm run build - + echo "${RELEASE}" >/opt/${APP}_version.txt msg_ok "Updated $APP to v${RELEASE}" From 7c7a9308b4486172dd31037dda6f46ffed2c3a9d Mon Sep 17 00:00:00 2001 From: vhsdream Date: Tue, 30 Sep 2025 14:43:11 -0400 Subject: [PATCH 1265/1733] rwMarkable: small tweaks --- ct/rwmarkable.sh | 6 ++++-- install/rwmarkable-install.sh | 1 - 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ct/rwmarkable.sh b/ct/rwmarkable.sh index a7d2664e2..c1c37045e 100644 --- a/ct/rwmarkable.sh +++ b/ct/rwmarkable.sh @@ -54,14 +54,16 @@ function update_script() { msg_info "Restoring configuration & data" mv /opt/app.env /opt/rwmarkable/.env - $STD tar xf /opt/data.tar + $STD tar -xf /opt/data.tar msg_ok "Restored configuration & data" msg_info "Restarting ${APP} service" systemctl start rwmarkable msg_ok "Restarted ${APP} service" + rm /opt/data.tar + msg_ok "Updated Successfully" fi - + exit } start diff --git a/install/rwmarkable-install.sh b/install/rwmarkable-install.sh index 602f65487..6f9f32e65 100644 --- a/install/rwmarkable-install.sh +++ b/install/rwmarkable-install.sh @@ -22,7 +22,6 @@ $STD yarn --frozen-lockfile $STD yarn next telemetry disable $STD yarn build mkdir -p data/{users,checklists,notes} -# maybe move some stuff around so the app is in a sensible spot? msg_ok "Successfully built app" msg_info "Creating .env file" From ee0121fb07803786341224deb0793fa757efe416 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 30 Sep 2025 18:43:40 +0000 Subject: [PATCH 1266/1733] Update .app files --- ct/headers/ghostfolio | 6 ------ ct/headers/myip | 6 ------ ct/headers/rwmarkable | 6 ++++++ ct/headers/warracker | 6 ------ 4 files changed, 6 insertions(+), 18 deletions(-) delete mode 100644 ct/headers/ghostfolio delete mode 100644 ct/headers/myip create mode 100644 ct/headers/rwmarkable delete mode 100644 ct/headers/warracker diff --git a/ct/headers/ghostfolio b/ct/headers/ghostfolio deleted file mode 100644 index 19e1f29f8..000000000 --- a/ct/headers/ghostfolio +++ /dev/null @@ -1,6 +0,0 @@ - ________ __ ____ ___ - / ____/ /_ ____ _____/ /_/ __/___ / (_)___ - / / __/ __ \/ __ \/ ___/ __/ /_/ __ \/ / / __ \ -/ /_/ / / / / /_/ (__ ) /_/ __/ /_/ / / / /_/ / -\____/_/ /_/\____/____/\__/_/ \____/_/_/\____/ - diff --git a/ct/headers/myip b/ct/headers/myip deleted file mode 100644 index d7a8d566c..000000000 --- a/ct/headers/myip +++ /dev/null @@ -1,6 +0,0 @@ - __ ___ ________ - / |/ /_ __/ _/ __ \ - / /|_/ / / / // // /_/ / - / / / / /_/ // // ____/ -/_/ /_/\__, /___/_/ - /____/ diff --git a/ct/headers/rwmarkable b/ct/headers/rwmarkable new file mode 100644 index 000000000..5ef3689cf --- /dev/null +++ b/ct/headers/rwmarkable @@ -0,0 +1,6 @@ + __ ___ __ __ __ + ______ __/ |/ /___ ______/ /______ _/ /_ / /__ + / ___/ | /| / / /|_/ / __ `/ ___/ //_/ __ `/ __ \/ / _ \ + / / | |/ |/ / / / / /_/ / / / ,< / /_/ / /_/ / / __/ +/_/ |__/|__/_/ /_/\__,_/_/ /_/|_|\__,_/_.___/_/\___/ + diff --git a/ct/headers/warracker b/ct/headers/warracker deleted file mode 100644 index b9e7c55d2..000000000 --- a/ct/headers/warracker +++ /dev/null @@ -1,6 +0,0 @@ - __ - _ ______ _______________ ______/ /_____ _____ -| | /| / / __ `/ ___/ ___/ __ `/ ___/ //_/ _ \/ ___/ -| |/ |/ / /_/ / / / / / /_/ / /__/ ,< / __/ / -|__/|__/\__,_/_/ /_/ \__,_/\___/_/|_|\___/_/ - From bf38566ff8f96e80d17f8e952f4802e527b7b8a4 Mon Sep 17 00:00:00 2001 From: Vincent <114195376+HydroshieldMKII@users.noreply.github.com> Date: Tue, 30 Sep 2025 14:47:15 -0400 Subject: [PATCH 1267/1733] Update script sources to use new repository paths for improved functionality --- misc/build.func | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/misc/build.func b/misc/build.func index 92e9c7198..7169b3545 100644 --- a/misc/build.func +++ b/misc/build.func @@ -128,17 +128,17 @@ variables() { # - Initialize error traps after loading # ------------------------------------------------------------------------------ -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) +source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/api.func) if command -v curl >/dev/null 2>&1; then - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) + source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/core.func) + source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/error_handler.func) load_functions catch_errors #echo "(build.func) Loaded core.func via curl" elif command -v wget >/dev/null 2>&1; then - source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) - source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) + source <(wget -qO- https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/core.func) + source <(wget -qO- https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/error_handler.func) load_functions catch_errors #echo "(build.func) Loaded core.func via wget" @@ -2062,7 +2062,7 @@ ssh_discover_default_files() { # - Otherwise: shows update/setting menu # ------------------------------------------------------------------------------ start() { - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) + source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/tools.func) if command -v pveversion >/dev/null 2>&1; then install_script || return 0 return 0 @@ -2141,9 +2141,9 @@ build_container() { TEMP_DIR=$(mktemp -d) pushd "$TEMP_DIR" >/dev/null if [ "$var_os" == "alpine" ]; then - export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-install.func)" + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/alpine-install.func)" else - export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/install.func)" fi export DIAGNOSTICS="$DIAGNOSTICS" export RANDOM_UUID="$RANDOM_UUID" @@ -2183,7 +2183,7 @@ build_container() { LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/passthrough.func) + source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/passthrough.func) select_hw_passthrough "$CTID" "$CT_TYPE" "$APP" # TUN device passthrough @@ -2295,7 +2295,7 @@ EOF' fi msg_ok "Customized LXC Container" install_ssh_keys_into_ct - if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)"; then + if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/install/${var_install}.sh)"; then exit $? fi } From c520602fcbe7946dc5eb65ae9d88969daf5fa064 Mon Sep 17 00:00:00 2001 From: Vincent <114195376+HydroshieldMKII@users.noreply.github.com> Date: Tue, 30 Sep 2025 14:51:54 -0400 Subject: [PATCH 1268/1733] Strip 'v' prefix from RELEASE in Guardian installation scripts for consistent folder naming --- ct/guardian.sh | 4 +++- install/guardian-install.sh | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ct/guardian.sh b/ct/guardian.sh index b3c4634b1..140a472a1 100644 --- a/ct/guardian.sh +++ b/ct/guardian.sh @@ -49,7 +49,9 @@ function update_script() { curl -fsSL -o "${RELEASE}.zip" "https://github.com/HydroshieldMKII/Guardian/archive/refs/tags/${RELEASE}.zip" unzip -q "${RELEASE}.zip" rm -rf /opt/Guardian - mv "Guardian-${RELEASE}/" "/opt/Guardian" + # Strip 'v' prefix from RELEASE for folder name + FOLDER_NAME=$(echo "${RELEASE}" | sed 's/^v//') + mv "Guardian-${FOLDER_NAME}/" "/opt/Guardian" # Build Backend cd /opt/Guardian/backend diff --git a/install/guardian-install.sh b/install/guardian-install.sh index 69eeecf37..51b67744b 100644 --- a/install/guardian-install.sh +++ b/install/guardian-install.sh @@ -30,7 +30,9 @@ msg_info "Setup Guardian" RELEASE=$(curl -fsSL https://api.github.com/repos/HydroshieldMKII/Guardian/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') curl -fsSL -o "${RELEASE}.zip" "https://github.com/HydroshieldMKII/Guardian/archive/refs/tags/${RELEASE}.zip" unzip -q "${RELEASE}.zip" -mv "Guardian-${RELEASE}/" "/opt/Guardian" +# Strip 'v' prefix from RELEASE for folder name +FOLDER_NAME=$(echo "${RELEASE}" | sed 's/^v//') +mv "Guardian-${FOLDER_NAME}/" "/opt/Guardian" echo "${RELEASE}" >/opt/Guardian_version.txt msg_ok "Setup Guardian" From f48609011b1937def38128e18a654bfb4f04e51d Mon Sep 17 00:00:00 2001 From: Vincent <114195376+HydroshieldMKII@users.noreply.github.com> Date: Tue, 30 Sep 2025 15:11:18 -0400 Subject: [PATCH 1269/1733] Refactor frontend setup in guardian-install.sh to streamline installation process and update service configuration --- install/guardian-install.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/install/guardian-install.sh b/install/guardian-install.sh index 51b67744b..c13645ee6 100644 --- a/install/guardian-install.sh +++ b/install/guardian-install.sh @@ -43,12 +43,11 @@ npm ci npm run build msg_ok "Built backend" -# ===== Build Frontend ===== -msg_info "Building frontend" +# ===== Install Frontend Dependencies ===== +msg_info "Installing frontend dependencies" cd /opt/Guardian/frontend npm ci -npm run build -msg_ok "Built frontend" +msg_ok "Installed frontend dependencies" # ===== Backend Service ===== msg_info "Creating Backend Service" @@ -81,7 +80,8 @@ Wants=guardian-backend.service [Service] WorkingDirectory=/opt/Guardian/frontend Environment=NODE_ENV=development -ExecStart=/usr/bin/npm run start +Environment=PORT=3000 +ExecStart=/usr/bin/npm run dev Restart=always RestartSec=3 From c9f81a23c5e038b099e0aeab52bb0c1460171b9f Mon Sep 17 00:00:00 2001 From: Vincent <114195376+HydroshieldMKII@users.noreply.github.com> Date: Tue, 30 Sep 2025 15:29:39 -0400 Subject: [PATCH 1270/1733] Enhance database backup and restore functionality during Guardian updates --- ct/guardian.sh | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/ct/guardian.sh b/ct/guardian.sh index 140a472a1..0028bf3e8 100644 --- a/ct/guardian.sh +++ b/ct/guardian.sh @@ -9,7 +9,7 @@ APP="Guardian" var_tags="${var_tags:-media;monitoring}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" -var_disk="${var_disk:-4}" +var_disk="${var_disk:-6}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" @@ -43,6 +43,13 @@ function update_script() { tar -czf "/opt/${APP}_backup_$(date +%F).tar.gz" /opt/Guardian msg_ok "Backup Created" + # Preserve Database + msg_info "Preserving Database" + if [[ -f "/opt/Guardian/backend/plex-guard.db" ]]; then + cp "/opt/Guardian/backend/plex-guard.db" "/tmp/plex-guard.db.backup" + msg_ok "Database backed up" + fi + # Execute Update msg_info "Updating $APP to v${RELEASE}" cd /tmp @@ -53,6 +60,14 @@ function update_script() { FOLDER_NAME=$(echo "${RELEASE}" | sed 's/^v//') mv "Guardian-${FOLDER_NAME}/" "/opt/Guardian" + # Restore Database + if [[ -f "/tmp/plex-guard.db.backup" ]]; then + msg_info "Restoring Database" + cp "/tmp/plex-guard.db.backup" "/opt/Guardian/backend/plex-guard.db" + rm "/tmp/plex-guard.db.backup" + msg_ok "Database restored" + fi + # Build Backend cd /opt/Guardian/backend npm ci @@ -73,7 +88,7 @@ function update_script() { # Cleaning up msg_info "Cleaning Up" - rm -rf /tmp/"${RELEASE}.zip" /tmp/"Guardian-${RELEASE}" + rm -rf /tmp/"${RELEASE}.zip" /tmp/"Guardian-${FOLDER_NAME}" /tmp/plex-guard.db.backup msg_ok "Cleanup Completed" msg_ok "Update Successful" From 7f876d101d30e77d26480636e06a4daaa86fa4c3 Mon Sep 17 00:00:00 2001 From: Vincent <114195376+HydroshieldMKII@users.noreply.github.com> Date: Tue, 30 Sep 2025 15:42:50 -0400 Subject: [PATCH 1271/1733] Refactor update_script and installation process for Guardian to enhance backup handling, streamline frontend build, and improve service configurations --- ct/guardian.sh | 22 ++++++---------------- install/guardian-install.sh | 20 ++++++++------------ 2 files changed, 14 insertions(+), 28 deletions(-) diff --git a/ct/guardian.sh b/ct/guardian.sh index 0028bf3e8..2a9a8c154 100644 --- a/ct/guardian.sh +++ b/ct/guardian.sh @@ -24,43 +24,37 @@ function update_script() { check_container_storage check_container_resources - # Check if installation is present | -f for file, -d for folder if [[ ! -d "/opt/${APP}" ]]; then msg_error "No ${APP} Installation Found!" exit fi - # Crawling the new version and checking whether an update is required + RELEASE=$(curl -fsSL https://api.github.com/repos/HydroshieldMKII/Guardian/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then - # Stopping Services + msg_info "Stopping $APP" systemctl stop guardian-backend guardian-frontend msg_ok "Stopped $APP" - # Creating Backup - msg_info "Creating Backup" - tar -czf "/opt/${APP}_backup_$(date +%F).tar.gz" /opt/Guardian - msg_ok "Backup Created" - # Preserve Database msg_info "Preserving Database" if [[ -f "/opt/Guardian/backend/plex-guard.db" ]]; then cp "/opt/Guardian/backend/plex-guard.db" "/tmp/plex-guard.db.backup" msg_ok "Database backed up" fi - # Execute Update + msg_info "Updating $APP to v${RELEASE}" cd /tmp + curl -fsSL -o "${RELEASE}.zip" "https://github.com/HydroshieldMKII/Guardian/archive/refs/tags/${RELEASE}.zip" unzip -q "${RELEASE}.zip" rm -rf /opt/Guardian - # Strip 'v' prefix from RELEASE for folder name + FOLDER_NAME=$(echo "${RELEASE}" | sed 's/^v//') mv "Guardian-${FOLDER_NAME}/" "/opt/Guardian" - # Restore Database if [[ -f "/tmp/plex-guard.db.backup" ]]; then msg_info "Restoring Database" cp "/tmp/plex-guard.db.backup" "/opt/Guardian/backend/plex-guard.db" @@ -68,25 +62,21 @@ function update_script() { msg_ok "Database restored" fi - # Build Backend cd /opt/Guardian/backend npm ci npm run build - # Build Frontend cd /opt/Guardian/frontend npm ci - npm run build + NODE_ENV=development npm run build echo "${RELEASE}" >/opt/${APP}_version.txt msg_ok "Updated $APP to v${RELEASE}" - # Starting Services msg_info "Starting $APP" systemctl start guardian-backend guardian-frontend msg_ok "Started $APP" - # Cleaning up msg_info "Cleaning Up" rm -rf /tmp/"${RELEASE}.zip" /tmp/"Guardian-${FOLDER_NAME}" /tmp/plex-guard.db.backup msg_ok "Cleanup Completed" diff --git a/install/guardian-install.sh b/install/guardian-install.sh index c13645ee6..06d547b0d 100644 --- a/install/guardian-install.sh +++ b/install/guardian-install.sh @@ -5,7 +5,6 @@ # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/HydroshieldMKII/Guardian -# Import Functions und Setup source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 @@ -14,7 +13,6 @@ setting_up_container network_check update_os -# Installing Dependencies msg_info "Installing Dependencies" $STD apt-get install -y \ git \ @@ -25,31 +23,30 @@ $STD apt-get install -y \ curl msg_ok "Installed Dependencies" -# Setup App msg_info "Setup Guardian" RELEASE=$(curl -fsSL https://api.github.com/repos/HydroshieldMKII/Guardian/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') curl -fsSL -o "${RELEASE}.zip" "https://github.com/HydroshieldMKII/Guardian/archive/refs/tags/${RELEASE}.zip" unzip -q "${RELEASE}.zip" -# Strip 'v' prefix from RELEASE for folder name + FOLDER_NAME=$(echo "${RELEASE}" | sed 's/^v//') mv "Guardian-${FOLDER_NAME}/" "/opt/Guardian" + echo "${RELEASE}" >/opt/Guardian_version.txt msg_ok "Setup Guardian" -# ===== Build Backend ===== + msg_info "Building backend" cd /opt/Guardian/backend npm ci npm run build msg_ok "Built backend" -# ===== Install Frontend Dependencies ===== -msg_info "Installing frontend dependencies" +msg_info "Building frontend" cd /opt/Guardian/frontend npm ci -msg_ok "Installed frontend dependencies" +NODE_ENV=development npm run build +msg_ok "Built frontend" -# ===== Backend Service ===== msg_info "Creating Backend Service" cat </etc/systemd/system/guardian-backend.service [Unit] @@ -69,7 +66,6 @@ EOF systemctl enable -q --now guardian-backend msg_ok "Created Backend Service" -# ===== Frontend Service ===== msg_info "Creating Frontend Service" cat </etc/systemd/system/guardian-frontend.service [Unit] @@ -79,9 +75,9 @@ Wants=guardian-backend.service [Service] WorkingDirectory=/opt/Guardian/frontend -Environment=NODE_ENV=development +Environment=NODE_ENV=production Environment=PORT=3000 -ExecStart=/usr/bin/npm run dev +ExecStart=/usr/bin/npm run start Restart=always RestartSec=3 From 8076c313dfaf46c863480ba54141f5e19c168d70 Mon Sep 17 00:00:00 2001 From: Vincent <114195376+HydroshieldMKII@users.noreply.github.com> Date: Tue, 30 Sep 2025 15:43:27 -0400 Subject: [PATCH 1272/1733] Remove redundant cleanup comment from guardian-install.sh --- install/guardian-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/install/guardian-install.sh b/install/guardian-install.sh index 06d547b0d..0edaa9f83 100644 --- a/install/guardian-install.sh +++ b/install/guardian-install.sh @@ -90,7 +90,6 @@ msg_ok "Created Frontend Service" motd_ssh customize -# Cleanup msg_info "Cleaning up" rm -f "${RELEASE}".zip $STD apt-get -y autoremove From 467a81b1e8ed325b215daca77d1c3b25942d6455 Mon Sep 17 00:00:00 2001 From: Vincent <114195376+HydroshieldMKII@users.noreply.github.com> Date: Tue, 30 Sep 2025 15:56:23 -0400 Subject: [PATCH 1273/1733] Refactor Guardian installation and update process to utilize Docker Compose, streamline dependency installation, and enhance service management --- ct/guardian.sh | 45 ++++------------ install/guardian-install.sh | 101 ++++++++++++++++-------------------- 2 files changed, 55 insertions(+), 91 deletions(-) diff --git a/ct/guardian.sh b/ct/guardian.sh index 2a9a8c154..fda14f816 100644 --- a/ct/guardian.sh +++ b/ct/guardian.sh @@ -34,56 +34,33 @@ function update_script() { if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then msg_info "Stopping $APP" - systemctl stop guardian-backend guardian-frontend + cd /opt/Guardian + docker compose down msg_ok "Stopped $APP" - - msg_info "Preserving Database" - if [[ -f "/opt/Guardian/backend/plex-guard.db" ]]; then - cp "/opt/Guardian/backend/plex-guard.db" "/tmp/plex-guard.db.backup" - msg_ok "Database backed up" - fi - - msg_info "Updating $APP to v${RELEASE}" - cd /tmp + # Download new docker-compose.yml + curl -fsSL -o docker-compose.yml "https://raw.githubusercontent.com/HydroshieldMKII/Guardian/main/docker-compose.yml" - curl -fsSL -o "${RELEASE}.zip" "https://github.com/HydroshieldMKII/Guardian/archive/refs/tags/${RELEASE}.zip" - unzip -q "${RELEASE}.zip" - rm -rf /opt/Guardian - - FOLDER_NAME=$(echo "${RELEASE}" | sed 's/^v//') - mv "Guardian-${FOLDER_NAME}/" "/opt/Guardian" - - if [[ -f "/tmp/plex-guard.db.backup" ]]; then - msg_info "Restoring Database" - cp "/tmp/plex-guard.db.backup" "/opt/Guardian/backend/plex-guard.db" - rm "/tmp/plex-guard.db.backup" - msg_ok "Database restored" - fi - - cd /opt/Guardian/backend - npm ci - npm run build - - cd /opt/Guardian/frontend - npm ci - NODE_ENV=development npm run build + # Pull new Docker images + docker compose pull echo "${RELEASE}" >/opt/${APP}_version.txt msg_ok "Updated $APP to v${RELEASE}" msg_info "Starting $APP" - systemctl start guardian-backend guardian-frontend + cd /opt/Guardian + docker compose up -d msg_ok "Started $APP" msg_info "Cleaning Up" - rm -rf /tmp/"${RELEASE}.zip" /tmp/"Guardian-${FOLDER_NAME}" /tmp/plex-guard.db.backup + rm -f /tmp/plex-guard.db.backup + docker system prune -f msg_ok "Cleanup Completed" msg_ok "Update Successful" else - msg_ok "No update required. ${APP} is already at v${RELEASE}" + msg_ok "No update required. ${APP} is already at ${RELEASE}" fi exit } diff --git a/install/guardian-install.sh b/install/guardian-install.sh index 0edaa9f83..620f907e3 100644 --- a/install/guardian-install.sh +++ b/install/guardian-install.sh @@ -15,83 +15,70 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - git \ - nodejs \ - npm \ - sqlite3 \ - unzip \ - curl + curl \ + ca-certificates \ + gnupg \ + lsb-release msg_ok "Installed Dependencies" -msg_info "Setup Guardian" -RELEASE=$(curl -fsSL https://api.github.com/repos/HydroshieldMKII/Guardian/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') -curl -fsSL -o "${RELEASE}.zip" "https://github.com/HydroshieldMKII/Guardian/archive/refs/tags/${RELEASE}.zip" -unzip -q "${RELEASE}.zip" +# Install Docker +msg_info "Installing Docker" +curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg +echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null +$STD apt-get update +$STD apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin +systemctl enable --now docker +msg_ok "Installed Docker" -FOLDER_NAME=$(echo "${RELEASE}" | sed 's/^v//') -mv "Guardian-${FOLDER_NAME}/" "/opt/Guardian" +# Setup Guardian +msg_info "Setting up Guardian" +mkdir -p /opt/Guardian +cd /opt/Guardian + +# Download docker-compose.yml from repository +RELEASE=$(curl -fsSL https://api.github.com/repos/HydroshieldMKII/Guardian/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') +curl -fsSL -o docker-compose.yml "https://raw.githubusercontent.com/HydroshieldMKII/Guardian/main/docker-compose.yml" + +# Create data directory for persistent storage +mkdir -p data echo "${RELEASE}" >/opt/Guardian_version.txt msg_ok "Setup Guardian" +# Start Guardian with Docker Compose +msg_info "Starting Guardian" +cd /opt/Guardian +docker compose up -d +msg_ok "Started Guardian" -msg_info "Building backend" -cd /opt/Guardian/backend -npm ci -npm run build -msg_ok "Built backend" - -msg_info "Building frontend" -cd /opt/Guardian/frontend -npm ci -NODE_ENV=development npm run build -msg_ok "Built frontend" - -msg_info "Creating Backend Service" -cat </etc/systemd/system/guardian-backend.service +# Create systemd service to manage Docker Compose +msg_info "Creating Guardian Service" +cat </etc/systemd/system/guardian.service [Unit] -Description=Guardian Backend -After=network.target +Description=Guardian Docker Compose +Requires=docker.service +After=docker.service +Wants=network-online.target +After=network-online.target [Service] -WorkingDirectory=/opt/Guardian/backend -Environment=NODE_ENV=development -ExecStart=/usr/bin/node dist/main.js -Restart=always -RestartSec=3 +Type=oneshot +RemainAfterExit=true +WorkingDirectory=/opt/Guardian +ExecStart=/usr/bin/docker compose up -d +ExecStop=/usr/bin/docker compose down +TimeoutStartSec=0 [Install] WantedBy=multi-user.target EOF -systemctl enable -q --now guardian-backend -msg_ok "Created Backend Service" - -msg_info "Creating Frontend Service" -cat </etc/systemd/system/guardian-frontend.service -[Unit] -Description=Guardian Frontend -After=guardian-backend.service network.target -Wants=guardian-backend.service - -[Service] -WorkingDirectory=/opt/Guardian/frontend -Environment=NODE_ENV=production -Environment=PORT=3000 -ExecStart=/usr/bin/npm run start -Restart=always -RestartSec=3 - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now guardian-frontend -msg_ok "Created Frontend Service" +systemctl enable guardian +msg_ok "Created Guardian Service" motd_ssh customize msg_info "Cleaning up" -rm -f "${RELEASE}".zip $STD apt-get -y autoremove $STD apt-get -y autoclean msg_ok "Cleaned" From 544150a57a53d9401bf5fd4c5a40b66c8590c903 Mon Sep 17 00:00:00 2001 From: Vincent <114195376+HydroshieldMKII@users.noreply.github.com> Date: Tue, 30 Sep 2025 15:59:05 -0400 Subject: [PATCH 1274/1733] Update docker-compose.yml download link to use the example file for Guardian setup --- install/guardian-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/guardian-install.sh b/install/guardian-install.sh index 620f907e3..6786666a8 100644 --- a/install/guardian-install.sh +++ b/install/guardian-install.sh @@ -37,7 +37,7 @@ cd /opt/Guardian # Download docker-compose.yml from repository RELEASE=$(curl -fsSL https://api.github.com/repos/HydroshieldMKII/Guardian/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') -curl -fsSL -o docker-compose.yml "https://raw.githubusercontent.com/HydroshieldMKII/Guardian/main/docker-compose.yml" +curl -fsSL -o docker-compose.yml "https://raw.githubusercontent.com/HydroshieldMKII/Guardian/main/docker-compose.example.yml" # Create data directory for persistent storage mkdir -p data From e48f2c35a8bc971db5a0eca9dff6b36f1d15b63e Mon Sep 17 00:00:00 2001 From: Vincent <114195376+HydroshieldMKII@users.noreply.github.com> Date: Tue, 30 Sep 2025 16:10:18 -0400 Subject: [PATCH 1275/1733] Update Guardian installation and update scripts to use example docker-compose.yml and remove unnecessary comments --- ct/guardian.sh | 5 +---- install/guardian-install.sh | 8 -------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/ct/guardian.sh b/ct/guardian.sh index fda14f816..d1da21136 100644 --- a/ct/guardian.sh +++ b/ct/guardian.sh @@ -39,10 +39,8 @@ function update_script() { msg_ok "Stopped $APP" msg_info "Updating $APP to v${RELEASE}" - # Download new docker-compose.yml - curl -fsSL -o docker-compose.yml "https://raw.githubusercontent.com/HydroshieldMKII/Guardian/main/docker-compose.yml" + curl -fsSL -o docker-compose.yml "https://raw.githubusercontent.com/HydroshieldMKII/Guardian/main/docker-compose.example.yml" - # Pull new Docker images docker compose pull echo "${RELEASE}" >/opt/${APP}_version.txt @@ -54,7 +52,6 @@ function update_script() { msg_ok "Started $APP" msg_info "Cleaning Up" - rm -f /tmp/plex-guard.db.backup docker system prune -f msg_ok "Cleanup Completed" diff --git a/install/guardian-install.sh b/install/guardian-install.sh index 6786666a8..4640ed06c 100644 --- a/install/guardian-install.sh +++ b/install/guardian-install.sh @@ -21,7 +21,6 @@ $STD apt-get install -y \ lsb-release msg_ok "Installed Dependencies" -# Install Docker msg_info "Installing Docker" curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null @@ -30,28 +29,21 @@ $STD apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plu systemctl enable --now docker msg_ok "Installed Docker" -# Setup Guardian msg_info "Setting up Guardian" mkdir -p /opt/Guardian cd /opt/Guardian -# Download docker-compose.yml from repository RELEASE=$(curl -fsSL https://api.github.com/repos/HydroshieldMKII/Guardian/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') curl -fsSL -o docker-compose.yml "https://raw.githubusercontent.com/HydroshieldMKII/Guardian/main/docker-compose.example.yml" -# Create data directory for persistent storage -mkdir -p data - echo "${RELEASE}" >/opt/Guardian_version.txt msg_ok "Setup Guardian" -# Start Guardian with Docker Compose msg_info "Starting Guardian" cd /opt/Guardian docker compose up -d msg_ok "Started Guardian" -# Create systemd service to manage Docker Compose msg_info "Creating Guardian Service" cat </etc/systemd/system/guardian.service [Unit] From a4420750bdb1dbd02503b83c739554e9d3842b71 Mon Sep 17 00:00:00 2001 From: Vincent <114195376+HydroshieldMKII@users.noreply.github.com> Date: Tue, 30 Sep 2025 16:20:09 -0400 Subject: [PATCH 1276/1733] Update URLs in build.func and install.func to use community-scripts repository --- misc/build.func | 22 +++++++++++----------- misc/install.func | 10 +++++----- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/misc/build.func b/misc/build.func index 7169b3545..d6b0af725 100644 --- a/misc/build.func +++ b/misc/build.func @@ -79,7 +79,7 @@ variables() { # [Dd]*) # for file in core.func error_handler.func tools.func; do # local_path="$FUNC_DIR/$file" -# url="https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/$file" +# url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/$file" # remote_tmp="$(mktemp)" # curl -fsSL "$url" -o "$remote_tmp" || continue @@ -128,17 +128,17 @@ variables() { # - Initialize error traps after loading # ------------------------------------------------------------------------------ -source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/api.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) if command -v curl >/dev/null 2>&1; then - source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/core.func) - source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/error_handler.func) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) load_functions catch_errors #echo "(build.func) Loaded core.func via curl" elif command -v wget >/dev/null 2>&1; then - source <(wget -qO- https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/core.func) - source <(wget -qO- https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/error_handler.func) + source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) load_functions catch_errors #echo "(build.func) Loaded core.func via wget" @@ -2062,7 +2062,7 @@ ssh_discover_default_files() { # - Otherwise: shows update/setting menu # ------------------------------------------------------------------------------ start() { - source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/tools.func) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) if command -v pveversion >/dev/null 2>&1; then install_script || return 0 return 0 @@ -2141,9 +2141,9 @@ build_container() { TEMP_DIR=$(mktemp -d) pushd "$TEMP_DIR" >/dev/null if [ "$var_os" == "alpine" ]; then - export FUNCTIONS_FILE_PATH="$(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/alpine-install.func)" + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-install.func)" else - export FUNCTIONS_FILE_PATH="$(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/install.func)" + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" fi export DIAGNOSTICS="$DIAGNOSTICS" export RANDOM_UUID="$RANDOM_UUID" @@ -2183,7 +2183,7 @@ build_container() { LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" - source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/passthrough.func) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/passthrough.func) select_hw_passthrough "$CTID" "$CT_TYPE" "$APP" # TUN device passthrough @@ -2295,7 +2295,7 @@ EOF' fi msg_ok "Customized LXC Container" install_ssh_keys_into_ct - if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/install/${var_install}.sh)"; then + if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)"; then exit $? fi } diff --git a/misc/install.func b/misc/install.func index 2ebc8e2e8..f741b921d 100644 --- a/misc/install.func +++ b/misc/install.func @@ -9,8 +9,8 @@ if ! command -v curl >/dev/null 2>&1; then apt-get update >/dev/null 2>&1 apt-get install -y curl >/dev/null 2>&1 fi -source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/core.func) -source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/error_handler.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) load_functions catch_errors @@ -32,7 +32,7 @@ verb_ip6() { # # This function handles errors # error_handler() { -# source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/api.func) +# source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/api.func) # local exit_code="$1" # local line_number="$2" # local command="${3:-}" @@ -147,7 +147,7 @@ EOF $STD apt-get -o Dpkg::Options::="--force-confold" -y dist-upgrade rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED msg_ok "Updated Container OS" - source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/tools.func) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) } # This function modifies the message of the day (motd) and SSH settings @@ -195,7 +195,7 @@ EOF systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//') msg_ok "Customized Container" fi - echo "bash -c \"\$(curl -fsSL https://github.com/HydroshieldMKII/ProxmoxVED/raw/add-guardian-app/ct/${app}.sh)\"" >/usr/bin/update + echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update chmod +x /usr/bin/update if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then mkdir -p /root/.ssh From 385931094c2a5d923b06113420cb5d3555788f3e Mon Sep 17 00:00:00 2001 From: Vincent <114195376+HydroshieldMKII@users.noreply.github.com> Date: Tue, 30 Sep 2025 16:38:39 -0400 Subject: [PATCH 1277/1733] Refactor Guardian installation script to remove Docker installation, streamline dependency setup, and enhance service management for backend and frontend --- install/guardian-install.sh | 93 +++++++++++++++++++++++-------------- 1 file changed, 57 insertions(+), 36 deletions(-) diff --git a/install/guardian-install.sh b/install/guardian-install.sh index 4640ed06c..0edaa9f83 100644 --- a/install/guardian-install.sh +++ b/install/guardian-install.sh @@ -15,62 +15,83 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - curl \ - ca-certificates \ - gnupg \ - lsb-release + git \ + nodejs \ + npm \ + sqlite3 \ + unzip \ + curl msg_ok "Installed Dependencies" -msg_info "Installing Docker" -curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg -echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null -$STD apt-get update -$STD apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin -systemctl enable --now docker -msg_ok "Installed Docker" - -msg_info "Setting up Guardian" -mkdir -p /opt/Guardian -cd /opt/Guardian - +msg_info "Setup Guardian" RELEASE=$(curl -fsSL https://api.github.com/repos/HydroshieldMKII/Guardian/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') -curl -fsSL -o docker-compose.yml "https://raw.githubusercontent.com/HydroshieldMKII/Guardian/main/docker-compose.example.yml" +curl -fsSL -o "${RELEASE}.zip" "https://github.com/HydroshieldMKII/Guardian/archive/refs/tags/${RELEASE}.zip" +unzip -q "${RELEASE}.zip" + +FOLDER_NAME=$(echo "${RELEASE}" | sed 's/^v//') +mv "Guardian-${FOLDER_NAME}/" "/opt/Guardian" echo "${RELEASE}" >/opt/Guardian_version.txt msg_ok "Setup Guardian" -msg_info "Starting Guardian" -cd /opt/Guardian -docker compose up -d -msg_ok "Started Guardian" -msg_info "Creating Guardian Service" -cat </etc/systemd/system/guardian.service +msg_info "Building backend" +cd /opt/Guardian/backend +npm ci +npm run build +msg_ok "Built backend" + +msg_info "Building frontend" +cd /opt/Guardian/frontend +npm ci +NODE_ENV=development npm run build +msg_ok "Built frontend" + +msg_info "Creating Backend Service" +cat </etc/systemd/system/guardian-backend.service [Unit] -Description=Guardian Docker Compose -Requires=docker.service -After=docker.service -Wants=network-online.target -After=network-online.target +Description=Guardian Backend +After=network.target [Service] -Type=oneshot -RemainAfterExit=true -WorkingDirectory=/opt/Guardian -ExecStart=/usr/bin/docker compose up -d -ExecStop=/usr/bin/docker compose down -TimeoutStartSec=0 +WorkingDirectory=/opt/Guardian/backend +Environment=NODE_ENV=development +ExecStart=/usr/bin/node dist/main.js +Restart=always +RestartSec=3 [Install] WantedBy=multi-user.target EOF -systemctl enable guardian -msg_ok "Created Guardian Service" +systemctl enable -q --now guardian-backend +msg_ok "Created Backend Service" + +msg_info "Creating Frontend Service" +cat </etc/systemd/system/guardian-frontend.service +[Unit] +Description=Guardian Frontend +After=guardian-backend.service network.target +Wants=guardian-backend.service + +[Service] +WorkingDirectory=/opt/Guardian/frontend +Environment=NODE_ENV=production +Environment=PORT=3000 +ExecStart=/usr/bin/npm run start +Restart=always +RestartSec=3 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now guardian-frontend +msg_ok "Created Frontend Service" motd_ssh customize msg_info "Cleaning up" +rm -f "${RELEASE}".zip $STD apt-get -y autoremove $STD apt-get -y autoclean msg_ok "Cleaned" From d30d51209137079c3851383db52edac5cde8be86 Mon Sep 17 00:00:00 2001 From: Vincent <114195376+HydroshieldMKII@users.noreply.github.com> Date: Tue, 30 Sep 2025 16:40:35 -0400 Subject: [PATCH 1278/1733] Update installation scripts to use HydroshieldMKII repository for Guardian app setup --- misc/build.func | 2 +- misc/install.func | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/misc/build.func b/misc/build.func index d6b0af725..689ef4e6a 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2295,7 +2295,7 @@ EOF' fi msg_ok "Customized LXC Container" install_ssh_keys_into_ct - if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)"; then + if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/install/${var_install}.sh)"; then exit $? fi } diff --git a/misc/install.func b/misc/install.func index f741b921d..030031e95 100644 --- a/misc/install.func +++ b/misc/install.func @@ -195,7 +195,7 @@ EOF systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//') msg_ok "Customized Container" fi - echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update + echo "bash -c \"\$(curl -fsSL https://github.com/HydroshieldMKII/ProxmoxVED/raw/add-guardian-app/ct/${app}.sh)\"" >/usr/bin/update chmod +x /usr/bin/update if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then mkdir -p /root/.ssh From e6fafe73252eabbfe3a2e386c72d6939048d2243 Mon Sep 17 00:00:00 2001 From: Enzo Legrand Date: Tue, 30 Sep 2025 22:48:32 +0200 Subject: [PATCH 1279/1733] fix: change the build.func path --- ct/sonarqube.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/sonarqube.sh b/ct/sonarqube.sh index 74761e50a..dcd4e37b7 100644 --- a/ct/sonarqube.sh +++ b/ct/sonarqube.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://raw.githubusercontent.com/prop4n/ProxmoxVED/refs/heads/add-script-sonarqube/misc/build.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: prop4n # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE From f6a8eed717c801259022867c99f872573a9a0b6a Mon Sep 17 00:00:00 2001 From: HydroshieldMKII Date: Tue, 30 Sep 2025 17:04:50 -0400 Subject: [PATCH 1280/1733] Reduce CPU allocation to 2 for Guardian installation and update environment variables for production deployment --- frontend/public/json/guardian.json | 2 +- install/guardian-install.sh | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/frontend/public/json/guardian.json b/frontend/public/json/guardian.json index 4bc9f6dc0..57381efdd 100644 --- a/frontend/public/json/guardian.json +++ b/frontend/public/json/guardian.json @@ -17,7 +17,7 @@ "type": "default", "script": "ct/guardian.sh", "resources": { - "cpu": 4, + "cpu": 2, "ram": 2048, "hdd": 6, "os": "Debian", diff --git a/install/guardian-install.sh b/install/guardian-install.sh index 0edaa9f83..ba902914b 100644 --- a/install/guardian-install.sh +++ b/install/guardian-install.sh @@ -44,7 +44,7 @@ msg_ok "Built backend" msg_info "Building frontend" cd /opt/Guardian/frontend npm ci -NODE_ENV=development npm run build +npm run build msg_ok "Built frontend" msg_info "Creating Backend Service" @@ -55,7 +55,7 @@ After=network.target [Service] WorkingDirectory=/opt/Guardian/backend -Environment=NODE_ENV=development +Environment=NODE_ENV=production ExecStart=/usr/bin/node dist/main.js Restart=always RestartSec=3 @@ -76,6 +76,7 @@ Wants=guardian-backend.service [Service] WorkingDirectory=/opt/Guardian/frontend Environment=NODE_ENV=production +Environment=DEPLOYMENT_MODE=standalone Environment=PORT=3000 ExecStart=/usr/bin/npm run start Restart=always From fa4e953f72e401dfdc912e9eb8843bc77ee51ca0 Mon Sep 17 00:00:00 2001 From: HydroshieldMKII Date: Tue, 30 Sep 2025 17:12:48 -0400 Subject: [PATCH 1281/1733] Update guardian-install.sh to set DEPLOYMENT_MODE for frontend build --- ct/guardian.sh | 0 install/guardian-install.sh | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) mode change 100644 => 100755 ct/guardian.sh diff --git a/ct/guardian.sh b/ct/guardian.sh old mode 100644 new mode 100755 diff --git a/install/guardian-install.sh b/install/guardian-install.sh index ba902914b..fd697cf13 100644 --- a/install/guardian-install.sh +++ b/install/guardian-install.sh @@ -44,7 +44,7 @@ msg_ok "Built backend" msg_info "Building frontend" cd /opt/Guardian/frontend npm ci -npm run build +DEPLOYMENT_MODE=standalone npm run build msg_ok "Built frontend" msg_info "Creating Backend Service" From a533865a80261af359da4b840a544af9f81a0031 Mon Sep 17 00:00:00 2001 From: HydroshieldMKII Date: Tue, 30 Sep 2025 17:13:02 -0400 Subject: [PATCH 1282/1733] Remove DEPLOYMENT_MODE environment variable from frontend service configuration --- install/guardian-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/install/guardian-install.sh b/install/guardian-install.sh index fd697cf13..747c6de8e 100644 --- a/install/guardian-install.sh +++ b/install/guardian-install.sh @@ -76,7 +76,6 @@ Wants=guardian-backend.service [Service] WorkingDirectory=/opt/Guardian/frontend Environment=NODE_ENV=production -Environment=DEPLOYMENT_MODE=standalone Environment=PORT=3000 ExecStart=/usr/bin/npm run start Restart=always From a4cc08a194c775180ee05be9e9ae7f017b06e6f3 Mon Sep 17 00:00:00 2001 From: HydroshieldMKII Date: Tue, 30 Sep 2025 17:13:29 -0400 Subject: [PATCH 1283/1733] Remove NODE_ENV and PORT environment variables from frontend and backend service configurations --- install/guardian-install.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/install/guardian-install.sh b/install/guardian-install.sh index 747c6de8e..876260cc2 100644 --- a/install/guardian-install.sh +++ b/install/guardian-install.sh @@ -55,7 +55,6 @@ After=network.target [Service] WorkingDirectory=/opt/Guardian/backend -Environment=NODE_ENV=production ExecStart=/usr/bin/node dist/main.js Restart=always RestartSec=3 @@ -75,8 +74,6 @@ Wants=guardian-backend.service [Service] WorkingDirectory=/opt/Guardian/frontend -Environment=NODE_ENV=production -Environment=PORT=3000 ExecStart=/usr/bin/npm run start Restart=always RestartSec=3 From e9d9d03aa8b24634fad91241cf32c0fd75e1e61f Mon Sep 17 00:00:00 2001 From: HydroshieldMKII Date: Tue, 30 Sep 2025 17:15:40 -0400 Subject: [PATCH 1284/1733] Refactor guardian-install.sh to streamline dependency installation by removing npm and ensuring proper package installation order --- install/guardian-install.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/install/guardian-install.sh b/install/guardian-install.sh index 876260cc2..eb314f21f 100644 --- a/install/guardian-install.sh +++ b/install/guardian-install.sh @@ -16,11 +16,14 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ git \ - nodejs \ - npm \ sqlite3 \ unzip \ - curl + curl \ + ca-certificates \ + gnupg + +curl -fsSL https://deb.nodesource.com/setup_24.x | bash - +$STD apt-get install -y nodejs msg_ok "Installed Dependencies" msg_info "Setup Guardian" From a63e8e58f3ada3fb8a892b622b66a0b733137337 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Tue, 30 Sep 2025 23:18:11 +0200 Subject: [PATCH 1285/1733] Update Notesnook --- install/notesnook-install.sh | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/install/notesnook-install.sh b/install/notesnook-install.sh index cedb85cad..dbc93cbf8 100644 --- a/install/notesnook-install.sh +++ b/install/notesnook-install.sh @@ -20,8 +20,7 @@ $STD apt-get install -y \ caddy msg_ok "Installed Dependencies" -LOCAL_IP=$(hostname -I | awk '{print $1}') -NODE_MODULE="yarn" setup_nodejs +NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "notesnook" "streetwriters/notesnook" "tarball" msg_info "Configuring Notesnook (Patience)" @@ -31,6 +30,19 @@ $STD npm install $STD npm run build:web msg_ok "Configured Notesnook" +msg_info "Configuring Caddy" +LOCAL_IP=$(hostname -I | awk '{print $1}') +cat </etc/caddy/Caddyfile +{ + email admin@example.com +} + +${LOCAL_IP} { + reverse_proxy 127.0.0.1:3000 +} +EOF +msg_ok "Configured Caddy" + msg_info "Creating Service" cat </etc/systemd/system/notesnook.service [Unit] @@ -41,16 +53,13 @@ After=network-online.target Type=simple User=root WorkingDirectory=/opt/notesnook -ExecStart=/usr/bin/npx serve -l tcp://0.0.0.0:3000 apps/web/build +ExecStart=/usr/bin/npx serve apps/web/build Restart=on-failure [Install] WantedBy=multi-user.target EOF -sed -i "s|^ExecStart=.*|ExecStart=/usr/bin/caddy reverse-proxy --from https://$LOCAL_IP --to localhost:3000|" /lib/systemd/system/caddy.service -sed -i "/^ExecReload=/d" /lib/systemd/system/caddy.service -systemctl daemon-reload -systemctl restart caddy +systemctl reload caddy systemctl enable -q --now notesnook msg_ok "Created Service" From b4646fbd78a52193f61ca50559c1ca76891f2a84 Mon Sep 17 00:00:00 2001 From: HydroshieldMKII Date: Tue, 30 Sep 2025 17:25:41 -0400 Subject: [PATCH 1286/1733] Refactor guardian-install.sh to simplify dependency installation and ensure DEPLOYMENT_MODE is set for frontend service --- install/guardian-install.sh | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/install/guardian-install.sh b/install/guardian-install.sh index eb314f21f..ad623502c 100644 --- a/install/guardian-install.sh +++ b/install/guardian-install.sh @@ -16,14 +16,11 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ git \ + nodejs \ + npm \ sqlite3 \ unzip \ - curl \ - ca-certificates \ - gnupg - -curl -fsSL https://deb.nodesource.com/setup_24.x | bash - -$STD apt-get install -y nodejs + curl msg_ok "Installed Dependencies" msg_info "Setup Guardian" @@ -77,6 +74,7 @@ Wants=guardian-backend.service [Service] WorkingDirectory=/opt/Guardian/frontend +Environment=DEPLOYMENT_MODE=standalone ExecStart=/usr/bin/npm run start Restart=always RestartSec=3 From aa2649a9337e6cb08d4fd5363024b730b2016316 Mon Sep 17 00:00:00 2001 From: HydroshieldMKII Date: Tue, 30 Sep 2025 17:34:25 -0400 Subject: [PATCH 1287/1733] Refactor guardian-install.sh to remove npm installation and ensure proper dependency setup --- install/guardian-install.sh | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/install/guardian-install.sh b/install/guardian-install.sh index ad623502c..de05a8e88 100644 --- a/install/guardian-install.sh +++ b/install/guardian-install.sh @@ -16,11 +16,15 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ git \ - nodejs \ - npm \ sqlite3 \ unzip \ - curl + curl \ + ca-certificates \ + gnupg + +curl -fsSL https://deb.nodesource.com/setup_24.x | bash - +$STD apt-get install -y nodejs + msg_ok "Installed Dependencies" msg_info "Setup Guardian" From e8e630c8417dd64e135af736b6927ca06e73ead3 Mon Sep 17 00:00:00 2001 From: HydroshieldMKII Date: Tue, 30 Sep 2025 17:41:22 -0400 Subject: [PATCH 1288/1733] Refactor update_script function to replace Docker commands with systemctl for service management and enhance database backup/restore process --- ct/guardian.sh | 44 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/ct/guardian.sh b/ct/guardian.sh index d1da21136..4b67897b7 100755 --- a/ct/guardian.sh +++ b/ct/guardian.sh @@ -34,30 +34,56 @@ function update_script() { if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then msg_info "Stopping $APP" - cd /opt/Guardian - docker compose down + systemctl stop guardian-backend guardian-frontend msg_ok "Stopped $APP" - msg_info "Updating $APP to v${RELEASE}" - curl -fsSL -o docker-compose.yml "https://raw.githubusercontent.com/HydroshieldMKII/Guardian/main/docker-compose.example.yml" - docker compose pull + msg_info "Saving Database" + if [[ -f "/opt/Guardian/backend/plex-guard.db" ]]; then + cp "/opt/Guardian/backend/plex-guard.db" "/tmp/plex-guard.db.backup" + msg_ok "Database backed up" + fi + + + msg_info "Updating $APP to v${RELEASE}" + cd /tmp + + curl -fsSL -o "${RELEASE}.zip" "https://github.com/HydroshieldMKII/Guardian/archive/refs/tags/${RELEASE}.zip" + unzip -q "${RELEASE}.zip" + rm -rf /opt/Guardian + + FOLDER_NAME=$(echo "${RELEASE}" | sed 's/^v//') + mv "Guardian-${FOLDER_NAME}/" "/opt/Guardian" + + if [[ -f "/tmp/plex-guard.db.backup" ]]; then + msg_info "Restoring Database" + cp "/tmp/plex-guard.db.backup" "/opt/Guardian/backend/plex-guard.db" + rm "/tmp/plex-guard.db.backup" + msg_ok "Database restored" + fi + + cd /opt/Guardian/backend + npm ci + npm run build + + cd /opt/Guardian/frontend + npm ci + DEPLOYMENT_MODE=standalone npm run build echo "${RELEASE}" >/opt/${APP}_version.txt msg_ok "Updated $APP to v${RELEASE}" msg_info "Starting $APP" - cd /opt/Guardian - docker compose up -d + systemctl start guardian-backend guardian-frontend msg_ok "Started $APP" msg_info "Cleaning Up" - docker system prune -f + rm -rf /tmp/"${RELEASE}.zip" /tmp/"Guardian-${FOLDER_NAME}" /tmp/plex-guard.db.backup msg_ok "Cleanup Completed" msg_ok "Update Successful" else - msg_ok "No update required. ${APP} is already at ${RELEASE}" + msg_ok "No update required. ${APP} is already at v${RELEASE}" fi exit } From 462c71c4578de32614332c5533b89b6b45dae34d Mon Sep 17 00:00:00 2001 From: HydroshieldMKII Date: Tue, 30 Sep 2025 18:04:40 -0400 Subject: [PATCH 1289/1733] Update script to source functions from new repository location for consistency --- misc/build.func | 14 +++++++------- misc/install.func | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/misc/build.func b/misc/build.func index 689ef4e6a..fd9f0092a 100644 --- a/misc/build.func +++ b/misc/build.func @@ -128,17 +128,17 @@ variables() { # - Initialize error traps after loading # ------------------------------------------------------------------------------ -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) +source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/api.func) if command -v curl >/dev/null 2>&1; then - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) + source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/core.func) + source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/error_handler.func) load_functions catch_errors #echo "(build.func) Loaded core.func via curl" elif command -v wget >/dev/null 2>&1; then - source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) - source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) + source <(wget -qO- https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/core.func) + source <(wget -qO- https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/error_handler.func) load_functions catch_errors #echo "(build.func) Loaded core.func via wget" @@ -2062,7 +2062,7 @@ ssh_discover_default_files() { # - Otherwise: shows update/setting menu # ------------------------------------------------------------------------------ start() { - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) + source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/tools.func) if command -v pveversion >/dev/null 2>&1; then install_script || return 0 return 0 @@ -2183,7 +2183,7 @@ build_container() { LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/passthrough.func) + source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/passthrough.func) select_hw_passthrough "$CTID" "$CT_TYPE" "$APP" # TUN device passthrough diff --git a/misc/install.func b/misc/install.func index 030031e95..c63ad7a0a 100644 --- a/misc/install.func +++ b/misc/install.func @@ -9,8 +9,8 @@ if ! command -v curl >/dev/null 2>&1; then apt-get update >/dev/null 2>&1 apt-get install -y curl >/dev/null 2>&1 fi -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) +source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/core.func) +source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/error_handler.func) load_functions catch_errors @@ -147,7 +147,7 @@ EOF $STD apt-get -o Dpkg::Options::="--force-confold" -y dist-upgrade rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED msg_ok "Updated Container OS" - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) + source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/tools.func) } # This function modifies the message of the day (motd) and SSH settings From 9b1c5d4af004ac54e139bc04ae910ffcd4229f5e Mon Sep 17 00:00:00 2001 From: HydroshieldMKII Date: Tue, 30 Sep 2025 18:11:59 -0400 Subject: [PATCH 1290/1733] Fix update message to display version without 'v' prefix --- ct/guardian.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/guardian.sh b/ct/guardian.sh index 4b67897b7..d14a29818 100755 --- a/ct/guardian.sh +++ b/ct/guardian.sh @@ -83,7 +83,7 @@ function update_script() { msg_ok "Update Successful" else - msg_ok "No update required. ${APP} is already at v${RELEASE}" + msg_ok "No update required. ${APP} is already at ${RELEASE}" fi exit } From 12659a9357fc49e34dd9fd19447ec7ad84f5c1ff Mon Sep 17 00:00:00 2001 From: HydroshieldMKII Date: Tue, 30 Sep 2025 18:13:20 -0400 Subject: [PATCH 1291/1733] Fix update message to display version without 'v' prefix --- ct/guardian.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/guardian.sh b/ct/guardian.sh index d14a29818..a1078f546 100755 --- a/ct/guardian.sh +++ b/ct/guardian.sh @@ -45,7 +45,7 @@ function update_script() { fi - msg_info "Updating $APP to v${RELEASE}" + msg_info "Updating $APP to ${RELEASE}" cd /tmp curl -fsSL -o "${RELEASE}.zip" "https://github.com/HydroshieldMKII/Guardian/archive/refs/tags/${RELEASE}.zip" From d8b1ee6c73d040eee55bd7d0b8ee451ce3d8884b Mon Sep 17 00:00:00 2001 From: HydroshieldMKII Date: Tue, 30 Sep 2025 18:15:22 -0400 Subject: [PATCH 1292/1733] Update guardian.sh to source build functions from the correct repository location --- ct/guardian.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/guardian.sh b/ct/guardian.sh index a1078f546..07203be27 100755 --- a/ct/guardian.sh +++ b/ct/guardian.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: HydroshieldMKII # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE From d7a1ccd59fd9ecb02c389c13c113852535951b00 Mon Sep 17 00:00:00 2001 From: HydroshieldMKII Date: Tue, 30 Sep 2025 18:20:46 -0400 Subject: [PATCH 1293/1733] Update build and install scripts to source functions from the main community repository --- misc/build.func | 16 ++++++++-------- misc/install.func | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/misc/build.func b/misc/build.func index fd9f0092a..5ada1a46f 100644 --- a/misc/build.func +++ b/misc/build.func @@ -128,17 +128,17 @@ variables() { # - Initialize error traps after loading # ------------------------------------------------------------------------------ -source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/api.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) if command -v curl >/dev/null 2>&1; then - source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/core.func) - source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/error_handler.func) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) load_functions catch_errors #echo "(build.func) Loaded core.func via curl" elif command -v wget >/dev/null 2>&1; then - source <(wget -qO- https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/core.func) - source <(wget -qO- https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/error_handler.func) + source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) load_functions catch_errors #echo "(build.func) Loaded core.func via wget" @@ -2062,7 +2062,7 @@ ssh_discover_default_files() { # - Otherwise: shows update/setting menu # ------------------------------------------------------------------------------ start() { - source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/tools.func) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) if command -v pveversion >/dev/null 2>&1; then install_script || return 0 return 0 @@ -2183,7 +2183,7 @@ build_container() { LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" - source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/passthrough.func) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/passthrough.func) select_hw_passthrough "$CTID" "$CT_TYPE" "$APP" # TUN device passthrough @@ -2295,7 +2295,7 @@ EOF' fi msg_ok "Customized LXC Container" install_ssh_keys_into_ct - if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/install/${var_install}.sh)"; then + if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/install/${var_install}.sh)"; then exit $? fi } diff --git a/misc/install.func b/misc/install.func index c63ad7a0a..f741b921d 100644 --- a/misc/install.func +++ b/misc/install.func @@ -9,8 +9,8 @@ if ! command -v curl >/dev/null 2>&1; then apt-get update >/dev/null 2>&1 apt-get install -y curl >/dev/null 2>&1 fi -source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/core.func) -source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/error_handler.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) load_functions catch_errors @@ -147,7 +147,7 @@ EOF $STD apt-get -o Dpkg::Options::="--force-confold" -y dist-upgrade rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED msg_ok "Updated Container OS" - source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/tools.func) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) } # This function modifies the message of the day (motd) and SSH settings @@ -195,7 +195,7 @@ EOF systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//') msg_ok "Customized Container" fi - echo "bash -c \"\$(curl -fsSL https://github.com/HydroshieldMKII/ProxmoxVED/raw/add-guardian-app/ct/${app}.sh)\"" >/usr/bin/update + echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update chmod +x /usr/bin/update if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then mkdir -p /root/.ssh From 64d22d0048e59dcad7f1300ecb2cc26196d53a0a Mon Sep 17 00:00:00 2001 From: Vincent <114195376+HydroshieldMKII@users.noreply.github.com> Date: Tue, 30 Sep 2025 18:23:48 -0400 Subject: [PATCH 1294/1733] Update script URL for LXC container installation --- misc/build.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index 5ada1a46f..d6b0af725 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2295,7 +2295,7 @@ EOF' fi msg_ok "Customized LXC Container" install_ssh_keys_into_ct - if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/install/${var_install}.sh)"; then + if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)"; then exit $? fi } From 81e0af933ad6c41493ee2904ed0792413f235236 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Wed, 1 Oct 2025 00:35:31 +0200 Subject: [PATCH 1295/1733] Update frontend/public/json/sonarqube.json --- frontend/public/json/sonarqube.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/sonarqube.json b/frontend/public/json/sonarqube.json index c86f2240e..d3a822f36 100644 --- a/frontend/public/json/sonarqube.json +++ b/frontend/public/json/sonarqube.json @@ -12,7 +12,7 @@ "interface_port": 9000, "documentation": "https://docs.sonarsource.com/sonarqube-server", "config_path": "/opt/sonarqube/conf/sonar.properties", - "website": null, + "website": "https://www.sonarsource.com/products/sonarqube/", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/sonarqube.webp", "description": "SonarQube Server automates code quality and security reviews and provides actionable code intelligence so developers can focus on building better, faster.", "install_methods": [ From d845799c05f167969df121d3df8042eb3f38d12b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Wed, 1 Oct 2025 00:35:39 +0200 Subject: [PATCH 1296/1733] Update install/sonarqube-install.sh --- install/sonarqube-install.sh | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/install/sonarqube-install.sh b/install/sonarqube-install.sh index eb9c5de29..7b6003b0b 100644 --- a/install/sonarqube-install.sh +++ b/install/sonarqube-install.sh @@ -12,11 +12,7 @@ setting_up_container network_check update_os -msg_info "Installing Dependencies (Patience)" -$STD apt-get install -y openjdk-17-jdk curl unzip -msg_ok "Installed Dependencies" - -RELEASE=$(curl -s https://api.github.com/repos/SonarSource/sonarqube/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') +JAVA_VERSION=21 setup_java PG_VERSION="17" setup_postgresql msg_info "Installing Postgresql" From 23c01311868df4f304c9f40a943deff507ad0ed6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Wed, 1 Oct 2025 00:35:47 +0200 Subject: [PATCH 1297/1733] Update install/sonarqube-install.sh --- install/sonarqube-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/sonarqube-install.sh b/install/sonarqube-install.sh index 7b6003b0b..bb068f6f0 100644 --- a/install/sonarqube-install.sh +++ b/install/sonarqube-install.sh @@ -33,7 +33,7 @@ msg_ok "Installed PostgreSQL" msg_info "Creating SonarQube User" useradd -r -m -U -d /opt/sonarqube -s /bin/bash sonarqube 2>/dev/null || true -msg_info "SonarQube Installation" +msg_info "Configuring SonarQube" curl -fsSL -o /tmp/sonarqube.zip "https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-${RELEASE}.zip" unzip -q /tmp/sonarqube.zip -d /tmp cp -r /tmp/sonarqube-*/* /opt/sonarqube/ From 5811b45b6338a0eab279671772191a75ff007c5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Wed, 1 Oct 2025 00:35:56 +0200 Subject: [PATCH 1298/1733] Update install/sonarqube-install.sh --- install/sonarqube-install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install/sonarqube-install.sh b/install/sonarqube-install.sh index bb068f6f0..80b4e055d 100644 --- a/install/sonarqube-install.sh +++ b/install/sonarqube-install.sh @@ -14,6 +14,7 @@ update_os JAVA_VERSION=21 setup_java PG_VERSION="17" setup_postgresql +fetch_and_deploy_gh_release "sonarqube" "SonarSource/sonarqube" "tarball" msg_info "Installing Postgresql" DB_NAME="sonarqube" From ca9d0ab99672c89b6404fef9669f21564d8b3066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Wed, 1 Oct 2025 00:36:03 +0200 Subject: [PATCH 1299/1733] Update install/sonarqube-install.sh --- install/sonarqube-install.sh | 4 ---- 1 file changed, 4 deletions(-) diff --git a/install/sonarqube-install.sh b/install/sonarqube-install.sh index 80b4e055d..6d578f47f 100644 --- a/install/sonarqube-install.sh +++ b/install/sonarqube-install.sh @@ -35,10 +35,6 @@ msg_info "Creating SonarQube User" useradd -r -m -U -d /opt/sonarqube -s /bin/bash sonarqube 2>/dev/null || true msg_info "Configuring SonarQube" -curl -fsSL -o /tmp/sonarqube.zip "https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-${RELEASE}.zip" -unzip -q /tmp/sonarqube.zip -d /tmp -cp -r /tmp/sonarqube-*/* /opt/sonarqube/ -rm -rf /tmp/sonarqube* chown -R sonarqube:sonarqube /opt/sonarqube chmod -R 755 /opt/sonarqube msg_ok "Installed SonarQube" From 674fc838e993007526b07cfb0ecd6cda94b0cc42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Wed, 1 Oct 2025 00:36:11 +0200 Subject: [PATCH 1300/1733] Update install/sonarqube-install.sh --- install/sonarqube-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/sonarqube-install.sh b/install/sonarqube-install.sh index 6d578f47f..4be1ce319 100644 --- a/install/sonarqube-install.sh +++ b/install/sonarqube-install.sh @@ -52,7 +52,7 @@ chmod +x /opt/sonarqube/bin/linux-x86-64/sonar.sh msg_ok "Configured SonarQube" msg_info "Creating Systemd Service" -cat >/etc/systemd/system/sonarqube.service </etc/systemd/system/sonarqube.service [Unit] Description=SonarQube service After=postgresql.service From 8d09af7b4e2d1434384f64e4f217f050fc80541d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Wed, 1 Oct 2025 00:36:19 +0200 Subject: [PATCH 1301/1733] Update install/sonarqube-install.sh --- install/sonarqube-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/install/sonarqube-install.sh b/install/sonarqube-install.sh index 4be1ce319..030810af0 100644 --- a/install/sonarqube-install.sh +++ b/install/sonarqube-install.sh @@ -70,7 +70,6 @@ LimitNPROC=8192 [Install] WantedBy=multi-user.target EOF -$STD systemctl daemon-reload msg_ok "Created Systemd Service" msg_info "Starting SonarQube service" From 14ae50541721fd6372d1cd93317fd5e9cd417bc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Wed, 1 Oct 2025 00:36:27 +0200 Subject: [PATCH 1302/1733] Update install/sonarqube-install.sh --- install/sonarqube-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/sonarqube-install.sh b/install/sonarqube-install.sh index 030810af0..c2eb34891 100644 --- a/install/sonarqube-install.sh +++ b/install/sonarqube-install.sh @@ -51,7 +51,7 @@ EOF chmod +x /opt/sonarqube/bin/linux-x86-64/sonar.sh msg_ok "Configured SonarQube" -msg_info "Creating Systemd Service" +msg_info "Creating Service" cat </etc/systemd/system/sonarqube.service [Unit] Description=SonarQube service From de5c3289c949d42dc4a6bbff1f29748a613dd731 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Wed, 1 Oct 2025 00:36:33 +0200 Subject: [PATCH 1303/1733] Update install/sonarqube-install.sh --- install/sonarqube-install.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/install/sonarqube-install.sh b/install/sonarqube-install.sh index c2eb34891..db4cc3107 100644 --- a/install/sonarqube-install.sh +++ b/install/sonarqube-install.sh @@ -70,9 +70,6 @@ LimitNPROC=8192 [Install] WantedBy=multi-user.target EOF -msg_ok "Created Systemd Service" - -msg_info "Starting SonarQube service" $STD systemctl enable --now sonarqube msg_ok "SonarQube service started" From c409d58b952925d7fbc9c308e3fce771364f5480 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Wed, 1 Oct 2025 00:36:40 +0200 Subject: [PATCH 1304/1733] Update install/sonarqube-install.sh --- install/sonarqube-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/sonarqube-install.sh b/install/sonarqube-install.sh index db4cc3107..0a199c9e9 100644 --- a/install/sonarqube-install.sh +++ b/install/sonarqube-install.sh @@ -71,7 +71,7 @@ LimitNPROC=8192 WantedBy=multi-user.target EOF $STD systemctl enable --now sonarqube -msg_ok "SonarQube service started" +msg_ok "Service Created" motd_ssh customize From 5b63249087f3d084ff14326d1d514400410aecf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Wed, 1 Oct 2025 00:36:47 +0200 Subject: [PATCH 1305/1733] Update ct/sonarqube.sh --- ct/sonarqube.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ct/sonarqube.sh b/ct/sonarqube.sh index dcd4e37b7..27a4ae05a 100644 --- a/ct/sonarqube.sh +++ b/ct/sonarqube.sh @@ -28,8 +28,7 @@ function update_script() { exit fi - RELEASE=$(curl -s https://api.github.com/repos/SonarSource/sonarqube/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') - if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then + if check_for_gh_release "sonarqube" "SonarSource/sonarqube"; then msg_info "Updating ${APP} to v${RELEASE}" systemctl stop sonarqube From 9de0b87e1a96860da0a90a6832f4dd6a9a9e6ff5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Wed, 1 Oct 2025 00:36:54 +0200 Subject: [PATCH 1306/1733] Update ct/sonarqube.sh --- ct/sonarqube.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/ct/sonarqube.sh b/ct/sonarqube.sh index 27a4ae05a..11f926fc5 100644 --- a/ct/sonarqube.sh +++ b/ct/sonarqube.sh @@ -29,7 +29,6 @@ function update_script() { fi if check_for_gh_release "sonarqube" "SonarSource/sonarqube"; then - msg_info "Updating ${APP} to v${RELEASE}" systemctl stop sonarqube From c16c9702cdc398034fd215fc70263c4bfadb1794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Wed, 1 Oct 2025 00:37:01 +0200 Subject: [PATCH 1307/1733] Update ct/sonarqube.sh --- ct/sonarqube.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ct/sonarqube.sh b/ct/sonarqube.sh index 11f926fc5..bc46854d4 100644 --- a/ct/sonarqube.sh +++ b/ct/sonarqube.sh @@ -29,8 +29,9 @@ function update_script() { fi if check_for_gh_release "sonarqube" "SonarSource/sonarqube"; then - + msg_info "Stopping service" systemctl stop sonarqube + msg_ok "Service stopped" BACKUP_DIR="/opt/sonarqube-backup" mv /opt/sonarqube ${BACKUP_DIR} From 4ce366903ff38d5f1558443c2b8aa9973960baa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Wed, 1 Oct 2025 00:37:07 +0200 Subject: [PATCH 1308/1733] Update ct/sonarqube.sh --- ct/sonarqube.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ct/sonarqube.sh b/ct/sonarqube.sh index bc46854d4..527d4be90 100644 --- a/ct/sonarqube.sh +++ b/ct/sonarqube.sh @@ -33,8 +33,10 @@ function update_script() { systemctl stop sonarqube msg_ok "Service stopped" + msg_info "Creating backup" BACKUP_DIR="/opt/sonarqube-backup" mv /opt/sonarqube ${BACKUP_DIR} + msg_ok "Backup created" curl -fsSL -o /tmp/sonarqube.zip "https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-${RELEASE}.zip" From 5a179d4c6941d1f487292a4751ff733f44a86f6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Wed, 1 Oct 2025 00:37:14 +0200 Subject: [PATCH 1309/1733] Update ct/sonarqube.sh --- ct/sonarqube.sh | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/ct/sonarqube.sh b/ct/sonarqube.sh index 527d4be90..a358a1c58 100644 --- a/ct/sonarqube.sh +++ b/ct/sonarqube.sh @@ -38,13 +38,7 @@ function update_script() { mv /opt/sonarqube ${BACKUP_DIR} msg_ok "Backup created" - curl -fsSL -o /tmp/sonarqube.zip "https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-${RELEASE}.zip" - - mkdir -p /opt/sonarqube - - unzip -q /tmp/sonarqube.zip -d /tmp - cp -r /tmp/sonarqube-${RELEASE}/* /opt/sonarqube/ - rm -rf /tmp/sonarqube* + fetch_and_deploy_gh_release "sonarqube" "SonarSource/sonarqube" "tarball" cp -rp ${BACKUP_DIR}/data/ /opt/sonarqube/data/ cp -rp ${BACKUP_DIR}/extensions/ /opt/sonarqube/extensions/ From 02462572b0889aaf4c4321257f8eba2ab4326fd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Wed, 1 Oct 2025 00:37:22 +0200 Subject: [PATCH 1310/1733] Update ct/sonarqube.sh --- ct/sonarqube.sh | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/ct/sonarqube.sh b/ct/sonarqube.sh index a358a1c58..27f713028 100644 --- a/ct/sonarqube.sh +++ b/ct/sonarqube.sh @@ -40,20 +40,18 @@ function update_script() { fetch_and_deploy_gh_release "sonarqube" "SonarSource/sonarqube" "tarball" + msg_info "Restoring backup" cp -rp ${BACKUP_DIR}/data/ /opt/sonarqube/data/ cp -rp ${BACKUP_DIR}/extensions/ /opt/sonarqube/extensions/ cp -p ${BACKUP_DIR}/conf/sonar.properties /opt/sonarqube/conf/sonar.properties rm -rf ${BACKUP_DIR} - chown -R sonarqube:sonarqube /opt/sonarqube - - echo "${RELEASE}" > /opt/${APP}_version.txt - + msg_ok "Backup restored" + + msg_info "Starting service" systemctl start sonarqube - - msg_ok "Updated to v${RELEASE}" - else - msg_ok "No update required. ${APP} is already at v${RELEASE}." + msg_ok "Service started" + msg_ok "Updated Successfully" fi exit } From 0653120ad6c567d602aabf524ceed02e3df82ede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Wed, 1 Oct 2025 00:37:31 +0200 Subject: [PATCH 1311/1733] Update ct/sonarqube.sh --- ct/sonarqube.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/ct/sonarqube.sh b/ct/sonarqube.sh index 27f713028..4c79daab8 100644 --- a/ct/sonarqube.sh +++ b/ct/sonarqube.sh @@ -64,6 +64,3 @@ msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9000${CL}" -echo -e "${YW}Default credentials:${CL}" -echo -e "${TAB}Username: admin" -echo -e "${TAB}Password: admin" From b300f8b5e134de394e5397c0679c4eed418fb637 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Wed, 1 Oct 2025 00:37:40 +0200 Subject: [PATCH 1312/1733] Update install/sonarqube-install.sh --- install/sonarqube-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/sonarqube-install.sh b/install/sonarqube-install.sh index 0a199c9e9..6ffcf4f1b 100644 --- a/install/sonarqube-install.sh +++ b/install/sonarqube-install.sh @@ -70,7 +70,7 @@ LimitNPROC=8192 [Install] WantedBy=multi-user.target EOF -$STD systemctl enable --now sonarqube +systemctl enable -q --now sonarqube msg_ok "Service Created" motd_ssh From c9f2f801acdd19d658a6b70437ee0fc3bade479b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Wed, 1 Oct 2025 00:37:46 +0200 Subject: [PATCH 1313/1733] Update install/sonarqube-install.sh --- install/sonarqube-install.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/install/sonarqube-install.sh b/install/sonarqube-install.sh index 6ffcf4f1b..2f7847f28 100644 --- a/install/sonarqube-install.sh +++ b/install/sonarqube-install.sh @@ -37,9 +37,6 @@ useradd -r -m -U -d /opt/sonarqube -s /bin/bash sonarqube 2>/dev/null || true msg_info "Configuring SonarQube" chown -R sonarqube:sonarqube /opt/sonarqube chmod -R 755 /opt/sonarqube -msg_ok "Installed SonarQube" - -msg_info "Configuring SonarQube" mkdir -p /opt/sonarqube/conf cat >/opt/sonarqube/conf/sonar.properties < Date: Wed, 1 Oct 2025 00:37:53 +0200 Subject: [PATCH 1314/1733] Update install/sonarqube-install.sh --- install/sonarqube-install.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/install/sonarqube-install.sh b/install/sonarqube-install.sh index 2f7847f28..7b362954d 100644 --- a/install/sonarqube-install.sh +++ b/install/sonarqube-install.sh @@ -31,8 +31,6 @@ $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME OWNER $DB_USER;" } >>~/sonarqube.creds msg_ok "Installed PostgreSQL" -msg_info "Creating SonarQube User" -useradd -r -m -U -d /opt/sonarqube -s /bin/bash sonarqube 2>/dev/null || true msg_info "Configuring SonarQube" chown -R sonarqube:sonarqube /opt/sonarqube From e19922adb59fdce217540b663850c2755b359b82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Wed, 1 Oct 2025 00:39:43 +0200 Subject: [PATCH 1315/1733] Update sonarqube-install.sh --- install/sonarqube-install.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/install/sonarqube-install.sh b/install/sonarqube-install.sh index 7b362954d..187ec31ba 100644 --- a/install/sonarqube-install.sh +++ b/install/sonarqube-install.sh @@ -12,7 +12,7 @@ setting_up_container network_check update_os -JAVA_VERSION=21 setup_java +JAVA_VERSION="21" setup_java PG_VERSION="17" setup_postgresql fetch_and_deploy_gh_release "sonarqube" "SonarSource/sonarqube" "tarball" @@ -31,7 +31,6 @@ $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME OWNER $DB_USER;" } >>~/sonarqube.creds msg_ok "Installed PostgreSQL" - msg_info "Configuring SonarQube" chown -R sonarqube:sonarqube /opt/sonarqube chmod -R 755 /opt/sonarqube From e33f32b56ef13190ee1996c08a104aebde94e0c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Wed, 1 Oct 2025 00:41:26 +0200 Subject: [PATCH 1316/1733] Update install/sonarqube-install.sh --- install/sonarqube-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/install/sonarqube-install.sh b/install/sonarqube-install.sh index 187ec31ba..e2b5eeed4 100644 --- a/install/sonarqube-install.sh +++ b/install/sonarqube-install.sh @@ -20,7 +20,6 @@ msg_info "Installing Postgresql" DB_NAME="sonarqube" DB_USER="sonarqube" DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) -systemctl start postgresql $STD sudo -u postgres psql -c "CREATE USER $DB_USER WITH PASSWORD '$DB_PASS';" $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME OWNER $DB_USER;" { From 71c8781e31e2ee476d9cfc85319f9fa5ae0f55a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Wed, 1 Oct 2025 00:41:55 +0200 Subject: [PATCH 1317/1733] Update sonarqube-install.sh --- install/sonarqube-install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install/sonarqube-install.sh b/install/sonarqube-install.sh index e2b5eeed4..626767139 100644 --- a/install/sonarqube-install.sh +++ b/install/sonarqube-install.sh @@ -31,6 +31,7 @@ $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME OWNER $DB_USER;" msg_ok "Installed PostgreSQL" msg_info "Configuring SonarQube" +$STD useradd -r -m -U -d /opt/sonarqube -s /bin/bash sonarqube chown -R sonarqube:sonarqube /opt/sonarqube chmod -R 755 /opt/sonarqube mkdir -p /opt/sonarqube/conf From b24eac99aa31b24ba3076e567665372d076369b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Wed, 1 Oct 2025 00:43:04 +0200 Subject: [PATCH 1318/1733] Update install/sonarqube-install.sh --- install/sonarqube-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/sonarqube-install.sh b/install/sonarqube-install.sh index 626767139..cac2de195 100644 --- a/install/sonarqube-install.sh +++ b/install/sonarqube-install.sh @@ -35,7 +35,7 @@ $STD useradd -r -m -U -d /opt/sonarqube -s /bin/bash sonarqube chown -R sonarqube:sonarqube /opt/sonarqube chmod -R 755 /opt/sonarqube mkdir -p /opt/sonarqube/conf -cat >/opt/sonarqube/conf/sonar.properties </opt/sonarqube/conf/sonar.properties sonar.jdbc.username=${DB_USER} sonar.jdbc.password=${DB_PASS} sonar.jdbc.url=jdbc:postgresql://localhost/${DB_NAME} From 96436cb6fe1848490852a77d062ccc18e4b798a3 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 1 Oct 2025 11:22:24 +0200 Subject: [PATCH 1319/1733] Update dispatcharr-install.sh --- install/dispatcharr-install.sh | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/install/dispatcharr-install.sh b/install/dispatcharr-install.sh index 1daedb24a..6f0da56e0 100644 --- a/install/dispatcharr-install.sh +++ b/install/dispatcharr-install.sh @@ -13,13 +13,8 @@ setting_up_container network_check update_os -# msg_info "Creating ${APP_USER} user" -# groupadd -f $APP_GROUP -# useradd -M -s /usr/sbin/nologin -g $APP_GROUP $APP_USER || true -# msg_ok "Created ${APP_USER} user" - msg_info "Installing Dependencies" -$STD apt-get install -y \ +$STD apt install -y \ build-essential \ gcc \ libpcre3-dev \ @@ -34,7 +29,6 @@ msg_ok "Installed Dependencies" PYTHON_VERSION="3.13" setup_uv NODE_VERSION="22" setup_nodejs PG_VERSION="16" setup_postgresql -fetch_and_deploy_gh_release "dispatcharr" "Dispatcharr/Dispatcharr" msg_info "Set up PostgreSQL Database" DB_NAME=dispatcharr_db @@ -54,9 +48,9 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" } >>~/dispatcharr.creds msg_ok "Set up PostgreSQL Database" +fetch_and_deploy_gh_release "dispatcharr" "Dispatcharr/Dispatcharr" + msg_info "Setup Python (uv) requirements (system)" -UV_PY="${PYTHON_VERSION:-3.13}" -$STD uv python install "$UV_PY" cd /opt/dispatcharr PYPI_URL="https://pypi.org/simple" mapfile -t EXTRA_INDEX_URLS < <(grep -E '^(--(extra-)?index-url|-i)\s' requirements.txt 2>/dev/null | awk '{print $2}' | sed 's#/*$##') From 155e880693e94517c64381dff12831e904949067 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 1 Oct 2025 11:22:45 +0200 Subject: [PATCH 1320/1733] Update dispatcharr-install.sh --- install/dispatcharr-install.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/install/dispatcharr-install.sh b/install/dispatcharr-install.sh index 6f0da56e0..cf7b5d630 100644 --- a/install/dispatcharr-install.sh +++ b/install/dispatcharr-install.sh @@ -209,6 +209,7 @@ motd_ssh customize msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean +$STD apt -y autoremove +$STD apt -y autoclean +$STD apt -y clean msg_ok "Cleaned" From 9798c7d6c95637da431c0589bd1f8dc40b63ca1c Mon Sep 17 00:00:00 2001 From: Vincent <114195376+HydroshieldMKII@users.noreply.github.com> Date: Wed, 1 Oct 2025 08:52:27 -0400 Subject: [PATCH 1321/1733] Updated install and update script for PR request. Update the json for the interface --- ct/guardian.sh | 81 ++++++++++++------------------ frontend/public/json/guardian.json | 6 +-- install/guardian-install.sh | 53 ++++++------------- 3 files changed, 50 insertions(+), 90 deletions(-) diff --git a/ct/guardian.sh b/ct/guardian.sh index 07203be27..1017b2c10 100755 --- a/ct/guardian.sh +++ b/ct/guardian.sh @@ -11,7 +11,7 @@ var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-6}" var_os="${var_os:-debian}" -var_version="${var_version:-12}" +var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" @@ -20,72 +20,55 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources +header_info +check_container_storage +check_container_resources - if [[ ! -d "/opt/${APP}" ]]; then +if [[ ! -d "/opt/guardian" ]] ; then msg_error "No ${APP} Installation Found!" exit - fi +fi - - RELEASE=$(curl -fsSL https://api.github.com/repos/HydroshieldMKII/Guardian/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then - - msg_info "Stopping $APP" +if check_for_gh_release "guardian" "HydroshieldMKII/Guardian" ; then + msg_info "Stopping Services" systemctl stop guardian-backend guardian-frontend - msg_ok "Stopped $APP" + msg_ok "Stopped Services" - msg_info "Saving Database" - if [[ -f "/opt/Guardian/backend/plex-guard.db" ]]; then - cp "/opt/Guardian/backend/plex-guard.db" "/tmp/plex-guard.db.backup" - msg_ok "Database backed up" + if [[ -f "/opt/Guardian/backend/plex-guard.db" ]] ; then + msg_info "Saving Database" + cp "/opt/Guardian/backend/plex-guard.db" "/tmp/plex-guard.db.backup" + msg_ok "Database backed up" fi + cp /opt/guardian/.env /opt + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "guardian" "HydroshieldMKII/Guardian" "tarball" "latest" "/opt/guardian" + mv /opt/.env /opt/guardian - msg_info "Updating $APP to ${RELEASE}" - cd /tmp - - curl -fsSL -o "${RELEASE}.zip" "https://github.com/HydroshieldMKII/Guardian/archive/refs/tags/${RELEASE}.zip" - unzip -q "${RELEASE}.zip" - rm -rf /opt/Guardian - - FOLDER_NAME=$(echo "${RELEASE}" | sed 's/^v//') - mv "Guardian-${FOLDER_NAME}/" "/opt/Guardian" - - if [[ -f "/tmp/plex-guard.db.backup" ]]; then - msg_info "Restoring Database" - cp "/tmp/plex-guard.db.backup" "/opt/Guardian/backend/plex-guard.db" - rm "/tmp/plex-guard.db.backup" - msg_ok "Database restored" + if [[ -f "/tmp/plex-guard.db.backup" ]] ; then + msg_info "Restoring Database" + cp "/tmp/plex-guard.db.backup" "/opt/Guardian/backend/plex-guard.db" + rm "/tmp/plex-guard.db.backup" + msg_ok "Database restored" fi + msg_info "Updating Guardian" cd /opt/Guardian/backend - npm ci - npm run build + $STD npm ci + $STD npm run build cd /opt/Guardian/frontend - npm ci - DEPLOYMENT_MODE=standalone npm run build + $STD npm ci + $STD DEPLOYMENT_MODE = standalone npm run build - echo "${RELEASE}" >/opt/${APP}_version.txt - msg_ok "Updated $APP to v${RELEASE}" + msg_ok "Updated Guardian" - msg_info "Starting $APP" + msg_info "Starting Services" systemctl start guardian-backend guardian-frontend - msg_ok "Started $APP" - - msg_info "Cleaning Up" - rm -rf /tmp/"${RELEASE}.zip" /tmp/"Guardian-${FOLDER_NAME}" /tmp/plex-guard.db.backup - msg_ok "Cleanup Completed" - - msg_ok "Update Successful" - else - msg_ok "No update required. ${APP} is already at ${RELEASE}" - fi - exit + msg_ok "Started Services" + msg_ok "Updated Successfully" +fi +exit } start diff --git a/frontend/public/json/guardian.json b/frontend/public/json/guardian.json index 57381efdd..97568819e 100644 --- a/frontend/public/json/guardian.json +++ b/frontend/public/json/guardian.json @@ -8,10 +8,10 @@ "privileged": false, "interface_port": 3000, "documentation": null, - "config_path": "", + "config_path": "/opt/guardian/.env", "website": "https://github.com/HydroshieldMKII/Guardian", "logo": null, - "description": "A tool to manage devices access to your Plex server with a nice Dashboard ", + "description": "Guardian is a lightweight companion app for Plex that lets you monitor, approve or block devices in real time. It helps you enforce per-user or global policies, stop unwanted sessions automatically and grant temporary access - all through a simple web interface.", "install_methods": [ { "type": "default", @@ -21,7 +21,7 @@ "ram": 2048, "hdd": 6, "os": "Debian", - "version": "12" + "version": "13" } } ], diff --git a/install/guardian-install.sh b/install/guardian-install.sh index de05a8e88..0a5cb5d66 100644 --- a/install/guardian-install.sh +++ b/install/guardian-install.sh @@ -14,86 +14,63 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt-get install -y \ - git \ - sqlite3 \ - unzip \ - curl \ - ca-certificates \ - gnupg - -curl -fsSL https://deb.nodesource.com/setup_24.x | bash - -$STD apt-get install -y nodejs - +$STD apt install -y \ + sqlite3 +NODE_VERSION="24" setup_nodejs msg_ok "Installed Dependencies" msg_info "Setup Guardian" -RELEASE=$(curl -fsSL https://api.github.com/repos/HydroshieldMKII/Guardian/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') -curl -fsSL -o "${RELEASE}.zip" "https://github.com/HydroshieldMKII/Guardian/archive/refs/tags/${RELEASE}.zip" -unzip -q "${RELEASE}.zip" - -FOLDER_NAME=$(echo "${RELEASE}" | sed 's/^v//') -mv "Guardian-${FOLDER_NAME}/" "/opt/Guardian" - -echo "${RELEASE}" >/opt/Guardian_version.txt -msg_ok "Setup Guardian" +fetch_and_deploy_gh_release "guardian" "HydroshieldMKII/Guardian" "tarball" "latest" "/opt/guardian" msg_info "Building backend" cd /opt/Guardian/backend -npm ci -npm run build +$STD npm ci +$STD npm run build msg_ok "Built backend" msg_info "Building frontend" cd /opt/Guardian/frontend -npm ci -DEPLOYMENT_MODE=standalone npm run build +$STD npm ci +$STD DEPLOYMENT_MODE=standalone npm run build msg_ok "Built frontend" -msg_info "Creating Backend Service" +msg_info "Creating Services" cat </etc/systemd/system/guardian-backend.service [Unit] Description=Guardian Backend After=network.target - [Service] WorkingDirectory=/opt/Guardian/backend ExecStart=/usr/bin/node dist/main.js Restart=always RestartSec=3 - [Install] WantedBy=multi-user.target EOF -systemctl enable -q --now guardian-backend -msg_ok "Created Backend Service" -msg_info "Creating Frontend Service" cat </etc/systemd/system/guardian-frontend.service [Unit] Description=Guardian Frontend After=guardian-backend.service network.target Wants=guardian-backend.service - [Service] WorkingDirectory=/opt/Guardian/frontend Environment=DEPLOYMENT_MODE=standalone ExecStart=/usr/bin/npm run start Restart=always RestartSec=3 - [Install] WantedBy=multi-user.target EOF + +systemctl enable -q --now guardian-backend systemctl enable -q --now guardian-frontend -msg_ok "Created Frontend Service" +msg_ok "Created Services" motd_ssh -customize -msg_info "Cleaning up" -rm -f "${RELEASE}".zip -$STD apt-get -y autoremove -$STD apt-get -y autoclean +apt -y autoremove +apt -y autoclean +apt -y clean msg_ok "Cleaned" From 54bd9259de445b58a8d94a1b20b7a9c3468bae8d Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 1 Oct 2025 14:54:08 +0200 Subject: [PATCH 1322/1733] Add setup_dotnet function to tools.func --- misc/tools.func | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/misc/tools.func b/misc/tools.func index db74a7a00..564402a36 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -1,5 +1,26 @@ #!/bin/bash +# ------------------------------------------------------------------------------ +# Installs Microsoft .NET +# +# Description: +# - Installs specified .NET version using Microsoft APT repo +# +# Variables: +# NET_VERSION - .NET version to install (default: "8.0") +# ------------------------------------------------------------------------------ + +function setup_dotnet() { + local NET_VERSION="${NET_VERSION:-8.0}" + + msg_info "Setup .NET $NET_VERSION" + curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor -o /usr/share/keyrings/microsoft-prod.gpg + curl -fsSL https://packages.microsoft.com/config/debian/12/prod.list -o /etc/apt/sources.list.d/msprod.list + $STD apt-get update + $STD apt-get install -y dotnet-sdk-$NET_VERSION + msg_ok "Setup .NET ${NET_VERSION}" +} + # ------------------------------------------------------------------------------ # Installs Node.js and optional global modules. # From ed6f915b9fe9a27e61b8bbdf962c4ea9735b5118 Mon Sep 17 00:00:00 2001 From: HydroshieldMKII Date: Wed, 1 Oct 2025 09:06:58 -0400 Subject: [PATCH 1323/1733] temporary edit build and install.func for testing --- ct/guardian.sh | 2 +- misc/build.func | 12 ++++++------ misc/install.func | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ct/guardian.sh b/ct/guardian.sh index 1017b2c10..ed8a561cf 100755 --- a/ct/guardian.sh +++ b/ct/guardian.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: HydroshieldMKII # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/misc/build.func b/misc/build.func index d6b0af725..d0c9988ab 100644 --- a/misc/build.func +++ b/misc/build.func @@ -128,17 +128,17 @@ variables() { # - Initialize error traps after loading # ------------------------------------------------------------------------------ -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) +source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/api.func) if command -v curl >/dev/null 2>&1; then - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) + source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/core.func) + source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/error_handler.func) load_functions catch_errors #echo "(build.func) Loaded core.func via curl" elif command -v wget >/dev/null 2>&1; then - source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) - source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) + source <(wget -qO- https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/core.func) + source <(wget -qO- https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/error_handler.func) load_functions catch_errors #echo "(build.func) Loaded core.func via wget" @@ -2295,7 +2295,7 @@ EOF' fi msg_ok "Customized LXC Container" install_ssh_keys_into_ct - if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)"; then + if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/install/${var_install}.sh)"; then exit $? fi } diff --git a/misc/install.func b/misc/install.func index f741b921d..bfa55d458 100644 --- a/misc/install.func +++ b/misc/install.func @@ -9,8 +9,8 @@ if ! command -v curl >/dev/null 2>&1; then apt-get update >/dev/null 2>&1 apt-get install -y curl >/dev/null 2>&1 fi -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) +source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/core.func) +source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/error_handler.func) load_functions catch_errors @@ -147,7 +147,7 @@ EOF $STD apt-get -o Dpkg::Options::="--force-confold" -y dist-upgrade rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED msg_ok "Updated Container OS" - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) + source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/tools.func) } # This function modifies the message of the day (motd) and SSH settings @@ -195,7 +195,7 @@ EOF systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//') msg_ok "Customized Container" fi - echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update + echo "bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/ct/${app}.sh)\"" >/usr/bin/update chmod +x /usr/bin/update if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then mkdir -p /root/.ssh From 7a8f1aa0ff8d0959fd88ec1d4f53b38c03464dcd Mon Sep 17 00:00:00 2001 From: HydroshieldMKII Date: Wed, 1 Oct 2025 09:10:20 -0400 Subject: [PATCH 1324/1733] Fix case sensitivity in Guardian paths across scripts --- ct/guardian.sh | 12 ++++++------ install/guardian-install.sh | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ct/guardian.sh b/ct/guardian.sh index ed8a561cf..4358f287f 100755 --- a/ct/guardian.sh +++ b/ct/guardian.sh @@ -35,9 +35,9 @@ if check_for_gh_release "guardian" "HydroshieldMKII/Guardian" ; then msg_ok "Stopped Services" - if [[ -f "/opt/Guardian/backend/plex-guard.db" ]] ; then + if [[ -f "/opt/guardian/backend/plex-guard.db" ]] ; then msg_info "Saving Database" - cp "/opt/Guardian/backend/plex-guard.db" "/tmp/plex-guard.db.backup" + cp "/opt/guardian/backend/plex-guard.db" "/tmp/plex-guard.db.backup" msg_ok "Database backed up" fi @@ -47,19 +47,19 @@ if check_for_gh_release "guardian" "HydroshieldMKII/Guardian" ; then if [[ -f "/tmp/plex-guard.db.backup" ]] ; then msg_info "Restoring Database" - cp "/tmp/plex-guard.db.backup" "/opt/Guardian/backend/plex-guard.db" + cp "/tmp/plex-guard.db.backup" "/opt/guardian/backend/plex-guard.db" rm "/tmp/plex-guard.db.backup" msg_ok "Database restored" fi msg_info "Updating Guardian" - cd /opt/Guardian/backend + cd /opt/guardian/backend $STD npm ci $STD npm run build - cd /opt/Guardian/frontend + cd /opt/guardian/frontend $STD npm ci - $STD DEPLOYMENT_MODE = standalone npm run build + $STD DEPLOYMENT_MODE=standalone npm run build msg_ok "Updated Guardian" diff --git a/install/guardian-install.sh b/install/guardian-install.sh index 0a5cb5d66..c6a745e5e 100644 --- a/install/guardian-install.sh +++ b/install/guardian-install.sh @@ -24,13 +24,13 @@ fetch_and_deploy_gh_release "guardian" "HydroshieldMKII/Guardian" "tarball" "lat msg_info "Building backend" -cd /opt/Guardian/backend +cd /opt/guardian/backend $STD npm ci $STD npm run build msg_ok "Built backend" msg_info "Building frontend" -cd /opt/Guardian/frontend +cd /opt/guardian/frontend $STD npm ci $STD DEPLOYMENT_MODE=standalone npm run build msg_ok "Built frontend" @@ -41,7 +41,7 @@ cat </etc/systemd/system/guardian-backend.service Description=Guardian Backend After=network.target [Service] -WorkingDirectory=/opt/Guardian/backend +WorkingDirectory=/opt/guardian/backend ExecStart=/usr/bin/node dist/main.js Restart=always RestartSec=3 @@ -55,7 +55,7 @@ Description=Guardian Frontend After=guardian-backend.service network.target Wants=guardian-backend.service [Service] -WorkingDirectory=/opt/Guardian/frontend +WorkingDirectory=/opt/guardian/frontend Environment=DEPLOYMENT_MODE=standalone ExecStart=/usr/bin/npm run start Restart=always From d2b343437d37a8bafcece099c120a44592ab2a62 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 1 Oct 2025 15:11:45 +0200 Subject: [PATCH 1325/1733] Update setup_dotnet function --- misc/tools.func | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/misc/tools.func b/misc/tools.func index 564402a36..b12b9a644 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -12,10 +12,15 @@ function setup_dotnet() { local NET_VERSION="${NET_VERSION:-8.0}" + DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{ print $2 }' /etc/os-release) msg_info "Setup .NET $NET_VERSION" curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor -o /usr/share/keyrings/microsoft-prod.gpg - curl -fsSL https://packages.microsoft.com/config/debian/12/prod.list -o /etc/apt/sources.list.d/msprod.list + if [[ "$DISTRO_CODENAME" != "trixie" ]]; then + curl -fsSL https://packages.microsoft.com/config/debian/12/prod.list -o /etc/apt/sources.list.d/msprod.list + else + curl -fsSL https://packages.microsoft.com/config/debian/13/prod.list -o /etc/apt/sources.list.d/msprod.list + fi $STD apt-get update $STD apt-get install -y dotnet-sdk-$NET_VERSION msg_ok "Setup .NET ${NET_VERSION}" From 8d44bed6c597de409cd607777298c508f0c260d1 Mon Sep 17 00:00:00 2001 From: HydroshieldMKII Date: Wed, 1 Oct 2025 09:35:51 -0400 Subject: [PATCH 1326/1733] Refactor frontend build process to export DEPLOYMENT_MODE before running npm build --- install/guardian-install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/install/guardian-install.sh b/install/guardian-install.sh index c6a745e5e..19c97ffef 100644 --- a/install/guardian-install.sh +++ b/install/guardian-install.sh @@ -32,7 +32,8 @@ msg_ok "Built backend" msg_info "Building frontend" cd /opt/guardian/frontend $STD npm ci -$STD DEPLOYMENT_MODE=standalone npm run build +export DEPLOYMENT_MODE=standalone +$STD npm run build msg_ok "Built frontend" msg_info "Creating Services" From 18c3b2979d1a571f0a5ae72a9bc77e310ce7354c Mon Sep 17 00:00:00 2001 From: HydroshieldMKII Date: Wed, 1 Oct 2025 09:36:16 -0400 Subject: [PATCH 1327/1733] Refactor build messages in guardian-install.sh for consistency --- install/guardian-install.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/install/guardian-install.sh b/install/guardian-install.sh index 19c97ffef..6b3766ee1 100644 --- a/install/guardian-install.sh +++ b/install/guardian-install.sh @@ -23,18 +23,16 @@ msg_info "Setup Guardian" fetch_and_deploy_gh_release "guardian" "HydroshieldMKII/Guardian" "tarball" "latest" "/opt/guardian" -msg_info "Building backend" +msg_info "Building App" cd /opt/guardian/backend $STD npm ci $STD npm run build -msg_ok "Built backend" -msg_info "Building frontend" cd /opt/guardian/frontend $STD npm ci export DEPLOYMENT_MODE=standalone $STD npm run build -msg_ok "Built frontend" +msg_ok "Built App" msg_info "Creating Services" cat </etc/systemd/system/guardian-backend.service From 660d8a21cbd4ee687a660b55b162d7010b3e9675 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Wed, 1 Oct 2025 15:39:34 +0200 Subject: [PATCH 1328/1733] Update guardian.sh --- ct/guardian.sh | 64 ++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 33 deletions(-) diff --git a/ct/guardian.sh b/ct/guardian.sh index 4358f287f..c33a5c06c 100755 --- a/ct/guardian.sh +++ b/ct/guardian.sh @@ -25,48 +25,46 @@ check_container_storage check_container_resources if [[ ! -d "/opt/guardian" ]] ; then - msg_error "No ${APP} Installation Found!" - exit + msg_error "No ${APP} Installation Found!" + exit fi if check_for_gh_release "guardian" "HydroshieldMKII/Guardian" ; then - msg_info "Stopping Services" - systemctl stop guardian-backend guardian-frontend - msg_ok "Stopped Services" + msg_info "Stopping Services" + systemctl stop guardian-backend guardian-frontend + msg_ok "Stopped Services" + if [[ -f "/opt/guardian/backend/plex-guard.db" ]] ; then + msg_info "Saving Database" + cp "/opt/guardian/backend/plex-guard.db" "/tmp/plex-guard.db.backup" + msg_ok "Database backed up" + fi - if [[ -f "/opt/guardian/backend/plex-guard.db" ]] ; then - msg_info "Saving Database" - cp "/opt/guardian/backend/plex-guard.db" "/tmp/plex-guard.db.backup" - msg_ok "Database backed up" - fi + cp /opt/guardian/.env /opt + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "guardian" "HydroshieldMKII/Guardian" "tarball" "latest" "/opt/guardian" + mv /opt/.env /opt/guardian - cp /opt/guardian/.env /opt - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "guardian" "HydroshieldMKII/Guardian" "tarball" "latest" "/opt/guardian" - mv /opt/.env /opt/guardian + if [[ -f "/tmp/plex-guard.db.backup" ]] ; then + msg_info "Restoring Database" + cp "/tmp/plex-guard.db.backup" "/opt/guardian/backend/plex-guard.db" + rm "/tmp/plex-guard.db.backup" + msg_ok "Database restored" + fi - if [[ -f "/tmp/plex-guard.db.backup" ]] ; then - msg_info "Restoring Database" - cp "/tmp/plex-guard.db.backup" "/opt/guardian/backend/plex-guard.db" - rm "/tmp/plex-guard.db.backup" - msg_ok "Database restored" - fi + msg_info "Updating Guardian" + cd /opt/guardian/backend + $STD npm ci + $STD npm run build - msg_info "Updating Guardian" - cd /opt/guardian/backend - $STD npm ci - $STD npm run build + cd /opt/guardian/frontend + $STD npm ci + $STD DEPLOYMENT_MODE=standalone npm run build + msg_ok "Updated Guardian" - cd /opt/guardian/frontend - $STD npm ci - $STD DEPLOYMENT_MODE=standalone npm run build - - msg_ok "Updated Guardian" - - msg_info "Starting Services" - systemctl start guardian-backend guardian-frontend - msg_ok "Started Services" - msg_ok "Updated Successfully" + msg_info "Starting Services" + systemctl start guardian-backend guardian-frontend + msg_ok "Started Services" + msg_ok "Updated Successfully" fi exit } From 07cc74b074a9dd6ff9503a31b33d770ea22a542d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Wed, 1 Oct 2025 15:41:31 +0200 Subject: [PATCH 1329/1733] Update guardian.json --- frontend/public/json/guardian.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frontend/public/json/guardian.json b/frontend/public/json/guardian.json index 97568819e..b7c6761d5 100644 --- a/frontend/public/json/guardian.json +++ b/frontend/public/json/guardian.json @@ -1,13 +1,15 @@ { "name": "Guardian", "slug": "guardian", - "categories": [13], + "categories": [ + 13 + ], "date_created": "2025-09-22", "type": "ct", "updateable": true, "privileged": false, "interface_port": 3000, - "documentation": null, + "documentation": "https://github.com/HydroshieldMKII/Guardian/blob/main/README.md", "config_path": "/opt/guardian/.env", "website": "https://github.com/HydroshieldMKII/Guardian", "logo": null, From e7a9bc04c683c072829498bf051a58a577f1698a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Wed, 1 Oct 2025 15:44:13 +0200 Subject: [PATCH 1330/1733] Update guardian-install.sh --- install/guardian-install.sh | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/install/guardian-install.sh b/install/guardian-install.sh index 6b3766ee1..b982bf3c9 100644 --- a/install/guardian-install.sh +++ b/install/guardian-install.sh @@ -14,31 +14,28 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt install -y \ - sqlite3 -NODE_VERSION="24" setup_nodejs +$STD apt install -y sqlite3 msg_ok "Installed Dependencies" -msg_info "Setup Guardian" +NODE_VERSION="24" setup_nodejs fetch_and_deploy_gh_release "guardian" "HydroshieldMKII/Guardian" "tarball" "latest" "/opt/guardian" - -msg_info "Building App" +msg_info "Configuring ${APPLICATION}" cd /opt/guardian/backend $STD npm ci $STD npm run build - cd /opt/guardian/frontend $STD npm ci export DEPLOYMENT_MODE=standalone $STD npm run build -msg_ok "Built App" +msg_ok "Configured {APPLICATION}" -msg_info "Creating Services" +msg_info "Creating Service" cat </etc/systemd/system/guardian-backend.service [Unit] Description=Guardian Backend After=network.target + [Service] WorkingDirectory=/opt/guardian/backend ExecStart=/usr/bin/node dist/main.js @@ -53,6 +50,7 @@ cat </etc/systemd/system/guardian-frontend.service Description=Guardian Frontend After=guardian-backend.service network.target Wants=guardian-backend.service + [Service] WorkingDirectory=/opt/guardian/frontend Environment=DEPLOYMENT_MODE=standalone @@ -65,9 +63,10 @@ EOF systemctl enable -q --now guardian-backend systemctl enable -q --now guardian-frontend -msg_ok "Created Services" +msg_ok "Created Service" motd_ssh +customize apt -y autoremove apt -y autoclean From b074b25daa2930a621873fcecce42adfea2d918a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Wed, 1 Oct 2025 15:44:47 +0200 Subject: [PATCH 1331/1733] Delete misc/install.func --- misc/install.func | 206 ---------------------------------------------- 1 file changed, 206 deletions(-) delete mode 100644 misc/install.func diff --git a/misc/install.func b/misc/install.func deleted file mode 100644 index bfa55d458..000000000 --- a/misc/install.func +++ /dev/null @@ -1,206 +0,0 @@ -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) -# Co-Author: MickLesk -# Co-Author: michelroegl-brunner -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE - -if ! command -v curl >/dev/null 2>&1; then - printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 - apt-get update >/dev/null 2>&1 - apt-get install -y curl >/dev/null 2>&1 -fi -source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/core.func) -source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/error_handler.func) -load_functions -catch_errors - -# This function enables IPv6 if it's not disabled and sets verbose mode -verb_ip6() { - set_std_mode # Set STD mode based on VERBOSE - - if [ "$DISABLEIPV6" == "yes" ]; then - echo "net.ipv6.conf.all.disable_ipv6 = 1" >>/etc/sysctl.conf - $STD sysctl -p - fi -} - -# # This function sets error handling options and defines the error_handler function to handle errors -# catch_errors() { -# set -Eeuo pipefail -# trap 'error_handler $LINENO "$BASH_COMMAND"' ERR -# } - -# # This function handles errors -# error_handler() { -# source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/api.func) -# local exit_code="$1" -# local line_number="$2" -# local command="${3:-}" - -# if [[ "$exit_code" -eq 0 ]]; then -# return 0 -# fi - -# printf "\e[?25h" -# echo -e "\n${RD}[ERROR]${CL} in line ${RD}${line_number}${CL}: exit code ${RD}${exit_code}${CL}: while executing command ${YW}${command}${CL}\n" -# exit "$exit_code" -#} - -# This function sets up the Container OS by generating the locale, setting the timezone, and checking the network connection -setting_up_container() { - msg_info "Setting up Container OS" - for ((i = RETRY_NUM; i > 0; i--)); do - if [ "$(hostname -I)" != "" ]; then - break - fi - echo 1>&2 -en "${CROSS}${RD} No Network! " - sleep $RETRY_EVERY - done - if [ "$(hostname -I)" = "" ]; then - echo 1>&2 -e "\n${CROSS}${RD} No Network After $RETRY_NUM Tries${CL}" - echo -e "${NETWORK}Check Network Settings" - exit 1 - fi - rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED - systemctl disable -q --now systemd-networkd-wait-online.service - msg_ok "Set up Container OS" - #msg_custom "${CM}" "${GN}" "Network Connected: ${BL}$(hostname -I)" - msg_ok "Network Connected: ${BL}$(hostname -I)" -} - -# This function checks the network connection by pinging a known IP address and prompts the user to continue if the internet is not connected -network_check() { - set +e - trap - ERR - ipv4_connected=false - ipv6_connected=false - sleep 1 - - # Check IPv4 connectivity to Google, Cloudflare & Quad9 DNS servers. - if ping -c 1 -W 1 1.1.1.1 &>/dev/null || ping -c 1 -W 1 8.8.8.8 &>/dev/null || ping -c 1 -W 1 9.9.9.9 &>/dev/null; then - msg_ok "IPv4 Internet Connected" - ipv4_connected=true - else - msg_error "IPv4 Internet Not Connected" - fi - - # Check IPv6 connectivity to Google, Cloudflare & Quad9 DNS servers. - if ping6 -c 1 -W 1 2606:4700:4700::1111 &>/dev/null || ping6 -c 1 -W 1 2001:4860:4860::8888 &>/dev/null || ping6 -c 1 -W 1 2620:fe::fe &>/dev/null; then - msg_ok "IPv6 Internet Connected" - ipv6_connected=true - else - msg_error "IPv6 Internet Not Connected" - fi - - # If both IPv4 and IPv6 checks fail, prompt the user - if [[ $ipv4_connected == false && $ipv6_connected == false ]]; then - read -r -p "No Internet detected, would you like to continue anyway? " prompt - if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then - echo -e "${INFO}${RD}Expect Issues Without Internet${CL}" - else - echo -e "${NETWORK}Check Network Settings" - exit 1 - fi - fi - - # DNS resolution checks for GitHub-related domains (IPv4 and/or IPv6) - GIT_HOSTS=("github.com" "raw.githubusercontent.com" "api.github.com" "git.community-scripts.org") - GIT_STATUS="Git DNS:" - DNS_FAILED=false - - for HOST in "${GIT_HOSTS[@]}"; do - RESOLVEDIP=$(getent hosts "$HOST" | awk '{ print $1 }' | grep -E '(^([0-9]{1,3}\.){3}[0-9]{1,3}$)|(^[a-fA-F0-9:]+$)' | head -n1) - if [[ -z "$RESOLVEDIP" ]]; then - GIT_STATUS+="$HOST:($DNSFAIL)" - DNS_FAILED=true - else - GIT_STATUS+=" $HOST:($DNSOK)" - fi - done - - if [[ "$DNS_FAILED" == true ]]; then - fatal "$GIT_STATUS" - else - msg_ok "$GIT_STATUS" - fi - - set -e - trap 'error_handler $LINENO "$BASH_COMMAND"' ERR -} - -# This function updates the Container OS by running apt-get update and upgrade -update_os() { - msg_info "Updating Container OS" - if [[ "$CACHER" == "yes" ]]; then - echo "Acquire::http::Proxy-Auto-Detect \"/usr/local/bin/apt-proxy-detect.sh\";" >/etc/apt/apt.conf.d/00aptproxy - cat <<'EOF' >/usr/local/bin/apt-proxy-detect.sh -#!/bin/bash -if nc -w1 -z "${CACHER_IP}" 3142; then - echo -n "http://${CACHER_IP}:3142" -else - echo -n "DIRECT" -fi -EOF - chmod +x /usr/local/bin/apt-proxy-detect.sh - fi - $STD apt-get update - $STD apt-get -o Dpkg::Options::="--force-confold" -y dist-upgrade - rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED - msg_ok "Updated Container OS" - source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/tools.func) -} - -# This function modifies the message of the day (motd) and SSH settings -motd_ssh() { - grep -qxF "export TERM='xterm-256color'" /root/.bashrc || echo "export TERM='xterm-256color'" >>/root/.bashrc - - if [ -f "/etc/os-release" ]; then - OS_NAME=$(grep ^NAME /etc/os-release | cut -d= -f2 | tr -d '"') - OS_VERSION=$(grep ^VERSION_ID /etc/os-release | cut -d= -f2 | tr -d '"') - elif [ -f "/etc/debian_version" ]; then - OS_NAME="Debian" - OS_VERSION=$(cat /etc/debian_version) - fi - - PROFILE_FILE="/etc/profile.d/00_lxc-details.sh" - echo "echo -e \"\"" >"$PROFILE_FILE" - echo -e "echo -e \"${BOLD}${YW}${APPLICATION} LXC Container - DEV Repository${CL}\"" >>"$PROFILE_FILE" - echo -e "echo -e \"${RD}WARNING: This is a DEVELOPMENT version (ProxmoxVED). Do NOT use in production!${CL}\"" >>"$PROFILE_FILE" - echo -e "echo -e \"${YW} OS: ${GN}${OS_NAME} - Version: ${OS_VERSION}${CL}\"" >>"$PROFILE_FILE" - echo -e "echo -e \"${YW} Hostname: ${GN}\$(hostname)${CL}\"" >>"$PROFILE_FILE" - echo -e "echo -e \"${YW} IP Address: ${GN}\$(hostname -I | awk '{print \$1}')${CL}\"" >>"$PROFILE_FILE" - echo -e "echo -e \"${YW} Repository: ${GN}https://github.com/community-scripts/ProxmoxVED${CL}\"" >>"$PROFILE_FILE" - echo "echo \"\"" >>"$PROFILE_FILE" - - chmod -x /etc/update-motd.d/* - - if [[ "${SSH_ROOT}" == "yes" ]]; then - sed -i "s/#PermitRootLogin prohibit-password/PermitRootLogin yes/g" /etc/ssh/sshd_config - systemctl restart sshd - fi -} - -# This function customizes the container by modifying the getty service and enabling auto-login for the root user -customize() { - if [[ "$PASSWORD" == "" ]]; then - msg_info "Customizing Container" - GETTY_OVERRIDE="/etc/systemd/system/container-getty@1.service.d/override.conf" - mkdir -p $(dirname $GETTY_OVERRIDE) - cat <$GETTY_OVERRIDE - [Service] - ExecStart= - ExecStart=-/sbin/agetty --autologin root --noclear --keep-baud tty%I 115200,38400,9600 \$TERM -EOF - systemctl daemon-reload - systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//') - msg_ok "Customized Container" - fi - echo "bash -c \"\$(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/ct/${app}.sh)\"" >/usr/bin/update - chmod +x /usr/bin/update - if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then - mkdir -p /root/.ssh - echo "${SSH_AUTHORIZED_KEY}" >/root/.ssh/authorized_keys - chmod 700 /root/.ssh - chmod 600 /root/.ssh/authorized_keys - fi -} From 1a6c5dbf7629be311803cfa1382c5791767d7678 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Wed, 1 Oct 2025 15:45:17 +0200 Subject: [PATCH 1332/1733] Delete misc/build.func --- misc/build.func | 2981 ----------------------------------------------- 1 file changed, 2981 deletions(-) delete mode 100644 misc/build.func diff --git a/misc/build.func b/misc/build.func deleted file mode 100644 index d0c9988ab..000000000 --- a/misc/build.func +++ /dev/null @@ -1,2981 +0,0 @@ -#!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG -# Author: tteck (tteckster) | MickLesk | michelroegl-brunner -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Revision: 1 - -# ------------------------------------------------------------------------------ -# variables() -# -# - Normalize application name (NSAPP = lowercase, no spaces) -# - Build installer filename (var_install) -# - Define regex for integer validation -# - Fetch hostname of Proxmox node -# - Set default values for diagnostics/method -# - Generate random UUID for tracking -# ------------------------------------------------------------------------------ -variables() { - NSAPP=$(echo "${APP,,}" | tr -d ' ') # This function sets the NSAPP variable by converting the value of the APP variable to lowercase and removing any spaces. - var_install="${NSAPP}-install" # sets the var_install variable by appending "-install" to the value of NSAPP. - INTEGER='^[0-9]+([.][0-9]+)?$' # it defines the INTEGER regular expression pattern. - PVEHOST_NAME=$(hostname) # gets the Proxmox Hostname and sets it to Uppercase - DIAGNOSTICS="yes" # sets the DIAGNOSTICS variable to "yes", used for the API call. - METHOD="default" # sets the METHOD variable to "default", used for the API call. - RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" # generates a random UUID and sets it to the RANDOM_UUID variable. - #CT_TYPE=${var_unprivileged:-$CT_TYPE} -} - -# ----------------------------------------------------------------------------- -# Community-Scripts bootstrap loader -# - Always sources build.func from remote -# - Updates local core files only if build.func changed -# - Local cache: /usr/local/community-scripts/core -# ----------------------------------------------------------------------------- - -# FUNC_DIR="/usr/local/community-scripts/core" -# mkdir -p "$FUNC_DIR" - -# BUILD_URL="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func" -# BUILD_REV="$FUNC_DIR/build.rev" -# DEVMODE="${DEVMODE:-no}" - -# # --- Step 1: fetch build.func content once, compute hash --- -# build_content="$(curl -fsSL "$BUILD_URL")" || { -# echo "❌ Failed to fetch build.func" -# exit 1 -# } - -# newhash=$(printf "%s" "$build_content" | sha256sum | awk '{print $1}') -# oldhash=$(cat "$BUILD_REV" 2>/dev/null || echo "") - -# # --- Step 2: if build.func changed, offer update for core files --- -# if [ "$newhash" != "$oldhash" ]; then -# echo "⚠️ build.func changed!" - -# while true; do -# read -rp "Refresh local core files? [y/N/diff]: " ans -# case "$ans" in -# [Yy]*) -# echo "$newhash" >"$BUILD_REV" - -# update_func_file() { -# local file="$1" -# local url="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/$file" -# local local_path="$FUNC_DIR/$file" - -# echo "⬇️ Downloading $file ..." -# curl -fsSL "$url" -o "$local_path" || { -# echo "❌ Failed to fetch $file" -# exit 1 -# } -# echo "✔️ Updated $file" -# } - -# update_func_file core.func -# update_func_file error_handler.func -# update_func_file tools.func -# break -# ;; -# [Dd]*) -# for file in core.func error_handler.func tools.func; do -# local_path="$FUNC_DIR/$file" -# url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/$file" -# remote_tmp="$(mktemp)" - -# curl -fsSL "$url" -o "$remote_tmp" || continue - -# if [ -f "$local_path" ]; then -# echo "🔍 Diff for $file:" -# diff -u "$local_path" "$remote_tmp" || echo "(no differences)" -# else -# echo "📦 New file $file will be installed" -# fi - -# rm -f "$remote_tmp" -# done -# ;; -# *) -# echo "❌ Skipped updating local core files" -# break -# ;; -# esac -# done -# else -# if [ "$DEVMODE" != "yes" ]; then -# echo "✔️ build.func unchanged → using existing local core files" -# fi -# fi - -# if [ -n "${_COMMUNITY_SCRIPTS_LOADER:-}" ]; then -# return 0 2>/dev/null || exit 0 -# fi -# _COMMUNITY_SCRIPTS_LOADER=1 - -# # --- Step 3: always source local versions of the core files --- -# source "$FUNC_DIR/core.func" -# source "$FUNC_DIR/error_handler.func" -# source "$FUNC_DIR/tools.func" - -# # --- Step 4: finally, source build.func directly from memory --- -# # (no tmp file needed) -# source <(printf "%s" "$build_content") - -# ------------------------------------------------------------------------------ -# Load core + error handler functions from community-scripts repo -# -# - Prefer curl if available, fallback to wget -# - Load: core.func, error_handler.func, api.func -# - Initialize error traps after loading -# ------------------------------------------------------------------------------ - -source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/api.func) - -if command -v curl >/dev/null 2>&1; then - source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/core.func) - source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/error_handler.func) - load_functions - catch_errors - #echo "(build.func) Loaded core.func via curl" -elif command -v wget >/dev/null 2>&1; then - source <(wget -qO- https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/core.func) - source <(wget -qO- https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/error_handler.func) - load_functions - catch_errors - #echo "(build.func) Loaded core.func via wget" -fi - -# ------------------------------------------------------------------------------ -# maxkeys_check() -# -# - Reads kernel keyring limits (maxkeys, maxbytes) -# - Checks current usage for LXC user (UID 100000) -# - Warns if usage is close to limits and suggests sysctl tuning -# - Exits if thresholds are exceeded -# - https://cleveruptime.com/docs/files/proc-key-users | https://docs.kernel.org/security/keys/core.html -# ------------------------------------------------------------------------------ - -maxkeys_check() { - # Read kernel parameters - per_user_maxkeys=$(cat /proc/sys/kernel/keys/maxkeys 2>/dev/null || echo 0) - per_user_maxbytes=$(cat /proc/sys/kernel/keys/maxbytes 2>/dev/null || echo 0) - - # Exit if kernel parameters are unavailable - if [[ "$per_user_maxkeys" -eq 0 || "$per_user_maxbytes" -eq 0 ]]; then - echo -e "${CROSS}${RD} Error: Unable to read kernel parameters. Ensure proper permissions.${CL}" - exit 1 - fi - - # Fetch key usage for user ID 100000 (typical for containers) - used_lxc_keys=$(awk '/100000:/ {print $2}' /proc/key-users 2>/dev/null || echo 0) - used_lxc_bytes=$(awk '/100000:/ {split($5, a, "/"); print a[1]}' /proc/key-users 2>/dev/null || echo 0) - - # Calculate thresholds and suggested new limits - threshold_keys=$((per_user_maxkeys - 100)) - threshold_bytes=$((per_user_maxbytes - 1000)) - new_limit_keys=$((per_user_maxkeys * 2)) - new_limit_bytes=$((per_user_maxbytes * 2)) - - # Check if key or byte usage is near limits - failure=0 - if [[ "$used_lxc_keys" -gt "$threshold_keys" ]]; then - echo -e "${CROSS}${RD} Warning: Key usage is near the limit (${used_lxc_keys}/${per_user_maxkeys}).${CL}" - echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxkeys=${new_limit_keys}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." - failure=1 - fi - if [[ "$used_lxc_bytes" -gt "$threshold_bytes" ]]; then - echo -e "${CROSS}${RD} Warning: Key byte usage is near the limit (${used_lxc_bytes}/${per_user_maxbytes}).${CL}" - echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxbytes=${new_limit_bytes}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." - failure=1 - fi - - # Provide next steps if issues are detected - if [[ "$failure" -eq 1 ]]; then - echo -e "${INFO} To apply changes, run: ${BOLD}service procps force-reload${CL}" - exit 1 - fi - - echo -e "${CM}${GN} All kernel key limits are within safe thresholds.${CL}" -} - -# ------------------------------------------------------------------------------ -# get_current_ip() -# -# - Returns current container IP depending on OS type -# - Debian/Ubuntu: uses `hostname -I` -# - Alpine: parses eth0 via `ip -4 addr` -# ------------------------------------------------------------------------------ -get_current_ip() { - if [ -f /etc/os-release ]; then - # Check for Debian/Ubuntu (uses hostname -I) - if grep -qE 'ID=debian|ID=ubuntu' /etc/os-release; then - CURRENT_IP=$(hostname -I | awk '{print $1}') - # Check for Alpine (uses ip command) - elif grep -q 'ID=alpine' /etc/os-release; then - CURRENT_IP=$(ip -4 addr show eth0 | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) - else - CURRENT_IP="Unknown" - fi - fi - echo "$CURRENT_IP" -} - -# ------------------------------------------------------------------------------ -# update_motd_ip() -# -# - Updates /etc/motd with current container IP -# - Removes old IP entries to avoid duplicates -# ------------------------------------------------------------------------------ -update_motd_ip() { - MOTD_FILE="/etc/motd" - - if [ -f "$MOTD_FILE" ]; then - # Remove existing IP Address lines to prevent duplication - sed -i '/IP Address:/d' "$MOTD_FILE" - - IP=$(get_current_ip) - # Add the new IP address - echo -e "${TAB}${NETWORK}${YW} IP Address: ${GN}${IP}${CL}" >>"$MOTD_FILE" - fi -} - -# ------------------------------------------------------------------------------ -# ssh_check() -# -# - Detects if script is running over SSH -# - Warns user and recommends using Proxmox shell -# - User can choose to continue or abort -# ------------------------------------------------------------------------------ -ssh_check() { - if [ -n "${SSH_CLIENT:+x}" ]; then - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's advisable to utilize the Proxmox shell rather than SSH, as there may be potential complications with variable retrieval. Proceed using SSH?" 10 72; then - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox --title "Proceed using SSH" "You've chosen to proceed using SSH. If any issues arise, please run the script in the Proxmox shell before creating a repository issue." 10 72 - else - clear - echo "Exiting due to SSH usage. Please consider using the Proxmox shell." - exit - fi - fi -} - -# ------------------------------------------------------------------------------ -# install_ssh_keys_into_ct() -# -# - Installs SSH keys into container root account if SSH is enabled -# - Uses pct push or direct input to authorized_keys -# - Falls back to warning if no keys provided -# ------------------------------------------------------------------------------ -install_ssh_keys_into_ct() { - [[ "$SSH" != "yes" ]] && return 0 - - if [[ -n "$SSH_KEYS_FILE" && -s "$SSH_KEYS_FILE" ]]; then - msg_info "Installing selected SSH keys into CT ${CTID}" - pct exec "$CTID" -- sh -c 'mkdir -p /root/.ssh && chmod 700 /root/.ssh' || { - msg_error "prepare /root/.ssh failed" - return 1 - } - pct push "$CTID" "$SSH_KEYS_FILE" /root/.ssh/authorized_keys >/dev/null 2>&1 || - pct exec "$CTID" -- sh -c "cat > /root/.ssh/authorized_keys" <"$SSH_KEYS_FILE" || { - msg_error "write authorized_keys failed" - return 1 - } - pct exec "$CTID" -- sh -c 'chmod 600 /root/.ssh/authorized_keys' || true - msg_ok "Installed SSH keys into CT ${CTID}" - return 0 - fi - - # Fallback: nichts ausgewählt - msg_warn "No SSH keys to install (skipping)." - return 0 -} - -# ------------------------------------------------------------------------------ -# base_settings() -# -# - Defines all base/default variables for container creation -# - Reads from environment variables (var_*) -# - Provides fallback defaults for OS type/version -# ------------------------------------------------------------------------------ -base_settings() { - # Default Settings - CT_TYPE=${var_unprivileged:-"1"} - DISK_SIZE=${var_disk:-"4"} - CORE_COUNT=${var_cpu:-"1"} - RAM_SIZE=${var_ram:-"1024"} - VERBOSE=${var_verbose:-"${1:-no}"} - PW=${var_pw:-""} - CT_ID=${var_ctid:-$NEXTID} - HN=${var_hostname:-$NSAPP} - BRG=${var_brg:-"vmbr0"} - NET=${var_net:-"dhcp"} - IPV6_METHOD=${var_ipv6_method:-"none"} - IPV6_STATIC=${var_ipv6_static:-""} - GATE=${var_gateway:-""} - APT_CACHER=${var_apt_cacher:-""} - APT_CACHER_IP=${var_apt_cacher_ip:-""} - MTU=${var_mtu:-""} - SD=${var_storage:-""} - NS=${var_ns:-""} - MAC=${var_mac:-""} - VLAN=${var_vlan:-""} - SSH=${var_ssh:-"no"} - SSH_AUTHORIZED_KEY=${var_ssh_authorized_key:-""} - UDHCPC_FIX=${var_udhcpc_fix:-""} - TAGS="community-script,${var_tags:-}" - ENABLE_FUSE=${var_fuse:-"${1:-no}"} - ENABLE_TUN=${var_tun:-"${1:-no}"} - - # Since these 2 are only defined outside of default_settings function, we add a temporary fallback. TODO: To align everything, we should add these as constant variables (e.g. OSTYPE and OSVERSION), but that would currently require updating the default_settings function for all existing scripts - if [ -z "$var_os" ]; then - var_os="debian" - fi - if [ -z "$var_version" ]; then - var_version="12" - fi -} - -# ------------------------------------------------------------------------------ -# echo_default() -# -# - Prints summary of default values (ID, OS, type, disk, RAM, CPU, etc.) -# - Uses icons and formatting for readability -# - Convert CT_TYPE to description -# ------------------------------------------------------------------------------ -echo_default() { - CT_TYPE_DESC="Unprivileged" - if [ "$CT_TYPE" -eq 0 ]; then - CT_TYPE_DESC="Privileged" - fi - - echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}${CT_ID}${CL}" - echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os ($var_version)${CL}" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" - if [ "$VERBOSE" == "yes" ]; then - echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}Enabled${CL}" - fi - echo -e "${CREATING}${BOLD}${BL}Creating a ${APP} LXC using the above default settings${CL}" - echo -e " " -} - -# ------------------------------------------------------------------------------ -# exit_script() -# -# - Called when user cancels an action -# - Clears screen and exits gracefully -# ------------------------------------------------------------------------------ -exit_script() { - clear - echo -e "\n${CROSS}${RD}User exited script${CL}\n" - exit -} - -# ------------------------------------------------------------------------------ -# find_host_ssh_keys() -# -# - Scans system for available SSH keys -# - Supports defaults (~/.ssh, /etc/ssh/authorized_keys) -# - Returns list of files containing valid SSH public keys -# - Sets FOUND_HOST_KEY_COUNT to number of keys found -# ------------------------------------------------------------------------------ -find_host_ssh_keys() { - local re='(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))' - local -a files=() cand=() - local g="${var_ssh_import_glob:-}" - local total=0 f base c - - shopt -s nullglob - if [[ -n "$g" ]]; then - for pat in $g; do cand+=($pat); done - else - cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2) - cand+=(/root/.ssh/*.pub) - cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*) - fi - shopt -u nullglob - - for f in "${cand[@]}"; do - [[ -f "$f" && -r "$f" ]] || continue - base="$(basename -- "$f")" - case "$base" in - known_hosts | known_hosts.* | config) continue ;; - id_*) [[ "$f" != *.pub ]] && continue ;; - esac - - # CRLF safe check for host keys - c=$(tr -d '\r' <"$f" | awk ' - /^[[:space:]]*#/ {next} - /^[[:space:]]*$/ {next} - {print} - ' | grep -E -c '"$re"' || true) - - if ((c > 0)); then - files+=("$f") - total=$((total + c)) - fi - done - - # Fallback to /root/.ssh/authorized_keys - if ((${#files[@]} == 0)) && [[ -r /root/.ssh/authorized_keys ]]; then - if grep -E -q "$re" /root/.ssh/authorized_keys; then - files+=(/root/.ssh/authorized_keys) - total=$((total + $(grep -E -c "$re" /root/.ssh/authorized_keys || echo 0))) - fi - fi - - FOUND_HOST_KEY_COUNT="$total" - ( - IFS=: - echo "${files[*]}" - ) -} - -# ------------------------------------------------------------------------------ -# advanced_settings() -# -# - Interactive whiptail menu for advanced configuration -# - Lets user set container type, password, CT ID, hostname, disk, CPU, RAM -# - Supports IPv4/IPv6, DNS, MAC, VLAN, tags, SSH keys, FUSE, verbose mode -# - Ends with confirmation or re-entry if cancelled -# ------------------------------------------------------------------------------ -advanced_settings() { - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox --title "Here is an instructional tip:" "To make a selection, use the Spacebar." 8 58 - # Setting Default Tag for Advanced Settings - TAGS="community-script;${var_tags:-}" - CT_DEFAULT_TYPE="${CT_TYPE}" - CT_TYPE="" - while [ -z "$CT_TYPE" ]; do - if [ "$CT_DEFAULT_TYPE" == "1" ]; then - if CT_TYPE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ - "1" "Unprivileged" ON \ - "0" "Privileged" OFF \ - 3>&1 1>&2 2>&3); then - if [ -n "$CT_TYPE" ]; then - CT_TYPE_DESC="Unprivileged" - if [ "$CT_TYPE" -eq 0 ]; then - CT_TYPE_DESC="Privileged" - fi - echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os | ${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" - fi - else - exit_script - fi - fi - if [ "$CT_DEFAULT_TYPE" == "0" ]; then - if CT_TYPE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ - "1" "Unprivileged" OFF \ - "0" "Privileged" ON \ - 3>&1 1>&2 2>&3); then - if [ -n "$CT_TYPE" ]; then - CT_TYPE_DESC="Unprivileged" - if [ "$CT_TYPE" -eq 0 ]; then - CT_TYPE_DESC="Privileged" - fi - echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os${CL}" - echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" - fi - else - exit_script - fi - fi - done - - while true; do - if PW1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --passwordbox "\nSet Root Password (needed for root ssh access)" 9 58 --title "PASSWORD (leave blank for automatic login)" 3>&1 1>&2 2>&3); then - # Empty = Autologin - if [[ -z "$PW1" ]]; then - PW="" - PW1="Automatic Login" - echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}$PW1${CL}" - break - fi - - # Invalid: contains spaces - if [[ "$PW1" == *" "* ]]; then - whiptail --msgbox "Password cannot contain spaces." 8 58 - continue - fi - - # Invalid: too short - if ((${#PW1} < 5)); then - whiptail --msgbox "Password must be at least 5 characters." 8 58 - continue - fi - - # Confirm password - if PW2=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then - if [[ "$PW1" == "$PW2" ]]; then - PW="-password $PW1" - echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}" - break - else - whiptail --msgbox "Passwords do not match. Please try again." 8 58 - fi - else - exit_script - fi - else - exit_script - fi - done - - if CT_ID=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Container ID" 8 58 "$NEXTID" --title "CONTAINER ID" 3>&1 1>&2 2>&3); then - if [ -z "$CT_ID" ]; then - CT_ID="$NEXTID" - echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" - else - echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" - fi - else - exit_script - fi - - while true; do - if CT_NAME=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 "$NSAPP" --title "HOSTNAME" 3>&1 1>&2 2>&3); then - if [ -z "$CT_NAME" ]; then - HN="$NSAPP" - else - HN=$(echo "${CT_NAME,,}" | tr -d ' ') - fi - # Hostname validate (RFC 1123) - if [[ "$HN" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ ]]; then - echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" - break - else - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --msgbox "❌ Invalid hostname: '$HN'\n\nOnly lowercase letters, digits and hyphens (-) are allowed.\nUnderscores (_) or other characters are not permitted!" 10 70 - fi - else - exit_script - fi - done - - while true; do - DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GB" 8 58 "$var_disk" --title "DISK SIZE" 3>&1 1>&2 2>&3) || exit_script - - if [ -z "$DISK_SIZE" ]; then - DISK_SIZE="$var_disk" - fi - - if [[ "$DISK_SIZE" =~ ^[1-9][0-9]*$ ]]; then - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" - break - else - whiptail --msgbox "Disk size must be a positive integer!" 8 58 - fi - done - - while true; do - CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ - --inputbox "Allocate CPU Cores" 8 58 "$var_cpu" --title "CORE COUNT" 3>&1 1>&2 2>&3) || exit_script - - if [ -z "$CORE_COUNT" ]; then - CORE_COUNT="$var_cpu" - fi - - if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" - break - else - whiptail --msgbox "CPU core count must be a positive integer!" 8 58 - fi - done - - while true; do - RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ - --inputbox "Allocate RAM in MiB" 8 58 "$var_ram" --title "RAM" 3>&1 1>&2 2>&3) || exit_script - - if [ -z "$RAM_SIZE" ]; then - RAM_SIZE="$var_ram" - fi - - if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" - break - else - whiptail --msgbox "RAM size must be a positive integer!" 8 58 - fi - done - - IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f) - BRIDGES="" - OLD_IFS=$IFS - IFS=$'\n' - for iface_filepath in ${IFACE_FILEPATH_LIST}; do - - iface_indexes_tmpfile=$(mktemp -q -u '.iface-XXXX') - (grep -Pn '^\s*iface' "${iface_filepath}" | cut -d':' -f1 && wc -l "${iface_filepath}" | cut -d' ' -f1) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' >"${iface_indexes_tmpfile}" || true - - if [ -f "${iface_indexes_tmpfile}" ]; then - - while read -r pair; do - start=$(echo "${pair}" | cut -d':' -f1) - end=$(echo "${pair}" | cut -d':' -f2) - - if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then - iface_name=$(sed "${start}q;d" "${iface_filepath}" | awk '{print $2}') - BRIDGES="${iface_name}"$'\n'"${BRIDGES}" - fi - - done <"${iface_indexes_tmpfile}" - rm -f "${iface_indexes_tmpfile}" - fi - - done - IFS=$OLD_IFS - BRIDGES=$(echo "$BRIDGES" | grep -v '^\s*$' | sort | uniq) - if [[ -z "$BRIDGES" ]]; then - BRG="vmbr0" - echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" - else - BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge:" 15 40 6 $(echo "$BRIDGES" | awk '{print $0, "Bridge"}') 3>&1 1>&2 2>&3) - if [[ -z "$BRG" ]]; then - exit_script - else - echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" - fi - fi - - # IPv4 methods: dhcp, static, none - while true; do - IPV4_METHOD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "IPv4 Address Management" \ - --menu "Select IPv4 Address Assignment Method:" 12 60 2 \ - "dhcp" "Automatic (DHCP, recommended)" \ - "static" "Static (manual entry)" \ - 3>&1 1>&2 2>&3) - - exit_status=$? - if [ $exit_status -ne 0 ]; then - exit_script - fi - - case "$IPV4_METHOD" in - dhcp) - NET="dhcp" - GATE="" - echo -e "${NETWORK}${BOLD}${DGN}IPv4: DHCP${CL}" - break - ;; - static) - # Static: call and validate CIDR address - while true; do - NET=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --inputbox "Enter Static IPv4 CIDR Address (e.g. 192.168.100.50/24)" 8 58 "" \ - --title "IPv4 ADDRESS" 3>&1 1>&2 2>&3) - if [ -z "$NET" ]; then - whiptail --msgbox "IPv4 address must not be empty." 8 58 - continue - elif [[ "$NET" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then - echo -e "${NETWORK}${BOLD}${DGN}IPv4 Address: ${BGN}$NET${CL}" - break - else - whiptail --msgbox "$NET is not a valid IPv4 CIDR address. Please enter a correct value!" 8 58 - fi - done - - # call and validate Gateway - while true; do - GATE1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --inputbox "Enter Gateway IP address for static IPv4" 8 58 "" \ - --title "Gateway IP" 3>&1 1>&2 2>&3) - if [ -z "$GATE1" ]; then - whiptail --msgbox "Gateway IP address cannot be empty." 8 58 - elif [[ ! "$GATE1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then - whiptail --msgbox "Invalid Gateway IP address format." 8 58 - else - GATE=",gw=$GATE1" - echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE1${CL}" - break - fi - done - break - ;; - esac - done - - # IPv6 Address Management selection - while true; do - IPV6_METHOD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --menu \ - "Select IPv6 Address Management Type:" 15 58 4 \ - "auto" "SLAAC/AUTO (recommended, default)" \ - "dhcp" "DHCPv6" \ - "static" "Static (manual entry)" \ - "none" "Disabled" \ - --default-item "auto" 3>&1 1>&2 2>&3) - [ $? -ne 0 ] && exit_script - - case "$IPV6_METHOD" in - auto) - echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}SLAAC/AUTO${CL}" - IPV6_ADDR="" - IPV6_GATE="" - break - ;; - dhcp) - echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}DHCPv6${CL}" - IPV6_ADDR="dhcp" - IPV6_GATE="" - break - ;; - static) - # Ask for static IPv6 address (CIDR notation, e.g., 2001:db8::1234/64) - while true; do - IPV6_ADDR=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox \ - "Set a static IPv6 CIDR address (e.g., 2001:db8::1234/64)" 8 58 "" \ - --title "IPv6 STATIC ADDRESS" 3>&1 1>&2 2>&3) || exit_script - if [[ "$IPV6_ADDR" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+(/[0-9]{1,3})$ ]]; then - echo -e "${NETWORK}${BOLD}${DGN}IPv6 Address: ${BGN}$IPV6_ADDR${CL}" - break - else - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox \ - "$IPV6_ADDR is an invalid IPv6 CIDR address. Please enter a valid IPv6 CIDR address (e.g., 2001:db8::1234/64)" 8 58 - fi - done - # Optional: ask for IPv6 gateway for static config - while true; do - IPV6_GATE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox \ - "Enter IPv6 gateway address (optional, leave blank for none)" 8 58 "" --title "IPv6 GATEWAY" 3>&1 1>&2 2>&3) - if [ -z "$IPV6_GATE" ]; then - IPV6_GATE="" - break - elif [[ "$IPV6_GATE" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+$ ]]; then - break - else - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox \ - "Invalid IPv6 gateway format." 8 58 - fi - done - break - ;; - none) - echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}Disabled${CL}" - IPV6_ADDR="none" - IPV6_GATE="" - break - ;; - *) - exit_script - ;; - esac - done - - if [ "$var_os" == "alpine" ]; then - APT_CACHER="" - APT_CACHER_IP="" - else - if APT_CACHER_IP=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set APT-Cacher IP (leave blank for none)" 8 58 --title "APT-Cacher IP" 3>&1 1>&2 2>&3); then - APT_CACHER="${APT_CACHER_IP:+yes}" - echo -e "${NETWORK}${BOLD}${DGN}APT-Cacher IP Address: ${BGN}${APT_CACHER_IP:-Default}${CL}" - else - exit_script - fi - fi - - # if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "IPv6" --yesno "Disable IPv6?" 10 58); then - # DISABLEIP6="yes" - # else - # DISABLEIP6="no" - # fi - # echo -e "${DISABLEIPV6}${BOLD}${DGN}Disable IPv6: ${BGN}$DISABLEIP6${CL}" - - if MTU1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default [The MTU of your selected vmbr, default is 1500])" 8 58 --title "MTU SIZE" 3>&1 1>&2 2>&3); then - if [ -z "$MTU1" ]; then - MTU1="Default" - MTU="" - else - MTU=",mtu=$MTU1" - fi - echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" - else - exit_script - fi - - if SD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a DNS Search Domain (leave blank for HOST)" 8 58 --title "DNS Search Domain" 3>&1 1>&2 2>&3); then - if [ -z "$SD" ]; then - SX=Host - SD="" - else - SX=$SD - SD="-searchdomain=$SD" - fi - echo -e "${SEARCH}${BOLD}${DGN}DNS Search Domain: ${BGN}$SX${CL}" - else - exit_script - fi - - if NX=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a DNS Server IP (leave blank for HOST)" 8 58 --title "DNS SERVER IP" 3>&1 1>&2 2>&3); then - if [ -z "$NX" ]; then - NX=Host - NS="" - else - NS="-nameserver=$NX" - fi - echo -e "${NETWORK}${BOLD}${DGN}DNS Server IP Address: ${BGN}$NX${CL}" - else - exit_script - fi - - if [ "$var_os" == "alpine" ] && [ "$NET" == "dhcp" ] && [ "$NX" != "Host" ]; then - UDHCPC_FIX="yes" - else - UDHCPC_FIX="no" - fi - export UDHCPC_FIX - - if MAC1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a MAC Address(leave blank for generated MAC)" 8 58 --title "MAC ADDRESS" 3>&1 1>&2 2>&3); then - if [ -z "$MAC1" ]; then - MAC1="Default" - MAC="" - else - MAC=",hwaddr=$MAC1" - echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" - fi - else - exit_script - fi - - if VLAN1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for no VLAN)" 8 58 --title "VLAN" 3>&1 1>&2 2>&3); then - if [ -z "$VLAN1" ]; then - VLAN1="Default" - VLAN="" - else - VLAN=",tag=$VLAN1" - fi - echo -e "${VLANTAG}${BOLD}${DGN}Vlan: ${BGN}$VLAN1${CL}" - else - exit_script - fi - - if ADV_TAGS=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Custom Tags?[If you remove all, there will be no tags!]" 8 58 "${TAGS}" --title "Advanced Tags" 3>&1 1>&2 2>&3); then - if [ -n "${ADV_TAGS}" ]; then - ADV_TAGS=$(echo "$ADV_TAGS" | tr -d '[:space:]') - TAGS="${ADV_TAGS}" - else - TAGS=";" - fi - echo -e "${NETWORK}${BOLD}${DGN}Tags: ${BGN}$TAGS${CL}" - else - exit_script - fi - - # --- SSH key provisioning (one dialog) --- - SSH_KEYS_FILE="$(mktemp)" - : >"$SSH_KEYS_FILE" - - IFS=$'\0' read -r -d '' -a _def_files < <(ssh_discover_default_files && printf '\0') - ssh_build_choices_from_files "${_def_files[@]}" - DEF_KEYS_COUNT="$COUNT" - - if [[ "$DEF_KEYS_COUNT" -gt 0 ]]; then - SSH_KEY_MODE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ - "Provision SSH keys for root:" 14 72 4 \ - "found" "Select from detected keys (${DEF_KEYS_COUNT})" \ - "manual" "Paste a single public key" \ - "folder" "Scan another folder (path or glob)" \ - "none" "No keys" 3>&1 1>&2 2>&3) || exit_script - else - SSH_KEY_MODE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ - "No host keys detected; choose manual/none:" 12 72 2 \ - "manual" "Paste a single public key" \ - "none" "No keys" 3>&1 1>&2 2>&3) || exit_script - fi - - case "$SSH_KEY_MODE" in - found) - SEL=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT HOST KEYS" \ - --checklist "Select one or more keys to import:" 20 140 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script - for tag in $SEL; do - tag="${tag%\"}" - tag="${tag#\"}" - line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) - [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" - done - ;; - manual) - SSH_AUTHORIZED_KEY="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --inputbox "Paste one SSH public key line (ssh-ed25519/ssh-rsa/...)" 10 72 --title "SSH Public Key" 3>&1 1>&2 2>&3)" - [[ -n "$SSH_AUTHORIZED_KEY" ]] && printf '%s\n' "$SSH_AUTHORIZED_KEY" >>"$SSH_KEYS_FILE" - ;; - folder) - GLOB_PATH="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --inputbox "Enter a folder or glob to scan (e.g. /root/.ssh/*.pub)" 10 72 --title "Scan Folder/Glob" 3>&1 1>&2 2>&3)" - if [[ -n "$GLOB_PATH" ]]; then - shopt -s nullglob - read -r -a _scan_files <<<"$GLOB_PATH" - shopt -u nullglob - if [[ "${#_scan_files[@]}" -gt 0 ]]; then - ssh_build_choices_from_files "${_scan_files[@]}" - if [[ "$COUNT" -gt 0 ]]; then - SEL=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT FOLDER KEYS" \ - --checklist "Select key(s) to import:" 20 78 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script - for tag in $SEL; do - tag="${tag%\"}" - tag="${tag#\"}" - line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) - [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" - done - else - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "No keys found in: $GLOB_PATH" 8 60 - fi - else - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "Path/glob returned no files." 8 60 - fi - fi - ;; - none) : ;; - esac - - # Dedupe + clean EOF - if [[ -s "$SSH_KEYS_FILE" ]]; then - sort -u -o "$SSH_KEYS_FILE" "$SSH_KEYS_FILE" - printf '\n' >>"$SSH_KEYS_FILE" - fi - - # SSH activate, if keys found or password set - if [[ -s "$SSH_KEYS_FILE" || "$PW" == -password* ]]; then - if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable root SSH access?" 10 58); then - SSH="yes" - else - SSH="no" - fi - else - SSH="no" - fi - echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}" - - export SSH_KEYS_FILE - if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "FUSE Support" --yesno "Enable FUSE support?\nRequired for tools like rclone, mergerfs, AppImage, etc." 10 58); then - ENABLE_FUSE="yes" - else - ENABLE_FUSE="no" - fi - echo -e "${FUSE}${BOLD}${DGN}Enable FUSE Support: ${BGN}$ENABLE_FUSE${CL}" - - if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then - VERBOSE="yes" - else - VERBOSE="no" - fi - echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}" - - if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create ${APP} LXC?" 10 58); then - echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}" - else - clear - header_info - echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings on node $PVEHOST_NAME${CL}" - advanced_settings - fi -} - -# ------------------------------------------------------------------------------ -# diagnostics_check() -# -# - Ensures diagnostics config file exists at /usr/local/community-scripts/diagnostics -# - Asks user whether to send anonymous diagnostic data -# - Saves DIAGNOSTICS=yes/no in the config file -# ------------------------------------------------------------------------------ -diagnostics_check() { - if ! [ -d "/usr/local/community-scripts" ]; then - mkdir -p /usr/local/community-scripts - fi - - if ! [ -f "/usr/local/community-scripts/diagnostics" ]; then - if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS" --yesno "Send Diagnostics of LXC Installation?\n\n(This only transmits data without user data, just RAM, CPU, LXC name, ...)" 10 58); then - cat </usr/local/community-scripts/diagnostics -DIAGNOSTICS=yes - -#This file is used to store the diagnostics settings for the Community-Scripts API. -#https://github.com/community-scripts/ProxmoxVED/discussions/1836 -#Your diagnostics will be sent to the Community-Scripts API for troubleshooting/statistical purposes. -#You can review the data at https://community-scripts.github.io/ProxmoxVE/data -#If you do not wish to send diagnostics, please set the variable 'DIAGNOSTICS' to "no" in /usr/local/community-scripts/diagnostics, or use the menue. -#This will disable the diagnostics feature. -#To send diagnostics, set the variable 'DIAGNOSTICS' to "yes" in /usr/local/community-scripts/diagnostics, or use the menue. -#This will enable the diagnostics feature. -#The following information will be sent: -#"ct_type" -#"disk_size" -#"core_count" -#"ram_size" -#"os_type" -#"os_version" -#"disableip6" -#"nsapp" -#"method" -#"pve_version" -#"status" -#If you have any concerns, please review the source code at /misc/build.func -EOF - DIAGNOSTICS="yes" - else - cat </usr/local/community-scripts/diagnostics -DIAGNOSTICS=no - -#This file is used to store the diagnostics settings for the Community-Scripts API. -#https://github.com/community-scripts/ProxmoxVED/discussions/1836 -#Your diagnostics will be sent to the Community-Scripts API for troubleshooting/statistical purposes. -#You can review the data at https://community-scripts.github.io/ProxmoxVE/data -#If you do not wish to send diagnostics, please set the variable 'DIAGNOSTICS' to "no" in /usr/local/community-scripts/diagnostics, or use the menue. -#This will disable the diagnostics feature. -#To send diagnostics, set the variable 'DIAGNOSTICS' to "yes" in /usr/local/community-scripts/diagnostics, or use the menue. -#This will enable the diagnostics feature. -#The following information will be sent: -#"ct_type" -#"disk_size" -#"core_count" -#"ram_size" -#"os_type" -#"os_version" -#"disableip6" -#"nsapp" -#"method" -#"pve_version" -#"status" -#If you have any concerns, please review the source code at /misc/build.func -EOF - DIAGNOSTICS="no" - fi - else - DIAGNOSTICS=$(awk -F '=' '/^DIAGNOSTICS/ {print $2}' /usr/local/community-scripts/diagnostics) - - fi - -} - -# ------------------------------------------------------------------------------ -# default_var_settings -# -# - Ensures /usr/local/community-scripts/default.vars exists (creates if missing) -# - Loads var_* values from default.vars (safe parser, no source/eval) -# - Precedence: ENV var_* > default.vars > built-in defaults -# - Maps var_verbose → VERBOSE -# - Calls base_settings "$VERBOSE" and echo_default -# ------------------------------------------------------------------------------ -default_var_settings() { - # Allowed var_* keys (alphabetically sorted) - local VAR_WHITELIST=( - var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_ctid var_disk var_fuse - var_gateway var_hostname var_ipv6_method var_ipv6_static var_mac var_mtu - var_net var_ns var_pw var_ram var_tags var_tun var_unprivileged - var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage - ) - - # Snapshot: environment variables (highest precedence) - declare -A _HARD_ENV=() - local _k - for _k in "${VAR_WHITELIST[@]}"; do - if printenv "$_k" >/dev/null 2>&1; then _HARD_ENV["$_k"]=1; fi - done - - # Find default.vars location - local _find_default_vars - _find_default_vars() { - local f - for f in \ - /usr/local/community-scripts/default.vars \ - "$HOME/.config/community-scripts/default.vars" \ - "./default.vars"; do - [ -f "$f" ] && { - echo "$f" - return 0 - } - done - return 1 - } - # Allow override of storages via env (for non-interactive use cases) - [ -n "${var_template_storage:-}" ] && TEMPLATE_STORAGE="$var_template_storage" - [ -n "${var_container_storage:-}" ] && CONTAINER_STORAGE="$var_container_storage" - - # Create once, with storages already selected, no var_ctid/var_hostname lines - local _ensure_default_vars - _ensure_default_vars() { - _find_default_vars >/dev/null 2>&1 && return 0 - - local canonical="/usr/local/community-scripts/default.vars" - msg_info "No default.vars found. Creating ${canonical}" - mkdir -p /usr/local/community-scripts - - # Pick storages before writing the file (always ask unless only one) - # Create a minimal temp file to write into - : >"$canonical" - - # Base content (no var_ctid / var_hostname here) - cat >"$canonical" <<'EOF' -# Community-Scripts defaults (var_* only). Lines starting with # are comments. -# Precedence: ENV var_* > default.vars > built-ins. -# Keep keys alphabetically sorted. - -# Container type -var_unprivileged=1 - -# Resources -var_cpu=1 -var_disk=4 -var_ram=1024 - -# Network -var_brg=vmbr0 -var_net=dhcp -var_ipv6_method=none -# var_gateway= -# var_ipv6_static= -# var_vlan= -# var_mtu= -# var_mac= -# var_ns= - -# SSH -var_ssh=no -# var_ssh_authorized_key= - -# APT cacher (optional) -# var_apt_cacher=yes -# var_apt_cacher_ip=192.168.1.10 - -# Features/Tags/verbosity -var_fuse=no -var_tun=no -var_tags=community-script -var_verbose=no - -# Security (root PW) – empty => autologin -# var_pw= -EOF - - # Now choose storages (always prompt unless just one exists) - choose_and_set_storage_for_file "$canonical" template - choose_and_set_storage_for_file "$canonical" container - - chmod 0644 "$canonical" - msg_ok "Created ${canonical}" - } - - # Whitelist check - local _is_whitelisted_key - _is_whitelisted_key() { - local k="$1" - local w - for w in "${VAR_WHITELIST[@]}"; do [ "$k" = "$w" ] && return 0; done - return 1 - } - - # Safe parser for KEY=VALUE lines - local _load_vars_file - _load_vars_file() { - local file="$1" - [ -f "$file" ] || return 0 - msg_info "Loading defaults from ${file}" - local line key val - while IFS= read -r line || [ -n "$line" ]; do - line="${line#"${line%%[![:space:]]*}"}" - line="${line%"${line##*[![:space:]]}"}" - [[ -z "$line" || "$line" == \#* ]] && continue - if [[ "$line" =~ ^([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then - local var_key="${BASH_REMATCH[1]}" - local var_val="${BASH_REMATCH[2]}" - - [[ "$var_key" != var_* ]] && continue - _is_whitelisted_key "$var_key" || { - msg_debug "Ignore non-whitelisted ${var_key}" - continue - } - - # Strip quotes - if [[ "$var_val" =~ ^\"(.*)\"$ ]]; then - var_val="${BASH_REMATCH[1]}" - elif [[ "$var_val" =~ ^\'(.*)\'$ ]]; then - var_val="${BASH_REMATCH[1]}" - fi - - # Unsafe characters - case $var_val in - \"*\") - var_val=${var_val#\"} - var_val=${var_val%\"} - ;; - \'*\') - var_val=${var_val#\'} - var_val=${var_val%\'} - ;; - esac # Hard env wins - [[ -n "${_HARD_ENV[$var_key]:-}" ]] && continue - # Set only if not already exported - [[ -z "${!var_key+x}" ]] && export "${var_key}=${var_val}" - else - msg_warn "Malformed line in ${file}: ${line}" - fi - done <"$file" - msg_ok "Loaded ${file}" - } - - # 1) Ensure file exists - _ensure_default_vars - - # 2) Load file - local dv - dv="$(_find_default_vars)" || { - msg_error "default.vars not found after ensure step" - return 1 - } - _load_vars_file "$dv" - - # 3) Map var_verbose → VERBOSE - if [[ -n "${var_verbose:-}" ]]; then - case "${var_verbose,,}" in 1 | yes | true | on) VERBOSE="yes" ;; 0 | no | false | off) VERBOSE="no" ;; *) VERBOSE="${var_verbose}" ;; esac - else - VERBOSE="no" - fi - - # 4) Apply base settings and show summary - METHOD="mydefaults-global" - base_settings "$VERBOSE" - header_info - echo -e "${DEFAULT}${BOLD}${BL}Using My Defaults (default.vars) on node $PVEHOST_NAME${CL}" - echo_default -} - -# ------------------------------------------------------------------------------ -# get_app_defaults_path() -# -# - Returns full path for app-specific defaults file -# - Example: /usr/local/community-scripts/defaults/.vars -# ------------------------------------------------------------------------------ - -get_app_defaults_path() { - local n="${NSAPP:-${APP,,}}" - echo "/usr/local/community-scripts/defaults/${n}.vars" -} - -# ------------------------------------------------------------------------------ -# maybe_offer_save_app_defaults -# -# - Called after advanced_settings returned with fully chosen values. -# - If no .vars exists, offers to persist current advanced settings -# into /usr/local/community-scripts/defaults/.vars -# - Only writes whitelisted var_* keys. -# - Extracts raw values from flags like ",gw=..." ",mtu=..." etc. -# ------------------------------------------------------------------------------ -if ! declare -p VAR_WHITELIST >/dev/null 2>&1; then - declare -ag VAR_WHITELIST=( - var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_ctid var_disk var_fuse - var_gateway var_hostname var_ipv6_method var_ipv6_static var_mac var_mtu - var_net var_ns var_pw var_ram var_tags var_tun var_unprivileged - var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage - ) -fi - -_is_whitelisted_key() { - local k="$1" - local w - for w in "${VAR_WHITELIST[@]}"; do [[ "$k" == "$w" ]] && return 0; done - return 1 -} - -_sanitize_value() { - # Disallow Command-Substitution / Shell-Meta - case "$1" in - *'$('* | *'`'* | *';'* | *'&'* | *'<('*) - echo "" - return 0 - ;; - esac - echo "$1" -} - -# Map-Parser: read var_* from file into _VARS_IN associative array -declare -A _VARS_IN -_load_vars_file() { - local file="$1" - [ -f "$file" ] || return 0 - msg_info "Loading defaults from ${file}" - local line key val - while IFS= read -r line || [ -n "$line" ]; do - line="${line#"${line%%[![:space:]]*}"}" - line="${line%"${line##*[![:space:]]}"}" - [ -z "$line" ] && continue - case "$line" in - \#*) continue ;; - esac - key=$(printf "%s" "$line" | cut -d= -f1) - val=$(printf "%s" "$line" | cut -d= -f2-) - case "$key" in - var_*) - if _is_whitelisted_key "$key"; then - [ -z "${!key+x}" ] && export "$key=$val" - fi - ;; - esac - done <"$file" - msg_ok "Loaded ${file}" -} - -# Diff function for two var_* files -> produces human-readable diff list for $1 (old) vs $2 (new) -_build_vars_diff() { - local oldf="$1" newf="$2" - local k - local -A OLD=() NEW=() - _load_vars_file_to_map "$oldf" - for k in "${!_VARS_IN[@]}"; do OLD["$k"]="${_VARS_IN[$k]}"; done - _load_vars_file_to_map "$newf" - for k in "${!_VARS_IN[@]}"; do NEW["$k"]="${_VARS_IN[$k]}"; done - - local out - out+="# Diff for ${APP} (${NSAPP})\n" - out+="# Old: ${oldf}\n# New: ${newf}\n\n" - - local found_change=0 - - # Changed & Removed - for k in "${!OLD[@]}"; do - if [[ -v NEW["$k"] ]]; then - if [[ "${OLD[$k]}" != "${NEW[$k]}" ]]; then - out+="~ ${k}\n - old: ${OLD[$k]}\n + new: ${NEW[$k]}\n" - found_change=1 - fi - else - out+="- ${k}\n - old: ${OLD[$k]}\n" - found_change=1 - fi - done - - # Added - for k in "${!NEW[@]}"; do - if [[ ! -v OLD["$k"] ]]; then - out+="+ ${k}\n + new: ${NEW[$k]}\n" - found_change=1 - fi - done - - if [[ $found_change -eq 0 ]]; then - out+="(No differences)\n" - fi - - printf "%b" "$out" -} - -# Build a temporary .vars file from current advanced settings -_build_current_app_vars_tmp() { - tmpf="$(mktemp /tmp/${NSAPP:-app}.vars.new.XXXXXX)" - - # NET/GW - _net="${NET:-}" - _gate="" - case "${GATE:-}" in - ,gw=*) _gate=$(echo "$GATE" | sed 's/^,gw=//') ;; - esac - - # IPv6 - _ipv6_method="${IPV6_METHOD:-auto}" - _ipv6_static="" - _ipv6_gateway="" - if [ "$_ipv6_method" = "static" ]; then - _ipv6_static="${IPV6_ADDR:-}" - _ipv6_gateway="${IPV6_GATE:-}" - fi - - # MTU/VLAN/MAC - _mtu="" - _vlan="" - _mac="" - case "${MTU:-}" in - ,mtu=*) _mtu=$(echo "$MTU" | sed 's/^,mtu=//') ;; - esac - case "${VLAN:-}" in - ,tag=*) _vlan=$(echo "$VLAN" | sed 's/^,tag=//') ;; - esac - case "${MAC:-}" in - ,hwaddr=*) _mac=$(echo "$MAC" | sed 's/^,hwaddr=//') ;; - esac - - # DNS / Searchdomain - _ns="" - _searchdomain="" - case "${NS:-}" in - -nameserver=*) _ns=$(echo "$NS" | sed 's/^-nameserver=//') ;; - esac - case "${SD:-}" in - -searchdomain=*) _searchdomain=$(echo "$SD" | sed 's/^-searchdomain=//') ;; - esac - - # SSH / APT / Features - _ssh="${SSH:-no}" - _ssh_auth="${SSH_AUTHORIZED_KEY:-}" - _apt_cacher="${APT_CACHER:-}" - _apt_cacher_ip="${APT_CACHER_IP:-}" - _fuse="${ENABLE_FUSE:-no}" - _tun="${ENABLE_TUN:-no}" - _tags="${TAGS:-}" - _verbose="${VERBOSE:-no}" - - # Type / Resources / Identity - _unpriv="${CT_TYPE:-1}" - _cpu="${CORE_COUNT:-1}" - _ram="${RAM_SIZE:-1024}" - _disk="${DISK_SIZE:-4}" - _hostname="${HN:-$NSAPP}" - - # Storage - _tpl_storage="${TEMPLATE_STORAGE:-${var_template_storage:-}}" - _ct_storage="${CONTAINER_STORAGE:-${var_container_storage:-}}" - - { - echo "# App-specific defaults for ${APP} (${NSAPP})" - echo "# Generated on $(date -u '+%Y-%m-%dT%H:%M:%SZ')" - echo - - echo "var_unprivileged=$(_sanitize_value "$_unpriv")" - echo "var_cpu=$(_sanitize_value "$_cpu")" - echo "var_ram=$(_sanitize_value "$_ram")" - echo "var_disk=$(_sanitize_value "$_disk")" - - [ -n "${BRG:-}" ] && echo "var_brg=$(_sanitize_value "$BRG")" - [ -n "$_net" ] && echo "var_net=$(_sanitize_value "$_net")" - [ -n "$_gate" ] && echo "var_gateway=$(_sanitize_value "$_gate")" - [ -n "$_mtu" ] && echo "var_mtu=$(_sanitize_value "$_mtu")" - [ -n "$_vlan" ] && echo "var_vlan=$(_sanitize_value "$_vlan")" - [ -n "$_mac" ] && echo "var_mac=$(_sanitize_value "$_mac")" - [ -n "$_ns" ] && echo "var_ns=$(_sanitize_value "$_ns")" - - [ -n "$_ipv6_method" ] && echo "var_ipv6_method=$(_sanitize_value "$_ipv6_method")" - [ -n "$_ipv6_static" ] && echo "var_ipv6_static=$(_sanitize_value "$_ipv6_static")" - - [ -n "$_ssh" ] && echo "var_ssh=$(_sanitize_value "$_ssh")" - [ -n "$_ssh_auth" ] && echo "var_ssh_authorized_key=$(_sanitize_value "$_ssh_auth")" - - [ -n "$_apt_cacher" ] && echo "var_apt_cacher=$(_sanitize_value "$_apt_cacher")" - [ -n "$_apt_cacher_ip" ] && echo "var_apt_cacher_ip=$(_sanitize_value "$_apt_cacher_ip")" - - [ -n "$_fuse" ] && echo "var_fuse=$(_sanitize_value "$_fuse")" - [ -n "$_tun" ] && echo "var_tun=$(_sanitize_value "$_tun")" - [ -n "$_tags" ] && echo "var_tags=$(_sanitize_value "$_tags")" - [ -n "$_verbose" ] && echo "var_verbose=$(_sanitize_value "$_verbose")" - - [ -n "$_hostname" ] && echo "var_hostname=$(_sanitize_value "$_hostname")" - [ -n "$_searchdomain" ] && echo "var_searchdomain=$(_sanitize_value "$_searchdomain")" - - [ -n "$_tpl_storage" ] && echo "var_template_storage=$(_sanitize_value "$_tpl_storage")" - [ -n "$_ct_storage" ] && echo "var_container_storage=$(_sanitize_value "$_ct_storage")" - } >"$tmpf" - - echo "$tmpf" -} - -# ------------------------------------------------------------------------------ -# maybe_offer_save_app_defaults() -# -# - Called after advanced_settings() -# - Offers to save current values as app defaults if not existing -# - If file exists: shows diff and allows Update, Keep, View Diff, or Cancel -# ------------------------------------------------------------------------------ -maybe_offer_save_app_defaults() { - local app_vars_path - app_vars_path="$(get_app_defaults_path)" - - # always build from current settings - local new_tmp diff_tmp - new_tmp="$(_build_current_app_vars_tmp)" - diff_tmp="$(mktemp -p /tmp "${NSAPP:-app}.vars.diff.XXXXXX")" - - # 1) if no file → offer to create - if [[ ! -f "$app_vars_path" ]]; then - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --yesno "Save these advanced settings as defaults for ${APP}?\n\nThis will create:\n${app_vars_path}" 12 72; then - mkdir -p "$(dirname "$app_vars_path")" - install -m 0644 "$new_tmp" "$app_vars_path" - msg_ok "Saved app defaults: ${app_vars_path}" - fi - rm -f "$new_tmp" "$diff_tmp" - return 0 - fi - - # 2) if file exists → build diff - _build_vars_diff "$app_vars_path" "$new_tmp" >"$diff_tmp" - - # if no differences → do nothing - if grep -q "^(No differences)$" "$diff_tmp"; then - rm -f "$new_tmp" "$diff_tmp" - return 0 - fi - - # 3) if file exists → show menu with default selection "Update Defaults" - local app_vars_file - app_vars_file="$(basename "$app_vars_path")" - - while true; do - local sel - sel="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "APP DEFAULTS – ${APP}" \ - --menu "Differences detected. What do you want to do?" 20 78 10 \ - "Update Defaults" "Write new values to ${app_vars_file}" \ - "Keep Current" "Keep existing defaults (no changes)" \ - "View Diff" "Show a detailed diff" \ - "Cancel" "Abort without changes" \ - --default-item "Update Defaults" \ - 3>&1 1>&2 2>&3)" || { sel="Cancel"; } - - case "$sel" in - "Update Defaults") - install -m 0644 "$new_tmp" "$app_vars_path" - msg_ok "Updated app defaults: ${app_vars_path}" - break - ;; - "Keep Current") - msg_info "Keeping current app defaults: ${app_vars_path}" - break - ;; - "View Diff") - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "Diff – ${APP}" \ - --scrolltext --textbox "$diff_tmp" 25 100 - ;; - "Cancel" | *) - msg_info "Canceled. No changes to app defaults." - break - ;; - esac - done - - rm -f "$new_tmp" "$diff_tmp" -} - -ensure_storage_selection_for_vars_file() { - vf="$1" - - # Read stored values (if any) - tpl=$(grep -E '^var_template_storage=' "$vf" | cut -d= -f2-) - ct=$(grep -E '^var_container_storage=' "$vf" | cut -d= -f2-) - - # Template storage - if [ -n "$tpl" ]; then - TEMPLATE_STORAGE="$tpl" - else - choose_and_set_storage_for_file "$vf" template - fi - - # Container storage - if [ -n "$ct" ]; then - CONTAINER_STORAGE="$ct" - else - choose_and_set_storage_for_file "$vf" container - fi - - echo_storage_summary_from_file "$vf" -} - -diagnostics_menu() { - if [ "${DIAGNOSTICS:-no}" = "yes" ]; then - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "DIAGNOSTIC SETTINGS" \ - --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ - --yes-button "No" --no-button "Back"; then - DIAGNOSTICS="no" - sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=no/' /usr/local/community-scripts/diagnostics - whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 - fi - else - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "DIAGNOSTIC SETTINGS" \ - --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ - --yes-button "Yes" --no-button "Back"; then - DIAGNOSTICS="yes" - sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=yes/' /usr/local/community-scripts/diagnostics - whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 - fi - fi -} - -# ------------------------------------------------------------------------------ -# install_script() -# -# - Main entrypoint for installation mode -# - Runs safety checks (pve_check, root_check, maxkeys_check, diagnostics_check) -# - Builds interactive menu (Default, Verbose, Advanced, My Defaults, App Defaults, Diagnostics, Storage, Exit) -# - Applies chosen settings and triggers container build -# ------------------------------------------------------------------------------ -install_script() { - pve_check - shell_check - root_check - arch_check - ssh_check - maxkeys_check - diagnostics_check - - if systemctl is-active -q ping-instances.service; then - systemctl -q stop ping-instances.service - fi - - NEXTID=$(pvesh get /cluster/nextid) - timezone=$(cat /etc/timezone) - header_info - - # --- Support CLI argument as direct preset (default, advanced, …) --- - CHOICE="${mode:-${1:-}}" - - # If no CLI argument → show whiptail menu - if [ -z "$CHOICE" ]; then - local menu_items=( - "1" "Default Install" - "2" "Advanced Install" - "3" "My Defaults" - ) - - if [ -f "$(get_app_defaults_path)" ]; then - menu_items+=("4" "App Defaults for ${APP}") - menu_items+=("5" "Settings") - else - menu_items+=("4" "Settings") - fi - - TMP_CHOICE=$(whiptail \ - --backtitle "Proxmox VE Helper Scripts" \ - --title "Community-Scripts Options" \ - --ok-button "Select" --cancel-button "Exit Script" \ - --notags \ - --menu "\nChoose an option:\n Use TAB or Arrow keys to navigate, ENTER to select.\n" \ - 20 60 9 \ - "${menu_items[@]}" \ - --default-item "1" \ - 3>&1 1>&2 2>&3) || exit_script - CHOICE="$TMP_CHOICE" - fi - - # --- Main case --- - case "$CHOICE" in - 1 | default | DEFAULT) - header_info - echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME${CL}" - VERBOSE="no" - METHOD="default" - base_settings "$VERBOSE" - echo_default - ensure_storage_selection_for_vars_file "/usr/local/community-scripts/default.vars" - ;; - 2 | advanced | ADVANCED) - header_info - echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Install on node $PVEHOST_NAME${CL}" - METHOD="advanced" - base_settings - advanced_settings - ensure_storage_selection_for_vars_file "/usr/local/community-scripts/default.vars" - maybe_offer_save_app_defaults - ;; - 3 | mydefaults | MYDEFAULTS) - default_var_settings || { - msg_error "Failed to apply default.vars" - exit 1 - } - ensure_storage_selection_for_vars_file "/usr/local/community-scripts/default.vars" - ;; - 4 | appdefaults | APPDEFAULTS) - if [ -f "$(get_app_defaults_path)" ]; then - header_info - echo -e "${DEFAULT}${BOLD}${BL}Using App Defaults for ${APP} on node $PVEHOST_NAME${CL}" - METHOD="appdefaults" - base_settings - _load_vars_file "$(get_app_defaults_path)" - echo_default - ensure_storage_selection_for_vars_file "$(get_app_defaults_path)" - else - msg_error "No App Defaults available for ${APP}" - exit 1 - fi - ;; - 5 | settings | SETTINGS) - settings_menu - ;; - *) - echo -e "${CROSS}${RD}Invalid option: $CHOICE${CL}" - exit 1 - ;; - esac -} - -# ------------------------------------------------------------------------------ -# check_storage_or_prompt() -# -# - Validates container/template storage -# - If invalid or missing, prompts user to select new storage -# - Updates vars file accordingly -# ------------------------------------------------------------------------------ -# check_storage_or_prompt() { -# local vars_file="$1" -# local changed=0 - -# if [ ! -f "$vars_file" ]; then -# msg_warn "No vars file found at $vars_file" -# return 0 -# fi - -# # Helper: validate storage ID -# _validate_storage() { -# local s="$1" -# [ -n "$s" ] || return 1 -# pvesm status -content images | awk 'NR>1 {print $1}' | grep -qx "$s" -# } - -# # Load current values (empty if not set) -# local ct_store tpl_store -# ct_store=$(awk -F= '/^var_container_storage=/ {print $2; exit}' "$vars_file") -# tpl_store=$(awk -F= '/^var_template_storage=/ {print $2; exit}' "$vars_file") - -# # Container storage -# if ! _validate_storage "$ct_store"; then -# local new_ct -# new_ct=$(pvesm status -content images | awk 'NR>1 {print $1" "$2" "$6}') -# new_ct=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "Select Container Storage" \ -# --menu "Choose container storage:" 20 60 10 $new_ct 3>&1 1>&2 2>&3) || return 0 -# if [ -n "$new_ct" ]; then -# sed -i "/^var_container_storage=/d" "$vars_file" -# echo "var_container_storage=$new_ct" >>"$vars_file" -# changed=1 -# msg_ok "Updated container storage in $vars_file → $new_ct" -# fi -# fi - -# # Template storage -# if ! _validate_storage "$tpl_store"; then -# local new_tpl -# new_tpl=$(pvesm status -content vztmpl | awk 'NR>1 {print $1" "$2" "$6}') -# new_tpl=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "Select Template Storage" \ -# --menu "Choose template storage:" 20 60 10 $new_tpl 3>&1 1>&2 2>&3) || return 0 -# if [ -n "$new_tpl" ]; then -# sed -i "/^var_template_storage=/d" "$vars_file" -# echo "var_template_storage=$new_tpl" >>"$vars_file" -# changed=1 -# msg_ok "Updated template storage in $vars_file → $new_tpl" -# fi -# fi - -# # Always succeed (no aborts from here) -# return 0 -# } - -# # ------------------------------------------------------------------------------ -# # storage_settings_menu() -# # -# # - Menu for managing storage defaults -# # - Options: update My Defaults or App Defaults storage -# # ------------------------------------------------------------------------------ -# storage_settings_menu() { -# local vars_file="/usr/local/community-scripts/default.vars" - -# check_storage_or_prompt "$vars_file" -# _echo_storage_summary "$vars_file" - -# # Always ask user if they want to update, even if values are valid -# if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "STORAGE SETTINGS" \ -# --yesno "Do you want to update the storage defaults?\n\nCurrent values will be kept unless you select new ones." 12 72; then - -# # container storage selection -# local new_ct -# new_ct=$(pvesm status -content images | awk 'NR>1 {print $1" "$2" "$6}') -# new_ct=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "Select Container Storage" \ -# --menu "Choose container storage:" 20 60 10 $new_ct 3>&1 1>&2 2>&3) || true -# if [ -n "$new_ct" ]; then -# sed -i '/^var_container_storage=/d' "$vars_file" -# echo "var_container_storage=$new_ct" >>"$vars_file" -# msg_ok "Updated container storage → $new_ct" -# fi - -# # template storage selection -# local new_tpl -# new_tpl=$(pvesm status -content vztmpl | awk 'NR>1 {print $1" "$2" "$6}') -# new_tpl=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ -# --title "Select Template Storage" \ -# --menu "Choose template storage:" 20 60 10 $new_tpl 3>&1 1>&2 2>&3) || true -# if [ -n "$new_tpl" ]; then -# sed -i '/^var_template_storage=/d' "$vars_file" -# echo "var_template_storage=$new_tpl" >>"$vars_file" -# msg_ok "Updated template storage → $new_tpl" -# fi -# fi -# } - -edit_default_storage() { - local vf="/usr/local/community-scripts/default.vars" - - # make sure file exists - if [ ! -f "$vf" ]; then - msg_info "No default.vars found, creating $vf" - mkdir -p /usr/local/community-scripts - touch "$vf" - fi - - # reuse the same Whiptail selection we already have - ensure_storage_selection_for_vars_file "$vf" -} - -settings_menu() { - while true; do - local settings_items=( - "1" "Manage API-Diagnostic Setting" - "2" "Edit Default.vars" - "3" "Edit Default Storage" - ) - if [ -f "$(get_app_defaults_path)" ]; then - settings_items+=("4" "Edit App.vars for ${APP}") - settings_items+=("5" "Exit") - else - settings_items+=("4" "Exit") - fi - - local choice - choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ - --title "Community-Scripts SETTINGS Menu" \ - --ok-button "OK" --cancel-button "Back" \ - --menu "\n\nChoose a settings option:\n\nUse TAB or Arrow keys to navigate, ENTER to select." 20 60 9 \ - "${settings_items[@]}" \ - 3>&1 1>&2 2>&3) || break - - case "$choice" in - 1) diagnostics_menu ;; - 2) ${EDITOR:-nano} /usr/local/community-scripts/default.vars ;; - 3) edit_default_storage ;; - 4) - if [ -f "$(get_app_defaults_path)" ]; then - ${EDITOR:-nano} "$(get_app_defaults_path)" - else - exit_script - fi - ;; - 5) exit_script ;; - esac - done -} - -# ===== Unified storage selection & writing to vars files ===== -_write_storage_to_vars() { - # $1 = vars_file, $2 = key (var_container_storage / var_template_storage), $3 = value - local vf="$1" key="$2" val="$3" - # remove uncommented and commented versions to avoid duplicates - sed -i "/^[#[:space:]]*${key}=/d" "$vf" - echo "${key}=${val}" >>"$vf" -} - -choose_and_set_storage_for_file() { - # $1 = vars_file, $2 = class ('container'|'template') - local vf="$1" class="$2" key="" current="" - case "$class" in - container) key="var_container_storage" ;; - template) key="var_template_storage" ;; - *) - msg_error "Unknown storage class: $class" - return 1 - ;; - esac - - current=$(awk -F= -v k="^${key}=" '$0 ~ k {print $2; exit}' "$vf") - - # If only one storage exists for the content type, auto-pick. Else always ask (your wish #4). - local content="rootdir" - [[ "$class" == "template" ]] && content="vztmpl" - local count - count=$(pvesm status -content "$content" | awk 'NR>1{print $1}' | wc -l) - - if [[ "$count" -eq 1 ]]; then - STORAGE_RESULT=$(pvesm status -content "$content" | awk 'NR>1{print $1; exit}') - STORAGE_INFO="" - else - # If the current value is preselectable, we could show it, but per your requirement we always offer selection - select_storage "$class" || return 1 - fi - - _write_storage_to_vars "$vf" "$key" "$STORAGE_RESULT" - - # Keep environment in sync for later steps (e.g. app-default save) - if [[ "$class" == "container" ]]; then - export var_container_storage="$STORAGE_RESULT" - export CONTAINER_STORAGE="$STORAGE_RESULT" - else - export var_template_storage="$STORAGE_RESULT" - export TEMPLATE_STORAGE="$STORAGE_RESULT" - fi - - msg_ok "Updated ${key} → ${STORAGE_RESULT}" -} - -echo_storage_summary_from_file() { - local vars_file="$1" - local ct_store tpl_store - ct_store=$(awk -F= '/^var_container_storage=/ {print $2; exit}' "$vars_file") - tpl_store=$(awk -F= '/^var_template_storage=/ {print $2; exit}' "$vars_file") - if [ -n "$tpl" ]; then - TEMPLATE_STORAGE="$tpl" - else - choose_and_set_storage_for_file "$vf" template - fi - - if [ -n "$ct" ]; then - CONTAINER_STORAGE="$ct" - else - choose_and_set_storage_for_file "$vf" container - fi -} - -# ------------------------------------------------------------------------------ -# check_container_resources() -# -# - Compares host RAM/CPU with required values -# - Warns if under-provisioned and asks user to continue or abort -# ------------------------------------------------------------------------------ -check_container_resources() { - current_ram=$(free -m | awk 'NR==2{print $2}') - current_cpu=$(nproc) - - if [[ "$current_ram" -lt "$var_ram" ]] || [[ "$current_cpu" -lt "$var_cpu" ]]; then - echo -e "\n${INFO}${HOLD} ${GN}Required: ${var_cpu} CPU, ${var_ram}MB RAM ${CL}| ${RD}Current: ${current_cpu} CPU, ${current_ram}MB RAM${CL}" - echo -e "${YWB}Please ensure that the ${APP} LXC is configured with at least ${var_cpu} vCPU and ${var_ram} MB RAM for the build process.${CL}\n" - echo -ne "${INFO}${HOLD} May cause data loss! ${INFO} Continue update with under-provisioned LXC? " - read -r prompt - if [[ ! ${prompt,,} =~ ^(yes)$ ]]; then - echo -e "${CROSS}${HOLD} ${YWB}Exiting based on user input.${CL}" - exit 1 - fi - else - echo -e "" - fi -} - -# ------------------------------------------------------------------------------ -# check_container_storage() -# -# - Checks /boot partition usage -# - Warns if usage >80% and asks user confirmation before proceeding -# ------------------------------------------------------------------------------ -check_container_storage() { - total_size=$(df /boot --output=size | tail -n 1) - local used_size=$(df /boot --output=used | tail -n 1) - usage=$((100 * used_size / total_size)) - if ((usage > 80)); then - echo -e "${INFO}${HOLD} ${YWB}Warning: Storage is dangerously low (${usage}%).${CL}" - echo -ne "Continue anyway? " - read -r prompt - if [[ ! ${prompt,,} =~ ^(y|yes)$ ]]; then - echo -e "${CROSS}${HOLD}${YWB}Exiting based on user input.${CL}" - exit 1 - fi - fi -} - -# ------------------------------------------------------------------------------ -# ssh_extract_keys_from_file() -# -# - Extracts valid SSH public keys from given file -# - Supports RSA, Ed25519, ECDSA and filters out comments/invalid lines -# ------------------------------------------------------------------------------ -ssh_extract_keys_from_file() { - local f="$1" - [[ -r "$f" ]] || return 0 - tr -d '\r' <"$f" | awk ' - /^[[:space:]]*#/ {next} - /^[[:space:]]*$/ {next} - # nackt: typ base64 [comment] - /^(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/ {print; next} - # mit Optionen: finde ab erstem Key-Typ - { - match($0, /(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/) - if (RSTART>0) { print substr($0, RSTART) } - } - ' -} - -# ------------------------------------------------------------------------------ -# ssh_build_choices_from_files() -# -# - Builds interactive whiptail checklist of available SSH keys -# - Generates fingerprint, type and comment for each key -# ------------------------------------------------------------------------------ -ssh_build_choices_from_files() { - local -a files=("$@") - CHOICES=() - COUNT=0 - MAPFILE="$(mktemp)" - local id key typ fp cmt base ln=0 - - for f in "${files[@]}"; do - [[ -f "$f" && -r "$f" ]] || continue - base="$(basename -- "$f")" - case "$base" in - known_hosts | known_hosts.* | config) continue ;; - id_*) [[ "$f" != *.pub ]] && continue ;; - esac - - # map every key in file - while IFS= read -r key; do - [[ -n "$key" ]] || continue - - typ="" - fp="" - cmt="" - # Only the pure key part (without options) is already included in ‘key’. - read -r _typ _b64 _cmt <<<"$key" - typ="${_typ:-key}" - cmt="${_cmt:-}" - # Fingerprint via ssh-keygen (if available) - if command -v ssh-keygen >/dev/null 2>&1; then - fp="$(printf '%s\n' "$key" | ssh-keygen -lf - 2>/dev/null | awk '{print $2}')" - fi - # Label shorten - [[ ${#cmt} -gt 40 ]] && cmt="${cmt:0:37}..." - - ln=$((ln + 1)) - COUNT=$((COUNT + 1)) - id="K${COUNT}" - echo "${id}|${key}" >>"$MAPFILE" - CHOICES+=("$id" "[$typ] ${fp:+$fp }${cmt:+$cmt }— ${base}" "OFF") - done < <(ssh_extract_keys_from_file "$f") - done -} - -# ------------------------------------------------------------------------------ -# ssh_discover_default_files() -# -# - Scans standard paths for SSH keys -# - Includes ~/.ssh/*.pub, /etc/ssh/authorized_keys, etc. -# ------------------------------------------------------------------------------ -ssh_discover_default_files() { - local -a cand=() - shopt -s nullglob - cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2) - cand+=(/root/.ssh/*.pub) - cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*) - shopt -u nullglob - printf '%s\0' "${cand[@]}" -} - -# ------------------------------------------------------------------------------ -# start() -# -# - Entry point of script -# - On Proxmox host: calls install_script -# - In silent mode: runs update_script -# - Otherwise: shows update/setting menu -# ------------------------------------------------------------------------------ -start() { - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) - if command -v pveversion >/dev/null 2>&1; then - install_script || return 0 - return 0 - elif [ ! -z ${PHS_SILENT+x} ] && [[ "${PHS_SILENT}" == "1" ]]; then - VERBOSE="no" - set_std_mode - update_script - else - CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "${APP} LXC Update/Setting" --menu \ - "Support/Update functions for ${APP} LXC. Choose an option:" \ - 12 60 3 \ - "1" "YES (Silent Mode)" \ - "2" "YES (Verbose Mode)" \ - "3" "NO (Cancel Update)" --nocancel --default-item "1" 3>&1 1>&2 2>&3) - - case "$CHOICE" in - 1) - VERBOSE="no" - set_std_mode - ;; - 2) - VERBOSE="yes" - set_std_mode - ;; - 3) - clear - exit_script - exit - ;; - esac - update_script - fi -} - -# ------------------------------------------------------------------------------ -# build_container() -# -# - Creates and configures the LXC container -# - Builds network string and applies features (FUSE, TUN, VAAPI passthrough) -# - Starts container and waits for network connectivity -# - Installs base packages, SSH keys, and runs -install.sh -# ------------------------------------------------------------------------------ -build_container() { - # if [ "$VERBOSE" == "yes" ]; then set -x; fi - - NET_STRING="-net0 name=eth0,bridge=${BRG:-vmbr0}" - NET_STRING+="${MAC:+,hwaddr=$MAC}" - NET_STRING+=",ip=${NET:-dhcp}" - NET_STRING+="${GATE:+,gw=$GATE}" - NET_STRING+="${VLAN:+,tag=$VLAN}" - NET_STRING+="${MTU:+,mtu=$MTU}" - case "$IPV6_METHOD" in - auto) NET_STRING="$NET_STRING,ip6=auto" ;; - dhcp) NET_STRING="$NET_STRING,ip6=dhcp" ;; - static) - NET_STRING="$NET_STRING,ip6=$IPV6_ADDR" - [ -n "$IPV6_GATE" ] && NET_STRING="$NET_STRING,gw6=$IPV6_GATE" - ;; - none) ;; - esac - - if [ "$CT_TYPE" == "1" ]; then - FEATURES="keyctl=1,nesting=1" - else - FEATURES="nesting=1" - fi - - if [ "$ENABLE_FUSE" == "yes" ]; then - FEATURES="$FEATURES,fuse=1" - fi - - #if [[ $DIAGNOSTICS == "yes" ]]; then - # post_to_api - #fi - - TEMP_DIR=$(mktemp -d) - pushd "$TEMP_DIR" >/dev/null - if [ "$var_os" == "alpine" ]; then - export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-install.func)" - else - export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" - fi - export DIAGNOSTICS="$DIAGNOSTICS" - export RANDOM_UUID="$RANDOM_UUID" - export CACHER="$APT_CACHER" - export CACHER_IP="$APT_CACHER_IP" - export tz="$timezone" - #export DISABLEIPV6="$DISABLEIP6" - export APPLICATION="$APP" - export app="$NSAPP" - export PASSWORD="$PW" - export VERBOSE="$VERBOSE" - export SSH_ROOT="${SSH}" - export SSH_AUTHORIZED_KEY - export CTID="$CT_ID" - export CTTYPE="$CT_TYPE" - export ENABLE_FUSE="$ENABLE_FUSE" - export ENABLE_TUN="$ENABLE_TUN" - export PCT_OSTYPE="$var_os" - export PCT_OSVERSION="${var_version%%.*}" - export PCT_DISK_SIZE="$DISK_SIZE" - export PCT_OPTIONS=" - -features $FEATURES - -hostname $HN - -tags $TAGS - $SD - $NS - $NET_STRING - -onboot 1 - -cores $CORE_COUNT - -memory $RAM_SIZE - -unprivileged $CT_TYPE - $PW -" - export TEMPLATE_STORAGE="${var_template_storage:-}" - export CONTAINER_STORAGE="${var_container_storage:-}" - create_lxc_container || exit $? - - LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" - - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/passthrough.func) - select_hw_passthrough "$CTID" "$CT_TYPE" "$APP" - - # TUN device passthrough - if [ "$ENABLE_TUN" == "yes" ]; then - cat <>"$LXC_CONFIG" -lxc.cgroup2.devices.allow: c 10:200 rwm -lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file -EOF - fi - - # This starts the container and executes -install.sh - msg_info "Starting LXC Container" - pct start "$CTID" - - # wait for status 'running' - for i in {1..10}; do - if pct status "$CTID" | grep -q "status: running"; then - msg_ok "Started LXC Container" - break - fi - sleep 1 - if [ "$i" -eq 10 ]; then - msg_error "LXC Container did not reach running state" - exit 1 - fi - done - - if [ "$var_os" != "alpine" ]; then - msg_info "Waiting for network in LXC container" - - # --- Step 1: Wait for IP --- - for i in {1..20}; do - ip_in_lxc=$(pct exec "$CTID" -- ip -4 addr show dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) - [ -n "$ip_in_lxc" ] && break - sleep 1 - done - - if [ -z "$ip_in_lxc" ]; then - msg_error "No IP assigned to CT $CTID after 20s" - exit 1 - fi - - # --- Step 2: Try to reach gateway --- - gw_ok=0 - for i in {1..10}; do - if pct exec "$CTID" -- ping -c1 -W1 "$GATEWAY" >/dev/null 2>&1; then - gw_ok=1 - break - fi - sleep 1 - done - - if [ "$gw_ok" -eq 1 ]; then - msg_ok "CT $CTID gateway $GATEWAY reachable (IP $ip_in_lxc)" - else - msg_warn "CT $CTID has IP $ip_in_lxc but gateway $GATEWAY did not reply" - fi - - # --- Step 3: DNS / Internet check --- - if pct exec "$CTID" -- getent hosts deb.debian.org >/dev/null 2>&1; then - msg_ok "Network in LXC is reachable (DNS OK, IP $ip_in_lxc)" - else - msg_warn "Network reachable (IP $ip_in_lxc), but DNS failed – applying fallback resolv.conf" - pct exec "$CTID" -- bash -c 'echo "nameserver 1.1.1.1" > /etc/resolv.conf && echo "nameserver 8.8.8.8" >> /etc/resolv.conf' - if pct exec "$CTID" -- getent hosts deb.debian.org >/dev/null 2>&1; then - msg_ok "Network reachable after DNS fallback" - else - msg_error "Still no DNS/network in LXC! Aborting customization." - exit_script - fi - fi - fi - - msg_info "Customizing LXC Container" - #if [ "$var_os" != "alpine" ]; then - # vaapi_inside_setup "$CTID" "$CT_TYPE" "$APP" - # nvidia_inside_setup "$CTID" "$CT_TYPE" "$APP" - #fi - if [ "$var_os" == "alpine" ]; then - sleep 3 - pct exec "$CTID" -- /bin/sh -c 'cat </etc/apk/repositories -http://dl-cdn.alpinelinux.org/alpine/latest-stable/main -http://dl-cdn.alpinelinux.org/alpine/latest-stable/community -EOF' - pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses jq >/dev/null" - else - sleep 3 - - pct exec "$CTID" -- bash -c "sed -i '/$LANG/ s/^# //' /etc/locale.gen" - pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \ - echo LANG=\$locale_line >/etc/default/locale && \ - locale-gen >/dev/null && \ - export LANG=\$locale_line" - - if [[ -z "${tz:-}" ]]; then - tz=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "Etc/UTC") - fi - - if pct exec "$CTID" -- test -e "/usr/share/zoneinfo/$tz"; then - pct exec "$CTID" -- bash -c "tz='$tz'; echo \"\$tz\" >/etc/timezone && ln -sf \"/usr/share/zoneinfo/\$tz\" /etc/localtime" - else - msg_warn "Skipping timezone setup – zone '$tz' not found in container" - fi - - pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y sudo curl mc gnupg2 jq >/dev/null" || { - msg_error "apt-get base packages installation failed" - exit 1 - } - fi - msg_ok "Customized LXC Container" - install_ssh_keys_into_ct - if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/install/${var_install}.sh)"; then - exit $? - fi -} - -destroy_lxc() { - if [[ -z "$CT_ID" ]]; then - msg_error "No CT_ID found. Nothing to remove." - return 1 - fi - - # Abbruch bei Ctrl-C / Ctrl-D / ESC - trap 'echo; msg_error "Aborted by user (SIGINT/SIGQUIT)"; return 130' INT QUIT - - local prompt - if ! read -rp "Remove this Container? " prompt; then - # read gibt != 0 zurück bei Ctrl-D/ESC - msg_error "Aborted input (Ctrl-D/ESC)" - return 130 - fi - - case "${prompt,,}" in - y | yes) - if pct stop "$CT_ID" &>/dev/null && pct destroy "$CT_ID" &>/dev/null; then - msg_ok "Removed Container $CT_ID" - else - msg_error "Failed to remove Container $CT_ID" - return 1 - fi - ;; - "" | n | no) - msg_info "Container was not removed." - ;; - *) - msg_warn "Invalid response. Container was not removed." - ;; - esac -} - -# ------------------------------------------------------------------------------ -# Storage discovery / selection helpers -# ------------------------------------------------------------------------------ -# ===== Storage discovery / selection helpers (ported from create_lxc.sh) ===== -resolve_storage_preselect() { - local class="$1" preselect="$2" required_content="" - case "$class" in - template) required_content="vztmpl" ;; - container) required_content="rootdir" ;; - *) return 1 ;; - esac - [[ -z "$preselect" ]] && return 1 - if ! pvesm status -content "$required_content" | awk 'NR>1{print $1}' | grep -qx -- "$preselect"; then - msg_warn "Preselected storage '${preselect}' does not support content '${required_content}' (or not found)" - return 1 - fi - - local line total used free - line="$(pvesm status | awk -v s="$preselect" 'NR>1 && $1==s {print $0}')" - if [[ -z "$line" ]]; then - STORAGE_INFO="n/a" - else - total="$(awk '{print $4}' <<<"$line")" - used="$(awk '{print $5}' <<<"$line")" - free="$(awk '{print $6}' <<<"$line")" - local total_h used_h free_h - if command -v numfmt >/dev/null 2>&1; then - total_h="$(numfmt --to=iec --suffix=B --format %.1f "$total" 2>/dev/null || echo "$total")" - used_h="$(numfmt --to=iec --suffix=B --format %.1f "$used" 2>/dev/null || echo "$used")" - free_h="$(numfmt --to=iec --suffix=B --format %.1f "$free" 2>/dev/null || echo "$free")" - STORAGE_INFO="Free: ${free_h} Used: ${used_h}" - else - STORAGE_INFO="Free: ${free} Used: ${used}" - fi - fi - STORAGE_RESULT="$preselect" - return 0 -} - -check_storage_support() { - local CONTENT="$1" VALID=0 - while IFS= read -r line; do - local STORAGE_NAME - STORAGE_NAME=$(awk '{print $1}' <<<"$line") - [[ -n "$STORAGE_NAME" ]] && VALID=1 - done < <(pvesm status -content "$CONTENT" 2>/dev/null | awk 'NR>1') - [[ $VALID -eq 1 ]] -} - -select_storage() { - local CLASS=$1 CONTENT CONTENT_LABEL - case $CLASS in - container) - CONTENT='rootdir' - CONTENT_LABEL='Container' - ;; - template) - CONTENT='vztmpl' - CONTENT_LABEL='Container template' - ;; - iso) - CONTENT='iso' - CONTENT_LABEL='ISO image' - ;; - images) - CONTENT='images' - CONTENT_LABEL='VM Disk image' - ;; - backup) - CONTENT='backup' - CONTENT_LABEL='Backup' - ;; - snippets) - CONTENT='snippets' - CONTENT_LABEL='Snippets' - ;; - *) - msg_error "Invalid storage class '$CLASS'" - return 1 - ;; - esac - - declare -A STORAGE_MAP - local -a MENU=() - local COL_WIDTH=0 - - while read -r TAG TYPE _ TOTAL USED FREE _; do - [[ -n "$TAG" && -n "$TYPE" ]] || continue - local DISPLAY="${TAG} (${TYPE})" - local USED_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$USED") - local FREE_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$FREE") - local INFO="Free: ${FREE_FMT}B Used: ${USED_FMT}B" - STORAGE_MAP["$DISPLAY"]="$TAG" - MENU+=("$DISPLAY" "$INFO" "OFF") - ((${#DISPLAY} > COL_WIDTH)) && COL_WIDTH=${#DISPLAY} - done < <(pvesm status -content "$CONTENT" | awk 'NR>1') - - if [[ ${#MENU[@]} -eq 0 ]]; then - msg_error "No storage found for content type '$CONTENT'." - return 2 - fi - - if [[ $((${#MENU[@]} / 3)) -eq 1 ]]; then - STORAGE_RESULT="${STORAGE_MAP[${MENU[0]}]}" - STORAGE_INFO="${MENU[1]}" - return 0 - fi - - local WIDTH=$((COL_WIDTH + 42)) - while true; do - local DISPLAY_SELECTED - DISPLAY_SELECTED=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "Storage Pools" \ - --radiolist "Which storage pool for ${CONTENT_LABEL,,}?\n(Spacebar to select)" \ - 16 "$WIDTH" 6 "${MENU[@]}" 3>&1 1>&2 2>&3) || { exit_script; } - - DISPLAY_SELECTED=$(sed 's/[[:space:]]*$//' <<<"$DISPLAY_SELECTED") - if [[ -z "$DISPLAY_SELECTED" || -z "${STORAGE_MAP[$DISPLAY_SELECTED]+_}" ]]; then - whiptail --msgbox "No valid storage selected. Please try again." 8 58 - continue - fi - STORAGE_RESULT="${STORAGE_MAP[$DISPLAY_SELECTED]}" - for ((i = 0; i < ${#MENU[@]}; i += 3)); do - if [[ "${MENU[$i]}" == "$DISPLAY_SELECTED" ]]; then - STORAGE_INFO="${MENU[$i + 1]}" - break - fi - done - return 0 - done -} - -create_lxc_container() { - # ------------------------------------------------------------------------------ - # Optional verbose mode (debug tracing) - # ------------------------------------------------------------------------------ - if [[ "${CREATE_LXC_VERBOSE:-no}" == "yes" ]]; then set -x; fi - - # ------------------------------------------------------------------------------ - # Helpers (dynamic versioning / template parsing) - # ------------------------------------------------------------------------------ - pkg_ver() { dpkg-query -W -f='${Version}\n' "$1" 2>/dev/null || echo ""; } - pkg_cand() { apt-cache policy "$1" 2>/dev/null | awk '/Candidate:/ {print $2}'; } - - ver_ge() { dpkg --compare-versions "$1" ge "$2"; } - ver_gt() { dpkg --compare-versions "$1" gt "$2"; } - ver_lt() { dpkg --compare-versions "$1" lt "$2"; } - - # Extract Debian OS minor from template name: debian-13-standard_13.1-1_amd64.tar.zst => "13.1" - parse_template_osver() { sed -n 's/.*_\([0-9][0-9]*\(\.[0-9]\+\)\?\)-.*/\1/p' <<<"$1"; } - - # Offer upgrade for pve-container/lxc-pve if candidate > installed; optional auto-retry pct create - # Returns: - # 0 = no upgrade needed - # 1 = upgraded (and if do_retry=yes and retry succeeded, creation done) - # 2 = user declined - # 3 = upgrade attempted but failed OR retry failed - offer_lxc_stack_upgrade_and_maybe_retry() { - local do_retry="${1:-no}" # yes|no - local _pvec_i _pvec_c _lxcp_i _lxcp_c need=0 - - _pvec_i="$(pkg_ver pve-container)" - _lxcp_i="$(pkg_ver lxc-pve)" - _pvec_c="$(pkg_cand pve-container)" - _lxcp_c="$(pkg_cand lxc-pve)" - - if [[ -n "$_pvec_c" && "$_pvec_c" != "none" ]]; then - ver_gt "$_pvec_c" "${_pvec_i:-0}" && need=1 - fi - if [[ -n "$_lxcp_c" && "$_lxcp_c" != "none" ]]; then - ver_gt "$_lxcp_c" "${_lxcp_i:-0}" && need=1 - fi - if [[ $need -eq 0 ]]; then - msg_debug "No newer candidate for pve-container/lxc-pve (installed=$_pvec_i/$_lxcp_i, cand=$_pvec_c/$_lxcp_c)" - return 0 - fi - - echo - echo "An update for the Proxmox LXC stack is available:" - echo " pve-container: installed=${_pvec_i:-n/a} candidate=${_pvec_c:-n/a}" - echo " lxc-pve : installed=${_lxcp_i:-n/a} candidate=${_lxcp_c:-n/a}" - echo - read -rp "Do you want to upgrade now? [y/N] " _ans - case "${_ans,,}" in - y | yes) - msg_info "Upgrading Proxmox LXC stack (pve-container, lxc-pve)" - if apt-get update -qq >/dev/null && apt-get install -y --only-upgrade pve-container lxc-pve >/dev/null; then - msg_ok "LXC stack upgraded." - if [[ "$do_retry" == "yes" ]]; then - msg_info "Retrying container creation after upgrade" - if pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then - msg_ok "Container created successfully after upgrade." - return 1 - else - msg_error "pct create still failed after upgrade. See $LOGFILE" - return 3 - fi - fi - return 1 - else - msg_error "Upgrade failed. Please check APT output." - return 3 - fi - ;; - *) return 2 ;; - esac - } - - # ------------------------------------------------------------------------------ - # Required input variables - # ------------------------------------------------------------------------------ - [[ "${CTID:-}" ]] || { - msg_error "You need to set 'CTID' variable." - exit 203 - } - [[ "${PCT_OSTYPE:-}" ]] || { - msg_error "You need to set 'PCT_OSTYPE' variable." - exit 204 - } - - msg_debug "CTID=$CTID" - msg_debug "PCT_OSTYPE=$PCT_OSTYPE" - msg_debug "PCT_OSVERSION=${PCT_OSVERSION:-default}" - - # ID checks - [[ "$CTID" -ge 100 ]] || { - msg_error "ID cannot be less than 100." - exit 205 - } - if qm status "$CTID" &>/dev/null || pct status "$CTID" &>/dev/null; then - echo -e "ID '$CTID' is already in use." - unset CTID - msg_error "Cannot use ID that is already in use." - exit 206 - fi - - # Storage capability check - check_storage_support "rootdir" || { - msg_error "No valid storage found for 'rootdir' [Container]" - exit 1 - } - check_storage_support "vztmpl" || { - msg_error "No valid storage found for 'vztmpl' [Template]" - exit 1 - } - - # Template storage selection - if resolve_storage_preselect template "${TEMPLATE_STORAGE:-}"; then - TEMPLATE_STORAGE="$STORAGE_RESULT" - TEMPLATE_STORAGE_INFO="$STORAGE_INFO" - msg_ok "Storage ${BL}${TEMPLATE_STORAGE}${CL} (${TEMPLATE_STORAGE_INFO}) [Template]" - else - while true; do - if select_storage template; then - TEMPLATE_STORAGE="$STORAGE_RESULT" - TEMPLATE_STORAGE_INFO="$STORAGE_INFO" - msg_ok "Storage ${BL}${TEMPLATE_STORAGE}${CL} (${TEMPLATE_STORAGE_INFO}) [Template]" - break - fi - done - fi - - # Container storage selection - if resolve_storage_preselect container "${CONTAINER_STORAGE:-}"; then - CONTAINER_STORAGE="$STORAGE_RESULT" - CONTAINER_STORAGE_INFO="$STORAGE_INFO" - msg_ok "Storage ${BL}${CONTAINER_STORAGE}${CL} (${CONTAINER_STORAGE_INFO}) [Container]" - else - while true; do - if select_storage container; then - CONTAINER_STORAGE="$STORAGE_RESULT" - CONTAINER_STORAGE_INFO="$STORAGE_INFO" - msg_ok "Storage ${BL}${CONTAINER_STORAGE}${CL} (${CONTAINER_STORAGE_INFO}) [Container]" - break - fi - done - fi - - # Validate content types - msg_info "Validating content types of storage '$CONTAINER_STORAGE'" - STORAGE_CONTENT=$(grep -A4 -E "^(zfspool|dir|lvmthin|lvm): $CONTAINER_STORAGE" /etc/pve/storage.cfg | grep content | awk '{$1=""; print $0}' | xargs) - msg_debug "Storage '$CONTAINER_STORAGE' has content types: $STORAGE_CONTENT" - grep -qw "rootdir" <<<"$STORAGE_CONTENT" || { - msg_error "Storage '$CONTAINER_STORAGE' does not support 'rootdir'. Cannot create LXC." - exit 217 - } - $STD msg_ok "Storage '$CONTAINER_STORAGE' supports 'rootdir'" - - msg_info "Validating content types of template storage '$TEMPLATE_STORAGE'" - TEMPLATE_CONTENT=$(grep -A4 -E "^[^:]+: $TEMPLATE_STORAGE" /etc/pve/storage.cfg | grep content | awk '{$1=""; print $0}' | xargs) - msg_debug "Template storage '$TEMPLATE_STORAGE' has content types: $TEMPLATE_CONTENT" - if ! grep -qw "vztmpl" <<<"$TEMPLATE_CONTENT"; then - msg_warn "Template storage '$TEMPLATE_STORAGE' does not declare 'vztmpl'. This may cause pct create to fail." - else - $STD msg_ok "Template storage '$TEMPLATE_STORAGE' supports 'vztmpl'" - fi - - # Free space check - STORAGE_FREE=$(pvesm status | awk -v s="$CONTAINER_STORAGE" '$1 == s { print $6 }') - REQUIRED_KB=$((${PCT_DISK_SIZE:-8} * 1024 * 1024)) - [[ "$STORAGE_FREE" -ge "$REQUIRED_KB" ]] || { - msg_error "Not enough space on '$CONTAINER_STORAGE'. Needed: ${PCT_DISK_SIZE:-8}G." - exit 214 - } - - # Cluster quorum (if cluster) - if [[ -f /etc/pve/corosync.conf ]]; then - msg_info "Checking cluster quorum" - if ! pvecm status | awk -F':' '/^Quorate/ { exit ($2 ~ /Yes/) ? 0 : 1 }'; then - msg_error "Cluster is not quorate. Start all nodes or configure quorum device (QDevice)." - exit 210 - fi - msg_ok "Cluster is quorate" - fi - - # ------------------------------------------------------------------------------ - # Template discovery & validation - # ------------------------------------------------------------------------------ - TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" - case "$PCT_OSTYPE" in - debian | ubuntu) TEMPLATE_PATTERN="-standard_" ;; - alpine | fedora | rocky | centos) TEMPLATE_PATTERN="-default_" ;; - *) TEMPLATE_PATTERN="" ;; - esac - - msg_info "Searching for template '$TEMPLATE_SEARCH'" - - mapfile -t LOCAL_TEMPLATES < <( - pveam list "$TEMPLATE_STORAGE" 2>/dev/null | - awk -v s="$TEMPLATE_SEARCH" -v p="$TEMPLATE_PATTERN" '$1 ~ s && $1 ~ p {print $1}' | - sed 's|.*/||' | sort -t - -k 2 -V - ) - - pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)." - mapfile -t ONLINE_TEMPLATES < <( - pveam available -section system 2>/dev/null | - sed -n "s/.*\($TEMPLATE_SEARCH.*$TEMPLATE_PATTERN.*\)/\1/p" | - sort -t - -k 2 -V - ) - ONLINE_TEMPLATE="" - [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" - - if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then - TEMPLATE="${LOCAL_TEMPLATES[-1]}" - TEMPLATE_SOURCE="local" - else - TEMPLATE="$ONLINE_TEMPLATE" - TEMPLATE_SOURCE="online" - fi - - TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" - if [[ -z "$TEMPLATE_PATH" ]]; then - TEMPLATE_BASE=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) - [[ -n "$TEMPLATE_BASE" ]] && TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE" - fi - [[ -n "$TEMPLATE_PATH" ]] || { - msg_error "Unable to resolve template path for $TEMPLATE_STORAGE. Check storage type and permissions." - exit 220 - } - - msg_ok "Template ${BL}$TEMPLATE${CL} [$TEMPLATE_SOURCE]" - msg_debug "Resolved TEMPLATE_PATH=$TEMPLATE_PATH" - - NEED_DOWNLOAD=0 - if [[ ! -f "$TEMPLATE_PATH" ]]; then - msg_info "Template not present locally – will download." - NEED_DOWNLOAD=1 - elif [[ ! -r "$TEMPLATE_PATH" ]]; then - msg_error "Template file exists but is not readable – check permissions." - exit 221 - elif [[ "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then - if [[ -n "$ONLINE_TEMPLATE" ]]; then - msg_warn "Template file too small (<1MB) – re-downloading." - NEED_DOWNLOAD=1 - else - msg_warn "Template looks too small, but no online version exists. Keeping local file." - fi - elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then - if [[ -n "$ONLINE_TEMPLATE" ]]; then - msg_warn "Template appears corrupted – re-downloading." - NEED_DOWNLOAD=1 - else - msg_warn "Template appears corrupted, but no online version exists. Keeping local file." - fi - else - $STD msg_ok "Template $TEMPLATE is present and valid." - fi - - if [[ "$TEMPLATE_SOURCE" == "local" && -n "$ONLINE_TEMPLATE" && "$TEMPLATE" != "$ONLINE_TEMPLATE" ]]; then - msg_warn "Local template is outdated: $TEMPLATE (latest available: $ONLINE_TEMPLATE)" - if whiptail --yesno "A newer template is available:\n$ONLINE_TEMPLATE\n\nDo you want to download and use it instead?" 12 70; then - TEMPLATE="$ONLINE_TEMPLATE" - NEED_DOWNLOAD=1 - else - msg_info "Continuing with local template $TEMPLATE" - fi - fi - - if [[ "$NEED_DOWNLOAD" -eq 1 ]]; then - [[ -f "$TEMPLATE_PATH" ]] && rm -f "$TEMPLATE_PATH" - for attempt in {1..3}; do - msg_info "Attempt $attempt: Downloading template $TEMPLATE to $TEMPLATE_STORAGE" - if pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null 2>&1; then - msg_ok "Template download successful." - break - fi - if [[ $attempt -eq 3 ]]; then - msg_error "Failed after 3 attempts. Please check network access, permissions, or manually run:\n pveam download $TEMPLATE_STORAGE $TEMPLATE" - exit 222 - fi - sleep $((attempt * 5)) - done - fi - - if ! pveam list "$TEMPLATE_STORAGE" 2>/dev/null | grep -q "$TEMPLATE"; then - msg_error "Template $TEMPLATE not available in storage $TEMPLATE_STORAGE after download." - exit 223 - fi - - # ------------------------------------------------------------------------------ - # Dynamic preflight for Debian 13.x: offer upgrade if available (no hard mins) - # ------------------------------------------------------------------------------ - if [[ "$PCT_OSTYPE" == "debian" ]]; then - OSVER="$(parse_template_osver "$TEMPLATE")" - if [[ -n "$OSVER" ]]; then - # Proactive, aber ohne Abbruch – nur Angebot - offer_lxc_stack_upgrade_and_maybe_retry "no" || true - fi - fi - - # ------------------------------------------------------------------------------ - # Create LXC Container - # ------------------------------------------------------------------------------ - msg_info "Creating LXC container" - - # Ensure subuid/subgid entries exist - grep -q "root:100000:65536" /etc/subuid || echo "root:100000:65536" >>/etc/subuid - grep -q "root:100000:65536" /etc/subgid || echo "root:100000:65536" >>/etc/subgid - - # Assemble pct options - PCT_OPTIONS=(${PCT_OPTIONS[@]:-${DEFAULT_PCT_OPTIONS[@]}}) - [[ " ${PCT_OPTIONS[*]} " =~ " -rootfs " ]] || PCT_OPTIONS+=(-rootfs "$CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}") - - # Lock by template file (avoid concurrent downloads/creates) - lockfile="/tmp/template.${TEMPLATE}.lock" - exec 9>"$lockfile" || { - msg_error "Failed to create lock file '$lockfile'." - exit 200 - } - flock -w 60 9 || { - msg_error "Timeout while waiting for template lock." - exit 211 - } - - LOGFILE="/tmp/pct_create_${CTID}.log" - msg_debug "pct create command: pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" - msg_debug "Logfile: $LOGFILE" - - # First attempt - if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >"$LOGFILE" 2>&1; then - msg_error "Container creation failed on ${TEMPLATE_STORAGE}. Checking template..." - - # Validate template file - if [[ ! -s "$TEMPLATE_PATH" || "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then - msg_warn "Template file too small or missing – re-downloading." - rm -f "$TEMPLATE_PATH" - pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" - elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then - if [[ -n "$ONLINE_TEMPLATE" ]]; then - msg_warn "Template appears corrupted – re-downloading." - rm -f "$TEMPLATE_PATH" - pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" - else - msg_warn "Template appears corrupted, but no online version exists. Skipping re-download." - fi - fi - - # Retry after repair - if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then - # Fallback to local storage - if [[ "$TEMPLATE_STORAGE" != "local" ]]; then - msg_warn "Retrying container creation with fallback to local storage..." - LOCAL_TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" - if [[ ! -f "$LOCAL_TEMPLATE_PATH" ]]; then - msg_info "Downloading template to local..." - pveam download local "$TEMPLATE" >/dev/null 2>&1 - fi - if pct create "$CTID" "local:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then - msg_ok "Container successfully created using local fallback." - else - # --- Dynamic stack upgrade + auto-retry on the well-known error pattern --- - if grep -qiE 'unsupported .* version' "$LOGFILE"; then - echo - echo "pct reported 'unsupported ... version' – your LXC stack might be too old for this template." - echo "We can try to upgrade 'pve-container' and 'lxc-pve' now and retry automatically." - if offer_lxc_stack_upgrade_and_maybe_retry "yes"; then - : # success after retry - else - rc=$? - case $rc in - 2) echo "Upgrade was declined. Please update and re-run: - apt update && apt install --only-upgrade pve-container lxc-pve" ;; - 3) echo "Upgrade and/or retry failed. Please inspect: $LOGFILE" ;; - esac - exit 231 - fi - else - msg_error "Container creation failed even with local fallback. See $LOGFILE" - if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then - set -x - bash -x -c "pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" 2>&1 | tee -a "$LOGFILE" - set +x - fi - exit 209 - fi - fi - else - msg_error "Container creation failed on local storage. See $LOGFILE" - # --- Dynamic stack upgrade + auto-retry on the well-known error pattern --- - if grep -qiE 'unsupported .* version' "$LOGFILE"; then - echo - echo "pct reported 'unsupported ... version' – your LXC stack might be too old for this template." - echo "We can try to upgrade 'pve-container' and 'lxc-pve' now and retry automatically." - if offer_lxc_stack_upgrade_and_maybe_retry "yes"; then - : # success after retry - else - rc=$? - case $rc in - 2) echo "Upgrade was declined. Please update and re-run: - apt update && apt install --only-upgrade pve-container lxc-pve" ;; - 3) echo "Upgrade and/or retry failed. Please inspect: $LOGFILE" ;; - esac - exit 231 - fi - else - if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then - set -x - bash -x -c "pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" 2>&1 | tee -a "$LOGFILE" - set +x - fi - exit 209 - fi - fi - fi - fi - - # Verify container exists - pct list | awk '{print $1}' | grep -qx "$CTID" || { - msg_error "Container ID $CTID not listed in 'pct list'. See $LOGFILE" - exit 215 - } - - # Verify config rootfs - grep -q '^rootfs:' "/etc/pve/lxc/$CTID.conf" || { - msg_error "RootFS entry missing in container config. See $LOGFILE" - exit 216 - } - - msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created." -} - -# ------------------------------------------------------------------------------ -# description() -# -# - Sets container description with HTML content (logo, links, badges) -# - Restarts ping-instances.service if present -# - Posts status "done" to API -# ------------------------------------------------------------------------------ -description() { - IP=$(pct exec "$CTID" ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) - - # Generate LXC Description - DESCRIPTION=$( - cat < - - Logo - - -

${APP} LXC

- -

- - spend Coffee - -

- - - - GitHub - - - - Discussions - - - - Issues - - -EOF - ) - pct set "$CTID" -description "$DESCRIPTION" - - if [[ -f /etc/systemd/system/ping-instances.service ]]; then - systemctl start ping-instances.service - fi - - post_update_to_api "done" "none" -} - -# ------------------------------------------------------------------------------ -# api_exit_script() -# -# - Exit trap handler -# - Reports exit codes to API with detailed reason -# - Handles known codes (100–209) and maps them to errors -# ------------------------------------------------------------------------------ -api_exit_script() { - exit_code=$? - if [ $exit_code -ne 0 ]; then - case $exit_code in - 100) post_update_to_api "failed" "100: Unexpected error in create_lxc.sh" ;; - 101) post_update_to_api "failed" "101: No network connection detected in create_lxc.sh" ;; - 200) post_update_to_api "failed" "200: LXC creation failed in create_lxc.sh" ;; - 201) post_update_to_api "failed" "201: Invalid Storage class in create_lxc.sh" ;; - 202) post_update_to_api "failed" "202: User aborted menu in create_lxc.sh" ;; - 203) post_update_to_api "failed" "203: CTID not set in create_lxc.sh" ;; - 204) post_update_to_api "failed" "204: PCT_OSTYPE not set in create_lxc.sh" ;; - 205) post_update_to_api "failed" "205: CTID cannot be less than 100 in create_lxc.sh" ;; - 206) post_update_to_api "failed" "206: CTID already in use in create_lxc.sh" ;; - 207) post_update_to_api "failed" "207: Template not found in create_lxc.sh" ;; - 208) post_update_to_api "failed" "208: Error downloading template in create_lxc.sh" ;; - 209) post_update_to_api "failed" "209: Container creation failed, but template is intact in create_lxc.sh" ;; - *) post_update_to_api "failed" "Unknown error, exit code: $exit_code in create_lxc.sh" ;; - esac - fi -} - -if command -v pveversion >/dev/null 2>&1; then - trap 'api_exit_script' EXIT -fi -trap 'post_update_to_api "failed" "$BASH_COMMAND"' ERR -trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT -trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM From 54ae842185b77870c37d96226f1be1c54062518a Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 1 Oct 2025 15:48:08 +0200 Subject: [PATCH 1333/1733] Update setup_dotnet function --- misc/tools.func | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index b12b9a644..e98332517 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -12,18 +12,30 @@ function setup_dotnet() { local NET_VERSION="${NET_VERSION:-8.0}" - DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{ print $2 }' /etc/os-release) + local DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{ print $2 }' /etc/os-release) + local NEED_NET_INSTALL=false - msg_info "Setup .NET $NET_VERSION" - curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor -o /usr/share/keyrings/microsoft-prod.gpg - if [[ "$DISTRO_CODENAME" != "trixie" ]]; then - curl -fsSL https://packages.microsoft.com/config/debian/12/prod.list -o /etc/apt/sources.list.d/msprod.list + if command -v dotnet >/dev/null; then + CURRENT_NET_VERSION=$(dotnet --version | awk -F. '{print $1 "." $2}') + if [[ "$CURRENT_NET_VERSION" == "$NET_VERSION" ]]; then + : + fi else - curl -fsSL https://packages.microsoft.com/config/debian/13/prod.list -o /etc/apt/sources.list.d/msprod.list + msg_info "Setup .NET $NET_VERSION" + NEED_NET_INSTALL=true + fi + + if [[ "$NEED_NET_INSTALL" == true ]]; then + curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor -o /usr/share/keyrings/microsoft-prod.gpg + if [[ "$DISTRO_CODENAME" != "trixie" ]]; then + curl -fsSL https://packages.microsoft.com/config/debian/12/prod.list -o /etc/apt/sources.list.d/msprod.list + else + curl -fsSL https://packages.microsoft.com/config/debian/13/prod.list -o /etc/apt/sources.list.d/msprod.list + fi + $STD apt-get update + $STD apt-get install -y dotnet-sdk-$NET_VERSION + msg_ok "Setup .NET ${NET_VERSION}" fi - $STD apt-get update - $STD apt-get install -y dotnet-sdk-$NET_VERSION - msg_ok "Setup .NET ${NET_VERSION}" } # ------------------------------------------------------------------------------ From 8137614d6b8687b6de5edeb2d0a19f26ef7ef93b Mon Sep 17 00:00:00 2001 From: HydroshieldMKII Date: Wed, 1 Oct 2025 10:32:31 -0400 Subject: [PATCH 1334/1733] Reverted changes in build and install --- misc/build.func | 2981 +++++++++++++++++++++++++++++++++++++++++++++ misc/install.func | 206 ++++ 2 files changed, 3187 insertions(+) create mode 100644 misc/build.func create mode 100644 misc/install.func diff --git a/misc/build.func b/misc/build.func new file mode 100644 index 000000000..d6b0af725 --- /dev/null +++ b/misc/build.func @@ -0,0 +1,2981 @@ +#!/usr/bin/env bash +# Copyright (c) 2021-2025 community-scripts ORG +# Author: tteck (tteckster) | MickLesk | michelroegl-brunner +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Revision: 1 + +# ------------------------------------------------------------------------------ +# variables() +# +# - Normalize application name (NSAPP = lowercase, no spaces) +# - Build installer filename (var_install) +# - Define regex for integer validation +# - Fetch hostname of Proxmox node +# - Set default values for diagnostics/method +# - Generate random UUID for tracking +# ------------------------------------------------------------------------------ +variables() { + NSAPP=$(echo "${APP,,}" | tr -d ' ') # This function sets the NSAPP variable by converting the value of the APP variable to lowercase and removing any spaces. + var_install="${NSAPP}-install" # sets the var_install variable by appending "-install" to the value of NSAPP. + INTEGER='^[0-9]+([.][0-9]+)?$' # it defines the INTEGER regular expression pattern. + PVEHOST_NAME=$(hostname) # gets the Proxmox Hostname and sets it to Uppercase + DIAGNOSTICS="yes" # sets the DIAGNOSTICS variable to "yes", used for the API call. + METHOD="default" # sets the METHOD variable to "default", used for the API call. + RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" # generates a random UUID and sets it to the RANDOM_UUID variable. + #CT_TYPE=${var_unprivileged:-$CT_TYPE} +} + +# ----------------------------------------------------------------------------- +# Community-Scripts bootstrap loader +# - Always sources build.func from remote +# - Updates local core files only if build.func changed +# - Local cache: /usr/local/community-scripts/core +# ----------------------------------------------------------------------------- + +# FUNC_DIR="/usr/local/community-scripts/core" +# mkdir -p "$FUNC_DIR" + +# BUILD_URL="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func" +# BUILD_REV="$FUNC_DIR/build.rev" +# DEVMODE="${DEVMODE:-no}" + +# # --- Step 1: fetch build.func content once, compute hash --- +# build_content="$(curl -fsSL "$BUILD_URL")" || { +# echo "❌ Failed to fetch build.func" +# exit 1 +# } + +# newhash=$(printf "%s" "$build_content" | sha256sum | awk '{print $1}') +# oldhash=$(cat "$BUILD_REV" 2>/dev/null || echo "") + +# # --- Step 2: if build.func changed, offer update for core files --- +# if [ "$newhash" != "$oldhash" ]; then +# echo "⚠️ build.func changed!" + +# while true; do +# read -rp "Refresh local core files? [y/N/diff]: " ans +# case "$ans" in +# [Yy]*) +# echo "$newhash" >"$BUILD_REV" + +# update_func_file() { +# local file="$1" +# local url="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/$file" +# local local_path="$FUNC_DIR/$file" + +# echo "⬇️ Downloading $file ..." +# curl -fsSL "$url" -o "$local_path" || { +# echo "❌ Failed to fetch $file" +# exit 1 +# } +# echo "✔️ Updated $file" +# } + +# update_func_file core.func +# update_func_file error_handler.func +# update_func_file tools.func +# break +# ;; +# [Dd]*) +# for file in core.func error_handler.func tools.func; do +# local_path="$FUNC_DIR/$file" +# url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/$file" +# remote_tmp="$(mktemp)" + +# curl -fsSL "$url" -o "$remote_tmp" || continue + +# if [ -f "$local_path" ]; then +# echo "🔍 Diff for $file:" +# diff -u "$local_path" "$remote_tmp" || echo "(no differences)" +# else +# echo "📦 New file $file will be installed" +# fi + +# rm -f "$remote_tmp" +# done +# ;; +# *) +# echo "❌ Skipped updating local core files" +# break +# ;; +# esac +# done +# else +# if [ "$DEVMODE" != "yes" ]; then +# echo "✔️ build.func unchanged → using existing local core files" +# fi +# fi + +# if [ -n "${_COMMUNITY_SCRIPTS_LOADER:-}" ]; then +# return 0 2>/dev/null || exit 0 +# fi +# _COMMUNITY_SCRIPTS_LOADER=1 + +# # --- Step 3: always source local versions of the core files --- +# source "$FUNC_DIR/core.func" +# source "$FUNC_DIR/error_handler.func" +# source "$FUNC_DIR/tools.func" + +# # --- Step 4: finally, source build.func directly from memory --- +# # (no tmp file needed) +# source <(printf "%s" "$build_content") + +# ------------------------------------------------------------------------------ +# Load core + error handler functions from community-scripts repo +# +# - Prefer curl if available, fallback to wget +# - Load: core.func, error_handler.func, api.func +# - Initialize error traps after loading +# ------------------------------------------------------------------------------ + +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) + +if command -v curl >/dev/null 2>&1; then + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) + load_functions + catch_errors + #echo "(build.func) Loaded core.func via curl" +elif command -v wget >/dev/null 2>&1; then + source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) + load_functions + catch_errors + #echo "(build.func) Loaded core.func via wget" +fi + +# ------------------------------------------------------------------------------ +# maxkeys_check() +# +# - Reads kernel keyring limits (maxkeys, maxbytes) +# - Checks current usage for LXC user (UID 100000) +# - Warns if usage is close to limits and suggests sysctl tuning +# - Exits if thresholds are exceeded +# - https://cleveruptime.com/docs/files/proc-key-users | https://docs.kernel.org/security/keys/core.html +# ------------------------------------------------------------------------------ + +maxkeys_check() { + # Read kernel parameters + per_user_maxkeys=$(cat /proc/sys/kernel/keys/maxkeys 2>/dev/null || echo 0) + per_user_maxbytes=$(cat /proc/sys/kernel/keys/maxbytes 2>/dev/null || echo 0) + + # Exit if kernel parameters are unavailable + if [[ "$per_user_maxkeys" -eq 0 || "$per_user_maxbytes" -eq 0 ]]; then + echo -e "${CROSS}${RD} Error: Unable to read kernel parameters. Ensure proper permissions.${CL}" + exit 1 + fi + + # Fetch key usage for user ID 100000 (typical for containers) + used_lxc_keys=$(awk '/100000:/ {print $2}' /proc/key-users 2>/dev/null || echo 0) + used_lxc_bytes=$(awk '/100000:/ {split($5, a, "/"); print a[1]}' /proc/key-users 2>/dev/null || echo 0) + + # Calculate thresholds and suggested new limits + threshold_keys=$((per_user_maxkeys - 100)) + threshold_bytes=$((per_user_maxbytes - 1000)) + new_limit_keys=$((per_user_maxkeys * 2)) + new_limit_bytes=$((per_user_maxbytes * 2)) + + # Check if key or byte usage is near limits + failure=0 + if [[ "$used_lxc_keys" -gt "$threshold_keys" ]]; then + echo -e "${CROSS}${RD} Warning: Key usage is near the limit (${used_lxc_keys}/${per_user_maxkeys}).${CL}" + echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxkeys=${new_limit_keys}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." + failure=1 + fi + if [[ "$used_lxc_bytes" -gt "$threshold_bytes" ]]; then + echo -e "${CROSS}${RD} Warning: Key byte usage is near the limit (${used_lxc_bytes}/${per_user_maxbytes}).${CL}" + echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxbytes=${new_limit_bytes}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." + failure=1 + fi + + # Provide next steps if issues are detected + if [[ "$failure" -eq 1 ]]; then + echo -e "${INFO} To apply changes, run: ${BOLD}service procps force-reload${CL}" + exit 1 + fi + + echo -e "${CM}${GN} All kernel key limits are within safe thresholds.${CL}" +} + +# ------------------------------------------------------------------------------ +# get_current_ip() +# +# - Returns current container IP depending on OS type +# - Debian/Ubuntu: uses `hostname -I` +# - Alpine: parses eth0 via `ip -4 addr` +# ------------------------------------------------------------------------------ +get_current_ip() { + if [ -f /etc/os-release ]; then + # Check for Debian/Ubuntu (uses hostname -I) + if grep -qE 'ID=debian|ID=ubuntu' /etc/os-release; then + CURRENT_IP=$(hostname -I | awk '{print $1}') + # Check for Alpine (uses ip command) + elif grep -q 'ID=alpine' /etc/os-release; then + CURRENT_IP=$(ip -4 addr show eth0 | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) + else + CURRENT_IP="Unknown" + fi + fi + echo "$CURRENT_IP" +} + +# ------------------------------------------------------------------------------ +# update_motd_ip() +# +# - Updates /etc/motd with current container IP +# - Removes old IP entries to avoid duplicates +# ------------------------------------------------------------------------------ +update_motd_ip() { + MOTD_FILE="/etc/motd" + + if [ -f "$MOTD_FILE" ]; then + # Remove existing IP Address lines to prevent duplication + sed -i '/IP Address:/d' "$MOTD_FILE" + + IP=$(get_current_ip) + # Add the new IP address + echo -e "${TAB}${NETWORK}${YW} IP Address: ${GN}${IP}${CL}" >>"$MOTD_FILE" + fi +} + +# ------------------------------------------------------------------------------ +# ssh_check() +# +# - Detects if script is running over SSH +# - Warns user and recommends using Proxmox shell +# - User can choose to continue or abort +# ------------------------------------------------------------------------------ +ssh_check() { + if [ -n "${SSH_CLIENT:+x}" ]; then + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH DETECTED" --yesno "It's advisable to utilize the Proxmox shell rather than SSH, as there may be potential complications with variable retrieval. Proceed using SSH?" 10 72; then + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox --title "Proceed using SSH" "You've chosen to proceed using SSH. If any issues arise, please run the script in the Proxmox shell before creating a repository issue." 10 72 + else + clear + echo "Exiting due to SSH usage. Please consider using the Proxmox shell." + exit + fi + fi +} + +# ------------------------------------------------------------------------------ +# install_ssh_keys_into_ct() +# +# - Installs SSH keys into container root account if SSH is enabled +# - Uses pct push or direct input to authorized_keys +# - Falls back to warning if no keys provided +# ------------------------------------------------------------------------------ +install_ssh_keys_into_ct() { + [[ "$SSH" != "yes" ]] && return 0 + + if [[ -n "$SSH_KEYS_FILE" && -s "$SSH_KEYS_FILE" ]]; then + msg_info "Installing selected SSH keys into CT ${CTID}" + pct exec "$CTID" -- sh -c 'mkdir -p /root/.ssh && chmod 700 /root/.ssh' || { + msg_error "prepare /root/.ssh failed" + return 1 + } + pct push "$CTID" "$SSH_KEYS_FILE" /root/.ssh/authorized_keys >/dev/null 2>&1 || + pct exec "$CTID" -- sh -c "cat > /root/.ssh/authorized_keys" <"$SSH_KEYS_FILE" || { + msg_error "write authorized_keys failed" + return 1 + } + pct exec "$CTID" -- sh -c 'chmod 600 /root/.ssh/authorized_keys' || true + msg_ok "Installed SSH keys into CT ${CTID}" + return 0 + fi + + # Fallback: nichts ausgewählt + msg_warn "No SSH keys to install (skipping)." + return 0 +} + +# ------------------------------------------------------------------------------ +# base_settings() +# +# - Defines all base/default variables for container creation +# - Reads from environment variables (var_*) +# - Provides fallback defaults for OS type/version +# ------------------------------------------------------------------------------ +base_settings() { + # Default Settings + CT_TYPE=${var_unprivileged:-"1"} + DISK_SIZE=${var_disk:-"4"} + CORE_COUNT=${var_cpu:-"1"} + RAM_SIZE=${var_ram:-"1024"} + VERBOSE=${var_verbose:-"${1:-no}"} + PW=${var_pw:-""} + CT_ID=${var_ctid:-$NEXTID} + HN=${var_hostname:-$NSAPP} + BRG=${var_brg:-"vmbr0"} + NET=${var_net:-"dhcp"} + IPV6_METHOD=${var_ipv6_method:-"none"} + IPV6_STATIC=${var_ipv6_static:-""} + GATE=${var_gateway:-""} + APT_CACHER=${var_apt_cacher:-""} + APT_CACHER_IP=${var_apt_cacher_ip:-""} + MTU=${var_mtu:-""} + SD=${var_storage:-""} + NS=${var_ns:-""} + MAC=${var_mac:-""} + VLAN=${var_vlan:-""} + SSH=${var_ssh:-"no"} + SSH_AUTHORIZED_KEY=${var_ssh_authorized_key:-""} + UDHCPC_FIX=${var_udhcpc_fix:-""} + TAGS="community-script,${var_tags:-}" + ENABLE_FUSE=${var_fuse:-"${1:-no}"} + ENABLE_TUN=${var_tun:-"${1:-no}"} + + # Since these 2 are only defined outside of default_settings function, we add a temporary fallback. TODO: To align everything, we should add these as constant variables (e.g. OSTYPE and OSVERSION), but that would currently require updating the default_settings function for all existing scripts + if [ -z "$var_os" ]; then + var_os="debian" + fi + if [ -z "$var_version" ]; then + var_version="12" + fi +} + +# ------------------------------------------------------------------------------ +# echo_default() +# +# - Prints summary of default values (ID, OS, type, disk, RAM, CPU, etc.) +# - Uses icons and formatting for readability +# - Convert CT_TYPE to description +# ------------------------------------------------------------------------------ +echo_default() { + CT_TYPE_DESC="Unprivileged" + if [ "$CT_TYPE" -eq 0 ]; then + CT_TYPE_DESC="Privileged" + fi + + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}${CT_ID}${CL}" + echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os ($var_version)${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + if [ "$VERBOSE" == "yes" ]; then + echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}Enabled${CL}" + fi + echo -e "${CREATING}${BOLD}${BL}Creating a ${APP} LXC using the above default settings${CL}" + echo -e " " +} + +# ------------------------------------------------------------------------------ +# exit_script() +# +# - Called when user cancels an action +# - Clears screen and exits gracefully +# ------------------------------------------------------------------------------ +exit_script() { + clear + echo -e "\n${CROSS}${RD}User exited script${CL}\n" + exit +} + +# ------------------------------------------------------------------------------ +# find_host_ssh_keys() +# +# - Scans system for available SSH keys +# - Supports defaults (~/.ssh, /etc/ssh/authorized_keys) +# - Returns list of files containing valid SSH public keys +# - Sets FOUND_HOST_KEY_COUNT to number of keys found +# ------------------------------------------------------------------------------ +find_host_ssh_keys() { + local re='(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))' + local -a files=() cand=() + local g="${var_ssh_import_glob:-}" + local total=0 f base c + + shopt -s nullglob + if [[ -n "$g" ]]; then + for pat in $g; do cand+=($pat); done + else + cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2) + cand+=(/root/.ssh/*.pub) + cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*) + fi + shopt -u nullglob + + for f in "${cand[@]}"; do + [[ -f "$f" && -r "$f" ]] || continue + base="$(basename -- "$f")" + case "$base" in + known_hosts | known_hosts.* | config) continue ;; + id_*) [[ "$f" != *.pub ]] && continue ;; + esac + + # CRLF safe check for host keys + c=$(tr -d '\r' <"$f" | awk ' + /^[[:space:]]*#/ {next} + /^[[:space:]]*$/ {next} + {print} + ' | grep -E -c '"$re"' || true) + + if ((c > 0)); then + files+=("$f") + total=$((total + c)) + fi + done + + # Fallback to /root/.ssh/authorized_keys + if ((${#files[@]} == 0)) && [[ -r /root/.ssh/authorized_keys ]]; then + if grep -E -q "$re" /root/.ssh/authorized_keys; then + files+=(/root/.ssh/authorized_keys) + total=$((total + $(grep -E -c "$re" /root/.ssh/authorized_keys || echo 0))) + fi + fi + + FOUND_HOST_KEY_COUNT="$total" + ( + IFS=: + echo "${files[*]}" + ) +} + +# ------------------------------------------------------------------------------ +# advanced_settings() +# +# - Interactive whiptail menu for advanced configuration +# - Lets user set container type, password, CT ID, hostname, disk, CPU, RAM +# - Supports IPv4/IPv6, DNS, MAC, VLAN, tags, SSH keys, FUSE, verbose mode +# - Ends with confirmation or re-entry if cancelled +# ------------------------------------------------------------------------------ +advanced_settings() { + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox --title "Here is an instructional tip:" "To make a selection, use the Spacebar." 8 58 + # Setting Default Tag for Advanced Settings + TAGS="community-script;${var_tags:-}" + CT_DEFAULT_TYPE="${CT_TYPE}" + CT_TYPE="" + while [ -z "$CT_TYPE" ]; do + if [ "$CT_DEFAULT_TYPE" == "1" ]; then + if CT_TYPE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ + "1" "Unprivileged" ON \ + "0" "Privileged" OFF \ + 3>&1 1>&2 2>&3); then + if [ -n "$CT_TYPE" ]; then + CT_TYPE_DESC="Unprivileged" + if [ "$CT_TYPE" -eq 0 ]; then + CT_TYPE_DESC="Privileged" + fi + echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os | ${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" + fi + else + exit_script + fi + fi + if [ "$CT_DEFAULT_TYPE" == "0" ]; then + if CT_TYPE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ + "1" "Unprivileged" OFF \ + "0" "Privileged" ON \ + 3>&1 1>&2 2>&3); then + if [ -n "$CT_TYPE" ]; then + CT_TYPE_DESC="Unprivileged" + if [ "$CT_TYPE" -eq 0 ]; then + CT_TYPE_DESC="Privileged" + fi + echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os${CL}" + echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" + fi + else + exit_script + fi + fi + done + + while true; do + if PW1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --passwordbox "\nSet Root Password (needed for root ssh access)" 9 58 --title "PASSWORD (leave blank for automatic login)" 3>&1 1>&2 2>&3); then + # Empty = Autologin + if [[ -z "$PW1" ]]; then + PW="" + PW1="Automatic Login" + echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}$PW1${CL}" + break + fi + + # Invalid: contains spaces + if [[ "$PW1" == *" "* ]]; then + whiptail --msgbox "Password cannot contain spaces." 8 58 + continue + fi + + # Invalid: too short + if ((${#PW1} < 5)); then + whiptail --msgbox "Password must be at least 5 characters." 8 58 + continue + fi + + # Confirm password + if PW2=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then + if [[ "$PW1" == "$PW2" ]]; then + PW="-password $PW1" + echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}" + break + else + whiptail --msgbox "Passwords do not match. Please try again." 8 58 + fi + else + exit_script + fi + else + exit_script + fi + done + + if CT_ID=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Container ID" 8 58 "$NEXTID" --title "CONTAINER ID" 3>&1 1>&2 2>&3); then + if [ -z "$CT_ID" ]; then + CT_ID="$NEXTID" + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" + else + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" + fi + else + exit_script + fi + + while true; do + if CT_NAME=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 "$NSAPP" --title "HOSTNAME" 3>&1 1>&2 2>&3); then + if [ -z "$CT_NAME" ]; then + HN="$NSAPP" + else + HN=$(echo "${CT_NAME,,}" | tr -d ' ') + fi + # Hostname validate (RFC 1123) + if [[ "$HN" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ ]]; then + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" + break + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --msgbox "❌ Invalid hostname: '$HN'\n\nOnly lowercase letters, digits and hyphens (-) are allowed.\nUnderscores (_) or other characters are not permitted!" 10 70 + fi + else + exit_script + fi + done + + while true; do + DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GB" 8 58 "$var_disk" --title "DISK SIZE" 3>&1 1>&2 2>&3) || exit_script + + if [ -z "$DISK_SIZE" ]; then + DISK_SIZE="$var_disk" + fi + + if [[ "$DISK_SIZE" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" + break + else + whiptail --msgbox "Disk size must be a positive integer!" 8 58 + fi + done + + while true; do + CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --inputbox "Allocate CPU Cores" 8 58 "$var_cpu" --title "CORE COUNT" 3>&1 1>&2 2>&3) || exit_script + + if [ -z "$CORE_COUNT" ]; then + CORE_COUNT="$var_cpu" + fi + + if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + break + else + whiptail --msgbox "CPU core count must be a positive integer!" 8 58 + fi + done + + while true; do + RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --inputbox "Allocate RAM in MiB" 8 58 "$var_ram" --title "RAM" 3>&1 1>&2 2>&3) || exit_script + + if [ -z "$RAM_SIZE" ]; then + RAM_SIZE="$var_ram" + fi + + if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + break + else + whiptail --msgbox "RAM size must be a positive integer!" 8 58 + fi + done + + IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f) + BRIDGES="" + OLD_IFS=$IFS + IFS=$'\n' + for iface_filepath in ${IFACE_FILEPATH_LIST}; do + + iface_indexes_tmpfile=$(mktemp -q -u '.iface-XXXX') + (grep -Pn '^\s*iface' "${iface_filepath}" | cut -d':' -f1 && wc -l "${iface_filepath}" | cut -d' ' -f1) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' >"${iface_indexes_tmpfile}" || true + + if [ -f "${iface_indexes_tmpfile}" ]; then + + while read -r pair; do + start=$(echo "${pair}" | cut -d':' -f1) + end=$(echo "${pair}" | cut -d':' -f2) + + if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then + iface_name=$(sed "${start}q;d" "${iface_filepath}" | awk '{print $2}') + BRIDGES="${iface_name}"$'\n'"${BRIDGES}" + fi + + done <"${iface_indexes_tmpfile}" + rm -f "${iface_indexes_tmpfile}" + fi + + done + IFS=$OLD_IFS + BRIDGES=$(echo "$BRIDGES" | grep -v '^\s*$' | sort | uniq) + if [[ -z "$BRIDGES" ]]; then + BRG="vmbr0" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + else + BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge:" 15 40 6 $(echo "$BRIDGES" | awk '{print $0, "Bridge"}') 3>&1 1>&2 2>&3) + if [[ -z "$BRG" ]]; then + exit_script + else + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + fi + fi + + # IPv4 methods: dhcp, static, none + while true; do + IPV4_METHOD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "IPv4 Address Management" \ + --menu "Select IPv4 Address Assignment Method:" 12 60 2 \ + "dhcp" "Automatic (DHCP, recommended)" \ + "static" "Static (manual entry)" \ + 3>&1 1>&2 2>&3) + + exit_status=$? + if [ $exit_status -ne 0 ]; then + exit_script + fi + + case "$IPV4_METHOD" in + dhcp) + NET="dhcp" + GATE="" + echo -e "${NETWORK}${BOLD}${DGN}IPv4: DHCP${CL}" + break + ;; + static) + # Static: call and validate CIDR address + while true; do + NET=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Enter Static IPv4 CIDR Address (e.g. 192.168.100.50/24)" 8 58 "" \ + --title "IPv4 ADDRESS" 3>&1 1>&2 2>&3) + if [ -z "$NET" ]; then + whiptail --msgbox "IPv4 address must not be empty." 8 58 + continue + elif [[ "$NET" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then + echo -e "${NETWORK}${BOLD}${DGN}IPv4 Address: ${BGN}$NET${CL}" + break + else + whiptail --msgbox "$NET is not a valid IPv4 CIDR address. Please enter a correct value!" 8 58 + fi + done + + # call and validate Gateway + while true; do + GATE1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Enter Gateway IP address for static IPv4" 8 58 "" \ + --title "Gateway IP" 3>&1 1>&2 2>&3) + if [ -z "$GATE1" ]; then + whiptail --msgbox "Gateway IP address cannot be empty." 8 58 + elif [[ ! "$GATE1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then + whiptail --msgbox "Invalid Gateway IP address format." 8 58 + else + GATE=",gw=$GATE1" + echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE1${CL}" + break + fi + done + break + ;; + esac + done + + # IPv6 Address Management selection + while true; do + IPV6_METHOD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --menu \ + "Select IPv6 Address Management Type:" 15 58 4 \ + "auto" "SLAAC/AUTO (recommended, default)" \ + "dhcp" "DHCPv6" \ + "static" "Static (manual entry)" \ + "none" "Disabled" \ + --default-item "auto" 3>&1 1>&2 2>&3) + [ $? -ne 0 ] && exit_script + + case "$IPV6_METHOD" in + auto) + echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}SLAAC/AUTO${CL}" + IPV6_ADDR="" + IPV6_GATE="" + break + ;; + dhcp) + echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}DHCPv6${CL}" + IPV6_ADDR="dhcp" + IPV6_GATE="" + break + ;; + static) + # Ask for static IPv6 address (CIDR notation, e.g., 2001:db8::1234/64) + while true; do + IPV6_ADDR=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox \ + "Set a static IPv6 CIDR address (e.g., 2001:db8::1234/64)" 8 58 "" \ + --title "IPv6 STATIC ADDRESS" 3>&1 1>&2 2>&3) || exit_script + if [[ "$IPV6_ADDR" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+(/[0-9]{1,3})$ ]]; then + echo -e "${NETWORK}${BOLD}${DGN}IPv6 Address: ${BGN}$IPV6_ADDR${CL}" + break + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox \ + "$IPV6_ADDR is an invalid IPv6 CIDR address. Please enter a valid IPv6 CIDR address (e.g., 2001:db8::1234/64)" 8 58 + fi + done + # Optional: ask for IPv6 gateway for static config + while true; do + IPV6_GATE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox \ + "Enter IPv6 gateway address (optional, leave blank for none)" 8 58 "" --title "IPv6 GATEWAY" 3>&1 1>&2 2>&3) + if [ -z "$IPV6_GATE" ]; then + IPV6_GATE="" + break + elif [[ "$IPV6_GATE" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+$ ]]; then + break + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox \ + "Invalid IPv6 gateway format." 8 58 + fi + done + break + ;; + none) + echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}Disabled${CL}" + IPV6_ADDR="none" + IPV6_GATE="" + break + ;; + *) + exit_script + ;; + esac + done + + if [ "$var_os" == "alpine" ]; then + APT_CACHER="" + APT_CACHER_IP="" + else + if APT_CACHER_IP=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set APT-Cacher IP (leave blank for none)" 8 58 --title "APT-Cacher IP" 3>&1 1>&2 2>&3); then + APT_CACHER="${APT_CACHER_IP:+yes}" + echo -e "${NETWORK}${BOLD}${DGN}APT-Cacher IP Address: ${BGN}${APT_CACHER_IP:-Default}${CL}" + else + exit_script + fi + fi + + # if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "IPv6" --yesno "Disable IPv6?" 10 58); then + # DISABLEIP6="yes" + # else + # DISABLEIP6="no" + # fi + # echo -e "${DISABLEIPV6}${BOLD}${DGN}Disable IPv6: ${BGN}$DISABLEIP6${CL}" + + if MTU1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default [The MTU of your selected vmbr, default is 1500])" 8 58 --title "MTU SIZE" 3>&1 1>&2 2>&3); then + if [ -z "$MTU1" ]; then + MTU1="Default" + MTU="" + else + MTU=",mtu=$MTU1" + fi + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" + else + exit_script + fi + + if SD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a DNS Search Domain (leave blank for HOST)" 8 58 --title "DNS Search Domain" 3>&1 1>&2 2>&3); then + if [ -z "$SD" ]; then + SX=Host + SD="" + else + SX=$SD + SD="-searchdomain=$SD" + fi + echo -e "${SEARCH}${BOLD}${DGN}DNS Search Domain: ${BGN}$SX${CL}" + else + exit_script + fi + + if NX=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a DNS Server IP (leave blank for HOST)" 8 58 --title "DNS SERVER IP" 3>&1 1>&2 2>&3); then + if [ -z "$NX" ]; then + NX=Host + NS="" + else + NS="-nameserver=$NX" + fi + echo -e "${NETWORK}${BOLD}${DGN}DNS Server IP Address: ${BGN}$NX${CL}" + else + exit_script + fi + + if [ "$var_os" == "alpine" ] && [ "$NET" == "dhcp" ] && [ "$NX" != "Host" ]; then + UDHCPC_FIX="yes" + else + UDHCPC_FIX="no" + fi + export UDHCPC_FIX + + if MAC1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a MAC Address(leave blank for generated MAC)" 8 58 --title "MAC ADDRESS" 3>&1 1>&2 2>&3); then + if [ -z "$MAC1" ]; then + MAC1="Default" + MAC="" + else + MAC=",hwaddr=$MAC1" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" + fi + else + exit_script + fi + + if VLAN1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for no VLAN)" 8 58 --title "VLAN" 3>&1 1>&2 2>&3); then + if [ -z "$VLAN1" ]; then + VLAN1="Default" + VLAN="" + else + VLAN=",tag=$VLAN1" + fi + echo -e "${VLANTAG}${BOLD}${DGN}Vlan: ${BGN}$VLAN1${CL}" + else + exit_script + fi + + if ADV_TAGS=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Custom Tags?[If you remove all, there will be no tags!]" 8 58 "${TAGS}" --title "Advanced Tags" 3>&1 1>&2 2>&3); then + if [ -n "${ADV_TAGS}" ]; then + ADV_TAGS=$(echo "$ADV_TAGS" | tr -d '[:space:]') + TAGS="${ADV_TAGS}" + else + TAGS=";" + fi + echo -e "${NETWORK}${BOLD}${DGN}Tags: ${BGN}$TAGS${CL}" + else + exit_script + fi + + # --- SSH key provisioning (one dialog) --- + SSH_KEYS_FILE="$(mktemp)" + : >"$SSH_KEYS_FILE" + + IFS=$'\0' read -r -d '' -a _def_files < <(ssh_discover_default_files && printf '\0') + ssh_build_choices_from_files "${_def_files[@]}" + DEF_KEYS_COUNT="$COUNT" + + if [[ "$DEF_KEYS_COUNT" -gt 0 ]]; then + SSH_KEY_MODE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ + "Provision SSH keys for root:" 14 72 4 \ + "found" "Select from detected keys (${DEF_KEYS_COUNT})" \ + "manual" "Paste a single public key" \ + "folder" "Scan another folder (path or glob)" \ + "none" "No keys" 3>&1 1>&2 2>&3) || exit_script + else + SSH_KEY_MODE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ + "No host keys detected; choose manual/none:" 12 72 2 \ + "manual" "Paste a single public key" \ + "none" "No keys" 3>&1 1>&2 2>&3) || exit_script + fi + + case "$SSH_KEY_MODE" in + found) + SEL=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT HOST KEYS" \ + --checklist "Select one or more keys to import:" 20 140 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script + for tag in $SEL; do + tag="${tag%\"}" + tag="${tag#\"}" + line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) + [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" + done + ;; + manual) + SSH_AUTHORIZED_KEY="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Paste one SSH public key line (ssh-ed25519/ssh-rsa/...)" 10 72 --title "SSH Public Key" 3>&1 1>&2 2>&3)" + [[ -n "$SSH_AUTHORIZED_KEY" ]] && printf '%s\n' "$SSH_AUTHORIZED_KEY" >>"$SSH_KEYS_FILE" + ;; + folder) + GLOB_PATH="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Enter a folder or glob to scan (e.g. /root/.ssh/*.pub)" 10 72 --title "Scan Folder/Glob" 3>&1 1>&2 2>&3)" + if [[ -n "$GLOB_PATH" ]]; then + shopt -s nullglob + read -r -a _scan_files <<<"$GLOB_PATH" + shopt -u nullglob + if [[ "${#_scan_files[@]}" -gt 0 ]]; then + ssh_build_choices_from_files "${_scan_files[@]}" + if [[ "$COUNT" -gt 0 ]]; then + SEL=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT FOLDER KEYS" \ + --checklist "Select key(s) to import:" 20 78 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script + for tag in $SEL; do + tag="${tag%\"}" + tag="${tag#\"}" + line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) + [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" + done + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "No keys found in: $GLOB_PATH" 8 60 + fi + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "Path/glob returned no files." 8 60 + fi + fi + ;; + none) : ;; + esac + + # Dedupe + clean EOF + if [[ -s "$SSH_KEYS_FILE" ]]; then + sort -u -o "$SSH_KEYS_FILE" "$SSH_KEYS_FILE" + printf '\n' >>"$SSH_KEYS_FILE" + fi + + # SSH activate, if keys found or password set + if [[ -s "$SSH_KEYS_FILE" || "$PW" == -password* ]]; then + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable root SSH access?" 10 58); then + SSH="yes" + else + SSH="no" + fi + else + SSH="no" + fi + echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}" + + export SSH_KEYS_FILE + if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "FUSE Support" --yesno "Enable FUSE support?\nRequired for tools like rclone, mergerfs, AppImage, etc." 10 58); then + ENABLE_FUSE="yes" + else + ENABLE_FUSE="no" + fi + echo -e "${FUSE}${BOLD}${DGN}Enable FUSE Support: ${BGN}$ENABLE_FUSE${CL}" + + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then + VERBOSE="yes" + else + VERBOSE="no" + fi + echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}" + + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create ${APP} LXC?" 10 58); then + echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}" + else + clear + header_info + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings on node $PVEHOST_NAME${CL}" + advanced_settings + fi +} + +# ------------------------------------------------------------------------------ +# diagnostics_check() +# +# - Ensures diagnostics config file exists at /usr/local/community-scripts/diagnostics +# - Asks user whether to send anonymous diagnostic data +# - Saves DIAGNOSTICS=yes/no in the config file +# ------------------------------------------------------------------------------ +diagnostics_check() { + if ! [ -d "/usr/local/community-scripts" ]; then + mkdir -p /usr/local/community-scripts + fi + + if ! [ -f "/usr/local/community-scripts/diagnostics" ]; then + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS" --yesno "Send Diagnostics of LXC Installation?\n\n(This only transmits data without user data, just RAM, CPU, LXC name, ...)" 10 58); then + cat </usr/local/community-scripts/diagnostics +DIAGNOSTICS=yes + +#This file is used to store the diagnostics settings for the Community-Scripts API. +#https://github.com/community-scripts/ProxmoxVED/discussions/1836 +#Your diagnostics will be sent to the Community-Scripts API for troubleshooting/statistical purposes. +#You can review the data at https://community-scripts.github.io/ProxmoxVE/data +#If you do not wish to send diagnostics, please set the variable 'DIAGNOSTICS' to "no" in /usr/local/community-scripts/diagnostics, or use the menue. +#This will disable the diagnostics feature. +#To send diagnostics, set the variable 'DIAGNOSTICS' to "yes" in /usr/local/community-scripts/diagnostics, or use the menue. +#This will enable the diagnostics feature. +#The following information will be sent: +#"ct_type" +#"disk_size" +#"core_count" +#"ram_size" +#"os_type" +#"os_version" +#"disableip6" +#"nsapp" +#"method" +#"pve_version" +#"status" +#If you have any concerns, please review the source code at /misc/build.func +EOF + DIAGNOSTICS="yes" + else + cat </usr/local/community-scripts/diagnostics +DIAGNOSTICS=no + +#This file is used to store the diagnostics settings for the Community-Scripts API. +#https://github.com/community-scripts/ProxmoxVED/discussions/1836 +#Your diagnostics will be sent to the Community-Scripts API for troubleshooting/statistical purposes. +#You can review the data at https://community-scripts.github.io/ProxmoxVE/data +#If you do not wish to send diagnostics, please set the variable 'DIAGNOSTICS' to "no" in /usr/local/community-scripts/diagnostics, or use the menue. +#This will disable the diagnostics feature. +#To send diagnostics, set the variable 'DIAGNOSTICS' to "yes" in /usr/local/community-scripts/diagnostics, or use the menue. +#This will enable the diagnostics feature. +#The following information will be sent: +#"ct_type" +#"disk_size" +#"core_count" +#"ram_size" +#"os_type" +#"os_version" +#"disableip6" +#"nsapp" +#"method" +#"pve_version" +#"status" +#If you have any concerns, please review the source code at /misc/build.func +EOF + DIAGNOSTICS="no" + fi + else + DIAGNOSTICS=$(awk -F '=' '/^DIAGNOSTICS/ {print $2}' /usr/local/community-scripts/diagnostics) + + fi + +} + +# ------------------------------------------------------------------------------ +# default_var_settings +# +# - Ensures /usr/local/community-scripts/default.vars exists (creates if missing) +# - Loads var_* values from default.vars (safe parser, no source/eval) +# - Precedence: ENV var_* > default.vars > built-in defaults +# - Maps var_verbose → VERBOSE +# - Calls base_settings "$VERBOSE" and echo_default +# ------------------------------------------------------------------------------ +default_var_settings() { + # Allowed var_* keys (alphabetically sorted) + local VAR_WHITELIST=( + var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_ctid var_disk var_fuse + var_gateway var_hostname var_ipv6_method var_ipv6_static var_mac var_mtu + var_net var_ns var_pw var_ram var_tags var_tun var_unprivileged + var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage + ) + + # Snapshot: environment variables (highest precedence) + declare -A _HARD_ENV=() + local _k + for _k in "${VAR_WHITELIST[@]}"; do + if printenv "$_k" >/dev/null 2>&1; then _HARD_ENV["$_k"]=1; fi + done + + # Find default.vars location + local _find_default_vars + _find_default_vars() { + local f + for f in \ + /usr/local/community-scripts/default.vars \ + "$HOME/.config/community-scripts/default.vars" \ + "./default.vars"; do + [ -f "$f" ] && { + echo "$f" + return 0 + } + done + return 1 + } + # Allow override of storages via env (for non-interactive use cases) + [ -n "${var_template_storage:-}" ] && TEMPLATE_STORAGE="$var_template_storage" + [ -n "${var_container_storage:-}" ] && CONTAINER_STORAGE="$var_container_storage" + + # Create once, with storages already selected, no var_ctid/var_hostname lines + local _ensure_default_vars + _ensure_default_vars() { + _find_default_vars >/dev/null 2>&1 && return 0 + + local canonical="/usr/local/community-scripts/default.vars" + msg_info "No default.vars found. Creating ${canonical}" + mkdir -p /usr/local/community-scripts + + # Pick storages before writing the file (always ask unless only one) + # Create a minimal temp file to write into + : >"$canonical" + + # Base content (no var_ctid / var_hostname here) + cat >"$canonical" <<'EOF' +# Community-Scripts defaults (var_* only). Lines starting with # are comments. +# Precedence: ENV var_* > default.vars > built-ins. +# Keep keys alphabetically sorted. + +# Container type +var_unprivileged=1 + +# Resources +var_cpu=1 +var_disk=4 +var_ram=1024 + +# Network +var_brg=vmbr0 +var_net=dhcp +var_ipv6_method=none +# var_gateway= +# var_ipv6_static= +# var_vlan= +# var_mtu= +# var_mac= +# var_ns= + +# SSH +var_ssh=no +# var_ssh_authorized_key= + +# APT cacher (optional) +# var_apt_cacher=yes +# var_apt_cacher_ip=192.168.1.10 + +# Features/Tags/verbosity +var_fuse=no +var_tun=no +var_tags=community-script +var_verbose=no + +# Security (root PW) – empty => autologin +# var_pw= +EOF + + # Now choose storages (always prompt unless just one exists) + choose_and_set_storage_for_file "$canonical" template + choose_and_set_storage_for_file "$canonical" container + + chmod 0644 "$canonical" + msg_ok "Created ${canonical}" + } + + # Whitelist check + local _is_whitelisted_key + _is_whitelisted_key() { + local k="$1" + local w + for w in "${VAR_WHITELIST[@]}"; do [ "$k" = "$w" ] && return 0; done + return 1 + } + + # Safe parser for KEY=VALUE lines + local _load_vars_file + _load_vars_file() { + local file="$1" + [ -f "$file" ] || return 0 + msg_info "Loading defaults from ${file}" + local line key val + while IFS= read -r line || [ -n "$line" ]; do + line="${line#"${line%%[![:space:]]*}"}" + line="${line%"${line##*[![:space:]]}"}" + [[ -z "$line" || "$line" == \#* ]] && continue + if [[ "$line" =~ ^([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then + local var_key="${BASH_REMATCH[1]}" + local var_val="${BASH_REMATCH[2]}" + + [[ "$var_key" != var_* ]] && continue + _is_whitelisted_key "$var_key" || { + msg_debug "Ignore non-whitelisted ${var_key}" + continue + } + + # Strip quotes + if [[ "$var_val" =~ ^\"(.*)\"$ ]]; then + var_val="${BASH_REMATCH[1]}" + elif [[ "$var_val" =~ ^\'(.*)\'$ ]]; then + var_val="${BASH_REMATCH[1]}" + fi + + # Unsafe characters + case $var_val in + \"*\") + var_val=${var_val#\"} + var_val=${var_val%\"} + ;; + \'*\') + var_val=${var_val#\'} + var_val=${var_val%\'} + ;; + esac # Hard env wins + [[ -n "${_HARD_ENV[$var_key]:-}" ]] && continue + # Set only if not already exported + [[ -z "${!var_key+x}" ]] && export "${var_key}=${var_val}" + else + msg_warn "Malformed line in ${file}: ${line}" + fi + done <"$file" + msg_ok "Loaded ${file}" + } + + # 1) Ensure file exists + _ensure_default_vars + + # 2) Load file + local dv + dv="$(_find_default_vars)" || { + msg_error "default.vars not found after ensure step" + return 1 + } + _load_vars_file "$dv" + + # 3) Map var_verbose → VERBOSE + if [[ -n "${var_verbose:-}" ]]; then + case "${var_verbose,,}" in 1 | yes | true | on) VERBOSE="yes" ;; 0 | no | false | off) VERBOSE="no" ;; *) VERBOSE="${var_verbose}" ;; esac + else + VERBOSE="no" + fi + + # 4) Apply base settings and show summary + METHOD="mydefaults-global" + base_settings "$VERBOSE" + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using My Defaults (default.vars) on node $PVEHOST_NAME${CL}" + echo_default +} + +# ------------------------------------------------------------------------------ +# get_app_defaults_path() +# +# - Returns full path for app-specific defaults file +# - Example: /usr/local/community-scripts/defaults/.vars +# ------------------------------------------------------------------------------ + +get_app_defaults_path() { + local n="${NSAPP:-${APP,,}}" + echo "/usr/local/community-scripts/defaults/${n}.vars" +} + +# ------------------------------------------------------------------------------ +# maybe_offer_save_app_defaults +# +# - Called after advanced_settings returned with fully chosen values. +# - If no .vars exists, offers to persist current advanced settings +# into /usr/local/community-scripts/defaults/.vars +# - Only writes whitelisted var_* keys. +# - Extracts raw values from flags like ",gw=..." ",mtu=..." etc. +# ------------------------------------------------------------------------------ +if ! declare -p VAR_WHITELIST >/dev/null 2>&1; then + declare -ag VAR_WHITELIST=( + var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_ctid var_disk var_fuse + var_gateway var_hostname var_ipv6_method var_ipv6_static var_mac var_mtu + var_net var_ns var_pw var_ram var_tags var_tun var_unprivileged + var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage + ) +fi + +_is_whitelisted_key() { + local k="$1" + local w + for w in "${VAR_WHITELIST[@]}"; do [[ "$k" == "$w" ]] && return 0; done + return 1 +} + +_sanitize_value() { + # Disallow Command-Substitution / Shell-Meta + case "$1" in + *'$('* | *'`'* | *';'* | *'&'* | *'<('*) + echo "" + return 0 + ;; + esac + echo "$1" +} + +# Map-Parser: read var_* from file into _VARS_IN associative array +declare -A _VARS_IN +_load_vars_file() { + local file="$1" + [ -f "$file" ] || return 0 + msg_info "Loading defaults from ${file}" + local line key val + while IFS= read -r line || [ -n "$line" ]; do + line="${line#"${line%%[![:space:]]*}"}" + line="${line%"${line##*[![:space:]]}"}" + [ -z "$line" ] && continue + case "$line" in + \#*) continue ;; + esac + key=$(printf "%s" "$line" | cut -d= -f1) + val=$(printf "%s" "$line" | cut -d= -f2-) + case "$key" in + var_*) + if _is_whitelisted_key "$key"; then + [ -z "${!key+x}" ] && export "$key=$val" + fi + ;; + esac + done <"$file" + msg_ok "Loaded ${file}" +} + +# Diff function for two var_* files -> produces human-readable diff list for $1 (old) vs $2 (new) +_build_vars_diff() { + local oldf="$1" newf="$2" + local k + local -A OLD=() NEW=() + _load_vars_file_to_map "$oldf" + for k in "${!_VARS_IN[@]}"; do OLD["$k"]="${_VARS_IN[$k]}"; done + _load_vars_file_to_map "$newf" + for k in "${!_VARS_IN[@]}"; do NEW["$k"]="${_VARS_IN[$k]}"; done + + local out + out+="# Diff for ${APP} (${NSAPP})\n" + out+="# Old: ${oldf}\n# New: ${newf}\n\n" + + local found_change=0 + + # Changed & Removed + for k in "${!OLD[@]}"; do + if [[ -v NEW["$k"] ]]; then + if [[ "${OLD[$k]}" != "${NEW[$k]}" ]]; then + out+="~ ${k}\n - old: ${OLD[$k]}\n + new: ${NEW[$k]}\n" + found_change=1 + fi + else + out+="- ${k}\n - old: ${OLD[$k]}\n" + found_change=1 + fi + done + + # Added + for k in "${!NEW[@]}"; do + if [[ ! -v OLD["$k"] ]]; then + out+="+ ${k}\n + new: ${NEW[$k]}\n" + found_change=1 + fi + done + + if [[ $found_change -eq 0 ]]; then + out+="(No differences)\n" + fi + + printf "%b" "$out" +} + +# Build a temporary .vars file from current advanced settings +_build_current_app_vars_tmp() { + tmpf="$(mktemp /tmp/${NSAPP:-app}.vars.new.XXXXXX)" + + # NET/GW + _net="${NET:-}" + _gate="" + case "${GATE:-}" in + ,gw=*) _gate=$(echo "$GATE" | sed 's/^,gw=//') ;; + esac + + # IPv6 + _ipv6_method="${IPV6_METHOD:-auto}" + _ipv6_static="" + _ipv6_gateway="" + if [ "$_ipv6_method" = "static" ]; then + _ipv6_static="${IPV6_ADDR:-}" + _ipv6_gateway="${IPV6_GATE:-}" + fi + + # MTU/VLAN/MAC + _mtu="" + _vlan="" + _mac="" + case "${MTU:-}" in + ,mtu=*) _mtu=$(echo "$MTU" | sed 's/^,mtu=//') ;; + esac + case "${VLAN:-}" in + ,tag=*) _vlan=$(echo "$VLAN" | sed 's/^,tag=//') ;; + esac + case "${MAC:-}" in + ,hwaddr=*) _mac=$(echo "$MAC" | sed 's/^,hwaddr=//') ;; + esac + + # DNS / Searchdomain + _ns="" + _searchdomain="" + case "${NS:-}" in + -nameserver=*) _ns=$(echo "$NS" | sed 's/^-nameserver=//') ;; + esac + case "${SD:-}" in + -searchdomain=*) _searchdomain=$(echo "$SD" | sed 's/^-searchdomain=//') ;; + esac + + # SSH / APT / Features + _ssh="${SSH:-no}" + _ssh_auth="${SSH_AUTHORIZED_KEY:-}" + _apt_cacher="${APT_CACHER:-}" + _apt_cacher_ip="${APT_CACHER_IP:-}" + _fuse="${ENABLE_FUSE:-no}" + _tun="${ENABLE_TUN:-no}" + _tags="${TAGS:-}" + _verbose="${VERBOSE:-no}" + + # Type / Resources / Identity + _unpriv="${CT_TYPE:-1}" + _cpu="${CORE_COUNT:-1}" + _ram="${RAM_SIZE:-1024}" + _disk="${DISK_SIZE:-4}" + _hostname="${HN:-$NSAPP}" + + # Storage + _tpl_storage="${TEMPLATE_STORAGE:-${var_template_storage:-}}" + _ct_storage="${CONTAINER_STORAGE:-${var_container_storage:-}}" + + { + echo "# App-specific defaults for ${APP} (${NSAPP})" + echo "# Generated on $(date -u '+%Y-%m-%dT%H:%M:%SZ')" + echo + + echo "var_unprivileged=$(_sanitize_value "$_unpriv")" + echo "var_cpu=$(_sanitize_value "$_cpu")" + echo "var_ram=$(_sanitize_value "$_ram")" + echo "var_disk=$(_sanitize_value "$_disk")" + + [ -n "${BRG:-}" ] && echo "var_brg=$(_sanitize_value "$BRG")" + [ -n "$_net" ] && echo "var_net=$(_sanitize_value "$_net")" + [ -n "$_gate" ] && echo "var_gateway=$(_sanitize_value "$_gate")" + [ -n "$_mtu" ] && echo "var_mtu=$(_sanitize_value "$_mtu")" + [ -n "$_vlan" ] && echo "var_vlan=$(_sanitize_value "$_vlan")" + [ -n "$_mac" ] && echo "var_mac=$(_sanitize_value "$_mac")" + [ -n "$_ns" ] && echo "var_ns=$(_sanitize_value "$_ns")" + + [ -n "$_ipv6_method" ] && echo "var_ipv6_method=$(_sanitize_value "$_ipv6_method")" + [ -n "$_ipv6_static" ] && echo "var_ipv6_static=$(_sanitize_value "$_ipv6_static")" + + [ -n "$_ssh" ] && echo "var_ssh=$(_sanitize_value "$_ssh")" + [ -n "$_ssh_auth" ] && echo "var_ssh_authorized_key=$(_sanitize_value "$_ssh_auth")" + + [ -n "$_apt_cacher" ] && echo "var_apt_cacher=$(_sanitize_value "$_apt_cacher")" + [ -n "$_apt_cacher_ip" ] && echo "var_apt_cacher_ip=$(_sanitize_value "$_apt_cacher_ip")" + + [ -n "$_fuse" ] && echo "var_fuse=$(_sanitize_value "$_fuse")" + [ -n "$_tun" ] && echo "var_tun=$(_sanitize_value "$_tun")" + [ -n "$_tags" ] && echo "var_tags=$(_sanitize_value "$_tags")" + [ -n "$_verbose" ] && echo "var_verbose=$(_sanitize_value "$_verbose")" + + [ -n "$_hostname" ] && echo "var_hostname=$(_sanitize_value "$_hostname")" + [ -n "$_searchdomain" ] && echo "var_searchdomain=$(_sanitize_value "$_searchdomain")" + + [ -n "$_tpl_storage" ] && echo "var_template_storage=$(_sanitize_value "$_tpl_storage")" + [ -n "$_ct_storage" ] && echo "var_container_storage=$(_sanitize_value "$_ct_storage")" + } >"$tmpf" + + echo "$tmpf" +} + +# ------------------------------------------------------------------------------ +# maybe_offer_save_app_defaults() +# +# - Called after advanced_settings() +# - Offers to save current values as app defaults if not existing +# - If file exists: shows diff and allows Update, Keep, View Diff, or Cancel +# ------------------------------------------------------------------------------ +maybe_offer_save_app_defaults() { + local app_vars_path + app_vars_path="$(get_app_defaults_path)" + + # always build from current settings + local new_tmp diff_tmp + new_tmp="$(_build_current_app_vars_tmp)" + diff_tmp="$(mktemp -p /tmp "${NSAPP:-app}.vars.diff.XXXXXX")" + + # 1) if no file → offer to create + if [[ ! -f "$app_vars_path" ]]; then + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --yesno "Save these advanced settings as defaults for ${APP}?\n\nThis will create:\n${app_vars_path}" 12 72; then + mkdir -p "$(dirname "$app_vars_path")" + install -m 0644 "$new_tmp" "$app_vars_path" + msg_ok "Saved app defaults: ${app_vars_path}" + fi + rm -f "$new_tmp" "$diff_tmp" + return 0 + fi + + # 2) if file exists → build diff + _build_vars_diff "$app_vars_path" "$new_tmp" >"$diff_tmp" + + # if no differences → do nothing + if grep -q "^(No differences)$" "$diff_tmp"; then + rm -f "$new_tmp" "$diff_tmp" + return 0 + fi + + # 3) if file exists → show menu with default selection "Update Defaults" + local app_vars_file + app_vars_file="$(basename "$app_vars_path")" + + while true; do + local sel + sel="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "APP DEFAULTS – ${APP}" \ + --menu "Differences detected. What do you want to do?" 20 78 10 \ + "Update Defaults" "Write new values to ${app_vars_file}" \ + "Keep Current" "Keep existing defaults (no changes)" \ + "View Diff" "Show a detailed diff" \ + "Cancel" "Abort without changes" \ + --default-item "Update Defaults" \ + 3>&1 1>&2 2>&3)" || { sel="Cancel"; } + + case "$sel" in + "Update Defaults") + install -m 0644 "$new_tmp" "$app_vars_path" + msg_ok "Updated app defaults: ${app_vars_path}" + break + ;; + "Keep Current") + msg_info "Keeping current app defaults: ${app_vars_path}" + break + ;; + "View Diff") + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "Diff – ${APP}" \ + --scrolltext --textbox "$diff_tmp" 25 100 + ;; + "Cancel" | *) + msg_info "Canceled. No changes to app defaults." + break + ;; + esac + done + + rm -f "$new_tmp" "$diff_tmp" +} + +ensure_storage_selection_for_vars_file() { + vf="$1" + + # Read stored values (if any) + tpl=$(grep -E '^var_template_storage=' "$vf" | cut -d= -f2-) + ct=$(grep -E '^var_container_storage=' "$vf" | cut -d= -f2-) + + # Template storage + if [ -n "$tpl" ]; then + TEMPLATE_STORAGE="$tpl" + else + choose_and_set_storage_for_file "$vf" template + fi + + # Container storage + if [ -n "$ct" ]; then + CONTAINER_STORAGE="$ct" + else + choose_and_set_storage_for_file "$vf" container + fi + + echo_storage_summary_from_file "$vf" +} + +diagnostics_menu() { + if [ "${DIAGNOSTICS:-no}" = "yes" ]; then + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "DIAGNOSTIC SETTINGS" \ + --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ + --yes-button "No" --no-button "Back"; then + DIAGNOSTICS="no" + sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=no/' /usr/local/community-scripts/diagnostics + whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 + fi + else + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "DIAGNOSTIC SETTINGS" \ + --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ + --yes-button "Yes" --no-button "Back"; then + DIAGNOSTICS="yes" + sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=yes/' /usr/local/community-scripts/diagnostics + whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 + fi + fi +} + +# ------------------------------------------------------------------------------ +# install_script() +# +# - Main entrypoint for installation mode +# - Runs safety checks (pve_check, root_check, maxkeys_check, diagnostics_check) +# - Builds interactive menu (Default, Verbose, Advanced, My Defaults, App Defaults, Diagnostics, Storage, Exit) +# - Applies chosen settings and triggers container build +# ------------------------------------------------------------------------------ +install_script() { + pve_check + shell_check + root_check + arch_check + ssh_check + maxkeys_check + diagnostics_check + + if systemctl is-active -q ping-instances.service; then + systemctl -q stop ping-instances.service + fi + + NEXTID=$(pvesh get /cluster/nextid) + timezone=$(cat /etc/timezone) + header_info + + # --- Support CLI argument as direct preset (default, advanced, …) --- + CHOICE="${mode:-${1:-}}" + + # If no CLI argument → show whiptail menu + if [ -z "$CHOICE" ]; then + local menu_items=( + "1" "Default Install" + "2" "Advanced Install" + "3" "My Defaults" + ) + + if [ -f "$(get_app_defaults_path)" ]; then + menu_items+=("4" "App Defaults for ${APP}") + menu_items+=("5" "Settings") + else + menu_items+=("4" "Settings") + fi + + TMP_CHOICE=$(whiptail \ + --backtitle "Proxmox VE Helper Scripts" \ + --title "Community-Scripts Options" \ + --ok-button "Select" --cancel-button "Exit Script" \ + --notags \ + --menu "\nChoose an option:\n Use TAB or Arrow keys to navigate, ENTER to select.\n" \ + 20 60 9 \ + "${menu_items[@]}" \ + --default-item "1" \ + 3>&1 1>&2 2>&3) || exit_script + CHOICE="$TMP_CHOICE" + fi + + # --- Main case --- + case "$CHOICE" in + 1 | default | DEFAULT) + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME${CL}" + VERBOSE="no" + METHOD="default" + base_settings "$VERBOSE" + echo_default + ensure_storage_selection_for_vars_file "/usr/local/community-scripts/default.vars" + ;; + 2 | advanced | ADVANCED) + header_info + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Install on node $PVEHOST_NAME${CL}" + METHOD="advanced" + base_settings + advanced_settings + ensure_storage_selection_for_vars_file "/usr/local/community-scripts/default.vars" + maybe_offer_save_app_defaults + ;; + 3 | mydefaults | MYDEFAULTS) + default_var_settings || { + msg_error "Failed to apply default.vars" + exit 1 + } + ensure_storage_selection_for_vars_file "/usr/local/community-scripts/default.vars" + ;; + 4 | appdefaults | APPDEFAULTS) + if [ -f "$(get_app_defaults_path)" ]; then + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using App Defaults for ${APP} on node $PVEHOST_NAME${CL}" + METHOD="appdefaults" + base_settings + _load_vars_file "$(get_app_defaults_path)" + echo_default + ensure_storage_selection_for_vars_file "$(get_app_defaults_path)" + else + msg_error "No App Defaults available for ${APP}" + exit 1 + fi + ;; + 5 | settings | SETTINGS) + settings_menu + ;; + *) + echo -e "${CROSS}${RD}Invalid option: $CHOICE${CL}" + exit 1 + ;; + esac +} + +# ------------------------------------------------------------------------------ +# check_storage_or_prompt() +# +# - Validates container/template storage +# - If invalid or missing, prompts user to select new storage +# - Updates vars file accordingly +# ------------------------------------------------------------------------------ +# check_storage_or_prompt() { +# local vars_file="$1" +# local changed=0 + +# if [ ! -f "$vars_file" ]; then +# msg_warn "No vars file found at $vars_file" +# return 0 +# fi + +# # Helper: validate storage ID +# _validate_storage() { +# local s="$1" +# [ -n "$s" ] || return 1 +# pvesm status -content images | awk 'NR>1 {print $1}' | grep -qx "$s" +# } + +# # Load current values (empty if not set) +# local ct_store tpl_store +# ct_store=$(awk -F= '/^var_container_storage=/ {print $2; exit}' "$vars_file") +# tpl_store=$(awk -F= '/^var_template_storage=/ {print $2; exit}' "$vars_file") + +# # Container storage +# if ! _validate_storage "$ct_store"; then +# local new_ct +# new_ct=$(pvesm status -content images | awk 'NR>1 {print $1" "$2" "$6}') +# new_ct=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ +# --title "Select Container Storage" \ +# --menu "Choose container storage:" 20 60 10 $new_ct 3>&1 1>&2 2>&3) || return 0 +# if [ -n "$new_ct" ]; then +# sed -i "/^var_container_storage=/d" "$vars_file" +# echo "var_container_storage=$new_ct" >>"$vars_file" +# changed=1 +# msg_ok "Updated container storage in $vars_file → $new_ct" +# fi +# fi + +# # Template storage +# if ! _validate_storage "$tpl_store"; then +# local new_tpl +# new_tpl=$(pvesm status -content vztmpl | awk 'NR>1 {print $1" "$2" "$6}') +# new_tpl=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ +# --title "Select Template Storage" \ +# --menu "Choose template storage:" 20 60 10 $new_tpl 3>&1 1>&2 2>&3) || return 0 +# if [ -n "$new_tpl" ]; then +# sed -i "/^var_template_storage=/d" "$vars_file" +# echo "var_template_storage=$new_tpl" >>"$vars_file" +# changed=1 +# msg_ok "Updated template storage in $vars_file → $new_tpl" +# fi +# fi + +# # Always succeed (no aborts from here) +# return 0 +# } + +# # ------------------------------------------------------------------------------ +# # storage_settings_menu() +# # +# # - Menu for managing storage defaults +# # - Options: update My Defaults or App Defaults storage +# # ------------------------------------------------------------------------------ +# storage_settings_menu() { +# local vars_file="/usr/local/community-scripts/default.vars" + +# check_storage_or_prompt "$vars_file" +# _echo_storage_summary "$vars_file" + +# # Always ask user if they want to update, even if values are valid +# if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ +# --title "STORAGE SETTINGS" \ +# --yesno "Do you want to update the storage defaults?\n\nCurrent values will be kept unless you select new ones." 12 72; then + +# # container storage selection +# local new_ct +# new_ct=$(pvesm status -content images | awk 'NR>1 {print $1" "$2" "$6}') +# new_ct=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ +# --title "Select Container Storage" \ +# --menu "Choose container storage:" 20 60 10 $new_ct 3>&1 1>&2 2>&3) || true +# if [ -n "$new_ct" ]; then +# sed -i '/^var_container_storage=/d' "$vars_file" +# echo "var_container_storage=$new_ct" >>"$vars_file" +# msg_ok "Updated container storage → $new_ct" +# fi + +# # template storage selection +# local new_tpl +# new_tpl=$(pvesm status -content vztmpl | awk 'NR>1 {print $1" "$2" "$6}') +# new_tpl=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ +# --title "Select Template Storage" \ +# --menu "Choose template storage:" 20 60 10 $new_tpl 3>&1 1>&2 2>&3) || true +# if [ -n "$new_tpl" ]; then +# sed -i '/^var_template_storage=/d' "$vars_file" +# echo "var_template_storage=$new_tpl" >>"$vars_file" +# msg_ok "Updated template storage → $new_tpl" +# fi +# fi +# } + +edit_default_storage() { + local vf="/usr/local/community-scripts/default.vars" + + # make sure file exists + if [ ! -f "$vf" ]; then + msg_info "No default.vars found, creating $vf" + mkdir -p /usr/local/community-scripts + touch "$vf" + fi + + # reuse the same Whiptail selection we already have + ensure_storage_selection_for_vars_file "$vf" +} + +settings_menu() { + while true; do + local settings_items=( + "1" "Manage API-Diagnostic Setting" + "2" "Edit Default.vars" + "3" "Edit Default Storage" + ) + if [ -f "$(get_app_defaults_path)" ]; then + settings_items+=("4" "Edit App.vars for ${APP}") + settings_items+=("5" "Exit") + else + settings_items+=("4" "Exit") + fi + + local choice + choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "Community-Scripts SETTINGS Menu" \ + --ok-button "OK" --cancel-button "Back" \ + --menu "\n\nChoose a settings option:\n\nUse TAB or Arrow keys to navigate, ENTER to select." 20 60 9 \ + "${settings_items[@]}" \ + 3>&1 1>&2 2>&3) || break + + case "$choice" in + 1) diagnostics_menu ;; + 2) ${EDITOR:-nano} /usr/local/community-scripts/default.vars ;; + 3) edit_default_storage ;; + 4) + if [ -f "$(get_app_defaults_path)" ]; then + ${EDITOR:-nano} "$(get_app_defaults_path)" + else + exit_script + fi + ;; + 5) exit_script ;; + esac + done +} + +# ===== Unified storage selection & writing to vars files ===== +_write_storage_to_vars() { + # $1 = vars_file, $2 = key (var_container_storage / var_template_storage), $3 = value + local vf="$1" key="$2" val="$3" + # remove uncommented and commented versions to avoid duplicates + sed -i "/^[#[:space:]]*${key}=/d" "$vf" + echo "${key}=${val}" >>"$vf" +} + +choose_and_set_storage_for_file() { + # $1 = vars_file, $2 = class ('container'|'template') + local vf="$1" class="$2" key="" current="" + case "$class" in + container) key="var_container_storage" ;; + template) key="var_template_storage" ;; + *) + msg_error "Unknown storage class: $class" + return 1 + ;; + esac + + current=$(awk -F= -v k="^${key}=" '$0 ~ k {print $2; exit}' "$vf") + + # If only one storage exists for the content type, auto-pick. Else always ask (your wish #4). + local content="rootdir" + [[ "$class" == "template" ]] && content="vztmpl" + local count + count=$(pvesm status -content "$content" | awk 'NR>1{print $1}' | wc -l) + + if [[ "$count" -eq 1 ]]; then + STORAGE_RESULT=$(pvesm status -content "$content" | awk 'NR>1{print $1; exit}') + STORAGE_INFO="" + else + # If the current value is preselectable, we could show it, but per your requirement we always offer selection + select_storage "$class" || return 1 + fi + + _write_storage_to_vars "$vf" "$key" "$STORAGE_RESULT" + + # Keep environment in sync for later steps (e.g. app-default save) + if [[ "$class" == "container" ]]; then + export var_container_storage="$STORAGE_RESULT" + export CONTAINER_STORAGE="$STORAGE_RESULT" + else + export var_template_storage="$STORAGE_RESULT" + export TEMPLATE_STORAGE="$STORAGE_RESULT" + fi + + msg_ok "Updated ${key} → ${STORAGE_RESULT}" +} + +echo_storage_summary_from_file() { + local vars_file="$1" + local ct_store tpl_store + ct_store=$(awk -F= '/^var_container_storage=/ {print $2; exit}' "$vars_file") + tpl_store=$(awk -F= '/^var_template_storage=/ {print $2; exit}' "$vars_file") + if [ -n "$tpl" ]; then + TEMPLATE_STORAGE="$tpl" + else + choose_and_set_storage_for_file "$vf" template + fi + + if [ -n "$ct" ]; then + CONTAINER_STORAGE="$ct" + else + choose_and_set_storage_for_file "$vf" container + fi +} + +# ------------------------------------------------------------------------------ +# check_container_resources() +# +# - Compares host RAM/CPU with required values +# - Warns if under-provisioned and asks user to continue or abort +# ------------------------------------------------------------------------------ +check_container_resources() { + current_ram=$(free -m | awk 'NR==2{print $2}') + current_cpu=$(nproc) + + if [[ "$current_ram" -lt "$var_ram" ]] || [[ "$current_cpu" -lt "$var_cpu" ]]; then + echo -e "\n${INFO}${HOLD} ${GN}Required: ${var_cpu} CPU, ${var_ram}MB RAM ${CL}| ${RD}Current: ${current_cpu} CPU, ${current_ram}MB RAM${CL}" + echo -e "${YWB}Please ensure that the ${APP} LXC is configured with at least ${var_cpu} vCPU and ${var_ram} MB RAM for the build process.${CL}\n" + echo -ne "${INFO}${HOLD} May cause data loss! ${INFO} Continue update with under-provisioned LXC? " + read -r prompt + if [[ ! ${prompt,,} =~ ^(yes)$ ]]; then + echo -e "${CROSS}${HOLD} ${YWB}Exiting based on user input.${CL}" + exit 1 + fi + else + echo -e "" + fi +} + +# ------------------------------------------------------------------------------ +# check_container_storage() +# +# - Checks /boot partition usage +# - Warns if usage >80% and asks user confirmation before proceeding +# ------------------------------------------------------------------------------ +check_container_storage() { + total_size=$(df /boot --output=size | tail -n 1) + local used_size=$(df /boot --output=used | tail -n 1) + usage=$((100 * used_size / total_size)) + if ((usage > 80)); then + echo -e "${INFO}${HOLD} ${YWB}Warning: Storage is dangerously low (${usage}%).${CL}" + echo -ne "Continue anyway? " + read -r prompt + if [[ ! ${prompt,,} =~ ^(y|yes)$ ]]; then + echo -e "${CROSS}${HOLD}${YWB}Exiting based on user input.${CL}" + exit 1 + fi + fi +} + +# ------------------------------------------------------------------------------ +# ssh_extract_keys_from_file() +# +# - Extracts valid SSH public keys from given file +# - Supports RSA, Ed25519, ECDSA and filters out comments/invalid lines +# ------------------------------------------------------------------------------ +ssh_extract_keys_from_file() { + local f="$1" + [[ -r "$f" ]] || return 0 + tr -d '\r' <"$f" | awk ' + /^[[:space:]]*#/ {next} + /^[[:space:]]*$/ {next} + # nackt: typ base64 [comment] + /^(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/ {print; next} + # mit Optionen: finde ab erstem Key-Typ + { + match($0, /(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/) + if (RSTART>0) { print substr($0, RSTART) } + } + ' +} + +# ------------------------------------------------------------------------------ +# ssh_build_choices_from_files() +# +# - Builds interactive whiptail checklist of available SSH keys +# - Generates fingerprint, type and comment for each key +# ------------------------------------------------------------------------------ +ssh_build_choices_from_files() { + local -a files=("$@") + CHOICES=() + COUNT=0 + MAPFILE="$(mktemp)" + local id key typ fp cmt base ln=0 + + for f in "${files[@]}"; do + [[ -f "$f" && -r "$f" ]] || continue + base="$(basename -- "$f")" + case "$base" in + known_hosts | known_hosts.* | config) continue ;; + id_*) [[ "$f" != *.pub ]] && continue ;; + esac + + # map every key in file + while IFS= read -r key; do + [[ -n "$key" ]] || continue + + typ="" + fp="" + cmt="" + # Only the pure key part (without options) is already included in ‘key’. + read -r _typ _b64 _cmt <<<"$key" + typ="${_typ:-key}" + cmt="${_cmt:-}" + # Fingerprint via ssh-keygen (if available) + if command -v ssh-keygen >/dev/null 2>&1; then + fp="$(printf '%s\n' "$key" | ssh-keygen -lf - 2>/dev/null | awk '{print $2}')" + fi + # Label shorten + [[ ${#cmt} -gt 40 ]] && cmt="${cmt:0:37}..." + + ln=$((ln + 1)) + COUNT=$((COUNT + 1)) + id="K${COUNT}" + echo "${id}|${key}" >>"$MAPFILE" + CHOICES+=("$id" "[$typ] ${fp:+$fp }${cmt:+$cmt }— ${base}" "OFF") + done < <(ssh_extract_keys_from_file "$f") + done +} + +# ------------------------------------------------------------------------------ +# ssh_discover_default_files() +# +# - Scans standard paths for SSH keys +# - Includes ~/.ssh/*.pub, /etc/ssh/authorized_keys, etc. +# ------------------------------------------------------------------------------ +ssh_discover_default_files() { + local -a cand=() + shopt -s nullglob + cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2) + cand+=(/root/.ssh/*.pub) + cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*) + shopt -u nullglob + printf '%s\0' "${cand[@]}" +} + +# ------------------------------------------------------------------------------ +# start() +# +# - Entry point of script +# - On Proxmox host: calls install_script +# - In silent mode: runs update_script +# - Otherwise: shows update/setting menu +# ------------------------------------------------------------------------------ +start() { + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) + if command -v pveversion >/dev/null 2>&1; then + install_script || return 0 + return 0 + elif [ ! -z ${PHS_SILENT+x} ] && [[ "${PHS_SILENT}" == "1" ]]; then + VERBOSE="no" + set_std_mode + update_script + else + CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "${APP} LXC Update/Setting" --menu \ + "Support/Update functions for ${APP} LXC. Choose an option:" \ + 12 60 3 \ + "1" "YES (Silent Mode)" \ + "2" "YES (Verbose Mode)" \ + "3" "NO (Cancel Update)" --nocancel --default-item "1" 3>&1 1>&2 2>&3) + + case "$CHOICE" in + 1) + VERBOSE="no" + set_std_mode + ;; + 2) + VERBOSE="yes" + set_std_mode + ;; + 3) + clear + exit_script + exit + ;; + esac + update_script + fi +} + +# ------------------------------------------------------------------------------ +# build_container() +# +# - Creates and configures the LXC container +# - Builds network string and applies features (FUSE, TUN, VAAPI passthrough) +# - Starts container and waits for network connectivity +# - Installs base packages, SSH keys, and runs -install.sh +# ------------------------------------------------------------------------------ +build_container() { + # if [ "$VERBOSE" == "yes" ]; then set -x; fi + + NET_STRING="-net0 name=eth0,bridge=${BRG:-vmbr0}" + NET_STRING+="${MAC:+,hwaddr=$MAC}" + NET_STRING+=",ip=${NET:-dhcp}" + NET_STRING+="${GATE:+,gw=$GATE}" + NET_STRING+="${VLAN:+,tag=$VLAN}" + NET_STRING+="${MTU:+,mtu=$MTU}" + case "$IPV6_METHOD" in + auto) NET_STRING="$NET_STRING,ip6=auto" ;; + dhcp) NET_STRING="$NET_STRING,ip6=dhcp" ;; + static) + NET_STRING="$NET_STRING,ip6=$IPV6_ADDR" + [ -n "$IPV6_GATE" ] && NET_STRING="$NET_STRING,gw6=$IPV6_GATE" + ;; + none) ;; + esac + + if [ "$CT_TYPE" == "1" ]; then + FEATURES="keyctl=1,nesting=1" + else + FEATURES="nesting=1" + fi + + if [ "$ENABLE_FUSE" == "yes" ]; then + FEATURES="$FEATURES,fuse=1" + fi + + #if [[ $DIAGNOSTICS == "yes" ]]; then + # post_to_api + #fi + + TEMP_DIR=$(mktemp -d) + pushd "$TEMP_DIR" >/dev/null + if [ "$var_os" == "alpine" ]; then + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-install.func)" + else + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" + fi + export DIAGNOSTICS="$DIAGNOSTICS" + export RANDOM_UUID="$RANDOM_UUID" + export CACHER="$APT_CACHER" + export CACHER_IP="$APT_CACHER_IP" + export tz="$timezone" + #export DISABLEIPV6="$DISABLEIP6" + export APPLICATION="$APP" + export app="$NSAPP" + export PASSWORD="$PW" + export VERBOSE="$VERBOSE" + export SSH_ROOT="${SSH}" + export SSH_AUTHORIZED_KEY + export CTID="$CT_ID" + export CTTYPE="$CT_TYPE" + export ENABLE_FUSE="$ENABLE_FUSE" + export ENABLE_TUN="$ENABLE_TUN" + export PCT_OSTYPE="$var_os" + export PCT_OSVERSION="${var_version%%.*}" + export PCT_DISK_SIZE="$DISK_SIZE" + export PCT_OPTIONS=" + -features $FEATURES + -hostname $HN + -tags $TAGS + $SD + $NS + $NET_STRING + -onboot 1 + -cores $CORE_COUNT + -memory $RAM_SIZE + -unprivileged $CT_TYPE + $PW +" + export TEMPLATE_STORAGE="${var_template_storage:-}" + export CONTAINER_STORAGE="${var_container_storage:-}" + create_lxc_container || exit $? + + LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" + + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/passthrough.func) + select_hw_passthrough "$CTID" "$CT_TYPE" "$APP" + + # TUN device passthrough + if [ "$ENABLE_TUN" == "yes" ]; then + cat <>"$LXC_CONFIG" +lxc.cgroup2.devices.allow: c 10:200 rwm +lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file +EOF + fi + + # This starts the container and executes -install.sh + msg_info "Starting LXC Container" + pct start "$CTID" + + # wait for status 'running' + for i in {1..10}; do + if pct status "$CTID" | grep -q "status: running"; then + msg_ok "Started LXC Container" + break + fi + sleep 1 + if [ "$i" -eq 10 ]; then + msg_error "LXC Container did not reach running state" + exit 1 + fi + done + + if [ "$var_os" != "alpine" ]; then + msg_info "Waiting for network in LXC container" + + # --- Step 1: Wait for IP --- + for i in {1..20}; do + ip_in_lxc=$(pct exec "$CTID" -- ip -4 addr show dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) + [ -n "$ip_in_lxc" ] && break + sleep 1 + done + + if [ -z "$ip_in_lxc" ]; then + msg_error "No IP assigned to CT $CTID after 20s" + exit 1 + fi + + # --- Step 2: Try to reach gateway --- + gw_ok=0 + for i in {1..10}; do + if pct exec "$CTID" -- ping -c1 -W1 "$GATEWAY" >/dev/null 2>&1; then + gw_ok=1 + break + fi + sleep 1 + done + + if [ "$gw_ok" -eq 1 ]; then + msg_ok "CT $CTID gateway $GATEWAY reachable (IP $ip_in_lxc)" + else + msg_warn "CT $CTID has IP $ip_in_lxc but gateway $GATEWAY did not reply" + fi + + # --- Step 3: DNS / Internet check --- + if pct exec "$CTID" -- getent hosts deb.debian.org >/dev/null 2>&1; then + msg_ok "Network in LXC is reachable (DNS OK, IP $ip_in_lxc)" + else + msg_warn "Network reachable (IP $ip_in_lxc), but DNS failed – applying fallback resolv.conf" + pct exec "$CTID" -- bash -c 'echo "nameserver 1.1.1.1" > /etc/resolv.conf && echo "nameserver 8.8.8.8" >> /etc/resolv.conf' + if pct exec "$CTID" -- getent hosts deb.debian.org >/dev/null 2>&1; then + msg_ok "Network reachable after DNS fallback" + else + msg_error "Still no DNS/network in LXC! Aborting customization." + exit_script + fi + fi + fi + + msg_info "Customizing LXC Container" + #if [ "$var_os" != "alpine" ]; then + # vaapi_inside_setup "$CTID" "$CT_TYPE" "$APP" + # nvidia_inside_setup "$CTID" "$CT_TYPE" "$APP" + #fi + if [ "$var_os" == "alpine" ]; then + sleep 3 + pct exec "$CTID" -- /bin/sh -c 'cat </etc/apk/repositories +http://dl-cdn.alpinelinux.org/alpine/latest-stable/main +http://dl-cdn.alpinelinux.org/alpine/latest-stable/community +EOF' + pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses jq >/dev/null" + else + sleep 3 + + pct exec "$CTID" -- bash -c "sed -i '/$LANG/ s/^# //' /etc/locale.gen" + pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \ + echo LANG=\$locale_line >/etc/default/locale && \ + locale-gen >/dev/null && \ + export LANG=\$locale_line" + + if [[ -z "${tz:-}" ]]; then + tz=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "Etc/UTC") + fi + + if pct exec "$CTID" -- test -e "/usr/share/zoneinfo/$tz"; then + pct exec "$CTID" -- bash -c "tz='$tz'; echo \"\$tz\" >/etc/timezone && ln -sf \"/usr/share/zoneinfo/\$tz\" /etc/localtime" + else + msg_warn "Skipping timezone setup – zone '$tz' not found in container" + fi + + pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y sudo curl mc gnupg2 jq >/dev/null" || { + msg_error "apt-get base packages installation failed" + exit 1 + } + fi + msg_ok "Customized LXC Container" + install_ssh_keys_into_ct + if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)"; then + exit $? + fi +} + +destroy_lxc() { + if [[ -z "$CT_ID" ]]; then + msg_error "No CT_ID found. Nothing to remove." + return 1 + fi + + # Abbruch bei Ctrl-C / Ctrl-D / ESC + trap 'echo; msg_error "Aborted by user (SIGINT/SIGQUIT)"; return 130' INT QUIT + + local prompt + if ! read -rp "Remove this Container? " prompt; then + # read gibt != 0 zurück bei Ctrl-D/ESC + msg_error "Aborted input (Ctrl-D/ESC)" + return 130 + fi + + case "${prompt,,}" in + y | yes) + if pct stop "$CT_ID" &>/dev/null && pct destroy "$CT_ID" &>/dev/null; then + msg_ok "Removed Container $CT_ID" + else + msg_error "Failed to remove Container $CT_ID" + return 1 + fi + ;; + "" | n | no) + msg_info "Container was not removed." + ;; + *) + msg_warn "Invalid response. Container was not removed." + ;; + esac +} + +# ------------------------------------------------------------------------------ +# Storage discovery / selection helpers +# ------------------------------------------------------------------------------ +# ===== Storage discovery / selection helpers (ported from create_lxc.sh) ===== +resolve_storage_preselect() { + local class="$1" preselect="$2" required_content="" + case "$class" in + template) required_content="vztmpl" ;; + container) required_content="rootdir" ;; + *) return 1 ;; + esac + [[ -z "$preselect" ]] && return 1 + if ! pvesm status -content "$required_content" | awk 'NR>1{print $1}' | grep -qx -- "$preselect"; then + msg_warn "Preselected storage '${preselect}' does not support content '${required_content}' (or not found)" + return 1 + fi + + local line total used free + line="$(pvesm status | awk -v s="$preselect" 'NR>1 && $1==s {print $0}')" + if [[ -z "$line" ]]; then + STORAGE_INFO="n/a" + else + total="$(awk '{print $4}' <<<"$line")" + used="$(awk '{print $5}' <<<"$line")" + free="$(awk '{print $6}' <<<"$line")" + local total_h used_h free_h + if command -v numfmt >/dev/null 2>&1; then + total_h="$(numfmt --to=iec --suffix=B --format %.1f "$total" 2>/dev/null || echo "$total")" + used_h="$(numfmt --to=iec --suffix=B --format %.1f "$used" 2>/dev/null || echo "$used")" + free_h="$(numfmt --to=iec --suffix=B --format %.1f "$free" 2>/dev/null || echo "$free")" + STORAGE_INFO="Free: ${free_h} Used: ${used_h}" + else + STORAGE_INFO="Free: ${free} Used: ${used}" + fi + fi + STORAGE_RESULT="$preselect" + return 0 +} + +check_storage_support() { + local CONTENT="$1" VALID=0 + while IFS= read -r line; do + local STORAGE_NAME + STORAGE_NAME=$(awk '{print $1}' <<<"$line") + [[ -n "$STORAGE_NAME" ]] && VALID=1 + done < <(pvesm status -content "$CONTENT" 2>/dev/null | awk 'NR>1') + [[ $VALID -eq 1 ]] +} + +select_storage() { + local CLASS=$1 CONTENT CONTENT_LABEL + case $CLASS in + container) + CONTENT='rootdir' + CONTENT_LABEL='Container' + ;; + template) + CONTENT='vztmpl' + CONTENT_LABEL='Container template' + ;; + iso) + CONTENT='iso' + CONTENT_LABEL='ISO image' + ;; + images) + CONTENT='images' + CONTENT_LABEL='VM Disk image' + ;; + backup) + CONTENT='backup' + CONTENT_LABEL='Backup' + ;; + snippets) + CONTENT='snippets' + CONTENT_LABEL='Snippets' + ;; + *) + msg_error "Invalid storage class '$CLASS'" + return 1 + ;; + esac + + declare -A STORAGE_MAP + local -a MENU=() + local COL_WIDTH=0 + + while read -r TAG TYPE _ TOTAL USED FREE _; do + [[ -n "$TAG" && -n "$TYPE" ]] || continue + local DISPLAY="${TAG} (${TYPE})" + local USED_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$USED") + local FREE_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$FREE") + local INFO="Free: ${FREE_FMT}B Used: ${USED_FMT}B" + STORAGE_MAP["$DISPLAY"]="$TAG" + MENU+=("$DISPLAY" "$INFO" "OFF") + ((${#DISPLAY} > COL_WIDTH)) && COL_WIDTH=${#DISPLAY} + done < <(pvesm status -content "$CONTENT" | awk 'NR>1') + + if [[ ${#MENU[@]} -eq 0 ]]; then + msg_error "No storage found for content type '$CONTENT'." + return 2 + fi + + if [[ $((${#MENU[@]} / 3)) -eq 1 ]]; then + STORAGE_RESULT="${STORAGE_MAP[${MENU[0]}]}" + STORAGE_INFO="${MENU[1]}" + return 0 + fi + + local WIDTH=$((COL_WIDTH + 42)) + while true; do + local DISPLAY_SELECTED + DISPLAY_SELECTED=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "Storage Pools" \ + --radiolist "Which storage pool for ${CONTENT_LABEL,,}?\n(Spacebar to select)" \ + 16 "$WIDTH" 6 "${MENU[@]}" 3>&1 1>&2 2>&3) || { exit_script; } + + DISPLAY_SELECTED=$(sed 's/[[:space:]]*$//' <<<"$DISPLAY_SELECTED") + if [[ -z "$DISPLAY_SELECTED" || -z "${STORAGE_MAP[$DISPLAY_SELECTED]+_}" ]]; then + whiptail --msgbox "No valid storage selected. Please try again." 8 58 + continue + fi + STORAGE_RESULT="${STORAGE_MAP[$DISPLAY_SELECTED]}" + for ((i = 0; i < ${#MENU[@]}; i += 3)); do + if [[ "${MENU[$i]}" == "$DISPLAY_SELECTED" ]]; then + STORAGE_INFO="${MENU[$i + 1]}" + break + fi + done + return 0 + done +} + +create_lxc_container() { + # ------------------------------------------------------------------------------ + # Optional verbose mode (debug tracing) + # ------------------------------------------------------------------------------ + if [[ "${CREATE_LXC_VERBOSE:-no}" == "yes" ]]; then set -x; fi + + # ------------------------------------------------------------------------------ + # Helpers (dynamic versioning / template parsing) + # ------------------------------------------------------------------------------ + pkg_ver() { dpkg-query -W -f='${Version}\n' "$1" 2>/dev/null || echo ""; } + pkg_cand() { apt-cache policy "$1" 2>/dev/null | awk '/Candidate:/ {print $2}'; } + + ver_ge() { dpkg --compare-versions "$1" ge "$2"; } + ver_gt() { dpkg --compare-versions "$1" gt "$2"; } + ver_lt() { dpkg --compare-versions "$1" lt "$2"; } + + # Extract Debian OS minor from template name: debian-13-standard_13.1-1_amd64.tar.zst => "13.1" + parse_template_osver() { sed -n 's/.*_\([0-9][0-9]*\(\.[0-9]\+\)\?\)-.*/\1/p' <<<"$1"; } + + # Offer upgrade for pve-container/lxc-pve if candidate > installed; optional auto-retry pct create + # Returns: + # 0 = no upgrade needed + # 1 = upgraded (and if do_retry=yes and retry succeeded, creation done) + # 2 = user declined + # 3 = upgrade attempted but failed OR retry failed + offer_lxc_stack_upgrade_and_maybe_retry() { + local do_retry="${1:-no}" # yes|no + local _pvec_i _pvec_c _lxcp_i _lxcp_c need=0 + + _pvec_i="$(pkg_ver pve-container)" + _lxcp_i="$(pkg_ver lxc-pve)" + _pvec_c="$(pkg_cand pve-container)" + _lxcp_c="$(pkg_cand lxc-pve)" + + if [[ -n "$_pvec_c" && "$_pvec_c" != "none" ]]; then + ver_gt "$_pvec_c" "${_pvec_i:-0}" && need=1 + fi + if [[ -n "$_lxcp_c" && "$_lxcp_c" != "none" ]]; then + ver_gt "$_lxcp_c" "${_lxcp_i:-0}" && need=1 + fi + if [[ $need -eq 0 ]]; then + msg_debug "No newer candidate for pve-container/lxc-pve (installed=$_pvec_i/$_lxcp_i, cand=$_pvec_c/$_lxcp_c)" + return 0 + fi + + echo + echo "An update for the Proxmox LXC stack is available:" + echo " pve-container: installed=${_pvec_i:-n/a} candidate=${_pvec_c:-n/a}" + echo " lxc-pve : installed=${_lxcp_i:-n/a} candidate=${_lxcp_c:-n/a}" + echo + read -rp "Do you want to upgrade now? [y/N] " _ans + case "${_ans,,}" in + y | yes) + msg_info "Upgrading Proxmox LXC stack (pve-container, lxc-pve)" + if apt-get update -qq >/dev/null && apt-get install -y --only-upgrade pve-container lxc-pve >/dev/null; then + msg_ok "LXC stack upgraded." + if [[ "$do_retry" == "yes" ]]; then + msg_info "Retrying container creation after upgrade" + if pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then + msg_ok "Container created successfully after upgrade." + return 1 + else + msg_error "pct create still failed after upgrade. See $LOGFILE" + return 3 + fi + fi + return 1 + else + msg_error "Upgrade failed. Please check APT output." + return 3 + fi + ;; + *) return 2 ;; + esac + } + + # ------------------------------------------------------------------------------ + # Required input variables + # ------------------------------------------------------------------------------ + [[ "${CTID:-}" ]] || { + msg_error "You need to set 'CTID' variable." + exit 203 + } + [[ "${PCT_OSTYPE:-}" ]] || { + msg_error "You need to set 'PCT_OSTYPE' variable." + exit 204 + } + + msg_debug "CTID=$CTID" + msg_debug "PCT_OSTYPE=$PCT_OSTYPE" + msg_debug "PCT_OSVERSION=${PCT_OSVERSION:-default}" + + # ID checks + [[ "$CTID" -ge 100 ]] || { + msg_error "ID cannot be less than 100." + exit 205 + } + if qm status "$CTID" &>/dev/null || pct status "$CTID" &>/dev/null; then + echo -e "ID '$CTID' is already in use." + unset CTID + msg_error "Cannot use ID that is already in use." + exit 206 + fi + + # Storage capability check + check_storage_support "rootdir" || { + msg_error "No valid storage found for 'rootdir' [Container]" + exit 1 + } + check_storage_support "vztmpl" || { + msg_error "No valid storage found for 'vztmpl' [Template]" + exit 1 + } + + # Template storage selection + if resolve_storage_preselect template "${TEMPLATE_STORAGE:-}"; then + TEMPLATE_STORAGE="$STORAGE_RESULT" + TEMPLATE_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${TEMPLATE_STORAGE}${CL} (${TEMPLATE_STORAGE_INFO}) [Template]" + else + while true; do + if select_storage template; then + TEMPLATE_STORAGE="$STORAGE_RESULT" + TEMPLATE_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${TEMPLATE_STORAGE}${CL} (${TEMPLATE_STORAGE_INFO}) [Template]" + break + fi + done + fi + + # Container storage selection + if resolve_storage_preselect container "${CONTAINER_STORAGE:-}"; then + CONTAINER_STORAGE="$STORAGE_RESULT" + CONTAINER_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${CONTAINER_STORAGE}${CL} (${CONTAINER_STORAGE_INFO}) [Container]" + else + while true; do + if select_storage container; then + CONTAINER_STORAGE="$STORAGE_RESULT" + CONTAINER_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${CONTAINER_STORAGE}${CL} (${CONTAINER_STORAGE_INFO}) [Container]" + break + fi + done + fi + + # Validate content types + msg_info "Validating content types of storage '$CONTAINER_STORAGE'" + STORAGE_CONTENT=$(grep -A4 -E "^(zfspool|dir|lvmthin|lvm): $CONTAINER_STORAGE" /etc/pve/storage.cfg | grep content | awk '{$1=""; print $0}' | xargs) + msg_debug "Storage '$CONTAINER_STORAGE' has content types: $STORAGE_CONTENT" + grep -qw "rootdir" <<<"$STORAGE_CONTENT" || { + msg_error "Storage '$CONTAINER_STORAGE' does not support 'rootdir'. Cannot create LXC." + exit 217 + } + $STD msg_ok "Storage '$CONTAINER_STORAGE' supports 'rootdir'" + + msg_info "Validating content types of template storage '$TEMPLATE_STORAGE'" + TEMPLATE_CONTENT=$(grep -A4 -E "^[^:]+: $TEMPLATE_STORAGE" /etc/pve/storage.cfg | grep content | awk '{$1=""; print $0}' | xargs) + msg_debug "Template storage '$TEMPLATE_STORAGE' has content types: $TEMPLATE_CONTENT" + if ! grep -qw "vztmpl" <<<"$TEMPLATE_CONTENT"; then + msg_warn "Template storage '$TEMPLATE_STORAGE' does not declare 'vztmpl'. This may cause pct create to fail." + else + $STD msg_ok "Template storage '$TEMPLATE_STORAGE' supports 'vztmpl'" + fi + + # Free space check + STORAGE_FREE=$(pvesm status | awk -v s="$CONTAINER_STORAGE" '$1 == s { print $6 }') + REQUIRED_KB=$((${PCT_DISK_SIZE:-8} * 1024 * 1024)) + [[ "$STORAGE_FREE" -ge "$REQUIRED_KB" ]] || { + msg_error "Not enough space on '$CONTAINER_STORAGE'. Needed: ${PCT_DISK_SIZE:-8}G." + exit 214 + } + + # Cluster quorum (if cluster) + if [[ -f /etc/pve/corosync.conf ]]; then + msg_info "Checking cluster quorum" + if ! pvecm status | awk -F':' '/^Quorate/ { exit ($2 ~ /Yes/) ? 0 : 1 }'; then + msg_error "Cluster is not quorate. Start all nodes or configure quorum device (QDevice)." + exit 210 + fi + msg_ok "Cluster is quorate" + fi + + # ------------------------------------------------------------------------------ + # Template discovery & validation + # ------------------------------------------------------------------------------ + TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" + case "$PCT_OSTYPE" in + debian | ubuntu) TEMPLATE_PATTERN="-standard_" ;; + alpine | fedora | rocky | centos) TEMPLATE_PATTERN="-default_" ;; + *) TEMPLATE_PATTERN="" ;; + esac + + msg_info "Searching for template '$TEMPLATE_SEARCH'" + + mapfile -t LOCAL_TEMPLATES < <( + pveam list "$TEMPLATE_STORAGE" 2>/dev/null | + awk -v s="$TEMPLATE_SEARCH" -v p="$TEMPLATE_PATTERN" '$1 ~ s && $1 ~ p {print $1}' | + sed 's|.*/||' | sort -t - -k 2 -V + ) + + pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)." + mapfile -t ONLINE_TEMPLATES < <( + pveam available -section system 2>/dev/null | + sed -n "s/.*\($TEMPLATE_SEARCH.*$TEMPLATE_PATTERN.*\)/\1/p" | + sort -t - -k 2 -V + ) + ONLINE_TEMPLATE="" + [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" + + if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then + TEMPLATE="${LOCAL_TEMPLATES[-1]}" + TEMPLATE_SOURCE="local" + else + TEMPLATE="$ONLINE_TEMPLATE" + TEMPLATE_SOURCE="online" + fi + + TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" + if [[ -z "$TEMPLATE_PATH" ]]; then + TEMPLATE_BASE=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) + [[ -n "$TEMPLATE_BASE" ]] && TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE" + fi + [[ -n "$TEMPLATE_PATH" ]] || { + msg_error "Unable to resolve template path for $TEMPLATE_STORAGE. Check storage type and permissions." + exit 220 + } + + msg_ok "Template ${BL}$TEMPLATE${CL} [$TEMPLATE_SOURCE]" + msg_debug "Resolved TEMPLATE_PATH=$TEMPLATE_PATH" + + NEED_DOWNLOAD=0 + if [[ ! -f "$TEMPLATE_PATH" ]]; then + msg_info "Template not present locally – will download." + NEED_DOWNLOAD=1 + elif [[ ! -r "$TEMPLATE_PATH" ]]; then + msg_error "Template file exists but is not readable – check permissions." + exit 221 + elif [[ "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then + if [[ -n "$ONLINE_TEMPLATE" ]]; then + msg_warn "Template file too small (<1MB) – re-downloading." + NEED_DOWNLOAD=1 + else + msg_warn "Template looks too small, but no online version exists. Keeping local file." + fi + elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then + if [[ -n "$ONLINE_TEMPLATE" ]]; then + msg_warn "Template appears corrupted – re-downloading." + NEED_DOWNLOAD=1 + else + msg_warn "Template appears corrupted, but no online version exists. Keeping local file." + fi + else + $STD msg_ok "Template $TEMPLATE is present and valid." + fi + + if [[ "$TEMPLATE_SOURCE" == "local" && -n "$ONLINE_TEMPLATE" && "$TEMPLATE" != "$ONLINE_TEMPLATE" ]]; then + msg_warn "Local template is outdated: $TEMPLATE (latest available: $ONLINE_TEMPLATE)" + if whiptail --yesno "A newer template is available:\n$ONLINE_TEMPLATE\n\nDo you want to download and use it instead?" 12 70; then + TEMPLATE="$ONLINE_TEMPLATE" + NEED_DOWNLOAD=1 + else + msg_info "Continuing with local template $TEMPLATE" + fi + fi + + if [[ "$NEED_DOWNLOAD" -eq 1 ]]; then + [[ -f "$TEMPLATE_PATH" ]] && rm -f "$TEMPLATE_PATH" + for attempt in {1..3}; do + msg_info "Attempt $attempt: Downloading template $TEMPLATE to $TEMPLATE_STORAGE" + if pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null 2>&1; then + msg_ok "Template download successful." + break + fi + if [[ $attempt -eq 3 ]]; then + msg_error "Failed after 3 attempts. Please check network access, permissions, or manually run:\n pveam download $TEMPLATE_STORAGE $TEMPLATE" + exit 222 + fi + sleep $((attempt * 5)) + done + fi + + if ! pveam list "$TEMPLATE_STORAGE" 2>/dev/null | grep -q "$TEMPLATE"; then + msg_error "Template $TEMPLATE not available in storage $TEMPLATE_STORAGE after download." + exit 223 + fi + + # ------------------------------------------------------------------------------ + # Dynamic preflight for Debian 13.x: offer upgrade if available (no hard mins) + # ------------------------------------------------------------------------------ + if [[ "$PCT_OSTYPE" == "debian" ]]; then + OSVER="$(parse_template_osver "$TEMPLATE")" + if [[ -n "$OSVER" ]]; then + # Proactive, aber ohne Abbruch – nur Angebot + offer_lxc_stack_upgrade_and_maybe_retry "no" || true + fi + fi + + # ------------------------------------------------------------------------------ + # Create LXC Container + # ------------------------------------------------------------------------------ + msg_info "Creating LXC container" + + # Ensure subuid/subgid entries exist + grep -q "root:100000:65536" /etc/subuid || echo "root:100000:65536" >>/etc/subuid + grep -q "root:100000:65536" /etc/subgid || echo "root:100000:65536" >>/etc/subgid + + # Assemble pct options + PCT_OPTIONS=(${PCT_OPTIONS[@]:-${DEFAULT_PCT_OPTIONS[@]}}) + [[ " ${PCT_OPTIONS[*]} " =~ " -rootfs " ]] || PCT_OPTIONS+=(-rootfs "$CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}") + + # Lock by template file (avoid concurrent downloads/creates) + lockfile="/tmp/template.${TEMPLATE}.lock" + exec 9>"$lockfile" || { + msg_error "Failed to create lock file '$lockfile'." + exit 200 + } + flock -w 60 9 || { + msg_error "Timeout while waiting for template lock." + exit 211 + } + + LOGFILE="/tmp/pct_create_${CTID}.log" + msg_debug "pct create command: pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" + msg_debug "Logfile: $LOGFILE" + + # First attempt + if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >"$LOGFILE" 2>&1; then + msg_error "Container creation failed on ${TEMPLATE_STORAGE}. Checking template..." + + # Validate template file + if [[ ! -s "$TEMPLATE_PATH" || "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then + msg_warn "Template file too small or missing – re-downloading." + rm -f "$TEMPLATE_PATH" + pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" + elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then + if [[ -n "$ONLINE_TEMPLATE" ]]; then + msg_warn "Template appears corrupted – re-downloading." + rm -f "$TEMPLATE_PATH" + pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" + else + msg_warn "Template appears corrupted, but no online version exists. Skipping re-download." + fi + fi + + # Retry after repair + if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then + # Fallback to local storage + if [[ "$TEMPLATE_STORAGE" != "local" ]]; then + msg_warn "Retrying container creation with fallback to local storage..." + LOCAL_TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" + if [[ ! -f "$LOCAL_TEMPLATE_PATH" ]]; then + msg_info "Downloading template to local..." + pveam download local "$TEMPLATE" >/dev/null 2>&1 + fi + if pct create "$CTID" "local:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then + msg_ok "Container successfully created using local fallback." + else + # --- Dynamic stack upgrade + auto-retry on the well-known error pattern --- + if grep -qiE 'unsupported .* version' "$LOGFILE"; then + echo + echo "pct reported 'unsupported ... version' – your LXC stack might be too old for this template." + echo "We can try to upgrade 'pve-container' and 'lxc-pve' now and retry automatically." + if offer_lxc_stack_upgrade_and_maybe_retry "yes"; then + : # success after retry + else + rc=$? + case $rc in + 2) echo "Upgrade was declined. Please update and re-run: + apt update && apt install --only-upgrade pve-container lxc-pve" ;; + 3) echo "Upgrade and/or retry failed. Please inspect: $LOGFILE" ;; + esac + exit 231 + fi + else + msg_error "Container creation failed even with local fallback. See $LOGFILE" + if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then + set -x + bash -x -c "pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" 2>&1 | tee -a "$LOGFILE" + set +x + fi + exit 209 + fi + fi + else + msg_error "Container creation failed on local storage. See $LOGFILE" + # --- Dynamic stack upgrade + auto-retry on the well-known error pattern --- + if grep -qiE 'unsupported .* version' "$LOGFILE"; then + echo + echo "pct reported 'unsupported ... version' – your LXC stack might be too old for this template." + echo "We can try to upgrade 'pve-container' and 'lxc-pve' now and retry automatically." + if offer_lxc_stack_upgrade_and_maybe_retry "yes"; then + : # success after retry + else + rc=$? + case $rc in + 2) echo "Upgrade was declined. Please update and re-run: + apt update && apt install --only-upgrade pve-container lxc-pve" ;; + 3) echo "Upgrade and/or retry failed. Please inspect: $LOGFILE" ;; + esac + exit 231 + fi + else + if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then + set -x + bash -x -c "pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" 2>&1 | tee -a "$LOGFILE" + set +x + fi + exit 209 + fi + fi + fi + fi + + # Verify container exists + pct list | awk '{print $1}' | grep -qx "$CTID" || { + msg_error "Container ID $CTID not listed in 'pct list'. See $LOGFILE" + exit 215 + } + + # Verify config rootfs + grep -q '^rootfs:' "/etc/pve/lxc/$CTID.conf" || { + msg_error "RootFS entry missing in container config. See $LOGFILE" + exit 216 + } + + msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created." +} + +# ------------------------------------------------------------------------------ +# description() +# +# - Sets container description with HTML content (logo, links, badges) +# - Restarts ping-instances.service if present +# - Posts status "done" to API +# ------------------------------------------------------------------------------ +description() { + IP=$(pct exec "$CTID" ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) + + # Generate LXC Description + DESCRIPTION=$( + cat < + + Logo + + +

${APP} LXC

+ +

+ + spend Coffee + +

+ + + + GitHub + + + + Discussions + + + + Issues + + +EOF + ) + pct set "$CTID" -description "$DESCRIPTION" + + if [[ -f /etc/systemd/system/ping-instances.service ]]; then + systemctl start ping-instances.service + fi + + post_update_to_api "done" "none" +} + +# ------------------------------------------------------------------------------ +# api_exit_script() +# +# - Exit trap handler +# - Reports exit codes to API with detailed reason +# - Handles known codes (100–209) and maps them to errors +# ------------------------------------------------------------------------------ +api_exit_script() { + exit_code=$? + if [ $exit_code -ne 0 ]; then + case $exit_code in + 100) post_update_to_api "failed" "100: Unexpected error in create_lxc.sh" ;; + 101) post_update_to_api "failed" "101: No network connection detected in create_lxc.sh" ;; + 200) post_update_to_api "failed" "200: LXC creation failed in create_lxc.sh" ;; + 201) post_update_to_api "failed" "201: Invalid Storage class in create_lxc.sh" ;; + 202) post_update_to_api "failed" "202: User aborted menu in create_lxc.sh" ;; + 203) post_update_to_api "failed" "203: CTID not set in create_lxc.sh" ;; + 204) post_update_to_api "failed" "204: PCT_OSTYPE not set in create_lxc.sh" ;; + 205) post_update_to_api "failed" "205: CTID cannot be less than 100 in create_lxc.sh" ;; + 206) post_update_to_api "failed" "206: CTID already in use in create_lxc.sh" ;; + 207) post_update_to_api "failed" "207: Template not found in create_lxc.sh" ;; + 208) post_update_to_api "failed" "208: Error downloading template in create_lxc.sh" ;; + 209) post_update_to_api "failed" "209: Container creation failed, but template is intact in create_lxc.sh" ;; + *) post_update_to_api "failed" "Unknown error, exit code: $exit_code in create_lxc.sh" ;; + esac + fi +} + +if command -v pveversion >/dev/null 2>&1; then + trap 'api_exit_script' EXIT +fi +trap 'post_update_to_api "failed" "$BASH_COMMAND"' ERR +trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT +trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM diff --git a/misc/install.func b/misc/install.func new file mode 100644 index 000000000..f741b921d --- /dev/null +++ b/misc/install.func @@ -0,0 +1,206 @@ +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# Co-Author: MickLesk +# Co-Author: michelroegl-brunner +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE + +if ! command -v curl >/dev/null 2>&1; then + printf "\r\e[2K%b" '\033[93m Setup Source \033[m' >&2 + apt-get update >/dev/null 2>&1 + apt-get install -y curl >/dev/null 2>&1 +fi +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) +load_functions +catch_errors + +# This function enables IPv6 if it's not disabled and sets verbose mode +verb_ip6() { + set_std_mode # Set STD mode based on VERBOSE + + if [ "$DISABLEIPV6" == "yes" ]; then + echo "net.ipv6.conf.all.disable_ipv6 = 1" >>/etc/sysctl.conf + $STD sysctl -p + fi +} + +# # This function sets error handling options and defines the error_handler function to handle errors +# catch_errors() { +# set -Eeuo pipefail +# trap 'error_handler $LINENO "$BASH_COMMAND"' ERR +# } + +# # This function handles errors +# error_handler() { +# source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/api.func) +# local exit_code="$1" +# local line_number="$2" +# local command="${3:-}" + +# if [[ "$exit_code" -eq 0 ]]; then +# return 0 +# fi + +# printf "\e[?25h" +# echo -e "\n${RD}[ERROR]${CL} in line ${RD}${line_number}${CL}: exit code ${RD}${exit_code}${CL}: while executing command ${YW}${command}${CL}\n" +# exit "$exit_code" +#} + +# This function sets up the Container OS by generating the locale, setting the timezone, and checking the network connection +setting_up_container() { + msg_info "Setting up Container OS" + for ((i = RETRY_NUM; i > 0; i--)); do + if [ "$(hostname -I)" != "" ]; then + break + fi + echo 1>&2 -en "${CROSS}${RD} No Network! " + sleep $RETRY_EVERY + done + if [ "$(hostname -I)" = "" ]; then + echo 1>&2 -e "\n${CROSS}${RD} No Network After $RETRY_NUM Tries${CL}" + echo -e "${NETWORK}Check Network Settings" + exit 1 + fi + rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED + systemctl disable -q --now systemd-networkd-wait-online.service + msg_ok "Set up Container OS" + #msg_custom "${CM}" "${GN}" "Network Connected: ${BL}$(hostname -I)" + msg_ok "Network Connected: ${BL}$(hostname -I)" +} + +# This function checks the network connection by pinging a known IP address and prompts the user to continue if the internet is not connected +network_check() { + set +e + trap - ERR + ipv4_connected=false + ipv6_connected=false + sleep 1 + + # Check IPv4 connectivity to Google, Cloudflare & Quad9 DNS servers. + if ping -c 1 -W 1 1.1.1.1 &>/dev/null || ping -c 1 -W 1 8.8.8.8 &>/dev/null || ping -c 1 -W 1 9.9.9.9 &>/dev/null; then + msg_ok "IPv4 Internet Connected" + ipv4_connected=true + else + msg_error "IPv4 Internet Not Connected" + fi + + # Check IPv6 connectivity to Google, Cloudflare & Quad9 DNS servers. + if ping6 -c 1 -W 1 2606:4700:4700::1111 &>/dev/null || ping6 -c 1 -W 1 2001:4860:4860::8888 &>/dev/null || ping6 -c 1 -W 1 2620:fe::fe &>/dev/null; then + msg_ok "IPv6 Internet Connected" + ipv6_connected=true + else + msg_error "IPv6 Internet Not Connected" + fi + + # If both IPv4 and IPv6 checks fail, prompt the user + if [[ $ipv4_connected == false && $ipv6_connected == false ]]; then + read -r -p "No Internet detected, would you like to continue anyway? " prompt + if [[ "${prompt,,}" =~ ^(y|yes)$ ]]; then + echo -e "${INFO}${RD}Expect Issues Without Internet${CL}" + else + echo -e "${NETWORK}Check Network Settings" + exit 1 + fi + fi + + # DNS resolution checks for GitHub-related domains (IPv4 and/or IPv6) + GIT_HOSTS=("github.com" "raw.githubusercontent.com" "api.github.com" "git.community-scripts.org") + GIT_STATUS="Git DNS:" + DNS_FAILED=false + + for HOST in "${GIT_HOSTS[@]}"; do + RESOLVEDIP=$(getent hosts "$HOST" | awk '{ print $1 }' | grep -E '(^([0-9]{1,3}\.){3}[0-9]{1,3}$)|(^[a-fA-F0-9:]+$)' | head -n1) + if [[ -z "$RESOLVEDIP" ]]; then + GIT_STATUS+="$HOST:($DNSFAIL)" + DNS_FAILED=true + else + GIT_STATUS+=" $HOST:($DNSOK)" + fi + done + + if [[ "$DNS_FAILED" == true ]]; then + fatal "$GIT_STATUS" + else + msg_ok "$GIT_STATUS" + fi + + set -e + trap 'error_handler $LINENO "$BASH_COMMAND"' ERR +} + +# This function updates the Container OS by running apt-get update and upgrade +update_os() { + msg_info "Updating Container OS" + if [[ "$CACHER" == "yes" ]]; then + echo "Acquire::http::Proxy-Auto-Detect \"/usr/local/bin/apt-proxy-detect.sh\";" >/etc/apt/apt.conf.d/00aptproxy + cat <<'EOF' >/usr/local/bin/apt-proxy-detect.sh +#!/bin/bash +if nc -w1 -z "${CACHER_IP}" 3142; then + echo -n "http://${CACHER_IP}:3142" +else + echo -n "DIRECT" +fi +EOF + chmod +x /usr/local/bin/apt-proxy-detect.sh + fi + $STD apt-get update + $STD apt-get -o Dpkg::Options::="--force-confold" -y dist-upgrade + rm -rf /usr/lib/python3.*/EXTERNALLY-MANAGED + msg_ok "Updated Container OS" + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) +} + +# This function modifies the message of the day (motd) and SSH settings +motd_ssh() { + grep -qxF "export TERM='xterm-256color'" /root/.bashrc || echo "export TERM='xterm-256color'" >>/root/.bashrc + + if [ -f "/etc/os-release" ]; then + OS_NAME=$(grep ^NAME /etc/os-release | cut -d= -f2 | tr -d '"') + OS_VERSION=$(grep ^VERSION_ID /etc/os-release | cut -d= -f2 | tr -d '"') + elif [ -f "/etc/debian_version" ]; then + OS_NAME="Debian" + OS_VERSION=$(cat /etc/debian_version) + fi + + PROFILE_FILE="/etc/profile.d/00_lxc-details.sh" + echo "echo -e \"\"" >"$PROFILE_FILE" + echo -e "echo -e \"${BOLD}${YW}${APPLICATION} LXC Container - DEV Repository${CL}\"" >>"$PROFILE_FILE" + echo -e "echo -e \"${RD}WARNING: This is a DEVELOPMENT version (ProxmoxVED). Do NOT use in production!${CL}\"" >>"$PROFILE_FILE" + echo -e "echo -e \"${YW} OS: ${GN}${OS_NAME} - Version: ${OS_VERSION}${CL}\"" >>"$PROFILE_FILE" + echo -e "echo -e \"${YW} Hostname: ${GN}\$(hostname)${CL}\"" >>"$PROFILE_FILE" + echo -e "echo -e \"${YW} IP Address: ${GN}\$(hostname -I | awk '{print \$1}')${CL}\"" >>"$PROFILE_FILE" + echo -e "echo -e \"${YW} Repository: ${GN}https://github.com/community-scripts/ProxmoxVED${CL}\"" >>"$PROFILE_FILE" + echo "echo \"\"" >>"$PROFILE_FILE" + + chmod -x /etc/update-motd.d/* + + if [[ "${SSH_ROOT}" == "yes" ]]; then + sed -i "s/#PermitRootLogin prohibit-password/PermitRootLogin yes/g" /etc/ssh/sshd_config + systemctl restart sshd + fi +} + +# This function customizes the container by modifying the getty service and enabling auto-login for the root user +customize() { + if [[ "$PASSWORD" == "" ]]; then + msg_info "Customizing Container" + GETTY_OVERRIDE="/etc/systemd/system/container-getty@1.service.d/override.conf" + mkdir -p $(dirname $GETTY_OVERRIDE) + cat <$GETTY_OVERRIDE + [Service] + ExecStart= + ExecStart=-/sbin/agetty --autologin root --noclear --keep-baud tty%I 115200,38400,9600 \$TERM +EOF + systemctl daemon-reload + systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//') + msg_ok "Customized Container" + fi + echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update + chmod +x /usr/bin/update + if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then + mkdir -p /root/.ssh + echo "${SSH_AUTHORIZED_KEY}" >/root/.ssh/authorized_keys + chmod 700 /root/.ssh + chmod 600 /root/.ssh/authorized_keys + fi +} From d6b981a1b2f0bb44bfc9014dc731176160b3b482 Mon Sep 17 00:00:00 2001 From: Vincent <114195376+HydroshieldMKII@users.noreply.github.com> Date: Wed, 1 Oct 2025 10:33:56 -0400 Subject: [PATCH 1335/1733] Update guardian.sh to source build.func from the correct repository --- ct/guardian.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/guardian.sh b/ct/guardian.sh index c33a5c06c..41fbaf30a 100755 --- a/ct/guardian.sh +++ b/ct/guardian.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/HydroshieldMKII/ProxmoxVED/refs/heads/add-guardian-app/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: HydroshieldMKII # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE From 6a5515a12c5188ae5a919370d6733a2333b3b400 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 1 Oct 2025 14:34:57 +0000 Subject: [PATCH 1336/1733] Update .app files --- ct/headers/guardian | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/guardian diff --git a/ct/headers/guardian b/ct/headers/guardian new file mode 100644 index 000000000..e19ee5708 --- /dev/null +++ b/ct/headers/guardian @@ -0,0 +1,6 @@ + ______ ___ + / ____/_ ______ __________/ (_)___ _____ + / / __/ / / / __ `/ ___/ __ / / __ `/ __ \ +/ /_/ / /_/ / /_/ / / / /_/ / / /_/ / / / / +\____/\__,_/\__,_/_/ \__,_/_/\__,_/_/ /_/ + From 57c9deb39bb393d35338a36a253783079ec9fc23 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 1 Oct 2025 17:35:19 +0200 Subject: [PATCH 1337/1733] Update guardian --- install/guardian-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/guardian-install.sh b/install/guardian-install.sh index b982bf3c9..8c917a7fb 100644 --- a/install/guardian-install.sh +++ b/install/guardian-install.sh @@ -28,7 +28,7 @@ cd /opt/guardian/frontend $STD npm ci export DEPLOYMENT_MODE=standalone $STD npm run build -msg_ok "Configured {APPLICATION}" +msg_ok "Configured ${APPLICATION}" msg_info "Creating Service" cat </etc/systemd/system/guardian-backend.service From 81d9e534ecb5fc77492f50c1143999c73106487f Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 1 Oct 2025 18:54:42 +0200 Subject: [PATCH 1338/1733] Update rwMarkable --- install/rwmarkable-install.sh | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/install/rwmarkable-install.sh b/install/rwmarkable-install.sh index 6f9f32e65..828c0a851 100644 --- a/install/rwmarkable-install.sh +++ b/install/rwmarkable-install.sh @@ -16,15 +16,13 @@ update_os NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs fetch_and_deploy_gh_release "rwMarkable" "fccview/rwMarkable" "tarball" "latest" "/opt/rwmarkable" -msg_info "Building app" +msg_info "Installing ${APPLICATION}" cd /opt/rwmarkable $STD yarn --frozen-lockfile $STD yarn next telemetry disable $STD yarn build mkdir -p data/{users,checklists,notes} -msg_ok "Successfully built app" -msg_info "Creating .env file" cat </opt/rwmarkable/.env NODE_ENV=production # HTTPS=true @@ -38,9 +36,9 @@ NODE_ENV=production # OIDC_CLIENT_SECRET=your_client_secret # Enable confidential client mode with client authentication # OIDC_ADMIN_GROUPS=admins # Map provider groups to admin role EOF -msg_ok "Created .env file" +msg_ok "Installed ${APPLICATION}" -msg_info "Creating rwMarkable Service" +msg_info "Creating Service" cat </etc/systemd/system/rwmarkable.service [Unit] Description=rwMarkable server @@ -56,7 +54,8 @@ Restart=on-abnormal WantedBy=multi-user.target EOF systemctl enable -q --now rwmarkable -msg_ok "Created rwMarkable Service" +msg_ok "Created Service" + motd_ssh customize From 7e6d5b357459039fd7db68530cc4475cb5c8343f Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 1 Oct 2025 18:56:40 +0200 Subject: [PATCH 1339/1733] Update rwMarkable --- install/rwmarkable-install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install/rwmarkable-install.sh b/install/rwmarkable-install.sh index 828c0a851..b5cb2abd8 100644 --- a/install/rwmarkable-install.sh +++ b/install/rwmarkable-install.sh @@ -62,4 +62,5 @@ customize msg_info "Cleaning up" $STD apt-get -y autoremove $STD apt-get -y autoclean +$STD apt clean -y msg_ok "Cleaned" From 479bd712eb5e344e61ce9d1dce40f8ae45daf5b9 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 1 Oct 2025 19:04:31 +0200 Subject: [PATCH 1340/1733] Update rwMarkable --- install/rwmarkable-install.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/install/rwmarkable-install.sh b/install/rwmarkable-install.sh index b5cb2abd8..afba71011 100644 --- a/install/rwmarkable-install.sh +++ b/install/rwmarkable-install.sh @@ -60,7 +60,7 @@ motd_ssh customize msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -$STD apt clean -y +$STD apt -y autoremove +$STD apt -y autoclean +$STD apt -y clean msg_ok "Cleaned" From 7456d8bf5a7a02ac5002130eec7136b38354b54f Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 2 Oct 2025 10:05:29 +0200 Subject: [PATCH 1341/1733] plex refactor --- ct/plex.sh | 55 ++++++++++++++++++++++++++++ frontend/public/json/phpmyadmin.json | 40 -------------------- install/plex-install.sh | 48 ++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 40 deletions(-) create mode 100644 ct/plex.sh delete mode 100644 frontend/public/json/phpmyadmin.json create mode 100644 install/plex-install.sh diff --git a/ct/plex.sh b/ct/plex.sh new file mode 100644 index 000000000..def84c8e9 --- /dev/null +++ b/ct/plex.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://www.plex.tv/ + +APP="Plex" +var_tags="${var_tags:-media}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-8}" +var_os="${var_os:-ubuntu}" +var_version="${var_version:-24.04}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -f /etc/apt/sources.list.d/plexmediaserver.list ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + UPD=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "SUPPORT" --radiolist --cancel-button Exit-Script "Spacebar = Select \nplexupdate info >> https://github.com/mrworf/plexupdate" 10 59 2 \ + "1" "Update LXC" ON \ + "2" "Install plexupdate" OFF \ + 3>&1 1>&2 2>&3) + if [ "$UPD" == "1" ]; then + msg_info "Updating ${APP} LXC" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Updated ${APP} LXC" + exit + fi + if [ "$UPD" == "2" ]; then + set +e + bash -c "$(curl -fsSL https://raw.githubusercontent.com/mrworf/plexupdate/master/extras/installer.sh)" + exit + fi +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:32400/web${CL}" diff --git a/frontend/public/json/phpmyadmin.json b/frontend/public/json/phpmyadmin.json deleted file mode 100644 index 6639b1c1e..000000000 --- a/frontend/public/json/phpmyadmin.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "PhpMyAdmin", - "slug": "phpmyadmin", - "categories": [ - 8 - ], - "date_created": "2025-04-29", - "type": "addon", - "updateable": true, - "privileged": false, - "interface_port": null, - "documentation": "https://www.phpmyadmin.net/docs/", - "config_path": "Debian/Ubuntu: /var/www/html/phpMyAdmin | Alpine: /usr/share/phpmyadmin", - "website": "https://www.phpmyadmin.net/", - "logo": "https://raw.githubusercontent.com/selfhst/icons/refs/heads/main/webp/phpmyadmin-light.webp", - "description": "phpMyAdmin is a free software tool written in PHP, intended to handle the administration of MySQL over the Web. phpMyAdmin supports a wide range of operations on MySQL and MariaDB. Frequently used operations (managing databases, tables, columns, relations, indexes, users, permissions, etc) can be performed via the user interface, while you still have the ability to directly execute any SQL statement.", - "install_methods": [ - { - "type": "default", - "script": "tools/addon/phpmyadmin.sh", - "resources": { - "cpu": null, - "ram": null, - "hdd": null, - "os": null, - "version": null - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "Execute within an existing LXC Console", - "type": "warning" - } - ] -} diff --git a/install/plex-install.sh b/install/plex-install.sh new file mode 100644 index 000000000..417697cbf --- /dev/null +++ b/install/plex-install.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://www.plex.tv/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Setting Up Hardware Acceleration" +$STD apt-get -y install {va-driver-all,ocl-icd-libopencl1,intel-opencl-icd,vainfo,intel-gpu-tools} +if [[ "$CTTYPE" == "0" ]]; then + chgrp video /dev/dri + chmod 755 /dev/dri + chmod 660 /dev/dri/* + $STD adduser $(id -u -n) video + $STD adduser $(id -u -n) render +fi +msg_ok "Set Up Hardware Acceleration" + +msg_info "Setting Up Plex Media Server Repository" +curl -fsSL https://downloads.plex.tv/plex-keys/PlexSign.key | tee /usr/share/keyrings/PlexSign.asc >/dev/null +echo "deb [signed-by=/usr/share/keyrings/PlexSign.asc] https://downloads.plex.tv/repo/deb/ public main" >/etc/apt/sources.list.d/plexmediaserver.list +msg_ok "Set Up Plex Media Server Repository" + +msg_info "Installing Plex Media Server" +$STD apt-get update +$STD apt-get -o Dpkg::Options::="--force-confold" install -y plexmediaserver +if [[ "$CTTYPE" == "0" ]]; then + sed -i -e 's/^ssl-cert:x:104:plex$/render:x:104:root,plex/' -e 's/^render:x:108:root$/ssl-cert:x:108:plex/' /etc/group +else + sed -i -e 's/^ssl-cert:x:104:plex$/render:x:104:plex/' -e 's/^render:x:108:$/ssl-cert:x:108:/' /etc/group +fi +msg_ok "Installed Plex Media Server" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From e2ec7c18e2d797c3aed5f18fd8dd691cfb0c2c44 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 2 Oct 2025 10:09:47 +0200 Subject: [PATCH 1342/1733] Migrate Plex repo to Deb822 and update install scripts Updated plex-install.sh and plex.sh to use the Deb822 repository format for Plex Media Server, replacing the old sources.list method. Also switched apt-get commands to apt for consistency and improved update/cleanup steps. --- ct/plex.sh | 41 +++++++++++++++++++++++------------------ install/plex-install.sh | 19 +++++++++++++------ 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/ct/plex.sh b/ct/plex.sh index def84c8e9..ef3fd9d6a 100644 --- a/ct/plex.sh +++ b/ct/plex.sh @@ -23,26 +23,31 @@ function update_script() { header_info check_container_storage check_container_resources - if [[ ! -f /etc/apt/sources.list.d/plexmediaserver.list ]]; then - msg_error "No ${APP} Installation Found!" - exit + + if [[ -f /etc/apt/sources.list.d/plexmediaserver.list ]]; then + msg_info "Migrating Plex repository to Deb822 format" + rm -f /etc/apt/sources.list.d/plexmediaserver.list + curl -fsSL https://downloads.plex.tv/plex-keys/PlexSign.key | tee /usr/share/keyrings/PlexSign.asc >/dev/null + cat </etc/apt/sources.list.d/plexmediaserver.sources +Types: deb +URIs: https://downloads.plex.tv/repo/deb/ +Suites: public +Components: main +Signed-By: /usr/share/keyrings/PlexSign.asc +EOF + msg_ok "Migrated Plex repository to Deb822" fi - UPD=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "SUPPORT" --radiolist --cancel-button Exit-Script "Spacebar = Select \nplexupdate info >> https://github.com/mrworf/plexupdate" 10 59 2 \ - "1" "Update LXC" ON \ - "2" "Install plexupdate" OFF \ - 3>&1 1>&2 2>&3) - if [ "$UPD" == "1" ]; then - msg_info "Updating ${APP} LXC" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "Updated ${APP} LXC" - exit - fi - if [ "$UPD" == "2" ]; then - set +e - bash -c "$(curl -fsSL https://raw.githubusercontent.com/mrworf/plexupdate/master/extras/installer.sh)" - exit + + if [[ ! -f /etc/apt/sources.list.d/plexmediaserver.sources ]]; then + msg_error "No ${APP} repository found!" + exit 1 fi + + msg_info "Updating ${APP}" + $STD apt update + $STD apt -y -o Dpkg::Options::="--force-confold" upgrade plexmediaserver + msg_ok "Updated ${APP}" + exit } start diff --git a/install/plex-install.sh b/install/plex-install.sh index 417697cbf..d04d20c7a 100644 --- a/install/plex-install.sh +++ b/install/plex-install.sh @@ -14,7 +14,7 @@ network_check update_os msg_info "Setting Up Hardware Acceleration" -$STD apt-get -y install {va-driver-all,ocl-icd-libopencl1,intel-opencl-icd,vainfo,intel-gpu-tools} +$STD apt -y install va-driver-all ocl-icd-libopencl1 intel-opencl-icd vainfo intel-gpu-tools if [[ "$CTTYPE" == "0" ]]; then chgrp video /dev/dri chmod 755 /dev/dri @@ -26,12 +26,18 @@ msg_ok "Set Up Hardware Acceleration" msg_info "Setting Up Plex Media Server Repository" curl -fsSL https://downloads.plex.tv/plex-keys/PlexSign.key | tee /usr/share/keyrings/PlexSign.asc >/dev/null -echo "deb [signed-by=/usr/share/keyrings/PlexSign.asc] https://downloads.plex.tv/repo/deb/ public main" >/etc/apt/sources.list.d/plexmediaserver.list +cat </etc/apt/sources.list.d/plexmediaserver.sources +Types: deb +URIs: https://downloads.plex.tv/repo/deb/ +Suites: public +Components: main +Signed-By: /usr/share/keyrings/PlexSign.asc +EOF msg_ok "Set Up Plex Media Server Repository" msg_info "Installing Plex Media Server" -$STD apt-get update -$STD apt-get -o Dpkg::Options::="--force-confold" install -y plexmediaserver +$STD apt update +$STD apt -y -o Dpkg::Options::="--force-confold" install plexmediaserver if [[ "$CTTYPE" == "0" ]]; then sed -i -e 's/^ssl-cert:x:104:plex$/render:x:104:root,plex/' -e 's/^render:x:108:root$/ssl-cert:x:108:plex/' /etc/group else @@ -43,6 +49,7 @@ motd_ssh customize msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean +$STD apt -y autoremove +$STD apt -y autoclean +$STD apt -y clean msg_ok "Cleaned" From ef616ac29b37b6ffee9b8abb864f9154402d0442 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 2 Oct 2025 10:28:51 +0200 Subject: [PATCH 1343/1733] Update post-pve-install.sh --- tools/pve/post-pve-install.sh | 74 +++++++++++++++++++++++++++++------ 1 file changed, 62 insertions(+), 12 deletions(-) diff --git a/tools/pve/post-pve-install.sh b/tools/pve/post-pve-install.sh index c783d2fc3..4764c0446 100644 --- a/tools/pve/post-pve-install.sh +++ b/tools/pve/post-pve-install.sh @@ -370,7 +370,6 @@ EOF REPO_FILE="" REPO_ACTIVE=0 REPO_COMMENTED=0 - # Suche nach existierendem Block (aktiv oder auskommentiert) for file in /etc/apt/sources.list.d/*.sources; do if grep -q "Components:.*pve-no-subscription" "$file"; then REPO_FILE="$file" @@ -481,24 +480,24 @@ EOF fi # ---- PVETEST ---- - if component_exists_in_sources "pvetest"; then - msg_ok "'pvetest' repository already exists (skipped)" + if component_exists_in_sources "pve-test"; then + msg_ok "'pve-test' repository already exists (skipped)" else CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "PVETEST" \ - --menu "The 'pvetest' repository can give advanced users access to new features and updates before they are officially released.\n\nAdd (Disabled) 'pvetest' repository (deb822)?" 14 58 2 \ + --menu "The 'pve-test' repository can give advanced users access to new features and updates before they are officially released.\n\nAdd (Disabled) 'pvetest' repository (deb822)?" 14 58 2 \ "yes" " " \ "no" " " 3>&2 2>&1 1>&3) case $CHOICE in yes) - msg_info "Adding 'pvetest' repository (deb822, disabled)" - cat >/etc/apt/sources.list.d/pvetest.sources </etc/apt/sources.list.d/pve-test.sources <.*<\/script>/d' /usr/share/pve-yew-mobile-gui/index.html.tpl && sed -i '/<\/head>/i ' /usr/share/pve-yew-mobile-gui/index.html.tpl + # Create external script, this is needed because DPkg::Post-Invoke is fidly with quote interpretation + mkdir -p /usr/local/bin + cat >/usr/local/bin/pve-remove-nag.sh <<'EOF' +#!/bin/sh +WEB_JS=/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js +if [ -s "$WEB_JS" ] && ! grep -q NoMoreNagging "$WEB_JS"; then + echo "Patching Web UI nag..." + sed -i -e "/data\.status/ s/!//" -e "/data\.status/ s/active/NoMoreNagging/" "$WEB_JS" +fi + +MOBILE_TPL=/usr/share/pve-yew-mobile-gui/index.html.tpl +MARKER="" +if [ -f "$MOBILE_TPL" ] && ! grep -q "$MARKER" "$MOBILE_TPL"; then + echo "Patching Mobile UI nag..." + printf "%s\n" \ + "$MARKER" \ + "" \ + "" >> "$MOBILE_TPL" +fi EOF -) - echo "DPkg::Post-Invoke { \"echo $encoded_script | base64 -d | bash\"; };" > /etc/apt/apt.conf.d/no-nag-script + chmod 755 /usr/local/bin/pve-remove-nag.sh + + cat >/etc/apt/apt.conf.d/no-nag-script <<'EOF' +DPkg::Post-Invoke { "/usr/local/bin/pve-remove-nag.sh"; }; +EOF + chmod 644 /etc/apt/apt.conf.d/no-nag-script + msg_ok "Disabled subscription nag (Delete browser cache)" ;; no) From 3826cb654040171502fb68b39a3b00100c6f6c1b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 2 Oct 2025 10:29:48 +0200 Subject: [PATCH 1344/1733] Update post-pve-install.sh --- tools/pve/post-pve-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/pve/post-pve-install.sh b/tools/pve/post-pve-install.sh index 4764c0446..5a7f1a278 100644 --- a/tools/pve/post-pve-install.sh +++ b/tools/pve/post-pve-install.sh @@ -189,7 +189,7 @@ start_routines_9() { # check if deb822 Sources (*.sources) exist if find /etc/apt/sources.list.d/ -maxdepth 1 -name '*.sources' | grep -q .; then whiptail --backtitle "Proxmox VE Helper Scripts" --title "Deb822 sources detected" \ - --msgbox "Modern deb822 sources (*.sources) already exist.\n\nNo changes to sources format required.\n\nYou may still have legacy sources.list or .list files, which you can disable in the next step." 12 65 + --msgbox "Modern deb822 sources (*.sources) already exist.\n\nNo changes to sources format required.\n\nYou may still have legacy sources.list or .list files, which you can disable in the next step." 12 65 || true else check_and_disable_legacy_sources() { local LEGACY_COUNT=0 From 062e9909b44a8d38f8f827957746920c3667ada8 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 2 Oct 2025 08:52:36 +0000 Subject: [PATCH 1345/1733] Update .app files --- ct/headers/plex | 6 ++++++ ct/headers/sonarqube | 6 ++++++ 2 files changed, 12 insertions(+) create mode 100644 ct/headers/plex create mode 100644 ct/headers/sonarqube diff --git a/ct/headers/plex b/ct/headers/plex new file mode 100644 index 000000000..2e27d0b8e --- /dev/null +++ b/ct/headers/plex @@ -0,0 +1,6 @@ + ____ __ + / __ \/ /__ _ __ + / /_/ / / _ \| |/_/ + / ____/ / __/> < +/_/ /_/\___/_/|_| + diff --git a/ct/headers/sonarqube b/ct/headers/sonarqube new file mode 100644 index 000000000..7e96bc0df --- /dev/null +++ b/ct/headers/sonarqube @@ -0,0 +1,6 @@ + _____ ____ __ + / ___/____ ____ ____ ______/ __ \__ __/ /_ ___ + \__ \/ __ \/ __ \/ __ `/ ___/ / / / / / / __ \/ _ \ + ___/ / /_/ / / / / /_/ / / / /_/ / /_/ / /_/ / __/ +/____/\____/_/ /_/\__,_/_/ \___\_\__,_/_.___/\___/ + From b2f70696720f4a0979a32466254c6fa6f48ab67e Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 2 Oct 2025 13:52:34 +0200 Subject: [PATCH 1346/1733] Revert sonarqube to old install method --- ct/sonarqube.sh | 73 +++++++++++++++++++----------------- install/sonarqube-install.sh | 9 ++++- 2 files changed, 46 insertions(+), 36 deletions(-) diff --git a/ct/sonarqube.sh b/ct/sonarqube.sh index 4c79daab8..c8338e4a5 100644 --- a/ct/sonarqube.sh +++ b/ct/sonarqube.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: prop4n # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -20,40 +20,45 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/sonarqube ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if check_for_gh_release "sonarqube" "SonarSource/sonarqube"; then - msg_info "Stopping service" - systemctl stop sonarqube - msg_ok "Service stopped" - - msg_info "Creating backup" - BACKUP_DIR="/opt/sonarqube-backup" - mv /opt/sonarqube ${BACKUP_DIR} - msg_ok "Backup created" - - fetch_and_deploy_gh_release "sonarqube" "SonarSource/sonarqube" "tarball" - - msg_info "Restoring backup" - cp -rp ${BACKUP_DIR}/data/ /opt/sonarqube/data/ - cp -rp ${BACKUP_DIR}/extensions/ /opt/sonarqube/extensions/ - cp -p ${BACKUP_DIR}/conf/sonar.properties /opt/sonarqube/conf/sonar.properties - rm -rf ${BACKUP_DIR} - chown -R sonarqube:sonarqube /opt/sonarqube - msg_ok "Backup restored" - - msg_info "Starting service" - systemctl start sonarqube - msg_ok "Service started" - msg_ok "Updated Successfully" - fi + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/sonarqube ]]; then + msg_error "No ${APP} Installation Found!" exit + fi + + if check_for_gh_release "sonarqube" "SonarSource/sonarqube"; then + msg_info "Stopping service" + systemctl stop sonarqube + msg_ok "Service stopped" + + msg_info "Creating backup" + BACKUP_DIR="/opt/sonarqube-backup" + mv /opt/sonarqube ${BACKUP_DIR} + msg_ok "Backup created" + + msg_info "Installing sonarqube" + RELEASE=$(curl -fsSL https://api.github.com/repos/SonarSource/sonarqube/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + curl -fsSL "https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-${RELEASE}.zip" -o $temp_file + unzip -q "$temp_file" -d /opt + mv /opt/sonarqube-* /opt/sonarqube + msg_ok "Installed sonarqube" + + msg_info "Restoring backup" + cp -rp ${BACKUP_DIR}/data/ /opt/sonarqube/data/ + cp -rp ${BACKUP_DIR}/extensions/ /opt/sonarqube/extensions/ + cp -p ${BACKUP_DIR}/conf/sonar.properties /opt/sonarqube/conf/sonar.properties + rm -rf ${BACKUP_DIR} + chown -R sonarqube:sonarqube /opt/sonarqube + msg_ok "Backup restored" + + msg_info "Starting service" + systemctl start sonarqube + msg_ok "Service started" + msg_ok "Updated Successfully" + fi + exit } start diff --git a/install/sonarqube-install.sh b/install/sonarqube-install.sh index cac2de195..57211e967 100644 --- a/install/sonarqube-install.sh +++ b/install/sonarqube-install.sh @@ -14,7 +14,6 @@ update_os JAVA_VERSION="21" setup_java PG_VERSION="17" setup_postgresql -fetch_and_deploy_gh_release "sonarqube" "SonarSource/sonarqube" "tarball" msg_info "Installing Postgresql" DB_NAME="sonarqube" @@ -31,11 +30,16 @@ $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME OWNER $DB_USER;" msg_ok "Installed PostgreSQL" msg_info "Configuring SonarQube" +temp_file=$(mktemp) +RELEASE=$(curl -fsSL https://api.github.com/repos/SonarSource/sonarqube/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') +curl -fsSL "https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-${RELEASE}.zip" -o $temp_file +unzip -q "$temp_file" -d /opt +mv /opt/sonarqube-* /opt/sonarqube $STD useradd -r -m -U -d /opt/sonarqube -s /bin/bash sonarqube chown -R sonarqube:sonarqube /opt/sonarqube chmod -R 755 /opt/sonarqube mkdir -p /opt/sonarqube/conf -cat </opt/sonarqube/conf/sonar.properties +cat </opt/sonarqube/conf/sonar.properties sonar.jdbc.username=${DB_USER} sonar.jdbc.password=${DB_PASS} sonar.jdbc.url=jdbc:postgresql://localhost/${DB_NAME} @@ -43,6 +47,7 @@ sonar.web.host=0.0.0.0 sonar.web.port=9000 EOF chmod +x /opt/sonarqube/bin/linux-x86-64/sonar.sh +echo ${RELEASE} >>~/.sonarqube msg_ok "Configured SonarQube" msg_info "Creating Service" From d1113eb90641559b82c5775641ed74a498ec1568 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 2 Oct 2025 18:24:00 +0200 Subject: [PATCH 1347/1733] Create gitea-mirror-install.sh --- install/gitea-mirror-install.sh | 77 +++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 install/gitea-mirror-install.sh diff --git a/install/gitea-mirror-install.sh b/install/gitea-mirror-install.sh new file mode 100644 index 000000000..7f15bd7af --- /dev/null +++ b/install/gitea-mirror-install.sh @@ -0,0 +1,77 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: CrazyWolf13 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/RayLabsHQ/gitea-mirror + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing dependencies" +$STD apt-get install -y \ + build-essential \ + openssl \ + sqlite3 \ + unzip +msg_ok "Installed Dependencies" + +msg_info "Installing Bun" +export BUN_INSTALL=/opt/bun +curl -fsSL https://bun.sh/install | $STD bash +ln -sf /opt/bun/bin/bun /usr/local/bin/bun +ln -sf /opt/bun/bin/bun /usr/local/bin/bunx +msg_ok "Installed Bun" + +fetch_and_deploy_gh_release "gitea-mirror" "RayLabsHQ/gitea-mirror" + +msg_info "Installing gitea-mirror" +cd /opt/gitea-mirror +$STD bun run setup +$STD bun run build +msg_ok "Installed gitea-mirror" + +msg_info "Creating Services" +APP_SECRET=$(openssl rand -base64 32) +APP_VERSION=$(grep -o '"version": *"[^"]*"' package.json | cut -d'"' -f4) +HOST_IP=$(hostname -I | awk '{print $1}') +cat </opt/gitea-mirror.env +# See here for config options: https://github.com/RayLabsHQ/gitea-mirror/blob/main/docs/ENVIRONMENT_VARIABLES.md +NODE_ENV=production +HOST=0.0.0.0 +PORT=4321 +DATABASE_URL=sqlite://data/gitea-mirror.db +BETTER_AUTH_URL=http://${HOST_IP}:4321 +BETTER_AUTH_SECRET=${APP_SECRET} +npm_package_version=${APP_VERSION} +EOF + +cat </etc/systemd/system/gitea-mirror.service +[Unit] +Description=Gitea Mirror +After=network.target +[Service] +Type=simple +WorkingDirectory=/opt/gitea-mirror +ExecStart=/usr/local/bin/bun dist/server/entry.mjs +Restart=on-failure +RestartSec=10 +EnvironmentFile=/opt/gitea-mirror.env +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now gitea-mirror +msg_ok "Created Service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From af424430d727b43c5c704baf542823b1ebcfa641 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 2 Oct 2025 18:28:01 +0200 Subject: [PATCH 1348/1733] Update gitea-mirror fetch command to include version Specify version '3.8.1' for gitea-mirror tarball download. --- ct/gitea-mirror.sh | 130 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 ct/gitea-mirror.sh diff --git a/ct/gitea-mirror.sh b/ct/gitea-mirror.sh new file mode 100644 index 000000000..98226ee5e --- /dev/null +++ b/ct/gitea-mirror.sh @@ -0,0 +1,130 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: CrazyWolf13 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/RayLabsHQ/gitea-mirror + +APP="gitea-mirror" +var_tags="${var_tags:-mirror;gitea}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-6}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" + +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/gitea-mirror ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + APP_VERSION=$(grep -o '"version": *"[^"]*"' /opt/gitea-mirror/package.json | cut -d'"' -f4) + if [[ $APP_VERSION =~ ^2\. ]]; then + if ! whiptail --backtitle "Gitea Mirror Update" --title "⚠️ VERSION 2.x DETECTED" --yesno \ + "WARNING: Version $APP_VERSION detected!\n\nUpdating from version 2.x will CLEAR ALL CONFIGURATION.\n\nThis includes:\n• API tokens\n• User settings\n• Repository configurations\n• All custom settings\n\nDo you want to continue with the update process?" 15 70 --defaultno; then + exit 0 + fi + + if ! whiptail --backtitle "Gitea Mirror Update" --title "⚠️ FINAL CONFIRMATION" --yesno \ + "FINAL WARNING: This update WILL clear all configuration!\n\nBEFORE PROCEEDING, please:\n\n• Copy API tokens to a safe location\n• Backup any custom configurations\n• Note down repository settings\n\nThis action CANNOT be undone!" 18 70 --defaultno; then + whiptail --backtitle "Gitea Mirror Update" --title "Update Cancelled" --msgbox "Update process cancelled. Please backup your configuration before proceeding." 8 60 + exit 0 + fi + whiptail --backtitle "Gitea Mirror Update" --title "Proceeding with Update" --msgbox \ + "Proceeding with version $APP_VERSION update.\n\nAll configuration will be cleared as warned." 8 50 + rm -rf /opt/gitea-mirror + fi + + if [[ ! -f /opt/gitea-mirror.env ]]; then + msg_info "Detected old Enviroment, updating files" + APP_SECRET=$(openssl rand -base64 32) + HOST_IP=$(hostname -I | awk '{print $1}') + cat </opt/gitea-mirror.env +# See here for config options: https://github.com/RayLabsHQ/gitea-mirror/blob/main/docs/ENVIRONMENT_VARIABLES.md +NODE_ENV=production +HOST=0.0.0.0 +PORT=4321 +DATABASE_URL=sqlite://data/gitea-mirror.db +BETTER_AUTH_URL=http://${HOST_IP}:4321 +BETTER_AUTH_SECRET=${APP_SECRET} +npm_package_version=${APP_VERSION} +EOF + rm /etc/systemd/system/gitea-mirror.service + cat </etc/systemd/system/gitea-mirror.service +[Unit] +Description=Gitea Mirror +After=network.target +[Service] +Type=simple +WorkingDirectory=/opt/gitea-mirror +ExecStart=/usr/local/bin/bun dist/server/entry.mjs +Restart=on-failure +RestartSec=10 +EnvironmentFile=/opt/gitea-mirror.env +[Install] +WantedBy=multi-user.target +EOF + systemctl daemon-reload + msg_ok "Old Enviroment fixed" +fi + + if check_for_gh_release "gitea-mirror" "RayLabsHQ/gitea-mirror"; then + msg_info "Stopping Services" + systemctl stop gitea-mirror + msg_ok "Services Stopped" + + msg_info "Backup Data" + mkdir -p /opt/gitea-mirror-backup/data + cp /opt/gitea-mirror/data/* /opt/gitea-mirror-backup/data/ + msg_ok "Backup Data" + + msg_info "Installing Bun" + export BUN_INSTALL=/opt/bun + curl -fsSL https://bun.sh/install | $STD bash + ln -sf /opt/bun/bin/bun /usr/local/bin/bun + ln -sf /opt/bun/bin/bun /usr/local/bin/bunx + msg_ok "Installed Bun" + + rm -rf /opt/gitea-mirror + fetch_and_deploy_gh_release "gitea-mirror" "RayLabsHQ/gitea-mirror" "tarball" "3.8.1" + + msg_info "Updating and rebuilding ${APP}" + cd /opt/gitea-mirror + $STD bun run setup + $STD bun run build + APP_VERSION=$(grep -o '"version": *"[^"]*"' package.json | cut -d'"' -f4) + + sudo sed -i.bak "s|^npm_package_version=.*|npm_package_version=${APP_VERSION}|" /opt/gitea-mirror.env + msg_ok "Updated and rebuilt ${APP}" + + msg_info "Restoring Data" + cp /opt/gitea-mirror-backup/data/* /opt/gitea-mirror/data + msg_ok "Restored Data" + + msg_info "Starting Service" + systemctl start gitea-mirror + msg_ok "Service Started" + msg_ok "Update Successfully" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:4321${CL}" From bf4b65916dd7b5a7d7550c9202e15eecbf1d6fdd Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 2 Oct 2025 18:28:22 +0200 Subject: [PATCH 1349/1733] Delete ct/ntfy.sh --- ct/ntfy.sh | 62 ------------------------------------------------------ 1 file changed, 62 deletions(-) delete mode 100644 ct/ntfy.sh diff --git a/ct/ntfy.sh b/ct/ntfy.sh deleted file mode 100644 index 74f1f702b..000000000 --- a/ct/ntfy.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) | Co-Author: CrazyWolf13 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://ntfy.sh/ - -APP="ntfy" -var_tags="${var_tags:-notification}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-512}" -var_disk="${var_disk:-2}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /var ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if [ -f /etc/apt/keyrings/archive.heckel.io.gpg ]; then - msg_info "Correcting old Ntfy Repository" - rm -f /etc/apt/keyrings/archive.heckel.io.gpg - rm -f /etc/apt/sources.list.d/archive.heckel.io.list - rm -f /etc/apt/sources.list.d/archive.heckel.io.list.bak - rm -f /etc/apt/sources.list.d/archive.heckel.io.sources - sudo curl -fsSL -o /etc/apt/keyrings/ntfy.gpg https://archive.ntfy.sh/apt/keyring.gpg - cat <<'EOF' >/etc/apt/sources.list.d/ntfy.sources -Types: deb -URIs: https://archive.ntfy.sh/apt/ -Suites: stable -Components: main -Signed-By: /etc/apt/keyrings/ntfy.gpg -EOF - msg_ok "Corrected old Ntfy Repository" - fi - - msg_info "Updating $APP LXC" - $STD apt update - $STD apt -y upgrade - msg_ok "Updated $APP LXC" - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" From 4cf8c4c5cff62c03b7771c8e92e96de60180da1b Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 2 Oct 2025 18:28:38 +0200 Subject: [PATCH 1350/1733] Delete ct/headers/ntfy --- ct/headers/ntfy | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 ct/headers/ntfy diff --git a/ct/headers/ntfy b/ct/headers/ntfy deleted file mode 100644 index e2e078da5..000000000 --- a/ct/headers/ntfy +++ /dev/null @@ -1,6 +0,0 @@ - __ ____ - ____ / /_/ __/_ __ - / __ \/ __/ /_/ / / / - / / / / /_/ __/ /_/ / -/_/ /_/\__/_/ \__, / - /____/ From fd24c835406f0b898deb52eceb364c9186f3e6de Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 2 Oct 2025 18:29:07 +0200 Subject: [PATCH 1351/1733] Delete install/ntfy-install.sh --- install/ntfy-install.sh | 39 --------------------------------------- 1 file changed, 39 deletions(-) delete mode 100644 install/ntfy-install.sh diff --git a/install/ntfy-install.sh b/install/ntfy-install.sh deleted file mode 100644 index fb7547c3e..000000000 --- a/install/ntfy-install.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) | Co-Author: CrazyWolf13 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://ntfy.sh/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing ntfy" -mkdir -p /etc/apt/keyrings -sudo curl -fsSL -o /etc/apt/keyrings/ntfy.gpg https://archive.ntfy.sh/apt/keyring.gpg - -cat <<'EOF' >/etc/apt/sources.list.d/ntfy.sources -Types: deb -URIs: https://archive.ntfy.sh/apt/ -Suites: stable -Components: main -Signed-By: /etc/apt/keyrings/ntfy.gpg -EOF - -$STD apt update -$STD apt install -y ntfy -systemctl enable -q --now ntfy -msg_ok "Installed ntfy" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt -y autoremove -$STD apt -y autoclean -msg_ok "Cleaned" From 5f3f63093044829746c510ef8f789eaa836f5bcc Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Thu, 2 Oct 2025 18:31:44 +0200 Subject: [PATCH 1352/1733] Fix version prefix in gitea-mirror fetch command --- ct/gitea-mirror.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/gitea-mirror.sh b/ct/gitea-mirror.sh index 98226ee5e..bfe93ff89 100644 --- a/ct/gitea-mirror.sh +++ b/ct/gitea-mirror.sh @@ -97,7 +97,7 @@ fi msg_ok "Installed Bun" rm -rf /opt/gitea-mirror - fetch_and_deploy_gh_release "gitea-mirror" "RayLabsHQ/gitea-mirror" "tarball" "3.8.1" + fetch_and_deploy_gh_release "gitea-mirror" "RayLabsHQ/gitea-mirror" "tarball" "v3.8.1" msg_info "Updating and rebuilding ${APP}" cd /opt/gitea-mirror From 60eb0d9dc925f3625a916d2c24e985ede61c43b2 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 2 Oct 2025 16:32:13 +0000 Subject: [PATCH 1353/1733] Update .app files --- ct/headers/gitea-mirror | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/gitea-mirror diff --git a/ct/headers/gitea-mirror b/ct/headers/gitea-mirror new file mode 100644 index 000000000..57003b058 --- /dev/null +++ b/ct/headers/gitea-mirror @@ -0,0 +1,6 @@ + _ __ _ + ____ _(_) /____ ____ _ ____ ___ (_)_____________ _____ + / __ `/ / __/ _ \/ __ `/_____/ __ `__ \/ / ___/ ___/ __ \/ ___/ + / /_/ / / /_/ __/ /_/ /_____/ / / / / / / / / / / /_/ / / + \__, /_/\__/\___/\__,_/ /_/ /_/ /_/_/_/ /_/ \____/_/ +/____/ From d344dd18d2735e5ff8bd382a78aa83a3e85e0578 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 3 Oct 2025 09:31:19 +0200 Subject: [PATCH 1354/1733] try --- tools/pve/post-pve-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/pve/post-pve-install.sh b/tools/pve/post-pve-install.sh index 5a7f1a278..ba32060cc 100644 --- a/tools/pve/post-pve-install.sh +++ b/tools/pve/post-pve-install.sh @@ -197,7 +197,7 @@ start_routines_9() { # Check sources.list if [[ -f "$listfile" ]] && grep -qE '^\s*deb ' "$listfile"; then - ((LEGACY_COUNT++)) + ((++ LEGACY_COUNT )) fi # Check .list files From 62aa06b34335164b20f30c3d98a8d540a3c6e1f1 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 3 Oct 2025 09:32:41 +0200 Subject: [PATCH 1355/1733] typo --- tools/pve/post-pve-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/pve/post-pve-install.sh b/tools/pve/post-pve-install.sh index ba32060cc..42df5d77a 100644 --- a/tools/pve/post-pve-install.sh +++ b/tools/pve/post-pve-install.sh @@ -197,7 +197,7 @@ start_routines_9() { # Check sources.list if [[ -f "$listfile" ]] && grep -qE '^\s*deb ' "$listfile"; then - ((++ LEGACY_COUNT )) + (( ++LEGACY_COUNT )) fi # Check .list files From 44eaa66d7433bf3c27ac008eefd50046a38a402e Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner <73236783+michelroegl-brunner@users.noreply.github.com> Date: Fri, 3 Oct 2025 10:46:19 +0200 Subject: [PATCH 1356/1733] Add petio.sh --- ct/petio.sh | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 ct/petio.sh diff --git a/ct/petio.sh b/ct/petio.sh new file mode 100644 index 000000000..22c8e93a5 --- /dev/null +++ b/ct/petio.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://petio.tv/ + +APP="Petio" +var_tags="${var_tags:-media}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-1024}" +var_disk="${var_disk:-4}" +var_os="${var_os:-ubuntu}" +var_version="${var_version:-20.04}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/Petio ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating $APP" + systemctl stop petio.service + curl -fsSL https://petio.tv/releases/latest -o petio-latest.zip + $STD unzip petio-latest.zip -d /opt/Petio + systemctl start petio.service + msg_ok "Updated $APP" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:7777${CL}" From 1bad290446d895f0298271c60788c96f5ea835b9 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner <73236783+michelroegl-brunner@users.noreply.github.com> Date: Fri, 3 Oct 2025 10:47:01 +0200 Subject: [PATCH 1357/1733] add petio-install-sh This script installs MongoDB 4.4 and Petio, sets up a systemd service for Petio, and performs cleanup operations. --- install/petio-install.sh | 69 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 install/petio-install.sh diff --git a/install/petio-install.sh b/install/petio-install.sh new file mode 100644 index 000000000..ff4337c71 --- /dev/null +++ b/install/petio-install.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://petio.tv/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing MongoDB 4.4" +curl -fsSL "https://www.mongodb.org/static/pgp/server-4.4.asc" | gpg --dearmor >/usr/share/keyrings/mongodb-server-4.4.gpg +# Determine OS ID +OS_ID=$(grep '^ID=' /etc/os-release | cut -d'=' -f2) + +if [ "$OS_ID" = "debian" ]; then + echo "deb [ signed-by=/usr/share/keyrings/mongodb-server-4.4.gpg ] http://repo.mongodb.org/apt/debian $(grep '^VERSION_CODENAME=' /etc/os-release | cut -d'=' -f2)/mongodb-org/4.4 main" >/etc/apt/sources.list.d/mongodb-org-4.4.list +else + echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-4.4.gpg ] https://repo.mongodb.org/apt/ubuntu $(grep '^VERSION_CODENAME=' /etc/os-release | cut -d'=' -f2)/mongodb-org/4.4 multiverse" >/etc/apt/sources.list.d/mongodb-org-4.4.list +fi + +$STD apt-get update +$STD apt-get install -y mongodb-org +sed -i 's/bindIp: 127.0.0.1/bindIp: 0.0.0.0/' /etc/mongod.conf +systemctl enable -q --now mongod +msg_ok "MongoDB 4.4 Installed" + +msg_info "Installing Petio" +useradd -M --shell=/bin/false petio +mkdir /opt/Petio +curl -fsSL "https://petio.tv/releases/latest" -o "petio-latest.zip" +$STD unzip petio-latest.zip -d /opt/Petio +rm -rf petio-latest.zip +chown -R petio:petio /opt/Petio +msg_ok "Installed Petio" + +msg_info "Creating Service" +cat </etc/systemd/system/petio.service +[Unit] +Description=Petio a content request system +After=network.target mongod.service + +[Service] +Type=simple +User=petio +Restart=on-failure +RestartSec=1 +ExecStart=/opt/Petio/bin/petio-linux + +[Install] +WantedBy=multi-user.target + + +EOF +systemctl enable -q --now petio +msg_ok "Created Service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 9eb434247277f3421a25f9591fd28e2beab23fd0 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 3 Oct 2025 10:48:30 +0200 Subject: [PATCH 1358/1733] Change petio to 24.04 --- ct/petio.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/petio.sh b/ct/petio.sh index 22c8e93a5..f9c4d1370 100644 --- a/ct/petio.sh +++ b/ct/petio.sh @@ -11,7 +11,7 @@ var_cpu="${var_cpu:-2}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-4}" var_os="${var_os:-ubuntu}" -var_version="${var_version:-20.04}" +var_version="${var_version:-24.04}" header_info "$APP" variables From 1c056eeb17918e0aa0755b77a481719795debc67 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 3 Oct 2025 08:48:55 +0000 Subject: [PATCH 1359/1733] Update .app files --- ct/headers/petio | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/petio diff --git a/ct/headers/petio b/ct/headers/petio new file mode 100644 index 000000000..d8deb745a --- /dev/null +++ b/ct/headers/petio @@ -0,0 +1,6 @@ + ____ __ _ + / __ \___ / /_(_)___ + / /_/ / _ \/ __/ / __ \ + / ____/ __/ /_/ / /_/ / +/_/ \___/\__/_/\____/ + From b66a0570651b94af8ff8eb1e9781bee33587c8c9 Mon Sep 17 00:00:00 2001 From: Marfnl Date: Fri, 3 Oct 2025 11:02:21 +0200 Subject: [PATCH 1360/1733] Add Prometheus-Blackbox-Exporter script (#956) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * prometheus-blackbox-exporter container maker for prometheus-blackbox-exporter * Update prometheus-blackbox-exporter.sh builder source Modifyed the builder to: https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main * prometheus-blackbox-exporter header prometheus-blackbox-exporter header artwork * add prometheus-blackbox-exporter-install adding the prometheus blackbox exporter installer * Adding prometheus-blackbox-exporter.json adding the front end page for prometheus blackbox exporter * Update ct/prometheus-blackbox-exporter.sh Simplify the code using apt update. Co-authored-by: Michel Roegl-Brunner <73236783+michelroegl-brunner@users.noreply.github.com> * Update install/prometheus-blackbox-exporter-install.sh Format the code. Co-authored-by: Michel Roegl-Brunner <73236783+michelroegl-brunner@users.noreply.github.com> * Update prometheus-blackbox-exporter.json Updated the logo resource to use selfh * Update prometheus-blackbox-exporter-install.sh Optimized the code: *double enable has been reduced *systemctl daemon-reload removed not needed. * Update prometheus-blackbox-exporter.sh Removed all comments * Update prometheus-blackbox-exporter-install.sh Removed all comments * Delete ct/headers/prometheus-blackbox-exporter.txt Removed the header file. * Update prometheus-blackbox-exporter-install.sh * Update prometheus-blackbox-exporter.json * Update prometheus-blackbox-exporter.sh * Update prometheus-blackbox-exporter.sh --------- Co-authored-by: Michel Roegl-Brunner <73236783+michelroegl-brunner@users.noreply.github.com> Co-authored-by: Slaviša Arežina <58952836+tremor021@users.noreply.github.com> --- ct/prometheus-blackbox-exporter.sh | 62 +++++++++++++++++++ .../json/prometheus-blackbox-exporter.json | 41 ++++++++++++ .../prometheus-blackbox-exporter-install.sh | 42 +++++++++++++ 3 files changed, 145 insertions(+) create mode 100644 ct/prometheus-blackbox-exporter.sh create mode 100644 frontend/public/json/prometheus-blackbox-exporter.json create mode 100644 install/prometheus-blackbox-exporter-install.sh diff --git a/ct/prometheus-blackbox-exporter.sh b/ct/prometheus-blackbox-exporter.sh new file mode 100644 index 000000000..44a62d494 --- /dev/null +++ b/ct/prometheus-blackbox-exporter.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Marfnl +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/prometheus/blackbox_exporter + +APP="Prometheus-Blackbox-Exporter" +var_tags="${var_tags:-monitoring;prometheus}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-512}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/blackbox-exporter ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "blackbox-exporter" "prometheus/blackbox_exporter"; then + msg_info "Stopping $APP" + systemctl stop blackbox-exporter + msg_ok "Stopped $APP" + + msg_info "Creating backup" + mv /opt/blackbox-exporter/blackbox.yml /opt + msg_ok "Backup created" + + fetch_and_deploy_gh_release "blackbox-exporter" "prometheus/blackbox_exporter" "prebuild" "latest" "/opt/blackbox-exporter" "blackbox_exporter-*.linux-amd64.tar.gz" + + msg_info "Restoring backup" + cp -r /opt/blackbox.yml /opt/blackbox-exporter + rm -f /opt/blackbox.yml + msg_ok "Backup restored" + + msg_info "Starting $APP" + systemctl start blackbox-exporter + msg_ok "Started $APP" + msg_ok "Update Successful" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9115${CL}" diff --git a/frontend/public/json/prometheus-blackbox-exporter.json b/frontend/public/json/prometheus-blackbox-exporter.json new file mode 100644 index 000000000..bc20843e5 --- /dev/null +++ b/frontend/public/json/prometheus-blackbox-exporter.json @@ -0,0 +1,41 @@ +{ + "name": "Prometheus Blackbox Exporter", + "slug": "prometheus-blackbox-exporter", + "categories": [ + 1, + 9 + ], + "date_created": "2025-09-29", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 9115, + "documentation": "https://github.com/prometheus/blackbox_exporter", + "website": "https://github.com/prometheus/blackbox_exporter", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/prometheus.webp", + "config_path": "/opt/blackbox-exporter/blackbox.yml", + "description": "An exporter allows blackbox probing of endpoints over HTTP, HTTPS, DNS, TCP, ICMP and gRPC for use by the Prometheus monitoring system.", + "install_methods": [ + { + "type": "default", + "script": "ct/prometheus-blackbox-exporter.sh", + "resources": { + "cpu": 1, + "ram": 512, + "hdd": 4, + "os": "debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Please adjust the Proxmox credentials in the configuration file!", + "type": "info" + } + ] +} diff --git a/install/prometheus-blackbox-exporter-install.sh b/install/prometheus-blackbox-exporter-install.sh new file mode 100644 index 000000000..be95d9645 --- /dev/null +++ b/install/prometheus-blackbox-exporter-install.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Marfnl +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/prometheus/blackbox_exporter + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +fetch_and_deploy_gh_release "blackbox-exporter" "prometheus/blackbox_exporter" "prebuild" "latest" "/opt/blackbox-exporter" "blackbox_exporter-*.linux-amd64.tar.gz" + +msg_info "Creating Service" +cat </etc/systemd/system/blackbox-exporter.service +[Unit] +Description=Blackbox Exporter Service +After=network.target + +[Service] +Type=simple +WorkingDirectory=/opt/blackbox-exporter +ExecStart=/opt/blackbox-exporter/blackbox_exporter +Restart=on-failure + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now blackbox-exporter +msg_ok "Service Created" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 94ad21ca8faf7fe9a5311fe7759f87f333c4eecb Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner <73236783+michelroegl-brunner@users.noreply.github.com> Date: Fri, 3 Oct 2025 11:04:22 +0200 Subject: [PATCH 1361/1733] Delete frontend/public/json/ntfy.json --- frontend/public/json/ntfy.json | 47 ---------------------------------- 1 file changed, 47 deletions(-) delete mode 100644 frontend/public/json/ntfy.json diff --git a/frontend/public/json/ntfy.json b/frontend/public/json/ntfy.json deleted file mode 100644 index 0bc32bbcc..000000000 --- a/frontend/public/json/ntfy.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "name": "ntfy", - "slug": "ntfy", - "categories": [ - 19 - ], - "date_created": "2024-05-02", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 80, - "documentation": "https://docs.ntfy.sh/", - "website": "https://ntfy.sh/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/ntfy.webp", - "config_path": "/etc/ntfy/server.yml", - "description": "ntfy (pronounced notify) is a simple HTTP-based pub-sub notification service. It allows you to send notifications to your phone or desktop via scripts from any computer, and/or using a REST API. It's infinitely flexible, and 100% free software.", - "install_methods": [ - { - "type": "default", - "script": "ct/ntfy.sh", - "resources": { - "cpu": 1, - "ram": 512, - "hdd": 2, - "os": "debian", - "version": "12" - } - }, - { - "type": "alpine", - "script": "ct/alpine-ntfy.sh", - "resources": { - "cpu": 1, - "ram": 256, - "hdd": 2, - "os": "alpine", - "version": "3.22" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] -} - From b21b567831f6ee2e5311b41f46b1ef2431bccad7 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 3 Oct 2025 12:50:28 +0200 Subject: [PATCH 1362/1733] Add pve scripts local --- ct/pve-scripts-lcoal.sh | 39 +++++++++++++ install/pve-scripts-local-install.sh | 82 ++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 ct/pve-scripts-lcoal.sh create mode 100644 install/pve-scripts-local-install.sh diff --git a/ct/pve-scripts-lcoal.sh b/ct/pve-scripts-lcoal.sh new file mode 100644 index 000000000..f25263a8f --- /dev/null +++ b/ct/pve-scripts-lcoal.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: michelroegl-brunner +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://www.debian.org/ + +APP="Debian" +var_tags="${var_tags:-os}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-4096}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /var ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "No Update function implementd" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" diff --git a/install/pve-scripts-local-install.sh b/install/pve-scripts-local-install.sh new file mode 100644 index 000000000..83f12f2cf --- /dev/null +++ b/install/pve-scripts-local-install.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: michelroegl-brunner +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE + + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" + $STD apt-get update + $STD apt-get install -y \ + build-essential \ + git \ + sshpass \ + expect +msg_ok "Dependencies installed." + +NODE_VERSION=24 setup_nodejs + +INSTALL_DIR=${INSTALL_DIR:-/opt/PVESciptslocal} + +if [ ! -d "$INSTALL_DIR/.git" ]; then + msg_info "Cloning repository into $INSTALL_DIR..." + $STD git clone https://github.com/michelroegl-brunner/PVESciptslocal.git "$INSTALL_DIR" + msg_ok "Repository cloned." +else + msg_info "Directory already exists. Pulling latest changes..." + $STD git -C "$INSTALL_DIR" pull + msg_ok "Repository updated." +fi + +cd "$INSTALL_DIR" || exit + +msg_info "Installing PVE Scripts local" +$STD npm install +cp .env.example .env +mkdir -p data +chmod 755 data +$STD npm run build +msg_ok "Installed PVE Scripts local" + +SERVICE_NAME="pvescriptslocal" +SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service" + +msg_info "Creating Service" +cat > "$SERVICE_FILE" < Date: Fri, 3 Oct 2025 12:54:55 +0200 Subject: [PATCH 1363/1733] Add pve scripts local --- ct/{pve-scripts-lcoal.sh => pve-scripts-local.sh} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ct/{pve-scripts-lcoal.sh => pve-scripts-local.sh} (100%) diff --git a/ct/pve-scripts-lcoal.sh b/ct/pve-scripts-local.sh similarity index 100% rename from ct/pve-scripts-lcoal.sh rename to ct/pve-scripts-local.sh From b2f25f1f297d9de78620579d700fcab09ba6d117 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 3 Oct 2025 13:01:24 +0200 Subject: [PATCH 1364/1733] Add pve scripts local --- ct/pve-scripts-local.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ct/pve-scripts-local.sh b/ct/pve-scripts-local.sh index f25263a8f..de63eb62a 100644 --- a/ct/pve-scripts-local.sh +++ b/ct/pve-scripts-local.sh @@ -5,7 +5,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://www.debian.org/ -APP="Debian" +APP="PVE-Scripts-Local" var_tags="${var_tags:-os}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" @@ -37,3 +37,5 @@ description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" From 1c071cbc1e55f97598f9624d69a7fce95591b472 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 3 Oct 2025 13:08:06 +0200 Subject: [PATCH 1365/1733] Add pve scripts local --- install/pve-scripts-local-install.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/install/pve-scripts-local-install.sh b/install/pve-scripts-local-install.sh index 83f12f2cf..714cdd115 100644 --- a/install/pve-scripts-local-install.sh +++ b/install/pve-scripts-local-install.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # Copyright (c) 2021-2025 community-scripts ORG -# Author: michelroegl-brunner +# Author: michelroegl-brunner # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -22,7 +22,7 @@ msg_info "Installing Dependencies" expect msg_ok "Dependencies installed." -NODE_VERSION=24 setup_nodejs +NODE_VERSION=22 setup_nodejs INSTALL_DIR=${INSTALL_DIR:-/opt/PVESciptslocal} @@ -36,7 +36,7 @@ else msg_ok "Repository updated." fi -cd "$INSTALL_DIR" || exit +cd "$INSTALL_DIR" || exit msg_info "Installing PVE Scripts local" $STD npm install From ebfb408b5409af48afb65087faf06ef85da26101 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 3 Oct 2025 13:09:15 +0200 Subject: [PATCH 1366/1733] Add pve scripts local --- install/pve-scripts-local-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/pve-scripts-local-install.sh b/install/pve-scripts-local-install.sh index 714cdd115..9e66749e2 100644 --- a/install/pve-scripts-local-install.sh +++ b/install/pve-scripts-local-install.sh @@ -36,7 +36,7 @@ else msg_ok "Repository updated." fi -cd "$INSTALL_DIR" || exit +cd "$INSTALL_DIR" msg_info "Installing PVE Scripts local" $STD npm install From 52c296ee299c91553f9b653af57e7910d1a83d56 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 3 Oct 2025 13:10:28 +0200 Subject: [PATCH 1367/1733] Add pve scripts local --- ct/pve-scripts-local.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/pve-scripts-local.sh b/ct/pve-scripts-local.sh index de63eb62a..d3ee234a0 100644 --- a/ct/pve-scripts-local.sh +++ b/ct/pve-scripts-local.sh @@ -6,7 +6,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV # Source: https://www.debian.org/ APP="PVE-Scripts-Local" -var_tags="${var_tags:-os}" +var_tags="${var_tags:-pve-scritps-local}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-4}" From 280c7695f5419699549168fa03b21a2a83903982 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 3 Oct 2025 11:12:30 +0000 Subject: [PATCH 1368/1733] Update .app files --- ct/headers/prometheus-blackbox-exporter | 6 ++++++ ct/headers/pve-scripts-local | 6 ++++++ 2 files changed, 12 insertions(+) create mode 100644 ct/headers/prometheus-blackbox-exporter create mode 100644 ct/headers/pve-scripts-local diff --git a/ct/headers/prometheus-blackbox-exporter b/ct/headers/prometheus-blackbox-exporter new file mode 100644 index 000000000..936f051d3 --- /dev/null +++ b/ct/headers/prometheus-blackbox-exporter @@ -0,0 +1,6 @@ + ____ __ __ ____ __ __ __ ______ __ + / __ \_________ ____ ___ ___ / /_/ /_ ___ __ _______ / __ )/ /___ ______/ /__/ /_ ____ _ __ / ____/ ______ ____ _____/ /____ _____ + / /_/ / ___/ __ \/ __ `__ \/ _ \/ __/ __ \/ _ \/ / / / ___/_____/ __ / / __ `/ ___/ //_/ __ \/ __ \| |/_/_____/ __/ | |/_/ __ \/ __ \/ ___/ __/ _ \/ ___/ + / ____/ / / /_/ / / / / / / __/ /_/ / / / __/ /_/ (__ )_____/ /_/ / / /_/ / /__/ ,< / /_/ / /_/ /> Date: Fri, 3 Oct 2025 14:48:13 +0200 Subject: [PATCH 1369/1733] Add pve scripts local --- install/pve-scripts-local-install.sh | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/install/pve-scripts-local-install.sh b/install/pve-scripts-local-install.sh index 9e66749e2..02b556fa7 100644 --- a/install/pve-scripts-local-install.sh +++ b/install/pve-scripts-local-install.sh @@ -17,26 +17,13 @@ msg_info "Installing Dependencies" $STD apt-get update $STD apt-get install -y \ build-essential \ - git \ sshpass \ expect msg_ok "Dependencies installed." NODE_VERSION=22 setup_nodejs -INSTALL_DIR=${INSTALL_DIR:-/opt/PVESciptslocal} - -if [ ! -d "$INSTALL_DIR/.git" ]; then - msg_info "Cloning repository into $INSTALL_DIR..." - $STD git clone https://github.com/michelroegl-brunner/PVESciptslocal.git "$INSTALL_DIR" - msg_ok "Repository cloned." -else - msg_info "Directory already exists. Pulling latest changes..." - $STD git -C "$INSTALL_DIR" pull - msg_ok "Repository updated." -fi - -cd "$INSTALL_DIR" +fetch_and_deploy_gh_release "ProxmoxVE-Local" "community-scripts/ProxmoxVE-Local" msg_info "Installing PVE Scripts local" $STD npm install From 979b440de2fd4126beb3dd4ca75fd0013ebdface Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 3 Oct 2025 14:52:59 +0200 Subject: [PATCH 1370/1733] Add pve scripts local --- install/pve-scripts-local-install.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/install/pve-scripts-local-install.sh b/install/pve-scripts-local-install.sh index 02b556fa7..62fe66f27 100644 --- a/install/pve-scripts-local-install.sh +++ b/install/pve-scripts-local-install.sh @@ -25,6 +25,8 @@ NODE_VERSION=22 setup_nodejs fetch_and_deploy_gh_release "ProxmoxVE-Local" "community-scripts/ProxmoxVE-Local" +cd /opt/ProxmoxVE-Local + msg_info "Installing PVE Scripts local" $STD npm install cp .env.example .env From fe6cea14107a66ffa777c9f9f1a3be316c93e692 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 3 Oct 2025 14:54:57 +0200 Subject: [PATCH 1371/1733] Add pve scripts local --- frontend/public/json/pve-scripts-local.json | 35 +++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 frontend/public/json/pve-scripts-local.json diff --git a/frontend/public/json/pve-scripts-local.json b/frontend/public/json/pve-scripts-local.json new file mode 100644 index 000000000..c8d5061fa --- /dev/null +++ b/frontend/public/json/pve-scripts-local.json @@ -0,0 +1,35 @@ +{ + "name": "PVEScriptsLocal", + "slug": "pve-scripts-local", + "categories": [ + 1 + ], + "date_created": "2025-10-03", + "type": "ct", + "updateable": false, + "privileged": false, + "interface_port": 3000, + "documentation": "https://github.com/community-scripts/ProxmoxVE-Local", + "config_path": "/opt/PVEScripts-Local/.env", + "website": "https://community-scripts.github.io/ProxmoxVE", + "logo": "https://community-scripts.github.io/ProxmoxVE/logo.png", + "description": "A modern web-based management interface for Proxmox VE (PVE) helper scripts. This tool provides a user-friendly way to discover, download, and execute community-sourced Proxmox scripts locally with real-time terminal output streaming.", + "install_methods": [ + { + "type": "default", + "script": "ct/pve-scripts-local.sh", + "resources": { + "cpu": 2, + "ram": 4096, + "hdd": 4, + "os": "Debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} From 2375d938808fb7c9f0dc6a8a852ecff787d3dfd8 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner <73236783+michelroegl-brunner@users.noreply.github.com> Date: Fri, 3 Oct 2025 15:01:20 +0200 Subject: [PATCH 1372/1733] Update service configuration for PVE Scripts installation --- install/pve-scripts-local-install.sh | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/install/pve-scripts-local-install.sh b/install/pve-scripts-local-install.sh index 62fe66f27..71f6977be 100644 --- a/install/pve-scripts-local-install.sh +++ b/install/pve-scripts-local-install.sh @@ -35,17 +35,14 @@ chmod 755 data $STD npm run build msg_ok "Installed PVE Scripts local" -SERVICE_NAME="pvescriptslocal" -SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service" - msg_info "Creating Service" -cat > "$SERVICE_FILE" < "etc/systemd/system/pvescriptslocal.service" < Date: Fri, 3 Oct 2025 15:03:23 +0200 Subject: [PATCH 1373/1733] Small fixes --- install/pve-scripts-local-install.sh | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/install/pve-scripts-local-install.sh b/install/pve-scripts-local-install.sh index 71f6977be..09c57e72d 100644 --- a/install/pve-scripts-local-install.sh +++ b/install/pve-scripts-local-install.sh @@ -4,7 +4,6 @@ # Author: michelroegl-brunner # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE - source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 @@ -14,20 +13,18 @@ network_check update_os msg_info "Installing Dependencies" - $STD apt-get update - $STD apt-get install -y \ - build-essential \ - sshpass \ - expect +$STD apt-get update +$STD apt-get install -y \ + build-essential \ + sshpass \ + expect msg_ok "Dependencies installed." NODE_VERSION=22 setup_nodejs - fetch_and_deploy_gh_release "ProxmoxVE-Local" "community-scripts/ProxmoxVE-Local" -cd /opt/ProxmoxVE-Local - msg_info "Installing PVE Scripts local" +cd /opt/ProxmoxVE-Local $STD npm install cp .env.example .env mkdir -p data @@ -36,7 +33,7 @@ $STD npm run build msg_ok "Installed PVE Scripts local" msg_info "Creating Service" -cat > "etc/systemd/system/pvescriptslocal.service" </etc/systemd/system/pvescriptslocal.service [Unit] Description=PVEScriptslocal Service After=network.target @@ -54,7 +51,6 @@ WantedBy=multi-user.target EOF systemctl enable -q --now pvescriptslocal - msg_ok "Created Service" motd_ssh @@ -65,4 +61,3 @@ $STD apt -y autoremove $STD apt -y autoclean $STD apt -y clean msg_ok "Cleaned" - From 4647307b9c40d5815662ed5c883fe146c2cfacf9 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 3 Oct 2025 15:20:18 +0200 Subject: [PATCH 1374/1733] update pve-scritps-local --- install/pve-scripts-local-install.sh | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/install/pve-scripts-local-install.sh b/install/pve-scripts-local-install.sh index 09c57e72d..368fb3087 100644 --- a/install/pve-scripts-local-install.sh +++ b/install/pve-scripts-local-install.sh @@ -13,11 +13,12 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt-get update -$STD apt-get install -y \ - build-essential \ - sshpass \ - expect + $STD apt-get update + $STD apt-get install -y \ + build-essential \ + sshpass \ + rsync \ + expect msg_ok "Dependencies installed." NODE_VERSION=22 setup_nodejs From 68555c442d7514e68761e30b6b5a99bd181a5e55 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Fri, 3 Oct 2025 18:08:35 +0200 Subject: [PATCH 1375/1733] GLPI test --- ct/glpi.sh | 47 +++++++++++++ install/glpi-install.sh | 150 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 197 insertions(+) create mode 100644 ct/glpi.sh create mode 100644 install/glpi-install.sh diff --git a/ct/glpi.sh b/ct/glpi.sh new file mode 100644 index 000000000..d03b80be8 --- /dev/null +++ b/ct/glpi.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Nícolas Pastorello (opastorello) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://www.glpi-project.org/ + +APP="GLPI" +var_tags="${var_tags:-asset-management;foss}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-10}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -d /opt/glpi ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + RELEASE=$(curl -fsSL https://api.github.com/repos/glpi-project/glpi/releases/latest | grep '"tag_name"' | sed -E 's/.*"tag_name": "([^"]+)".*/\1/') + if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then + msg_error "Currently we don't provide an update function for this ${APP}." + else + msg_ok "No update required. ${APP} is already at v${RELEASE}." + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:80${CL}" \ No newline at end of file diff --git a/install/glpi-install.sh b/install/glpi-install.sh new file mode 100644 index 000000000..23564153f --- /dev/null +++ b/install/glpi-install.sh @@ -0,0 +1,150 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Nícolas Pastorello (opastorello) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://www.glpi-project.org/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt-get install -y \ + git \ + apache2 \ + php8.2-{apcu,cli,common,curl,gd,imap,ldap,mysql,xmlrpc,xml,mbstring,bcmath,intl,zip,redis,bz2,soap} \ + php-cas \ + libapache2-mod-php +msg_ok "Installed Dependencies" + +setup_mariadb + +msg_info "Setting up database" +DB_NAME=glpi_db +DB_USER=glpi +DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) +mariadb-tzinfo-to-sql /usr/share/zoneinfo | mariadb mysql +$STD mariadb -u root -e "CREATE DATABASE $DB_NAME;" +$STD mariadb -u root -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';" +$STD mariadb -u root -e "GRANT ALL PRIVILEGES ON $DB_NAME.* TO '$DB_USER'@'localhost';" +$STD mariadb -u root -e "GRANT SELECT ON \`mysql\`.\`time_zone_name\` TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" +{ + echo "GLPI Database Credentials" + echo "Database: $DB_NAME" + echo "Username: $DB_USER" + echo "Password: $DB_PASS" +} >>~/glpi_db.creds +msg_ok "Set up database" + +msg_info "Installing GLPi" +cd /opt +RELEASE=$(curl -fsSL https://api.github.com/repos/glpi-project/glpi/releases/latest | grep '"tag_name"' | sed -E 's/.*"tag_name": "([^"]+)".*/\1/') +curl -fsSL "https://github.com/glpi-project/glpi/releases/download/${RELEASE}/glpi-${RELEASE}.tgz" -o "glpi-${RELEASE}.tgz" +$STD tar -xzvf glpi-${RELEASE}.tgz +cd /opt/glpi +$STD php bin/console db:install --db-name=$DB_NAME --db-user=$DB_USER --db-password=$DB_PASS --no-interaction +echo "${RELEASE}" >/opt/${APPLICATION}_version.txt +msg_ok "Installed GLPi" + +msg_info "Setting Downstream file" +cat </opt/glpi/inc/downstream.php +/etc/glpi/local_define.php +/etc/apache2/sites-available/glpi.conf + + ServerName localhost + DocumentRoot /opt/glpi/public + + + Require all granted + RewriteEngine On + RewriteCond %{HTTP:Authorization} ^(.+)$ + RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^(.*)$ index.php [QSA,L] + + + ErrorLog \${APACHE_LOG_DIR}/glpi_error.log + CustomLog \${APACHE_LOG_DIR}/glpi_access.log combined + +EOF +$STD a2dissite 000-default.conf +$STD a2enmod rewrite +$STD a2ensite glpi.conf +msg_ok "Setup Service" + +msg_info "Setup Cronjob" +echo "* * * * * php /opt/glpi/front/cron.php" | crontab - +msg_ok "Setup Cronjob" + +msg_info "Update PHP Params" +PHP_VERSION=$(ls /etc/php/ | grep -E '^[0-9]+\.[0-9]+$' | head -n 1) +PHP_INI="/etc/php/$PHP_VERSION/apache2/php.ini" +sed -i 's/^upload_max_filesize = .*/upload_max_filesize = 20M/' $PHP_INI +sed -i 's/^post_max_size = .*/post_max_size = 20M/' $PHP_INI +sed -i 's/^max_execution_time = .*/max_execution_time = 60/' $PHP_INI +sed -i 's/^max_input_vars = .*/max_input_vars = 5000/' $PHP_INI +sed -i 's/^memory_limit = .*/memory_limit = 256M/' $PHP_INI +sed -i 's/^;\?\s*session.cookie_httponly\s*=.*/session.cookie_httponly = On/' $PHP_INI +systemctl restart apache2 +msg_ok "Update PHP Params" + +motd_ssh +customize + +msg_info "Cleaning up" +rm -rf /opt/glpi/install +rm -rf /opt/glpi-${RELEASE}.tgz +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From f382b8ce6413ed0575edc71a6f60cb0299f81d2c Mon Sep 17 00:00:00 2001 From: tremor021 Date: Fri, 3 Oct 2025 18:17:34 +0200 Subject: [PATCH 1376/1733] GLPI test fix --- install/glpi-install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/install/glpi-install.sh b/install/glpi-install.sh index 23564153f..50a6323b1 100644 --- a/install/glpi-install.sh +++ b/install/glpi-install.sh @@ -47,7 +47,7 @@ RELEASE=$(curl -fsSL https://api.github.com/repos/glpi-project/glpi/releases/lat curl -fsSL "https://github.com/glpi-project/glpi/releases/download/${RELEASE}/glpi-${RELEASE}.tgz" -o "glpi-${RELEASE}.tgz" $STD tar -xzvf glpi-${RELEASE}.tgz cd /opt/glpi -$STD php bin/console db:install --db-name=$DB_NAME --db-user=$DB_USER --db-password=$DB_PASS --no-interaction +$STD php bin/console db:install --db-name=$DB_NAME --db-user=$DB_USER --db-password=$DB_PASS --no-interaction --allow-superuser echo "${RELEASE}" >/opt/${APPLICATION}_version.txt msg_ok "Installed GLPi" @@ -71,6 +71,7 @@ define('GLPI_DOC_DIR', GLPI_VAR_DIR); define('GLPI_CRON_DIR', GLPI_VAR_DIR . '/_cron'); define('GLPI_DUMP_DIR', GLPI_VAR_DIR . '/_dumps'); define('GLPI_GRAPH_DIR', GLPI_VAR_DIR . '/_graphs'); +define('GLPI_LOCAL_I18N_DIR', GLPI_VAR_DIR . '/_locales'); define('GLPI_LOCK_DIR', GLPI_VAR_DIR . '/_lock'); define('GLPI_PICTURE_DIR', GLPI_VAR_DIR . '/_pictures'); define('GLPI_PLUGIN_DOC_DIR', GLPI_VAR_DIR . '/_plugins'); From 51c77af180c75de38a3e2e7c8782adfe0eb82572 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Fri, 3 Oct 2025 19:06:46 +0200 Subject: [PATCH 1377/1733] GLPI test fix --- install/glpi-install.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/install/glpi-install.sh b/install/glpi-install.sh index 50a6323b1..64610fb2f 100644 --- a/install/glpi-install.sh +++ b/install/glpi-install.sh @@ -44,8 +44,8 @@ msg_ok "Set up database" msg_info "Installing GLPi" cd /opt RELEASE=$(curl -fsSL https://api.github.com/repos/glpi-project/glpi/releases/latest | grep '"tag_name"' | sed -E 's/.*"tag_name": "([^"]+)".*/\1/') -curl -fsSL "https://github.com/glpi-project/glpi/releases/download/${RELEASE}/glpi-${RELEASE}.tgz" -o "glpi-${RELEASE}.tgz" -$STD tar -xzvf glpi-${RELEASE}.tgz +curl -fsSL "https://github.com/glpi-project/glpi/releases/download/10.0.20/glpi-10.0.20.tgz" -o "glpi-10.0.20.tgz" +$STD tar -xzvf glpi-10.0.20.tgz cd /opt/glpi $STD php bin/console db:install --db-name=$DB_NAME --db-user=$DB_USER --db-password=$DB_PASS --no-interaction --allow-superuser echo "${RELEASE}" >/opt/${APPLICATION}_version.txt @@ -81,6 +81,7 @@ define('GLPI_TMP_DIR', GLPI_VAR_DIR . '/_tmp'); define('GLPI_UPLOAD_DIR', GLPI_VAR_DIR . '/_uploads'); define('GLPI_CACHE_DIR', GLPI_VAR_DIR . '/_cache'); define('GLPI_LOG_DIR', '/var/log/glpi'); +define('GLPI_THEMES_DIR',GLPI_VAR_DIR . '/_themes'); EOF msg_ok "Configured Downstream file" From 01cd18a26e798a273d2915ae41157b6393fae226 Mon Sep 17 00:00:00 2001 From: blackteaextract <56299795+blackteaextract@users.noreply.github.com> Date: Fri, 3 Oct 2025 19:57:00 +0200 Subject: [PATCH 1378/1733] Update opencloud-install.sh updated url --- install/opencloud-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/opencloud-install.sh b/install/opencloud-install.sh index 59f15950e..f369eee85 100644 --- a/install/opencloud-install.sh +++ b/install/opencloud-install.sh @@ -56,7 +56,7 @@ msg_ok "Installed ${APPLICATION}" msg_info "Configuring ${APPLICATION}" curl -fsSL https://raw.githubusercontent.com/opencloud-eu/opencloud-compose/refs/heads/main/config/opencloud/csp.yaml -o "$CONFIG_DIR"/csp.yaml -curl -fsSL https://github.com/opencloud-eu/opencloud/raw/refs/heads/main/deployments/examples/opencloud_full/config/opencloud/proxy.yaml -o "$CONFIG_DIR"/proxy.yaml.bak +curl -fsSL https://raw.githubusercontent.com/opencloud-eu/opencloud-compose/refs/heads/main/config/opencloud/proxy.yaml -o "$CONFIG_DIR"/proxy.yaml.bak cat <"$ENV_FILE" OC_URL=https://${OC_HOST} From 4fcd8731336f1851fd7a59752c242276cb048fd4 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sun, 5 Oct 2025 15:57:20 -0400 Subject: [PATCH 1379/1733] rwMarkable: backup custom configs & themes when updating --- ct/rwmarkable.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/rwmarkable.sh b/ct/rwmarkable.sh index c1c37045e..202183162 100644 --- a/ct/rwmarkable.sh +++ b/ct/rwmarkable.sh @@ -37,7 +37,7 @@ function update_script() { msg_info "Backing up configuration & data" cd /opt/rwmarkable cp ./.env /opt/app.env - $STD tar -cf /opt/data.tar ./data + $STD tar -cf /opt/data_config.tar ./data ./config msg_ok "Backed up configuration & data" NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs @@ -54,7 +54,7 @@ function update_script() { msg_info "Restoring configuration & data" mv /opt/app.env /opt/rwmarkable/.env - $STD tar -xf /opt/data.tar + $STD tar -xf /opt/data_config.tar msg_ok "Restored configuration & data" msg_info "Restarting ${APP} service" From 369bcd9e0465eb5be9442f8846cbdbb02f90c962 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sun, 5 Oct 2025 16:05:46 -0400 Subject: [PATCH 1380/1733] rwMarkable: fix rm --- ct/rwmarkable.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/rwmarkable.sh b/ct/rwmarkable.sh index 202183162..28dd6a5b1 100644 --- a/ct/rwmarkable.sh +++ b/ct/rwmarkable.sh @@ -60,7 +60,7 @@ function update_script() { msg_info "Restarting ${APP} service" systemctl start rwmarkable msg_ok "Restarted ${APP} service" - rm /opt/data.tar + rm /opt/data_config.tar msg_ok "Updated Successfully" fi exit From e68b727db6f7a627dac4a399eb237ae1e2d51cf7 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sun, 5 Oct 2025 20:06:15 +0000 Subject: [PATCH 1381/1733] Update .app files --- ct/headers/glpi | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/glpi diff --git a/ct/headers/glpi b/ct/headers/glpi new file mode 100644 index 000000000..789b62590 --- /dev/null +++ b/ct/headers/glpi @@ -0,0 +1,6 @@ + ________ ____ ____ + / ____/ / / __ \/ _/ + / / __/ / / /_/ // / +/ /_/ / /___/ ____// / +\____/_____/_/ /___/ + From b145baf7dd7879c30f92b161c62b3074b798c20f Mon Sep 17 00:00:00 2001 From: vhsdream Date: Mon, 6 Oct 2025 10:02:16 -0400 Subject: [PATCH 1382/1733] rwMarkable: increase disk resources --- ct/rwmarkable.sh | 2 +- frontend/public/json/rwmarkable.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/rwmarkable.sh b/ct/rwmarkable.sh index 28dd6a5b1..c98d3d887 100644 --- a/ct/rwmarkable.sh +++ b/ct/rwmarkable.sh @@ -9,7 +9,7 @@ APP="rwMarkable" var_tags="${var_tags:-tasks;notes}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" -var_disk="${var_disk:-4}" +var_disk="${var_disk:-6}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" diff --git a/frontend/public/json/rwmarkable.json b/frontend/public/json/rwmarkable.json index 8eaed9acc..9b1f395ca 100644 --- a/frontend/public/json/rwmarkable.json +++ b/frontend/public/json/rwmarkable.json @@ -21,7 +21,7 @@ "resources": { "cpu": 2, "ram": 2048, - "hdd": 4, + "hdd": 6, "os": "debian", "version": "13" } From 86c508169578b9172f0fc10bc9c386717b8b88c8 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Tue, 7 Oct 2025 19:13:16 +0200 Subject: [PATCH 1383/1733] Add palmr test --- ct/palmr.sh | 75 +++++++++++++++++++++++++++++++ install/palmr-install.sh | 95 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 170 insertions(+) create mode 100644 ct/palmr.sh create mode 100644 install/palmr-install.sh diff --git a/ct/palmr.sh b/ct/palmr.sh new file mode 100644 index 000000000..eb7fec6fa --- /dev/null +++ b/ct/palmr.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: vhsdream +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/kyantech/Palmr + +APP="Palmr" +var_tags="${var_tags:-files}" +var_cpu="${var_cpu:-4}" +var_ram="${var_ram:-6144}" +var_disk="${var_disk:-6}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/palmr_data ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + if check_for_gh_release "palmr" "kyantech/Palmr"; then + msg_info "Stopping Services" + systemctl stop palmr-frontend palmr-backend + msg_ok "Stopped Services" + + cp /opt/palmr/apps/server/.env /opt/palmr.env + rm -rf /opt/palmr + fetch_and_deploy_gh_release "Palmr" "kyantech/Palmr" "tarball" "latest" "/opt/palmr" + + PNPM="$(jq -r '.packageManager' /opt/palmr/package.json)" + NODE_VERSION="20" NODE_MODULE="$PNPM" setup_nodejs + + msg_info "Updating ${APP}" + cd /opt/palmr/apps/server + mv /opt/palmr.env /opt/palmr/apps/server/.env + $STD pnpm install + $STD npx prisma generate + $STD npx prisma migrate deploy + $STD npx prisma db push + $STD pnpm build + + cd /opt/palmr/apps/web + export NODE_ENV=production + export NEXT_TELEMETRY_DISABLED=1 + mv ./.env.example ./.env + $STD pnpm install + $STD pnpm build + chown -R palmr:palmr /opt/palmr_data /opt/palmr + msg_ok "Updated ${APP}" + + msg_info "Starting Services" + systemctl start palmr-backend palmr-frontend + msg_ok "Started Services" + msg_ok "Updated Successfully" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" diff --git a/install/palmr-install.sh b/install/palmr-install.sh new file mode 100644 index 000000000..9833ed5d8 --- /dev/null +++ b/install/palmr-install.sh @@ -0,0 +1,95 @@ +#!/usr/bin/env bash + +# Copyright (c) 2025 Community Scripts ORG +# Author: vhsdream +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/kyantech/Palmr + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +fetch_and_deploy_gh_release "Palmr" "kyantech/Palmr" "tarball" "latest" "/opt/palmr" +PNPM="$(jq -r '.packageManager' /opt/palmr/package.json)" +NODE_VERSION="20" NODE_MODULE="$PNPM" setup_nodejs + +msg_info "Configuring palmr backend" +PALMR_DIR="/opt/palmr_data" +mkdir -p "$PALMR_DIR" +PALMR_DB="${PALMR_DIR}/palmr.db" +PALMR_KEY="$(openssl rand -hex 32)" +cd /opt/palmr/apps/server +sed -e 's/_ENCRYPTION=true/_ENCRYPTION=false/' \ + -e '/^# ENC/s/# //' \ + -e "s/ENCRYPTION_KEY=.*$/ENCRYPTION_KEY=$PALMR_KEY/" \ + -e "s|file:.*$|file:$PALMR_DB\"|" \ + -e "\|db\"$|a\\# Uncomment below when using a reverse proxy\\ +# SECURE_SITE=true\\ +# Uncomment and add your path if using symlinks for data storage\\ +# CUSTOM_PATH=" \ + .env.example >./.env +$STD pnpm install +$STD npx prisma generate +$STD npx prisma migrate deploy +$STD npx prisma db push +$STD pnpm db:seed +$STD pnpm build +msg_ok "Configured palmr backend" + +msg_info "Configuring palmr frontend" +cd /opt/palmr/apps/web +mv ./.env.example ./.env +export NODE_ENV=production +export NEXT_TELEMETRY_DISABLED=1 +$STD pnpm install +$STD pnpm build +msg_ok "Configured palmr frontend" + +msg_info "Creating service" +useradd -d "$PALMR_DIR" -M -s /usr/sbin/nologin -U palmr +chown -R palmr:palmr "$PALMR_DIR" /opt/palmr +cat </etc/systemd/system/palmr-backend.service +[Unit] +Description=palmr Backend Service +After=network.target + +[Service] +Type=simple +User=palmr +Group=palmr +WorkingDirectory=/opt/palmr_data +ExecStart=/usr/bin/node /opt/palmr/apps/server/dist/server.js + +[Install] +WantedBy=multi-user.target +EOF + +cat </etc/systemd/system/palmr-frontend.service +[Unit] +Description=palmr Frontend Service +After=network.target palmr-backend.service + +[Service] +Type=simple +User=palmr +Group=palmr +WorkingDirectory=/opt/palmr/apps/web +ExecStart=/usr/bin/pnpm start + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now palmr-backend palmr-frontend +msg_ok "Created service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From d0b4fd9f5f7c5036908dafcc4f60ee7c4cc032c4 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 10 Oct 2025 09:23:03 +0200 Subject: [PATCH 1384/1733] Change Bridge detection to inlcude interface.vlan bridges as well --- misc/build.func | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/misc/build.func b/misc/build.func index c53632a9e..159e230f0 100644 --- a/misc/build.func +++ b/misc/build.func @@ -598,7 +598,7 @@ advanced_settings() { start=$(echo "${pair}" | cut -d':' -f1) end=$(echo "${pair}" | cut -d':' -f2) - if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then + if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge|vlan-raw-device)\b'; then iface_name=$(sed "${start}q;d" "${iface_filepath}" | awk '{print $2}') BRIDGES="${iface_name}"$'\n'"${BRIDGES}" fi @@ -618,8 +618,23 @@ advanced_settings() { BRIDGE_MENU_OPTIONS=() while IFS= read -r bridge; do if [[ -n "$bridge" ]]; then - # Get description from Proxmox built-in method - find comment for this specific bridge - description=$(grep -A 10 "iface $bridge" /etc/network/interfaces | grep '^#' | head -n1 | sed 's/^#\s*//') + # Check if this is a VLAN interface (pattern: name.vlan_id) + if [[ "$bridge" =~ ^(.+)\.([0-9]+)$ ]]; then + parent_device="${BASH_REMATCH[1]}" + vlan_id="${BASH_REMATCH[2]}" + + # Try to get the actual parent device from vlan-raw-device line + vlan_raw_device=$(grep -A 10 "iface $bridge" /etc/network/interfaces | grep '^\s*vlan-raw-device' | awk '{print $2}') + if [[ -n "$vlan_raw_device" ]]; then + parent_device="$vlan_raw_device" + fi + + description="VLAN $vlan_id on $parent_device" + else + # Regular bridge - get description from Proxmox built-in method - find comment for this specific bridge + description=$(grep -A 10 "iface $bridge" /etc/network/interfaces | grep '^#' | head -n1 | sed 's/^#\s*//') + fi + if [[ -n "$description" ]]; then BRIDGE_MENU_OPTIONS+=("$bridge" "${description}") else From b006c4f74bae0d0b35f98ad6e4cee24ee2f9c88c Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 10 Oct 2025 09:28:20 +0200 Subject: [PATCH 1385/1733] Revert: Change Bridge detection to inlcude interface.vlan bridges as well --- misc/build.func | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/misc/build.func b/misc/build.func index 159e230f0..c53632a9e 100644 --- a/misc/build.func +++ b/misc/build.func @@ -598,7 +598,7 @@ advanced_settings() { start=$(echo "${pair}" | cut -d':' -f1) end=$(echo "${pair}" | cut -d':' -f2) - if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge|vlan-raw-device)\b'; then + if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then iface_name=$(sed "${start}q;d" "${iface_filepath}" | awk '{print $2}') BRIDGES="${iface_name}"$'\n'"${BRIDGES}" fi @@ -618,23 +618,8 @@ advanced_settings() { BRIDGE_MENU_OPTIONS=() while IFS= read -r bridge; do if [[ -n "$bridge" ]]; then - # Check if this is a VLAN interface (pattern: name.vlan_id) - if [[ "$bridge" =~ ^(.+)\.([0-9]+)$ ]]; then - parent_device="${BASH_REMATCH[1]}" - vlan_id="${BASH_REMATCH[2]}" - - # Try to get the actual parent device from vlan-raw-device line - vlan_raw_device=$(grep -A 10 "iface $bridge" /etc/network/interfaces | grep '^\s*vlan-raw-device' | awk '{print $2}') - if [[ -n "$vlan_raw_device" ]]; then - parent_device="$vlan_raw_device" - fi - - description="VLAN $vlan_id on $parent_device" - else - # Regular bridge - get description from Proxmox built-in method - find comment for this specific bridge - description=$(grep -A 10 "iface $bridge" /etc/network/interfaces | grep '^#' | head -n1 | sed 's/^#\s*//') - fi - + # Get description from Proxmox built-in method - find comment for this specific bridge + description=$(grep -A 10 "iface $bridge" /etc/network/interfaces | grep '^#' | head -n1 | sed 's/^#\s*//') if [[ -n "$description" ]]; then BRIDGE_MENU_OPTIONS+=("$bridge" "${description}") else From 960fddb9ee01d5b3439885d61a5a18e03686555f Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 10 Oct 2025 11:18:59 +0200 Subject: [PATCH 1386/1733] Add docs for all files in /misc --- docs/misc/README.md | 42 + .../build.func/BUILD_FUNC_ARCHITECTURE.md | 410 ++++++++++ .../BUILD_FUNC_ENVIRONMENT_VARIABLES.md | 248 ++++++ .../build.func/BUILD_FUNC_EXECUTION_FLOWS.md | 413 ++++++++++ docs/misc/build.func/BUILD_FUNC_FLOWCHART.md | 244 ++++++ .../BUILD_FUNC_FUNCTIONS_REFERENCE.md | 361 +++++++++ .../build.func/BUILD_FUNC_USAGE_EXAMPLES.md | 600 +++++++++++++++ docs/misc/build.func/README.md | 260 +++++++ docs/misc/core.func/CORE_FLOWCHART.md | 316 ++++++++ .../core.func/CORE_FUNCTIONS_REFERENCE.md | 637 +++++++++++++++ docs/misc/core.func/CORE_INTEGRATION.md | 517 +++++++++++++ docs/misc/core.func/CORE_USAGE_EXAMPLES.md | 728 ++++++++++++++++++ docs/misc/core.func/README.md | 181 +++++ .../ERROR_HANDLER_FLOWCHART.md | 347 +++++++++ .../ERROR_HANDLER_FUNCTIONS_REFERENCE.md | 424 ++++++++++ .../ERROR_HANDLER_INTEGRATION.md | 512 ++++++++++++ .../ERROR_HANDLER_USAGE_EXAMPLES.md | 625 +++++++++++++++ docs/misc/error_handler.func/README.md | 228 ++++++ misc/build.func | 2 + 19 files changed, 7095 insertions(+) create mode 100644 docs/misc/README.md create mode 100644 docs/misc/build.func/BUILD_FUNC_ARCHITECTURE.md create mode 100644 docs/misc/build.func/BUILD_FUNC_ENVIRONMENT_VARIABLES.md create mode 100644 docs/misc/build.func/BUILD_FUNC_EXECUTION_FLOWS.md create mode 100644 docs/misc/build.func/BUILD_FUNC_FLOWCHART.md create mode 100644 docs/misc/build.func/BUILD_FUNC_FUNCTIONS_REFERENCE.md create mode 100644 docs/misc/build.func/BUILD_FUNC_USAGE_EXAMPLES.md create mode 100644 docs/misc/build.func/README.md create mode 100644 docs/misc/core.func/CORE_FLOWCHART.md create mode 100644 docs/misc/core.func/CORE_FUNCTIONS_REFERENCE.md create mode 100644 docs/misc/core.func/CORE_INTEGRATION.md create mode 100644 docs/misc/core.func/CORE_USAGE_EXAMPLES.md create mode 100644 docs/misc/core.func/README.md create mode 100644 docs/misc/error_handler.func/ERROR_HANDLER_FLOWCHART.md create mode 100644 docs/misc/error_handler.func/ERROR_HANDLER_FUNCTIONS_REFERENCE.md create mode 100644 docs/misc/error_handler.func/ERROR_HANDLER_INTEGRATION.md create mode 100644 docs/misc/error_handler.func/ERROR_HANDLER_USAGE_EXAMPLES.md create mode 100644 docs/misc/error_handler.func/README.md diff --git a/docs/misc/README.md b/docs/misc/README.md new file mode 100644 index 000000000..562d68999 --- /dev/null +++ b/docs/misc/README.md @@ -0,0 +1,42 @@ +# Misc Documentation + +This directory contains miscellaneous documentation for various components of the Proxmox Community Scripts project. + +## Documentation Categories + +### 📁 [build.func/](./build.func/) +Comprehensive documentation for the `build.func` script - the core orchestration script for Proxmox LXC container creation. + +**Contents:** +- Visual execution flowcharts +- Complete environment variables reference +- Function documentation +- Detailed execution flows +- System architecture guide +- Practical usage examples + +### 📁 [core.func/](./core.func/) +Fundamental utility functions and system checks that form the foundation for all other scripts. + +**Contents:** +- Visual execution flowcharts +- Complete function reference +- Practical usage examples +- Integration with other components + +### 📁 [error_handler.func/](./error_handler.func/) +Comprehensive error handling and signal management for Proxmox Community Scripts. + +**Contents:** +- Visual execution flowcharts +- Complete function reference +- Practical usage examples +- Integration with other components + +## Other Documentation + +Additional miscellaneous documentation may be added here as the project grows. + +--- + +*This directory contains specialized documentation for specific components of the Proxmox Community Scripts project.* diff --git a/docs/misc/build.func/BUILD_FUNC_ARCHITECTURE.md b/docs/misc/build.func/BUILD_FUNC_ARCHITECTURE.md new file mode 100644 index 000000000..1d9c5ed22 --- /dev/null +++ b/docs/misc/build.func/BUILD_FUNC_ARCHITECTURE.md @@ -0,0 +1,410 @@ +# build.func Architecture Guide + +## Overview + +This document provides a high-level architectural overview of `build.func`, including module dependencies, data flow, integration points, and system architecture. + +## High-Level Architecture + +``` +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ Proxmox Host System │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ build.func │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────────────┐ │ │ +│ │ │ Entry Point │ │ Configuration │ │ Container Creation │ │ │ +│ │ │ │ │ │ │ │ │ │ +│ │ │ • start() │ │ • variables() │ │ • build_container() │ │ │ +│ │ │ • install_ │ │ • base_ │ │ • create_lxc_container() │ │ │ +│ │ │ script() │ │ settings() │ │ • configure_gpu_ │ │ │ +│ │ │ • advanced_ │ │ • select_ │ │ passthrough() │ │ │ +│ │ │ settings() │ │ storage() │ │ • fix_gpu_gids() │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ Module Dependencies │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────────────┐ │ │ +│ │ │ core.func │ │ error_handler. │ │ api.func │ │ │ +│ │ │ │ │ func │ │ │ │ │ +│ │ │ • Basic │ │ • Error │ │ • Proxmox API │ │ │ +│ │ │ utilities │ │ handling │ │ interactions │ │ │ +│ │ │ • Common │ │ • Error │ │ • Container │ │ │ +│ │ │ functions │ │ recovery │ │ management │ │ │ +│ │ │ • System │ │ • Cleanup │ │ • Status │ │ │ +│ │ │ utilities │ │ functions │ │ monitoring │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────────────┘ │ │ +│ │ │ │ +│ │ ┌─────────────────────────────────────────────────────────────────────────┐ │ │ +│ │ │ tools.func │ │ │ +│ │ │ │ │ │ +│ │ │ • Additional utilities │ │ │ +│ │ │ • Helper functions │ │ │ +│ │ │ • System tools │ │ │ +│ │ └─────────────────────────────────────────────────────────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────────┘ +``` + +## Module Dependencies + +### Core Dependencies + +``` +build.func Dependencies: +├── core.func +│ ├── Basic system utilities +│ ├── Common functions +│ ├── System information +│ └── File operations +├── error_handler.func +│ ├── Error handling +│ ├── Error recovery +│ ├── Cleanup functions +│ └── Error logging +├── api.func +│ ├── Proxmox API interactions +│ ├── Container management +│ ├── Status monitoring +│ └── Configuration updates +└── tools.func + ├── Additional utilities + ├── Helper functions + ├── System tools + └── Custom functions +``` + +### Dependency Flow + +``` +Dependency Flow: +├── build.func +│ ├── Sources core.func +│ ├── Sources error_handler.func +│ ├── Sources api.func +│ └── Sources tools.func +├── core.func +│ ├── Basic utilities +│ └── System functions +├── error_handler.func +│ ├── Error management +│ └── Recovery functions +├── api.func +│ ├── Proxmox integration +│ └── Container operations +└── tools.func + ├── Additional tools + └── Helper functions +``` + +## Data Flow Architecture + +### Configuration Data Flow + +``` +Configuration Data Flow: +├── Environment Variables +│ ├── Hard environment variables +│ ├── App-specific .vars +│ ├── Global default.vars +│ └── Built-in defaults +├── Variable Resolution +│ ├── Apply precedence chain +│ ├── Validate settings +│ └── Resolve conflicts +├── Configuration Storage +│ ├── Memory variables +│ ├── Temporary files +│ └── Persistent storage +└── Configuration Usage + ├── Container creation + ├── Feature configuration + └── Settings persistence +``` + +### Container Data Flow + +``` +Container Data Flow: +├── Input Data +│ ├── Configuration variables +│ ├── Resource specifications +│ ├── Network settings +│ └── Storage requirements +├── Processing +│ ├── Validation +│ ├── Conflict resolution +│ ├── Resource allocation +│ └── Configuration generation +├── Container Creation +│ ├── LXC container creation +│ ├── Network configuration +│ ├── Storage setup +│ └── Feature configuration +└── Output + ├── Container status + ├── Access information + ├── Configuration files + └── Log files +``` + +## Integration Architecture + +### With Proxmox System + +``` +Proxmox Integration: +├── Proxmox Host +│ ├── LXC container management +│ ├── Storage management +│ ├── Network management +│ └── Resource management +├── Proxmox API +│ ├── Container operations +│ ├── Configuration updates +│ ├── Status monitoring +│ └── Error handling +├── Proxmox Configuration +│ ├── /etc/pve/lxc/.conf +│ ├── Storage configuration +│ ├── Network configuration +│ └── Resource configuration +└── Proxmox Services + ├── Container services + ├── Network services + ├── Storage services + └── Monitoring services +``` + +### With Install Scripts + +``` +Install Script Integration: +├── build.func +│ ├── Creates container +│ ├── Configures basic settings +│ ├── Starts container +│ └── Provides access +├── Install Scripts +│ ├── -install.sh +│ ├── Downloads application +│ ├── Configures application +│ └── Sets up services +├── Container +│ ├── Running application +│ ├── Configured services +│ ├── Network access +│ └── Storage access +└── Integration Points + ├── Container creation + ├── Network configuration + ├── Storage setup + └── Service configuration +``` + +## System Architecture Components + +### Core Components + +``` +System Components: +├── Entry Point +│ ├── start() function +│ ├── Context detection +│ ├── Environment capture +│ └── Workflow routing +├── Configuration Management +│ ├── Variable resolution +│ ├── Settings persistence +│ ├── Default management +│ └── Validation +├── Container Creation +│ ├── LXC container creation +│ ├── Network configuration +│ ├── Storage setup +│ └── Feature configuration +├── Hardware Integration +│ ├── GPU passthrough +│ ├── USB passthrough +│ ├── Storage management +│ └── Network management +└── Error Handling + ├── Error detection + ├── Error recovery + ├── Cleanup functions + └── User notification +``` + +### User Interface Components + +``` +UI Components: +├── Menu System +│ ├── Installation mode selection +│ ├── Configuration menus +│ ├── Storage selection +│ └── GPU configuration +├── Interactive Elements +│ ├── Whiptail menus +│ ├── User prompts +│ ├── Confirmation dialogs +│ └── Error messages +├── Non-Interactive Mode +│ ├── Environment variable driven +│ ├── Silent execution +│ ├── Automated configuration +│ └── Error handling +└── Output + ├── Status messages + ├── Progress indicators + ├── Completion information + └── Access details +``` + +## Security Architecture + +### Security Considerations + +``` +Security Architecture: +├── Container Security +│ ├── Unprivileged containers (default) +│ ├── Privileged containers (when needed) +│ ├── Resource limits +│ └── Access controls +├── Network Security +│ ├── Network isolation +│ ├── VLAN support +│ ├── Firewall integration +│ └── Access controls +├── Storage Security +│ ├── Storage isolation +│ ├── Access controls +│ ├── Encryption support +│ └── Backup integration +├── GPU Security +│ ├── Device isolation +│ ├── Permission management +│ ├── Access controls +│ └── Security validation +└── API Security + ├── Authentication + ├── Authorization + ├── Input validation + └── Error handling +``` + +## Performance Architecture + +### Performance Considerations + +``` +Performance Architecture: +├── Execution Optimization +│ ├── Parallel operations +│ ├── Efficient algorithms +│ ├── Minimal user interaction +│ └── Optimized validation +├── Resource Management +│ ├── Memory efficiency +│ ├── CPU optimization +│ ├── Disk usage optimization +│ └── Network efficiency +├── Caching +│ ├── Configuration caching +│ ├── Template caching +│ ├── Storage caching +│ └── GPU detection caching +└── Monitoring + ├── Performance monitoring + ├── Resource monitoring + ├── Error monitoring + └── Status monitoring +``` + +## Deployment Architecture + +### Deployment Scenarios + +``` +Deployment Scenarios: +├── Single Container +│ ├── Individual application +│ ├── Standard configuration +│ ├── Basic networking +│ └── Standard storage +├── Multiple Containers +│ ├── Application stack +│ ├── Shared networking +│ ├── Shared storage +│ └── Coordinated deployment +├── High Availability +│ ├── Redundant containers +│ ├── Load balancing +│ ├── Failover support +│ └── Monitoring integration +└── Development Environment + ├── Development containers + ├── Testing containers + ├── Staging containers + └── Production containers +``` + +## Maintenance Architecture + +### Maintenance Components + +``` +Maintenance Architecture: +├── Updates +│ ├── Container updates +│ ├── Application updates +│ ├── Configuration updates +│ └── Security updates +├── Monitoring +│ ├── Container monitoring +│ ├── Resource monitoring +│ ├── Performance monitoring +│ └── Error monitoring +├── Backup +│ ├── Configuration backup +│ ├── Container backup +│ ├── Storage backup +│ └── Recovery procedures +└── Troubleshooting + ├── Error diagnosis + ├── Log analysis + ├── Performance analysis + └── Recovery procedures +``` + +## Future Architecture Considerations + +### Scalability + +``` +Scalability Considerations: +├── Horizontal Scaling +│ ├── Multiple containers +│ ├── Load balancing +│ ├── Distributed deployment +│ └── Resource distribution +├── Vertical Scaling +│ ├── Resource scaling +│ ├── Performance optimization +│ ├── Capacity planning +│ └── Resource management +├── Automation +│ ├── Automated deployment +│ ├── Automated scaling +│ ├── Automated monitoring +│ └── Automated recovery +└── Integration + ├── External systems + ├── Cloud integration + ├── Container orchestration + └── Service mesh +``` diff --git a/docs/misc/build.func/BUILD_FUNC_ENVIRONMENT_VARIABLES.md b/docs/misc/build.func/BUILD_FUNC_ENVIRONMENT_VARIABLES.md new file mode 100644 index 000000000..b116d3106 --- /dev/null +++ b/docs/misc/build.func/BUILD_FUNC_ENVIRONMENT_VARIABLES.md @@ -0,0 +1,248 @@ +# build.func Environment Variables Reference + +## Overview + +This document provides a comprehensive reference of all environment variables used in `build.func`, organized by category and usage context. + +## Variable Categories + +### Core Container Variables + +| Variable | Description | Default | Set In | Used In | +|----------|-------------|---------|---------|---------| +| `APP` | Application name (e.g., "plex", "nextcloud") | - | Environment | Throughout | +| `NSAPP` | Namespace application name | `$APP` | Environment | Throughout | +| `CTID` | Container ID | - | Environment | Container creation | +| `CT_TYPE` | Container type ("install" or "update") | "install" | Environment | Entry point | +| `CT_NAME` | Container name | `$APP` | Environment | Container creation | + +### Operating System Variables + +| Variable | Description | Default | Set In | Used In | +|----------|-------------|---------|---------|---------| +| `var_os` | Operating system selection | "debian" | base_settings() | OS selection | +| `var_version` | OS version | "12" | base_settings() | Template selection | +| `var_template` | Template name | Auto-generated | base_settings() | Template download | + +### Resource Configuration Variables + +| Variable | Description | Default | Set In | Used In | +|----------|-------------|---------|---------|---------| +| `var_cpu` | CPU cores | "2" | base_settings() | Container creation | +| `var_ram` | RAM in MB | "2048" | base_settings() | Container creation | +| `var_disk` | Disk size in GB | "8" | base_settings() | Container creation | +| `DISK_SIZE` | Disk size (alternative) | `$var_disk` | Environment | Container creation | +| `CORE_COUNT` | CPU cores (alternative) | `$var_cpu` | Environment | Container creation | +| `RAM_SIZE` | RAM size (alternative) | `$var_ram` | Environment | Container creation | + +### Network Configuration Variables + +| Variable | Description | Default | Set In | Used In | +|----------|-------------|---------|---------|---------| +| `var_net` | Network interface | "vmbr0" | base_settings() | Network config | +| `var_bridge` | Bridge interface | "vmbr0" | base_settings() | Network config | +| `var_gateway` | Gateway IP | "192.168.1.1" | base_settings() | Network config | +| `var_ip` | Container IP address | - | User input | Network config | +| `var_ipv6` | IPv6 address | - | User input | Network config | +| `var_vlan` | VLAN ID | - | User input | Network config | +| `var_mtu` | MTU size | "1500" | base_settings() | Network config | +| `var_mac` | MAC address | Auto-generated | base_settings() | Network config | +| `NET` | Network interface (alternative) | `$var_net` | Environment | Network config | +| `BRG` | Bridge interface (alternative) | `$var_bridge` | Environment | Network config | +| `GATE` | Gateway IP (alternative) | `$var_gateway` | Environment | Network config | +| `IPV6_METHOD` | IPv6 configuration method | "none" | Environment | Network config | +| `VLAN` | VLAN ID (alternative) | `$var_vlan` | Environment | Network config | +| `MTU` | MTU size (alternative) | `$var_mtu` | Environment | Network config | +| `MAC` | MAC address (alternative) | `$var_mac` | Environment | Network config | + +### Storage Configuration Variables + +| Variable | Description | Default | Set In | Used In | +|----------|-------------|---------|---------|---------| +| `var_template_storage` | Storage for templates | - | select_storage() | Template storage | +| `var_container_storage` | Storage for container disks | - | select_storage() | Container storage | +| `TEMPLATE_STORAGE` | Template storage (alternative) | `$var_template_storage` | Environment | Template storage | +| `CONTAINER_STORAGE` | Container storage (alternative) | `$var_container_storage` | Environment | Container storage | + +### Feature Flags + +| Variable | Description | Default | Set In | Used In | +|----------|-------------|---------|---------|---------| +| `ENABLE_FUSE` | Enable FUSE support | "true" | base_settings() | Container features | +| `ENABLE_TUN` | Enable TUN/TAP support | "true" | base_settings() | Container features | +| `ENABLE_KEYCTL` | Enable keyctl support | "true" | base_settings() | Container features | +| `ENABLE_MOUNT` | Enable mount support | "true" | base_settings() | Container features | +| `ENABLE_NESTING` | Enable nesting support | "false" | base_settings() | Container features | +| `ENABLE_PRIVILEGED` | Enable privileged mode | "false" | base_settings() | Container features | +| `ENABLE_UNPRIVILEGED` | Enable unprivileged mode | "true" | base_settings() | Container features | +| `VERBOSE` | Enable verbose output | "false" | Environment | Logging | +| `SSH` | Enable SSH key provisioning | "true" | base_settings() | SSH setup | + +### GPU Passthrough Variables + +| Variable | Description | Default | Set In | Used In | +|----------|-------------|---------|---------|---------| +| `GPU_APPS` | List of apps that support GPU | - | Environment | GPU detection | +| `var_gpu` | GPU selection | - | User input | GPU passthrough | +| `var_gpu_type` | GPU type (intel/amd/nvidia) | - | detect_gpu_devices() | GPU passthrough | +| `var_gpu_devices` | GPU device list | - | detect_gpu_devices() | GPU passthrough | + +### API and Diagnostics Variables + +| Variable | Description | Default | Set In | Used In | +|----------|-------------|---------|---------|---------| +| `DIAGNOSTICS` | Enable diagnostics mode | "false" | Environment | Diagnostics | +| `METHOD` | Installation method | "install" | Environment | Installation flow | +| `RANDOM_UUID` | Random UUID for tracking | - | Environment | Logging | +| `API_TOKEN` | Proxmox API token | - | Environment | API calls | +| `API_USER` | Proxmox API user | - | Environment | API calls | + +### Settings Persistence Variables + +| Variable | Description | Default | Set In | Used In | +|----------|-------------|---------|---------|---------| +| `SAVE_DEFAULTS` | Save settings as defaults | "false" | User input | Settings persistence | +| `SAVE_APP_DEFAULTS` | Save app-specific defaults | "false" | User input | Settings persistence | +| `DEFAULT_VARS_FILE` | Path to default.vars | "/usr/local/community-scripts/default.vars" | Environment | Settings persistence | +| `APP_DEFAULTS_FILE` | Path to app.vars | "/usr/local/community-scripts/defaults/$APP.vars" | Environment | Settings persistence | + +## Variable Precedence Chain + +Variables are resolved in the following order (highest to lowest priority): + +1. **Hard Environment Variables**: Set before script execution +2. **App-specific .vars file**: `/usr/local/community-scripts/defaults/.vars` +3. **Global default.vars file**: `/usr/local/community-scripts/default.vars` +4. **Built-in defaults**: Set in `base_settings()` function + +## Critical Variables for Non-Interactive Use + +For silent/non-interactive execution, these variables must be set: + +```bash +# Core container settings +export APP="plex" +export CTID="100" +export var_hostname="plex-server" + +# OS selection +export var_os="debian" +export var_version="12" + +# Resource allocation +export var_cpu="4" +export var_ram="4096" +export var_disk="20" + +# Network configuration +export var_net="vmbr0" +export var_gateway="192.168.1.1" +export var_ip="192.168.1.100" + +# Storage selection +export var_template_storage="local" +export var_container_storage="local" + +# Feature flags +export ENABLE_FUSE="true" +export ENABLE_TUN="true" +export SSH="true" +``` + +## Environment Variable Usage Patterns + +### 1. Container Creation +```bash +# Basic container creation +export APP="nextcloud" +export CTID="101" +export var_hostname="nextcloud-server" +export var_os="debian" +export var_version="12" +export var_cpu="2" +export var_ram="2048" +export var_disk="10" +export var_net="vmbr0" +export var_gateway="192.168.1.1" +export var_ip="192.168.1.101" +export var_template_storage="local" +export var_container_storage="local" +``` + +### 2. GPU Passthrough +```bash +# Enable GPU passthrough +export GPU_APPS="plex,jellyfin,emby" +export var_gpu="intel" +export ENABLE_PRIVILEGED="true" +``` + +### 3. Advanced Network Configuration +```bash +# VLAN and IPv6 configuration +export var_vlan="100" +export var_ipv6="2001:db8::100" +export IPV6_METHOD="static" +export var_mtu="9000" +``` + +### 4. Storage Configuration +```bash +# Custom storage locations +export var_template_storage="nfs-storage" +export var_container_storage="ssd-storage" +``` + +## Variable Validation + +The script validates variables at several points: + +1. **Container ID validation**: Must be unique and within valid range +2. **IP address validation**: Must be valid IPv4/IPv6 format +3. **Storage validation**: Must exist and support required content types +4. **Resource validation**: Must be within reasonable limits +5. **Network validation**: Must be valid network configuration + +## Common Variable Combinations + +### Development Container +```bash +export APP="dev-container" +export CTID="200" +export var_hostname="dev-server" +export var_os="ubuntu" +export var_version="22.04" +export var_cpu="4" +export var_ram="4096" +export var_disk="20" +export ENABLE_NESTING="true" +export ENABLE_PRIVILEGED="true" +``` + +### Media Server with GPU +```bash +export APP="plex" +export CTID="300" +export var_hostname="plex-server" +export var_os="debian" +export var_version="12" +export var_cpu="6" +export var_ram="8192" +export var_disk="50" +export GPU_APPS="plex" +export var_gpu="nvidia" +export ENABLE_PRIVILEGED="true" +``` + +### Lightweight Service +```bash +export APP="nginx" +export CTID="400" +export var_hostname="nginx-proxy" +export var_os="alpine" +export var_version="3.18" +export var_cpu="1" +export var_ram="512" +export var_disk="2" +export ENABLE_UNPRIVILEGED="true" +``` diff --git a/docs/misc/build.func/BUILD_FUNC_EXECUTION_FLOWS.md b/docs/misc/build.func/BUILD_FUNC_EXECUTION_FLOWS.md new file mode 100644 index 000000000..47a0035e2 --- /dev/null +++ b/docs/misc/build.func/BUILD_FUNC_EXECUTION_FLOWS.md @@ -0,0 +1,413 @@ +# build.func Execution Flows + +## Overview + +This document details the execution flows for different installation modes and scenarios in `build.func`, including variable precedence, decision trees, and workflow patterns. + +## Installation Modes + +### 1. Default Install Flow + +**Purpose**: Uses built-in defaults with minimal user interaction +**Use Case**: Quick container creation with standard settings + +``` +Default Install Flow: +├── start() +│ ├── Detect execution context +│ ├── Capture hard environment variables +│ └── Set CT_TYPE="install" +├── install_script() +│ ├── Display installation mode menu +│ ├── User selects "Default Install" +│ └── Proceed with defaults +├── variables() +│ ├── base_settings() # Set built-in defaults +│ ├── Load app.vars (if exists) +│ ├── Load default.vars (if exists) +│ └── Apply variable precedence +├── build_container() +│ ├── validate_settings() +│ ├── check_conflicts() +│ └── create_lxc_container() +└── default_var_settings() + └── Offer to save as defaults +``` + +**Key Characteristics**: +- Minimal user prompts +- Uses built-in defaults +- Fast execution +- Suitable for standard deployments + +### 2. Advanced Install Flow + +**Purpose**: Full interactive configuration via whiptail menus +**Use Case**: Custom container configuration with full control + +``` +Advanced Install Flow: +├── start() +│ ├── Detect execution context +│ ├── Capture hard environment variables +│ └── Set CT_TYPE="install" +├── install_script() +│ ├── Display installation mode menu +│ ├── User selects "Advanced Install" +│ └── Proceed with advanced configuration +├── variables() +│ ├── base_settings() # Set built-in defaults +│ ├── Load app.vars (if exists) +│ ├── Load default.vars (if exists) +│ └── Apply variable precedence +├── advanced_settings() +│ ├── OS Selection Menu +│ ├── Resource Configuration Menu +│ ├── Network Configuration Menu +│ ├── select_storage() +│ │ ├── resolve_storage_preselect() +│ │ └── choose_and_set_storage_for_file() +│ ├── GPU Configuration Menu +│ │ └── detect_gpu_devices() +│ └── Feature Flags Menu +├── build_container() +│ ├── validate_settings() +│ ├── check_conflicts() +│ └── create_lxc_container() +└── default_var_settings() + └── Offer to save as defaults +``` + +**Key Characteristics**: +- Full interactive configuration +- Whiptail menus for all options +- Complete control over settings +- Suitable for custom deployments + +### 3. My Defaults Flow + +**Purpose**: Loads settings from global default.vars file +**Use Case**: Using previously saved global defaults + +``` +My Defaults Flow: +├── start() +│ ├── Detect execution context +│ ├── Capture hard environment variables +│ └── Set CT_TYPE="install" +├── install_script() +│ ├── Display installation mode menu +│ ├── User selects "My Defaults" +│ └── Proceed with loaded defaults +├── variables() +│ ├── base_settings() # Set built-in defaults +│ ├── Load app.vars (if exists) +│ ├── Load default.vars # Load global defaults +│ └── Apply variable precedence +├── build_container() +│ ├── validate_settings() +│ ├── check_conflicts() +│ └── create_lxc_container() +└── default_var_settings() + └── Offer to save as defaults +``` + +**Key Characteristics**: +- Uses global default.vars file +- Minimal user interaction +- Consistent with previous settings +- Suitable for repeated deployments + +### 4. App Defaults Flow + +**Purpose**: Loads settings from app-specific .vars file +**Use Case**: Using previously saved app-specific defaults + +``` +App Defaults Flow: +├── start() +│ ├── Detect execution context +│ ├── Capture hard environment variables +│ └── Set CT_TYPE="install" +├── install_script() +│ ├── Display installation mode menu +│ ├── User selects "App Defaults" +│ └── Proceed with app-specific defaults +├── variables() +│ ├── base_settings() # Set built-in defaults +│ ├── Load app.vars # Load app-specific defaults +│ ├── Load default.vars (if exists) +│ └── Apply variable precedence +├── build_container() +│ ├── validate_settings() +│ ├── check_conflicts() +│ └── create_lxc_container() +└── default_var_settings() + └── Offer to save as defaults +``` + +**Key Characteristics**: +- Uses app-specific .vars file +- Minimal user interaction +- App-optimized settings +- Suitable for app-specific deployments + +## Variable Precedence Chain + +### Precedence Order (Highest to Lowest) + +1. **Hard Environment Variables**: Set before script execution +2. **App-specific .vars file**: `/usr/local/community-scripts/defaults/.vars` +3. **Global default.vars file**: `/usr/local/community-scripts/default.vars` +4. **Built-in defaults**: Set in `base_settings()` function + +### Variable Resolution Process + +``` +Variable Resolution: +├── Capture hard environment variables at start() +├── Load built-in defaults in base_settings() +├── Load global default.vars (if exists) +├── Load app-specific .vars (if exists) +└── Apply precedence chain + ├── Hard env vars override all + ├── App.vars override default.vars and built-ins + ├── Default.vars override built-ins + └── Built-ins are fallback defaults +``` + +## Storage Selection Logic + +### Storage Resolution Flow + +``` +Storage Selection: +├── Check if storage is preselected +│ ├── var_template_storage set? → Validate and use +│ └── var_container_storage set? → Validate and use +├── Count available storage options +│ ├── Only 1 option → Auto-select +│ └── Multiple options → Prompt user +├── User selection via whiptail +│ ├── Template storage selection +│ └── Container storage selection +└── Validate selected storage + ├── Check availability + ├── Check content type support + └── Proceed with selection +``` + +### Storage Validation + +``` +Storage Validation: +├── Check storage exists +├── Check storage is online +├── Check content type support +│ ├── Template storage: vztmpl support +│ └── Container storage: rootdir support +├── Check available space +└── Validate permissions +``` + +## GPU Passthrough Flow + +### GPU Detection and Configuration + +``` +GPU Passthrough Flow: +├── detect_gpu_devices() +│ ├── Scan for Intel GPUs +│ │ ├── Check i915 driver +│ │ └── Detect devices +│ ├── Scan for AMD GPUs +│ │ ├── Check AMDGPU driver +│ │ └── Detect devices +│ └── Scan for NVIDIA GPUs +│ ├── Check NVIDIA driver +│ ├── Detect devices +│ └── Check CUDA support +├── Check GPU passthrough eligibility +│ ├── Is app in GPU_APPS list? +│ ├── Is container privileged? +│ └── Proceed if eligible +├── GPU selection logic +│ ├── Single GPU type → Auto-select +│ └── Multiple GPU types → Prompt user +├── configure_gpu_passthrough() +│ ├── Add GPU device entries +│ ├── Configure permissions +│ └── Update container config +└── fix_gpu_gids() + ├── Update GPU group IDs + └── Configure access permissions +``` + +### GPU Eligibility Check + +``` +GPU Eligibility: +├── Check app support +│ ├── Is APP in GPU_APPS list? +│ └── Proceed if supported +├── Check container privileges +│ ├── Is ENABLE_PRIVILEGED="true"? +│ └── Proceed if privileged +└── Check hardware availability + ├── Are GPUs detected? + └── Proceed if available +``` + +## Network Configuration Flow + +### Network Setup Process + +``` +Network Configuration: +├── Basic network settings +│ ├── var_net (network interface) +│ ├── var_bridge (bridge interface) +│ └── var_gateway (gateway IP) +├── IP configuration +│ ├── var_ip (IPv4 address) +│ ├── var_ipv6 (IPv6 address) +│ └── IPV6_METHOD (IPv6 method) +├── Advanced network settings +│ ├── var_vlan (VLAN ID) +│ ├── var_mtu (MTU size) +│ └── var_mac (MAC address) +└── Network validation + ├── Check IP format + ├── Check gateway reachability + └── Validate network configuration +``` + +## Container Creation Flow + +### LXC Container Creation Process + +``` +Container Creation: +├── create_lxc_container() +│ ├── Create basic container +│ ├── Configure network +│ ├── Set up storage +│ ├── Configure features +│ ├── Set resource limits +│ ├── Configure startup +│ └── Start container +├── Post-creation configuration +│ ├── Wait for network +│ ├── Configure GPU (if enabled) +│ ├── Set up SSH keys +│ └── Run post-install scripts +└── Finalization + ├── Display container info + ├── Show access details + └── Provide next steps +``` + +## Error Handling Flows + +### Validation Error Flow + +``` +Validation Error Flow: +├── validate_settings() +│ ├── Check configuration validity +│ └── Return error if invalid +├── check_conflicts() +│ ├── Check for conflicts +│ └── Return error if conflicts found +├── Error handling +│ ├── Display error message +│ ├── cleanup_on_error() +│ └── Exit with error code +└── User notification + ├── Show error details + └── Suggest fixes +``` + +### Storage Error Flow + +``` +Storage Error Flow: +├── Storage selection fails +├── Retry storage selection +│ ├── Show available options +│ └── Allow user to retry +├── Storage validation fails +│ ├── Show validation errors +│ └── Allow user to fix +└── Fallback to default storage + ├── Use fallback storage + └── Continue with creation +``` + +### GPU Error Flow + +``` +GPU Error Flow: +├── GPU detection fails +├── Fall back to no GPU +│ ├── Disable GPU passthrough +│ └── Continue without GPU +├── GPU configuration fails +│ ├── Show configuration errors +│ └── Allow user to retry +└── GPU permission errors + ├── Fix GPU permissions + └── Retry configuration +``` + +## Integration Flows + +### With Install Scripts + +``` +Install Script Integration: +├── build.func creates container +├── Container starts successfully +├── Install script execution +│ ├── Download and install app +│ ├── Configure app settings +│ └── Set up services +└── Post-installation configuration + ├── Verify installation + ├── Configure access + └── Display completion info +``` + +### With Proxmox API + +``` +Proxmox API Integration: +├── API authentication +├── Container creation via API +├── Configuration updates via API +├── Status monitoring via API +└── Error handling via API +``` + +## Performance Considerations + +### Execution Time Optimization + +``` +Performance Optimization: +├── Parallel operations where possible +├── Minimal user interaction in default mode +├── Efficient storage selection +├── Optimized GPU detection +└── Streamlined validation +``` + +### Resource Usage + +``` +Resource Usage: +├── Minimal memory footprint +├── Efficient disk usage +├── Optimized network usage +└── Minimal CPU overhead +``` diff --git a/docs/misc/build.func/BUILD_FUNC_FLOWCHART.md b/docs/misc/build.func/BUILD_FUNC_FLOWCHART.md new file mode 100644 index 000000000..e406f46fd --- /dev/null +++ b/docs/misc/build.func/BUILD_FUNC_FLOWCHART.md @@ -0,0 +1,244 @@ +# build.func Execution Flowchart + +## Main Execution Flow + +``` +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ START() │ +│ Entry point when build.func is sourced or executed │ +└─────────────────────┬───────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ Check Environment │ +│ • Detect if running on Proxmox host vs inside container │ +│ • Capture hard environment variables │ +│ • Set CT_TYPE based on context │ +└─────────────────────┬───────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ Determine Action │ +│ • If CT_TYPE="update" → update_script() │ +│ • If CT_TYPE="install" → install_script() │ +└─────────────────────┬───────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ INSTALL_SCRIPT() │ +│ Main container creation workflow │ +└─────────────────────┬───────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ Installation Mode Selection │ +│ │ +│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────┐ │ +│ │ Default │ │ Advanced │ │ My Defaults │ │ App Defaults│ │ +│ │ Install │ │ Install │ │ │ │ │ │ +│ │ │ │ │ │ │ │ │ │ +│ │ • Use built-in │ │ • Full whiptail │ │ • Load from │ │ • Load from │ │ +│ │ defaults │ │ menus │ │ default.vars │ │ app.vars │ │ +│ │ • Minimal │ │ • Interactive │ │ • Override │ │ • App- │ │ +│ │ prompts │ │ configuration │ │ built-ins │ │ specific │ │ +│ └─────────────────┘ └─────────────────┘ └─────────────────┘ └─────────────┘ │ +└─────────────────────┬───────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ VARIABLES() │ +│ • Load variable precedence chain: │ +│ 1. Hard environment variables │ +│ 2. App-specific .vars file │ +│ 3. Global default.vars file │ +│ 4. Built-in defaults in base_settings() │ +└─────────────────────┬───────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ BASE_SETTINGS() │ +│ • Set core container parameters │ +│ • Configure OS selection │ +│ • Set resource defaults (CPU, RAM, Disk) │ +│ • Configure network defaults │ +└─────────────────────┬───────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ Storage Selection Logic │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ SELECT_STORAGE() │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────────┐ │ │ +│ │ │ Template │ │ Container │ │ Resolution │ │ │ +│ │ │ Storage │ │ Storage │ │ Logic │ │ │ +│ │ │ │ │ │ │ │ │ │ +│ │ │ • Check if │ │ • Check if │ │ 1. Only 1 storage │ │ │ +│ │ │ preselected │ │ preselected │ │ → Auto-select │ │ │ +│ │ │ • Validate │ │ • Validate │ │ 2. Preselected │ │ │ +│ │ │ availability │ │ availability │ │ → Validate & use │ │ │ +│ │ │ • Prompt if │ │ • Prompt if │ │ 3. Multiple options │ │ │ +│ │ │ needed │ │ needed │ │ → Prompt user │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────┬───────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ BUILD_CONTAINER() │ +│ • Validate all settings │ +│ • Check for conflicts │ +│ • Prepare container configuration │ +└─────────────────────┬───────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ CREATE_LXC_CONTAINER() │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ Container Creation Process │ │ +│ │ │ │ +│ │ 1. Create LXC container with basic configuration │ │ +│ │ 2. Configure network settings │ │ +│ │ 3. Set up storage and mount points │ │ +│ │ 4. Configure features (FUSE, TUN, etc.) │ │ +│ │ 5. Set resource limits │ │ +│ │ 6. Configure startup options │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────┬───────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ GPU Passthrough Decision Tree │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ DETECT_GPU_DEVICES() │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────────┐ │ │ +│ │ │ Intel GPU │ │ AMD GPU │ │ NVIDIA GPU │ │ │ +│ │ │ │ │ │ │ │ │ │ +│ │ │ • Check i915 │ │ • Check AMDGPU │ │ • Check NVIDIA │ │ │ +│ │ │ driver │ │ driver │ │ driver │ │ │ +│ │ │ • Detect │ │ • Detect │ │ • Detect devices │ │ │ +│ │ │ devices │ │ devices │ │ • Check CUDA support │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ GPU Selection Logic │ │ +│ │ │ │ +│ │ • Is app in GPU_APPS list? OR Is container privileged? │ │ +│ │ └─ YES → Proceed with GPU configuration │ │ +│ │ └─ NO → Skip GPU passthrough │ │ +│ │ │ │ +│ │ • Single GPU type detected? │ │ +│ │ └─ YES → Auto-select and configure │ │ +│ │ └─ NO → Prompt user for selection │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────┬───────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ CONFIGURE_GPU_PASSTHROUGH() │ +│ • Add GPU device entries to /etc/pve/lxc/.conf │ +│ • Configure proper device permissions │ +│ • Set up device mapping │ +└─────────────────────┬───────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ Container Finalization │ +│ • Start container │ +│ • Wait for network connectivity │ +│ • Fix GPU GIDs (if GPU passthrough enabled) │ +│ • Configure SSH keys (if enabled) │ +│ • Run post-installation scripts │ +└─────────────────────┬───────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ Settings Persistence │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ DEFAULT_VAR_SETTINGS() │ │ +│ │ │ │ +│ │ • Offer to save current settings as defaults │ │ +│ │ • Save to /usr/local/community-scripts/default.vars │ │ +│ │ • Save to /usr/local/community-scripts/defaults/.vars │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────┬───────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ COMPLETION │ +│ • Display container information │ +│ • Show access details │ +│ • Provide next steps │ +└─────────────────────────────────────────────────────────────────────────────────┘ +``` + +## Key Decision Points + +### 1. Installation Mode Selection +- **Default**: Uses built-in defaults, minimal user interaction +- **Advanced**: Full interactive configuration via whiptail menus +- **My Defaults**: Loads settings from global default.vars file +- **App Defaults**: Loads settings from app-specific .vars file + +### 2. Storage Selection Logic +``` +Storage Selection Flow: +├── Check if storage is preselected via environment variables +│ ├── YES → Validate availability and use +│ └── NO → Continue to resolution logic +├── Count available storage options for content type +│ ├── Only 1 option → Auto-select +│ └── Multiple options → Prompt user via whiptail +└── Validate selected storage and proceed +``` + +### 3. GPU Passthrough Decision Tree +``` +GPU Passthrough Flow: +├── Detect available GPU hardware +│ ├── Intel GPU detected +│ ├── AMD GPU detected +│ └── NVIDIA GPU detected +├── Check if GPU passthrough should be enabled +│ ├── App is in GPU_APPS list? → YES +│ ├── Container is privileged? → YES +│ └── Neither? → Skip GPU passthrough +├── Configure GPU passthrough +│ ├── Single GPU type → Auto-configure +│ └── Multiple GPU types → Prompt user +└── Fix GPU GIDs post-creation +``` + +### 4. Variable Precedence Chain +``` +Variable Resolution Order: +1. Hard environment variables (captured at start) +2. App-specific .vars file (/usr/local/community-scripts/defaults/.vars) +3. Global default.vars file (/usr/local/community-scripts/default.vars) +4. Built-in defaults in base_settings() function +``` + +## Error Handling Flow + +``` +Error Handling: +├── Validation errors → Display error message and exit +├── Storage errors → Retry storage selection +├── Network errors → Retry network configuration +├── GPU errors → Fall back to no GPU passthrough +└── Container creation errors → Cleanup and exit +``` + +## Integration Points + +- **Core Functions**: Depends on core.func for basic utilities +- **Error Handling**: Uses error_handler.func for error management +- **API Functions**: Uses api.func for Proxmox API interactions +- **Tools**: Uses tools.func for additional utilities +- **Install Scripts**: Integrates with -install.sh scripts diff --git a/docs/misc/build.func/BUILD_FUNC_FUNCTIONS_REFERENCE.md b/docs/misc/build.func/BUILD_FUNC_FUNCTIONS_REFERENCE.md new file mode 100644 index 000000000..a8128d4e9 --- /dev/null +++ b/docs/misc/build.func/BUILD_FUNC_FUNCTIONS_REFERENCE.md @@ -0,0 +1,361 @@ +# build.func Functions Reference + +## Overview + +This document provides a comprehensive reference of all functions in `build.func`, organized alphabetically with detailed descriptions, parameters, and usage information. + +## Function Categories + +### Initialization Functions + +#### `start()` +**Purpose**: Main entry point when build.func is sourced or executed +**Parameters**: None +**Returns**: None +**Side Effects**: +- Detects execution context (Proxmox host vs container) +- Captures hard environment variables +- Sets CT_TYPE based on context +- Routes to appropriate workflow (install_script or update_script) +**Dependencies**: None +**Environment Variables Used**: `CT_TYPE`, `APP`, `CTID` + +#### `variables()` +**Purpose**: Load and resolve all configuration variables using precedence chain +**Parameters**: None +**Returns**: None +**Side Effects**: +- Loads app-specific .vars file +- Loads global default.vars file +- Applies variable precedence chain +- Sets all configuration variables +**Dependencies**: `base_settings()` +**Environment Variables Used**: All configuration variables + +#### `base_settings()` +**Purpose**: Set built-in default values for all configuration variables +**Parameters**: None +**Returns**: None +**Side Effects**: Sets default values for all variables +**Dependencies**: None +**Environment Variables Used**: All configuration variables + +### UI and Menu Functions + +#### `install_script()` +**Purpose**: Main installation workflow coordinator +**Parameters**: None +**Returns**: None +**Side Effects**: +- Displays installation mode selection menu +- Coordinates the entire installation process +- Handles user interaction and validation +**Dependencies**: `variables()`, `build_container()`, `default_var_settings()` +**Environment Variables Used**: `APP`, `CTID`, `var_hostname` + +#### `advanced_settings()` +**Purpose**: Provide advanced configuration options via whiptail menus +**Parameters**: None +**Returns**: None +**Side Effects**: +- Displays whiptail menus for configuration +- Updates configuration variables based on user input +- Validates user selections +**Dependencies**: `select_storage()`, `detect_gpu_devices()` +**Environment Variables Used**: All configuration variables + +#### `settings_menu()` +**Purpose**: Display and handle settings configuration menu +**Parameters**: None +**Returns**: None +**Side Effects**: Updates configuration variables +**Dependencies**: `advanced_settings()` +**Environment Variables Used**: All configuration variables + +### Storage Functions + +#### `select_storage()` +**Purpose**: Handle storage selection for templates and containers +**Parameters**: None +**Returns**: None +**Side Effects**: +- Resolves storage preselection +- Prompts user for storage selection if needed +- Validates storage availability +- Sets var_template_storage and var_container_storage +**Dependencies**: `resolve_storage_preselect()`, `choose_and_set_storage_for_file()` +**Environment Variables Used**: `var_template_storage`, `var_container_storage`, `TEMPLATE_STORAGE`, `CONTAINER_STORAGE` + +#### `resolve_storage_preselect()` +**Purpose**: Resolve preselected storage options +**Parameters**: +- `storage_type`: Type of storage (template or container) +**Returns**: Storage name if valid, empty if invalid +**Side Effects**: Validates storage availability +**Dependencies**: None +**Environment Variables Used**: `var_template_storage`, `var_container_storage` + +#### `choose_and_set_storage_for_file()` +**Purpose**: Interactive storage selection via whiptail +**Parameters**: +- `storage_type`: Type of storage (template or container) +- `content_type`: Content type (vztmpl or rootdir) +**Returns**: None +**Side Effects**: +- Displays whiptail menu +- Updates storage variables +- Validates selection +**Dependencies**: None +**Environment Variables Used**: `var_template_storage`, `var_container_storage` + +### Container Creation Functions + +#### `build_container()` +**Purpose**: Validate settings and prepare container creation +**Parameters**: None +**Returns**: None +**Side Effects**: +- Validates all configuration +- Checks for conflicts +- Prepares container configuration +- Calls create_lxc_container() +**Dependencies**: `create_lxc_container()` +**Environment Variables Used**: All configuration variables + +#### `create_lxc_container()` +**Purpose**: Create the actual LXC container +**Parameters**: None +**Returns**: None +**Side Effects**: +- Creates LXC container with basic configuration +- Configures network settings +- Sets up storage and mount points +- Configures features (FUSE, TUN, etc.) +- Sets resource limits +- Configures startup options +- Starts container +**Dependencies**: `configure_gpu_passthrough()`, `fix_gpu_gids()` +**Environment Variables Used**: All configuration variables + +### GPU and Hardware Functions + +#### `detect_gpu_devices()` +**Purpose**: Detect available GPU hardware on the system +**Parameters**: None +**Returns**: None +**Side Effects**: +- Scans for Intel, AMD, and NVIDIA GPUs +- Updates var_gpu_type and var_gpu_devices +- Determines GPU capabilities +**Dependencies**: None +**Environment Variables Used**: `var_gpu_type`, `var_gpu_devices`, `GPU_APPS` + +#### `configure_gpu_passthrough()` +**Purpose**: Configure GPU passthrough for the container +**Parameters**: None +**Returns**: None +**Side Effects**: +- Adds GPU device entries to container config +- Configures proper device permissions +- Sets up device mapping +- Updates /etc/pve/lxc/.conf +**Dependencies**: `detect_gpu_devices()` +**Environment Variables Used**: `var_gpu`, `var_gpu_type`, `var_gpu_devices`, `CTID` + +#### `fix_gpu_gids()` +**Purpose**: Fix GPU group IDs after container creation +**Parameters**: None +**Returns**: None +**Side Effects**: +- Updates GPU group IDs in container +- Ensures proper GPU access permissions +- Configures video and render groups +**Dependencies**: `configure_gpu_passthrough()` +**Environment Variables Used**: `CTID`, `var_gpu_type` + +### Settings Persistence Functions + +#### `default_var_settings()` +**Purpose**: Offer to save current settings as defaults +**Parameters**: None +**Returns**: None +**Side Effects**: +- Prompts user to save settings +- Saves to default.vars file +- Saves to app-specific .vars file +**Dependencies**: `maybe_offer_save_app_defaults()` +**Environment Variables Used**: All configuration variables + +#### `maybe_offer_save_app_defaults()` +**Purpose**: Offer to save app-specific defaults +**Parameters**: None +**Returns**: None +**Side Effects**: +- Prompts user to save app-specific settings +- Saves to app.vars file +- Updates app-specific configuration +**Dependencies**: None +**Environment Variables Used**: `APP`, `SAVE_APP_DEFAULTS` + +### Utility Functions + +#### `validate_settings()` +**Purpose**: Validate all configuration settings +**Parameters**: None +**Returns**: 0 if valid, 1 if invalid +**Side Effects**: +- Checks for configuration conflicts +- Validates resource limits +- Validates network configuration +- Validates storage configuration +**Dependencies**: None +**Environment Variables Used**: All configuration variables + +#### `check_conflicts()` +**Purpose**: Check for configuration conflicts +**Parameters**: None +**Returns**: 0 if no conflicts, 1 if conflicts found +**Side Effects**: +- Checks for conflicting settings +- Validates resource allocation +- Checks network configuration +**Dependencies**: None +**Environment Variables Used**: All configuration variables + +#### `cleanup_on_error()` +**Purpose**: Clean up resources on error +**Parameters**: None +**Returns**: None +**Side Effects**: +- Removes partially created containers +- Cleans up temporary files +- Resets configuration +**Dependencies**: None +**Environment Variables Used**: `CTID` + +## Function Call Flow + +### Main Installation Flow +``` +start() +├── variables() +│ ├── base_settings() +│ ├── Load app.vars +│ └── Load default.vars +├── install_script() +│ ├── advanced_settings() +│ │ ├── select_storage() +│ │ │ ├── resolve_storage_preselect() +│ │ │ └── choose_and_set_storage_for_file() +│ │ └── detect_gpu_devices() +│ ├── build_container() +│ │ ├── validate_settings() +│ │ ├── check_conflicts() +│ │ └── create_lxc_container() +│ │ ├── configure_gpu_passthrough() +│ │ └── fix_gpu_gids() +│ └── default_var_settings() +│ └── maybe_offer_save_app_defaults() +``` + +### Error Handling Flow +``` +Error Detection +├── validate_settings() +│ └── check_conflicts() +├── Error Handling +│ └── cleanup_on_error() +└── Exit with error code +``` + +## Function Dependencies + +### Core Dependencies +- `start()` → `install_script()` → `build_container()` → `create_lxc_container()` +- `variables()` → `base_settings()` +- `advanced_settings()` → `select_storage()` → `detect_gpu_devices()` + +### Storage Dependencies +- `select_storage()` → `resolve_storage_preselect()` +- `select_storage()` → `choose_and_set_storage_for_file()` + +### GPU Dependencies +- `configure_gpu_passthrough()` → `detect_gpu_devices()` +- `fix_gpu_gids()` → `configure_gpu_passthrough()` + +### Settings Dependencies +- `default_var_settings()` → `maybe_offer_save_app_defaults()` + +## Function Usage Examples + +### Basic Container Creation +```bash +# Set required variables +export APP="plex" +export CTID="100" +export var_hostname="plex-server" + +# Call main functions +start() # Entry point +# → variables() # Load configuration +# → install_script() # Main workflow +# → build_container() # Create container +# → create_lxc_container() # Actual creation +``` + +### Advanced Configuration +```bash +# Set advanced variables +export var_os="debian" +export var_version="12" +export var_cpu="4" +export var_ram="4096" +export var_disk="20" + +# Call advanced functions +advanced_settings() # Interactive configuration +# → select_storage() # Storage selection +# → detect_gpu_devices() # GPU detection +``` + +### GPU Passthrough +```bash +# Enable GPU passthrough +export GPU_APPS="plex" +export var_gpu="nvidia" + +# Call GPU functions +detect_gpu_devices() # Detect hardware +configure_gpu_passthrough() # Configure passthrough +fix_gpu_gids() # Fix permissions +``` + +### Settings Persistence +```bash +# Save settings as defaults +export SAVE_DEFAULTS="true" +export SAVE_APP_DEFAULTS="true" + +# Call persistence functions +default_var_settings() # Save global defaults +maybe_offer_save_app_defaults() # Save app defaults +``` + +## Function Error Handling + +### Validation Functions +- `validate_settings()`: Returns 0 for valid, 1 for invalid +- `check_conflicts()`: Returns 0 for no conflicts, 1 for conflicts + +### Error Recovery +- `cleanup_on_error()`: Cleans up on any error +- Error codes are propagated up the call stack +- Critical errors cause script termination + +### Error Types +1. **Configuration Errors**: Invalid settings or conflicts +2. **Resource Errors**: Insufficient resources or conflicts +3. **Network Errors**: Invalid network configuration +4. **Storage Errors**: Storage not available or invalid +5. **GPU Errors**: GPU configuration failures +6. **Container Creation Errors**: LXC creation failures diff --git a/docs/misc/build.func/BUILD_FUNC_USAGE_EXAMPLES.md b/docs/misc/build.func/BUILD_FUNC_USAGE_EXAMPLES.md new file mode 100644 index 000000000..b5ad83d6e --- /dev/null +++ b/docs/misc/build.func/BUILD_FUNC_USAGE_EXAMPLES.md @@ -0,0 +1,600 @@ +# build.func Usage Examples + +## Overview + +This document provides practical usage examples for `build.func`, covering common scenarios, CLI examples, and environment variable combinations. + +## Basic Usage Examples + +### 1. Simple Container Creation + +**Scenario**: Create a basic Plex media server container + +```bash +# Set basic environment variables +export APP="plex" +export CTID="100" +export var_hostname="plex-server" +export var_os="debian" +export var_version="12" +export var_cpu="4" +export var_ram="4096" +export var_disk="20" +export var_net="vmbr0" +export var_gateway="192.168.1.1" +export var_ip="192.168.1.100" +export var_template_storage="local" +export var_container_storage="local" + +# Execute build.func +source build.func +``` + +**Expected Output**: +``` +Creating Plex container... +Container ID: 100 +Hostname: plex-server +OS: Debian 12 +Resources: 4 CPU, 4GB RAM, 20GB Disk +Network: 192.168.1.100/24 +Container created successfully! +``` + +### 2. Advanced Configuration + +**Scenario**: Create a Nextcloud container with custom settings + +```bash +# Set advanced environment variables +export APP="nextcloud" +export CTID="101" +export var_hostname="nextcloud-server" +export var_os="ubuntu" +export var_version="22.04" +export var_cpu="6" +export var_ram="8192" +export var_disk="50" +export var_net="vmbr0" +export var_gateway="192.168.1.1" +export var_ip="192.168.1.101" +export var_vlan="100" +export var_mtu="9000" +export var_template_storage="nfs-storage" +export var_container_storage="ssd-storage" +export ENABLE_FUSE="true" +export ENABLE_TUN="true" +export SSH="true" + +# Execute build.func +source build.func +``` + +### 3. GPU Passthrough Configuration + +**Scenario**: Create a Jellyfin container with NVIDIA GPU passthrough + +```bash +# Set GPU passthrough variables +export APP="jellyfin" +export CTID="102" +export var_hostname="jellyfin-server" +export var_os="debian" +export var_version="12" +export var_cpu="8" +export var_ram="16384" +export var_disk="30" +export var_net="vmbr0" +export var_gateway="192.168.1.1" +export var_ip="192.168.1.102" +export var_template_storage="local" +export var_container_storage="local" +export GPU_APPS="jellyfin" +export var_gpu="nvidia" +export ENABLE_PRIVILEGED="true" +export ENABLE_FUSE="true" +export ENABLE_TUN="true" + +# Execute build.func +source build.func +``` + +## Silent/Non-Interactive Examples + +### 1. Automated Deployment + +**Scenario**: Deploy multiple containers without user interaction + +```bash +#!/bin/bash +# Automated deployment script + +# Function to create container +create_container() { + local app=$1 + local ctid=$2 + local ip=$3 + + export APP="$app" + export CTID="$ctid" + export var_hostname="${app}-server" + export var_os="debian" + export var_version="12" + export var_cpu="2" + export var_ram="2048" + export var_disk="10" + export var_net="vmbr0" + export var_gateway="192.168.1.1" + export var_ip="$ip" + export var_template_storage="local" + export var_container_storage="local" + export ENABLE_FUSE="true" + export ENABLE_TUN="true" + export SSH="true" + + source build.func +} + +# Create multiple containers +create_container "plex" "100" "192.168.1.100" +create_container "nextcloud" "101" "192.168.1.101" +create_container "nginx" "102" "192.168.1.102" +``` + +### 2. Development Environment Setup + +**Scenario**: Create development containers with specific configurations + +```bash +#!/bin/bash +# Development environment setup + +# Development container configuration +export APP="dev-container" +export CTID="200" +export var_hostname="dev-server" +export var_os="ubuntu" +export var_version="22.04" +export var_cpu="4" +export var_ram="4096" +export var_disk="20" +export var_net="vmbr0" +export var_gateway="192.168.1.1" +export var_ip="192.168.1.200" +export var_template_storage="local" +export var_container_storage="local" +export ENABLE_NESTING="true" +export ENABLE_PRIVILEGED="true" +export ENABLE_FUSE="true" +export ENABLE_TUN="true" +export SSH="true" + +# Execute build.func +source build.func +``` + +## Network Configuration Examples + +### 1. VLAN Configuration + +**Scenario**: Create container with VLAN support + +```bash +# VLAN configuration +export APP="web-server" +export CTID="300" +export var_hostname="web-server" +export var_os="debian" +export var_version="12" +export var_cpu="2" +export var_ram="2048" +export var_disk="10" +export var_net="vmbr0" +export var_gateway="192.168.100.1" +export var_ip="192.168.100.100" +export var_vlan="100" +export var_mtu="1500" +export var_template_storage="local" +export var_container_storage="local" + +source build.func +``` + +### 2. IPv6 Configuration + +**Scenario**: Create container with IPv6 support + +```bash +# IPv6 configuration +export APP="ipv6-server" +export CTID="301" +export var_hostname="ipv6-server" +export var_os="debian" +export var_version="12" +export var_cpu="2" +export var_ram="2048" +export var_disk="10" +export var_net="vmbr0" +export var_gateway="192.168.1.1" +export var_ip="192.168.1.101" +export var_ipv6="2001:db8::101" +export IPV6_METHOD="static" +export var_template_storage="local" +export var_container_storage="local" + +source build.func +``` + +## Storage Configuration Examples + +### 1. Custom Storage Locations + +**Scenario**: Use different storage for templates and containers + +```bash +# Custom storage configuration +export APP="storage-test" +export CTID="400" +export var_hostname="storage-test" +export var_os="debian" +export var_version="12" +export var_cpu="2" +export var_ram="2048" +export var_disk="10" +export var_net="vmbr0" +export var_gateway="192.168.1.1" +export var_ip="192.168.1.140" +export var_template_storage="nfs-storage" +export var_container_storage="ssd-storage" + +source build.func +``` + +### 2. High-Performance Storage + +**Scenario**: Use high-performance storage for resource-intensive applications + +```bash +# High-performance storage configuration +export APP="database-server" +export CTID="401" +export var_hostname="database-server" +export var_os="debian" +export var_version="12" +export var_cpu="8" +export var_ram="16384" +export var_disk="100" +export var_net="vmbr0" +export var_gateway="192.168.1.1" +export var_ip="192.168.1.141" +export var_template_storage="nvme-storage" +export var_container_storage="nvme-storage" + +source build.func +``` + +## Feature Configuration Examples + +### 1. Privileged Container + +**Scenario**: Create privileged container for system-level access + +```bash +# Privileged container configuration +export APP="system-container" +export CTID="500" +export var_hostname="system-container" +export var_os="debian" +export var_version="12" +export var_cpu="4" +export var_ram="4096" +export var_disk="20" +export var_net="vmbr0" +export var_gateway="192.168.1.1" +export var_ip="192.168.1.150" +export var_template_storage="local" +export var_container_storage="local" +export ENABLE_PRIVILEGED="true" +export ENABLE_FUSE="true" +export ENABLE_TUN="true" +export ENABLE_KEYCTL="true" +export ENABLE_MOUNT="true" + +source build.func +``` + +### 2. Unprivileged Container + +**Scenario**: Create secure unprivileged container + +```bash +# Unprivileged container configuration +export APP="secure-container" +export CTID="501" +export var_hostname="secure-container" +export var_os="debian" +export var_version="12" +export var_cpu="2" +export var_ram="2048" +export var_disk="10" +export var_net="vmbr0" +export var_gateway="192.168.1.1" +export var_ip="192.168.1.151" +export var_template_storage="local" +export var_container_storage="local" +export ENABLE_UNPRIVILEGED="true" +export ENABLE_FUSE="true" +export ENABLE_TUN="true" + +source build.func +``` + +## Settings Persistence Examples + +### 1. Save Global Defaults + +**Scenario**: Save current settings as global defaults + +```bash +# Save global defaults +export APP="default-test" +export CTID="600" +export var_hostname="default-test" +export var_os="debian" +export var_version="12" +export var_cpu="2" +export var_ram="2048" +export var_disk="10" +export var_net="vmbr0" +export var_gateway="192.168.1.1" +export var_ip="192.168.1.160" +export var_template_storage="local" +export var_container_storage="local" +export SAVE_DEFAULTS="true" + +source build.func +``` + +### 2. Save App-Specific Defaults + +**Scenario**: Save settings as app-specific defaults + +```bash +# Save app-specific defaults +export APP="plex" +export CTID="601" +export var_hostname="plex-server" +export var_os="debian" +export var_version="12" +export var_cpu="4" +export var_ram="4096" +export var_disk="20" +export var_net="vmbr0" +export var_gateway="192.168.1.1" +export var_ip="192.168.1.161" +export var_template_storage="local" +export var_container_storage="local" +export SAVE_APP_DEFAULTS="true" + +source build.func +``` + +## Error Handling Examples + +### 1. Validation Error Handling + +**Scenario**: Handle configuration validation errors + +```bash +#!/bin/bash +# Error handling example + +# Set invalid configuration +export APP="error-test" +export CTID="700" +export var_hostname="error-test" +export var_os="invalid-os" +export var_version="invalid-version" +export var_cpu="invalid-cpu" +export var_ram="invalid-ram" +export var_disk="invalid-disk" +export var_net="invalid-network" +export var_gateway="invalid-gateway" +export var_ip="invalid-ip" + +# Execute with error handling +if source build.func; then + echo "Container created successfully!" +else + echo "Error: Container creation failed!" + echo "Please check your configuration and try again." +fi +``` + +### 2. Storage Error Handling + +**Scenario**: Handle storage selection errors + +```bash +#!/bin/bash +# Storage error handling + +# Set invalid storage +export APP="storage-error-test" +export CTID="701" +export var_hostname="storage-error-test" +export var_os="debian" +export var_version="12" +export var_cpu="2" +export var_ram="2048" +export var_disk="10" +export var_net="vmbr0" +export var_gateway="192.168.1.1" +export var_ip="192.168.1.170" +export var_template_storage="nonexistent-storage" +export var_container_storage="nonexistent-storage" + +# Execute with error handling +if source build.func; then + echo "Container created successfully!" +else + echo "Error: Storage not available!" + echo "Please check available storage and try again." +fi +``` + +## Integration Examples + +### 1. With Install Scripts + +**Scenario**: Integrate with application install scripts + +```bash +#!/bin/bash +# Integration with install scripts + +# Create container +export APP="plex" +export CTID="800" +export var_hostname="plex-server" +export var_os="debian" +export var_version="12" +export var_cpu="4" +export var_ram="4096" +export var_disk="20" +export var_net="vmbr0" +export var_gateway="192.168.1.1" +export var_ip="192.168.1.180" +export var_template_storage="local" +export var_container_storage="local" + +# Create container +source build.func + +# Run install script +if [ -f "plex-install.sh" ]; then + source plex-install.sh +else + echo "Install script not found!" +fi +``` + +### 2. With Monitoring + +**Scenario**: Integrate with monitoring systems + +```bash +#!/bin/bash +# Monitoring integration + +# Create container with monitoring +export APP="monitored-app" +export CTID="801" +export var_hostname="monitored-app" +export var_os="debian" +export var_version="12" +export var_cpu="2" +export var_ram="2048" +export var_disk="10" +export var_net="vmbr0" +export var_gateway="192.168.1.1" +export var_ip="192.168.1.181" +export var_template_storage="local" +export var_container_storage="local" +export DIAGNOSTICS="true" + +# Create container +source build.func + +# Set up monitoring +if [ -f "monitoring-setup.sh" ]; then + source monitoring-setup.sh +fi +``` + +## Best Practices + +### 1. Environment Variable Management + +```bash +#!/bin/bash +# Best practice: Environment variable management + +# Set configuration file +CONFIG_FILE="/etc/build.func.conf" + +# Load configuration if exists +if [ -f "$CONFIG_FILE" ]; then + source "$CONFIG_FILE" +fi + +# Set required variables +export APP="${APP:-plex}" +export CTID="${CTID:-100}" +export var_hostname="${var_hostname:-plex-server}" +export var_os="${var_os:-debian}" +export var_version="${var_version:-12}" +export var_cpu="${var_cpu:-2}" +export var_ram="${var_ram:-2048}" +export var_disk="${var_disk:-10}" +export var_net="${var_net:-vmbr0}" +export var_gateway="${var_gateway:-192.168.1.1}" +export var_ip="${var_ip:-192.168.1.100}" +export var_template_storage="${var_template_storage:-local}" +export var_container_storage="${var_container_storage:-local}" + +# Execute build.func +source build.func +``` + +### 2. Error Handling and Logging + +```bash +#!/bin/bash +# Best practice: Error handling and logging + +# Set log file +LOG_FILE="/var/log/build.func.log" + +# Function to log messages +log_message() { + echo "$(date): $1" >> "$LOG_FILE" +} + +# Function to create container with error handling +create_container() { + local app=$1 + local ctid=$2 + + log_message "Starting container creation for $app (ID: $ctid)" + + # Set variables + export APP="$app" + export CTID="$ctid" + export var_hostname="${app}-server" + export var_os="debian" + export var_version="12" + export var_cpu="2" + export var_ram="2048" + export var_disk="10" + export var_net="vmbr0" + export var_gateway="192.168.1.1" + export var_ip="192.168.1.$ctid" + export var_template_storage="local" + export var_container_storage="local" + + # Create container + if source build.func; then + log_message "Container $app created successfully (ID: $ctid)" + return 0 + else + log_message "Error: Failed to create container $app (ID: $ctid)" + return 1 + fi +} + +# Create containers +create_container "plex" "100" +create_container "nextcloud" "101" +create_container "nginx" "102" +``` diff --git a/docs/misc/build.func/README.md b/docs/misc/build.func/README.md new file mode 100644 index 000000000..c7ede47c7 --- /dev/null +++ b/docs/misc/build.func/README.md @@ -0,0 +1,260 @@ +# build.func Documentation + +## Overview + +This directory contains comprehensive documentation for the `build.func` script, which is the core orchestration script for Proxmox LXC container creation in the Community Scripts project. + +## Documentation Files + +### 📊 [BUILD_FUNC_FLOWCHART.md](./BUILD_FUNC_FLOWCHART.md) +Visual ASCII flowchart showing the main execution flow, decision trees, and key decision points in the build.func script. + +**Contents:** +- Main execution flow diagram +- Installation mode selection flows +- Storage selection workflow +- GPU passthrough decision logic +- Variable precedence chain +- Error handling flow +- Integration points + +### 🔧 [BUILD_FUNC_ENVIRONMENT_VARIABLES.md](./BUILD_FUNC_ENVIRONMENT_VARIABLES.md) +Complete reference of all environment variables used in build.func, organized by category and usage context. + +**Contents:** +- Core container variables +- Operating system variables +- Resource configuration variables +- Network configuration variables +- Storage configuration variables +- Feature flags +- GPU passthrough variables +- API and diagnostics variables +- Settings persistence variables +- Variable precedence chain +- Critical variables for non-interactive use +- Common variable combinations + +### 📚 [BUILD_FUNC_FUNCTIONS_REFERENCE.md](./BUILD_FUNC_FUNCTIONS_REFERENCE.md) +Alphabetical function reference with detailed descriptions, parameters, dependencies, and usage information. + +**Contents:** +- Initialization functions +- UI and menu functions +- Storage functions +- Container creation functions +- GPU and hardware functions +- Settings persistence functions +- Utility functions +- Function call flow +- Function dependencies +- Function usage examples +- Function error handling + +### 🔄 [BUILD_FUNC_EXECUTION_FLOWS.md](./BUILD_FUNC_EXECUTION_FLOWS.md) +Detailed execution flows for different installation modes and scenarios, including variable precedence and decision trees. + +**Contents:** +- Default install flow +- Advanced install flow +- My defaults flow +- App defaults flow +- Variable precedence chain +- Storage selection logic +- GPU passthrough flow +- Network configuration flow +- Container creation flow +- Error handling flows +- Integration flows +- Performance considerations + +### 🏗️ [BUILD_FUNC_ARCHITECTURE.md](./BUILD_FUNC_ARCHITECTURE.md) +High-level architectural overview including module dependencies, data flow, integration points, and system architecture. + +**Contents:** +- High-level architecture diagram +- Module dependencies +- Data flow architecture +- Integration architecture +- System architecture components +- User interface components +- Security architecture +- Performance architecture +- Deployment architecture +- Maintenance architecture +- Future architecture considerations + +### 💡 [BUILD_FUNC_USAGE_EXAMPLES.md](./BUILD_FUNC_USAGE_EXAMPLES.md) +Practical usage examples covering common scenarios, CLI examples, and environment variable combinations. + +**Contents:** +- Basic usage examples +- Silent/non-interactive examples +- Network configuration examples +- Storage configuration examples +- Feature configuration examples +- Settings persistence examples +- Error handling examples +- Integration examples +- Best practices + +## Quick Start Guide + +### For New Users +1. Start with [BUILD_FUNC_FLOWCHART.md](./BUILD_FUNC_FLOWCHART.md) to understand the overall flow +2. Review [BUILD_FUNC_ENVIRONMENT_VARIABLES.md](./BUILD_FUNC_ENVIRONMENT_VARIABLES.md) for configuration options +3. Follow examples in [BUILD_FUNC_USAGE_EXAMPLES.md](./BUILD_FUNC_USAGE_EXAMPLES.md) + +### For Developers +1. Read [BUILD_FUNC_ARCHITECTURE.md](./BUILD_FUNC_ARCHITECTURE.md) for system overview +2. Study [BUILD_FUNC_FUNCTIONS_REFERENCE.md](./BUILD_FUNC_FUNCTIONS_REFERENCE.md) for function details +3. Review [BUILD_FUNC_EXECUTION_FLOWS.md](./BUILD_FUNC_EXECUTION_FLOWS.md) for implementation details + +### For System Administrators +1. Focus on [BUILD_FUNC_USAGE_EXAMPLES.md](./BUILD_FUNC_USAGE_EXAMPLES.md) for deployment scenarios +2. Review [BUILD_FUNC_ENVIRONMENT_VARIABLES.md](./BUILD_FUNC_ENVIRONMENT_VARIABLES.md) for configuration management +3. Check [BUILD_FUNC_ARCHITECTURE.md](./BUILD_FUNC_ARCHITECTURE.md) for security and performance considerations + +## Key Concepts + +### Variable Precedence +Variables are resolved in this order (highest to lowest priority): +1. Hard environment variables (set before script execution) +2. App-specific .vars file (`/usr/local/community-scripts/defaults/.vars`) +3. Global default.vars file (`/usr/local/community-scripts/default.vars`) +4. Built-in defaults (set in `base_settings()` function) + +### Installation Modes +- **Default Install**: Uses built-in defaults, minimal prompts +- **Advanced Install**: Full interactive configuration via whiptail +- **My Defaults**: Loads from global default.vars file +- **App Defaults**: Loads from app-specific .vars file + +### Storage Selection Logic +1. If only 1 storage exists for content type → auto-select +2. If preselected via environment variables → validate and use +3. Otherwise → prompt user via whiptail + +### GPU Passthrough Flow +1. Detect hardware (Intel/AMD/NVIDIA) +2. Check if app is in GPU_APPS list OR container is privileged +3. Auto-select if single GPU type, prompt if multiple +4. Configure `/etc/pve/lxc/.conf` with proper device entries +5. Fix GIDs post-creation to match container's video/render groups + +## Common Use Cases + +### Basic Container Creation +```bash +export APP="plex" +export CTID="100" +export var_hostname="plex-server" +export var_os="debian" +export var_version="12" +export var_cpu="4" +export var_ram="4096" +export var_disk="20" +export var_net="vmbr0" +export var_gateway="192.168.1.1" +export var_ip="192.168.1.100" +export var_template_storage="local" +export var_container_storage="local" + +source build.func +``` + +### GPU Passthrough +```bash +export APP="jellyfin" +export CTID="101" +export var_hostname="jellyfin-server" +export var_os="debian" +export var_version="12" +export var_cpu="8" +export var_ram="16384" +export var_disk="30" +export var_net="vmbr0" +export var_gateway="192.168.1.1" +export var_ip="192.168.1.101" +export var_template_storage="local" +export var_container_storage="local" +export GPU_APPS="jellyfin" +export var_gpu="nvidia" +export ENABLE_PRIVILEGED="true" + +source build.func +``` + +### Silent/Non-Interactive Deployment +```bash +#!/bin/bash +# Automated deployment +export APP="nginx" +export CTID="102" +export var_hostname="nginx-proxy" +export var_os="alpine" +export var_version="3.18" +export var_cpu="1" +export var_ram="512" +export var_disk="2" +export var_net="vmbr0" +export var_gateway="192.168.1.1" +export var_ip="192.168.1.102" +export var_template_storage="local" +export var_container_storage="local" +export ENABLE_UNPRIVILEGED="true" + +source build.func +``` + +## Troubleshooting + +### Common Issues +1. **Container creation fails**: Check resource availability and configuration validity +2. **Storage errors**: Verify storage exists and supports required content types +3. **Network errors**: Validate network configuration and IP address availability +4. **GPU passthrough issues**: Check hardware detection and container privileges +5. **Permission errors**: Verify user permissions and container privileges + +### Debug Mode +Enable verbose output for debugging: +```bash +export VERBOSE="true" +export DIAGNOSTICS="true" +source build.func +``` + +### Log Files +Check system logs for detailed error information: +- `/var/log/syslog` +- `/var/log/pve/lxc/.log` +- Container-specific logs + +## Contributing + +When contributing to build.func documentation: +1. Update relevant documentation files +2. Add examples for new features +3. Update architecture diagrams if needed +4. Test all examples before submitting +5. Follow the existing documentation style + +## Related Documentation + +- [Main README](../../README.md) - Project overview +- [Installation Guide](../../install/) - Installation scripts +- [Container Templates](../../ct/) - Container templates +- [Tools](../../tools/) - Additional tools and utilities + +## Support + +For issues and questions: +1. Check this documentation first +2. Review the [troubleshooting section](#troubleshooting) +3. Check existing issues in the project repository +4. Create a new issue with detailed information + +--- + +*Last updated: $(date)* +*Documentation version: 1.0* diff --git a/docs/misc/core.func/CORE_FLOWCHART.md b/docs/misc/core.func/CORE_FLOWCHART.md new file mode 100644 index 000000000..2b9dd98ce --- /dev/null +++ b/docs/misc/core.func/CORE_FLOWCHART.md @@ -0,0 +1,316 @@ +# core.func Execution Flowchart + +## Main Execution Flow + +``` +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ core.func Loading │ +│ Entry point when core.func is sourced by other scripts │ +└─────────────────────┬───────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ Load Prevention Check │ +│ • Check if _CORE_FUNC_LOADED is set │ +│ • Return early if already loaded │ +│ • Set _CORE_FUNC_LOADED=1 to prevent reloading │ +└─────────────────────┬───────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ LOAD_FUNCTIONS() │ +│ Main function loader - sets up all core utilities │ +└─────────────────────┬───────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ Core Function Loading Sequence │ +│ │ +│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────────────┐ │ +│ │ color() │ │ formatting() │ │ icons() │ │ +│ │ │ │ │ │ │ │ +│ │ • Set ANSI │ │ • Set format │ │ • Set symbolic icons │ │ +│ │ color codes │ │ helpers │ │ • Define message │ │ +│ │ • Define │ │ • Tab, bold, │ │ symbols │ │ +│ │ colors │ │ line reset │ │ • Status indicators │ │ +│ └─────────────────┘ └─────────────────┘ └─────────────────────────────┘ │ +│ │ +│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────────────┐ │ +│ │ default_vars() │ │ set_std_mode() │ │ Additional Functions │ │ +│ │ │ │ │ │ │ │ +│ │ • Set retry │ │ • Set verbose │ │ • Add more functions │ │ +│ │ variables │ │ mode │ │ as needed │ │ +│ │ • Initialize │ │ • Configure │ │ │ │ +│ │ counters │ │ STD variable │ │ │ │ +│ └─────────────────┘ └─────────────────┘ └─────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────────┘ +``` + +## System Check Functions Flow + +``` +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ System Validation Flow │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ PVE_CHECK() │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Get PVE │ │ Check PVE │ │ Check PVE │ │ │ +│ │ │ Version │ │ 8.x Support │ │ 9.x Support │ │ │ +│ │ │ │ │ │ │ │ │ │ +│ │ │ • pveversion │ │ • Allow 8.0-8.9│ │ • Allow ONLY 9.0 │ │ │ +│ │ │ • Parse version │ │ • Reject others │ │ • Reject 9.1+ │ │ │ +│ │ │ • Extract │ │ • Exit if │ │ • Exit if │ │ │ +│ │ │ major.minor │ │ unsupported │ │ unsupported │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ ARCH_CHECK() │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Check │ │ AMD64 Check │ │ PiMox Warning │ │ │ +│ │ │ Architecture │ │ │ │ │ │ │ +│ │ │ │ │ • dpkg --print- │ │ • Show PiMox │ │ │ +│ │ │ • Get system │ │ architecture │ │ message │ │ │ +│ │ │ architecture │ │ • Must be │ │ • Point to ARM64 │ │ │ +│ │ │ • Compare with │ │ "amd64" │ │ support │ │ │ +│ │ │ "amd64" │ │ • Exit if not │ │ • Exit script │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ SHELL_CHECK() │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Check │ │ Bash Check │ │ Error Handling │ │ │ +│ │ │ Shell Type │ │ │ │ │ │ │ +│ │ │ │ │ • ps -p $$ -o │ │ • Clear screen │ │ │ +│ │ │ • Get current │ │ comm= │ │ • Show error │ │ │ +│ │ │ shell │ │ • Must be │ │ • Sleep and exit │ │ │ +│ │ │ • Compare with │ │ "bash" │ │ │ │ │ +│ │ │ "bash" │ │ • Exit if not │ │ │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ ROOT_CHECK() │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Check │ │ Root Check │ │ Sudo Check │ │ │ +│ │ │ User ID │ │ │ │ │ │ │ +│ │ │ │ │ • id -u │ │ • Check parent │ │ │ +│ │ │ • Get user ID │ │ • Must be 0 │ │ process │ │ │ +│ │ │ • Check if │ │ • Exit if not │ │ • Detect sudo │ │ │ +│ │ │ root (0) │ │ root │ │ usage │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────────┘ +``` + +## Message System Flow + +``` +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ Message System Flow │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ MSG_INFO() │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Message │ │ Duplicate │ │ Display Mode │ │ │ +│ │ │ Validation │ │ Check │ │ Selection │ │ │ +│ │ │ │ │ │ │ │ │ │ +│ │ │ • Check if │ │ • Track shown │ │ • Verbose mode: │ │ │ +│ │ │ message │ │ messages │ │ Show directly │ │ │ +│ │ │ exists │ │ • Skip if │ │ • Normal mode: │ │ │ +│ │ │ • Return if │ │ already │ │ Start spinner │ │ │ +│ │ │ empty │ │ shown │ │ │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ SPINNER() │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Spinner │ │ Animation │ │ Display │ │ │ +│ │ │ Initialization│ │ Loop │ │ Control │ │ │ +│ │ │ │ │ │ │ │ │ │ +│ │ │ • Define │ │ • Cycle through │ │ • Print spinner │ │ │ +│ │ │ characters │ │ characters │ │ character │ │ │ +│ │ │ • Set index │ │ • Sleep 0.1s │ │ • Print message │ │ │ +│ │ │ • Start loop │ │ • Increment │ │ • Clear line │ │ │ +│ │ │ │ │ index │ │ │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ STOP_SPINNER() │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Get Spinner │ │ Kill Process │ │ Cleanup │ │ │ +│ │ │ PID │ │ │ │ │ │ │ +│ │ │ │ │ • Send TERM │ │ • Remove PID file │ │ │ +│ │ │ • From │ │ • Wait for │ │ • Unset variables │ │ │ +│ │ │ SPINNER_PID │ │ termination │ │ • Reset terminal │ │ │ +│ │ │ • From PID │ │ • Force kill │ │ settings │ │ │ +│ │ │ file │ │ if needed │ │ │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────────┘ +``` + +## Silent Execution Flow + +``` +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ SILENT() Execution Flow │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ Command Execution │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Setup │ │ Execute │ │ Capture Output │ │ │ +│ │ │ Environment │ │ Command │ │ │ │ │ +│ │ │ │ │ │ │ • Redirect stdout │ │ │ +│ │ │ • Disable │ │ • Run command │ │ to log file │ │ │ +│ │ │ error │ │ • Capture │ │ • Redirect stderr │ │ │ +│ │ │ handling │ │ return code │ │ to log file │ │ │ +│ │ │ • Remove │ │ • Store exit │ │ • Log all output │ │ │ +│ │ │ traps │ │ code │ │ │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ Error Handling │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Check Exit │ │ Load Error │ │ Display Error │ │ │ +│ │ │ Code │ │ Handler │ │ Information │ │ │ +│ │ │ │ │ │ │ │ │ │ +│ │ │ • If exit code │ │ • Source │ │ • Show error code │ │ │ +│ │ │ != 0 │ │ error_handler │ │ • Show explanation │ │ │ +│ │ │ • Proceed to │ │ if needed │ │ • Show command │ │ │ +│ │ │ error │ │ • Get error │ │ • Show log lines │ │ │ +│ │ │ handling │ │ explanation │ │ • Show full log │ │ │ +│ │ │ │ │ │ │ command │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ Log Management │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Log File │ │ Log Display │ │ Log Access │ │ │ +│ │ │ Management │ │ │ │ │ │ │ +│ │ │ │ │ • Show last 10 │ │ • Provide command │ │ │ +│ │ │ • Create log │ │ lines │ │ to view full log │ │ │ +│ │ │ file path │ │ • Count total │ │ • Show line count │ │ │ +│ │ │ • Use process │ │ lines │ │ • Enable debugging │ │ │ +│ │ │ ID in name │ │ • Format │ │ │ │ │ +│ │ │ │ │ output │ │ │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────────┘ +``` + +## Header Management Flow + +``` +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ Header Management Flow │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ GET_HEADER() │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Prepare │ │ Check Local │ │ Download Header │ │ │ +│ │ │ Parameters │ │ File │ │ │ │ │ +│ │ │ │ │ │ │ • Construct URL │ │ │ +│ │ │ • Get app name │ │ • Check if │ │ • Download file │ │ │ +│ │ │ from APP │ │ file exists │ │ • Save to local │ │ │ +│ │ │ • Get app type │ │ • Check if │ │ path │ │ │ +│ │ │ from APP_TYPE │ │ file has │ │ • Return success │ │ │ +│ │ │ • Construct │ │ content │ │ status │ │ │ +│ │ │ paths │ │ • Return if │ │ │ │ │ +│ │ │ │ │ available │ │ │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ HEADER_INFO() │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Get Header │ │ Clear Screen │ │ Display Header │ │ │ +│ │ │ Content │ │ │ │ │ │ │ +│ │ │ │ │ • Clear │ │ • Show header │ │ │ +│ │ │ • Call │ │ terminal │ │ content if │ │ │ +│ │ │ get_header() │ │ • Get terminal │ │ available │ │ │ +│ │ │ • Handle │ │ width │ │ • Format output │ │ │ +│ │ │ errors │ │ • Set default │ │ • Center content │ │ │ +│ │ │ • Return │ │ width if │ │ if possible │ │ │ +│ │ │ content │ │ needed │ │ │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────────┘ +``` + +## Swap Management Flow + +``` +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ CHECK_OR_CREATE_SWAP() Flow │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ Swap Detection │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Check Active │ │ Swap Found │ │ No Swap Found │ │ │ +│ │ │ Swap │ │ │ │ │ │ │ +│ │ │ │ │ • Show success │ │ • Show error │ │ │ +│ │ │ • Use swapon │ │ message │ │ message │ │ │ +│ │ │ command │ │ • Return 0 │ │ • Ask user for │ │ │ +│ │ │ • Check for │ │ │ │ creation │ │ │ +│ │ │ swap devices │ │ │ │ • Proceed to │ │ │ +│ │ │ • Return │ │ │ │ creation flow │ │ │ +│ │ │ status │ │ │ │ │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ Swap Creation │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ User Input │ │ Size │ │ File Creation │ │ │ +│ │ │ Collection │ │ Validation │ │ │ │ │ +│ │ │ │ │ │ │ • Create swap file │ │ │ +│ │ │ • Ask for │ │ • Validate │ │ with dd │ │ │ +│ │ │ confirmation │ │ numeric input │ │ • Set permissions │ │ │ +│ │ │ • Convert to │ │ • Check range │ │ • Format swap │ │ │ +│ │ │ lowercase │ │ • Abort if │ │ • Activate swap │ │ │ +│ │ │ • Check for │ │ invalid │ │ • Show success │ │ │ +│ │ │ y/yes │ │ │ │ message │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────────┘ +``` + +## Integration Points + +### With Other Scripts +- **build.func**: Provides system checks and UI functions +- **tools.func**: Uses core utilities for extended operations +- **api.func**: Uses system checks and error handling +- **error_handler.func**: Provides error explanations for silent execution + +### External Dependencies +- **curl**: For downloading header files +- **tput**: For terminal control (installed if missing) +- **swapon/mkswap**: For swap management +- **pveversion**: For Proxmox version checking + +### Data Flow +- **Input**: Environment variables, command parameters +- **Processing**: System validation, UI rendering, command execution +- **Output**: Messages, log files, exit codes, system state changes diff --git a/docs/misc/core.func/CORE_FUNCTIONS_REFERENCE.md b/docs/misc/core.func/CORE_FUNCTIONS_REFERENCE.md new file mode 100644 index 000000000..1dacb1609 --- /dev/null +++ b/docs/misc/core.func/CORE_FUNCTIONS_REFERENCE.md @@ -0,0 +1,637 @@ +# core.func Functions Reference + +## Overview + +This document provides a comprehensive alphabetical reference of all functions in `core.func`, including parameters, dependencies, usage examples, and error handling. + +## Function Categories + +### Initialization Functions + +#### `load_functions()` +**Purpose**: Main function loader that initializes all core utilities +**Parameters**: None +**Returns**: None +**Side Effects**: +- Sets `__FUNCTIONS_LOADED=1` to prevent reloading +- Calls all core function groups in sequence +- Initializes color, formatting, icons, defaults, and standard mode +**Dependencies**: None +**Environment Variables Used**: `__FUNCTIONS_LOADED` + +**Usage Example**: +```bash +# Automatically called when core.func is sourced +source core.func +# load_functions() is called automatically +``` + +### Color and Formatting Functions + +#### `color()` +**Purpose**: Set ANSI color codes for styled terminal output +**Parameters**: None +**Returns**: None +**Side Effects**: Sets global color variables +**Dependencies**: None +**Environment Variables Used**: None + +**Sets Variables**: +- `YW`: Yellow +- `YWB`: Bright yellow +- `BL`: Blue +- `RD`: Red +- `BGN`: Bright green +- `GN`: Green +- `DGN`: Dark green +- `CL`: Clear/reset + +**Usage Example**: +```bash +color +echo -e "${GN}Success message${CL}" +echo -e "${RD}Error message${CL}" +``` + +#### `color_spinner()` +**Purpose**: Set color codes specifically for spinner output +**Parameters**: None +**Returns**: None +**Side Effects**: Sets spinner-specific color variables +**Dependencies**: None +**Environment Variables Used**: None + +**Sets Variables**: +- `CS_YW`: Yellow for spinner +- `CS_YWB`: Bright yellow for spinner +- `CS_CL`: Clear for spinner + +#### `formatting()` +**Purpose**: Define formatting helpers for terminal output +**Parameters**: None +**Returns**: None +**Side Effects**: Sets global formatting variables +**Dependencies**: None +**Environment Variables Used**: None + +**Sets Variables**: +- `BFR`: Back and forward reset +- `BOLD`: Bold text +- `HOLD`: Space character +- `TAB`: Two spaces +- `TAB3`: Six spaces + +### Icon Functions + +#### `icons()` +**Purpose**: Set symbolic icons used throughout user feedback and prompts +**Parameters**: None +**Returns**: None +**Side Effects**: Sets global icon variables +**Dependencies**: `formatting()` (for TAB variable) +**Environment Variables Used**: `TAB`, `CL` + +**Sets Variables**: +- `CM`: Check mark +- `CROSS`: Cross mark +- `DNSOK`: DNS success +- `DNSFAIL`: DNS failure +- `INFO`: Information icon +- `OS`: Operating system icon +- `OSVERSION`: OS version icon +- `CONTAINERTYPE`: Container type icon +- `DISKSIZE`: Disk size icon +- `CPUCORE`: CPU core icon +- `RAMSIZE`: RAM size icon +- `SEARCH`: Search icon +- `VERBOSE_CROPPED`: Verbose mode icon +- `VERIFYPW`: Password verification icon +- `CONTAINERID`: Container ID icon +- `HOSTNAME`: Hostname icon +- `BRIDGE`: Bridge icon +- `NETWORK`: Network icon +- `GATEWAY`: Gateway icon +- `DISABLEIPV6`: IPv6 disable icon +- `DEFAULT`: Default settings icon +- `MACADDRESS`: MAC address icon +- `VLANTAG`: VLAN tag icon +- `ROOTSSH`: SSH key icon +- `CREATING`: Creating icon +- `ADVANCED`: Advanced settings icon +- `FUSE`: FUSE icon +- `HOURGLASS`: Hourglass icon + +### Default Variables Functions + +#### `default_vars()` +**Purpose**: Set default retry and wait variables for system actions +**Parameters**: None +**Returns**: None +**Side Effects**: Sets retry configuration variables +**Dependencies**: None +**Environment Variables Used**: None + +**Sets Variables**: +- `RETRY_NUM`: Number of retry attempts (default: 10) +- `RETRY_EVERY`: Seconds between retries (default: 3) +- `i`: Retry counter initialized to RETRY_NUM + +#### `set_std_mode()` +**Purpose**: Set default verbose mode for script execution +**Parameters**: None +**Returns**: None +**Side Effects**: Sets STD variable based on VERBOSE setting +**Dependencies**: None +**Environment Variables Used**: `VERBOSE` + +**Sets Variables**: +- `STD`: "silent" if VERBOSE != "yes", empty string if VERBOSE = "yes" + +### Silent Execution Functions + +#### `silent()` +**Purpose**: Execute commands silently with detailed error reporting +**Parameters**: `$*` - Command and arguments to execute +**Returns**: None (exits on error) +**Side Effects**: +- Executes command with output redirected to log file +- On error, displays detailed error information +- Exits with command's exit code +**Dependencies**: `error_handler.func` (for error explanations) +**Environment Variables Used**: `SILENT_LOGFILE` + +**Usage Example**: +```bash +silent apt-get update +silent apt-get install -y package-name +``` + +**Error Handling**: +- Captures command output to `/tmp/silent.$$.log` +- Shows error code explanation +- Displays last 10 lines of log +- Provides command to view full log + +### System Check Functions + +#### `shell_check()` +**Purpose**: Verify that the script is running in Bash shell +**Parameters**: None +**Returns**: None (exits if not Bash) +**Side Effects**: +- Checks current shell process +- Exits with error message if not Bash +**Dependencies**: None +**Environment Variables Used**: None + +**Usage Example**: +```bash +shell_check +# Script continues if Bash, exits if not +``` + +#### `root_check()` +**Purpose**: Ensure script is running as root user +**Parameters**: None +**Returns**: None (exits if not root) +**Side Effects**: +- Checks user ID and parent process +- Exits with error message if not root +**Dependencies**: None +**Environment Variables Used**: None + +**Usage Example**: +```bash +root_check +# Script continues if root, exits if not +``` + +#### `pve_check()` +**Purpose**: Verify Proxmox VE version compatibility +**Parameters**: None +**Returns**: None (exits if unsupported version) +**Side Effects**: +- Checks PVE version using pveversion command +- Exits with error message if unsupported +**Dependencies**: `pveversion` command +**Environment Variables Used**: None + +**Supported Versions**: +- Proxmox VE 8.0 - 8.9 +- Proxmox VE 9.0 (only) + +**Usage Example**: +```bash +pve_check +# Script continues if supported version, exits if not +``` + +#### `arch_check()` +**Purpose**: Verify system architecture is AMD64 +**Parameters**: None +**Returns**: None (exits if not AMD64) +**Side Effects**: +- Checks system architecture +- Exits with PiMox warning if not AMD64 +**Dependencies**: `dpkg` command +**Environment Variables Used**: None + +**Usage Example**: +```bash +arch_check +# Script continues if AMD64, exits if not +``` + +#### `ssh_check()` +**Purpose**: Detect and warn about external SSH usage +**Parameters**: None +**Returns**: None +**Side Effects**: +- Checks SSH_CLIENT environment variable +- Warns if connecting from external IP +- Allows local connections (127.0.0.1 or host IP) +**Dependencies**: None +**Environment Variables Used**: `SSH_CLIENT` + +**Usage Example**: +```bash +ssh_check +# Shows warning if external SSH, continues anyway +``` + +### Header Management Functions + +#### `get_header()` +**Purpose**: Download and cache application header files +**Parameters**: None (uses APP and APP_TYPE variables) +**Returns**: Header content on success, empty on failure +**Side Effects**: +- Downloads header from remote URL +- Caches header locally +- Creates directory structure if needed +**Dependencies**: `curl` command +**Environment Variables Used**: `APP`, `APP_TYPE` + +**Usage Example**: +```bash +export APP="plex" +export APP_TYPE="ct" +header_content=$(get_header) +``` + +#### `header_info()` +**Purpose**: Display application header information +**Parameters**: None (uses APP variable) +**Returns**: None +**Side Effects**: +- Clears screen +- Displays header content +- Gets terminal width for formatting +**Dependencies**: `get_header()`, `tput` command +**Environment Variables Used**: `APP` + +**Usage Example**: +```bash +export APP="plex" +header_info +# Displays Plex header information +``` + +### Utility Functions + +#### `ensure_tput()` +**Purpose**: Ensure tput command is available for terminal control +**Parameters**: None +**Returns**: None +**Side Effects**: +- Installs ncurses package if tput missing +- Works on Alpine and Debian-based systems +**Dependencies**: `apk` or `apt-get` package managers +**Environment Variables Used**: None + +**Usage Example**: +```bash +ensure_tput +# Installs ncurses if needed, continues if already available +``` + +#### `is_alpine()` +**Purpose**: Detect if running on Alpine Linux +**Parameters**: None +**Returns**: 0 if Alpine, 1 if not Alpine +**Side Effects**: None +**Dependencies**: None +**Environment Variables Used**: `var_os`, `PCT_OSTYPE` + +**Usage Example**: +```bash +if is_alpine; then + echo "Running on Alpine Linux" +else + echo "Not running on Alpine Linux" +fi +``` + +#### `is_verbose_mode()` +**Purpose**: Check if verbose mode is enabled +**Parameters**: None +**Returns**: 0 if verbose mode, 1 if not verbose +**Side Effects**: None +**Dependencies**: None +**Environment Variables Used**: `VERBOSE`, `var_verbose` + +**Usage Example**: +```bash +if is_verbose_mode; then + echo "Verbose mode enabled" +else + echo "Verbose mode disabled" +fi +``` + +#### `fatal()` +**Purpose**: Display fatal error and terminate script +**Parameters**: `$1` - Error message +**Returns**: None (terminates script) +**Side Effects**: +- Displays error message +- Sends INT signal to current process +**Dependencies**: `msg_error()` +**Environment Variables Used**: None + +**Usage Example**: +```bash +fatal "Critical error occurred" +# Script terminates after displaying error +``` + +### Spinner Functions + +#### `spinner()` +**Purpose**: Display animated spinner for progress indication +**Parameters**: None (uses SPINNER_MSG variable) +**Returns**: None (runs indefinitely) +**Side Effects**: +- Displays rotating spinner characters +- Uses terminal control sequences +**Dependencies**: `color_spinner()` +**Environment Variables Used**: `SPINNER_MSG` + +**Usage Example**: +```bash +SPINNER_MSG="Processing..." +spinner & +SPINNER_PID=$! +# Spinner runs in background +``` + +#### `clear_line()` +**Purpose**: Clear current terminal line +**Parameters**: None +**Returns**: None +**Side Effects**: Clears current line using terminal control +**Dependencies**: `tput` command +**Environment Variables Used**: None + +#### `stop_spinner()` +**Purpose**: Stop running spinner and cleanup +**Parameters**: None +**Returns**: None +**Side Effects**: +- Kills spinner process +- Removes PID file +- Resets terminal settings +- Unsets spinner variables +**Dependencies**: None +**Environment Variables Used**: `SPINNER_PID`, `SPINNER_MSG` + +**Usage Example**: +```bash +stop_spinner +# Stops spinner and cleans up +``` + +### Message Functions + +#### `msg_info()` +**Purpose**: Display informational message with spinner +**Parameters**: `$1` - Message text +**Returns**: None +**Side Effects**: +- Starts spinner if not in verbose mode +- Tracks shown messages to prevent duplicates +- Displays message with hourglass icon in verbose mode +**Dependencies**: `spinner()`, `is_verbose_mode()`, `is_alpine()` +**Environment Variables Used**: `MSG_INFO_SHOWN` + +**Usage Example**: +```bash +msg_info "Installing package..." +# Shows spinner with message +``` + +#### `msg_ok()` +**Purpose**: Display success message +**Parameters**: `$1` - Success message text +**Returns**: None +**Side Effects**: +- Stops spinner +- Displays green checkmark with message +- Removes message from shown tracking +**Dependencies**: `stop_spinner()` +**Environment Variables Used**: `MSG_INFO_SHOWN` + +**Usage Example**: +```bash +msg_ok "Package installed successfully" +# Shows green checkmark with message +``` + +#### `msg_error()` +**Purpose**: Display error message +**Parameters**: `$1` - Error message text +**Returns**: None +**Side Effects**: +- Stops spinner +- Displays red cross with message +**Dependencies**: `stop_spinner()` +**Environment Variables Used**: None + +**Usage Example**: +```bash +msg_error "Installation failed" +# Shows red cross with message +``` + +#### `msg_warn()` +**Purpose**: Display warning message +**Parameters**: `$1` - Warning message text +**Returns**: None +**Side Effects**: +- Stops spinner +- Displays yellow info icon with message +**Dependencies**: `stop_spinner()` +**Environment Variables Used**: None + +**Usage Example**: +```bash +msg_warn "This operation may take some time" +# Shows yellow info icon with message +``` + +#### `msg_custom()` +**Purpose**: Display custom message with specified symbol and color +**Parameters**: +- `$1` - Custom symbol (default: "[*]") +- `$2` - Color code (default: "\e[36m") +- `$3` - Message text +**Returns**: None +**Side Effects**: +- Stops spinner +- Displays custom formatted message +**Dependencies**: `stop_spinner()` +**Environment Variables Used**: None + +**Usage Example**: +```bash +msg_custom "⚡" "\e[33m" "Custom warning message" +# Shows custom symbol and color with message +``` + +#### `msg_debug()` +**Purpose**: Display debug message if debug mode enabled +**Parameters**: `$*` - Debug message text +**Returns**: None +**Side Effects**: +- Only displays if var_full_verbose is set +- Shows timestamp and debug prefix +**Dependencies**: None +**Environment Variables Used**: `var_full_verbose`, `var_verbose` + +**Usage Example**: +```bash +export var_full_verbose=1 +msg_debug "Debug information here" +# Shows debug message with timestamp +``` + +### System Management Functions + +#### `check_or_create_swap()` +**Purpose**: Check for active swap and optionally create swap file +**Parameters**: None +**Returns**: 0 if swap exists or created, 1 if skipped +**Side Effects**: +- Checks for active swap +- Prompts user to create swap if none found +- Creates swap file if user confirms +**Dependencies**: `swapon`, `dd`, `mkswap` commands +**Environment Variables Used**: None + +**Usage Example**: +```bash +if check_or_create_swap; then + echo "Swap is available" +else + echo "No swap available" +fi +``` + +## Function Call Hierarchy + +### Initialization Flow +``` +load_functions() +├── color() +├── formatting() +├── icons() +├── default_vars() +└── set_std_mode() +``` + +### Message System Flow +``` +msg_info() +├── is_verbose_mode() +├── is_alpine() +├── spinner() +└── color_spinner() + +msg_ok() +├── stop_spinner() +└── clear_line() + +msg_error() +└── stop_spinner() + +msg_warn() +└── stop_spinner() +``` + +### System Check Flow +``` +pve_check() +├── pveversion command +└── version parsing + +arch_check() +├── dpkg command +└── architecture check + +shell_check() +├── ps command +└── shell detection + +root_check() +├── id command +└── parent process check +``` + +### Silent Execution Flow +``` +silent() +├── Command execution +├── Output redirection +├── Error handling +├── error_handler.func loading +└── Log management +``` + +## Error Handling Patterns + +### System Check Errors +- All system check functions exit with appropriate error messages +- Clear indication of what's wrong and how to fix it +- Graceful exit with sleep delay for user to read message + +### Silent Execution Errors +- Commands executed via `silent()` capture output to log file +- On failure, displays error code explanation +- Shows last 10 lines of log output +- Provides command to view full log + +### Spinner Errors +- Spinner functions handle process cleanup on exit +- Trap handlers ensure spinners are stopped +- Terminal settings are restored on error + +## Environment Variable Dependencies + +### Required Variables +- `APP`: Application name for header display +- `APP_TYPE`: Application type (ct/vm) for header paths +- `VERBOSE`: Verbose mode setting + +### Optional Variables +- `var_os`: OS type for Alpine detection +- `PCT_OSTYPE`: Alternative OS type variable +- `var_verbose`: Alternative verbose setting +- `var_full_verbose`: Debug mode setting + +### Internal Variables +- `_CORE_FUNC_LOADED`: Prevents multiple loading +- `__FUNCTIONS_LOADED`: Prevents multiple function loading +- `SILENT_LOGFILE`: Silent execution log file path +- `SPINNER_PID`: Spinner process ID +- `SPINNER_MSG`: Spinner message text +- `MSG_INFO_SHOWN`: Tracks shown info messages diff --git a/docs/misc/core.func/CORE_INTEGRATION.md b/docs/misc/core.func/CORE_INTEGRATION.md new file mode 100644 index 000000000..b203f1c73 --- /dev/null +++ b/docs/misc/core.func/CORE_INTEGRATION.md @@ -0,0 +1,517 @@ +# core.func Integration Guide + +## Overview + +This document describes how `core.func` integrates with other components in the Proxmox Community Scripts project, including dependencies, data flow, and API surface. + +## Dependencies + +### External Dependencies + +#### Required Commands +- **`pveversion`**: Proxmox VE version checking +- **`dpkg`**: Architecture detection +- **`ps`**: Process and shell detection +- **`id`**: User ID checking +- **`curl`**: Header file downloading +- **`swapon`**: Swap status checking +- **`dd`**: Swap file creation +- **`mkswap`**: Swap file formatting + +#### Optional Commands +- **`tput`**: Terminal control (installed if missing) +- **`apk`**: Alpine package manager +- **`apt-get`**: Debian package manager + +### Internal Dependencies + +#### error_handler.func +- **Purpose**: Provides error code explanations for silent execution +- **Usage**: Automatically loaded when `silent()` encounters errors +- **Integration**: Called via `explain_exit_code()` function +- **Data Flow**: Error code → explanation → user display + +## Integration Points + +### With build.func + +#### System Validation +```bash +# build.func uses core.func for system checks +source core.func +pve_check +arch_check +shell_check +root_check +``` + +#### User Interface +```bash +# build.func uses core.func for UI elements +msg_info "Creating container..." +msg_ok "Container created successfully" +msg_error "Container creation failed" +``` + +#### Silent Execution +```bash +# build.func uses core.func for command execution +silent pct create "$CTID" "$TEMPLATE" \ + --hostname "$HOSTNAME" \ + --memory "$MEMORY" \ + --cores "$CORES" +``` + +### With tools.func + +#### Utility Functions +```bash +# tools.func uses core.func utilities +source core.func + +# System checks +pve_check +root_check + +# UI elements +msg_info "Running maintenance tasks..." +msg_ok "Maintenance completed" +``` + +#### Error Handling +```bash +# tools.func uses core.func for error handling +if silent systemctl restart service; then + msg_ok "Service restarted" +else + msg_error "Service restart failed" +fi +``` + +### With api.func + +#### System Validation +```bash +# api.func uses core.func for system checks +source core.func +pve_check +root_check +``` + +#### API Operations +```bash +# api.func uses core.func for API calls +msg_info "Connecting to Proxmox API..." +if silent curl -k -H "Authorization: PVEAPIToken=$API_TOKEN" \ + "$API_URL/api2/json/nodes/$NODE/lxc"; then + msg_ok "API connection successful" +else + msg_error "API connection failed" +fi +``` + +### With error_handler.func + +#### Error Explanations +```bash +# error_handler.func provides explanations for core.func +explain_exit_code() { + local code="$1" + case "$code" in + 1) echo "General error" ;; + 2) echo "Misuse of shell builtins" ;; + 126) echo "Command invoked cannot execute" ;; + 127) echo "Command not found" ;; + 128) echo "Invalid argument to exit" ;; + *) echo "Unknown error code" ;; + esac +} +``` + +### With install.func + +#### Installation Process +```bash +# install.func uses core.func for installation +source core.func + +# System checks +pve_check +root_check + +# Installation steps +msg_info "Installing packages..." +silent apt-get update +silent apt-get install -y package + +msg_ok "Installation completed" +``` + +### With alpine-install.func + +#### Alpine-Specific Operations +```bash +# alpine-install.func uses core.func for Alpine operations +source core.func + +# Alpine detection +if is_alpine; then + msg_info "Detected Alpine Linux" + silent apk add --no-cache package +else + msg_info "Detected Debian-based system" + silent apt-get install -y package +fi +``` + +### With alpine-tools.func + +#### Alpine Utilities +```bash +# alpine-tools.func uses core.func for Alpine tools +source core.func + +# Alpine-specific operations +if is_alpine; then + msg_info "Running Alpine-specific operations..." + # Alpine tools logic + msg_ok "Alpine operations completed" +fi +``` + +### With passthrough.func + +#### Hardware Passthrough +```bash +# passthrough.func uses core.func for hardware operations +source core.func + +# System checks +pve_check +root_check + +# Hardware operations +msg_info "Configuring GPU passthrough..." +if silent lspci | grep -i nvidia; then + msg_ok "NVIDIA GPU detected" +else + msg_warn "No NVIDIA GPU found" +fi +``` + +### With vm-core.func + +#### VM Operations +```bash +# vm-core.func uses core.func for VM management +source core.func + +# System checks +pve_check +root_check + +# VM operations +msg_info "Creating virtual machine..." +silent qm create "$VMID" \ + --name "$VMNAME" \ + --memory "$MEMORY" \ + --cores "$CORES" + +msg_ok "Virtual machine created" +``` + +## Data Flow + +### Input Data + +#### Environment Variables +- **`APP`**: Application name for header display +- **`APP_TYPE`**: Application type (ct/vm) for header paths +- **`VERBOSE`**: Verbose mode setting +- **`var_os`**: OS type for Alpine detection +- **`PCT_OSTYPE`**: Alternative OS type variable +- **`var_verbose`**: Alternative verbose setting +- **`var_full_verbose`**: Debug mode setting + +#### Command Parameters +- **Function arguments**: Passed to individual functions +- **Command arguments**: Passed to `silent()` function +- **User input**: Collected via `read` commands + +### Processing Data + +#### System Information +- **Proxmox version**: Parsed from `pveversion` output +- **Architecture**: Retrieved from `dpkg --print-architecture` +- **Shell type**: Detected from process information +- **User ID**: Retrieved from `id -u` +- **SSH connection**: Detected from `SSH_CLIENT` environment + +#### UI State +- **Message tracking**: `MSG_INFO_SHOWN` associative array +- **Spinner state**: `SPINNER_PID` and `SPINNER_MSG` variables +- **Terminal state**: Cursor position and display mode + +#### Error Information +- **Exit codes**: Captured from command execution +- **Log output**: Redirected to temporary log files +- **Error explanations**: Retrieved from error_handler.func + +### Output Data + +#### User Interface +- **Colored messages**: ANSI color codes for terminal output +- **Icons**: Symbolic representations for different message types +- **Spinners**: Animated progress indicators +- **Formatted text**: Consistent message formatting + +#### System State +- **Exit codes**: Returned from functions +- **Log files**: Created for silent execution +- **Configuration**: Modified system settings +- **Process state**: Spinner processes and cleanup + +## API Surface + +### Public Functions + +#### System Validation +- **`pve_check()`**: Proxmox VE version validation +- **`arch_check()`**: Architecture validation +- **`shell_check()`**: Shell validation +- **`root_check()`**: Privilege validation +- **`ssh_check()`**: SSH connection warning + +#### User Interface +- **`msg_info()`**: Informational messages +- **`msg_ok()`**: Success messages +- **`msg_error()`**: Error messages +- **`msg_warn()`**: Warning messages +- **`msg_custom()`**: Custom messages +- **`msg_debug()`**: Debug messages + +#### Spinner Control +- **`spinner()`**: Start spinner animation +- **`stop_spinner()`**: Stop spinner and cleanup +- **`clear_line()`**: Clear current terminal line + +#### Silent Execution +- **`silent()`**: Execute commands with error handling + +#### Utility Functions +- **`is_alpine()`**: Alpine Linux detection +- **`is_verbose_mode()`**: Verbose mode detection +- **`fatal()`**: Fatal error handling +- **`ensure_tput()`**: Terminal control setup + +#### Header Management +- **`get_header()`**: Download application headers +- **`header_info()`**: Display header information + +#### System Management +- **`check_or_create_swap()`**: Swap file management + +### Internal Functions + +#### Initialization +- **`load_functions()`**: Function loader +- **`color()`**: Color setup +- **`formatting()`**: Formatting setup +- **`icons()`**: Icon setup +- **`default_vars()`**: Default variables +- **`set_std_mode()`**: Standard mode setup + +#### Color Management +- **`color_spinner()`**: Spinner colors + +### Global Variables + +#### Color Variables +- **`YW`**, **`YWB`**, **`BL`**, **`RD`**, **`BGN`**, **`GN`**, **`DGN`**, **`CL`**: Color codes +- **`CS_YW`**, **`CS_YWB`**, **`CS_CL`**: Spinner colors + +#### Formatting Variables +- **`BFR`**, **`BOLD`**, **`HOLD`**, **`TAB`**, **`TAB3`**: Formatting helpers + +#### Icon Variables +- **`CM`**, **`CROSS`**, **`INFO`**, **`OS`**, **`OSVERSION`**, etc.: Message icons + +#### Configuration Variables +- **`RETRY_NUM`**, **`RETRY_EVERY`**: Retry settings +- **`STD`**: Standard mode setting +- **`SILENT_LOGFILE`**: Log file path + +#### State Variables +- **`_CORE_FUNC_LOADED`**: Loading prevention +- **`__FUNCTIONS_LOADED`**: Function loading prevention +- **`SPINNER_PID`**, **`SPINNER_MSG`**: Spinner state +- **`MSG_INFO_SHOWN`**: Message tracking + +## Integration Patterns + +### Standard Integration Pattern + +```bash +#!/usr/bin/env bash +# Standard integration pattern + +# 1. Source core.func first +source core.func + +# 2. Run system checks +pve_check +arch_check +shell_check +root_check + +# 3. Set up error handling +trap 'stop_spinner' EXIT INT TERM + +# 4. Use UI functions +msg_info "Starting operation..." + +# 5. Use silent execution +silent command + +# 6. Show completion +msg_ok "Operation completed" +``` + +### Minimal Integration Pattern + +```bash +#!/usr/bin/env bash +# Minimal integration pattern + +source core.func +pve_check +root_check + +msg_info "Running operation..." +silent command +msg_ok "Operation completed" +``` + +### Advanced Integration Pattern + +```bash +#!/usr/bin/env bash +# Advanced integration pattern + +source core.func + +# System validation +pve_check +arch_check +shell_check +root_check +ssh_check + +# Error handling +trap 'stop_spinner' EXIT INT TERM + +# Verbose mode handling +if is_verbose_mode; then + msg_info "Verbose mode enabled" +fi + +# OS-specific operations +if is_alpine; then + msg_info "Alpine Linux detected" + # Alpine-specific logic +else + msg_info "Debian-based system detected" + # Debian-specific logic +fi + +# Operation execution +msg_info "Starting operation..." +if silent command; then + msg_ok "Operation succeeded" +else + msg_error "Operation failed" + exit 1 +fi +``` + +## Error Handling Integration + +### Silent Execution Error Flow + +``` +silent() command +├── Execute command +├── Capture output to log +├── Check exit code +├── If error: +│ ├── Load error_handler.func +│ ├── Get error explanation +│ ├── Display error details +│ ├── Show log excerpt +│ └── Exit with error code +└── If success: Continue +``` + +### System Check Error Flow + +``` +System Check Function +├── Check system state +├── If valid: Return 0 +└── If invalid: + ├── Display error message + ├── Show fix instructions + ├── Sleep for user to read + └── Exit with error code +``` + +## Performance Considerations + +### Loading Optimization +- **Single Loading**: `_CORE_FUNC_LOADED` prevents multiple loading +- **Function Loading**: `__FUNCTIONS_LOADED` prevents multiple function loading +- **Lazy Loading**: Functions loaded only when needed + +### Memory Usage +- **Minimal Footprint**: Core functions use minimal memory +- **Variable Reuse**: Global variables reused across functions +- **Cleanup**: Spinner processes cleaned up on exit + +### Execution Speed +- **Fast Checks**: System checks are optimized for speed +- **Efficient Spinners**: Spinner animation uses minimal CPU +- **Quick Messages**: Message functions optimized for performance + +## Security Considerations + +### Privilege Escalation +- **Root Check**: Ensures script runs with sufficient privileges +- **Shell Check**: Validates shell environment +- **Process Validation**: Checks parent process for sudo usage + +### Input Validation +- **Parameter Checking**: Functions validate input parameters +- **Error Handling**: Proper error handling prevents crashes +- **Safe Execution**: Silent execution with proper error handling + +### System Protection +- **Version Validation**: Ensures compatible Proxmox version +- **Architecture Check**: Prevents execution on unsupported systems +- **SSH Warning**: Warns about external SSH usage + +## Future Integration Considerations + +### Extensibility +- **Function Groups**: Easy to add new function groups +- **Message Types**: Easy to add new message types +- **System Checks**: Easy to add new system checks + +### Compatibility +- **Version Support**: Easy to add new Proxmox versions +- **OS Support**: Easy to add new operating systems +- **Architecture Support**: Easy to add new architectures + +### Performance +- **Optimization**: Functions can be optimized for better performance +- **Caching**: Results can be cached for repeated operations +- **Parallelization**: Operations can be parallelized where appropriate diff --git a/docs/misc/core.func/CORE_USAGE_EXAMPLES.md b/docs/misc/core.func/CORE_USAGE_EXAMPLES.md new file mode 100644 index 000000000..c702bd2ed --- /dev/null +++ b/docs/misc/core.func/CORE_USAGE_EXAMPLES.md @@ -0,0 +1,728 @@ +# core.func Usage Examples + +## Overview + +This document provides practical usage examples for `core.func` functions, covering common scenarios, integration patterns, and best practices. + +## Basic Script Setup + +### Standard Script Initialization + +```bash +#!/usr/bin/env bash +# Standard script setup using core.func + +# Source core functions +source core.func + +# Run system checks +pve_check +arch_check +shell_check +root_check + +# Optional: Check SSH connection +ssh_check + +# Set up error handling +trap 'stop_spinner' EXIT INT TERM + +# Your script logic here +msg_info "Starting script execution" +# ... script code ... +msg_ok "Script completed successfully" +``` + +### Minimal Script Setup + +```bash +#!/usr/bin/env bash +# Minimal setup for simple scripts + +source core.func + +# Basic checks only +pve_check +root_check + +# Simple execution +msg_info "Running operation" +# ... your code ... +msg_ok "Operation completed" +``` + +## Message Display Examples + +### Progress Indication + +```bash +#!/usr/bin/env bash +source core.func + +# Show progress with spinner +msg_info "Downloading package..." +sleep 2 +msg_ok "Download completed" + +msg_info "Installing package..." +sleep 3 +msg_ok "Installation completed" + +msg_info "Configuring service..." +sleep 1 +msg_ok "Configuration completed" +``` + +### Error Handling + +```bash +#!/usr/bin/env bash +source core.func + +# Function with error handling +install_package() { + local package="$1" + + msg_info "Installing $package..." + + if silent apt-get install -y "$package"; then + msg_ok "$package installed successfully" + return 0 + else + msg_error "Failed to install $package" + return 1 + fi +} + +# Usage +if install_package "nginx"; then + msg_ok "Nginx installation completed" +else + msg_error "Nginx installation failed" + exit 1 +fi +``` + +### Warning Messages + +```bash +#!/usr/bin/env bash +source core.func + +# Show warnings for potentially dangerous operations +msg_warn "This will modify system configuration" +read -p "Continue? [y/N]: " confirm + +if [[ "$confirm" =~ ^[yY]$ ]]; then + msg_info "Proceeding with modification..." + # ... dangerous operation ... + msg_ok "Modification completed" +else + msg_info "Operation cancelled" +fi +``` + +### Custom Messages + +```bash +#!/usr/bin/env bash +source core.func + +# Custom message with specific icon and color +msg_custom "🚀" "\e[32m" "Launching application" +msg_custom "⚡" "\e[33m" "High performance mode enabled" +msg_custom "🔒" "\e[31m" "Security mode activated" +``` + +### Debug Messages + +```bash +#!/usr/bin/env bash +source core.func + +# Enable debug mode +export var_full_verbose=1 + +# Debug messages +msg_debug "Variable value: $some_variable" +msg_debug "Function called: $FUNCNAME" +msg_debug "Current directory: $(pwd)" +``` + +## Silent Execution Examples + +### Package Management + +```bash +#!/usr/bin/env bash +source core.func + +# Update package lists +msg_info "Updating package lists..." +silent apt-get update + +# Install packages +msg_info "Installing required packages..." +silent apt-get install -y curl wget git + +# Upgrade packages +msg_info "Upgrading packages..." +silent apt-get upgrade -y + +msg_ok "Package management completed" +``` + +### File Operations + +```bash +#!/usr/bin/env bash +source core.func + +# Create directories +msg_info "Creating directory structure..." +silent mkdir -p /opt/myapp/{config,logs,data} + +# Set permissions +msg_info "Setting permissions..." +silent chmod 755 /opt/myapp +silent chmod 644 /opt/myapp/config/* + +# Copy files +msg_info "Copying configuration files..." +silent cp config/* /opt/myapp/config/ + +msg_ok "File operations completed" +``` + +### Service Management + +```bash +#!/usr/bin/env bash +source core.func + +# Start service +msg_info "Starting service..." +silent systemctl start myservice + +# Enable service +msg_info "Enabling service..." +silent systemctl enable myservice + +# Check service status +msg_info "Checking service status..." +if silent systemctl is-active --quiet myservice; then + msg_ok "Service is running" +else + msg_error "Service failed to start" +fi +``` + +### Network Operations + +```bash +#!/usr/bin/env bash +source core.func + +# Test network connectivity +msg_info "Testing network connectivity..." +if silent ping -c 1 8.8.8.8; then + msg_ok "Network connectivity confirmed" +else + msg_error "Network connectivity failed" +fi + +# Download files +msg_info "Downloading configuration..." +silent curl -fsSL https://example.com/config -o /tmp/config + +# Extract archives +msg_info "Extracting archive..." +silent tar -xzf /tmp/archive.tar.gz -C /opt/ +``` + +## System Check Examples + +### Comprehensive System Validation + +```bash +#!/usr/bin/env bash +source core.func + +# Complete system validation +validate_system() { + msg_info "Validating system requirements..." + + # Check Proxmox version + if pve_check; then + msg_ok "Proxmox VE version is supported" + fi + + # Check architecture + if arch_check; then + msg_ok "System architecture is supported" + fi + + # Check shell + if shell_check; then + msg_ok "Shell environment is correct" + fi + + # Check privileges + if root_check; then + msg_ok "Running with sufficient privileges" + fi + + # Check SSH connection + ssh_check + + msg_ok "System validation completed" +} + +# Run validation +validate_system +``` + +### Conditional System Checks + +```bash +#!/usr/bin/env bash +source core.func + +# Check if running in container +if [[ -f /.dockerenv ]] || [[ -f /run/.containerenv ]]; then + msg_warn "Running inside container" + # Skip some checks +else + # Full system checks + pve_check + arch_check +fi + +# Always check shell and privileges +shell_check +root_check +``` + +## Header Management Examples + +### Application Header Display + +```bash +#!/usr/bin/env bash +source core.func + +# Set application information +export APP="plex" +export APP_TYPE="ct" + +# Display header +header_info + +# Continue with application setup +msg_info "Setting up Plex Media Server..." +``` + +### Custom Header Handling + +```bash +#!/usr/bin/env bash +source core.func + +# Get header content +export APP="nextcloud" +export APP_TYPE="ct" + +header_content=$(get_header) +if [[ -n "$header_content" ]]; then + echo "Header found:" + echo "$header_content" +else + msg_warn "No header found for $APP" +fi +``` + +## Swap Management Examples + +### Interactive Swap Creation + +```bash +#!/usr/bin/env bash +source core.func + +# Check and create swap +if check_or_create_swap; then + msg_ok "Swap is available" +else + msg_warn "No swap available - continuing without swap" +fi +``` + +### Automated Swap Check + +```bash +#!/usr/bin/env bash +source core.func + +# Check swap without prompting +check_swap_quiet() { + if swapon --noheadings --show | grep -q 'swap'; then + msg_ok "Swap is active" + return 0 + else + msg_warn "No active swap detected" + return 1 + fi +} + +if check_swap_quiet; then + msg_info "System has sufficient swap" +else + msg_warn "Consider adding swap for better performance" +fi +``` + +## Spinner Usage Examples + +### Long-Running Operations + +```bash +#!/usr/bin/env bash +source core.func + +# Long-running operation with spinner +long_operation() { + msg_info "Processing large dataset..." + + # Simulate long operation + for i in {1..100}; do + sleep 0.1 + # Update spinner message periodically + if (( i % 20 == 0 )); then + SPINNER_MSG="Processing... $i%" + fi + done + + msg_ok "Dataset processing completed" +} + +long_operation +``` + +### Background Operations + +```bash +#!/usr/bin/env bash +source core.func + +# Background operation with spinner +background_operation() { + msg_info "Starting background process..." + + # Start spinner + SPINNER_MSG="Processing in background..." + spinner & + SPINNER_PID=$! + + # Do background work + sleep 5 + + # Stop spinner + stop_spinner + msg_ok "Background process completed" +} + +background_operation +``` + +## Integration Examples + +### With build.func + +```bash +#!/usr/bin/env bash +# Integration with build.func + +source core.func +source build.func + +# Use core functions for system validation +pve_check +arch_check +root_check + +# Use build.func for container creation +export APP="plex" +export CTID="100" +# ... container creation ... +``` + +### With tools.func + +```bash +#!/usr/bin/env bash +# Integration with tools.func + +source core.func +source tools.func + +# Use core functions for UI +msg_info "Starting maintenance tasks..." + +# Use tools.func for maintenance +update_system +cleanup_logs +optimize_storage + +msg_ok "Maintenance completed" +``` + +### With error_handler.func + +```bash +#!/usr/bin/env bash +# Integration with error_handler.func + +source core.func +source error_handler.func + +# Use core functions for execution +msg_info "Running operation..." + +# Silent execution will use error_handler for explanations +silent apt-get install -y package + +msg_ok "Operation completed" +``` + +## Best Practices Examples + +### Error Handling Pattern + +```bash +#!/usr/bin/env bash +source core.func + +# Robust error handling +run_with_error_handling() { + local operation="$1" + local description="$2" + + msg_info "$description" + + if silent "$operation"; then + msg_ok "$description completed successfully" + return 0 + else + msg_error "$description failed" + return 1 + fi +} + +# Usage +run_with_error_handling "apt-get update" "Package list update" +run_with_error_handling "apt-get install -y nginx" "Nginx installation" +``` + +### Verbose Mode Handling + +```bash +#!/usr/bin/env bash +source core.func + +# Handle verbose mode +if is_verbose_mode; then + msg_info "Verbose mode enabled - showing detailed output" + # Show more information +else + msg_info "Normal mode - showing minimal output" + # Show less information +fi +``` + +### Alpine Linux Detection + +```bash +#!/usr/bin/env bash +source core.func + +# Handle different OS types +if is_alpine; then + msg_info "Detected Alpine Linux" + # Use Alpine-specific commands + silent apk add --no-cache package +else + msg_info "Detected Debian-based system" + # Use Debian-specific commands + silent apt-get install -y package +fi +``` + +### Conditional Execution + +```bash +#!/usr/bin/env bash +source core.func + +# Conditional execution based on system state +if [[ -f /etc/nginx/nginx.conf ]]; then + msg_warn "Nginx configuration already exists" + read -p "Overwrite? [y/N]: " overwrite + if [[ "$overwrite" =~ ^[yY]$ ]]; then + msg_info "Overwriting configuration..." + # ... overwrite logic ... + else + msg_info "Skipping configuration" + fi +else + msg_info "Creating new Nginx configuration..." + # ... create logic ... +fi +``` + +## Advanced Usage Examples + +### Custom Spinner Messages + +```bash +#!/usr/bin/env bash +source core.func + +# Custom spinner with progress +download_with_progress() { + local url="$1" + local file="$2" + + msg_info "Starting download..." + + # Start spinner + SPINNER_MSG="Downloading..." + spinner & + SPINNER_PID=$! + + # Download with progress + curl -L "$url" -o "$file" --progress-bar + + # Stop spinner + stop_spinner + msg_ok "Download completed" +} + +download_with_progress "https://example.com/file.tar.gz" "/tmp/file.tar.gz" +``` + +### Message Deduplication + +```bash +#!/usr/bin/env bash +source core.func + +# Messages are automatically deduplicated +for i in {1..5}; do + msg_info "Processing item $i" + # This message will only show once +done + +# Different messages will show separately +msg_info "Starting phase 1" +msg_info "Starting phase 2" +msg_info "Starting phase 3" +``` + +### Terminal Control + +```bash +#!/usr/bin/env bash +source core.func + +# Ensure terminal control is available +ensure_tput + +# Use terminal control +clear_line +echo "This line will be cleared" +clear_line +echo "New content" +``` + +## Troubleshooting Examples + +### Debug Mode + +```bash +#!/usr/bin/env bash +source core.func + +# Enable debug mode +export var_full_verbose=1 +export VERBOSE="yes" + +# Debug information +msg_debug "Script started" +msg_debug "Current user: $(whoami)" +msg_debug "Current directory: $(pwd)" +msg_debug "Environment variables: $(env | grep -E '^(APP|CTID|VERBOSE)')" +``` + +### Silent Execution Debugging + +```bash +#!/usr/bin/env bash +source core.func + +# Debug silent execution +debug_silent() { + local cmd="$1" + local log_file="/tmp/debug.$$.log" + + echo "Command: $cmd" > "$log_file" + echo "Timestamp: $(date)" >> "$log_file" + echo "Working directory: $(pwd)" >> "$log_file" + echo "Environment:" >> "$log_file" + env >> "$log_file" + echo "--- Command Output ---" >> "$log_file" + + if silent "$cmd"; then + msg_ok "Command succeeded" + else + msg_error "Command failed - check $log_file for details" + fi +} + +debug_silent "apt-get update" +``` + +### Error Recovery + +```bash +#!/usr/bin/env bash +source core.func + +# Error recovery pattern +retry_operation() { + local max_attempts=3 + local attempt=1 + + while [[ $attempt -le $max_attempts ]]; do + msg_info "Attempt $attempt of $max_attempts" + + if silent "$@"; then + msg_ok "Operation succeeded on attempt $attempt" + return 0 + else + msg_warn "Attempt $attempt failed" + ((attempt++)) + + if [[ $attempt -le $max_attempts ]]; then + msg_info "Retrying in 5 seconds..." + sleep 5 + fi + fi + done + + msg_error "Operation failed after $max_attempts attempts" + return 1 +} + +# Usage +retry_operation "apt-get install -y package" +``` diff --git a/docs/misc/core.func/README.md b/docs/misc/core.func/README.md new file mode 100644 index 000000000..52c62af6e --- /dev/null +++ b/docs/misc/core.func/README.md @@ -0,0 +1,181 @@ +# core.func Documentation + +## Overview + +The `core.func` file provides fundamental utility functions and system checks that form the foundation for all other scripts in the Proxmox Community Scripts project. It handles basic system operations, user interface elements, validation, and core infrastructure. + +## Purpose and Use Cases + +- **System Validation**: Checks for Proxmox VE compatibility, architecture, shell requirements +- **User Interface**: Provides colored output, icons, spinners, and formatted messages +- **Core Utilities**: Basic functions used across all scripts +- **Error Handling**: Silent execution with detailed error reporting +- **System Information**: OS detection, verbose mode handling, swap management + +## Quick Reference + +### Key Function Groups +- **System Checks**: `pve_check()`, `arch_check()`, `shell_check()`, `root_check()` +- **User Interface**: `msg_info()`, `msg_ok()`, `msg_error()`, `msg_warn()`, `spinner()` +- **Core Utilities**: `silent()`, `is_alpine()`, `is_verbose_mode()`, `get_header()` +- **System Management**: `check_or_create_swap()`, `ensure_tput()` + +### Dependencies +- **External**: `curl` for downloading headers, `tput` for terminal control +- **Internal**: `error_handler.func` for error explanations + +### Integration Points +- Used by: All other `.func` files and installation scripts +- Uses: `error_handler.func` for error explanations +- Provides: Core utilities for `build.func`, `tools.func`, `api.func` + +## Documentation Files + +### 📊 [CORE_FLOWCHART.md](./CORE_FLOWCHART.md) +Visual execution flows showing how core functions interact and the system validation process. + +### 📚 [CORE_FUNCTIONS_REFERENCE.md](./CORE_FUNCTIONS_REFERENCE.md) +Complete alphabetical reference of all functions with parameters, dependencies, and usage details. + +### 💡 [CORE_USAGE_EXAMPLES.md](./CORE_USAGE_EXAMPLES.md) +Practical examples showing how to use core functions in scripts and common patterns. + +### 🔗 [CORE_INTEGRATION.md](./CORE_INTEGRATION.md) +How core.func integrates with other components and provides foundational services. + +## Key Features + +### System Validation +- **Proxmox VE Version Check**: Supports PVE 8.0-8.9 and 9.0 +- **Architecture Check**: Ensures AMD64 architecture (excludes PiMox) +- **Shell Check**: Validates Bash shell usage +- **Root Check**: Ensures root privileges +- **SSH Check**: Warns about external SSH usage + +### User Interface +- **Colored Output**: ANSI color codes for styled terminal output +- **Icons**: Symbolic icons for different message types +- **Spinners**: Animated progress indicators +- **Formatted Messages**: Consistent message formatting across scripts + +### Core Utilities +- **Silent Execution**: Execute commands with detailed error reporting +- **OS Detection**: Alpine Linux detection +- **Verbose Mode**: Handle verbose output settings +- **Header Management**: Download and display application headers +- **Swap Management**: Check and create swap files + +## Common Usage Patterns + +### Basic Script Setup +```bash +# Source core functions +source core.func + +# Run system checks +pve_check +arch_check +shell_check +root_check +``` + +### Message Display +```bash +# Show progress +msg_info "Installing package..." + +# Show success +msg_ok "Package installed successfully" + +# Show error +msg_error "Installation failed" + +# Show warning +msg_warn "This operation may take some time" +``` + +### Silent Command Execution +```bash +# Execute command silently with error handling +silent apt-get update +silent apt-get install -y package-name +``` + +## Environment Variables + +### Core Variables +- `VERBOSE`: Enable verbose output mode +- `SILENT_LOGFILE`: Path to silent execution log file +- `APP`: Application name for header display +- `APP_TYPE`: Application type (ct/vm) for header paths + +### Internal Variables +- `_CORE_FUNC_LOADED`: Prevents multiple loading +- `__FUNCTIONS_LOADED`: Prevents multiple function loading +- `RETRY_NUM`: Number of retry attempts (default: 10) +- `RETRY_EVERY`: Seconds between retries (default: 3) + +## Error Handling + +### Silent Execution Errors +- Commands executed via `silent()` capture output to log file +- On failure, displays error code explanation +- Shows last 10 lines of log output +- Provides command to view full log + +### System Check Failures +- Each system check function exits with appropriate error message +- Clear indication of what's wrong and how to fix it +- Graceful exit with sleep delay for user to read message + +## Best Practices + +### Script Initialization +1. Source `core.func` first +2. Run system checks early +3. Set up error handling +4. Use appropriate message functions + +### Message Usage +1. Use `msg_info()` for progress updates +2. Use `msg_ok()` for successful completions +3. Use `msg_error()` for failures +4. Use `msg_warn()` for warnings + +### Silent Execution +1. Use `silent()` for commands that might fail +2. Check return codes after silent execution +3. Provide meaningful error messages + +## Troubleshooting + +### Common Issues +1. **Proxmox Version**: Ensure running supported PVE version +2. **Architecture**: Script only works on AMD64 systems +3. **Shell**: Must use Bash shell +4. **Permissions**: Must run as root +5. **Network**: SSH warnings for external connections + +### Debug Mode +Enable verbose output for debugging: +```bash +export VERBOSE="yes" +source core.func +``` + +### Log Files +Check silent execution logs: +```bash +cat /tmp/silent.$$.log +``` + +## Related Documentation + +- [build.func](../build.func/) - Main container creation script +- [error_handler.func](../error_handler.func/) - Error handling utilities +- [tools.func](../tools.func/) - Extended utility functions +- [api.func](../api.func/) - Proxmox API interactions + +--- + +*This documentation covers the core.func file which provides fundamental utilities for all Proxmox Community Scripts.* diff --git a/docs/misc/error_handler.func/ERROR_HANDLER_FLOWCHART.md b/docs/misc/error_handler.func/ERROR_HANDLER_FLOWCHART.md new file mode 100644 index 000000000..984596d7f --- /dev/null +++ b/docs/misc/error_handler.func/ERROR_HANDLER_FLOWCHART.md @@ -0,0 +1,347 @@ +# error_handler.func Execution Flowchart + +## Main Error Handling Flow + +``` +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ Error Handler Initialization │ +│ Entry point when error_handler.func is sourced by other scripts │ +└─────────────────────┬───────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ CATCH_ERRORS() │ +│ Initialize error handling traps and strict mode │ +└─────────────────────┬───────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ Trap Setup Sequence │ +│ │ +│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────────────┐ │ +│ │ Set Strict │ │ Set Error │ │ Set Signal │ │ +│ │ Mode │ │ Trap │ │ Traps │ │ +│ │ │ │ │ │ │ │ +│ │ • -Ee │ │ • ERR trap │ │ • EXIT trap │ │ +│ │ • -o pipefail │ │ • error_handler │ │ • INT trap │ │ +│ │ • -u (if │ │ function │ │ • TERM trap │ │ +│ │ STRICT_UNSET) │ │ │ │ │ │ +│ └─────────────────┘ └─────────────────┘ └─────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────────┘ +``` + +## Error Handler Flow + +``` +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ ERROR_HANDLER() Flow │ +│ Main error handler triggered by ERR trap or manual call │ +└─────────────────────┬───────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ Error Detection │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ Error Information Collection │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Get Exit │ │ Get Command │ │ Get Line │ │ │ +│ │ │ Code │ │ Information │ │ Number │ │ │ +│ │ │ │ │ │ │ │ │ │ +│ │ │ • From $? or │ │ • From │ │ • From │ │ │ +│ │ │ parameter │ │ BASH_COMMAND │ │ BASH_LINENO[0] │ │ │ +│ │ │ • Store in │ │ • Clean $STD │ │ • Default to │ │ │ +│ │ │ exit_code │ │ references │ │ "unknown" │ │ │ +│ │ │ │ │ • Store in │ │ • Store in │ │ │ +│ │ │ │ │ command │ │ line_number │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ Success Check │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ Exit Code Validation │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Check Exit │ │ Success │ │ Error │ │ +│ │ │ Code │ │ Path │ │ Path │ │ +│ │ │ │ │ │ │ │ │ +│ │ │ • If exit_code │ │ • Return 0 │ │ • Continue to │ │ +│ │ │ == 0 │ │ • No error │ │ error handling │ │ +│ │ │ • Success │ │ processing │ │ • Process error │ │ +│ │ │ • No error │ │ │ │ information │ │ +│ │ │ handling │ │ │ │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ Error Processing │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ Error Explanation │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Get Error │ │ Display Error │ │ Log Error │ │ │ +│ │ │ Explanation │ │ Information │ │ Information │ │ │ +│ │ │ │ │ │ │ │ │ +│ │ │ • Call │ │ • Show error │ │ • Write to debug │ │ +│ │ │ explain_exit_ │ │ message │ │ log if enabled │ │ +│ │ │ code() │ │ • Show line │ │ • Include │ │ +│ │ │ • Get human- │ │ number │ │ timestamp │ │ +│ │ │ readable │ │ • Show command │ │ • Include exit │ │ +│ │ │ message │ │ • Show exit │ │ code │ │ +│ │ │ │ │ code │ │ • Include command │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ Silent Log Integration │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ Silent Log Display │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Check Silent │ │ Display Log │ │ Exit with │ │ +│ │ │ Log File │ │ Content │ │ Error Code │ │ +│ │ │ │ │ │ │ │ │ +│ │ │ • Check if │ │ • Show last 20 │ │ • Exit with │ │ +│ │ │ SILENT_ │ │ lines │ │ original exit │ │ +│ │ │ LOGFILE set │ │ • Show file │ │ code │ │ +│ │ │ • Check if │ │ path │ │ • Terminate script │ │ +│ │ │ file exists │ │ • Format │ │ execution │ │ +│ │ │ • Check if │ │ output │ │ │ │ +│ │ │ file has │ │ │ │ │ │ +│ │ │ content │ │ │ │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────────┘ +``` + +## Signal Handling Flow + +``` +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ Signal Handler Flow │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ Signal Detection │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ SIGINT │ │ SIGTERM │ │ EXIT │ │ │ +│ │ │ (Ctrl+C) │ │ (Termination) │ │ (Script End) │ │ │ +│ │ │ │ │ │ │ │ │ +│ │ │ • User │ │ • System │ │ • Normal script │ │ +│ │ │ interruption │ │ termination │ │ completion │ │ +│ │ │ • Graceful │ │ • Graceful │ │ • Error exit │ │ +│ │ │ handling │ │ handling │ │ • Signal exit │ │ +│ │ │ • Exit code │ │ • Exit code │ │ • Cleanup │ │ +│ │ │ 130 │ │ 143 │ │ operations │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ ON_INTERRUPT() Flow │ +│ Handles SIGINT (Ctrl+C) signals │ +└─────────────────────┬───────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ Interrupt Processing │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ User Interruption Handling │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Display │ │ Cleanup │ │ Exit with │ │ │ +│ │ │ Message │ │ Operations │ │ Code 130 │ │ │ +│ │ │ │ │ │ │ │ │ +│ │ │ • Show │ │ • Stop │ │ • Exit with │ │ +│ │ │ interruption │ │ processes │ │ SIGINT code │ │ +│ │ │ message │ │ • Clean up │ │ • Terminate script │ │ +│ │ │ • Use red │ │ temporary │ │ execution │ │ +│ │ │ color │ │ files │ │ │ │ +│ │ │ • Clear │ │ • Remove lock │ │ │ │ +│ │ │ terminal │ │ files │ │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────────┘ +``` + +## Exit Handler Flow + +``` +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ ON_EXIT() Flow │ +│ Handles script exit cleanup │ +└─────────────────────┬───────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ Exit Cleanup │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ Cleanup Operations │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Lock File │ │ Temporary │ │ Exit with │ │ │ +│ │ │ Cleanup │ │ File │ │ Original Code │ │ │ +│ │ │ │ │ Cleanup │ │ │ │ +│ │ │ • Check if │ │ • Remove │ │ • Exit with │ │ +│ │ │ lockfile │ │ temporary │ │ original exit │ │ +│ │ │ variable set │ │ files │ │ code │ │ +│ │ │ • Check if │ │ • Clean up │ │ • Preserve exit │ │ +│ │ │ lockfile │ │ process │ │ status │ │ +│ │ │ exists │ │ state │ │ • Terminate │ │ +│ │ │ • Remove │ │ │ │ execution │ │ +│ │ │ lockfile │ │ │ │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────────┘ +``` + +## Error Code Explanation Flow + +``` +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ EXPLAIN_EXIT_CODE() Flow │ +│ Converts numeric exit codes to human-readable explanations │ +└─────────────────────┬───────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ Error Code Classification │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ Error Code Categories │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Generic/ │ │ Package │ │ Node.js │ │ │ +│ │ │ Shell │ │ Manager │ │ Errors │ │ │ +│ │ │ Errors │ │ Errors │ │ │ │ +│ │ │ │ │ │ │ • 243: Out of │ │ +│ │ │ • 1: General │ │ • 100: APT │ │ memory │ │ +│ │ │ error │ │ package │ │ • 245: Invalid │ │ +│ │ │ • 2: Shell │ │ error │ │ option │ │ +│ │ │ builtin │ │ • 101: APT │ │ • 246: Parse │ │ +│ │ │ misuse │ │ config error │ │ error │ │ +│ │ │ • 126: Cannot │ │ • 255: DPKG │ │ • 247: Fatal │ │ +│ │ │ execute │ │ fatal error │ │ error │ │ +│ │ │ • 127: Command │ │ │ │ • 248: Addon │ │ +│ │ │ not found │ │ │ │ failure │ │ +│ │ │ • 128: Invalid │ │ │ │ • 249: Inspector │ │ +│ │ │ exit │ │ │ │ error │ │ +│ │ │ • 130: SIGINT │ │ │ │ • 254: Unknown │ │ +│ │ │ • 137: SIGKILL │ │ │ │ fatal error │ │ +│ │ │ • 139: Segfault │ │ │ │ │ │ +│ │ │ • 143: SIGTERM │ │ │ │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Python │ │ Database │ │ Proxmox │ │ │ +│ │ │ Errors │ │ Errors │ │ Custom │ │ │ +│ │ │ │ │ │ │ Errors │ │ +│ │ │ • 210: Virtual │ │ • PostgreSQL: │ │ • 200: Lock file │ │ +│ │ │ env missing │ │ 231-234 │ │ failed │ │ +│ │ │ • 211: Dep │ │ • MySQL: 241- │ │ • 203: Missing │ │ +│ │ │ resolution │ │ 244 │ │ CTID │ │ +│ │ │ • 212: Install │ │ • MongoDB: 251- │ │ • 204: Missing │ │ +│ │ │ aborted │ │ 254 │ │ PCT_OSTYPE │ │ +│ │ │ │ │ │ │ • 205: Invalid │ │ +│ │ │ │ │ │ │ CTID │ │ +│ │ │ │ │ │ │ • 209: Container │ │ +│ │ │ │ │ │ │ creation failed │ │ +│ │ │ │ │ │ │ • 210: Cluster │ │ +│ │ │ │ │ │ │ not quorate │ │ +│ │ │ │ │ │ │ • 214: No storage │ │ +│ │ │ │ │ │ │ space │ │ +│ │ │ │ │ │ │ • 215: CTID not │ │ +│ │ │ │ │ │ │ listed │ │ +│ │ │ │ │ │ │ • 216: RootFS │ │ +│ │ │ │ │ │ │ missing │ │ +│ │ │ │ │ │ │ • 217: Storage │ │ +│ │ │ │ │ │ │ not supported │ │ +│ │ │ │ │ │ │ • 220: Template │ │ +│ │ │ │ │ │ │ path error │ │ +│ │ │ │ │ │ │ • 222: Template │ │ +│ │ │ │ │ │ │ download failed │ │ +│ │ │ │ │ │ │ • 223: Template │ │ +│ │ │ │ │ │ │ not available │ │ +│ │ │ │ │ │ │ • 231: LXC stack │ │ +│ │ │ │ │ │ │ upgrade failed │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ Default Case │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ Unknown Error Handling │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Check for │ │ Return │ │ Log Unknown │ │ │ +│ │ │ Unknown │ │ Generic │ │ Error │ │ │ +│ │ │ Code │ │ Message │ │ │ │ +│ │ │ │ │ │ │ • Log to debug │ │ +│ │ │ • If no match │ │ • "Unknown │ │ file if enabled │ │ +│ │ │ found │ │ error" │ │ • Include error │ │ +│ │ │ • Use default │ │ • Return to │ │ code │ │ +│ │ │ case │ │ caller │ │ • Include │ │ +│ │ │ │ │ │ │ timestamp │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────────┘ +``` + +## Debug Logging Flow + +``` +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ Debug Log Integration │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ Debug Log Writing │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Check Debug │ │ Write Error │ │ Format Log │ │ │ +│ │ │ Log File │ │ Information │ │ Entry │ │ │ +│ │ │ │ │ │ │ │ │ +│ │ │ • Check if │ │ • Timestamp │ │ • Error separator │ │ +│ │ │ DEBUG_LOGFILE │ │ • Exit code │ │ • Structured │ │ +│ │ │ set │ │ • Explanation │ │ format │ │ +│ │ │ • Check if │ │ • Line number │ │ • Easy to parse │ │ +│ │ │ file exists │ │ • Command │ │ • Easy to read │ │ +│ │ │ • Check if │ │ • Append to │ │ │ │ +│ │ │ file writable │ │ file │ │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────────┘ +``` + +## Integration Points + +### With core.func +- **Silent Execution**: Provides error explanations for silent() function +- **Color Variables**: Uses color variables for error display +- **Log Integration**: Integrates with SILENT_LOGFILE + +### With Other Scripts +- **Error Traps**: Sets up ERR trap for automatic error handling +- **Signal Traps**: Handles SIGINT, SIGTERM, and EXIT signals +- **Cleanup**: Provides cleanup on script exit + +### External Dependencies +- **None**: Pure Bash implementation +- **Color Support**: Requires color variables from core.func +- **Log Files**: Uses standard file operations diff --git a/docs/misc/error_handler.func/ERROR_HANDLER_FUNCTIONS_REFERENCE.md b/docs/misc/error_handler.func/ERROR_HANDLER_FUNCTIONS_REFERENCE.md new file mode 100644 index 000000000..2270ffb76 --- /dev/null +++ b/docs/misc/error_handler.func/ERROR_HANDLER_FUNCTIONS_REFERENCE.md @@ -0,0 +1,424 @@ +# error_handler.func Functions Reference + +## Overview + +This document provides a comprehensive alphabetical reference of all functions in `error_handler.func`, including parameters, dependencies, usage examples, and error handling. + +## Function Categories + +### Error Explanation Functions + +#### `explain_exit_code()` +**Purpose**: Convert numeric exit codes to human-readable explanations +**Parameters**: +- `$1` - Exit code to explain +**Returns**: Human-readable error explanation string +**Side Effects**: None +**Dependencies**: None +**Environment Variables Used**: None + +**Supported Exit Codes**: +- **Generic/Shell**: 1, 2, 126, 127, 128, 130, 137, 139, 143 +- **Package Manager**: 100, 101, 255 +- **Node.js**: 243, 245, 246, 247, 248, 249, 254 +- **Python**: 210, 211, 212 +- **PostgreSQL**: 231, 232, 233, 234 +- **MySQL/MariaDB**: 241, 242, 243, 244 +- **MongoDB**: 251, 252, 253, 254 +- **Proxmox Custom**: 200, 203, 204, 205, 209, 210, 214, 215, 216, 217, 220, 222, 223, 231 + +**Usage Example**: +```bash +explanation=$(explain_exit_code 127) +echo "Error 127: $explanation" +# Output: Error 127: Command not found +``` + +**Error Code Examples**: +```bash +explain_exit_code 1 # "General error / Operation not permitted" +explain_exit_code 126 # "Command invoked cannot execute (permission problem?)" +explain_exit_code 127 # "Command not found" +explain_exit_code 130 # "Terminated by Ctrl+C (SIGINT)" +explain_exit_code 200 # "Custom: Failed to create lock file" +explain_exit_code 999 # "Unknown error" +``` + +### Error Handling Functions + +#### `error_handler()` +**Purpose**: Main error handler triggered by ERR trap or manual call +**Parameters**: +- `$1` - Exit code (optional, defaults to $?) +- `$2` - Command that failed (optional, defaults to BASH_COMMAND) +**Returns**: None (exits with error code) +**Side Effects**: +- Displays detailed error information +- Logs error to debug file if enabled +- Shows silent log content if available +- Exits with original error code +**Dependencies**: `explain_exit_code()` +**Environment Variables Used**: `DEBUG_LOGFILE`, `SILENT_LOGFILE` + +**Usage Example**: +```bash +# Automatic error handling via ERR trap +set -e +trap 'error_handler' ERR + +# Manual error handling +error_handler 127 "command_not_found" +``` + +**Error Information Displayed**: +- Error message with color coding +- Line number where error occurred +- Exit code with explanation +- Command that failed +- Silent log content (last 20 lines) +- Debug log entry (if enabled) + +### Signal Handling Functions + +#### `on_interrupt()` +**Purpose**: Handle SIGINT (Ctrl+C) signals gracefully +**Parameters**: None +**Returns**: None (exits with code 130) +**Side Effects**: +- Displays interruption message +- Exits with SIGINT code (130) +**Dependencies**: None +**Environment Variables Used**: None + +**Usage Example**: +```bash +# Set up interrupt handler +trap on_interrupt INT + +# User presses Ctrl+C +# Handler displays: "Interrupted by user (SIGINT)" +# Script exits with code 130 +``` + +#### `on_terminate()` +**Purpose**: Handle SIGTERM signals gracefully +**Parameters**: None +**Returns**: None (exits with code 143) +**Side Effects**: +- Displays termination message +- Exits with SIGTERM code (143) +**Dependencies**: None +**Environment Variables Used**: None + +**Usage Example**: +```bash +# Set up termination handler +trap on_terminate TERM + +# System sends SIGTERM +# Handler displays: "Terminated by signal (SIGTERM)" +# Script exits with code 143 +``` + +### Cleanup Functions + +#### `on_exit()` +**Purpose**: Handle script exit cleanup +**Parameters**: None +**Returns**: None (exits with original exit code) +**Side Effects**: +- Removes lock file if set +- Exits with original exit code +**Dependencies**: None +**Environment Variables Used**: `lockfile` + +**Usage Example**: +```bash +# Set up exit handler +trap on_exit EXIT + +# Set lock file +lockfile="/tmp/my_script.lock" + +# Script exits normally or with error +# Handler removes lock file and exits +``` + +### Initialization Functions + +#### `catch_errors()` +**Purpose**: Initialize error handling traps and strict mode +**Parameters**: None +**Returns**: None +**Side Effects**: +- Sets strict error handling mode +- Sets up error traps +- Sets up signal traps +- Sets up exit trap +**Dependencies**: None +**Environment Variables Used**: `STRICT_UNSET` + +**Strict Mode Settings**: +- `-E`: Exit on command failure +- `-e`: Exit on any error +- `-o pipefail`: Exit on pipe failure +- `-u`: Exit on unset variables (if STRICT_UNSET=1) + +**Trap Setup**: +- `ERR`: Calls `error_handler` on command failure +- `EXIT`: Calls `on_exit` on script exit +- `INT`: Calls `on_interrupt` on SIGINT +- `TERM`: Calls `on_terminate` on SIGTERM + +**Usage Example**: +```bash +# Initialize error handling +catch_errors + +# Script now has full error handling +# All errors will be caught and handled +``` + +## Function Call Hierarchy + +### Error Handling Flow +``` +Command Failure +├── ERR trap triggered +├── error_handler() called +│ ├── Get exit code +│ ├── Get command info +│ ├── Get line number +│ ├── explain_exit_code() +│ ├── Display error info +│ ├── Log to debug file +│ ├── Show silent log +│ └── Exit with error code +``` + +### Signal Handling Flow +``` +Signal Received +├── Signal trap triggered +├── Appropriate handler called +│ ├── on_interrupt() for SIGINT +│ ├── on_terminate() for SIGTERM +│ └── on_exit() for EXIT +└── Exit with signal code +``` + +### Initialization Flow +``` +catch_errors() +├── Set strict mode +│ ├── -E (exit on failure) +│ ├── -e (exit on error) +│ ├── -o pipefail (pipe failure) +│ └── -u (unset variables, if enabled) +└── Set up traps + ├── ERR → error_handler + ├── EXIT → on_exit + ├── INT → on_interrupt + └── TERM → on_terminate +``` + +## Error Code Reference + +### Generic/Shell Errors +| Code | Description | +|------|-------------| +| 1 | General error / Operation not permitted | +| 2 | Misuse of shell builtins (e.g. syntax error) | +| 126 | Command invoked cannot execute (permission problem?) | +| 127 | Command not found | +| 128 | Invalid argument to exit | +| 130 | Terminated by Ctrl+C (SIGINT) | +| 137 | Killed (SIGKILL / Out of memory?) | +| 139 | Segmentation fault (core dumped) | +| 143 | Terminated (SIGTERM) | + +### Package Manager Errors +| Code | Description | +|------|-------------| +| 100 | APT: Package manager error (broken packages / dependency problems) | +| 101 | APT: Configuration error (bad sources.list, malformed config) | +| 255 | DPKG: Fatal internal error | + +### Node.js Errors +| Code | Description | +|------|-------------| +| 243 | Node.js: Out of memory (JavaScript heap out of memory) | +| 245 | Node.js: Invalid command-line option | +| 246 | Node.js: Internal JavaScript Parse Error | +| 247 | Node.js: Fatal internal error | +| 248 | Node.js: Invalid C++ addon / N-API failure | +| 249 | Node.js: Inspector error | +| 254 | npm/pnpm/yarn: Unknown fatal error | + +### Python Errors +| Code | Description | +|------|-------------| +| 210 | Python: Virtualenv / uv environment missing or broken | +| 211 | Python: Dependency resolution failed | +| 212 | Python: Installation aborted (permissions or EXTERNALLY-MANAGED) | + +### Database Errors +| Code | Description | +|------|-------------| +| 231 | PostgreSQL: Connection failed (server not running / wrong socket) | +| 232 | PostgreSQL: Authentication failed (bad user/password) | +| 233 | PostgreSQL: Database does not exist | +| 234 | PostgreSQL: Fatal error in query / syntax | +| 241 | MySQL/MariaDB: Connection failed (server not running / wrong socket) | +| 242 | MySQL/MariaDB: Authentication failed (bad user/password) | +| 243 | MySQL/MariaDB: Database does not exist | +| 244 | MySQL/MariaDB: Fatal error in query / syntax | +| 251 | MongoDB: Connection failed (server not running) | +| 252 | MongoDB: Authentication failed (bad user/password) | +| 253 | MongoDB: Database not found | +| 254 | MongoDB: Fatal query error | + +### Proxmox Custom Errors +| Code | Description | +|------|-------------| +| 200 | Custom: Failed to create lock file | +| 203 | Custom: Missing CTID variable | +| 204 | Custom: Missing PCT_OSTYPE variable | +| 205 | Custom: Invalid CTID (<100) | +| 209 | Custom: Container creation failed | +| 210 | Custom: Cluster not quorate | +| 214 | Custom: Not enough storage space | +| 215 | Custom: Container ID not listed | +| 216 | Custom: RootFS entry missing in config | +| 217 | Custom: Storage does not support rootdir | +| 220 | Custom: Unable to resolve template path | +| 222 | Custom: Template download failed after 3 attempts | +| 223 | Custom: Template not available after download | +| 231 | Custom: LXC stack upgrade/retry failed | + +## Environment Variable Dependencies + +### Required Variables +- **`lockfile`**: Lock file path for cleanup (set by calling script) + +### Optional Variables +- **`DEBUG_LOGFILE`**: Path to debug log file for error logging +- **`SILENT_LOGFILE`**: Path to silent execution log file +- **`STRICT_UNSET`**: Enable strict unset variable checking (0/1) + +### Internal Variables +- **`exit_code`**: Current exit code +- **`command`**: Failed command +- **`line_number`**: Line number where error occurred +- **`explanation`**: Error explanation text + +## Error Handling Patterns + +### Automatic Error Handling +```bash +#!/usr/bin/env bash +source error_handler.func + +# Initialize error handling +catch_errors + +# All commands are now monitored +# Errors will be automatically caught and handled +``` + +### Manual Error Handling +```bash +#!/usr/bin/env bash +source error_handler.func + +# Manual error handling +if ! command -v required_tool >/dev/null 2>&1; then + error_handler 127 "required_tool not found" +fi +``` + +### Custom Error Codes +```bash +#!/usr/bin/env bash +source error_handler.func + +# Use custom error codes +if [[ ! -f /required/file ]]; then + echo "Error: Required file missing" + exit 200 # Custom error code +fi +``` + +### Signal Handling +```bash +#!/usr/bin/env bash +source error_handler.func + +# Set up signal handling +trap on_interrupt INT +trap on_terminate TERM +trap on_exit EXIT + +# Script handles signals gracefully +``` + +## Integration Examples + +### With core.func +```bash +#!/usr/bin/env bash +source core.func +source error_handler.func + +# Silent execution uses error_handler for explanations +silent apt-get install -y package +# If command fails, error_handler provides explanation +``` + +### With build.func +```bash +#!/usr/bin/env bash +source core.func +source error_handler.func +source build.func + +# Container creation with error handling +# Errors are caught and explained +``` + +### With tools.func +```bash +#!/usr/bin/env bash +source core.func +source error_handler.func +source tools.func + +# Tool operations with error handling +# All errors are properly handled and explained +``` + +## Best Practices + +### Error Handling Setup +1. Source error_handler.func early in script +2. Call catch_errors() to initialize traps +3. Use appropriate exit codes for different error types +4. Provide meaningful error messages + +### Signal Handling +1. Always set up signal traps +2. Provide graceful cleanup on interruption +3. Use appropriate exit codes for signals +4. Clean up temporary files and processes + +### Error Reporting +1. Use explain_exit_code() for user-friendly messages +2. Log errors to debug files when needed +3. Provide context information (line numbers, commands) +4. Integrate with silent execution logging + +### Custom Error Codes +1. Use Proxmox custom error codes (200-231) for container/VM errors +2. Use standard error codes for common operations +3. Document custom error codes in script comments +4. Provide clear error messages for custom codes diff --git a/docs/misc/error_handler.func/ERROR_HANDLER_INTEGRATION.md b/docs/misc/error_handler.func/ERROR_HANDLER_INTEGRATION.md new file mode 100644 index 000000000..c3cf3b5c9 --- /dev/null +++ b/docs/misc/error_handler.func/ERROR_HANDLER_INTEGRATION.md @@ -0,0 +1,512 @@ +# error_handler.func Integration Guide + +## Overview + +This document describes how `error_handler.func` integrates with other components in the Proxmox Community Scripts project, including dependencies, data flow, and API surface. + +## Dependencies + +### External Dependencies + +#### Required Commands +- **None**: Pure Bash implementation + +#### Optional Commands +- **None**: No external command dependencies + +### Internal Dependencies + +#### core.func +- **Purpose**: Provides color variables for error display +- **Usage**: Uses `RD`, `CL`, `YWB` color variables +- **Integration**: Called automatically when core.func is sourced +- **Data Flow**: Color variables → error display formatting + +## Integration Points + +### With core.func + +#### Silent Execution Integration +```bash +# core.func silent() function uses error_handler.func +silent() { + local cmd="$*" + local caller_line="${BASH_LINENO[0]:-unknown}" + + # Execute command + "$@" >>"$SILENT_LOGFILE" 2>&1 + local rc=$? + + if [[ $rc -ne 0 ]]; then + # Load error_handler.func if needed + if ! declare -f explain_exit_code >/dev/null 2>&1; then + source error_handler.func + fi + + # Get error explanation + local explanation + explanation="$(explain_exit_code "$rc")" + + # Display error with explanation + printf "\e[?25h" + echo -e "\n${RD}[ERROR]${CL} in line ${RD}${caller_line}${CL}: exit code ${RD}${rc}${CL} (${explanation})" + echo -e "${RD}Command:${CL} ${YWB}${cmd}${CL}\n" + + exit "$rc" + fi +} +``` + +#### Color Variable Usage +```bash +# error_handler.func uses color variables from core.func +error_handler() { + # ... error handling logic ... + + # Use color variables for error display + echo -e "\n${RD}[ERROR]${CL} in line ${RD}${line_number}${CL}: exit code ${RD}${exit_code}${CL} (${explanation}): while executing command ${YWB}${command}${CL}\n" +} + +on_interrupt() { + echo -e "\n${RD}Interrupted by user (SIGINT)${CL}" + exit 130 +} + +on_terminate() { + echo -e "\n${RD}Terminated by signal (SIGTERM)${CL}" + exit 143 +} +``` + +### With build.func + +#### Container Creation Error Handling +```bash +# build.func uses error_handler.func for container operations +source core.func +source error_handler.func + +# Container creation with error handling +create_container() { + # Set up error handling + catch_errors + + # Container creation operations + silent pct create "$CTID" "$TEMPLATE" \ + --hostname "$HOSTNAME" \ + --memory "$MEMORY" \ + --cores "$CORES" + + # If creation fails, error_handler provides explanation +} +``` + +#### Template Download Error Handling +```bash +# build.func uses error_handler.func for template operations +download_template() { + # Template download with error handling + if ! silent curl -fsSL "$TEMPLATE_URL" -o "$TEMPLATE_FILE"; then + # error_handler provides detailed explanation + exit 222 # Template download failed + fi +} +``` + +### With tools.func + +#### Maintenance Operations Error Handling +```bash +# tools.func uses error_handler.func for maintenance operations +source core.func +source error_handler.func + +# Maintenance operations with error handling +update_system() { + catch_errors + + # System update operations + silent apt-get update + silent apt-get upgrade -y + + # Error handling provides explanations for failures +} + +cleanup_logs() { + catch_errors + + # Log cleanup operations + silent find /var/log -name "*.log" -mtime +30 -delete + + # Error handling provides explanations for permission issues +} +``` + +### With api.func + +#### API Operations Error Handling +```bash +# api.func uses error_handler.func for API operations +source core.func +source error_handler.func + +# API operations with error handling +api_call() { + catch_errors + + # API call with error handling + if ! silent curl -k -H "Authorization: PVEAPIToken=$API_TOKEN" \ + "$API_URL/api2/json/nodes/$NODE/lxc"; then + # error_handler provides explanation for API failures + exit 1 + fi +} +``` + +### With install.func + +#### Installation Process Error Handling +```bash +# install.func uses error_handler.func for installation operations +source core.func +source error_handler.func + +# Installation with error handling +install_package() { + local package="$1" + + catch_errors + + # Package installation + silent apt-get install -y "$package" + + # Error handling provides explanations for installation failures +} +``` + +### With alpine-install.func + +#### Alpine Installation Error Handling +```bash +# alpine-install.func uses error_handler.func for Alpine operations +source core.func +source error_handler.func + +# Alpine installation with error handling +install_alpine_package() { + local package="$1" + + catch_errors + + # Alpine package installation + silent apk add --no-cache "$package" + + # Error handling provides explanations for Alpine-specific failures +} +``` + +### With alpine-tools.func + +#### Alpine Tools Error Handling +```bash +# alpine-tools.func uses error_handler.func for Alpine tools +source core.func +source error_handler.func + +# Alpine tools with error handling +alpine_tool_operation() { + catch_errors + + # Alpine-specific tool operations + silent alpine_command + + # Error handling provides explanations for Alpine tool failures +} +``` + +### With passthrough.func + +#### Hardware Passthrough Error Handling +```bash +# passthrough.func uses error_handler.func for hardware operations +source core.func +source error_handler.func + +# Hardware passthrough with error handling +configure_gpu_passthrough() { + catch_errors + + # GPU passthrough operations + silent lspci | grep -i nvidia + + # Error handling provides explanations for hardware failures +} +``` + +### With vm-core.func + +#### VM Operations Error Handling +```bash +# vm-core.func uses error_handler.func for VM operations +source core.func +source error_handler.func + +# VM operations with error handling +create_vm() { + catch_errors + + # VM creation operations + silent qm create "$VMID" \ + --name "$VMNAME" \ + --memory "$MEMORY" \ + --cores "$CORES" + + # Error handling provides explanations for VM creation failures +} +``` + +## Data Flow + +### Input Data + +#### Environment Variables +- **`DEBUG_LOGFILE`**: Path to debug log file for error logging +- **`SILENT_LOGFILE`**: Path to silent execution log file +- **`STRICT_UNSET`**: Enable strict unset variable checking (0/1) +- **`lockfile`**: Lock file path for cleanup (set by calling script) + +#### Function Parameters +- **Exit codes**: Passed to `explain_exit_code()` and `error_handler()` +- **Command information**: Passed to `error_handler()` for context +- **Signal information**: Passed to signal handlers + +#### System Information +- **Exit codes**: Retrieved from `$?` variable +- **Command information**: Retrieved from `BASH_COMMAND` variable +- **Line numbers**: Retrieved from `BASH_LINENO[0]` variable +- **Process information**: Retrieved from system calls + +### Processing Data + +#### Error Code Processing +- **Code classification**: Categorize exit codes by type +- **Explanation lookup**: Map codes to human-readable messages +- **Context collection**: Gather command and line information +- **Log preparation**: Format error information for logging + +#### Signal Processing +- **Signal detection**: Identify received signals +- **Handler selection**: Choose appropriate signal handler +- **Cleanup operations**: Perform necessary cleanup +- **Exit code setting**: Set appropriate exit codes + +#### Log Processing +- **Debug logging**: Write error information to debug log +- **Silent log integration**: Display silent log content +- **Log formatting**: Format log entries for readability +- **Log analysis**: Provide log analysis capabilities + +### Output Data + +#### Error Information +- **Error messages**: Human-readable error explanations +- **Context information**: Line numbers, commands, timestamps +- **Color formatting**: ANSI color codes for terminal display +- **Log content**: Silent log excerpts and debug information + +#### System State +- **Exit codes**: Returned from functions +- **Log files**: Created and updated for error tracking +- **Cleanup status**: Lock file removal and process cleanup +- **Signal handling**: Graceful signal processing + +## API Surface + +### Public Functions + +#### Error Explanation +- **`explain_exit_code()`**: Convert exit codes to explanations +- **Parameters**: Exit code to explain +- **Returns**: Human-readable explanation string +- **Usage**: Called by error_handler() and other functions + +#### Error Handling +- **`error_handler()`**: Main error handler function +- **Parameters**: Exit code (optional), command (optional) +- **Returns**: None (exits with error code) +- **Usage**: Called by ERR trap or manually + +#### Signal Handling +- **`on_interrupt()`**: Handle SIGINT signals +- **`on_terminate()`**: Handle SIGTERM signals +- **`on_exit()`**: Handle script exit cleanup +- **Parameters**: None +- **Returns**: None (exits with signal code) +- **Usage**: Called by signal traps + +#### Initialization +- **`catch_errors()`**: Initialize error handling +- **Parameters**: None +- **Returns**: None +- **Usage**: Called to set up error handling traps + +### Internal Functions + +#### None +- All functions in error_handler.func are public +- No internal helper functions +- Direct implementation of all functionality + +### Global Variables + +#### Configuration Variables +- **`DEBUG_LOGFILE`**: Debug log file path +- **`SILENT_LOGFILE`**: Silent log file path +- **`STRICT_UNSET`**: Strict mode setting +- **`lockfile`**: Lock file path + +#### State Variables +- **`exit_code`**: Current exit code +- **`command`**: Failed command +- **`line_number`**: Line number where error occurred +- **`explanation`**: Error explanation text + +## Integration Patterns + +### Standard Integration Pattern + +```bash +#!/usr/bin/env bash +# Standard integration pattern + +# 1. Source core.func first +source core.func + +# 2. Source error_handler.func +source error_handler.func + +# 3. Initialize error handling +catch_errors + +# 4. Use silent execution +silent command + +# 5. Errors are automatically handled +``` + +### Minimal Integration Pattern + +```bash +#!/usr/bin/env bash +# Minimal integration pattern + +source error_handler.func +catch_errors + +# Basic error handling +command +``` + +### Advanced Integration Pattern + +```bash +#!/usr/bin/env bash +# Advanced integration pattern + +source core.func +source error_handler.func + +# Set up comprehensive error handling +export DEBUG_LOGFILE="/tmp/debug.log" +export SILENT_LOGFILE="/tmp/silent.log" +lockfile="/tmp/script.lock" +touch "$lockfile" + +catch_errors +trap on_interrupt INT +trap on_terminate TERM +trap on_exit EXIT + +# Advanced error handling +silent command +``` + +## Error Handling Integration + +### Automatic Error Handling +- **ERR Trap**: Automatically catches command failures +- **Error Explanation**: Provides human-readable error messages +- **Context Information**: Shows line numbers and commands +- **Log Integration**: Displays silent log content + +### Manual Error Handling +- **Custom Error Codes**: Use Proxmox custom error codes +- **Error Recovery**: Implement retry logic with error handling +- **Conditional Handling**: Different handling for different error types +- **Error Analysis**: Analyze error patterns and trends + +### Signal Handling Integration +- **Graceful Interruption**: Handle Ctrl+C gracefully +- **Clean Termination**: Handle SIGTERM signals +- **Exit Cleanup**: Clean up resources on script exit +- **Lock File Management**: Remove lock files on exit + +## Performance Considerations + +### Error Handling Overhead +- **Minimal Impact**: Error handling adds minimal overhead +- **Trap Setup**: Trap setup is done once during initialization +- **Error Processing**: Error processing is only done on failures +- **Log Writing**: Log writing is only done when enabled + +### Memory Usage +- **Minimal Footprint**: Error handler uses minimal memory +- **Variable Reuse**: Global variables reused across functions +- **No Memory Leaks**: Proper cleanup prevents memory leaks +- **Efficient Processing**: Efficient error code processing + +### Execution Speed +- **Fast Error Detection**: Quick error detection and handling +- **Efficient Explanation**: Fast error code explanation lookup +- **Minimal Delay**: Minimal delay in error handling +- **Quick Exit**: Fast exit on error conditions + +## Security Considerations + +### Error Information Disclosure +- **Controlled Disclosure**: Only necessary error information is shown +- **Log Security**: Log files have appropriate permissions +- **Sensitive Data**: Sensitive data is not logged +- **Error Sanitization**: Error messages are sanitized + +### Signal Handling Security +- **Signal Validation**: Only expected signals are handled +- **Cleanup Security**: Secure cleanup of temporary files +- **Lock File Security**: Secure lock file management +- **Process Security**: Secure process termination + +### Log File Security +- **File Permissions**: Log files have appropriate permissions +- **Log Rotation**: Log files are rotated to prevent disk filling +- **Log Cleanup**: Old log files are cleaned up +- **Log Access**: Log access is controlled + +## Future Integration Considerations + +### Extensibility +- **New Error Codes**: Easy to add new error code explanations +- **Custom Handlers**: Easy to add custom error handlers +- **Signal Extensions**: Easy to add new signal handlers +- **Log Formats**: Easy to add new log formats + +### Compatibility +- **Bash Version**: Compatible with different Bash versions +- **System Compatibility**: Compatible with different systems +- **Script Compatibility**: Compatible with different script types +- **Error Code Compatibility**: Compatible with different error codes + +### Performance +- **Optimization**: Error handling can be optimized for better performance +- **Caching**: Error explanations can be cached for faster lookup +- **Parallel Processing**: Error handling can be parallelized +- **Resource Management**: Better resource management for error handling diff --git a/docs/misc/error_handler.func/ERROR_HANDLER_USAGE_EXAMPLES.md b/docs/misc/error_handler.func/ERROR_HANDLER_USAGE_EXAMPLES.md new file mode 100644 index 000000000..cfb668711 --- /dev/null +++ b/docs/misc/error_handler.func/ERROR_HANDLER_USAGE_EXAMPLES.md @@ -0,0 +1,625 @@ +# error_handler.func Usage Examples + +## Overview + +This document provides practical usage examples for `error_handler.func` functions, covering common scenarios, integration patterns, and best practices. + +## Basic Error Handling Setup + +### Standard Script Initialization + +```bash +#!/usr/bin/env bash +# Standard error handling setup + +# Source error handler +source error_handler.func + +# Initialize error handling +catch_errors + +# Your script code here +# All errors will be automatically caught and handled +echo "Script running..." +apt-get update +apt-get install -y package +echo "Script completed successfully" +``` + +### Minimal Error Handling + +```bash +#!/usr/bin/env bash +# Minimal error handling setup + +source error_handler.func +catch_errors + +# Simple script with error handling +echo "Starting operation..." +command_that_might_fail +echo "Operation completed" +``` + +## Error Code Explanation Examples + +### Basic Error Explanation + +```bash +#!/usr/bin/env bash +source error_handler.func + +# Explain common error codes +echo "Error 1: $(explain_exit_code 1)" +echo "Error 127: $(explain_exit_code 127)" +echo "Error 130: $(explain_exit_code 130)" +echo "Error 200: $(explain_exit_code 200)" +``` + +### Error Code Testing + +```bash +#!/usr/bin/env bash +source error_handler.func + +# Test all error codes +test_error_codes() { + local codes=(1 2 126 127 128 130 137 139 143 100 101 255 200 203 204 205) + + for code in "${codes[@]}"; do + echo "Code $code: $(explain_exit_code $code)" + done +} + +test_error_codes +``` + +### Custom Error Code Usage + +```bash +#!/usr/bin/env bash +source error_handler.func + +# Use custom error codes +check_requirements() { + if [[ ! -f /required/file ]]; then + echo "Error: Required file missing" + exit 200 # Custom error code + fi + + if [[ -z "$CTID" ]]; then + echo "Error: CTID not set" + exit 203 # Custom error code + fi + + if [[ $CTID -lt 100 ]]; then + echo "Error: Invalid CTID" + exit 205 # Custom error code + fi +} + +check_requirements +``` + +## Signal Handling Examples + +### Interrupt Handling + +```bash +#!/usr/bin/env bash +source error_handler.func + +# Set up interrupt handler +trap on_interrupt INT + +echo "Script running... Press Ctrl+C to interrupt" +sleep 10 +echo "Script completed normally" +``` + +### Termination Handling + +```bash +#!/usr/bin/env bash +source error_handler.func + +# Set up termination handler +trap on_terminate TERM + +echo "Script running... Send SIGTERM to terminate" +sleep 10 +echo "Script completed normally" +``` + +### Complete Signal Handling + +```bash +#!/usr/bin/env bash +source error_handler.func + +# Set up all signal handlers +trap on_interrupt INT +trap on_terminate TERM +trap on_exit EXIT + +echo "Script running with full signal handling" +sleep 10 +echo "Script completed normally" +``` + +## Cleanup Examples + +### Lock File Cleanup + +```bash +#!/usr/bin/env bash +source error_handler.func + +# Set up lock file +lockfile="/tmp/my_script.lock" +touch "$lockfile" + +# Set up exit handler +trap on_exit EXIT + +echo "Script running with lock file..." +sleep 5 +echo "Script completed - lock file will be removed" +``` + +### Temporary File Cleanup + +```bash +#!/usr/bin/env bash +source error_handler.func + +# Create temporary files +temp_file1="/tmp/temp1.$$" +temp_file2="/tmp/temp2.$$" +touch "$temp_file1" "$temp_file2" + +# Set up cleanup +cleanup() { + rm -f "$temp_file1" "$temp_file2" + echo "Temporary files cleaned up" +} + +trap cleanup EXIT + +echo "Script running with temporary files..." +sleep 5 +echo "Script completed - temporary files will be cleaned up" +``` + +## Debug Logging Examples + +### Basic Debug Logging + +```bash +#!/usr/bin/env bash +source error_handler.func + +# Enable debug logging +export DEBUG_LOGFILE="/tmp/debug.log" +catch_errors + +echo "Script with debug logging" +apt-get update +apt-get install -y package +``` + +### Debug Log Analysis + +```bash +#!/usr/bin/env bash +source error_handler.func + +# Enable debug logging +export DEBUG_LOGFILE="/tmp/debug.log" +catch_errors + +# Function to analyze debug log +analyze_debug_log() { + if [[ -f "$DEBUG_LOGFILE" ]]; then + echo "Debug log analysis:" + echo "Total errors: $(grep -c "ERROR" "$DEBUG_LOGFILE")" + echo "Recent errors:" + tail -n 5 "$DEBUG_LOGFILE" + else + echo "No debug log found" + fi +} + +# Run script +echo "Running script..." +apt-get update + +# Analyze results +analyze_debug_log +``` + +## Silent Execution Integration + +### With core.func Silent Execution + +```bash +#!/usr/bin/env bash +source core.func +source error_handler.func + +# Silent execution with error handling +echo "Installing packages..." +silent apt-get update +silent apt-get install -y nginx + +echo "Configuring service..." +silent systemctl enable nginx +silent systemctl start nginx + +echo "Installation completed" +``` + +### Silent Execution Error Handling + +```bash +#!/usr/bin/env bash +source core.func +source error_handler.func + +# Function with silent execution and error handling +install_package() { + local package="$1" + + echo "Installing $package..." + if silent apt-get install -y "$package"; then + echo "$package installed successfully" + return 0 + else + echo "Failed to install $package" + return 1 + fi +} + +# Install multiple packages +packages=("nginx" "apache2" "mysql-server") +for package in "${packages[@]}"; do + if ! install_package "$package"; then + echo "Stopping installation due to error" + exit 1 + fi +done +``` + +## Advanced Error Handling Examples + +### Conditional Error Handling + +```bash +#!/usr/bin/env bash +source error_handler.func + +# Conditional error handling based on environment +setup_error_handling() { + if [[ "${STRICT_MODE:-0}" == "1" ]]; then + echo "Enabling strict mode" + export STRICT_UNSET=1 + fi + + catch_errors + echo "Error handling configured" +} + +setup_error_handling +``` + +### Error Recovery + +```bash +#!/usr/bin/env bash +source error_handler.func + +# Error recovery pattern +retry_operation() { + local max_attempts=3 + local attempt=1 + + while [[ $attempt -le $max_attempts ]]; do + echo "Attempt $attempt of $max_attempts" + + if silent "$@"; then + echo "Operation succeeded on attempt $attempt" + return 0 + else + echo "Attempt $attempt failed" + ((attempt++)) + + if [[ $attempt -le $max_attempts ]]; then + echo "Retrying in 5 seconds..." + sleep 5 + fi + fi + done + + echo "Operation failed after $max_attempts attempts" + return 1 +} + +# Use retry pattern +retry_operation apt-get update +retry_operation apt-get install -y package +``` + +### Custom Error Handler + +```bash +#!/usr/bin/env bash +source error_handler.func + +# Custom error handler for specific operations +custom_error_handler() { + local exit_code=${1:-$?} + local command=${2:-${BASH_COMMAND:-unknown}} + + case "$exit_code" in + 127) + echo "Custom handling: Command not found - $command" + echo "Suggestions:" + echo "1. Check if the command is installed" + echo "2. Check if the command is in PATH" + echo "3. Check spelling" + ;; + 126) + echo "Custom handling: Permission denied - $command" + echo "Suggestions:" + echo "1. Check file permissions" + echo "2. Run with appropriate privileges" + echo "3. Check if file is executable" + ;; + *) + # Use default error handler + error_handler "$exit_code" "$command" + ;; + esac +} + +# Set up custom error handler +trap 'custom_error_handler' ERR + +# Test custom error handling +nonexistent_command +``` + +## Integration Examples + +### With build.func + +```bash +#!/usr/bin/env bash +# Integration with build.func + +source core.func +source error_handler.func +source build.func + +# Container creation with error handling +export APP="plex" +export CTID="100" + +# Errors will be caught and explained +# Silent execution will use error_handler for explanations +``` + +### With tools.func + +```bash +#!/usr/bin/env bash +# Integration with tools.func + +source core.func +source error_handler.func +source tools.func + +# Tool operations with error handling +# All errors are properly handled and explained +``` + +### With api.func + +```bash +#!/usr/bin/env bash +# Integration with api.func + +source core.func +source error_handler.func +source api.func + +# API operations with error handling +# Network errors and API errors are properly handled +``` + +## Best Practices Examples + +### Comprehensive Error Handling + +```bash +#!/usr/bin/env bash +# Comprehensive error handling example + +source error_handler.func + +# Set up comprehensive error handling +setup_comprehensive_error_handling() { + # Enable debug logging + export DEBUG_LOGFILE="/tmp/script_debug.log" + + # Set up lock file + lockfile="/tmp/script.lock" + touch "$lockfile" + + # Initialize error handling + catch_errors + + # Set up signal handlers + trap on_interrupt INT + trap on_terminate TERM + trap on_exit EXIT + + echo "Comprehensive error handling configured" +} + +setup_comprehensive_error_handling + +# Script operations +echo "Starting script operations..." +# ... script code ... +echo "Script operations completed" +``` + +### Error Handling for Different Scenarios + +```bash +#!/usr/bin/env bash +source error_handler.func + +# Different error handling for different scenarios +handle_package_errors() { + local exit_code=$1 + case "$exit_code" in + 100) + echo "Package manager error - trying to fix..." + apt-get --fix-broken install + ;; + 101) + echo "Configuration error - checking sources..." + apt-get update + ;; + *) + error_handler "$exit_code" + ;; + esac +} + +handle_network_errors() { + local exit_code=$1 + case "$exit_code" in + 127) + echo "Network command not found - checking connectivity..." + ping -c 1 8.8.8.8 + ;; + *) + error_handler "$exit_code" + ;; + esac +} + +# Use appropriate error handler +if [[ "$1" == "package" ]]; then + trap 'handle_package_errors $?' ERR +elif [[ "$1" == "network" ]]; then + trap 'handle_network_errors $?' ERR +else + catch_errors +fi +``` + +### Error Handling with Logging + +```bash +#!/usr/bin/env bash +source error_handler.func + +# Error handling with detailed logging +setup_logging_error_handling() { + # Create log directory + mkdir -p /var/log/script_errors + + # Set up debug logging + export DEBUG_LOGFILE="/var/log/script_errors/debug.log" + + # Set up silent logging + export SILENT_LOGFILE="/var/log/script_errors/silent.log" + + # Initialize error handling + catch_errors + + echo "Logging error handling configured" +} + +setup_logging_error_handling + +# Script operations with logging +echo "Starting logged operations..." +# ... script code ... +echo "Logged operations completed" +``` + +## Troubleshooting Examples + +### Debug Mode + +```bash +#!/usr/bin/env bash +source error_handler.func + +# Enable debug mode +export DEBUG_LOGFILE="/tmp/debug.log" +export STRICT_UNSET=1 + +catch_errors + +echo "Debug mode enabled" +# Script operations +``` + +### Error Analysis + +```bash +#!/usr/bin/env bash +source error_handler.func + +# Function to analyze errors +analyze_errors() { + local log_file="${1:-$DEBUG_LOGFILE}" + + if [[ -f "$log_file" ]]; then + echo "Error Analysis:" + echo "Total errors: $(grep -c "ERROR" "$log_file")" + echo "Error types:" + grep "ERROR" "$log_file" | awk '{print $NF}' | sort | uniq -c + echo "Recent errors:" + tail -n 10 "$log_file" + else + echo "No error log found" + fi +} + +# Run script with error analysis +analyze_errors +``` + +### Error Recovery Testing + +```bash +#!/usr/bin/env bash +source error_handler.func + +# Test error recovery +test_error_recovery() { + local test_cases=( + "nonexistent_command" + "apt-get install nonexistent_package" + "systemctl start nonexistent_service" + ) + + for test_case in "${test_cases[@]}"; do + echo "Testing: $test_case" + if silent $test_case; then + echo "Unexpected success" + else + echo "Expected failure handled" + fi + done +} + +test_error_recovery +``` diff --git a/docs/misc/error_handler.func/README.md b/docs/misc/error_handler.func/README.md new file mode 100644 index 000000000..3c4448184 --- /dev/null +++ b/docs/misc/error_handler.func/README.md @@ -0,0 +1,228 @@ +# error_handler.func Documentation + +## Overview + +The `error_handler.func` file provides comprehensive error handling and signal management for Proxmox Community Scripts. It offers detailed error code explanations, graceful error recovery, and proper cleanup mechanisms. + +## Purpose and Use Cases + +- **Error Code Explanation**: Provides human-readable explanations for exit codes +- **Signal Handling**: Manages SIGINT, SIGTERM, and other signals gracefully +- **Error Recovery**: Implements proper cleanup and error reporting +- **Debug Logging**: Records error information for troubleshooting +- **Silent Execution Support**: Integrates with core.func silent execution + +## Quick Reference + +### Key Function Groups +- **Error Explanation**: `explain_exit_code()` - Convert exit codes to human-readable messages +- **Error Handling**: `error_handler()` - Main error handler with detailed reporting +- **Signal Handlers**: `on_interrupt()`, `on_terminate()` - Graceful signal handling +- **Cleanup**: `on_exit()` - Cleanup on script exit +- **Trap Setup**: `catch_errors()` - Initialize error handling traps + +### Dependencies +- **External**: None (pure Bash implementation) +- **Internal**: Uses color variables from core.func + +### Integration Points +- Used by: All scripts via core.func silent execution +- Uses: Color variables from core.func +- Provides: Error explanations for core.func silent function + +## Documentation Files + +### 📊 [ERROR_HANDLER_FLOWCHART.md](./ERROR_HANDLER_FLOWCHART.md) +Visual execution flows showing error handling processes and signal management. + +### 📚 [ERROR_HANDLER_FUNCTIONS_REFERENCE.md](./ERROR_HANDLER_FUNCTIONS_REFERENCE.md) +Complete alphabetical reference of all functions with parameters, dependencies, and usage details. + +### 💡 [ERROR_HANDLER_USAGE_EXAMPLES.md](./ERROR_HANDLER_USAGE_EXAMPLES.md) +Practical examples showing how to use error handling functions and common patterns. + +### 🔗 [ERROR_HANDLER_INTEGRATION.md](./ERROR_HANDLER_INTEGRATION.md) +How error_handler.func integrates with other components and provides error handling services. + +## Key Features + +### Error Code Categories +- **Generic/Shell Errors**: Exit codes 1, 2, 126, 127, 128, 130, 137, 139, 143 +- **Package Manager Errors**: APT/DPKG errors (100, 101, 255) +- **Node.js Errors**: JavaScript runtime errors (243-249, 254) +- **Python Errors**: Python environment and dependency errors (210-212) +- **Database Errors**: PostgreSQL, MySQL, MongoDB errors (231-254) +- **Proxmox Custom Errors**: Container and VM specific errors (200-231) + +### Signal Handling +- **SIGINT (Ctrl+C)**: Graceful interruption handling +- **SIGTERM**: Graceful termination handling +- **EXIT**: Cleanup on script exit +- **ERR**: Error trap for command failures + +### Error Reporting +- **Detailed Messages**: Human-readable error explanations +- **Context Information**: Line numbers, commands, timestamps +- **Log Integration**: Silent log file integration +- **Debug Logging**: Optional debug log file support + +## Common Usage Patterns + +### Basic Error Handling Setup +```bash +#!/usr/bin/env bash +# Basic error handling setup + +source error_handler.func + +# Initialize error handling +catch_errors + +# Your script code here +# Errors will be automatically handled +``` + +### Manual Error Explanation +```bash +#!/usr/bin/env bash +source error_handler.func + +# Get error explanation +explanation=$(explain_exit_code 127) +echo "Error 127: $explanation" +# Output: Error 127: Command not found +``` + +### Custom Error Handling +```bash +#!/usr/bin/env bash +source error_handler.func + +# Custom error handling +if ! command -v required_tool >/dev/null 2>&1; then + echo "Error: required_tool not found" + exit 127 +fi +``` + +## Environment Variables + +### Debug Variables +- `DEBUG_LOGFILE`: Path to debug log file for error logging +- `SILENT_LOGFILE`: Path to silent execution log file +- `STRICT_UNSET`: Enable strict unset variable checking (0/1) + +### Internal Variables +- `lockfile`: Lock file path for cleanup (set by calling script) +- `exit_code`: Current exit code +- `command`: Failed command +- `line_number`: Line number where error occurred + +## Error Categories + +### Generic/Shell Errors +- **1**: General error / Operation not permitted +- **2**: Misuse of shell builtins (syntax error) +- **126**: Command invoked cannot execute (permission problem) +- **127**: Command not found +- **128**: Invalid argument to exit +- **130**: Terminated by Ctrl+C (SIGINT) +- **137**: Killed (SIGKILL / Out of memory) +- **139**: Segmentation fault (core dumped) +- **143**: Terminated (SIGTERM) + +### Package Manager Errors +- **100**: APT package manager error (broken packages) +- **101**: APT configuration error (bad sources.list) +- **255**: DPKG fatal internal error + +### Node.js Errors +- **243**: JavaScript heap out of memory +- **245**: Invalid command-line option +- **246**: Internal JavaScript parse error +- **247**: Fatal internal error +- **248**: Invalid C++ addon / N-API failure +- **249**: Inspector error +- **254**: npm/pnpm/yarn unknown fatal error + +### Python Errors +- **210**: Virtualenv/uv environment missing or broken +- **211**: Dependency resolution failed +- **212**: Installation aborted (permissions or EXTERNALLY-MANAGED) + +### Database Errors +- **PostgreSQL (231-234)**: Connection, authentication, database, query errors +- **MySQL/MariaDB (241-244)**: Connection, authentication, database, query errors +- **MongoDB (251-254)**: Connection, authentication, database, query errors + +### Proxmox Custom Errors +- **200**: Failed to create lock file +- **203**: Missing CTID variable +- **204**: Missing PCT_OSTYPE variable +- **205**: Invalid CTID (<100) +- **209**: Container creation failed +- **210**: Cluster not quorate +- **214**: Not enough storage space +- **215**: Container ID not listed +- **216**: RootFS entry missing in config +- **217**: Storage does not support rootdir +- **220**: Unable to resolve template path +- **222**: Template download failed after 3 attempts +- **223**: Template not available after download +- **231**: LXC stack upgrade/retry failed + +## Best Practices + +### Error Handling Setup +1. Source error_handler.func early in script +2. Call catch_errors() to initialize traps +3. Use proper exit codes for different error types +4. Provide meaningful error messages + +### Signal Handling +1. Always set up signal traps +2. Provide graceful cleanup on interruption +3. Use appropriate exit codes for signals +4. Clean up temporary files and processes + +### Error Reporting +1. Use explain_exit_code() for user-friendly messages +2. Log errors to debug files when needed +3. Provide context information (line numbers, commands) +4. Integrate with silent execution logging + +## Troubleshooting + +### Common Issues +1. **Missing Error Handler**: Ensure error_handler.func is sourced +2. **Trap Not Set**: Call catch_errors() to initialize traps +3. **Color Variables**: Ensure core.func is sourced for colors +4. **Lock Files**: Clean up lock files in on_exit() + +### Debug Mode +Enable debug logging for detailed error information: +```bash +export DEBUG_LOGFILE="/tmp/debug.log" +source error_handler.func +catch_errors +``` + +### Error Code Testing +Test error explanations: +```bash +source error_handler.func +for code in 1 2 126 127 128 130 137 139 143; do + echo "Code $code: $(explain_exit_code $code)" +done +``` + +## Related Documentation + +- [core.func](../core.func/) - Core utilities and silent execution +- [build.func](../build.func/) - Container creation with error handling +- [tools.func](../tools.func/) - Extended utilities with error handling +- [api.func](../api.func/) - API operations with error handling + +--- + +*This documentation covers the error_handler.func file which provides comprehensive error handling for all Proxmox Community Scripts.* diff --git a/misc/build.func b/misc/build.func index c53632a9e..c24798689 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1625,6 +1625,8 @@ install_script() { NEXTID=$(pvesh get /cluster/nextid) timezone=$(cat /etc/timezone) + + # Show APP Header header_info # --- Support CLI argument as direct preset (default, advanced, …) --- From 7b71096991ce7c658c20e55a4a855c1b4a465d8e Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 10 Oct 2025 11:34:22 +0200 Subject: [PATCH 1387/1733] Add template issue closer --- .github/workflows/close_template_issue.yml | 47 ++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 .github/workflows/close_template_issue.yml diff --git a/.github/workflows/close_template_issue.yml b/.github/workflows/close_template_issue.yml new file mode 100644 index 000000000..a3cf7a91c --- /dev/null +++ b/.github/workflows/close_template_issue.yml @@ -0,0 +1,47 @@ +name: Auto-Close Wrong Template Issues +on: + issues: + types: [opened] + +jobs: + close_tteck_issues: + if: github.repository == 'community-scripts/ProxmoxVE' + runs-on: ubuntu-latest + steps: + - name: Auto-close if wrong Template issue detected + uses: actions/github-script@v7 + with: + script: | + const issue = context.payload.issue; + const content = `${issue.title}\n${issue.body}`; + const issueNumber = issue.number; + + // Check for tteck script mention + if (content.includes("Template debian-13-standard_13.1-2_amd64.tar.zst [local]") || content.includes("Container creation failed. Checking if template is corrupted or incomplete.")) || content.includes("Template is valid, but container creation still failed."){ + const message = `Hello, it looks like you are referencing a container creation issue!. + + We get many simmilar issues with this topic, so please check this disscusion #8126. + If this did not solve your problem, please reopen this issue. + + This issue is being closed automatically.`; + + await github.rest.issues.createComment({ + ...context.repo, + issue_number: issueNumber, + body: message + }); + + // Optionally apply a label like "not planned" + await github.rest.issues.addLabels({ + ...context.repo, + issue_number: issueNumber, + labels: ["not planned"] + }); + + // Close the issue + await github.rest.issues.update({ + ...context.repo, + issue_number: issueNumber, + state: "closed" + }); + } From ba90d60bf9e984ee4407775f84cc440e03d8a1f7 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 10 Oct 2025 11:36:32 +0200 Subject: [PATCH 1388/1733] more docs --- docs/misc/README.md | 9 + docs/misc/api.func/API_FLOWCHART.md | 342 ++++++++ docs/misc/api.func/API_FUNCTIONS_REFERENCE.md | 433 ++++++++++ docs/misc/api.func/API_INTEGRATION.md | 643 ++++++++++++++ docs/misc/api.func/API_USAGE_EXAMPLES.md | 794 ++++++++++++++++++ docs/misc/api.func/README.md | 199 +++++ 6 files changed, 2420 insertions(+) create mode 100644 docs/misc/api.func/API_FLOWCHART.md create mode 100644 docs/misc/api.func/API_FUNCTIONS_REFERENCE.md create mode 100644 docs/misc/api.func/API_INTEGRATION.md create mode 100644 docs/misc/api.func/API_USAGE_EXAMPLES.md create mode 100644 docs/misc/api.func/README.md diff --git a/docs/misc/README.md b/docs/misc/README.md index 562d68999..9cf5d9333 100644 --- a/docs/misc/README.md +++ b/docs/misc/README.md @@ -33,6 +33,15 @@ Comprehensive error handling and signal management for Proxmox Community Scripts - Practical usage examples - Integration with other components +### 📁 [api.func/](./api.func/) +Proxmox API integration and diagnostic reporting functionality for Community Scripts. + +**Contents:** +- Visual execution flowcharts +- Complete function reference +- Practical usage examples +- Integration with other components + ## Other Documentation Additional miscellaneous documentation may be added here as the project grows. diff --git a/docs/misc/api.func/API_FLOWCHART.md b/docs/misc/api.func/API_FLOWCHART.md new file mode 100644 index 000000000..a46cd56e9 --- /dev/null +++ b/docs/misc/api.func/API_FLOWCHART.md @@ -0,0 +1,342 @@ +# api.func Execution Flowchart + +## Main API Communication Flow + +``` +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ API Communication Initialization │ +│ Entry point when api.func functions are called by installation scripts │ +└─────────────────────┬───────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ Prerequisites Check │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ Prerequisites Validation │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Check curl │ │ Check │ │ Check │ │ │ +│ │ │ Availability │ │ Diagnostics │ │ Random UUID │ │ │ +│ │ │ │ │ Setting │ │ │ │ +│ │ │ • command -v │ │ • DIAGNOSTICS │ │ • RANDOM_UUID │ │ +│ │ │ curl │ │ = "yes" │ │ not empty │ │ +│ │ │ • Return if │ │ • Return if │ │ • Return if │ │ +│ │ │ not found │ │ disabled │ │ not set │ │ +│ │ │ │ │ │ │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ Data Collection │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ System Information Gathering │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Get PVE │ │ Collect │ │ Prepare JSON │ │ │ +│ │ │ Version │ │ Environment │ │ Payload │ │ +│ │ │ │ │ Variables │ │ │ │ +│ │ │ • pveversion │ │ • CT_TYPE │ │ • Create JSON │ │ +│ │ │ command │ │ • DISK_SIZE │ │ structure │ │ +│ │ │ • Parse version │ │ • CORE_COUNT │ │ • Include all │ │ +│ │ │ • Extract │ │ • RAM_SIZE │ │ variables │ │ +│ │ │ major.minor │ │ • var_os │ │ • Format for API │ │ +│ │ │ │ │ • var_version │ │ │ │ +│ │ │ │ │ • NSAPP │ │ │ │ +│ │ │ │ │ • METHOD │ │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ API Request Execution │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ HTTP Request Processing │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Prepare │ │ Execute │ │ Handle │ │ │ +│ │ │ Request │ │ HTTP Request │ │ Response │ │ +│ │ │ │ │ │ │ │ │ +│ │ │ • Set API URL │ │ • curl -s -w │ │ • Capture HTTP │ │ +│ │ │ • Set headers │ │ "%{http_code}" │ │ status code │ │ +│ │ │ • Set payload │ │ • POST request │ │ • Store response │ │ +│ │ │ • Content-Type │ │ • JSON data │ │ • Handle errors │ │ +│ │ │ application/ │ │ • Follow │ │ gracefully │ │ +│ │ │ json │ │ redirects │ │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────────┘ +``` + +## LXC API Reporting Flow + +``` +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ POST_TO_API() Flow │ +│ Send LXC container installation data to API │ +└─────────────────────┬───────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ LXC Data Preparation │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ LXC-Specific Data Collection │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Set LXC │ │ Include LXC │ │ Set Status │ │ │ +│ │ │ Type │ │ Variables │ │ Information │ │ +│ │ │ │ │ │ │ │ │ +│ │ │ • ct_type: 1 │ │ • DISK_SIZE │ │ • status: │ │ +│ │ │ • type: "lxc" │ │ • CORE_COUNT │ │ "installing" │ │ +│ │ │ • Include all │ │ • RAM_SIZE │ │ • Include all │ │ +│ │ │ LXC data │ │ • var_os │ │ tracking data │ │ +│ │ │ │ │ • var_version │ │ │ │ +│ │ │ │ │ • DISABLEIP6 │ │ │ │ +│ │ │ │ │ • NSAPP │ │ │ │ +│ │ │ │ │ • METHOD │ │ │ │ +│ │ │ │ │ • pve_version │ │ │ │ +│ │ │ │ │ • random_id │ │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ JSON Payload Creation │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ JSON Structure Generation │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Create JSON │ │ Validate │ │ Format for │ │ │ +│ │ │ Structure │ │ Data │ │ API Request │ │ +│ │ │ │ │ │ │ │ │ +│ │ │ • Use heredoc │ │ • Check all │ │ • Ensure proper │ │ +│ │ │ syntax │ │ variables │ │ JSON format │ │ +│ │ │ • Include all │ │ are set │ │ • Escape special │ │ +│ │ │ required │ │ • Validate │ │ characters │ │ +│ │ │ fields │ │ data types │ │ • Set content │ │ +│ │ │ • Format │ │ • Handle │ │ type │ │ +│ │ │ properly │ │ missing │ │ │ │ +│ │ │ │ │ values │ │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────────┘ +``` + +## VM API Reporting Flow + +``` +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ POST_TO_API_VM() Flow │ +│ Send VM installation data to API │ +└─────────────────────┬───────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ VM Data Preparation │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ VM-Specific Data Collection │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Check │ │ Set VM │ │ Process Disk │ │ │ +│ │ │ Diagnostics │ │ Type │ │ Size │ │ +│ │ │ File │ │ │ │ │ │ +│ │ │ │ │ • ct_type: 2 │ │ • Remove 'G' │ │ +│ │ │ • Check file │ │ • type: "vm" │ │ suffix │ │ +│ │ │ existence │ │ • Include all │ │ • Convert to │ │ +│ │ │ • Read │ │ VM data │ │ numeric value │ │ +│ │ │ DIAGNOSTICS │ │ │ │ • Store in │ │ +│ │ │ setting │ │ │ │ DISK_SIZE_API │ │ +│ │ │ • Parse value │ │ │ │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ VM JSON Payload Creation │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ VM-Specific JSON Structure │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Include VM │ │ Set VM │ │ Format VM │ │ │ +│ │ │ Variables │ │ Status │ │ Data for API │ │ +│ │ │ │ │ │ │ │ │ +│ │ │ • DISK_SIZE_API │ │ • status: │ │ • Ensure proper │ │ +│ │ │ • CORE_COUNT │ │ "installing" │ │ JSON format │ │ +│ │ │ • RAM_SIZE │ │ • Include all │ │ • Handle VM- │ │ +│ │ │ • var_os │ │ tracking │ │ specific data │ │ +│ │ │ • var_version │ │ information │ │ • Set appropriate │ │ +│ │ │ • NSAPP │ │ │ │ content type │ │ +│ │ │ • METHOD │ │ │ │ │ │ +│ │ │ • pve_version │ │ │ │ │ │ +│ │ │ • random_id │ │ │ │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────────┘ +``` + +## Status Update Flow + +``` +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ POST_UPDATE_TO_API() Flow │ +│ Send installation completion status to API │ +└─────────────────────┬───────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ Update Prevention Check │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ Duplicate Update Prevention │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Check │ │ Set Flag │ │ Return Early │ │ │ +│ │ │ POST_UPDATE_ │ │ if First │ │ if Already │ │ +│ │ │ DONE │ │ Update │ │ Updated │ │ +│ │ │ │ │ │ │ │ │ +│ │ │ • Check if │ │ • Set │ │ • Return 0 │ │ +│ │ │ already │ │ POST_UPDATE_ │ │ • Skip API call │ │ +│ │ │ updated │ │ DONE=true │ │ • Prevent │ │ +│ │ │ • Prevent │ │ • Continue │ │ duplicate │ │ +│ │ │ duplicate │ │ with update │ │ requests │ │ +│ │ │ requests │ │ │ │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ Status and Error Processing │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ Status Determination │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Determine │ │ Get Error │ │ Prepare Status │ │ │ +│ │ │ Status │ │ Description │ │ Data │ │ +│ │ │ │ │ │ │ │ │ +│ │ │ • status: │ │ • Call │ │ • Include status │ │ +│ │ │ "success" or │ │ get_error_ │ │ • Include error │ │ +│ │ │ "failed" │ │ description() │ │ description │ │ +│ │ │ • Set exit │ │ • Get human- │ │ • Include random │ │ +│ │ │ code based │ │ readable │ │ ID for tracking │ │ +│ │ │ on status │ │ error message │ │ │ │ +│ │ │ • Default to │ │ • Handle │ │ │ │ +│ │ │ error if │ │ unknown │ │ │ │ +│ │ │ not set │ │ errors │ │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ Status Update API Request │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ Status Update Payload Creation │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Create │ │ Send Status │ │ Mark Update │ │ │ +│ │ │ Status JSON │ │ Update │ │ Complete │ │ +│ │ │ │ │ │ │ │ │ +│ │ │ • Include │ │ • POST to │ │ • Set │ │ +│ │ │ status │ │ updatestatus │ │ POST_UPDATE_ │ │ +│ │ │ • Include │ │ endpoint │ │ DONE=true │ │ +│ │ │ error │ │ • Include JSON │ │ • Prevent further │ │ +│ │ │ description │ │ payload │ │ updates │ │ +│ │ │ • Include │ │ • Handle │ │ • Complete │ │ +│ │ │ random_id │ │ response │ │ process │ │ +│ │ │ │ │ gracefully │ │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────────┘ +``` + +## Error Description Flow + +``` +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ GET_ERROR_DESCRIPTION() Flow │ +│ Convert numeric exit codes to human-readable explanations │ +└─────────────────────┬───────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ Error Code Classification │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ Error Code Categories │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ General │ │ Network │ │ LXC-Specific │ │ │ +│ │ │ System │ │ Errors │ │ Errors │ │ +│ │ │ Errors │ │ │ │ │ │ +│ │ │ │ │ • 18: Connection│ │ • 100-101: LXC │ │ +│ │ │ • 0-9: Basic │ │ failed │ │ install errors │ │ +│ │ │ errors │ │ • 22: Invalid │ │ • 200-209: LXC │ │ +│ │ │ • 126-128: │ │ argument │ │ creation errors │ │ +│ │ │ Command │ │ • 28: No space │ │ │ │ +│ │ │ errors │ │ • 35: Timeout │ │ │ │ +│ │ │ • 129-143: │ │ • 56: TLS error │ │ │ │ +│ │ │ Signal │ │ • 60: SSL cert │ │ │ │ +│ │ │ errors │ │ error │ │ │ │ +│ │ │ • 152: Resource │ │ │ │ │ │ +│ │ │ limit │ │ │ │ │ │ +│ │ │ • 255: Unknown │ │ │ │ │ │ +│ │ │ critical │ │ │ │ │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────────────────────┐ +│ Error Message Return │ +│ │ +│ ┌─────────────────────────────────────────────────────────────────────────────┐ │ +│ │ Error Message Formatting │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │ │ +│ │ │ Match Error │ │ Return │ │ Default Case │ │ │ +│ │ │ Code │ │ Description │ │ │ │ +│ │ │ │ │ │ │ │ │ +│ │ │ • Use case │ │ • Return │ │ • Return "Unknown │ │ +│ │ │ statement │ │ human- │ │ error code │ │ +│ │ │ • Match │ │ readable │ │ (exit_code)" │ │ +│ │ │ specific │ │ message │ │ • Handle │ │ +│ │ │ codes │ │ • Include │ │ unrecognized │ │ +│ │ │ • Handle │ │ context │ │ codes │ │ +│ │ │ ranges │ │ information │ │ • Provide fallback │ │ +│ │ │ │ │ │ │ message │ │ +│ │ └─────────────────┘ └─────────────────┘ └─────────────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────────────────────┘ +``` + +## Integration Points + +### With Installation Scripts +- **build.func**: Sends LXC installation data +- **vm-core.func**: Sends VM installation data +- **install.func**: Reports installation status +- **alpine-install.func**: Reports Alpine installation data + +### With Error Handling +- **error_handler.func**: Provides error explanations +- **core.func**: Uses error descriptions in silent execution +- **Diagnostic reporting**: Tracks error patterns + +### External Dependencies +- **curl**: HTTP client for API communication +- **Community Scripts API**: External API endpoint +- **Network connectivity**: Required for API communication diff --git a/docs/misc/api.func/API_FUNCTIONS_REFERENCE.md b/docs/misc/api.func/API_FUNCTIONS_REFERENCE.md new file mode 100644 index 000000000..732261f49 --- /dev/null +++ b/docs/misc/api.func/API_FUNCTIONS_REFERENCE.md @@ -0,0 +1,433 @@ +# api.func Functions Reference + +## Overview + +This document provides a comprehensive alphabetical reference of all functions in `api.func`, including parameters, dependencies, usage examples, and error handling. + +## Function Categories + +### Error Description Functions + +#### `get_error_description()` +**Purpose**: Convert numeric exit codes to human-readable explanations +**Parameters**: +- `$1` - Exit code to explain +**Returns**: Human-readable error explanation string +**Side Effects**: None +**Dependencies**: None +**Environment Variables Used**: None + +**Supported Exit Codes**: +- **General System**: 0-9, 18, 22, 28, 35, 56, 60, 125-128, 129-143, 152, 255 +- **LXC-Specific**: 100-101, 200-209 +- **Docker**: 125 + +**Usage Example**: +```bash +error_msg=$(get_error_description 127) +echo "Error 127: $error_msg" +# Output: Error 127: Command not found: Incorrect path or missing dependency. +``` + +**Error Code Examples**: +```bash +get_error_description 0 # " " (space) +get_error_description 1 # "General error: An unspecified error occurred." +get_error_description 127 # "Command not found: Incorrect path or missing dependency." +get_error_description 200 # "LXC creation failed." +get_error_description 255 # "Unknown critical error, often due to missing permissions or broken scripts." +``` + +### API Communication Functions + +#### `post_to_api()` +**Purpose**: Send LXC container installation data to community-scripts.org API +**Parameters**: None (uses environment variables) +**Returns**: None +**Side Effects**: +- Sends HTTP POST request to API +- Stores response in RESPONSE variable +- Requires curl command and network connectivity +**Dependencies**: `curl` command +**Environment Variables Used**: `DIAGNOSTICS`, `RANDOM_UUID`, `CT_TYPE`, `DISK_SIZE`, `CORE_COUNT`, `RAM_SIZE`, `var_os`, `var_version`, `DISABLEIP6`, `NSAPP`, `METHOD` + +**Prerequisites**: +- `curl` command must be available +- `DIAGNOSTICS` must be set to "yes" +- `RANDOM_UUID` must be set and not empty + +**API Endpoint**: `http://api.community-scripts.org/dev/upload` + +**JSON Payload Structure**: +```json +{ + "ct_type": 1, + "type": "lxc", + "disk_size": 8, + "core_count": 2, + "ram_size": 2048, + "os_type": "debian", + "os_version": "12", + "disableip6": "true", + "nsapp": "plex", + "method": "install", + "pve_version": "8.0", + "status": "installing", + "random_id": "uuid-string" +} +``` + +**Usage Example**: +```bash +export DIAGNOSTICS="yes" +export RANDOM_UUID="$(uuidgen)" +export CT_TYPE=1 +export DISK_SIZE=8 +export CORE_COUNT=2 +export RAM_SIZE=2048 +export var_os="debian" +export var_version="12" +export NSAPP="plex" +export METHOD="install" + +post_to_api +``` + +#### `post_to_api_vm()` +**Purpose**: Send VM installation data to community-scripts.org API +**Parameters**: None (uses environment variables) +**Returns**: None +**Side Effects**: +- Sends HTTP POST request to API +- Stores response in RESPONSE variable +- Requires curl command and network connectivity +**Dependencies**: `curl` command, diagnostics file +**Environment Variables Used**: `DIAGNOSTICS`, `RANDOM_UUID`, `DISK_SIZE`, `CORE_COUNT`, `RAM_SIZE`, `var_os`, `var_version`, `NSAPP`, `METHOD` + +**Prerequisites**: +- `/usr/local/community-scripts/diagnostics` file must exist +- `DIAGNOSTICS` must be set to "yes" in diagnostics file +- `curl` command must be available +- `RANDOM_UUID` must be set and not empty + +**API Endpoint**: `http://api.community-scripts.org/dev/upload` + +**JSON Payload Structure**: +```json +{ + "ct_type": 2, + "type": "vm", + "disk_size": 8, + "core_count": 2, + "ram_size": 2048, + "os_type": "debian", + "os_version": "12", + "disableip6": "", + "nsapp": "plex", + "method": "install", + "pve_version": "8.0", + "status": "installing", + "random_id": "uuid-string" +} +``` + +**Usage Example**: +```bash +# Create diagnostics file +echo "DIAGNOSTICS=yes" > /usr/local/community-scripts/diagnostics + +export RANDOM_UUID="$(uuidgen)" +export DISK_SIZE="8G" +export CORE_COUNT=2 +export RAM_SIZE=2048 +export var_os="debian" +export var_version="12" +export NSAPP="plex" +export METHOD="install" + +post_to_api_vm +``` + +#### `post_update_to_api()` +**Purpose**: Send installation completion status to community-scripts.org API +**Parameters**: +- `$1` - Status ("success" or "failed", default: "failed") +- `$2` - Exit code (default: 1) +**Returns**: None +**Side Effects**: +- Sends HTTP POST request to API +- Sets POST_UPDATE_DONE=true to prevent duplicates +- Stores response in RESPONSE variable +**Dependencies**: `curl` command, `get_error_description()` +**Environment Variables Used**: `DIAGNOSTICS`, `RANDOM_UUID` + +**Prerequisites**: +- `curl` command must be available +- `DIAGNOSTICS` must be set to "yes" +- `RANDOM_UUID` must be set and not empty +- POST_UPDATE_DONE must be false (prevents duplicates) + +**API Endpoint**: `http://api.community-scripts.org/dev/upload/updatestatus` + +**JSON Payload Structure**: +```json +{ + "status": "success", + "error": "Error description from get_error_description()", + "random_id": "uuid-string" +} +``` + +**Usage Example**: +```bash +export DIAGNOSTICS="yes" +export RANDOM_UUID="$(uuidgen)" + +# Report successful installation +post_update_to_api "success" 0 + +# Report failed installation +post_update_to_api "failed" 127 +``` + +## Function Call Hierarchy + +### API Communication Flow +``` +post_to_api() +├── Check curl availability +├── Check DIAGNOSTICS setting +├── Check RANDOM_UUID +├── Get PVE version +├── Create JSON payload +└── Send HTTP POST request + +post_to_api_vm() +├── Check diagnostics file +├── Check curl availability +├── Check DIAGNOSTICS setting +├── Check RANDOM_UUID +├── Process disk size +├── Get PVE version +├── Create JSON payload +└── Send HTTP POST request + +post_update_to_api() +├── Check POST_UPDATE_DONE flag +├── Check curl availability +├── Check DIAGNOSTICS setting +├── Check RANDOM_UUID +├── Determine status and exit code +├── Get error description +├── Create JSON payload +├── Send HTTP POST request +└── Set POST_UPDATE_DONE=true +``` + +### Error Description Flow +``` +get_error_description() +├── Match exit code +├── Return appropriate description +└── Handle unknown codes +``` + +## Error Code Reference + +### General System Errors +| Code | Description | +|------|-------------| +| 0 | (space) | +| 1 | General error: An unspecified error occurred. | +| 2 | Incorrect shell usage or invalid command arguments. | +| 3 | Unexecuted function or invalid shell condition. | +| 4 | Error opening a file or invalid path. | +| 5 | I/O error: An input/output failure occurred. | +| 6 | No such device or address. | +| 7 | Insufficient memory or resource exhaustion. | +| 8 | Non-executable file or invalid file format. | +| 9 | Failed child process execution. | +| 18 | Connection to a remote server failed. | +| 22 | Invalid argument or faulty network connection. | +| 28 | No space left on device. | +| 35 | Timeout while establishing a connection. | +| 56 | Faulty TLS connection. | +| 60 | SSL certificate error. | + +### Command Execution Errors +| Code | Description | +|------|-------------| +| 125 | Docker error: Container could not start. | +| 126 | Command not executable: Incorrect permissions or missing dependencies. | +| 127 | Command not found: Incorrect path or missing dependency. | +| 128 | Invalid exit signal, e.g., incorrect Git command. | + +### Signal Errors +| Code | Description | +|------|-------------| +| 129 | Signal 1 (SIGHUP): Process terminated due to hangup. | +| 130 | Signal 2 (SIGINT): Manual termination via Ctrl+C. | +| 132 | Signal 4 (SIGILL): Illegal machine instruction. | +| 133 | Signal 5 (SIGTRAP): Debugging error or invalid breakpoint signal. | +| 134 | Signal 6 (SIGABRT): Program aborted itself. | +| 135 | Signal 7 (SIGBUS): Memory error, invalid memory address. | +| 137 | Signal 9 (SIGKILL): Process forcibly terminated (OOM-killer or 'kill -9'). | +| 139 | Signal 11 (SIGSEGV): Segmentation fault, possibly due to invalid pointer access. | +| 141 | Signal 13 (SIGPIPE): Pipe closed unexpectedly. | +| 143 | Signal 15 (SIGTERM): Process terminated normally. | +| 152 | Signal 24 (SIGXCPU): CPU time limit exceeded. | + +### LXC-Specific Errors +| Code | Description | +|------|-------------| +| 100 | LXC install error: Unexpected error in create_lxc.sh. | +| 101 | LXC install error: No network connection detected. | +| 200 | LXC creation failed. | +| 201 | LXC error: Invalid Storage class. | +| 202 | User aborted menu in create_lxc.sh. | +| 203 | CTID not set in create_lxc.sh. | +| 204 | PCT_OSTYPE not set in create_lxc.sh. | +| 205 | CTID cannot be less than 100 in create_lxc.sh. | +| 206 | CTID already in use in create_lxc.sh. | +| 207 | Template not found in create_lxc.sh. | +| 208 | Error downloading template in create_lxc.sh. | +| 209 | Container creation failed, but template is intact in create_lxc.sh. | + +### Other Errors +| Code | Description | +|------|-------------| +| 255 | Unknown critical error, often due to missing permissions or broken scripts. | +| * | Unknown error code (exit_code). | + +## Environment Variable Dependencies + +### Required Variables +- **`DIAGNOSTICS`**: Enable/disable diagnostic reporting ("yes"/"no") +- **`RANDOM_UUID`**: Unique identifier for tracking + +### Optional Variables +- **`CT_TYPE`**: Container type (1 for LXC, 2 for VM) +- **`DISK_SIZE`**: Disk size in GB (or GB with 'G' suffix for VM) +- **`CORE_COUNT`**: Number of CPU cores +- **`RAM_SIZE`**: RAM size in MB +- **`var_os`**: Operating system type +- **`var_version`**: OS version +- **`DISABLEIP6`**: IPv6 disable setting +- **`NSAPP`**: Namespace application name +- **`METHOD`**: Installation method + +### Internal Variables +- **`POST_UPDATE_DONE`**: Prevents duplicate status updates +- **`API_URL`**: Community scripts API endpoint +- **`JSON_PAYLOAD`**: API request payload +- **`RESPONSE`**: API response +- **`DISK_SIZE_API`**: Processed disk size for VM API + +## Error Handling Patterns + +### API Communication Errors +- All API functions handle curl failures gracefully +- Network errors don't block installation process +- Missing prerequisites cause early return +- Duplicate updates are prevented + +### Error Description Errors +- Unknown error codes return generic message +- All error codes are handled with case statement +- Fallback message includes the actual error code + +### Prerequisites Validation +- Check curl availability before API calls +- Validate DIAGNOSTICS setting +- Ensure RANDOM_UUID is set +- Check for duplicate updates + +## Integration Examples + +### With build.func +```bash +#!/usr/bin/env bash +source core.func +source api.func +source build.func + +# Set up API reporting +export DIAGNOSTICS="yes" +export RANDOM_UUID="$(uuidgen)" + +# Report installation start +post_to_api + +# Container creation... +# ... build.func code ... + +# Report completion +if [[ $? -eq 0 ]]; then + post_update_to_api "success" 0 +else + post_update_to_api "failed" $? +fi +``` + +### With vm-core.func +```bash +#!/usr/bin/env bash +source core.func +source api.func +source vm-core.func + +# Set up API reporting +export DIAGNOSTICS="yes" +export RANDOM_UUID="$(uuidgen)" + +# Report VM installation start +post_to_api_vm + +# VM creation... +# ... vm-core.func code ... + +# Report completion +post_update_to_api "success" 0 +``` + +### With error_handler.func +```bash +#!/usr/bin/env bash +source core.func +source error_handler.func +source api.func + +# Use error descriptions +error_code=127 +error_msg=$(get_error_description $error_code) +echo "Error $error_code: $error_msg" + +# Report error to API +post_update_to_api "failed" $error_code +``` + +## Best Practices + +### API Usage +1. Always check prerequisites before API calls +2. Use unique identifiers for tracking +3. Handle API failures gracefully +4. Don't block installation on API failures + +### Error Reporting +1. Use appropriate error codes +2. Provide meaningful error descriptions +3. Report both success and failure cases +4. Prevent duplicate status updates + +### Diagnostic Reporting +1. Respect user privacy settings +2. Only send data when diagnostics enabled +3. Use anonymous tracking identifiers +4. Include relevant system information + +### Error Handling +1. Handle unknown error codes gracefully +2. Provide fallback error messages +3. Include error code in unknown error messages +4. Use consistent error message format diff --git a/docs/misc/api.func/API_INTEGRATION.md b/docs/misc/api.func/API_INTEGRATION.md new file mode 100644 index 000000000..f325dace2 --- /dev/null +++ b/docs/misc/api.func/API_INTEGRATION.md @@ -0,0 +1,643 @@ +# api.func Integration Guide + +## Overview + +This document describes how `api.func` integrates with other components in the Proxmox Community Scripts project, including dependencies, data flow, and API surface. + +## Dependencies + +### External Dependencies + +#### Required Commands +- **`curl`**: HTTP client for API communication +- **`uuidgen`**: Generate unique identifiers (optional, can use other methods) + +#### Optional Commands +- **None**: No other external command dependencies + +### Internal Dependencies + +#### Environment Variables from Other Scripts +- **build.func**: Provides container creation variables +- **vm-core.func**: Provides VM creation variables +- **core.func**: Provides system information variables +- **Installation scripts**: Provide application-specific variables + +## Integration Points + +### With build.func + +#### LXC Container Reporting +```bash +# build.func uses api.func for container reporting +source core.func +source api.func +source build.func + +# Set up API reporting +export DIAGNOSTICS="yes" +export RANDOM_UUID="$(uuidgen)" + +# Container creation with API reporting +create_container() { + # Set container parameters + export CT_TYPE=1 + export DISK_SIZE="$var_disk" + export CORE_COUNT="$var_cpu" + export RAM_SIZE="$var_ram" + export var_os="$var_os" + export var_version="$var_version" + export NSAPP="$APP" + export METHOD="install" + + # Report installation start + post_to_api + + # Container creation using build.func + # ... build.func container creation logic ... + + # Report completion + if [[ $? -eq 0 ]]; then + post_update_to_api "success" 0 + else + post_update_to_api "failed" $? + fi +} +``` + +#### Error Reporting Integration +```bash +# build.func uses api.func for error reporting +handle_container_error() { + local exit_code=$1 + local error_msg=$(get_error_description $exit_code) + + echo "Container creation failed: $error_msg" + post_update_to_api "failed" $exit_code +} +``` + +### With vm-core.func + +#### VM Installation Reporting +```bash +# vm-core.func uses api.func for VM reporting +source core.func +source api.func +source vm-core.func + +# Set up VM API reporting +mkdir -p /usr/local/community-scripts +echo "DIAGNOSTICS=yes" > /usr/local/community-scripts/diagnostics + +export RANDOM_UUID="$(uuidgen)" + +# VM creation with API reporting +create_vm() { + # Set VM parameters + export DISK_SIZE="${var_disk}G" + export CORE_COUNT="$var_cpu" + export RAM_SIZE="$var_ram" + export var_os="$var_os" + export var_version="$var_version" + export NSAPP="$APP" + export METHOD="install" + + # Report VM installation start + post_to_api_vm + + # VM creation using vm-core.func + # ... vm-core.func VM creation logic ... + + # Report completion + post_update_to_api "success" 0 +} +``` + +### With core.func + +#### System Information Integration +```bash +# core.func provides system information for api.func +source core.func +source api.func + +# Get system information for API reporting +get_system_info_for_api() { + # Get PVE version using core.func utilities + local pve_version=$(pveversion | awk -F'[/ ]' '{print $2}') + + # Set API parameters + export var_os="$var_os" + export var_version="$var_version" + + # Use core.func error handling with api.func reporting + if silent apt-get update; then + post_update_to_api "success" 0 + else + post_update_to_api "failed" $? + fi +} +``` + +### With error_handler.func + +#### Error Description Integration +```bash +# error_handler.func uses api.func for error descriptions +source core.func +source error_handler.func +source api.func + +# Enhanced error handler with API reporting +enhanced_error_handler() { + local exit_code=${1:-$?} + local command=${2:-${BASH_COMMAND:-unknown}} + + # Get error description from api.func + local error_msg=$(get_error_description $exit_code) + + # Display error information + echo "Error $exit_code: $error_msg" + echo "Command: $command" + + # Report error to API + export DIAGNOSTICS="yes" + export RANDOM_UUID="$(uuidgen)" + post_update_to_api "failed" $exit_code + + # Use standard error handler + error_handler $exit_code $command +} +``` + +### With install.func + +#### Installation Process Reporting +```bash +# install.func uses api.func for installation reporting +source core.func +source api.func +source install.func + +# Installation with API reporting +install_package_with_reporting() { + local package="$1" + + # Set up API reporting + export DIAGNOSTICS="yes" + export RANDOM_UUID="$(uuidgen)" + export NSAPP="$package" + export METHOD="install" + + # Report installation start + post_to_api + + # Package installation using install.func + if install_package "$package"; then + echo "$package installed successfully" + post_update_to_api "success" 0 + return 0 + else + local exit_code=$? + local error_msg=$(get_error_description $exit_code) + echo "$package installation failed: $error_msg" + post_update_to_api "failed" $exit_code + return $exit_code + fi +} +``` + +### With alpine-install.func + +#### Alpine Installation Reporting +```bash +# alpine-install.func uses api.func for Alpine reporting +source core.func +source api.func +source alpine-install.func + +# Alpine installation with API reporting +install_alpine_with_reporting() { + local app="$1" + + # Set up API reporting + export DIAGNOSTICS="yes" + export RANDOM_UUID="$(uuidgen)" + export NSAPP="$app" + export METHOD="install" + export var_os="alpine" + + # Report Alpine installation start + post_to_api + + # Alpine installation using alpine-install.func + if install_alpine_app "$app"; then + echo "Alpine $app installed successfully" + post_update_to_api "success" 0 + return 0 + else + local exit_code=$? + local error_msg=$(get_error_description $exit_code) + echo "Alpine $app installation failed: $error_msg" + post_update_to_api "failed" $exit_code + return $exit_code + fi +} +``` + +### With alpine-tools.func + +#### Alpine Tools Reporting +```bash +# alpine-tools.func uses api.func for Alpine tools reporting +source core.func +source api.func +source alpine-tools.func + +# Alpine tools with API reporting +run_alpine_tool_with_reporting() { + local tool="$1" + + # Set up API reporting + export DIAGNOSTICS="yes" + export RANDOM_UUID="$(uuidgen)" + export NSAPP="alpine-tools" + export METHOD="tool" + + # Report tool execution start + post_to_api + + # Run Alpine tool using alpine-tools.func + if run_alpine_tool "$tool"; then + echo "Alpine tool $tool executed successfully" + post_update_to_api "success" 0 + return 0 + else + local exit_code=$? + local error_msg=$(get_error_description $exit_code) + echo "Alpine tool $tool failed: $error_msg" + post_update_to_api "failed" $exit_code + return $exit_code + fi +} +``` + +### With passthrough.func + +#### Hardware Passthrough Reporting +```bash +# passthrough.func uses api.func for hardware reporting +source core.func +source api.func +source passthrough.func + +# Hardware passthrough with API reporting +configure_passthrough_with_reporting() { + local hardware_type="$1" + + # Set up API reporting + export DIAGNOSTICS="yes" + export RANDOM_UUID="$(uuidgen)" + export NSAPP="passthrough" + export METHOD="hardware" + + # Report passthrough configuration start + post_to_api + + # Configure passthrough using passthrough.func + if configure_passthrough "$hardware_type"; then + echo "Hardware passthrough configured successfully" + post_update_to_api "success" 0 + return 0 + else + local exit_code=$? + local error_msg=$(get_error_description $exit_code) + echo "Hardware passthrough failed: $error_msg" + post_update_to_api "failed" $exit_code + return $exit_code + fi +} +``` + +### With tools.func + +#### Maintenance Operations Reporting +```bash +# tools.func uses api.func for maintenance reporting +source core.func +source api.func +source tools.func + +# Maintenance operations with API reporting +run_maintenance_with_reporting() { + local operation="$1" + + # Set up API reporting + export DIAGNOSTICS="yes" + export RANDOM_UUID="$(uuidgen)" + export NSAPP="maintenance" + export METHOD="tool" + + # Report maintenance start + post_to_api + + # Run maintenance using tools.func + if run_maintenance_operation "$operation"; then + echo "Maintenance operation $operation completed successfully" + post_update_to_api "success" 0 + return 0 + else + local exit_code=$? + local error_msg=$(get_error_description $exit_code) + echo "Maintenance operation $operation failed: $error_msg" + post_update_to_api "failed" $exit_code + return $exit_code + fi +} +``` + +## Data Flow + +### Input Data + +#### Environment Variables from Other Scripts +- **`CT_TYPE`**: Container type (1 for LXC, 2 for VM) +- **`DISK_SIZE`**: Disk size in GB +- **`CORE_COUNT`**: Number of CPU cores +- **`RAM_SIZE`**: RAM size in MB +- **`var_os`**: Operating system type +- **`var_version`**: OS version +- **`DISABLEIP6`**: IPv6 disable setting +- **`NSAPP`**: Namespace application name +- **`METHOD`**: Installation method +- **`DIAGNOSTICS`**: Enable/disable diagnostic reporting +- **`RANDOM_UUID`**: Unique identifier for tracking + +#### Function Parameters +- **Exit codes**: Passed to `get_error_description()` and `post_update_to_api()` +- **Status information**: Passed to `post_update_to_api()` +- **API endpoints**: Hardcoded in functions + +#### System Information +- **PVE version**: Retrieved from `pveversion` command +- **Disk size processing**: Processed for VM API (removes 'G' suffix) +- **Error codes**: Retrieved from command exit codes + +### Processing Data + +#### API Request Preparation +- **JSON payload creation**: Format data for API consumption +- **Data validation**: Ensure required fields are present +- **Error handling**: Handle missing or invalid data +- **Content type setting**: Set appropriate HTTP headers + +#### Error Processing +- **Error code mapping**: Map numeric codes to descriptions +- **Error message formatting**: Format error descriptions +- **Unknown error handling**: Handle unrecognized error codes +- **Fallback messages**: Provide default error messages + +#### API Communication +- **HTTP request preparation**: Prepare curl commands +- **Response handling**: Capture HTTP response codes +- **Error handling**: Handle network and API errors +- **Duplicate prevention**: Prevent duplicate status updates + +### Output Data + +#### API Communication +- **HTTP requests**: Sent to community-scripts.org API +- **Response codes**: Captured from API responses +- **Error information**: Reported to API +- **Status updates**: Sent to API + +#### Error Information +- **Error descriptions**: Human-readable error messages +- **Error codes**: Mapped to descriptions +- **Context information**: Error context and details +- **Fallback messages**: Default error messages + +#### System State +- **POST_UPDATE_DONE**: Prevents duplicate updates +- **RESPONSE**: Stores API response +- **JSON_PAYLOAD**: Stores formatted API data +- **API_URL**: Stores API endpoint + +## API Surface + +### Public Functions + +#### Error Description +- **`get_error_description()`**: Convert exit codes to explanations +- **Parameters**: Exit code to explain +- **Returns**: Human-readable explanation string +- **Usage**: Called by other functions and scripts + +#### API Communication +- **`post_to_api()`**: Send LXC installation data +- **`post_to_api_vm()`**: Send VM installation data +- **`post_update_to_api()`**: Send status updates +- **Parameters**: Status and exit code (for updates) +- **Returns**: None +- **Usage**: Called by installation scripts + +### Internal Functions + +#### None +- All functions in api.func are public +- No internal helper functions +- Direct implementation of all functionality + +### Global Variables + +#### Configuration Variables +- **`DIAGNOSTICS`**: Diagnostic reporting setting +- **`RANDOM_UUID`**: Unique tracking identifier +- **`POST_UPDATE_DONE`**: Duplicate update prevention + +#### Data Variables +- **`CT_TYPE`**: Container type +- **`DISK_SIZE`**: Disk size +- **`CORE_COUNT`**: CPU core count +- **`RAM_SIZE`**: RAM size +- **`var_os`**: Operating system +- **`var_version`**: OS version +- **`DISABLEIP6`**: IPv6 setting +- **`NSAPP`**: Application namespace +- **`METHOD`**: Installation method + +#### Internal Variables +- **`API_URL`**: API endpoint URL +- **`JSON_PAYLOAD`**: API request payload +- **`RESPONSE`**: API response +- **`DISK_SIZE_API`**: Processed disk size for VM API + +## Integration Patterns + +### Standard Integration Pattern + +```bash +#!/usr/bin/env bash +# Standard integration pattern + +# 1. Source core.func first +source core.func + +# 2. Source api.func +source api.func + +# 3. Set up API reporting +export DIAGNOSTICS="yes" +export RANDOM_UUID="$(uuidgen)" + +# 4. Set application parameters +export NSAPP="$APP" +export METHOD="install" + +# 5. Report installation start +post_to_api + +# 6. Perform installation +# ... installation logic ... + +# 7. Report completion +post_update_to_api "success" 0 +``` + +### Minimal Integration Pattern + +```bash +#!/usr/bin/env bash +# Minimal integration pattern + +source api.func + +# Basic error reporting +export DIAGNOSTICS="yes" +export RANDOM_UUID="$(uuidgen)" + +# Report failure +post_update_to_api "failed" 127 +``` + +### Advanced Integration Pattern + +```bash +#!/usr/bin/env bash +# Advanced integration pattern + +source core.func +source api.func +source error_handler.func + +# Set up comprehensive API reporting +export DIAGNOSTICS="yes" +export RANDOM_UUID="$(uuidgen)" +export CT_TYPE=1 +export DISK_SIZE=8 +export CORE_COUNT=2 +export RAM_SIZE=2048 +export var_os="debian" +export var_version="12" +export METHOD="install" + +# Enhanced error handling with API reporting +enhanced_error_handler() { + local exit_code=${1:-$?} + local command=${2:-${BASH_COMMAND:-unknown}} + + local error_msg=$(get_error_description $exit_code) + echo "Error $exit_code: $error_msg" + + post_update_to_api "failed" $exit_code + error_handler $exit_code $command +} + +trap 'enhanced_error_handler' ERR + +# Advanced operations with API reporting +post_to_api +# ... operations ... +post_update_to_api "success" 0 +``` + +## Error Handling Integration + +### Automatic Error Reporting +- **Error Descriptions**: Provides human-readable error messages +- **API Integration**: Reports errors to community-scripts.org API +- **Error Tracking**: Tracks error patterns for project improvement +- **Diagnostic Data**: Contributes to anonymous usage analytics + +### Manual Error Reporting +- **Custom Error Codes**: Use appropriate error codes for different scenarios +- **Error Context**: Provide context information for errors +- **Status Updates**: Report both success and failure cases +- **Error Analysis**: Analyze error patterns and trends + +### API Communication Errors +- **Network Failures**: Handle API communication failures gracefully +- **Missing Prerequisites**: Check prerequisites before API calls +- **Duplicate Prevention**: Prevent duplicate status updates +- **Error Recovery**: Handle API errors without blocking installation + +## Performance Considerations + +### API Communication Overhead +- **Minimal Impact**: API calls add minimal overhead +- **Asynchronous**: API calls don't block installation process +- **Error Handling**: API failures don't affect installation +- **Optional**: API reporting is optional and can be disabled + +### Memory Usage +- **Minimal Footprint**: API functions use minimal memory +- **Variable Reuse**: Global variables reused across functions +- **No Memory Leaks**: Proper cleanup prevents memory leaks +- **Efficient Processing**: Efficient JSON payload creation + +### Execution Speed +- **Fast API Calls**: Quick API communication +- **Efficient Error Processing**: Fast error code processing +- **Minimal Delay**: Minimal delay in API operations +- **Non-blocking**: API calls don't block installation + +## Security Considerations + +### Data Privacy +- **Anonymous Reporting**: Only anonymous data is sent +- **No Sensitive Data**: No sensitive information is transmitted +- **User Control**: Users can disable diagnostic reporting +- **Data Minimization**: Only necessary data is sent + +### API Security +- **HTTPS**: API communication uses secure protocols +- **Data Validation**: API data is validated before sending +- **Error Handling**: API errors are handled securely +- **No Credentials**: No authentication credentials are sent + +### Network Security +- **Secure Communication**: Uses secure HTTP protocols +- **Error Handling**: Network errors are handled gracefully +- **No Data Leakage**: No sensitive data is leaked +- **Secure Endpoints**: Uses trusted API endpoints + +## Future Integration Considerations + +### Extensibility +- **New API Endpoints**: Easy to add new API endpoints +- **Additional Data**: Easy to add new data fields +- **Error Codes**: Easy to add new error code descriptions +- **API Versions**: Easy to support new API versions + +### Compatibility +- **API Versioning**: Compatible with different API versions +- **Data Format**: Compatible with different data formats +- **Error Codes**: Compatible with different error code systems +- **Network Protocols**: Compatible with different network protocols + +### Performance +- **Optimization**: API communication can be optimized +- **Caching**: API responses can be cached +- **Batch Operations**: Multiple operations can be batched +- **Async Processing**: API calls can be made asynchronous diff --git a/docs/misc/api.func/API_USAGE_EXAMPLES.md b/docs/misc/api.func/API_USAGE_EXAMPLES.md new file mode 100644 index 000000000..616ebc927 --- /dev/null +++ b/docs/misc/api.func/API_USAGE_EXAMPLES.md @@ -0,0 +1,794 @@ +# api.func Usage Examples + +## Overview + +This document provides practical usage examples for `api.func` functions, covering common scenarios, integration patterns, and best practices. + +## Basic API Setup + +### Standard API Initialization + +```bash +#!/usr/bin/env bash +# Standard API setup for LXC containers + +source api.func + +# Set up diagnostic reporting +export DIAGNOSTICS="yes" +export RANDOM_UUID="$(uuidgen)" + +# Set container parameters +export CT_TYPE=1 +export DISK_SIZE=8 +export CORE_COUNT=2 +export RAM_SIZE=2048 +export var_os="debian" +export var_version="12" +export NSAPP="plex" +export METHOD="install" + +# Report installation start +post_to_api + +# Your installation code here +# ... installation logic ... + +# Report completion +if [[ $? -eq 0 ]]; then + post_update_to_api "success" 0 +else + post_update_to_api "failed" $? +fi +``` + +### VM API Setup + +```bash +#!/usr/bin/env bash +# API setup for VMs + +source api.func + +# Create diagnostics file for VM +mkdir -p /usr/local/community-scripts +echo "DIAGNOSTICS=yes" > /usr/local/community-scripts/diagnostics + +# Set up VM parameters +export RANDOM_UUID="$(uuidgen)" +export DISK_SIZE="20G" +export CORE_COUNT=4 +export RAM_SIZE=4096 +export var_os="ubuntu" +export var_version="22.04" +export NSAPP="nextcloud" +export METHOD="install" + +# Report VM installation start +post_to_api_vm + +# Your VM installation code here +# ... VM creation logic ... + +# Report completion +post_update_to_api "success" 0 +``` + +## Error Description Examples + +### Basic Error Explanation + +```bash +#!/usr/bin/env bash +source api.func + +# Explain common error codes +echo "Error 0: '$(get_error_description 0)'" +echo "Error 1: $(get_error_description 1)" +echo "Error 127: $(get_error_description 127)" +echo "Error 200: $(get_error_description 200)" +echo "Error 255: $(get_error_description 255)" +``` + +### Error Code Testing + +```bash +#!/usr/bin/env bash +source api.func + +# Test all error codes +test_error_codes() { + local codes=(0 1 2 127 128 130 137 139 143 200 203 205 255) + + for code in "${codes[@]}"; do + echo "Code $code: $(get_error_description $code)" + done +} + +test_error_codes +``` + +### Error Handling with Descriptions + +```bash +#!/usr/bin/env bash +source api.func + +# Function with error handling +run_command_with_error_handling() { + local command="$1" + local description="$2" + + echo "Running: $description" + + if $command; then + echo "Success: $description" + return 0 + else + local exit_code=$? + local error_msg=$(get_error_description $exit_code) + echo "Error $exit_code: $error_msg" + return $exit_code + fi +} + +# Usage +run_command_with_error_handling "apt-get update" "Package list update" +run_command_with_error_handling "nonexistent_command" "Test command" +``` + +## API Communication Examples + +### LXC Installation Reporting + +```bash +#!/usr/bin/env bash +source api.func + +# Complete LXC installation with API reporting +install_lxc_with_reporting() { + local app="$1" + local ctid="$2" + + # Set up API reporting + export DIAGNOSTICS="yes" + export RANDOM_UUID="$(uuidgen)" + export CT_TYPE=1 + export DISK_SIZE=10 + export CORE_COUNT=2 + export RAM_SIZE=2048 + export var_os="debian" + export var_version="12" + export NSAPP="$app" + export METHOD="install" + + # Report installation start + post_to_api + + # Installation process + echo "Installing $app container (ID: $ctid)..." + + # Simulate installation + sleep 2 + + # Check if installation succeeded + if [[ $? -eq 0 ]]; then + echo "Installation completed successfully" + post_update_to_api "success" 0 + return 0 + else + echo "Installation failed" + post_update_to_api "failed" $? + return 1 + fi +} + +# Install multiple containers +install_lxc_with_reporting "plex" "100" +install_lxc_with_reporting "nextcloud" "101" +install_lxc_with_reporting "nginx" "102" +``` + +### VM Installation Reporting + +```bash +#!/usr/bin/env bash +source api.func + +# Complete VM installation with API reporting +install_vm_with_reporting() { + local app="$1" + local vmid="$2" + + # Create diagnostics file + mkdir -p /usr/local/community-scripts + echo "DIAGNOSTICS=yes" > /usr/local/community-scripts/diagnostics + + # Set up API reporting + export RANDOM_UUID="$(uuidgen)" + export DISK_SIZE="20G" + export CORE_COUNT=4 + export RAM_SIZE=4096 + export var_os="ubuntu" + export var_version="22.04" + export NSAPP="$app" + export METHOD="install" + + # Report VM installation start + post_to_api_vm + + # VM installation process + echo "Installing $app VM (ID: $vmid)..." + + # Simulate VM creation + sleep 3 + + # Check if VM creation succeeded + if [[ $? -eq 0 ]]; then + echo "VM installation completed successfully" + post_update_to_api "success" 0 + return 0 + else + echo "VM installation failed" + post_update_to_api "failed" $? + return 1 + fi +} + +# Install multiple VMs +install_vm_with_reporting "nextcloud" "200" +install_vm_with_reporting "wordpress" "201" +``` + +## Status Update Examples + +### Success Reporting + +```bash +#!/usr/bin/env bash +source api.func + +# Report successful installation +report_success() { + local operation="$1" + + export DIAGNOSTICS="yes" + export RANDOM_UUID="$(uuidgen)" + + echo "Reporting successful $operation" + post_update_to_api "success" 0 +} + +# Usage +report_success "container installation" +report_success "package installation" +report_success "service configuration" +``` + +### Failure Reporting + +```bash +#!/usr/bin/env bash +source api.func + +# Report failed installation +report_failure() { + local operation="$1" + local exit_code="$2" + + export DIAGNOSTICS="yes" + export RANDOM_UUID="$(uuidgen)" + + local error_msg=$(get_error_description $exit_code) + echo "Reporting failed $operation: $error_msg" + post_update_to_api "failed" $exit_code +} + +# Usage +report_failure "container creation" 200 +report_failure "package installation" 127 +report_failure "service start" 1 +``` + +### Conditional Status Reporting + +```bash +#!/usr/bin/env bash +source api.func + +# Conditional status reporting +report_installation_status() { + local operation="$1" + local exit_code="$2" + + export DIAGNOSTICS="yes" + export RANDOM_UUID="$(uuidgen)" + + if [[ $exit_code -eq 0 ]]; then + echo "Reporting successful $operation" + post_update_to_api "success" 0 + else + local error_msg=$(get_error_description $exit_code) + echo "Reporting failed $operation: $error_msg" + post_update_to_api "failed" $exit_code + fi +} + +# Usage +report_installation_status "container creation" 0 +report_installation_status "package installation" 127 +``` + +## Advanced Usage Examples + +### Batch Installation with API Reporting + +```bash +#!/usr/bin/env bash +source api.func + +# Batch installation with comprehensive API reporting +batch_install_with_reporting() { + local apps=("plex" "nextcloud" "nginx" "mysql") + local ctids=(100 101 102 103) + + # Set up API reporting + export DIAGNOSTICS="yes" + export RANDOM_UUID="$(uuidgen)" + export CT_TYPE=1 + export DISK_SIZE=8 + export CORE_COUNT=2 + export RAM_SIZE=2048 + export var_os="debian" + export var_version="12" + export METHOD="install" + + local success_count=0 + local failure_count=0 + + for i in "${!apps[@]}"; do + local app="${apps[$i]}" + local ctid="${ctids[$i]}" + + echo "Installing $app (ID: $ctid)..." + + # Set app-specific parameters + export NSAPP="$app" + + # Report installation start + post_to_api + + # Simulate installation + if install_app "$app" "$ctid"; then + echo "$app installed successfully" + post_update_to_api "success" 0 + ((success_count++)) + else + echo "$app installation failed" + post_update_to_api "failed" $? + ((failure_count++)) + fi + + echo "---" + done + + echo "Batch installation completed: $success_count successful, $failure_count failed" +} + +# Mock installation function +install_app() { + local app="$1" + local ctid="$2" + + # Simulate installation + sleep 1 + + # Simulate occasional failures + if [[ $((RANDOM % 10)) -eq 0 ]]; then + return 1 + fi + + return 0 +} + +batch_install_with_reporting +``` + +### Error Analysis and Reporting + +```bash +#!/usr/bin/env bash +source api.func + +# Analyze and report errors +analyze_and_report_errors() { + local log_file="$1" + + export DIAGNOSTICS="yes" + export RANDOM_UUID="$(uuidgen)" + + if [[ ! -f "$log_file" ]]; then + echo "Log file not found: $log_file" + return 1 + fi + + # Extract error codes from log + local error_codes=$(grep -o 'exit code [0-9]\+' "$log_file" | grep -o '[0-9]\+' | sort -u) + + if [[ -z "$error_codes" ]]; then + echo "No errors found in log" + post_update_to_api "success" 0 + return 0 + fi + + echo "Found error codes: $error_codes" + + # Report each unique error + for code in $error_codes; do + local error_msg=$(get_error_description $code) + echo "Error $code: $error_msg" + post_update_to_api "failed" $code + done +} + +# Usage +analyze_and_report_errors "/var/log/installation.log" +``` + +### API Health Check + +```bash +#!/usr/bin/env bash +source api.func + +# Check API connectivity and functionality +check_api_health() { + echo "Checking API health..." + + # Test prerequisites + if ! command -v curl >/dev/null 2>&1; then + echo "ERROR: curl not available" + return 1 + fi + + # Test error description function + local test_error=$(get_error_description 127) + if [[ -z "$test_error" ]]; then + echo "ERROR: Error description function not working" + return 1 + fi + + echo "Error description test: $test_error" + + # Test API connectivity (without sending data) + local api_url="http://api.community-scripts.org/dev/upload" + if curl -s --head "$api_url" >/dev/null 2>&1; then + echo "API endpoint is reachable" + else + echo "WARNING: API endpoint not reachable" + fi + + echo "API health check completed" +} + +check_api_health +``` + +## Integration Examples + +### With build.func + +```bash +#!/usr/bin/env bash +# Integration with build.func + +source core.func +source api.func +source build.func + +# Set up API reporting +export DIAGNOSTICS="yes" +export RANDOM_UUID="$(uuidgen)" + +# Container creation with API reporting +create_container_with_reporting() { + local app="$1" + local ctid="$2" + + # Set container parameters + export APP="$app" + export CTID="$ctid" + export var_hostname="${app}-server" + export var_os="debian" + export var_version="12" + export var_cpu="2" + export var_ram="2048" + export var_disk="10" + export var_net="vmbr0" + export var_gateway="192.168.1.1" + export var_ip="192.168.1.$ctid" + export var_template_storage="local" + export var_container_storage="local" + + # Report installation start + post_to_api + + # Create container using build.func + if source build.func; then + echo "Container $app created successfully" + post_update_to_api "success" 0 + return 0 + else + echo "Container $app creation failed" + post_update_to_api "failed" $? + return 1 + fi +} + +# Create containers +create_container_with_reporting "plex" "100" +create_container_with_reporting "nextcloud" "101" +``` + +### With vm-core.func + +```bash +#!/usr/bin/env bash +# Integration with vm-core.func + +source core.func +source api.func +source vm-core.func + +# Set up VM API reporting +mkdir -p /usr/local/community-scripts +echo "DIAGNOSTICS=yes" > /usr/local/community-scripts/diagnostics + +export RANDOM_UUID="$(uuidgen)" + +# VM creation with API reporting +create_vm_with_reporting() { + local app="$1" + local vmid="$2" + + # Set VM parameters + export APP="$app" + export VMID="$vmid" + export var_hostname="${app}-vm" + export var_os="ubuntu" + export var_version="22.04" + export var_cpu="4" + export var_ram="4096" + export var_disk="20" + + # Report VM installation start + post_to_api_vm + + # Create VM using vm-core.func + if source vm-core.func; then + echo "VM $app created successfully" + post_update_to_api "success" 0 + return 0 + else + echo "VM $app creation failed" + post_update_to_api "failed" $? + return 1 + fi +} + +# Create VMs +create_vm_with_reporting "nextcloud" "200" +create_vm_with_reporting "wordpress" "201" +``` + +### With error_handler.func + +```bash +#!/usr/bin/env bash +# Integration with error_handler.func + +source core.func +source error_handler.func +source api.func + +# Enhanced error handling with API reporting +enhanced_error_handler() { + local exit_code=${1:-$?} + local command=${2:-${BASH_COMMAND:-unknown}} + + # Get error description from api.func + local error_msg=$(get_error_description $exit_code) + + # Display error information + echo "Error $exit_code: $error_msg" + echo "Command: $command" + + # Report error to API + export DIAGNOSTICS="yes" + export RANDOM_UUID="$(uuidgen)" + post_update_to_api "failed" $exit_code + + # Use standard error handler + error_handler $exit_code $command +} + +# Set up enhanced error handling +trap 'enhanced_error_handler' ERR + +# Test enhanced error handling +nonexistent_command +``` + +## Best Practices Examples + +### Comprehensive API Integration + +```bash +#!/usr/bin/env bash +# Comprehensive API integration example + +source core.func +source api.func + +# Set up comprehensive API reporting +setup_api_reporting() { + # Enable diagnostics + export DIAGNOSTICS="yes" + export RANDOM_UUID="$(uuidgen)" + + # Set common parameters + export CT_TYPE=1 + export DISK_SIZE=8 + export CORE_COUNT=2 + export RAM_SIZE=2048 + export var_os="debian" + export var_version="12" + export METHOD="install" + + echo "API reporting configured" +} + +# Installation with comprehensive reporting +install_with_comprehensive_reporting() { + local app="$1" + local ctid="$2" + + # Set up API reporting + setup_api_reporting + export NSAPP="$app" + + # Report installation start + post_to_api + + # Installation process + echo "Installing $app..." + + # Simulate installation steps + local steps=("Downloading" "Installing" "Configuring" "Starting") + for step in "${steps[@]}"; do + echo "$step $app..." + sleep 1 + done + + # Check installation result + if [[ $? -eq 0 ]]; then + echo "$app installation completed successfully" + post_update_to_api "success" 0 + return 0 + else + echo "$app installation failed" + post_update_to_api "failed" $? + return 1 + fi +} + +# Install multiple applications +apps=("plex" "nextcloud" "nginx" "mysql") +ctids=(100 101 102 103) + +for i in "${!apps[@]}"; do + install_with_comprehensive_reporting "${apps[$i]}" "${ctids[$i]}" + echo "---" +done +``` + +### Error Recovery with API Reporting + +```bash +#!/usr/bin/env bash +source api.func + +# Error recovery with API reporting +retry_with_api_reporting() { + local operation="$1" + local max_attempts=3 + local attempt=1 + + export DIAGNOSTICS="yes" + export RANDOM_UUID="$(uuidgen)" + + while [[ $attempt -le $max_attempts ]]; do + echo "Attempt $attempt of $max_attempts: $operation" + + if $operation; then + echo "Operation succeeded on attempt $attempt" + post_update_to_api "success" 0 + return 0 + else + local exit_code=$? + local error_msg=$(get_error_description $exit_code) + echo "Attempt $attempt failed: $error_msg" + + post_update_to_api "failed" $exit_code + + ((attempt++)) + + if [[ $attempt -le $max_attempts ]]; then + echo "Retrying in 5 seconds..." + sleep 5 + fi + fi + done + + echo "Operation failed after $max_attempts attempts" + return 1 +} + +# Usage +retry_with_api_reporting "apt-get update" +retry_with_api_reporting "apt-get install -y package" +``` + +### API Reporting with Logging + +```bash +#!/usr/bin/env bash +source api.func + +# API reporting with detailed logging +install_with_logging_and_api() { + local app="$1" + local log_file="/var/log/${app}_installation.log" + + # Set up API reporting + export DIAGNOSTICS="yes" + export RANDOM_UUID="$(uuidgen)" + export NSAPP="$app" + + # Start logging + exec > >(tee -a "$log_file") + exec 2>&1 + + echo "Starting $app installation at $(date)" + + # Report installation start + post_to_api + + # Installation process + echo "Installing $app..." + + # Simulate installation + if install_app "$app"; then + echo "$app installation completed successfully at $(date)" + post_update_to_api "success" 0 + return 0 + else + local exit_code=$? + local error_msg=$(get_error_description $exit_code) + echo "$app installation failed at $(date): $error_msg" + post_update_to_api "failed" $exit_code + return $exit_code + fi +} + +# Mock installation function +install_app() { + local app="$1" + echo "Installing $app..." + sleep 2 + return 0 +} + +# Install with logging and API reporting +install_with_logging_and_api "plex" +``` diff --git a/docs/misc/api.func/README.md b/docs/misc/api.func/README.md new file mode 100644 index 000000000..6cf90d23d --- /dev/null +++ b/docs/misc/api.func/README.md @@ -0,0 +1,199 @@ +# api.func Documentation + +## Overview + +The `api.func` file provides Proxmox API integration and diagnostic reporting functionality for the Community Scripts project. It handles API communication, error reporting, and status updates to the community-scripts.org API. + +## Purpose and Use Cases + +- **API Communication**: Send installation and status data to community-scripts.org API +- **Diagnostic Reporting**: Report installation progress and errors for analytics +- **Error Description**: Provide detailed error code explanations +- **Status Updates**: Track installation success/failure status +- **Analytics**: Contribute anonymous usage data for project improvement + +## Quick Reference + +### Key Function Groups +- **Error Handling**: `get_error_description()` - Convert exit codes to human-readable messages +- **API Communication**: `post_to_api()`, `post_to_api_vm()` - Send installation data +- **Status Updates**: `post_update_to_api()` - Report installation completion status + +### Dependencies +- **External**: `curl` command for HTTP requests +- **Internal**: Uses environment variables from other scripts + +### Integration Points +- Used by: All installation scripts for diagnostic reporting +- Uses: Environment variables from build.func and other scripts +- Provides: API communication and error reporting services + +## Documentation Files + +### 📊 [API_FLOWCHART.md](./API_FLOWCHART.md) +Visual execution flows showing API communication processes and error handling. + +### 📚 [API_FUNCTIONS_REFERENCE.md](./API_FUNCTIONS_REFERENCE.md) +Complete alphabetical reference of all functions with parameters, dependencies, and usage details. + +### 💡 [API_USAGE_EXAMPLES.md](./API_USAGE_EXAMPLES.md) +Practical examples showing how to use API functions and common patterns. + +### 🔗 [API_INTEGRATION.md](./API_INTEGRATION.md) +How api.func integrates with other components and provides API services. + +## Key Features + +### Error Code Descriptions +- **Comprehensive Coverage**: 50+ error codes with detailed explanations +- **LXC-Specific Errors**: Container creation and management errors +- **System Errors**: General system and network errors +- **Signal Errors**: Process termination and signal errors + +### API Communication +- **LXC Reporting**: Send LXC container installation data +- **VM Reporting**: Send VM installation data +- **Status Updates**: Report installation success/failure +- **Diagnostic Data**: Anonymous usage analytics + +### Diagnostic Integration +- **Optional Reporting**: Only sends data when diagnostics enabled +- **Privacy Respect**: Respects user privacy settings +- **Error Tracking**: Tracks installation errors for improvement +- **Usage Analytics**: Contributes to project statistics + +## Common Usage Patterns + +### Basic API Setup +```bash +#!/usr/bin/env bash +# Basic API setup + +source api.func + +# Set up diagnostic reporting +export DIAGNOSTICS="yes" +export RANDOM_UUID="$(uuidgen)" + +# Report installation start +post_to_api +``` + +### Error Reporting +```bash +#!/usr/bin/env bash +source api.func + +# Get error description +error_msg=$(get_error_description 127) +echo "Error 127: $error_msg" +# Output: Error 127: Command not found: Incorrect path or missing dependency. +``` + +### Status Updates +```bash +#!/usr/bin/env bash +source api.func + +# Report successful installation +post_update_to_api "success" 0 + +# Report failed installation +post_update_to_api "failed" 127 +``` + +## Environment Variables + +### Required Variables +- `DIAGNOSTICS`: Enable/disable diagnostic reporting ("yes"/"no") +- `RANDOM_UUID`: Unique identifier for tracking + +### Optional Variables +- `CT_TYPE`: Container type (1 for LXC, 2 for VM) +- `DISK_SIZE`: Disk size in GB +- `CORE_COUNT`: Number of CPU cores +- `RAM_SIZE`: RAM size in MB +- `var_os`: Operating system type +- `var_version`: OS version +- `DISABLEIP6`: IPv6 disable setting +- `NSAPP`: Namespace application name +- `METHOD`: Installation method + +### Internal Variables +- `POST_UPDATE_DONE`: Prevents duplicate status updates +- `API_URL`: Community scripts API endpoint +- `JSON_PAYLOAD`: API request payload +- `RESPONSE`: API response + +## Error Code Categories + +### General System Errors +- **0-9**: Basic system errors +- **18, 22, 28, 35**: Network and I/O errors +- **56, 60**: TLS/SSL errors +- **125-128**: Command execution errors +- **129-143**: Signal errors +- **152**: Resource limit errors +- **255**: Unknown critical errors + +### LXC-Specific Errors +- **100-101**: LXC installation errors +- **200-209**: LXC creation and management errors + +### Docker Errors +- **125**: Docker container start errors + +## Best Practices + +### Diagnostic Reporting +1. Always check if diagnostics are enabled +2. Respect user privacy settings +3. Use unique identifiers for tracking +4. Report both success and failure cases + +### Error Handling +1. Use appropriate error codes +2. Provide meaningful error descriptions +3. Handle API communication failures gracefully +4. Don't block installation on API failures + +### API Usage +1. Check for curl availability +2. Handle network failures gracefully +3. Use appropriate HTTP methods +4. Include all required data + +## Troubleshooting + +### Common Issues +1. **API Communication Fails**: Check network connectivity and curl availability +2. **Diagnostics Not Working**: Verify DIAGNOSTICS setting and RANDOM_UUID +3. **Missing Error Descriptions**: Check error code coverage +4. **Duplicate Updates**: POST_UPDATE_DONE prevents duplicates + +### Debug Mode +Enable diagnostic reporting for debugging: +```bash +export DIAGNOSTICS="yes" +export RANDOM_UUID="$(uuidgen)" +``` + +### API Testing +Test API communication: +```bash +source api.func +export DIAGNOSTICS="yes" +export RANDOM_UUID="test-$(date +%s)" +post_to_api +``` + +## Related Documentation + +- [core.func](../core.func/) - Core utilities and error handling +- [error_handler.func](../error_handler.func/) - Error handling utilities +- [build.func](../build.func/) - Container creation with API integration +- [tools.func](../tools.func/) - Extended utilities with API integration + +--- + +*This documentation covers the api.func file which provides API communication and diagnostic reporting for all Proxmox Community Scripts.* From f19bda13c0b1f22ffb4c172245dcb842175d4c54 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 10 Oct 2025 11:36:55 +0200 Subject: [PATCH 1389/1733] Add template issue closer --- .github/workflows/close_template_issue.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/close_template_issue.yml b/.github/workflows/close_template_issue.yml index a3cf7a91c..9ea118661 100644 --- a/.github/workflows/close_template_issue.yml +++ b/.github/workflows/close_template_issue.yml @@ -5,7 +5,7 @@ on: jobs: close_tteck_issues: - if: github.repository == 'community-scripts/ProxmoxVE' + if: github.repository == 'community-scripts/ProxmoxVED' runs-on: ubuntu-latest steps: - name: Auto-close if wrong Template issue detected From 3fc951817cb4d3d64d9cdcce4db65983efc92d56 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 10 Oct 2025 11:38:20 +0200 Subject: [PATCH 1390/1733] Add template issue closer --- .github/workflows/close_template_issue.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/close_template_issue.yml b/.github/workflows/close_template_issue.yml index 9ea118661..d5a5cbc6d 100644 --- a/.github/workflows/close_template_issue.yml +++ b/.github/workflows/close_template_issue.yml @@ -17,7 +17,7 @@ jobs: const issueNumber = issue.number; // Check for tteck script mention - if (content.includes("Template debian-13-standard_13.1-2_amd64.tar.zst [local]") || content.includes("Container creation failed. Checking if template is corrupted or incomplete.")) || content.includes("Template is valid, but container creation still failed."){ + if (content.includes("Template debian-13-standard_13.1-2_amd64.tar.zst [local]") || content.includes("Container creation failed. Checking if template is corrupted or incomplete.") || content.includes("Template is valid, but container creation still failed.")){ const message = `Hello, it looks like you are referencing a container creation issue!. We get many simmilar issues with this topic, so please check this disscusion #8126. From be8390a2cae9bb4e6fc0e898048ad6826cda7f76 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Fri, 10 Oct 2025 20:33:51 +0200 Subject: [PATCH 1391/1733] Remove rwMarkable and GLPI scripts --- ct/glpi.sh | 47 ----------- ct/rwmarkable.sh | 76 ----------------- install/glpi-install.sh | 152 ---------------------------------- install/rwmarkable-install.sh | 66 --------------- 4 files changed, 341 deletions(-) delete mode 100644 ct/glpi.sh delete mode 100644 ct/rwmarkable.sh delete mode 100644 install/glpi-install.sh delete mode 100644 install/rwmarkable-install.sh diff --git a/ct/glpi.sh b/ct/glpi.sh deleted file mode 100644 index d03b80be8..000000000 --- a/ct/glpi.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Nícolas Pastorello (opastorello) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://www.glpi-project.org/ - -APP="GLPI" -var_tags="${var_tags:-asset-management;foss}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-10}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - - if [[ ! -d /opt/glpi ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - RELEASE=$(curl -fsSL https://api.github.com/repos/glpi-project/glpi/releases/latest | grep '"tag_name"' | sed -E 's/.*"tag_name": "([^"]+)".*/\1/') - if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then - msg_error "Currently we don't provide an update function for this ${APP}." - else - msg_ok "No update required. ${APP} is already at v${RELEASE}." - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:80${CL}" \ No newline at end of file diff --git a/ct/rwmarkable.sh b/ct/rwmarkable.sh deleted file mode 100644 index c98d3d887..000000000 --- a/ct/rwmarkable.sh +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env bash -source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: vhsdream -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://github.com/fccview/rwMarkable - -APP="rwMarkable" -var_tags="${var_tags:-tasks;notes}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-6}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - - if [[ ! -d /opt/rwmarkable ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if check_for_gh_release "rwMarkable" "fccview/rwMarkable"; then - msg_info "Stopping ${APP}" - systemctl stop rwmarkable - msg_ok "Stopped ${APP}" - - msg_info "Backing up configuration & data" - cd /opt/rwmarkable - cp ./.env /opt/app.env - $STD tar -cf /opt/data_config.tar ./data ./config - msg_ok "Backed up configuration & data" - - NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs - cd /opt - export CLEAN_INSTALL=1 - fetch_and_deploy_gh_release "rwMarkable" "fccview/rwMarkable" "tarball" "latest" "/opt/rwmarkable" - - msg_info "Updating app" - cd /opt/rwmarkable - $STD yarn --frozen-lockfile - $STD yarn next telemetry disable - $STD yarn build - msg_ok "Updated app" - - msg_info "Restoring configuration & data" - mv /opt/app.env /opt/rwmarkable/.env - $STD tar -xf /opt/data_config.tar - msg_ok "Restored configuration & data" - - msg_info "Restarting ${APP} service" - systemctl start rwmarkable - msg_ok "Restarted ${APP} service" - rm /opt/data_config.tar - msg_ok "Updated Successfully" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" diff --git a/install/glpi-install.sh b/install/glpi-install.sh deleted file mode 100644 index 64610fb2f..000000000 --- a/install/glpi-install.sh +++ /dev/null @@ -1,152 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Nícolas Pastorello (opastorello) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://www.glpi-project.org/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt-get install -y \ - git \ - apache2 \ - php8.2-{apcu,cli,common,curl,gd,imap,ldap,mysql,xmlrpc,xml,mbstring,bcmath,intl,zip,redis,bz2,soap} \ - php-cas \ - libapache2-mod-php -msg_ok "Installed Dependencies" - -setup_mariadb - -msg_info "Setting up database" -DB_NAME=glpi_db -DB_USER=glpi -DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) -mariadb-tzinfo-to-sql /usr/share/zoneinfo | mariadb mysql -$STD mariadb -u root -e "CREATE DATABASE $DB_NAME;" -$STD mariadb -u root -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';" -$STD mariadb -u root -e "GRANT ALL PRIVILEGES ON $DB_NAME.* TO '$DB_USER'@'localhost';" -$STD mariadb -u root -e "GRANT SELECT ON \`mysql\`.\`time_zone_name\` TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" -{ - echo "GLPI Database Credentials" - echo "Database: $DB_NAME" - echo "Username: $DB_USER" - echo "Password: $DB_PASS" -} >>~/glpi_db.creds -msg_ok "Set up database" - -msg_info "Installing GLPi" -cd /opt -RELEASE=$(curl -fsSL https://api.github.com/repos/glpi-project/glpi/releases/latest | grep '"tag_name"' | sed -E 's/.*"tag_name": "([^"]+)".*/\1/') -curl -fsSL "https://github.com/glpi-project/glpi/releases/download/10.0.20/glpi-10.0.20.tgz" -o "glpi-10.0.20.tgz" -$STD tar -xzvf glpi-10.0.20.tgz -cd /opt/glpi -$STD php bin/console db:install --db-name=$DB_NAME --db-user=$DB_USER --db-password=$DB_PASS --no-interaction --allow-superuser -echo "${RELEASE}" >/opt/${APPLICATION}_version.txt -msg_ok "Installed GLPi" - -msg_info "Setting Downstream file" -cat </opt/glpi/inc/downstream.php -/etc/glpi/local_define.php -/etc/apache2/sites-available/glpi.conf - - ServerName localhost - DocumentRoot /opt/glpi/public - - - Require all granted - RewriteEngine On - RewriteCond %{HTTP:Authorization} ^(.+)$ - RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] - RewriteCond %{REQUEST_FILENAME} !-f - RewriteRule ^(.*)$ index.php [QSA,L] - - - ErrorLog \${APACHE_LOG_DIR}/glpi_error.log - CustomLog \${APACHE_LOG_DIR}/glpi_access.log combined - -EOF -$STD a2dissite 000-default.conf -$STD a2enmod rewrite -$STD a2ensite glpi.conf -msg_ok "Setup Service" - -msg_info "Setup Cronjob" -echo "* * * * * php /opt/glpi/front/cron.php" | crontab - -msg_ok "Setup Cronjob" - -msg_info "Update PHP Params" -PHP_VERSION=$(ls /etc/php/ | grep -E '^[0-9]+\.[0-9]+$' | head -n 1) -PHP_INI="/etc/php/$PHP_VERSION/apache2/php.ini" -sed -i 's/^upload_max_filesize = .*/upload_max_filesize = 20M/' $PHP_INI -sed -i 's/^post_max_size = .*/post_max_size = 20M/' $PHP_INI -sed -i 's/^max_execution_time = .*/max_execution_time = 60/' $PHP_INI -sed -i 's/^max_input_vars = .*/max_input_vars = 5000/' $PHP_INI -sed -i 's/^memory_limit = .*/memory_limit = 256M/' $PHP_INI -sed -i 's/^;\?\s*session.cookie_httponly\s*=.*/session.cookie_httponly = On/' $PHP_INI -systemctl restart apache2 -msg_ok "Update PHP Params" - -motd_ssh -customize - -msg_info "Cleaning up" -rm -rf /opt/glpi/install -rm -rf /opt/glpi-${RELEASE}.tgz -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" diff --git a/install/rwmarkable-install.sh b/install/rwmarkable-install.sh deleted file mode 100644 index afba71011..000000000 --- a/install/rwmarkable-install.sh +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: vhsdream -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/fccview/rwMarkable - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs -fetch_and_deploy_gh_release "rwMarkable" "fccview/rwMarkable" "tarball" "latest" "/opt/rwmarkable" - -msg_info "Installing ${APPLICATION}" -cd /opt/rwmarkable -$STD yarn --frozen-lockfile -$STD yarn next telemetry disable -$STD yarn build -mkdir -p data/{users,checklists,notes} - -cat </opt/rwmarkable/.env -NODE_ENV=production -# HTTPS=true - -# --- SSO with OIDC (optional) -# SSO_MODE=oidc -# OIDC_ISSUER= -# OIDC_CLIENT_ID= -# APP_URL= -# SSO_FALLBACK_LOCAL=true # Allow both SSO and normal login -# OIDC_CLIENT_SECRET=your_client_secret # Enable confidential client mode with client authentication -# OIDC_ADMIN_GROUPS=admins # Map provider groups to admin role -EOF -msg_ok "Installed ${APPLICATION}" - -msg_info "Creating Service" -cat </etc/systemd/system/rwmarkable.service -[Unit] -Description=rwMarkable server -After=network.target - -[Service] -WorkingDirectory=/opt/rwmarkable -EnvironmentFile=/opt/rwmarkable/.env -ExecStart=yarn start -Restart=on-abnormal - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now rwmarkable -msg_ok "Created Service" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt -y autoremove -$STD apt -y autoclean -$STD apt -y clean -msg_ok "Cleaned" From cd24d8b9c56472e8e1a1ce41d48c04eb3136bed3 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 10 Oct 2025 18:34:13 +0000 Subject: [PATCH 1392/1733] Update .app files --- ct/headers/glpi | 6 ------ ct/headers/palmr | 6 ++++++ ct/headers/rwmarkable | 6 ------ 3 files changed, 6 insertions(+), 12 deletions(-) delete mode 100644 ct/headers/glpi create mode 100644 ct/headers/palmr delete mode 100644 ct/headers/rwmarkable diff --git a/ct/headers/glpi b/ct/headers/glpi deleted file mode 100644 index 789b62590..000000000 --- a/ct/headers/glpi +++ /dev/null @@ -1,6 +0,0 @@ - ________ ____ ____ - / ____/ / / __ \/ _/ - / / __/ / / /_/ // / -/ /_/ / /___/ ____// / -\____/_____/_/ /___/ - diff --git a/ct/headers/palmr b/ct/headers/palmr new file mode 100644 index 000000000..c485f3c60 --- /dev/null +++ b/ct/headers/palmr @@ -0,0 +1,6 @@ + ____ __ + / __ \____ _/ /___ ___ _____ + / /_/ / __ `/ / __ `__ \/ ___/ + / ____/ /_/ / / / / / / / / +/_/ \__,_/_/_/ /_/ /_/_/ + diff --git a/ct/headers/rwmarkable b/ct/headers/rwmarkable deleted file mode 100644 index 5ef3689cf..000000000 --- a/ct/headers/rwmarkable +++ /dev/null @@ -1,6 +0,0 @@ - __ ___ __ __ __ - ______ __/ |/ /___ ______/ /______ _/ /_ / /__ - / ___/ | /| / / /|_/ / __ `/ ___/ //_/ __ `/ __ \/ / _ \ - / / | |/ |/ / / / / /_/ / / / ,< / /_/ / /_/ / / __/ -/_/ |__/|__/_/ /_/\__,_/_/ /_/|_|\__,_/_.___/_/\___/ - From 5015cbeccc8ab910e7e9fb767a4930d407a14c01 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Fri, 10 Oct 2025 21:41:42 +0200 Subject: [PATCH 1393/1733] Add LimeSurvey script --- ct/limesurvey.sh | 44 +++++++++++++++++++++ install/limesurvey-install.sh | 74 +++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 ct/limesurvey.sh create mode 100644 install/limesurvey-install.sh diff --git a/ct/limesurvey.sh b/ct/limesurvey.sh new file mode 100644 index 000000000..1b04f6f90 --- /dev/null +++ b/ct/limesurvey.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://community.limesurvey.org/ + +APP="LimeSurvey" +var_tags="${var_tags:-os}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-1024}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/limesurvey ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating $APP LXC" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Updated $APP LXC" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}" diff --git a/install/limesurvey-install.sh b/install/limesurvey-install.sh new file mode 100644 index 000000000..3ac474e9f --- /dev/null +++ b/install/limesurvey-install.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://community.limesurvey.org/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +PHP_VERSION="8.3" PHP_APACHE="YES" PHP_FPM="YES" PHP_MODULE="gd2,imap,ldap,zlib,mysql" setup_php +setup_mariadb + +msg_info "Configuring MariaDB Database" +DB_NAME=limesurvey_db +DB_USER=limesurvey +DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) +$STD mariadb -u root -e "CREATE DATABASE $DB_NAME CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" +$STD mariadb -u root -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';" +$STD mariadb -u root -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" +{ + echo "LimeSurvey-Credentials" + echo "LimeSurvey Database User: $DB_USER" + echo "LimeSurvey Database Password: $DB_PASS" + echo "LimeSurvey Database Name: $DB_NAME" +} >>~/limesurvey.creds +msg_ok "Configured MariaDB Database" + +msg_info "Setting up LimeSurvey" +temp_file=$(mktemp) +RELEASE=$(curl -s https://community.limesurvey.org/downloads/ | grep -oE 'https://download\.limesurvey\.org/latest-master/limesurvey[0-9.+]+\.zip' | head -n1) +curl -fsSL "$RELEASE" -o "$temp_file" +unzip -q "$temp_file" -d /opt + +cat </etc/apache2/sites-enabled/000-default.conf + + ServerAdmin webmaster@localhost + DocumentRoot /opt/limesurvey + DirectoryIndex index.php index.html index.cgi index.pl index.xhtml + Options +ExecCGI + + + Options FollowSymLinks + Require all granted + AllowOverride All + + + + Require all granted + + + ErrorLog /var/log/apache2/error.log + CustomLog /var/log/apache2/access.log combined + +EOF +chown -R www-data:www-data "/opt/limesurvey" +chmod -R 750 "/opt/limesurvey" +systemctl reload apache2 +msg_ok "Set up LimeSurvey" + +motd_ssh +customize + +msg_info "Cleaning up" +rm -rf "$temp_file" +$STD apt -y autoremove +$STD apt -y autoclean +$STD apt -y clean +msg_ok "Cleaned" From d5fd51a81aa41c060e2a37c6bea7c03f3e52b838 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Fri, 10 Oct 2025 21:47:00 +0200 Subject: [PATCH 1394/1733] LimeSurvey: small fixes --- ct/limesurvey.sh | 2 +- install/limesurvey-install.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/limesurvey.sh b/ct/limesurvey.sh index 1b04f6f90..2c45cbed6 100644 --- a/ct/limesurvey.sh +++ b/ct/limesurvey.sh @@ -41,4 +41,4 @@ description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" diff --git a/install/limesurvey-install.sh b/install/limesurvey-install.sh index 3ac474e9f..2f619776a 100644 --- a/install/limesurvey-install.sh +++ b/install/limesurvey-install.sh @@ -13,7 +13,7 @@ setting_up_container network_check update_os -PHP_VERSION="8.3" PHP_APACHE="YES" PHP_FPM="YES" PHP_MODULE="gd2,imap,ldap,zlib,mysql" setup_php +PHP_VERSION="8.3" PHP_APACHE="YES" PHP_FPM="YES" PHP_MODULE="imap,ldap,mysql" setup_php setup_mariadb msg_info "Configuring MariaDB Database" From 34ddee913c69e3b6a2884ba5fcd9cf3e4c8430be Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 10 Oct 2025 19:47:31 +0000 Subject: [PATCH 1395/1733] Update .app files --- ct/headers/limesurvey | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/limesurvey diff --git a/ct/headers/limesurvey b/ct/headers/limesurvey new file mode 100644 index 000000000..9d071d0f1 --- /dev/null +++ b/ct/headers/limesurvey @@ -0,0 +1,6 @@ + __ _ _____ + / / (_)___ ___ ___ / ___/__ ________ _____ __ __ + / / / / __ `__ \/ _ \\__ \/ / / / ___/ | / / _ \/ / / / + / /___/ / / / / / / __/__/ / /_/ / / | |/ / __/ /_/ / +/_____/_/_/ /_/ /_/\___/____/\__,_/_/ |___/\___/\__, / + /____/ From 1617d4160324e1b4277812d1bdd98c929bd2cc52 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Fri, 10 Oct 2025 21:54:58 +0200 Subject: [PATCH 1396/1733] LimeSurvey: small fixes --- ct/limesurvey.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/limesurvey.sh b/ct/limesurvey.sh index 2c45cbed6..e22768551 100644 --- a/ct/limesurvey.sh +++ b/ct/limesurvey.sh @@ -8,8 +8,8 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV APP="LimeSurvey" var_tags="${var_tags:-os}" var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-1024}" -var_disk="${var_disk:-4}" +var_ram="${var_ram:-512}" +var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" From 481ef8eeaa6215dfdeef8778538b94c368eb2393 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Fri, 10 Oct 2025 22:04:09 +0200 Subject: [PATCH 1397/1733] LimeSurvey: small fixes --- ct/limesurvey.sh | 6 ++--- frontend/public/json/limesurvey.json | 35 ++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 frontend/public/json/limesurvey.json diff --git a/ct/limesurvey.sh b/ct/limesurvey.sh index e22768551..c0ee8221d 100644 --- a/ct/limesurvey.sh +++ b/ct/limesurvey.sh @@ -27,10 +27,8 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - msg_info "Updating $APP LXC" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "Updated $APP LXC" + + msg_warn "Application is updated via Web Interface" exit } diff --git a/frontend/public/json/limesurvey.json b/frontend/public/json/limesurvey.json new file mode 100644 index 000000000..dac3a5ada --- /dev/null +++ b/frontend/public/json/limesurvey.json @@ -0,0 +1,35 @@ +{ + "name": "LimeSurvey", + "slug": "limesurvey", + "categories": [ + 25 + ], + "date_created": "2025-09-22", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 80, + "documentation": "https://www.limesurvey.org/manual/LimeSurvey_Manual", + "config_path": "", + "website": "https://community.limesurvey.org/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/limesurvey.webp", + "description": "LimeSurvey is the simple, quick and anonymous online survey tool that's bursting with juicy insights. Calling students, professionals and enterprises: design a survey and get the best insights, it’s free and as easy as squeezing a lime. Make a free online survey now!", + "install_methods": [ + { + "type": "default", + "script": "ct/limesurvey.sh", + "resources": { + "cpu": 1, + "ram": 512, + "hdd": 2, + "os": "Debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} From be28eb1faf29c5b737d1cbe5a57442b34eb98e46 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Fri, 10 Oct 2025 22:06:24 +0200 Subject: [PATCH 1398/1733] LimeSurvey: small fixes --- frontend/public/json/limesurvey.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/public/json/limesurvey.json b/frontend/public/json/limesurvey.json index dac3a5ada..0d67ef391 100644 --- a/frontend/public/json/limesurvey.json +++ b/frontend/public/json/limesurvey.json @@ -31,5 +31,10 @@ "username": null, "password": null }, - "notes": [] + "notes": [ + { + "text": "You will need to input database credentials into LimeSurvey installer. Use `cat ~/limesurvey.creds` inside LXC.", + "type": "info" + } + ] } From c7067d3933b71ead275ce73501286e849c8b84e0 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 10 Oct 2025 23:12:32 +0200 Subject: [PATCH 1399/1733] Delete frontend/public/json/rwmarkable.json --- frontend/public/json/rwmarkable.json | 36 ---------------------------- 1 file changed, 36 deletions(-) delete mode 100644 frontend/public/json/rwmarkable.json diff --git a/frontend/public/json/rwmarkable.json b/frontend/public/json/rwmarkable.json deleted file mode 100644 index 9b1f395ca..000000000 --- a/frontend/public/json/rwmarkable.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "rwMarkable", - "slug": "rwmarkable", - "categories": [ - 12 - ], - "date_created": "2024-10-02", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 3000, - "documentation": "https://github.com/fccview/rwMarkable/blob/main/README.md", - "website": "https://github.com/fccview/rwMarkable", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/rwmarkable.webp", - "config_path": "/opt/rwmarkable/.env", - "description": "A simple, self-hosted app for your checklists and notes. Tired of bloated, cloud-based to-do apps? rwMarkable is a lightweight alternative for managing your personal checklists and notes. It's built with Next.js 14, is easy to deploy, and keeps all your data on your own server.", - "install_methods": [ - { - "type": "default", - "script": "ct/rwmarkable.sh", - "resources": { - "cpu": 2, - "ram": 2048, - "hdd": 6, - "os": "debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] -} - From 0d222e5cb0fdf120b85882aed22e55f6e24c7c96 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 13 Oct 2025 23:01:04 +0200 Subject: [PATCH 1400/1733] deb13 --- ct/debian.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/debian.sh b/ct/debian.sh index a7ed93b3d..7798f275b 100644 --- a/ct/debian.sh +++ b/ct/debian.sh @@ -11,7 +11,7 @@ var_cpu="${var_cpu:-4}" var_ram="${var_ram:-4096}" var_disk="${var_disk:-15}" var_os="${var_os:-debian}" -var_version="${var_version:-12}" +var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" #var_fuse="${var_fuse:-no}" #var_tun="${var_tun:-no}" From 66cb422ec4b338b497c50343c11810641512fd37 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Tue, 14 Oct 2025 11:45:15 +0200 Subject: [PATCH 1401/1733] Test InvenTree --- ct/inventree.sh | 45 ++++++++++++++++++++++++++++++++++++ install/inventree-install.sh | 40 ++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 ct/inventree.sh create mode 100644 install/inventree-install.sh diff --git a/ct/inventree.sh b/ct/inventree.sh new file mode 100644 index 000000000..f59fffa5c --- /dev/null +++ b/ct/inventree.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/inventree/InvenTree + +APP="InvenTree" +var_tags="${var_tags:-inventory}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-6}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -d "/opt/inventree" ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating $APP" + $STD apt-get update + $STD apt-get install --only-upgrade inventree -y + msg_ok "Updated $APP" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" diff --git a/install/inventree-install.sh b/install/inventree-install.sh new file mode 100644 index 000000000..aba26f1be --- /dev/null +++ b/install/inventree-install.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/inventree/InvenTree + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +temp_file=$(mktemp) +curl -fsSL "http://archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2_amd64.deb" -o "$temp_file" +$STD dpkg -i $temp_file +msg_ok "Installed Dependencies" + +msg_info "Setting up InvenTree Repository" +mkdir -p /etc/apt/keyrings +curl -fsSL https://dl.packager.io/srv/inventree/InvenTree/key | gpg --dearmor -o /etc/apt/keyrings/inventree.gpg +echo "deb [signed-by=/etc/apt/keyrings/inventree.gpg] https://dl.packager.io/srv/deb/inventree/InvenTree/stable/ubuntu 20.04 main" >/etc/apt/sources.list.d/inventree.list +msg_ok "Set up InvenTree Repository" + +msg_info "Setup ${APPLICATION} (Patience)" +$STD apt-get update +$STD apt-get install -y inventree +msg_ok "Setup ${APPLICATION}" + +motd_ssh +customize + +msg_info "Cleaning up" +rm -f $temp_file +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From cc1bbad31f2f2c63f732a79a6b493fdaac4c0b58 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 14 Oct 2025 09:45:45 +0000 Subject: [PATCH 1402/1733] Update .app files --- ct/headers/inventree | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/inventree diff --git a/ct/headers/inventree b/ct/headers/inventree new file mode 100644 index 000000000..6057785ae --- /dev/null +++ b/ct/headers/inventree @@ -0,0 +1,6 @@ + ____ ______ + / _/___ _ _____ ____/_ __/_______ ___ + / // __ \ | / / _ \/ __ \/ / / ___/ _ \/ _ \ + _/ // / / / |/ / __/ / / / / / / / __/ __/ +/___/_/ /_/|___/\___/_/ /_/_/ /_/ \___/\___/ + From 9538c17658d828151f5aaaccda26a03ed75be34f Mon Sep 17 00:00:00 2001 From: tremor021 Date: Tue, 14 Oct 2025 12:05:43 +0200 Subject: [PATCH 1403/1733] Delete Invetree test --- ct/inventree.sh | 45 ------------------------------------ install/inventree-install.sh | 40 -------------------------------- 2 files changed, 85 deletions(-) delete mode 100644 ct/inventree.sh delete mode 100644 install/inventree-install.sh diff --git a/ct/inventree.sh b/ct/inventree.sh deleted file mode 100644 index f59fffa5c..000000000 --- a/ct/inventree.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Slaviša Arežina (tremor021) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/inventree/InvenTree - -APP="InvenTree" -var_tags="${var_tags:-inventory}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-6}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - - if [[ ! -d "/opt/inventree" ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - msg_info "Updating $APP" - $STD apt-get update - $STD apt-get install --only-upgrade inventree -y - msg_ok "Updated $APP" - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" diff --git a/install/inventree-install.sh b/install/inventree-install.sh deleted file mode 100644 index aba26f1be..000000000 --- a/install/inventree-install.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Slaviša Arežina (tremor021) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/inventree/InvenTree - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -temp_file=$(mktemp) -curl -fsSL "http://archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2_amd64.deb" -o "$temp_file" -$STD dpkg -i $temp_file -msg_ok "Installed Dependencies" - -msg_info "Setting up InvenTree Repository" -mkdir -p /etc/apt/keyrings -curl -fsSL https://dl.packager.io/srv/inventree/InvenTree/key | gpg --dearmor -o /etc/apt/keyrings/inventree.gpg -echo "deb [signed-by=/etc/apt/keyrings/inventree.gpg] https://dl.packager.io/srv/deb/inventree/InvenTree/stable/ubuntu 20.04 main" >/etc/apt/sources.list.d/inventree.list -msg_ok "Set up InvenTree Repository" - -msg_info "Setup ${APPLICATION} (Patience)" -$STD apt-get update -$STD apt-get install -y inventree -msg_ok "Setup ${APPLICATION}" - -motd_ssh -customize - -msg_info "Cleaning up" -rm -f $temp_file -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" From 2c40f93ab168ce49f5198c4371c8b83e5f41c1b5 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Tue, 14 Oct 2025 18:49:07 +0200 Subject: [PATCH 1404/1733] Add Open Archiver script --- ct/openarchiver.sh | 42 +++++++++++ install/openarchiver-install.sh | 120 ++++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+) create mode 100644 ct/openarchiver.sh create mode 100644 install/openarchiver-install.sh diff --git a/ct/openarchiver.sh b/ct/openarchiver.sh new file mode 100644 index 000000000..6f7a83c06 --- /dev/null +++ b/ct/openarchiver.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://openarchiver.com/ + +APP="Open Archiver" +var_tags="${var_tags:-os}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-2}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/openarchiver ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + msg_warn "Application is updated via Web Interface" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" diff --git a/install/openarchiver-install.sh b/install/openarchiver-install.sh new file mode 100644 index 000000000..492b927c0 --- /dev/null +++ b/install/openarchiver-install.sh @@ -0,0 +1,120 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://openarchiver.com/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing dependendencies" +$STD apt install -y valkey +msg_ok "Installed dependendencies" + +NODE_VERSION="22" NODE_MODULE="pnpm" setup_nodejs +PG_VERSION="17" setup_postgresql +fetch_and_deploy_gh_release "meilisearch" "meilisearch/meilisearch" "binary" +fetch_and_deploy_gh_release "openarchiver" "LogicLabs-OU/OpenArchiver" "tarball" +JWT_KEY="$(openssl rand -hex 32)" +SECRET_KEY="$(openssl rand -hex 32)" + +msg_info "Setting up PostgreSQL" +DB_NAME="openarchiver_db" +DB_USER="openarchiver" +DB_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-18)" +$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" +$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" +{ + echo "Open Archiver DB Credentials" + echo "Database Name: $DB_NAME" + echo "Database User: $DB_USER" + echo "Database Password: $DB_PASS" +} >>~/openarchiver.creds +msg_ok "Set up PostgreSQL" + +msg_info "Configuring MeiliSearch" +curl -fsSL https://raw.githubusercontent.com/meilisearch/meilisearch/latest/config.toml -o /etc/meilisearch.toml +MASTER_KEY=$(openssl rand -base64 12) +sed -i \ + -e 's|^env =.*|env = "production"|' \ + -e "s|^# master_key =.*|master_key = \"$MASTER_KEY\"|" \ + -e 's|^db_path =.*|db_path = "/var/lib/meilisearch/data"|' \ + -e 's|^dump_dir =.*|dump_dir = "/var/lib/meilisearch/dumps"|' \ + -e 's|^snapshot_dir =.*|snapshot_dir = "/var/lib/meilisearch/snapshots"|' \ + -e 's|^# no_analytics = true|no_analytics = true|' \ + -e 's|^http_addr =.*|http_addr = "127.0.0.1:7700"|' \ + /etc/meilisearch.toml + +cat </etc/systemd/system/meilisearch.service +[Unit] +Description=Meilisearch +After=network.target + +[Service] +ExecStart=/usr/bin/meilisearch --config-file-path /etc/meilisearch.toml +Restart=always + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now meilisearch +sleep 5 +msg_ok "Configured MeiliSearch" + +msg_info "Setting up Open Archiver" +mkdir -p /opt/openarchiver-data +cd /opt/openarchiver +cp .env.example .env +sed -i "s|^NODE_ENV=.*|NODE_ENV=production|g" /opt/openarchiver/.env +sed -i "s|^POSTGRES_DB=.*|POSTGRES_DB=openarchiver_db|g" /opt/openarchiver/.env +sed -i "s|^POSTGRES_USER=.*|POSTGRES_USER=openarchiver|g" /opt/openarchiver/.env +sed -i "s|^POSTGRES_PASSWORD=.*|POSTGRES_PASSWORD=$DB_PASS|g" /opt/openarchiver/.env +sed -i "s|^DATABASE_URL=.*|DATABASE_URL=\"postgresql://openarchiver:$DB_PASS@localhost:5432/openarchiver_db\"|g" /opt/openarchiver/.env +sed -i "s|^MEILI_HOST=.*|MEILI_HOST=http://localhost:7700|g" /opt/openarchiver/.env +sed -i "s|^MEILI_MASTER_KEY=.*|MEILI_MASTER_KEY=$MASTER_KEY|g" /opt/openarchiver/.env +sed -i "s|^REDIS_HOST=.*|REDIS_HOST=localhost|g" /opt/openarchiver/.env +sed -i "s|^REDIS_PASSWORD=.*|REDIS_PASSWORD=|g" /opt/openarchiver/.env +sed -i "s|^STORAGE_LOCAL_ROOT_PATH=.*|STORAGE_LOCAL_ROOT_PATH=/opt/openarchiver-data|g" /opt/openarchiver/.env +sed -i "s|^JWT_SECRET=.*|JWT_SECRET=$JWT_KEY|g" /opt/openarchiver/.env +sed -i "s|^ENCRYPTION_KEY=.*|ENCRYPTION_KEY=$SECRET_KEY|g" /opt/openarchiver/.env +sed -i "s|^TIKA_URL=.*|TIKA_URL=|g" /opt/openarchiver/.env +$STD pnpm install --shamefully-hoist --frozen-lockfile --prod=false +$STD pnpm build +msg_ok "Setup Open Archiver" + +msg_info "Creating Service" +cat </etc/systemd/system/openarchiver.service +[Unit] +Description=Open Archiver Service +After=network-online.target + +[Service] +Type=simple +User=root +WorkingDirectory=/opt/openarchiver +ExecStart=/usr/bin/pnpm start +Restart=on-failure + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now openarchiver +msg_ok "Created Service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt -y autoremove +$STD apt -y autoclean +$STD apt -y clean +msg_ok "Cleaned" From 499ff07c7516e4f61b21b44ebb353bb9b6de7903 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Tue, 14 Oct 2025 19:08:14 +0200 Subject: [PATCH 1405/1733] Update Open Archiver --- ct/openarchiver.sh | 2 +- install/openarchiver-install.sh | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ct/openarchiver.sh b/ct/openarchiver.sh index 6f7a83c06..ce114aa17 100644 --- a/ct/openarchiver.sh +++ b/ct/openarchiver.sh @@ -9,7 +9,7 @@ APP="Open Archiver" var_tags="${var_tags:-os}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" -var_disk="${var_disk:-2}" +var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" diff --git a/install/openarchiver-install.sh b/install/openarchiver-install.sh index 492b927c0..4b61930b2 100644 --- a/install/openarchiver-install.sh +++ b/install/openarchiver-install.sh @@ -100,6 +100,7 @@ After=network-online.target [Service] Type=simple User=root +EnvironmentFile=/opt/openarchiver/.env WorkingDirectory=/opt/openarchiver ExecStart=/usr/bin/pnpm start Restart=on-failure From b959eeaa5c5cbdc692f226f13dedb1431e32ebaf Mon Sep 17 00:00:00 2001 From: tremor021 Date: Tue, 14 Oct 2025 19:39:43 +0200 Subject: [PATCH 1406/1733] Update Open Archiver --- ct/openarchiver.sh | 2 +- install/openarchiver-install.sh | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ct/openarchiver.sh b/ct/openarchiver.sh index ce114aa17..6bd2de36a 100644 --- a/ct/openarchiver.sh +++ b/ct/openarchiver.sh @@ -8,7 +8,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV APP="Open Archiver" var_tags="${var_tags:-os}" var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" +var_ram="${var_ram:-3072}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" diff --git a/install/openarchiver-install.sh b/install/openarchiver-install.sh index 4b61930b2..56d9797be 100644 --- a/install/openarchiver-install.sh +++ b/install/openarchiver-install.sh @@ -89,6 +89,8 @@ sed -i "s|^ENCRYPTION_KEY=.*|ENCRYPTION_KEY=$SECRET_KEY|g" /opt/openarchiver/.en sed -i "s|^TIKA_URL=.*|TIKA_URL=|g" /opt/openarchiver/.env $STD pnpm install --shamefully-hoist --frozen-lockfile --prod=false $STD pnpm build +$STD pnpm db:generate +$STD pnpm db:migrate msg_ok "Setup Open Archiver" msg_info "Creating Service" @@ -102,7 +104,7 @@ Type=simple User=root EnvironmentFile=/opt/openarchiver/.env WorkingDirectory=/opt/openarchiver -ExecStart=/usr/bin/pnpm start +ExecStart=/usr/bin/pnpm docker-start Restart=on-failure [Install] From 1b857049bccfcb3803549dda37ab38ffd4880c12 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Tue, 14 Oct 2025 19:40:58 +0200 Subject: [PATCH 1407/1733] Update Open Archiver --- ct/openarchiver.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/openarchiver.sh b/ct/openarchiver.sh index 6bd2de36a..a92bbdf0a 100644 --- a/ct/openarchiver.sh +++ b/ct/openarchiver.sh @@ -39,4 +39,4 @@ description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" From 4927cf2db883497bb556503e355f818008031e0a Mon Sep 17 00:00:00 2001 From: tremor021 Date: Tue, 14 Oct 2025 20:11:20 +0200 Subject: [PATCH 1408/1733] test Open Archiver --- install/openarchiver-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/openarchiver-install.sh b/install/openarchiver-install.sh index 56d9797be..b92d54df8 100644 --- a/install/openarchiver-install.sh +++ b/install/openarchiver-install.sh @@ -90,7 +90,7 @@ sed -i "s|^TIKA_URL=.*|TIKA_URL=|g" /opt/openarchiver/.env $STD pnpm install --shamefully-hoist --frozen-lockfile --prod=false $STD pnpm build $STD pnpm db:generate -$STD pnpm db:migrate +#$STD pnpm db:migrate msg_ok "Setup Open Archiver" msg_info "Creating Service" From 93be50c45158b758e1a3173f5c761c114c7e0909 Mon Sep 17 00:00:00 2001 From: SunFlowerOwl <85146049+SunFlowerOwl@users.noreply.github.com> Date: Wed, 15 Oct 2025 03:02:25 -0400 Subject: [PATCH 1409/1733] Create docker-transmission-openvpn application (#866) * Create docker-transmission-openvpn application * Partially addressed review 1 * Fix: Avoid .env generation error * Add execute right on image scripts * Fix: Force transmission home var * Fix: unsupported custom app path * Review: Make .env generation simpler * Fix: Change cron command causing error due to crlf * Review: Rework fetching WebUI * Improve cleaning up * Add warning message to setup vpn provider * Rework update function * Fix: Change var_tun default value to yes * Review: Remove Healthcheck cron job * Review: Directly deploy WebUI to /opt/transmission-ui/ * Make custom config persistent when updating * Fix: fetch WebUI from correct URL (old one was invalid) * Fix: resolve conflicting Privoxy instance * Fix: align binary path when fetching branch vs release * Security: Remove old and deprecated WebUI for security reasons * Review: drop unnecessary packages and optimize implementations * Review: Implement local network dynamic detection * CR, OS: Migrate to Debian 13 * Use quiet mode for several commands as recommended * Review: Replace hard coding app name instead of using var $APP * Review: Reduce amount of msg_blocks * refactor * refactor --------- Co-authored-by: Tobias <96661824+CrazyWolf13@users.noreply.github.com> --- ct/transmission-openvpn.sh | 86 +++++++++++ .../public/json/transmission-openvpn.json | 40 +++++ install/transmission-openvpn-install.sh | 140 ++++++++++++++++++ 3 files changed, 266 insertions(+) create mode 100644 ct/transmission-openvpn.sh create mode 100644 frontend/public/json/transmission-openvpn.json create mode 100644 install/transmission-openvpn-install.sh diff --git a/ct/transmission-openvpn.sh b/ct/transmission-openvpn.sh new file mode 100644 index 000000000..647339241 --- /dev/null +++ b/ct/transmission-openvpn.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: SunFlowerOwl +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/haugene/docker-transmission-openvpn + +APP="transmission-openvpn" +var_tags="${var_tags:-torrent;vpn}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-512}" +var_disk="${var_disk:-8}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" +var_tun="${var_tun:-yes}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/transmission-openvpn/ ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + msg_info "Updating Dependencies" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Updated Dependencies" + + if check_for_gh_release "docker-transmission-openvpn" "haugene/docker-transmission-openvpn"; then + msg_info "Stopping Service" + systemctl stop openvpn-custom + msg_ok "Stopped Service" + + msg_info "Creating Backup" + mv /etc/openvpn/custom /opt/transmission-openvpn/ + rm -f /opt/transmission-openvpn/config-failure.sh + msg_ok "Created Backup" + + fetch_and_deploy_gh_release "docker-transmission-openvpn" "haugene/docker-transmission-openvpn" "tarball" "latest" "/opt/docker-transmission-openvpn" + + msg_info "Configuring transmission-openvpn" + rm -rf /etc/openvpn/* /etc/transmission/* /etc/scripts/* /opt/privoxy/* + cp -r /opt/docker-transmission-openvpn/openvpn/* /etc/openvpn/ + cp -r /opt/docker-transmission-openvpn/transmission/* /etc/transmission/ + cp -r /opt/docker-transmission-openvpn/scripts/* /etc/scripts/ + cp -r /opt/docker-transmission-openvpn/privoxy/scripts/* /opt/privoxy/ + chmod +x /etc/openvpn/*.sh + chmod +x /etc/scripts/*.sh + chmod +x /opt/privoxy/*.sh + msg_ok "Configured transmission-openvpn" + + msg_info "Restoring Backup" + cp -r /opt/transmission-openvpn/custom/* /etc/openvpn/custom/ + msg_ok "Restored Backup" + + msg_info "Starting Service" + systemctl start openvpn-custom + msg_ok "Started Service" + fi + + msg_info "Cleaning up" + $STD apt -y autoremove + $STD apt -y autoclean + $STD apt -y clean + rm -rf /opt/docker-transmission-openvpn + msg_ok "Cleaned" + + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9091${CL}" diff --git a/frontend/public/json/transmission-openvpn.json b/frontend/public/json/transmission-openvpn.json new file mode 100644 index 000000000..965122f8e --- /dev/null +++ b/frontend/public/json/transmission-openvpn.json @@ -0,0 +1,40 @@ +{ + "name": "Transmission-Openvpn", + "slug": "transmission-openvpn", + "categories": [ + 11 + ], + "date_created": "2025-09-04", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 9091, + "documentation": "https://haugene.github.io/docker-transmission-openvpn/", + "config_path": "/opt/transmission-openvpn/", + "website": "https://github.com/haugene/docker-transmission-openvpn", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/transmission.webp", + "description": "This project runs Transmission + OpenVPN natively in an LXC container, using the popular docker-transmission-openvpn image as a base. It ensures all torrent traffic is securely routed through a VPN tunnel, supports a wide range of VPN providers, and offers flexible configuration options", + "install_methods": [ + { + "type": "default", + "script": "ct/transmission-openvpn.sh", + "resources": { + "cpu": 1, + "ram": 512, + "hdd": 8, + "os": "Debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "This application requires a VPN provider to work. Please refer to your VPN provider’s documentation for setting up OpenVPN.", + "type": "warning" + } + ] +} diff --git a/install/transmission-openvpn-install.sh b/install/transmission-openvpn-install.sh new file mode 100644 index 000000000..7eeeb422c --- /dev/null +++ b/install/transmission-openvpn-install.sh @@ -0,0 +1,140 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: SunFlowerOwl +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/haugene/docker-transmission-openvpn + +# Import Functions und Setup +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt install -y \ + dnsutils \ + iputils-ping \ + ufw \ + iproute2 +mkdir -p /etc/systemd/system-preset +echo "disable *" > /etc/systemd/system-preset/99-no-autostart.preset +$STD apt install -y \ + transmission-daemon \ + privoxy +rm -f /etc/systemd/system-preset/99-no-autostart.preset +$STD systemctl preset-all +$STD systemctl disable --now transmission-daemon +$STD systemctl mask transmission-daemon +$STD systemctl disable --now privoxy +$STD systemctl mask privoxy +$STD apt install -y openvpn +msg_ok "Installed Dependencies" + +fetch_and_deploy_gh_release "docker-transmission-openvpn" "haugene/docker-transmission-openvpn" "tarball" "latest" "/opt/docker-transmission-openvpn" + +msg_info "Configuring transmission-openvpn" +$STD useradd -u 911 -U -d /config -s /usr/sbin/nologin abc +mkdir -p /etc/openvpn /etc/transmission /etc/scripts /opt/privoxy +cp -r /opt/docker-transmission-openvpn/openvpn/* /etc/openvpn/ +cp -r /opt/docker-transmission-openvpn/transmission/* /etc/transmission/ +cp -r /opt/docker-transmission-openvpn/scripts/* /etc/scripts/ +cp -r /opt/docker-transmission-openvpn/privoxy/scripts/* /opt/privoxy/ +chmod +x /etc/openvpn/*.sh +chmod +x /etc/scripts/*.sh +chmod +x /opt/privoxy/*.sh +$STD ln -s /usr/bin/transmission-daemon /usr/local/bin/transmission-daemon +$STD update-alternatives --set iptables /usr/sbin/iptables-legacy +$STD update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy +msg_ok "Configured transmission-openvpn" + +msg_info "Creating Service" +LOCAL_SUBNETS=$( + ip -o -4 addr show \ + | awk '!/127.0.0.1/ { + split($4, a, "/"); ip=a[1]; mask=a[2]; + split(ip, o, "."); + if (mask < 8) { + print "*.*.*.*"; + } else if (mask < 16) { + print o[1]".*.*.*"; + } else if (mask < 24) { + print o[1]"."o[2]".*.*"; + } else { + print o[1]"."o[2]"."o[3]".*"; + } + }' \ + | sort -u | paste -sd, - +) +TRANSMISSION_RPC_WHITELIST="127.0.0.*,${LOCAL_SUBNETS}" +mkdir -p /opt/transmission-openvpn +cat < "/opt/transmission-openvpn/.env" +OPENVPN_USERNAME="username" +OPENVPN_PASSWORD="password" +OPENVPN_PROVIDER="PIA" +OPENVPN_CONFIG=france +OPENVPN_OPTS="--inactive 3600 --ping 10 --ping-exit 60 --mute-replay-warnings" +CUSTOM_OPENVPN_CONFIG_DIR="/opt/transmission-openvpn" +GLOBAL_APPLY_PERMISSIONS="true" +TRANSMISSION_HOME="/config/transmission-home" +TRANSMISSION_RPC_PORT="9091" +TRANSMISSION_RPC_USERNAME="" +TRANSMISSION_RPC_PASSWORD="" +TRANSMISSION_DOWNLOAD_DIR="/data/complete" +TRANSMISSION_INCOMPLETE_DIR="/data/incomplete" +TRANSMISSION_WATCH_DIR="/data/watch" +TRANSMISSION_WEB_UI="" +TRANSMISSION_UMASK="2" +TRANSMISSION_RATIO_LIMIT_ENABLED="true" +TRANSMISSION_RATIO_LIMIT="0" +TRANSMISSION_RPC_WHITELIST_ENABLED="false" +TRANSMISSION_RPC_WHITELIST="${TRANSMISSION_RPC_WHITELIST}" +CREATE_TUN_DEVICE="false" +ENABLE_UFW="false" +UFW_ALLOW_GW_NET="false" +UFW_EXTRA_PORTS="" +UFW_DISABLE_IPTABLES_REJECT="false" +PUID="911" +PGID="" +PEER_DNS="true" +PEER_DNS_PIN_ROUTES="true" +DROP_DEFAULT_ROUTE="" +WEBPROXY_ENABLED="true" +WEBPROXY_PORT="8118" +WEBPROXY_BIND_ADDRESS="" +WEBPROXY_USERNAME="" +WEBPROXY_PASSWORD="" +LOG_TO_STDOUT="false" +HEALTH_CHECK_HOST="google.com" +SELFHEAL="false" +EOF +cat < /etc/systemd/system/openvpn-custom.service +[Unit] +Description=Custom OpenVPN start service +After=network.target + +[Service] +Type=simple +ExecStart=/etc/openvpn/start.sh +Restart=on-failure +RestartSec=5 +EnvironmentFile=/opt/transmission-openvpn/.env + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable --now -q openvpn-custom.service +msg_ok "Created Service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt -y autoremove +$STD apt -y autoclean +$STD apt -y clean +rm -rf /opt/docker-transmission-openvpn +msg_ok "Cleaned" From 9a793e8cadec984330d315afce26d64423bc2650 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 15 Oct 2025 07:02:51 +0000 Subject: [PATCH 1410/1733] Update .app files --- ct/headers/inventree | 6 ------ ct/headers/openarchiver | 6 ++++++ ct/headers/transmission-openvpn | 6 ++++++ 3 files changed, 12 insertions(+), 6 deletions(-) delete mode 100644 ct/headers/inventree create mode 100644 ct/headers/openarchiver create mode 100644 ct/headers/transmission-openvpn diff --git a/ct/headers/inventree b/ct/headers/inventree deleted file mode 100644 index 6057785ae..000000000 --- a/ct/headers/inventree +++ /dev/null @@ -1,6 +0,0 @@ - ____ ______ - / _/___ _ _____ ____/_ __/_______ ___ - / // __ \ | / / _ \/ __ \/ / / ___/ _ \/ _ \ - _/ // / / / |/ / __/ / / / / / / / __/ __/ -/___/_/ /_/|___/\___/_/ /_/_/ /_/ \___/\___/ - diff --git a/ct/headers/openarchiver b/ct/headers/openarchiver new file mode 100644 index 000000000..cad7937d3 --- /dev/null +++ b/ct/headers/openarchiver @@ -0,0 +1,6 @@ + ____ ___ __ _ + / __ \____ ___ ____ / | __________/ /_ (_) _____ _____ + / / / / __ \/ _ \/ __ \ / /| | / ___/ ___/ __ \/ / | / / _ \/ ___/ +/ /_/ / /_/ / __/ / / / / ___ |/ / / /__/ / / / /| |/ / __/ / +\____/ .___/\___/_/ /_/ /_/ |_/_/ \___/_/ /_/_/ |___/\___/_/ + /_/ diff --git a/ct/headers/transmission-openvpn b/ct/headers/transmission-openvpn new file mode 100644 index 000000000..3c6830b05 --- /dev/null +++ b/ct/headers/transmission-openvpn @@ -0,0 +1,6 @@ + __ _ _ + / /__________ _____ _________ ___ (_)_________(_)___ ____ ____ ____ ___ ____ _ ______ ____ + / __/ ___/ __ `/ __ \/ ___/ __ `__ \/ / ___/ ___/ / __ \/ __ \______/ __ \/ __ \/ _ \/ __ \ | / / __ \/ __ \ +/ /_/ / / /_/ / / / (__ ) / / / / / (__ |__ ) / /_/ / / / /_____/ /_/ / /_/ / __/ / / / |/ / /_/ / / / / +\__/_/ \__,_/_/ /_/____/_/ /_/ /_/_/____/____/_/\____/_/ /_/ \____/ .___/\___/_/ /_/|___/ .___/_/ /_/ + /_/ /_/ From b9b9aafefc102d0d4728c1783a4171b605129fef Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 16 Oct 2025 19:04:49 +0200 Subject: [PATCH 1411/1733] Update Open Archiver --- ct/openarchiver.sh | 37 +++++++++++++++++++------- frontend/public/json/openarchiver.json | 35 ++++++++++++++++++++++++ install/openarchiver-install.sh | 3 +-- 3 files changed, 64 insertions(+), 11 deletions(-) create mode 100644 frontend/public/json/openarchiver.json diff --git a/ct/openarchiver.sh b/ct/openarchiver.sh index a92bbdf0a..c65ec9f3d 100644 --- a/ct/openarchiver.sh +++ b/ct/openarchiver.sh @@ -20,16 +20,35 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/openarchiver ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - msg_warn "Application is updated via Web Interface" + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/openarchiver ]]; then + msg_error "No Open Archiver Installation Found!" exit + fi + + if check_for_gh_release "openarchiver" "LogicLabs-OU/OpenArchiver"; then + msg_info "Stopping Services" + systemctl stop openarchiver + msg_ok "Stopped Services" + + cp /opt/openarchiver/.env /opt/openarchiver.env + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "openarchiver" "LogicLabs-OU/OpenArchiver" "tarball" "latest" "/opt/openarchiver" + mv /opt/openarchiver.env /opt/openarchiver/.env + + msg_info "Updating Open Archiver" + $STD pnpm install --shamefully-hoist --frozen-lockfile --prod=false + $STD pnpm build + $STD pnpm db:migrate + msg_ok "Updated Open Archiver" + + msg_info "Starting Services" + systemctl start openarchiver + msg_ok "Started Services" + msg_ok "Updated Successfully" + fi + exit } start diff --git a/frontend/public/json/openarchiver.json b/frontend/public/json/openarchiver.json new file mode 100644 index 000000000..268d36da6 --- /dev/null +++ b/frontend/public/json/openarchiver.json @@ -0,0 +1,35 @@ +{ + "name": "Open Archiver", + "slug": "openarchiver", + "categories": [ + 7 + ], + "date_created": "2025-09-30", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 3000, + "documentation": "https://docs.openarchiver.com/", + "config_path": "/opt/openarchiver/.env", + "website": "https://openarchiver.com/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/OpenArchiver.webp", + "description": "Open Archiver is a secure, self-hosted email archiving solution, and it's completely open source. Get an email archiver that enables full-text search across email and attachments. Create a permanent, searchable, and compliant mail archive from Google Workspace, Microsoft 35, and any IMAP server.", + "install_methods": [ + { + "type": "default", + "script": "ct/openarchiver.sh", + "resources": { + "cpu": 2, + "ram": 3072, + "hdd": 8, + "os": "debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} diff --git a/install/openarchiver-install.sh b/install/openarchiver-install.sh index b92d54df8..57c900925 100644 --- a/install/openarchiver-install.sh +++ b/install/openarchiver-install.sh @@ -89,8 +89,7 @@ sed -i "s|^ENCRYPTION_KEY=.*|ENCRYPTION_KEY=$SECRET_KEY|g" /opt/openarchiver/.en sed -i "s|^TIKA_URL=.*|TIKA_URL=|g" /opt/openarchiver/.env $STD pnpm install --shamefully-hoist --frozen-lockfile --prod=false $STD pnpm build -$STD pnpm db:generate -#$STD pnpm db:migrate +$STD pnpm db:migrate msg_ok "Setup Open Archiver" msg_info "Creating Service" From f215a7c754546a9b2018fda35b5d25ee259d7b4e Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 16 Oct 2025 19:10:25 +0200 Subject: [PATCH 1412/1733] Update Open Archiver --- frontend/public/json/openarchiver.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/public/json/openarchiver.json b/frontend/public/json/openarchiver.json index 268d36da6..e1d96d91d 100644 --- a/frontend/public/json/openarchiver.json +++ b/frontend/public/json/openarchiver.json @@ -31,5 +31,10 @@ "username": null, "password": null }, - "notes": [] + "notes": [ + { + "text": "Data directory is: `/opt/openarchiver-data`. If you have a lot of email, you might consider mounting external storage to this directory.", + "type": "info" + } + ] } From 4888a827c591d33534ac7927a7065c32d544c3fe Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 16 Oct 2025 19:13:52 +0200 Subject: [PATCH 1413/1733] Update Open Archiver --- ct/{openarchiver.sh => open-archiver.sh} | 0 .../public/json/{openarchiver.json => open-archiver.json} | 2 +- .../{openarchiver-install.sh => open-archiver-install.sh} | 8 ++++---- 3 files changed, 5 insertions(+), 5 deletions(-) rename ct/{openarchiver.sh => open-archiver.sh} (100%) rename frontend/public/json/{openarchiver.json => open-archiver.json} (96%) rename install/{openarchiver-install.sh => open-archiver-install.sh} (96%) diff --git a/ct/openarchiver.sh b/ct/open-archiver.sh similarity index 100% rename from ct/openarchiver.sh rename to ct/open-archiver.sh diff --git a/frontend/public/json/openarchiver.json b/frontend/public/json/open-archiver.json similarity index 96% rename from frontend/public/json/openarchiver.json rename to frontend/public/json/open-archiver.json index e1d96d91d..66d1f8091 100644 --- a/frontend/public/json/openarchiver.json +++ b/frontend/public/json/open-archiver.json @@ -17,7 +17,7 @@ "install_methods": [ { "type": "default", - "script": "ct/openarchiver.sh", + "script": "ct/open-archiver.sh", "resources": { "cpu": 2, "ram": 3072, diff --git a/install/openarchiver-install.sh b/install/open-archiver-install.sh similarity index 96% rename from install/openarchiver-install.sh rename to install/open-archiver-install.sh index 57c900925..53eba7fff 100644 --- a/install/openarchiver-install.sh +++ b/install/open-archiver-install.sh @@ -34,10 +34,10 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8' $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" { - echo "Open Archiver DB Credentials" - echo "Database Name: $DB_NAME" - echo "Database User: $DB_USER" - echo "Database Password: $DB_PASS" + echo "Open Archiver DB Credentials" + echo "Database Name: $DB_NAME" + echo "Database User: $DB_USER" + echo "Database Password: $DB_PASS" } >>~/openarchiver.creds msg_ok "Set up PostgreSQL" From df5cb910a8e8481b12eb4e483a26c5163712ee75 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 16 Oct 2025 19:20:57 +0200 Subject: [PATCH 1414/1733] Update Open Archiver --- ct/open-archiver.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/open-archiver.sh b/ct/open-archiver.sh index c65ec9f3d..8bc105a23 100644 --- a/ct/open-archiver.sh +++ b/ct/open-archiver.sh @@ -5,7 +5,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://openarchiver.com/ -APP="Open Archiver" +APP="Open-Archiver" var_tags="${var_tags:-os}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-3072}" From e340f1cbd4ab241170a6b560046a45c58d95ed7f Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 16 Oct 2025 19:58:57 +0200 Subject: [PATCH 1415/1733] Update Open Archiver --- install/open-archiver-install.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/install/open-archiver-install.sh b/install/open-archiver-install.sh index 53eba7fff..4c21e230e 100644 --- a/install/open-archiver-install.sh +++ b/install/open-archiver-install.sh @@ -23,6 +23,8 @@ fetch_and_deploy_gh_release "meilisearch" "meilisearch/meilisearch" "binary" fetch_and_deploy_gh_release "openarchiver" "LogicLabs-OU/OpenArchiver" "tarball" JWT_KEY="$(openssl rand -hex 32)" SECRET_KEY="$(openssl rand -hex 32)" +IP_ADDR=$(hostname -I | awk '{print $1}') + msg_info "Setting up PostgreSQL" DB_NAME="openarchiver_db" @@ -87,6 +89,8 @@ sed -i "s|^STORAGE_LOCAL_ROOT_PATH=.*|STORAGE_LOCAL_ROOT_PATH=/opt/openarchiver- sed -i "s|^JWT_SECRET=.*|JWT_SECRET=$JWT_KEY|g" /opt/openarchiver/.env sed -i "s|^ENCRYPTION_KEY=.*|ENCRYPTION_KEY=$SECRET_KEY|g" /opt/openarchiver/.env sed -i "s|^TIKA_URL=.*|TIKA_URL=|g" /opt/openarchiver/.env +IP_ADDR=$(hostname -I | awk '{print $1}') +echo "ORIGIN=http://$IP_ADDR:3000" >> /opt/openarchiver/.env $STD pnpm install --shamefully-hoist --frozen-lockfile --prod=false $STD pnpm build $STD pnpm db:migrate From a3adc327255969722537aa41b37ffa437bfd4039 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 16 Oct 2025 19:59:24 +0200 Subject: [PATCH 1416/1733] Update Open Archiver --- install/open-archiver-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/install/open-archiver-install.sh b/install/open-archiver-install.sh index 4c21e230e..2029e0d5a 100644 --- a/install/open-archiver-install.sh +++ b/install/open-archiver-install.sh @@ -89,7 +89,6 @@ sed -i "s|^STORAGE_LOCAL_ROOT_PATH=.*|STORAGE_LOCAL_ROOT_PATH=/opt/openarchiver- sed -i "s|^JWT_SECRET=.*|JWT_SECRET=$JWT_KEY|g" /opt/openarchiver/.env sed -i "s|^ENCRYPTION_KEY=.*|ENCRYPTION_KEY=$SECRET_KEY|g" /opt/openarchiver/.env sed -i "s|^TIKA_URL=.*|TIKA_URL=|g" /opt/openarchiver/.env -IP_ADDR=$(hostname -I | awk '{print $1}') echo "ORIGIN=http://$IP_ADDR:3000" >> /opt/openarchiver/.env $STD pnpm install --shamefully-hoist --frozen-lockfile --prod=false $STD pnpm build From e6634aaa933ae7352541fc8185b06ae87e62aa81 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 16 Oct 2025 20:01:07 +0200 Subject: [PATCH 1417/1733] Update Open Archiver --- install/open-archiver-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/install/open-archiver-install.sh b/install/open-archiver-install.sh index 2029e0d5a..628ede760 100644 --- a/install/open-archiver-install.sh +++ b/install/open-archiver-install.sh @@ -25,7 +25,6 @@ JWT_KEY="$(openssl rand -hex 32)" SECRET_KEY="$(openssl rand -hex 32)" IP_ADDR=$(hostname -I | awk '{print $1}') - msg_info "Setting up PostgreSQL" DB_NAME="openarchiver_db" DB_USER="openarchiver" From 9286678bc0cbb8da865dbbca4719ab3062b86d15 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 16 Oct 2025 21:57:49 +0200 Subject: [PATCH 1418/1733] Refactor Tools.func --- misc/tools.func | 3936 ++++++++++++++++++++++++++----------------- misc/tools.func.bak | 2208 ++++++++++++++++++++++++ 2 files changed, 4593 insertions(+), 1551 deletions(-) create mode 100644 misc/tools.func.bak diff --git a/misc/tools.func b/misc/tools.func index e98332517..a9a385cdd 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -1,786 +1,939 @@ #!/bin/bash -# ------------------------------------------------------------------------------ -# Installs Microsoft .NET -# -# Description: -# - Installs specified .NET version using Microsoft APT repo -# -# Variables: -# NET_VERSION - .NET version to install (default: "8.0") -# ------------------------------------------------------------------------------ +# ============================================================================== +# HELPER FUNCTIONS FOR PACKAGE MANAGEMENT +# ============================================================================== -function setup_dotnet() { - local NET_VERSION="${NET_VERSION:-8.0}" - local DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{ print $2 }' /etc/os-release) - local NEED_NET_INSTALL=false +# ------------------------------------------------------------------------------ +# Cache installed version to avoid repeated checks +# ------------------------------------------------------------------------------ +cache_installed_version() { + local app="$1" + local version="$2" + mkdir -p /var/cache/app-versions + echo "$version" >"/var/cache/app-versions/${app}_version.txt" +} - if command -v dotnet >/dev/null; then - CURRENT_NET_VERSION=$(dotnet --version | awk -F. '{print $1 "." $2}') - if [[ "$CURRENT_NET_VERSION" == "$NET_VERSION" ]]; then - : - fi - else - msg_info "Setup .NET $NET_VERSION" - NEED_NET_INSTALL=true +get_cached_version() { + local app="$1" + [[ -f "/var/cache/app-versions/${app}_version.txt" ]] && cat "/var/cache/app-versions/${app}_version.txt" +} + +# ------------------------------------------------------------------------------ +# Unified package upgrade function (with apt update caching) +# ------------------------------------------------------------------------------ +upgrade_package() { + local package="$1" + msg_info "Upgrading $package" + + # Use same caching logic as ensure_dependencies + local apt_cache_file="/var/cache/apt-update-timestamp" + local current_time=$(date +%s) + local last_update=0 + + if [[ -f "$apt_cache_file" ]]; then + last_update=$(cat "$apt_cache_file" 2>/dev/null || echo 0) fi - if [[ "$NEED_NET_INSTALL" == true ]]; then - curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor -o /usr/share/keyrings/microsoft-prod.gpg - if [[ "$DISTRO_CODENAME" != "trixie" ]]; then - curl -fsSL https://packages.microsoft.com/config/debian/12/prod.list -o /etc/apt/sources.list.d/msprod.list - else - curl -fsSL https://packages.microsoft.com/config/debian/13/prod.list -o /etc/apt/sources.list.d/msprod.list + if ((current_time - last_update > 300)); then + $STD apt update + echo "$current_time" >"$apt_cache_file" + fi + + $STD apt install --only-upgrade -y "$package" + msg_ok "Upgraded $package" +} + +# ------------------------------------------------------------------------------ +# Repository availability check +# ------------------------------------------------------------------------------ +verify_repo_available() { + local repo_url="$1" + local suite="$2" + + if curl -fsSL --max-time 10 "${repo_url}/dists/${suite}/Release" &>/dev/null; then + return 0 + fi + return 1 +} + +# ------------------------------------------------------------------------------ +# Ensure dependencies are installed (with apt update caching) +# ------------------------------------------------------------------------------ +ensure_dependencies() { + local deps=("$@") + local missing=() + + for dep in "${deps[@]}"; do + if ! command -v "$dep" &>/dev/null && ! is_package_installed "$dep"; then + missing+=("$dep") fi - $STD apt-get update - $STD apt-get install -y dotnet-sdk-$NET_VERSION - msg_ok "Setup .NET ${NET_VERSION}" + done + + if [[ ${#missing[@]} -gt 0 ]]; then + msg_info "Installing dependencies: ${missing[*]}" + + # Only run apt update if not done recently (within last 5 minutes) + local apt_cache_file="/var/cache/apt-update-timestamp" + local current_time=$(date +%s) + local last_update=0 + + if [[ -f "$apt_cache_file" ]]; then + last_update=$(cat "$apt_cache_file" 2>/dev/null || echo 0) + fi + + if ((current_time - last_update > 300)); then + $STD apt update + echo "$current_time" >"$apt_cache_file" + fi + + $STD apt install -y "${missing[@]}" + msg_ok "Installed dependencies" fi } # ------------------------------------------------------------------------------ -# Installs Node.js and optional global modules. -# -# Description: -# - Installs specified Node.js version using NodeSource APT repo -# - Optionally installs or updates global npm modules -# -# Variables: -# NODE_VERSION - Node.js version to install (default: 22) -# NODE_MODULE - Comma-separated list of global modules (e.g. "yarn,@vue/cli@5.0.0") +# Smart version comparison # ------------------------------------------------------------------------------ +version_gt() { + test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1" +} -function setup_nodejs() { - local NODE_VERSION="${NODE_VERSION:-22}" - local NODE_MODULE="${NODE_MODULE:-}" - local CURRENT_NODE_VERSION="" - local NEED_NODE_INSTALL=false +# ------------------------------------------------------------------------------ +# Get system architecture (normalized) +# ------------------------------------------------------------------------------ +get_system_arch() { + local arch_type="${1:-dpkg}" # dpkg, uname, or both + local arch - if command -v node >/dev/null; then - CURRENT_NODE_VERSION="$(node -v | grep -oP '^v\K[0-9]+')" - if [[ "$CURRENT_NODE_VERSION" != "$NODE_VERSION" ]]; then - msg_info "Old Node.js $CURRENT_NODE_VERSION found, replacing with $NODE_VERSION" - NEED_NODE_INSTALL=true - fi - else - msg_info "Setup Node.js $NODE_VERSION" - NEED_NODE_INSTALL=true - fi + case "$arch_type" in + dpkg) + arch=$(dpkg --print-architecture 2>/dev/null) + ;; + uname) + arch=$(uname -m) + [[ "$arch" == "x86_64" ]] && arch="amd64" + [[ "$arch" == "aarch64" ]] && arch="arm64" + ;; + both | *) + arch=$(dpkg --print-architecture 2>/dev/null || uname -m) + [[ "$arch" == "x86_64" ]] && arch="amd64" + [[ "$arch" == "aarch64" ]] && arch="arm64" + ;; + esac - if ! command -v jq &>/dev/null; then - $STD apt-get update - $STD apt-get install -y jq || { - msg_error "Failed to install jq" + echo "$arch" +} + +# ------------------------------------------------------------------------------ +# Create temporary directory with automatic cleanup +# ------------------------------------------------------------------------------ +create_temp_dir() { + local tmp_dir=$(mktemp -d) + # Set trap to cleanup on EXIT, ERR, INT, TERM + trap "rm -rf '$tmp_dir'" EXIT ERR INT TERM + echo "$tmp_dir" +} + +# ------------------------------------------------------------------------------ +# Check if package is installed (faster than dpkg -l | grep) +# ------------------------------------------------------------------------------ +is_package_installed() { + local package="$1" + dpkg-query -W -f='${Status}' "$package" 2>/dev/null | grep -q "^install ok installed$" +} + +# ------------------------------------------------------------------------------ +# GitHub API call with authentication and rate limit handling +# ------------------------------------------------------------------------------ +github_api_call() { + local url="$1" + local output_file="${2:-/dev/stdout}" + local max_retries=3 + local retry_delay=2 + + local header_args=() + [[ -n "${GITHUB_TOKEN:-}" ]] && header_args=(-H "Authorization: Bearer $GITHUB_TOKEN") + + for attempt in $(seq 1 $max_retries); do + local http_code + http_code=$(curl -fsSL -w "%{http_code}" -o "$output_file" \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "${header_args[@]}" \ + "$url" 2>/dev/null || echo "000") + + case "$http_code" in + 200) + return 0 + ;; + 403) + # Rate limit - check if we can retry + if [[ $attempt -lt $max_retries ]]; then + msg_warn "GitHub API rate limit, waiting ${retry_delay}s... (attempt $attempt/$max_retries)" + sleep "$retry_delay" + retry_delay=$((retry_delay * 2)) + continue + fi + msg_error "GitHub API rate limit exceeded. Set GITHUB_TOKEN to increase limits." return 1 - } + ;; + 404) + msg_error "GitHub API endpoint not found: $url" + return 1 + ;; + *) + if [[ $attempt -lt $max_retries ]]; then + sleep "$retry_delay" + continue + fi + msg_error "GitHub API call failed with HTTP $http_code" + return 1 + ;; + esac + done + + return 1 +} + +should_upgrade() { + local current="$1" + local target="$2" + + [[ -z "$current" ]] && return 0 + version_gt "$target" "$current" && return 0 + return 1 +} + +# ------------------------------------------------------------------------------ +# Get OS information (cached for performance) +# ------------------------------------------------------------------------------ +get_os_info() { + local field="${1:-all}" # id, codename, version, version_id, all + + # Cache OS info to avoid repeated file reads + if [[ -z "${_OS_ID:-}" ]]; then + export _OS_ID=$(awk -F= '/^ID=/{gsub(/"/,"",$2); print $2}' /etc/os-release) + export _OS_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{gsub(/"/,"",$2); print $2}' /etc/os-release) + export _OS_VERSION=$(awk -F= '/^VERSION_ID=/{gsub(/"/,"",$2); print $2}' /etc/os-release) + export _OS_VERSION_FULL=$(awk -F= '/^VERSION=/{gsub(/"/,"",$2); print $2}' /etc/os-release) fi - if [[ "$NEED_NODE_INSTALL" == true ]]; then - $STD apt-get purge -y nodejs - rm -f /etc/apt/sources.list.d/nodesource.list /usr/share/keyrings/nodesource.gpg + case "$field" in + id) echo "$_OS_ID" ;; + codename) echo "$_OS_CODENAME" ;; + version) echo "$_OS_VERSION" ;; + version_id) echo "$_OS_VERSION" ;; + version_full) echo "$_OS_VERSION_FULL" ;; + all) echo "ID=$_OS_ID CODENAME=$_OS_CODENAME VERSION=$_OS_VERSION" ;; + *) echo "$_OS_ID" ;; + esac +} - mkdir -p /usr/share/keyrings - curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | - gpg --dearmor -o /usr/share/keyrings/nodesource.gpg || { - msg_error "Failed to import NodeSource GPG key" - exit 1 - } - chmod 644 /usr/share/keyrings/nodesource.gpg +# ------------------------------------------------------------------------------ +# Check if running on specific OS +# ------------------------------------------------------------------------------ +is_debian() { + [[ "$(get_os_info id)" == "debian" ]] +} - local ARCH - ARCH=$(dpkg --print-architecture) - if ! [[ "$ARCH" =~ ^(amd64|arm64|armhf)$ ]]; then - msg_error "Unsupported architecture: $ARCH" - exit 1 +is_ubuntu() { + [[ "$(get_os_info id)" == "ubuntu" ]] +} + +is_alpine() { + [[ "$(get_os_info id)" == "alpine" ]] +} + +# ------------------------------------------------------------------------------ +# Get Debian/Ubuntu major version +# ------------------------------------------------------------------------------ +get_os_version_major() { + local version=$(get_os_info version) + echo "${version%%.*}" +} + +# ------------------------------------------------------------------------------ +# Download file with retry logic and progress +# ------------------------------------------------------------------------------ +download_file() { + local url="$1" + local output="$2" + local max_retries="${3:-3}" + local show_progress="${4:-false}" + + local curl_opts=(-fsSL) + [[ "$show_progress" == "true" ]] && curl_opts=(-fL#) + + for attempt in $(seq 1 $max_retries); do + if curl "${curl_opts[@]}" -o "$output" "$url"; then + return 0 fi - echo "deb [arch=$ARCH signed-by=/usr/share/keyrings/nodesource.gpg] https://deb.nodesource.com/node_${NODE_VERSION}.x nodistro main" >/etc/apt/sources.list.d/nodesource.list + if [[ $attempt -lt $max_retries ]]; then + msg_warn "Download failed, retrying... (attempt $attempt/$max_retries)" + sleep 2 + fi + done - cat </etc/apt/preferences.d/nodejs -Package: nodejs -Pin: origin deb.nodesource.com -Pin-Priority: 700 + msg_error "Failed to download: $url" + return 1 +} + +# ------------------------------------------------------------------------------ +# Get fallback suite for repository (comprehensive mapping) +# ------------------------------------------------------------------------------ +get_fallback_suite() { + local distro_id="$1" + local distro_codename="$2" + local repo_base_url="$3" + + # Check if current codename works + if verify_repo_available "$repo_base_url" "$distro_codename"; then + echo "$distro_codename" + return 0 + fi + + # Comprehensive fallback mappings + case "$distro_id" in + debian) + case "$distro_codename" in + # Debian 13 (Trixie) → Debian 12 (Bookworm) + trixie | forky | sid) + echo "bookworm" + ;; + # Debian 12 (Bookworm) stays + bookworm) + echo "bookworm" + ;; + # Debian 11 (Bullseye) stays + bullseye) + echo "bullseye" + ;; + # Unknown → latest stable + *) + echo "bookworm" + ;; + esac + ;; + ubuntu) + case "$distro_codename" in + # Ubuntu 24.10 (Oracular) → 24.04 LTS (Noble) + oracular | plucky) + echo "noble" + ;; + # Ubuntu 24.04 LTS (Noble) stays + noble) + echo "noble" + ;; + # Ubuntu 23.10 (Mantic) → 22.04 LTS (Jammy) + mantic | lunar) + echo "jammy" + ;; + # Ubuntu 22.04 LTS (Jammy) stays + jammy) + echo "jammy" + ;; + # Ubuntu 20.04 LTS (Focal) stays + focal) + echo "focal" + ;; + # Unknown → latest LTS + *) + echo "jammy" + ;; + esac + ;; + *) + echo "$distro_codename" + ;; + esac +} + +# ------------------------------------------------------------------------------ +# Verify package source and version +# ------------------------------------------------------------------------------ +verify_package_source() { + local package="$1" + local expected_version="$2" + + if apt-cache policy "$package" 2>/dev/null | grep -q "$expected_version"; then + return 0 + fi + return 1 +} + +# ------------------------------------------------------------------------------ +# Check if running on LTS version +# ------------------------------------------------------------------------------ +is_lts_version() { + local os_id=$(get_os_info id) + local codename=$(get_os_info codename) + + if [[ "$os_id" == "ubuntu" ]]; then + case "$codename" in + focal | jammy | noble) return 0 ;; # 20.04, 22.04, 24.04 + *) return 1 ;; + esac + elif [[ "$os_id" == "debian" ]]; then + # Debian releases are all "stable" + case "$codename" in + bullseye | bookworm | trixie) return 0 ;; + *) return 1 ;; + esac + fi + + return 1 +} + +# ------------------------------------------------------------------------------ +# Get optimal number of parallel jobs (cached) +# ------------------------------------------------------------------------------ +get_parallel_jobs() { + if [[ -z "${_PARALLEL_JOBS:-}" ]]; then + local cpu_count=$(nproc 2>/dev/null || echo 1) + local mem_gb=$(free -g | awk '/^Mem:/{print $2}') + + # Limit by available memory (assume 1GB per job for compilation) + local max_by_mem=$((mem_gb > 0 ? mem_gb : 1)) + local max_jobs=$((cpu_count < max_by_mem ? cpu_count : max_by_mem)) + + # At least 1, at most cpu_count + export _PARALLEL_JOBS=$((max_jobs > 0 ? max_jobs : 1)) + fi + echo "$_PARALLEL_JOBS" +} + +# ------------------------------------------------------------------------------ +# Get default PHP version for OS +# ------------------------------------------------------------------------------ +get_default_php_version() { + local os_id=$(get_os_info id) + local os_version=$(get_os_version_major) + + case "$os_id" in + debian) + case "$os_version" in + 13) echo "8.3" ;; # Debian 13 (Trixie) + 12) echo "8.2" ;; # Debian 12 (Bookworm) + 11) echo "7.4" ;; # Debian 11 (Bullseye) + *) echo "8.2" ;; + esac + ;; + ubuntu) + case "$os_version" in + 24) echo "8.3" ;; # Ubuntu 24.04 LTS (Noble) + 22) echo "8.1" ;; # Ubuntu 22.04 LTS (Jammy) + 20) echo "7.4" ;; # Ubuntu 20.04 LTS (Focal) + *) echo "8.1" ;; + esac + ;; + *) + echo "8.2" + ;; + esac +} + +# ------------------------------------------------------------------------------ +# Get default Python version for OS +# ------------------------------------------------------------------------------ +get_default_python_version() { + local os_id=$(get_os_info id) + local os_version=$(get_os_version_major) + + case "$os_id" in + debian) + case "$os_version" in + 13) echo "3.12" ;; # Debian 13 (Trixie) + 12) echo "3.11" ;; # Debian 12 (Bookworm) + 11) echo "3.9" ;; # Debian 11 (Bullseye) + *) echo "3.11" ;; + esac + ;; + ubuntu) + case "$os_version" in + 24) echo "3.12" ;; # Ubuntu 24.04 LTS + 22) echo "3.10" ;; # Ubuntu 22.04 LTS + 20) echo "3.8" ;; # Ubuntu 20.04 LTS + *) echo "3.10" ;; + esac + ;; + *) + echo "3.11" + ;; + esac +} + +# ------------------------------------------------------------------------------ +# Get default Node.js LTS version +# ------------------------------------------------------------------------------ +get_default_nodejs_version() { + # Always return current LTS (as of 2025) + echo "20" +} + +# ------------------------------------------------------------------------------ +# Check if package manager is locked +# ------------------------------------------------------------------------------ +is_apt_locked() { + if fuser /var/lib/dpkg/lock-frontend &>/dev/null || + fuser /var/lib/apt/lists/lock &>/dev/null || + fuser /var/cache/apt/archives/lock &>/dev/null; then + return 0 + fi + return 1 +} + +# ------------------------------------------------------------------------------ +# Wait for apt to be available +# ------------------------------------------------------------------------------ +wait_for_apt() { + local max_wait="${1:-300}" # 5 minutes default + local waited=0 + + while is_apt_locked; do + if [[ $waited -ge $max_wait ]]; then + msg_error "Timeout waiting for apt to be available" + return 1 + fi + + debug_log "Waiting for apt to be available... (${waited}s)" + sleep 5 + waited=$((waited + 5)) + done + + return 0 +} + +# ------------------------------------------------------------------------------ +# Cleanup old repository files (migration helper) +# ------------------------------------------------------------------------------ +cleanup_old_repo_files() { + local app="$1" + + # Remove old-style .list files (including backups) + rm -f /etc/apt/sources.list.d/"${app}"*.list + rm -f /etc/apt/sources.list.d/"${app}"*.list.save + rm -f /etc/apt/sources.list.d/"${app}"*.list.distUpgrade + rm -f /etc/apt/sources.list.d/"${app}"*.list.dpkg-* + + # Remove old GPG keys from trusted.gpg.d + rm -f /etc/apt/trusted.gpg.d/"${app}"*.gpg + + # Remove duplicate .sources files (keep only the main one) + local sources_file="/etc/apt/sources.list.d/${app}.sources" + if [[ -f "$sources_file" ]]; then + find /etc/apt/sources.list.d/ -name "${app}*.sources" ! -name "${app}.sources" -delete 2>/dev/null || true + fi +} + +# ------------------------------------------------------------------------------ +# Standardized deb822 repository setup +# ------------------------------------------------------------------------------ +setup_deb822_repo() { + local name="$1" + local gpg_url="$2" + local repo_url="$3" + local suite="$4" + local component="${5:-main}" + local architectures="${6:-amd64 arm64}" + + msg_info "Setting up $name repository" + + # Cleanup old configs + cleanup_old_repo_files "$name" + + # Ensure keyring directory exists + mkdir -p /etc/apt/keyrings + + # Download GPG key + curl -fsSL "$gpg_url" | gpg --dearmor -o "/etc/apt/keyrings/${name}.gpg" + + # Create deb822 sources file + cat </etc/apt/sources.list.d/${name}.sources +Types: deb +URIs: $repo_url +Suites: $suite +Components: $component +Architectures: $architectures +Signed-By: /etc/apt/keyrings/${name}.gpg EOF - sleep 2 - if ! apt-get update >/dev/null 2>&1; then - msg_warn "APT update failed – retrying in 5s" - sleep 5 - if ! apt-get update >/dev/null 2>&1; then - msg_error "Failed to update APT repositories after adding NodeSource" - exit 1 - fi - fi + # Use cached apt update + local apt_cache_file="/var/cache/apt-update-timestamp" + local current_time=$(date +%s) + local last_update=0 - if ! apt-get install -y -t nodistro nodejs >/dev/null 2>&1; then - msg_error "Failed to install Node.js ${NODE_VERSION} from NodeSource" - apt-cache policy nodejs | tee "$STD" - exit 1 - fi - - $STD npm install -g npm@latest || { - msg_error "Failed to update npm to latest version" - } - msg_ok "Setup Node.js ${NODE_VERSION}" + if [[ -f "$apt_cache_file" ]]; then + last_update=$(cat "$apt_cache_file" 2>/dev/null || echo 0) fi - export NODE_OPTIONS="--max-old-space-size=4096" - - [[ -d /opt ]] || mkdir -p /opt - cd /opt || { - msg_error "Failed to set safe working directory before npm install" - exit 1 - } - - if [[ -n "$NODE_MODULE" ]]; then - IFS=',' read -ra MODULES <<<"$NODE_MODULE" - for mod in "${MODULES[@]}"; do - local MODULE_NAME MODULE_REQ_VERSION MODULE_INSTALLED_VERSION - if [[ "$mod" == @*/*@* ]]; then - MODULE_NAME="${mod%@*}" - MODULE_REQ_VERSION="${mod##*@}" - elif [[ "$mod" == *"@"* ]]; then - MODULE_NAME="${mod%@*}" - MODULE_REQ_VERSION="${mod##*@}" - else - MODULE_NAME="$mod" - MODULE_REQ_VERSION="latest" - fi - - if npm list -g --depth=0 "$MODULE_NAME" >/dev/null 2>&1; then - MODULE_INSTALLED_VERSION="$(npm list -g --depth=0 "$MODULE_NAME" | grep "$MODULE_NAME@" | awk -F@ '{print $2}' | tr -d '[:space:]')" - if [[ "$MODULE_REQ_VERSION" != "latest" && "$MODULE_REQ_VERSION" != "$MODULE_INSTALLED_VERSION" ]]; then - msg_info "Updating $MODULE_NAME from v$MODULE_INSTALLED_VERSION to v$MODULE_REQ_VERSION" - $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}" || { - msg_error "Failed to update $MODULE_NAME to version $MODULE_REQ_VERSION" - exit 1 - } - elif [[ "$MODULE_REQ_VERSION" == "latest" ]]; then - msg_info "Updating $MODULE_NAME to latest version" - $STD npm install -g "${MODULE_NAME}@latest" || { - msg_error "Failed to update $MODULE_NAME to latest version" - exit 1 - } - fi - else - msg_info "Installing $MODULE_NAME@$MODULE_REQ_VERSION" - $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}" || { - msg_error "Failed to install $MODULE_NAME@$MODULE_REQ_VERSION" - exit 1 - } - fi - done - msg_ok "Installed Node.js modules: $NODE_MODULE" + # For repo changes, always update but respect short-term cache (30s) + if ((current_time - last_update > 30)); then + $STD apt update + echo "$current_time" >"$apt_cache_file" fi + + msg_ok "Set up $name repository" } # ------------------------------------------------------------------------------ -# Installs or upgrades PostgreSQL and optional extensions/modules. -# -# Description: -# - Detects existing PostgreSQL version -# - Dumps all databases before upgrade -# - Adds PGDG repo and installs specified version -# - Installs optional PG_MODULES (e.g. postgis, contrib) -# - Restores dumped data post-upgrade -# -# Variables: -# PG_VERSION - Major PostgreSQL version (e.g. 15, 16) (default: 16) -# PG_MODULES - Comma-separated list of extensions (e.g. "postgis,contrib") +# Package version hold/unhold helpers # ------------------------------------------------------------------------------ -function setup_postgresql() { - local PG_VERSION="${PG_VERSION:-16}" - local PG_MODULES="${PG_MODULES:-}" - local CURRENT_PG_VERSION="" - local DISTRO - local NEED_PG_INSTALL=false - DISTRO="$(awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release)" +hold_package_version() { + local package="$1" + $STD apt-mark hold "$package" +} - if command -v psql >/dev/null; then - CURRENT_PG_VERSION="$(psql -V | awk '{print $3}' | cut -d. -f1)" - if [[ "$CURRENT_PG_VERSION" == "$PG_VERSION" ]]; then - : # PostgreSQL is already at the desired version – no action needed - else - $STD msg_info "Detected PostgreSQL $CURRENT_PG_VERSION, preparing upgrade to $PG_VERSION" - NEED_PG_INSTALL=true - fi +unhold_package_version() { + local package="$1" + $STD apt-mark unhold "$package" +} + +# ------------------------------------------------------------------------------ +# Safe service restart with verification +# ------------------------------------------------------------------------------ +safe_service_restart() { + local service="$1" + + if systemctl is-active --quiet "$service"; then + $STD systemctl restart "$service" else - NEED_PG_INSTALL=true + $STD systemctl start "$service" fi - if [[ "$NEED_PG_INSTALL" == true ]]; then - if [[ -n "$CURRENT_PG_VERSION" ]]; then - $STD msg_info "Dumping PostgreSQL $CURRENT_PG_VERSION data" - su - postgres -c "pg_dumpall > /var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql" - $STD msg_ok "Data dump completed" - - systemctl stop postgresql - fi - - rm -f /etc/apt/sources.list.d/pgdg.list /etc/apt/trusted.gpg.d/postgresql.gpg - - $STD msg_info "Adding PostgreSQL PGDG repository" - curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | - gpg --dearmor -o /etc/apt/trusted.gpg.d/postgresql.gpg - - echo "deb https://apt.postgresql.org/pub/repos/apt ${DISTRO}-pgdg main" \ - >/etc/apt/sources.list.d/pgdg.list - $STD apt-get update - $STD msg_ok "Repository added" - - msg_info "Setup PostgreSQL $PG_VERSION" - $STD apt-get install -y "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}" - - if [[ -n "$CURRENT_PG_VERSION" ]]; then - $STD apt-get purge -y "postgresql-${CURRENT_PG_VERSION}" "postgresql-client-${CURRENT_PG_VERSION}" || true - fi - systemctl enable -q --now postgresql - - if [[ -n "$CURRENT_PG_VERSION" ]]; then - $STD msg_info "Restoring dumped data" - su - postgres -c "psql < /var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql" - $STD msg_ok "Data restored" - fi - - $STD msg_ok "PostgreSQL $PG_VERSION installed" - fi - - # Install optional PostgreSQL modules - if [[ -n "$PG_MODULES" ]]; then - IFS=',' read -ra MODULES <<<"$PG_MODULES" - for module in "${MODULES[@]}"; do - local pkg="postgresql-${PG_VERSION}-${module}" - $STD msg_info "Setup PostgreSQL module/s: $pkg" - $STD apt-get install -y "$pkg" || { - msg_error "Failed to install $pkg" - continue - } - done - $STD msg_ok "Setup PostgreSQL modules" + if ! systemctl is-active --quiet "$service"; then + msg_error "Failed to start $service" + systemctl status "$service" --no-pager + return 1 fi + return 0 } # ------------------------------------------------------------------------------ -# Installs or updates MariaDB from official repo. -# -# Description: -# - Detects current MariaDB version and replaces it if necessary -# - Preserves existing database data -# - Dynamically determines latest GA version if "latest" is given -# -# Variables: -# MARIADB_VERSION - MariaDB version to install (e.g. 10.11, latest) (default: latest) -# ------------------------------------------------------------------------------ test +# Enable and start service (with error handling) +# ------------------------------------------------------------------------------ +enable_and_start_service() { + local service="$1" -setup_mariadb() { - local MARIADB_VERSION="${MARIADB_VERSION:-latest}" - local DISTRO_CODENAME - DISTRO_CODENAME="$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release)" - CURRENT_OS="$(awk -F= '/^ID=/{print $2}' /etc/os-release)" + if ! systemctl enable "$service" &>/dev/null; then + msg_warn "Could not enable $service (may not be installed yet)" + fi - if ! curl -fsI http://mirror.mariadb.org/repo/ >/dev/null; then - msg_error "MariaDB mirror not reachable" + if ! systemctl start "$service" &>/dev/null; then + msg_error "Failed to start $service" + systemctl status "$service" --no-pager return 1 fi - msg_info "Setting up MariaDB $MARIADB_VERSION" - # Grab dynamic latest LTS version - if [[ "$MARIADB_VERSION" == "latest" ]]; then - MARIADB_VERSION=$(curl -fsSL http://mirror.mariadb.org/repo/ | - grep -Eo '[0-9]+\.[0-9]+\.[0-9]+/' | - grep -vE 'rc/|rolling/' | - sed 's|/||' | - sort -Vr | - head -n1) - if [[ -z "$MARIADB_VERSION" ]]; then - msg_error "Could not determine latest GA MariaDB version" - return 1 - fi + return 0 +} + +# ------------------------------------------------------------------------------ +# Check if service is enabled +# ------------------------------------------------------------------------------ +is_service_enabled() { + local service="$1" + systemctl is-enabled --quiet "$service" 2>/dev/null +} + +# ------------------------------------------------------------------------------ +# Check if service is running +# ------------------------------------------------------------------------------ +is_service_running() { + local service="$1" + systemctl is-active --quiet "$service" 2>/dev/null +} + +# ------------------------------------------------------------------------------ +# Extract version from JSON (GitHub releases) +# ------------------------------------------------------------------------------ +extract_version_from_json() { + local json="$1" + local field="${2:-tag_name}" + local strip_v="${3:-true}" + + ensure_dependencies jq + + local version + version=$(echo "$json" | jq -r ".${field} // empty") + + if [[ -z "$version" ]]; then + return 1 fi - local CURRENT_VERSION="" - if command -v mariadb >/dev/null; then - CURRENT_VERSION=$(mariadb --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+') + if [[ "$strip_v" == "true" ]]; then + echo "${version#v}" + else + echo "$version" + fi +} + +# ------------------------------------------------------------------------------ +# Get latest GitHub release version +# ------------------------------------------------------------------------------ +get_latest_github_release() { + local repo="$1" + local strip_v="${2:-true}" + local temp_file=$(mktemp) + + if ! github_api_call "https://api.github.com/repos/${repo}/releases/latest" "$temp_file"; then + rm -f "$temp_file" + return 1 fi - if [[ "$CURRENT_VERSION" == "$MARIADB_VERSION" ]]; then - $STD msg_info "MariaDB $MARIADB_VERSION, upgrading" - $STD apt-get update - $STD apt-get install --only-upgrade -y mariadb-server mariadb-client - $STD msg_ok "MariaDB upgraded to $MARIADB_VERSION" + local version + version=$(extract_version_from_json "$(cat "$temp_file")" "tag_name" "$strip_v") + rm -f "$temp_file" + + if [[ -z "$version" ]]; then + return 1 + fi + + echo "$version" +} + +# ------------------------------------------------------------------------------ +# Debug logging (only if DEBUG=1) +# ------------------------------------------------------------------------------ +debug_log() { + [[ "${DEBUG:-0}" == "1" ]] && echo "[DEBUG] $*" >&2 +} + +# ------------------------------------------------------------------------------ +# Performance timing helper +# ------------------------------------------------------------------------------ +start_timer() { + echo $(date +%s) +} + +end_timer() { + local start_time="$1" + local label="${2:-Operation}" + local end_time=$(date +%s) + local duration=$((end_time - start_time)) + debug_log "$label took ${duration}s" +} + +# ------------------------------------------------------------------------------ +# GPG key fingerprint verification +# ------------------------------------------------------------------------------ +verify_gpg_fingerprint() { + local key_file="$1" + local expected_fingerprint="$2" + + local actual_fingerprint + actual_fingerprint=$(gpg --show-keys --with-fingerprint --with-colons "$key_file" 2>&1 | grep -m1 '^fpr:' | cut -d: -f10) + + if [[ "$actual_fingerprint" == "$expected_fingerprint" ]]; then return 0 fi - if [[ -n "$CURRENT_VERSION" ]]; then - $STD msg_info "Upgrading MariaDB $CURRENT_VERSION to $MARIADB_VERSION" - $STD systemctl stop mariadb >/dev/null 2>&1 || true - $STD apt-get purge -y 'mariadb*' || true - rm -f /etc/apt/sources.list.d/mariadb.list /etc/apt/trusted.gpg.d/mariadb.gpg - else - $STD msg_info "Setup MariaDB $MARIADB_VERSION" + msg_error "GPG fingerprint mismatch! Expected: $expected_fingerprint, Got: $actual_fingerprint" + return 1 +} + +# ============================================================================== +# EXISTING FUNCTIONS +# ============================================================================== + +# ------------------------------------------------------------------------------ +# Checks for new GitHub release (latest tag). +# +# Description: +# - Queries the GitHub API for the latest release tag +# - Compares it to a local cached version (~/.) +# - If newer, sets global CHECK_UPDATE_RELEASE and returns 0 +# +# Usage: +# if check_for_gh_release "flaresolverr" "FlareSolverr/FlareSolverr" [optional] "v1.1.1"; then +# # trigger update... +# fi +# exit 0 +# } (end of update_script not from the function) +# +# Notes: +# - Requires `jq` (auto-installed if missing) +# - Does not modify anything, only checks version state +# - Does not support pre-releases +# ------------------------------------------------------------------------------ +check_for_gh_release() { + local app="$1" + local source="$2" + local pinned_version_in="${3:-}" # optional + local app_lc="${app,,}" + local current_file="$HOME/.${app_lc}" + + msg_info "Checking for update: ${app}" + + # DNS check + if ! getent hosts api.github.com >/dev/null 2>&1; then + msg_error "Network error: cannot resolve api.github.com" + return 1 fi - curl -fsSL "https://mariadb.org/mariadb_release_signing_key.asc" | - gpg --dearmor -o /etc/apt/trusted.gpg.d/mariadb.gpg + ensure_dependencies jq - echo "deb [signed-by=/etc/apt/trusted.gpg.d/mariadb.gpg] http://mirror.mariadb.org/repo/${MARIADB_VERSION}/${CURRENT_OS} ${DISTRO_CODENAME} main" \ - >/etc/apt/sources.list.d/mariadb.list - - $STD apt-get update - - local MARIADB_MAJOR_MINOR - MARIADB_MAJOR_MINOR=$(echo "$MARIADB_VERSION" | awk -F. '{print $1"."$2}') - if [[ -n "$MARIADB_MAJOR_MINOR" ]]; then - echo "mariadb-server-$MARIADB_MAJOR_MINOR mariadb-server/feedback boolean false" | debconf-set-selections - else - for ver in 12.1 12.0 11.4 11.3 11.2 11.1 11.0 10.11 10.6 10.5 10.4 10.3; do - echo "mariadb-server-$ver mariadb-server/feedback boolean false" | debconf-set-selections - done - fi - DEBIAN_FRONTEND=noninteractive $STD apt-get install -y mariadb-server mariadb-client || { - msg_warn "Failed to install MariaDB ${MARIADB_VERSION} from upstream repo – trying distro package as fallback..." - # Cleanup, remove upstream repo to avoid conflicts - rm -f /etc/apt/sources.list.d/mariadb.list /etc/apt/trusted.gpg.d/mariadb.gpg - $STD apt-get update - # Final fallback: distro package - DEBIAN_FRONTEND=noninteractive $STD apt-get install -y mariadb-server mariadb-client || { - msg_error "MariaDB installation failed even with distro fallback!" - return 1 - } - msg_ok "Setup MariaDB (distro fallback)" - return 0 + # Fetch releases and exclude drafts/prereleases + local releases_json + releases_json=$(curl -fsSL --max-time 20 \ + -H 'Accept: application/vnd.github+json' \ + -H 'X-GitHub-Api-Version: 2022-11-28' \ + "https://api.github.com/repos/${source}/releases") || { + msg_error "Unable to fetch releases for ${app}" + return 1 } - msg_ok "Setup MariaDB $MARIADB_VERSION" -} - -# ------------------------------------------------------------------------------ -# Installs or upgrades MySQL and configures APT repo. -# -# Description: -# - Detects existing MySQL installation -# - Purges conflicting packages before installation -# - Supports clean upgrade -# -# Variables: -# MYSQL_VERSION - MySQL version to install (e.g. 5.7, 8.0) (default: 8.0) -# ------------------------------------------------------------------------------ - -function setup_mysql() { - local MYSQL_VERSION="${MYSQL_VERSION:-8.0}" - local CURRENT_VERSION="" - local NEED_INSTALL=false - CURRENT_OS="$(awk -F= '/^ID=/{print $2}' /etc/os-release)" - - if command -v mysql >/dev/null; then - CURRENT_VERSION="$(mysql --version | grep -oP 'Distrib\s+\K[0-9]+\.[0-9]+')" - if [[ "$CURRENT_VERSION" != "$MYSQL_VERSION" ]]; then - $STD msg_info "MySQL $CURRENT_VERSION will be upgraded to $MYSQL_VERSION" - NEED_INSTALL=true - else - # Check for patch-level updates - if apt list --upgradable 2>/dev/null | grep -q '^mysql-server/'; then - $STD msg_info "MySQL $CURRENT_VERSION available for upgrade" - $STD apt-get update - $STD apt-get install --only-upgrade -y mysql-server - $STD msg_ok "MySQL upgraded" - fi - return - fi - else - msg_info "Setup MySQL $MYSQL_VERSION" - NEED_INSTALL=true + mapfile -t raw_tags < <(jq -r '.[] | select(.draft==false and .prerelease==false) | .tag_name' <<<"$releases_json") + if ((${#raw_tags[@]} == 0)); then + msg_error "No stable releases found for ${app}" + return 1 fi - if [[ "$NEED_INSTALL" == true ]]; then - $STD systemctl stop mysql || true - $STD apt-get purge -y "^mysql-server.*" "^mysql-client.*" "^mysql-common.*" || true - rm -f /etc/apt/sources.list.d/mysql.list /etc/apt/trusted.gpg.d/mysql.gpg - - local DISTRO_CODENAME - DISTRO_CODENAME="$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release)" - curl -fsSL https://repo.mysql.com/RPM-GPG-KEY-mysql-2023 | gpg --dearmor -o /etc/apt/trusted.gpg.d/mysql.gpg - echo "deb [signed-by=/etc/apt/trusted.gpg.d/mysql.gpg] https://repo.mysql.com/apt/${CURRENT_OS}/ ${DISTRO_CODENAME} mysql-${MYSQL_VERSION}" \ - >/etc/apt/sources.list.d/mysql.list - - export DEBIAN_FRONTEND=noninteractive - $STD apt-get update - $STD apt-get install -y mysql-server - msg_ok "Setup MySQL $MYSQL_VERSION" - fi -} - -# ------------------------------------------------------------------------------ -# Installs PHP with selected modules and configures Apache/FPM support. -# -# Description: -# - Adds Sury PHP repo if needed -# - Installs default and user-defined modules -# - Patches php.ini for CLI, Apache, and FPM as needed -# -# Variables: -# PHP_VERSION - PHP version to install (default: 8.4) -# PHP_MODULE - Additional comma-separated modules -# PHP_APACHE - Set YES to enable PHP with Apache -# PHP_FPM - Set YES to enable PHP-FPM -# PHP_MEMORY_LIMIT - (default: 512M) -# PHP_UPLOAD_MAX_FILESIZE - (default: 128M) -# PHP_POST_MAX_SIZE - (default: 128M) -# PHP_MAX_EXECUTION_TIME - (default: 300) -# ------------------------------------------------------------------------------ - -function setup_php() { - local PHP_VERSION="${PHP_VERSION:-8.4}" - local PHP_MODULE="${PHP_MODULE:-}" - local PHP_APACHE="${PHP_APACHE:-NO}" - local PHP_FPM="${PHP_FPM:-NO}" - local DISTRO_CODENAME - DISTRO_CODENAME=$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release) - - local DEFAULT_MODULES="bcmath,cli,curl,gd,intl,mbstring,opcache,readline,xml,zip" - local COMBINED_MODULES - - local PHP_MEMORY_LIMIT="${PHP_MEMORY_LIMIT:-512M}" - local PHP_UPLOAD_MAX_FILESIZE="${PHP_UPLOAD_MAX_FILESIZE:-128M}" - local PHP_POST_MAX_SIZE="${PHP_POST_MAX_SIZE:-128M}" - local PHP_MAX_EXECUTION_TIME="${PHP_MAX_EXECUTION_TIME:-300}" - - # Merge default + user-defined modules - if [[ -n "$PHP_MODULE" ]]; then - COMBINED_MODULES="${DEFAULT_MODULES},${PHP_MODULE}" - else - COMBINED_MODULES="${DEFAULT_MODULES}" - fi - - # Deduplicate - COMBINED_MODULES=$(echo "$COMBINED_MODULES" | tr ',' '\n' | awk '!seen[$0]++' | paste -sd, -) - - # Get current PHP-CLI version - local CURRENT_PHP="" - if command -v php >/dev/null 2>&1; then - CURRENT_PHP=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2) - fi - - if [[ -z "$CURRENT_PHP" ]]; then - msg_info "Setup PHP $PHP_VERSION" - elif [[ "$CURRENT_PHP" != "$PHP_VERSION" ]]; then - msg_info "Old PHP $CURRENT_PHP detected, Setup new PHP $PHP_VERSION" - $STD apt-get purge -y "php${CURRENT_PHP//./}"* || true - fi - - # Ensure Sury repo is available - if [[ ! -f /etc/apt/sources.list.d/php.list ]]; then - $STD curl -fsSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb - $STD dpkg -i /tmp/debsuryorg-archive-keyring.deb - echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ ${DISTRO_CODENAME} main" \ - >/etc/apt/sources.list.d/php.list - $STD apt-get update - fi - - # Build module list - local MODULE_LIST="php${PHP_VERSION}" - IFS=',' read -ra MODULES <<<"$COMBINED_MODULES" - for mod in "${MODULES[@]}"; do - if apt-cache show "php${PHP_VERSION}-${mod}" >/dev/null 2>&1; then - MODULE_LIST+=" php${PHP_VERSION}-${mod}" - else - msg_warn "PHP-Module ${mod} for PHP ${PHP_VERSION} not found – skipping" - fi - done - if [[ "$PHP_FPM" == "YES" ]]; then - MODULE_LIST+=" php${PHP_VERSION}-fpm" - fi - - # install apache2 with PHP support if requested - if [[ "$PHP_APACHE" == "YES" ]]; then - if ! dpkg -l | grep -q "libapache2-mod-php${PHP_VERSION}"; then - msg_info "Installing Apache with PHP${PHP_VERSION} support" - $STD apt-get install -y apache2 libapache2-mod-php${PHP_VERSION} - else - msg_info "Apache with PHP${PHP_VERSION} already installed – skipping install" - fi - fi - - # setup / update PHP modules - $STD apt-get install -y $MODULE_LIST - msg_ok "Setup PHP $PHP_VERSION" - - # optional stop old PHP-FPM service - if [[ "$PHP_FPM" == "YES" && -n "$CURRENT_PHP" && "$CURRENT_PHP" != "$PHP_VERSION" ]]; then - $STD systemctl stop php"${CURRENT_PHP}"-fpm || true - $STD systemctl disable php"${CURRENT_PHP}"-fpm || true - fi - - # Patch all relevant php.ini files - local PHP_INI_PATHS=("/etc/php/${PHP_VERSION}/cli/php.ini") - [[ "$PHP_FPM" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/fpm/php.ini") - [[ "$PHP_APACHE" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/apache2/php.ini") - for ini in "${PHP_INI_PATHS[@]}"; do - if [[ -f "$ini" ]]; then - $STD msg_info "Patching $ini" - sed -i "s|^memory_limit = .*|memory_limit = ${PHP_MEMORY_LIMIT}|" "$ini" - sed -i "s|^upload_max_filesize = .*|upload_max_filesize = ${PHP_UPLOAD_MAX_FILESIZE}|" "$ini" - sed -i "s|^post_max_size = .*|post_max_size = ${PHP_POST_MAX_SIZE}|" "$ini" - sed -i "s|^max_execution_time = .*|max_execution_time = ${PHP_MAX_EXECUTION_TIME}|" "$ini" - $STD msg_ok "Patched $ini" - fi + local clean_tags=() + for t in "${raw_tags[@]}"; do + clean_tags+=("${t#v}") done - # patch Apache configuration if needed - if [[ "$PHP_APACHE" == "YES" ]]; then - for mod in $(ls /etc/apache2/mods-enabled/ 2>/dev/null | grep -E '^php[0-9]\.[0-9]\.conf$' | sed 's/\.conf//'); do - if [[ "$mod" != "php${PHP_VERSION}" ]]; then - $STD a2dismod "$mod" || true + local latest_raw="${raw_tags[0]}" + local latest_clean="${clean_tags[0]}" + + # current installed (stored without v) + local current="" + if [[ -f "$current_file" ]]; then + current="$(<"$current_file")" + else + # Migration: search for any /opt/*_version.txt + local legacy_files + mapfile -t legacy_files < <(find /opt -maxdepth 1 -type f -name "*_version.txt" 2>/dev/null) + if ((${#legacy_files[@]} == 1)); then + current="$(<"${legacy_files[0]}")" + echo "${current#v}" >"$current_file" + rm -f "${legacy_files[0]}" + fi + fi + current="${current#v}" + + # Pinned version handling + if [[ -n "$pinned_version_in" ]]; then + local pin_clean="${pinned_version_in#v}" + local match_raw="" + for i in "${!clean_tags[@]}"; do + if [[ "${clean_tags[$i]}" == "$pin_clean" ]]; then + match_raw="${raw_tags[$i]}" + break fi done - $STD a2enmod mpm_prefork - $STD a2enmod "php${PHP_VERSION}" - $STD systemctl restart apache2 || true - fi - # enable and restart PHP-FPM if requested - if [[ "$PHP_FPM" == "YES" ]]; then - if systemctl list-unit-files | grep -q "php${PHP_VERSION}-fpm.service"; then - $STD systemctl enable php${PHP_VERSION}-fpm - $STD systemctl restart php${PHP_VERSION}-fpm - else - msg_warn "FPM requested but service php${PHP_VERSION}-fpm not found" - fi - fi -} - -# ------------------------------------------------------------------------------ -# Installs or updates Composer globally. -# -# Description: -# - Downloads latest version from getcomposer.org -# - Installs to /usr/local/bin/composer -# ------------------------------------------------------------------------------ - -function setup_composer() { - local COMPOSER_BIN="/usr/local/bin/composer" - export COMPOSER_ALLOW_SUPERUSER=1 - - # Check if composer is already installed - if [[ -x "$COMPOSER_BIN" ]]; then - local CURRENT_VERSION - CURRENT_VERSION=$("$COMPOSER_BIN" --version | awk '{print $3}') - $STD msg_info "Old Composer $CURRENT_VERSION found, updating to latest" - else - msg_info "Setup Composer" - fi - - # Download and install latest composer - curl -fsSL https://getcomposer.org/installer -o /tmp/composer-setup.php - php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer >/dev/null 2>&1 - - if [[ $? -ne 0 ]]; then - msg_error "Failed to install Composer" - return 1 - fi - - chmod +x "$COMPOSER_BIN" - $STD composer diagnose - msg_ok "Setup Composer" -} - -# ------------------------------------------------------------------------------ -# Installs Go (Golang) from official tarball. -# -# Description: -# - Determines system architecture -# - Downloads latest version if GO_VERSION not set -# -# Variables: -# GO_VERSION - Version to install (e.g. 1.22.2 or latest) -# ------------------------------------------------------------------------------ - -function setup_go() { - local ARCH - case "$(uname -m)" in - x86_64) ARCH="amd64" ;; - aarch64) ARCH="arm64" ;; - *) - msg_error "Unsupported architecture: $(uname -m)" - return 1 - ;; - esac - - # Determine version - if [[ -z "${GO_VERSION:-}" || "${GO_VERSION}" == "latest" ]]; then - GO_VERSION=$(curl -fsSL https://go.dev/VERSION?m=text | head -n1 | sed 's/^go//') - if [[ -z "$GO_VERSION" ]]; then - msg_error "Could not determine latest Go version" + if [[ -z "$match_raw" ]]; then + msg_error "Pinned version ${pinned_version_in} not found upstream" return 1 fi - fi - local GO_BIN="/usr/local/bin/go" - local GO_INSTALL_DIR="/usr/local/go" - - if [[ -x "$GO_BIN" ]]; then - local CURRENT_VERSION - CURRENT_VERSION=$("$GO_BIN" version | awk '{print $3}' | sed 's/go//') - if [[ "$CURRENT_VERSION" == "$GO_VERSION" ]]; then + if [[ "$current" != "$pin_clean" ]]; then + msg_info "${app} pinned to ${pinned_version_in} (installed ${current:-none}) → update required" + CHECK_UPDATE_RELEASE="$match_raw" return 0 + fi + + if [[ "$pin_clean" == "$latest_clean" ]]; then + msg_ok "${app} pinned to ${pinned_version_in} (up to date)" else - $STD msg_info "Old Go Installation ($CURRENT_VERSION) found, upgrading to $GO_VERSION" - rm -rf "$GO_INSTALL_DIR" + msg_ok "${app} pinned to ${pinned_version_in} (already installed, upstream ${latest_raw})" fi - else - msg_info "Setup Go $GO_VERSION" - fi - - local TARBALL="go${GO_VERSION}.linux-${ARCH}.tar.gz" - local URL="https://go.dev/dl/${TARBALL}" - local TMP_TAR=$(mktemp) - - curl -fsSL "$URL" -o "$TMP_TAR" || { - msg_error "Failed to download $TARBALL" return 1 - } - - tar -C /usr/local -xzf "$TMP_TAR" - ln -sf /usr/local/go/bin/go /usr/local/bin/go - ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt - rm -f "$TMP_TAR" - - msg_ok "Setup Go $GO_VERSION" -} - -# ------------------------------------------------------------------------------ -# Installs Temurin JDK via Adoptium APT repository. -# -# Description: -# - Removes previous JDK if version mismatch -# - Installs or upgrades to specified JAVA_VERSION -# -# Variables: -# JAVA_VERSION - Temurin JDK version to install (e.g. 17, 21) -# ------------------------------------------------------------------------------ - -function setup_java() { - local JAVA_VERSION="${JAVA_VERSION:-21}" - local DISTRO_CODENAME - DISTRO_CODENAME=$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release) - local DESIRED_PACKAGE="temurin-${JAVA_VERSION}-jdk" - - # Add Adoptium repo if missing - if [[ ! -f /etc/apt/sources.list.d/adoptium.list ]]; then - $STD msg_info "Setting up Adoptium Repository" - mkdir -p /etc/apt/keyrings - curl -fsSL "https://packages.adoptium.net/artifactory/api/gpg/key/public" | gpg --dearmor -o /etc/apt/trusted.gpg.d/adoptium.gpg - echo "deb [signed-by=/etc/apt/trusted.gpg.d/adoptium.gpg] https://packages.adoptium.net/artifactory/deb ${DISTRO_CODENAME} main" \ - >/etc/apt/sources.list.d/adoptium.list - $STD apt-get update - $STD msg_ok "Set up Adoptium Repository" fi - # Detect currently installed temurin version - local INSTALLED_VERSION="" - if dpkg -l | grep -q "temurin-.*-jdk"; then - INSTALLED_VERSION=$(dpkg -l | awk '/temurin-.*-jdk/{print $2}' | grep -oP 'temurin-\K[0-9]+') - fi - - if [[ "$INSTALLED_VERSION" == "$JAVA_VERSION" ]]; then - $STD msg_info "Upgrading Temurin JDK $JAVA_VERSION" - $STD apt-get update - $STD apt-get install --only-upgrade -y "$DESIRED_PACKAGE" - $STD msg_ok "Upgraded Temurin JDK $JAVA_VERSION" - else - if [[ -n "$INSTALLED_VERSION" ]]; then - $STD msg_info "Removing Temurin JDK $INSTALLED_VERSION" - $STD apt-get purge -y "temurin-${INSTALLED_VERSION}-jdk" - fi - - msg_info "Setup Temurin JDK $JAVA_VERSION" - $STD apt-get install -y "$DESIRED_PACKAGE" - msg_ok "Setup Temurin JDK $JAVA_VERSION" - fi -} - -# ------------------------------------------------------------------------------ -# Installs or updates MongoDB to specified major version. -# -# Description: -# - Preserves data across installations -# - Adds official MongoDB repo -# -# Variables: -# MONGO_VERSION - MongoDB major version to install (e.g. 7.0, 8.0) -# ------------------------------------------------------------------------------ - -function setup_mongodb() { - local MONGO_VERSION="${MONGO_VERSION:-8.0}" - local DISTRO_ID DISTRO_CODENAME MONGO_BASE_URL - DISTRO_ID=$(awk -F= '/^ID=/{ gsub(/"/,"",$2); print $2 }' /etc/os-release) - DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{ print $2 }' /etc/os-release) - - # Check AVX support - if ! grep -qm1 'avx[^ ]*' /proc/cpuinfo; then - local major="${MONGO_VERSION%%.*}" - if ((major > 5)); then - msg_error "MongoDB ${MONGO_VERSION} requires AVX support, which is not available on this system." - return 1 - fi - fi - - case "$DISTRO_ID" in - ubuntu) - MONGO_BASE_URL="https://repo.mongodb.org/apt/ubuntu" - REPO_COMPONENT="multiverse" - ;; - debian) - MONGO_BASE_URL="https://repo.mongodb.org/apt/debian" - REPO_COMPONENT="main" - ;; - *) - msg_error "Unsupported distribution: $DISTRO_ID" - return 1 - ;; - esac - - local REPO_LIST="/etc/apt/sources.list.d/mongodb-org-${MONGO_VERSION}.list" - - local INSTALLED_VERSION="" - if command -v mongod >/dev/null; then - INSTALLED_VERSION=$(mongod --version | awk '/db version/{print $3}' | cut -d. -f1,2) - fi - - if [[ "$INSTALLED_VERSION" == "$MONGO_VERSION" ]]; then - $STD msg_info "Upgrading MongoDB $MONGO_VERSION" - $STD apt-get update - $STD apt-get install --only-upgrade -y mongodb-org - $STD msg_ok "Upgraded MongoDB $MONGO_VERSION" + # No pinning → use latest + if [[ -z "$current" || "$current" != "$latest_clean" ]]; then + CHECK_UPDATE_RELEASE="$latest_raw" + msg_info "New release available: ${latest_raw} (current: v${current:-none})" return 0 fi - if [[ -n "$INSTALLED_VERSION" ]]; then - $STD systemctl stop mongod || true - $STD apt-get purge -y mongodb-org || true - rm -f /etc/apt/sources.list.d/mongodb-org-*.list - rm -f /etc/apt/trusted.gpg.d/mongodb-*.gpg - else - msg_info "Setup MongoDB $MONGO_VERSION" + msg_ok "${app} is up to date (${latest_raw})" + return 1 +} + +# ------------------------------------------------------------------------------ +# Creates and installs self-signed certificates. +# +# Description: +# - Create a self-signed certificate with option to override application name +# +# Variables: +# APP - Application name (default: $APPLICATION variable) +# ------------------------------------------------------------------------------ +create_self_signed_cert() { + local APP_NAME="${1:-${APPLICATION}}" + local CERT_DIR="/etc/ssl/${APP_NAME}" + local CERT_KEY="${CERT_DIR}/${APP_NAME}.key" + local CERT_CRT="${CERT_DIR}/${APP_NAME}.crt" + + if [[ -f "$CERT_CRT" && -f "$CERT_KEY" ]]; then + return 0 fi - curl -fsSL "https://pgp.mongodb.com/server-${MONGO_VERSION}.asc" | gpg --dearmor -o "/etc/apt/trusted.gpg.d/mongodb-${MONGO_VERSION}.gpg" - echo "deb [signed-by=/etc/apt/trusted.gpg.d/mongodb-${MONGO_VERSION}.gpg] ${MONGO_BASE_URL} ${DISTRO_CODENAME}/mongodb-org/${MONGO_VERSION} main" \ - >"$REPO_LIST" + $STD apt update + $STD apt install -y openssl - $STD apt-get update || { - msg_error "APT update failed — invalid MongoDB repo for ${DISTRO_ID}-${DISTRO_CODENAME}?" - return 1 - } + mkdir -p "$CERT_DIR" + $STD openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 \ + -subj "/C=US/ST=State/L=City/O=Organization/CN=${APP_NAME}" \ + -keyout "$CERT_KEY" \ + -out "$CERT_CRT" - $STD apt-get install -y mongodb-org + chmod 600 "$CERT_KEY" + chmod 644 "$CERT_CRT" +} - mkdir -p /var/lib/mongodb - chown -R mongodb:mongodb /var/lib/mongodb +# ------------------------------------------------------------------------------ +# Downloads file with optional progress indicator using pv. +# +# Arguments: +# $1 - URL +# $2 - Destination path +# ------------------------------------------------------------------------------ - $STD systemctl enable mongod - $STD systemctl start mongod - msg_ok "Setup MongoDB $MONGO_VERSION" +function download_with_progress() { + local url="$1" + local output="$2" + if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi + + ensure_dependencies pv + set -o pipefail + + # Content-Length aus HTTP-Header holen + local content_length + content_length=$(curl -fsSLI "$url" | awk '/Content-Length/ {print $2}' | tr -d '\r' || true) + + if [[ -z "$content_length" ]]; then + if ! curl -fL# -o "$output" "$url"; then + msg_error "Download failed" + return 1 + fi + else + if ! curl -fsSL "$url" | pv -s "$content_length" >"$output"; then + msg_error "Download failed" + return 1 + fi + fi +} + +# ------------------------------------------------------------------------------ +# Ensures /usr/local/bin is permanently in system PATH. +# +# Description: +# - Adds to /etc/profile.d if not present +# ------------------------------------------------------------------------------ + +function ensure_usr_local_bin_persist() { + local PROFILE_FILE="/etc/profile.d/custom_path.sh" + + if [[ ! -f "$PROFILE_FILE" ]] && ! command -v pveversion &>/dev/null; then + echo 'export PATH="/usr/local/bin:$PATH"' >"$PROFILE_FILE" + chmod +x "$PROFILE_FILE" + fi } # ------------------------------------------------------------------------------ @@ -844,9 +997,7 @@ function fetch_and_deploy_gh_release() { local current_version="" [[ -f "$version_file" ]] && current_version=$(<"$version_file") - if ! command -v jq &>/dev/null; then - $STD apt-get install -y jq &>/dev/null - fi + ensure_dependencies jq local api_url="https://api.github.com/repos/$repo/releases" [[ "$version" != "latest" ]] && api_url="$api_url/tags/$version" || api_url="$api_url/latest" @@ -977,7 +1128,7 @@ function fetch_and_deploy_gh_release() { } chmod 644 "$tmpdir/$filename" - $STD apt-get install -y "$tmpdir/$filename" || { + $STD apt install -y "$tmpdir/$filename" || { $STD dpkg -i "$tmpdir/$filename" || { msg_error "Both apt and dpkg installation failed" rm -rf "$tmpdir" @@ -1027,9 +1178,7 @@ function fetch_and_deploy_gh_release() { fi if [[ "$filename" == *.zip ]]; then - if ! command -v unzip &>/dev/null; then - $STD apt-get install -y unzip - fi + ensure_dependencies unzip unzip -q "$tmpdir/$filename" -d "$unpack_tmp" elif [[ "$filename" == *.tar.* || "$filename" == *.tgz ]]; then tar -xf "$tmpdir/$filename" -C "$unpack_tmp" @@ -1131,6 +1280,606 @@ function fetch_and_deploy_gh_release() { rm -rf "$tmpdir" } +# ------------------------------------------------------------------------------ +# Loads LOCAL_IP from persistent store or detects if missing. +# +# Description: +# - Loads from /run/local-ip.env or performs runtime lookup +# ------------------------------------------------------------------------------ + +function import_local_ip() { + local IP_FILE="/run/local-ip.env" + if [[ -f "$IP_FILE" ]]; then + # shellcheck disable=SC1090 + source "$IP_FILE" + fi + + if [[ -z "${LOCAL_IP:-}" ]]; then + get_current_ip() { + local targets=("8.8.8.8" "1.1.1.1" "192.168.1.1" "10.0.0.1" "172.16.0.1" "default") + local ip + + for target in "${targets[@]}"; do + if [[ "$target" == "default" ]]; then + ip=$(ip route get 1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') + else + ip=$(ip route get "$target" 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') + fi + if [[ -n "$ip" ]]; then + echo "$ip" + return 0 + fi + done + + return 1 + } + + LOCAL_IP="$(get_current_ip || true)" + if [[ -z "$LOCAL_IP" ]]; then + msg_error "Could not determine LOCAL_IP" + return 1 + fi + fi + + export LOCAL_IP +} + +# ------------------------------------------------------------------------------ +# Installs Adminer (Debian/Ubuntu via APT, Alpine via direct download). +# +# Description: +# - Adds Adminer to Apache or web root +# - Supports Alpine and Debian-based systems +# ------------------------------------------------------------------------------ + +function setup_adminer() { + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "adminer") + + if grep -qi alpine /etc/os-release; then + msg_info "Installing Adminer (Alpine)" + mkdir -p /var/www/localhost/htdocs/adminer + curl -fsSL https://github.com/vrana/adminer/releases/latest/download/adminer.php \ + -o /var/www/localhost/htdocs/adminer/index.php || { + msg_error "Failed to download Adminer" + return 1 + } + cache_installed_version "adminer" "latest-alpine" + msg_ok "Installed Adminer (Alpine)" + else + msg_info "Installing Adminer (Debian/Ubuntu)" + ensure_dependencies adminer + $STD a2enconf adminer + $STD systemctl reload apache2 + local VERSION + VERSION=$(dpkg -s adminer 2>/dev/null | grep '^Version:' | awk '{print $2}') + cache_installed_version "adminer" "${VERSION:-unknown}" + msg_ok "Installed Adminer (Debian/Ubuntu)" + fi +} + +# ------------------------------------------------------------------------------ +# Installs or updates Composer globally (robust, idempotent). +# +# - Installs to /usr/local/bin/composer +# - Removes old binaries/symlinks in /usr/bin, /bin, /root/.composer, etc. +# - Ensures /usr/local/bin is in PATH (permanent) +# ------------------------------------------------------------------------------ + +function setup_composer() { + local COMPOSER_BIN="/usr/local/bin/composer" + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "composer") + export COMPOSER_ALLOW_SUPERUSER=1 + + for old in /usr/bin/composer /bin/composer /root/.composer/vendor/bin/composer; do + [[ -e "$old" && "$old" != "$COMPOSER_BIN" ]] && rm -f "$old" + done + + ensure_usr_local_bin_persist + export PATH="/usr/local/bin:$PATH" + + if [[ -x "$COMPOSER_BIN" ]]; then + local CURRENT_VERSION + CURRENT_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}') + msg_info "Updating Composer from $CURRENT_VERSION to latest" + else + msg_info "Installing Composer" + fi + + curl -fsSL https://getcomposer.org/installer -o /tmp/composer-setup.php || { + msg_error "Failed to download Composer installer" + return 1 + } + + $STD php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer + rm -f /tmp/composer-setup.php + + if [[ ! -x "$COMPOSER_BIN" ]]; then + msg_error "Composer installation failed" + return 1 + fi + + chmod +x "$COMPOSER_BIN" + $STD "$COMPOSER_BIN" self-update --no-interaction || true + + local FINAL_VERSION + FINAL_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}') + cache_installed_version "composer" "$FINAL_VERSION" + msg_ok "Installed Composer $FINAL_VERSION" +} + +# ------------------------------------------------------------------------------ +# Installs FFmpeg from source or prebuilt binary (Debian/Ubuntu only). +# +# Description: +# - Downloads and builds FFmpeg from GitHub (https://github.com/FFmpeg/FFmpeg) +# - Supports specific version override via FFMPEG_VERSION (e.g. n7.1.1) +# - Supports build profile via FFMPEG_TYPE: +# - minimal : x264, vpx, mp3 only +# - medium : adds subtitles, fonts, opus, vorbis +# - full : adds dav1d, svt-av1, zlib, numa +# - binary : downloads static build (johnvansickle.com) +# - Defaults to latest stable version and full feature set +# +# Notes: +# - Requires: curl, jq, build-essential, and matching codec libraries +# - Result is installed to /usr/local/bin/ffmpeg +# ------------------------------------------------------------------------------ + +function setup_ffmpeg() { + local TMP_DIR=$(mktemp -d) + local GITHUB_REPO="FFmpeg/FFmpeg" + local VERSION="${FFMPEG_VERSION:-latest}" + local TYPE="${FFMPEG_TYPE:-full}" + local BIN_PATH="/usr/local/bin/ffmpeg" + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "ffmpeg") + + # Binary fallback mode + if [[ "$TYPE" == "binary" ]]; then + msg_info "Installing FFmpeg (static binary)" + curl -fsSL https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz -o "$TMP_DIR/ffmpeg.tar.xz" || { + msg_error "Failed to download FFmpeg binary" + rm -rf "$TMP_DIR" + return 1 + } + tar -xf "$TMP_DIR/ffmpeg.tar.xz" -C "$TMP_DIR" + local EXTRACTED_DIR + EXTRACTED_DIR=$(find "$TMP_DIR" -maxdepth 1 -type d -name "ffmpeg-*") + cp "$EXTRACTED_DIR/ffmpeg" "$BIN_PATH" + cp "$EXTRACTED_DIR/ffprobe" /usr/local/bin/ffprobe + chmod +x "$BIN_PATH" /usr/local/bin/ffprobe + local FINAL_VERSION=$($BIN_PATH -version | head -n1 | awk '{print $3}') + rm -rf "$TMP_DIR" + cache_installed_version "ffmpeg" "$FINAL_VERSION" + ensure_usr_local_bin_persist + msg_ok "Installed FFmpeg binary $FINAL_VERSION" + return 0 + fi + + ensure_dependencies jq + + # Auto-detect latest stable version if none specified + if [[ "$VERSION" == "latest" || -z "$VERSION" ]]; then + VERSION=$(curl -fsSL "https://api.github.com/repos/${GITHUB_REPO}/tags" | + jq -r '.[].name' | + grep -E '^n[0-9]+\.[0-9]+\.[0-9]+$' | + sort -V | tail -n1) + fi + + if [[ -z "$VERSION" ]]; then + msg_error "Could not determine FFmpeg version" + rm -rf "$TMP_DIR" + return 1 + fi + + msg_info "Installing FFmpeg ${VERSION} ($TYPE)" + + # Dependency selection + local DEPS=(build-essential yasm nasm pkg-config) + case "$TYPE" in + minimal) + DEPS+=(libx264-dev libvpx-dev libmp3lame-dev) + ;; + medium) + DEPS+=(libx264-dev libvpx-dev libmp3lame-dev libfreetype6-dev libass-dev libopus-dev libvorbis-dev) + ;; + full) + DEPS+=( + libx264-dev libx265-dev libvpx-dev libmp3lame-dev + libfreetype6-dev libass-dev libopus-dev libvorbis-dev + libdav1d-dev libsvtav1-dev zlib1g-dev libnuma-dev + libva-dev libdrm-dev + ) + ;; + *) + msg_error "Invalid FFMPEG_TYPE: $TYPE" + rm -rf "$TMP_DIR" + return 1 + ;; + esac + + ensure_dependencies "${DEPS[@]}" + + curl -fsSL "https://github.com/${GITHUB_REPO}/archive/refs/tags/${VERSION}.tar.gz" -o "$TMP_DIR/ffmpeg.tar.gz" || { + msg_error "Failed to download FFmpeg source" + rm -rf "$TMP_DIR" + return 1 + } + + tar -xzf "$TMP_DIR/ffmpeg.tar.gz" -C "$TMP_DIR" || { + msg_error "Failed to extract FFmpeg" + rm -rf "$TMP_DIR" + return 1 + } + + cd "$TMP_DIR/FFmpeg-"* || { + msg_error "Source extraction failed" + rm -rf "$TMP_DIR" + return 1 + } + + local args=( + --enable-gpl + --enable-shared + --enable-nonfree + --disable-static + --enable-libx264 + --enable-libvpx + --enable-libmp3lame + ) + + if [[ "$TYPE" != "minimal" ]]; then + args+=(--enable-libfreetype --enable-libass --enable-libopus --enable-libvorbis) + fi + + if [[ "$TYPE" == "full" ]]; then + args+=(--enable-libx265 --enable-libdav1d --enable-zlib) + args+=(--enable-vaapi --enable-libdrm) + fi + + if [[ ${#args[@]} -eq 0 ]]; then + msg_error "FFmpeg configure args array is empty" + rm -rf "$TMP_DIR" + return 1 + fi + + $STD ./configure "${args[@]}" + $STD make -j"$(nproc)" + $STD make install + echo "/usr/local/lib" >/etc/ld.so.conf.d/ffmpeg.conf + $STD ldconfig + + ldconfig -p | grep libavdevice >/dev/null || { + msg_error "libavdevice not registered with dynamic linker" + rm -rf "$TMP_DIR" + return 1 + } + + if ! command -v ffmpeg &>/dev/null; then + msg_error "FFmpeg installation failed" + rm -rf "$TMP_DIR" + return 1 + fi + + local FINAL_VERSION + FINAL_VERSION=$(ffmpeg -version | head -n1 | awk '{print $3}') + rm -rf "$TMP_DIR" + cache_installed_version "ffmpeg" "$FINAL_VERSION" + ensure_usr_local_bin_persist + msg_ok "Installed FFmpeg $FINAL_VERSION" +} + +# ------------------------------------------------------------------------------ +# Installs Go (Golang) from official tarball. +# +# Description: +# - Determines system architecture +# - Downloads latest version if GO_VERSION not set +# +# Variables: +# GO_VERSION - Version to install (e.g. 1.22.2 or latest) +# ------------------------------------------------------------------------------ + +function setup_go() { + local ARCH + case "$(uname -m)" in + x86_64) ARCH="amd64" ;; + aarch64) ARCH="arm64" ;; + *) + msg_error "Unsupported architecture: $(uname -m)" + return 1 + ;; + esac + + # Determine version + if [[ -z "${GO_VERSION:-}" || "${GO_VERSION}" == "latest" ]]; then + GO_VERSION=$(curl -fsSL https://go.dev/VERSION?m=text | head -n1 | sed 's/^go//') + if [[ -z "$GO_VERSION" ]]; then + msg_error "Could not determine latest Go version" + return 1 + fi + fi + + local GO_BIN="/usr/local/bin/go" + local GO_INSTALL_DIR="/usr/local/go" + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "go") + + if [[ -x "$GO_BIN" ]]; then + local CURRENT_VERSION + CURRENT_VERSION=$("$GO_BIN" version | awk '{print $3}' | sed 's/go//') + if [[ "$CURRENT_VERSION" == "$GO_VERSION" ]]; then + if [[ "$CACHED_VERSION" == "$GO_VERSION" ]]; then + return 0 + fi + cache_installed_version "go" "$GO_VERSION" + return 0 + else + msg_info "Upgrading Go from $CURRENT_VERSION to $GO_VERSION" + rm -rf "$GO_INSTALL_DIR" + fi + else + msg_info "Installing Go $GO_VERSION" + fi + + local TARBALL="go${GO_VERSION}.linux-${ARCH}.tar.gz" + local URL="https://go.dev/dl/${TARBALL}" + local TMP_TAR=$(mktemp) + + curl -fsSL "$URL" -o "$TMP_TAR" || { + msg_error "Failed to download $TARBALL" + rm -f "$TMP_TAR" + return 1 + } + + $STD tar -C /usr/local -xzf "$TMP_TAR" + ln -sf /usr/local/go/bin/go /usr/local/bin/go + ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt + rm -f "$TMP_TAR" + + cache_installed_version "go" "$GO_VERSION" + ensure_usr_local_bin_persist + msg_ok "Installed Go $GO_VERSION" +} + +# ------------------------------------------------------------------------------ +# Installs or updates Ghostscript (gs) from source. +# +# Description: +# - Fetches latest release +# - Builds and installs system-wide +# ------------------------------------------------------------------------------ + +function setup_gs() { + local TMP_DIR=$(mktemp -d) + local CURRENT_VERSION=$(gs --version 2>/dev/null || echo "0") + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "ghostscript") + + ensure_dependencies jq + + local RELEASE_JSON + RELEASE_JSON=$(curl -fsSL https://api.github.com/repos/ArtifexSoftware/ghostpdl-downloads/releases/latest) + local LATEST_VERSION + LATEST_VERSION=$(echo "$RELEASE_JSON" | jq -r '.tag_name' | sed 's/^gs//') + local LATEST_VERSION_DOTTED + LATEST_VERSION_DOTTED=$(echo "$RELEASE_JSON" | jq -r '.name' | grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+') + + if [[ -z "$LATEST_VERSION" || -z "$LATEST_VERSION_DOTTED" ]]; then + msg_error "Could not determine latest Ghostscript version" + rm -rf "$TMP_DIR" + return 1 + fi + + if dpkg --compare-versions "$CURRENT_VERSION" ge "$LATEST_VERSION_DOTTED" 2>/dev/null; then + if [[ "$CACHED_VERSION" == "$LATEST_VERSION_DOTTED" ]]; then + rm -rf "$TMP_DIR" + return 0 + fi + cache_installed_version "ghostscript" "$LATEST_VERSION_DOTTED" + rm -rf "$TMP_DIR" + return 0 + fi + + msg_info "Installing Ghostscript $LATEST_VERSION_DOTTED" + + curl -fsSL "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs${LATEST_VERSION}/ghostscript-${LATEST_VERSION_DOTTED}.tar.gz" -o "$TMP_DIR/ghostscript.tar.gz" || { + msg_error "Failed to download Ghostscript" + rm -rf "$TMP_DIR" + return 1 + } + + if ! tar -xzf "$TMP_DIR/ghostscript.tar.gz" -C "$TMP_DIR"; then + msg_error "Failed to extract Ghostscript archive" + rm -rf "$TMP_DIR" + return 1 + fi + + cd "$TMP_DIR/ghostscript-${LATEST_VERSION_DOTTED}" || { + msg_error "Failed to enter Ghostscript source directory" + rm -rf "$TMP_DIR" + return 1 + } + + ensure_dependencies build-essential libpng-dev zlib1g-dev + + $STD ./configure + $STD make -j"$(nproc)" + $STD make install + + hash -r + if [[ ! -x "$(command -v gs)" ]]; then + if [[ -x /usr/local/bin/gs ]]; then + ln -sf /usr/local/bin/gs /usr/bin/gs + fi + fi + + rm -rf "$TMP_DIR" + cache_installed_version "ghostscript" "$LATEST_VERSION_DOTTED" + ensure_usr_local_bin_persist + msg_ok "Installed Ghostscript $LATEST_VERSION_DOTTED" +} + +# ------------------------------------------------------------------------------ +# Installs ImageMagick 7 from source (Debian/Ubuntu only). +# +# Description: +# - Downloads the latest ImageMagick source tarball +# - Builds and installs ImageMagick to /usr/local +# - Configures dynamic linker (ldconfig) +# +# Notes: +# - Requires: build-essential, libtool, libjpeg-dev, libpng-dev, etc. +# ------------------------------------------------------------------------------ +function setup_imagemagick() { + local TMP_DIR=$(mktemp -d) + local VERSION="" + local BINARY_PATH="/usr/local/bin/magick" + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "imagemagick") + + if command -v magick &>/dev/null; then + VERSION=$(magick -version | awk '/^Version/ {print $3}') + if [[ "$CACHED_VERSION" == "$VERSION" ]]; then + rm -rf "$TMP_DIR" + return 0 + fi + cache_installed_version "imagemagick" "$VERSION" + rm -rf "$TMP_DIR" + return 0 + fi + + msg_info "Installing ImageMagick (Patience)" + + ensure_dependencies \ + build-essential \ + libtool \ + libjpeg-dev \ + libpng-dev \ + libtiff-dev \ + libwebp-dev \ + libheif-dev \ + libde265-dev \ + libopenjp2-7-dev \ + libxml2-dev \ + liblcms2-dev \ + libfreetype6-dev \ + libraw-dev \ + libfftw3-dev \ + liblqr-1-0-dev \ + libgsl-dev \ + pkg-config \ + ghostscript + + curl -fsSL https://imagemagick.org/archive/ImageMagick.tar.gz -o "$TMP_DIR/ImageMagick.tar.gz" || { + msg_error "Failed to download ImageMagick" + rm -rf "$TMP_DIR" + return 1 + } + + tar -xzf "$TMP_DIR/ImageMagick.tar.gz" -C "$TMP_DIR" || { + msg_error "Failed to extract ImageMagick" + rm -rf "$TMP_DIR" + return 1 + } + + cd "$TMP_DIR"/ImageMagick-* || { + msg_error "Source extraction failed" + rm -rf "$TMP_DIR" + return 1 + } + + $STD ./configure --disable-static + $STD make -j"$(nproc)" + $STD make install + $STD ldconfig /usr/local/lib + + if [[ ! -x "$BINARY_PATH" ]]; then + msg_error "ImageMagick installation failed" + rm -rf "$TMP_DIR" + return 1 + fi + + VERSION=$("$BINARY_PATH" -version | awk '/^Version/ {print $3}') + rm -rf "$TMP_DIR" + cache_installed_version "imagemagick" "$VERSION" + ensure_usr_local_bin_persist + msg_ok "Installed ImageMagick $VERSION" +} + +# ------------------------------------------------------------------------------ +# Installs Temurin JDK via Adoptium APT repository. +# +# Description: +# - Removes previous JDK if version mismatch +# - Installs or upgrades to specified JAVA_VERSION +# +# Variables: +# JAVA_VERSION - Temurin JDK version to install (e.g. 17, 21) +# ------------------------------------------------------------------------------ + +function setup_java() { + local JAVA_VERSION="${JAVA_VERSION:-21}" + local DISTRO_ID DISTRO_CODENAME + DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + DISTRO_CODENAME=$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release) + local DESIRED_PACKAGE="temurin-${JAVA_VERSION}-jdk" + + # Check cached version + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "temurin-jdk") + + # Add repo nur wenn nötig + if [[ ! -f /etc/apt/sources.list.d/adoptium.sources ]]; then + # Cleanup old repository files + cleanup_old_repo_files "adoptium" + + # Use helper function to get fallback suite + local SUITE + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://packages.adoptium.net/artifactory/deb") + + # Use standardized repo setup + setup_deb822_repo \ + "adoptium" \ + "https://packages.adoptium.net/artifactory/api/gpg/key/public" \ + "https://packages.adoptium.net/artifactory/deb" \ + "$SUITE" \ + "main" \ + "amd64 arm64" + fi + + local INSTALLED_VERSION="" + if dpkg -l | grep -q "temurin-.*-jdk"; then + INSTALLED_VERSION=$(dpkg -l | awk '/temurin-.*-jdk/{print $2}' | grep -oP 'temurin-\K[0-9]+') + fi + + if [[ "$INSTALLED_VERSION" == "$JAVA_VERSION" ]]; then + if [[ "$CACHED_VERSION" == "$JAVA_VERSION" ]]; then + # Already at correct version, just upgrade if available + upgrade_package "$DESIRED_PACKAGE" + else + msg_info "Upgrading Temurin JDK $JAVA_VERSION" + $STD apt update + $STD apt install --only-upgrade -y "$DESIRED_PACKAGE" + cache_installed_version "temurin-jdk" "$JAVA_VERSION" + msg_ok "Upgraded Temurin JDK $JAVA_VERSION" + fi + else + if [[ -n "$INSTALLED_VERSION" ]]; then + msg_info "Removing old Temurin JDK $INSTALLED_VERSION" + $STD apt purge -y "temurin-${INSTALLED_VERSION}-jdk" + msg_ok "Removed old Temurin JDK" + fi + + msg_info "Installing Temurin JDK $JAVA_VERSION" + $STD apt install -y "$DESIRED_PACKAGE" + cache_installed_version "temurin-jdk" "$JAVA_VERSION" + msg_ok "Installed Temurin JDK $JAVA_VERSION" + fi +} + # ------------------------------------------------------------------------------ # Installs a local IP updater script using networkd-dispatcher. # @@ -1149,8 +1898,8 @@ function setup_local_ip_helper() { # Install networkd-dispatcher if not present if ! dpkg -s networkd-dispatcher >/dev/null 2>&1; then - $STD apt-get update - $STD apt-get install -y networkd-dispatcher + $STD apt update + $STD apt install -y networkd-dispatcher fi # Write update_local_ip.sh @@ -1210,81 +1959,895 @@ EOF } # ------------------------------------------------------------------------------ -# Loads LOCAL_IP from persistent store or detects if missing. +# Installs or updates MariaDB from official repo. # # Description: -# - Loads from /run/local-ip.env or performs runtime lookup +# - Detects current MariaDB version and replaces it if necessary +# - Preserves existing database data +# - Dynamically determines latest GA version if "latest" is given +# +# Variables: +# MARIADB_VERSION - MariaDB version to install (e.g. 10.11, latest) (default: latest) # ------------------------------------------------------------------------------ -function import_local_ip() { - local IP_FILE="/run/local-ip.env" - if [[ -f "$IP_FILE" ]]; then - # shellcheck disable=SC1090 - source "$IP_FILE" +setup_mariadb() { + local MARIADB_VERSION="${MARIADB_VERSION:-latest}" + local DISTRO_ID DISTRO_CODENAME + DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + + if ! curl -fsI http://mirror.mariadb.org/repo/ >/dev/null; then + msg_error "MariaDB mirror not reachable" + return 1 fi - if [[ -z "${LOCAL_IP:-}" ]]; then - get_current_ip() { - local targets=("8.8.8.8" "1.1.1.1" "192.168.1.1" "10.0.0.1" "172.16.0.1" "default") - local ip - - for target in "${targets[@]}"; do - if [[ "$target" == "default" ]]; then - ip=$(ip route get 1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') - else - ip=$(ip route get "$target" 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') - fi - if [[ -n "$ip" ]]; then - echo "$ip" - return 0 - fi - done - - return 1 - } - - LOCAL_IP="$(get_current_ip || true)" - if [[ -z "$LOCAL_IP" ]]; then - msg_error "Could not determine LOCAL_IP" + if [[ "$MARIADB_VERSION" == "latest" ]]; then + MARIADB_VERSION=$(curl -fsSL http://mirror.mariadb.org/repo/ | + grep -Eo '[0-9]+\.[0-9]+\.[0-9]+/' | + grep -vE 'rc/|rolling/' | + sed 's|/||' | + sort -Vr | + head -n1) + if [[ -z "$MARIADB_VERSION" ]]; then + msg_error "Could not determine latest GA MariaDB version" return 1 fi fi - export LOCAL_IP + local CURRENT_VERSION="" + if command -v mariadb >/dev/null; then + CURRENT_VERSION=$(mariadb --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+') + fi + + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "mariadb") + + if [[ "$CURRENT_VERSION" == "$MARIADB_VERSION" ]]; then + if [[ "$CACHED_VERSION" == "$MARIADB_VERSION" ]]; then + upgrade_package mariadb-server + upgrade_package mariadb-client + else + msg_info "Upgrading MariaDB $MARIADB_VERSION" + $STD apt update + $STD apt install --only-upgrade -y mariadb-server mariadb-client + cache_installed_version "mariadb" "$MARIADB_VERSION" + msg_ok "Upgraded MariaDB $MARIADB_VERSION" + fi + return 0 + fi + + msg_info "Installing MariaDB $MARIADB_VERSION" + + if [[ -n "$CURRENT_VERSION" ]]; then + $STD systemctl stop mariadb >/dev/null 2>&1 || true + $STD apt purge -y 'mariadb*' || true + fi + + # Cleanup old repository files + cleanup_old_repo_files "mariadb" + + # Use helper function to get fallback suite + local SUITE + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "http://mirror.mariadb.org/repo/${MARIADB_VERSION}/${DISTRO_ID}") + + # Use standardized repo setup + setup_deb822_repo \ + "mariadb" \ + "https://mariadb.org/mariadb_release_signing_key.asc" \ + "http://mirror.mariadb.org/repo/${MARIADB_VERSION}/${DISTRO_ID}" \ + "$SUITE" \ + "main" \ + "amd64 arm64" + + local MARIADB_MAJOR_MINOR + MARIADB_MAJOR_MINOR=$(echo "$MARIADB_VERSION" | awk -F. '{print $1"."$2}') + if [[ -n "$MARIADB_MAJOR_MINOR" ]]; then + echo "mariadb-server-$MARIADB_MAJOR_MINOR mariadb-server/feedback boolean false" | debconf-set-selections + fi + + DEBIAN_FRONTEND=noninteractive $STD apt install -y mariadb-server mariadb-client || { + cleanup_old_repo_files "mariadb" + $STD apt update + DEBIAN_FRONTEND=noninteractive $STD apt install -y mariadb-server mariadb-client + } + + cache_installed_version "mariadb" "$MARIADB_VERSION" + msg_ok "Installed MariaDB $MARIADB_VERSION" } # ------------------------------------------------------------------------------ -# Downloads file with optional progress indicator using pv. +# Installs or updates MongoDB to specified major version. # -# Arguments: -# $1 - URL -# $2 - Destination path +# Description: +# - Preserves data across installations +# - Adds official MongoDB repo +# +# Variables: +# MONGO_VERSION - MongoDB major version to install (e.g. 7.0, 8.0) # ------------------------------------------------------------------------------ -function download_with_progress() { - local url="$1" - local output="$2" - if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi +function setup_mongodb() { + local MONGO_VERSION="${MONGO_VERSION:-8.0}" + local DISTRO_ID DISTRO_CODENAME + DISTRO_ID=$(awk -F= '/^ID=/{ gsub(/"/,"",$2); print $2 }' /etc/os-release) + DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{ print $2 }' /etc/os-release) - if ! command -v pv &>/dev/null; then - $STD apt-get install -y pv - fi - set -o pipefail - - # Content-Length aus HTTP-Header holen - local content_length - content_length=$(curl -fsSLI "$url" | awk '/Content-Length/ {print $2}' | tr -d '\r' || true) - - if [[ -z "$content_length" ]]; then - if ! curl -fL# -o "$output" "$url"; then - msg_error "Download failed" + # Check AVX support + if ! grep -qm1 'avx[^ ]*' /proc/cpuinfo; then + local major="${MONGO_VERSION%%.*}" + if ((major > 5)); then + msg_error "MongoDB ${MONGO_VERSION} requires AVX support, which is not available on this system." return 1 fi + fi + + case "$DISTRO_ID" in + ubuntu) + MONGO_BASE_URL="https://repo.mongodb.org/apt/ubuntu" + REPO_COMPONENT="multiverse" + ;; + debian) + MONGO_BASE_URL="https://repo.mongodb.org/apt/debian" + REPO_COMPONENT="main" + ;; + *) + msg_error "Unsupported distribution: $DISTRO_ID" + return 1 + ;; + esac + + local INSTALLED_VERSION="" + if command -v mongod >/dev/null; then + INSTALLED_VERSION=$(mongod --version | awk '/db version/{print $3}' | cut -d. -f1,2) + fi + + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "mongodb") + + if [[ "$INSTALLED_VERSION" == "$MONGO_VERSION" ]]; then + if [[ "$CACHED_VERSION" == "$MONGO_VERSION" ]]; then + upgrade_package mongodb-org + else + msg_info "Upgrading MongoDB $MONGO_VERSION" + $STD apt update + $STD apt install --only-upgrade -y mongodb-org + cache_installed_version "mongodb" "$MONGO_VERSION" + msg_ok "Upgraded MongoDB $MONGO_VERSION" + fi + return 0 + fi + + msg_info "Installing MongoDB $MONGO_VERSION" + + if [[ -n "$INSTALLED_VERSION" ]]; then + $STD systemctl stop mongod || true + $STD apt purge -y mongodb-org || true + fi + + # Cleanup old repository files + cleanup_old_repo_files "mongodb-org-${MONGO_VERSION}" + + # Use helper function to get fallback suite + local SUITE + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "$MONGO_BASE_URL") + + # Use standardized repo setup + mkdir -p /etc/apt/keyrings + curl -fsSL "https://pgp.mongodb.com/server-${MONGO_VERSION}.asc" | gpg --dearmor -o "/etc/apt/keyrings/mongodb-${MONGO_VERSION}.gpg" + + cat </etc/apt/sources.list.d/mongodb-org-${MONGO_VERSION}.sources +Types: deb +URIs: ${MONGO_BASE_URL} +Suites: ${SUITE}/mongodb-org/${MONGO_VERSION} +Components: ${REPO_COMPONENT} +Architectures: amd64 arm64 +Signed-By: /etc/apt/keyrings/mongodb-${MONGO_VERSION}.gpg +EOF + + $STD apt update || { + msg_error "APT update failed — invalid MongoDB repo for ${DISTRO_ID}-${DISTRO_CODENAME}?" + return 1 + } + + $STD apt install -y mongodb-org + + mkdir -p /var/lib/mongodb + chown -R mongodb:mongodb /var/lib/mongodb + + $STD systemctl enable mongod + safe_service_restart mongod + cache_installed_version "mongodb" "$MONGO_VERSION" + msg_ok "Installed MongoDB $MONGO_VERSION" +} + +# ------------------------------------------------------------------------------ +# Installs or upgrades MySQL and configures APT repo. +# +# Description: +# - Detects existing MySQL installation +# - Purges conflicting packages before installation +# - Supports clean upgrade +# +# Variables: +# MYSQL_VERSION - MySQL version to install (e.g. 5.7, 8.0) (default: 8.0) +# ------------------------------------------------------------------------------ + +function setup_mysql() { + local MYSQL_VERSION="${MYSQL_VERSION:-8.0}" + local CURRENT_VERSION="" + local NEED_INSTALL=false + local DISTRO_ID DISTRO_CODENAME + DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + + if command -v mysql >/dev/null; then + CURRENT_VERSION="$(mysql --version | grep -oP '[0-9]+\.[0-9]+' | head -n1)" + if [[ "$CURRENT_VERSION" != "$MYSQL_VERSION" ]]; then + msg_info "MySQL $CURRENT_VERSION will be upgraded to $MYSQL_VERSION" + NEED_INSTALL=true + else + if apt list --upgradable 2>/dev/null | grep -q '^mysql-server/'; then + msg_info "MySQL $CURRENT_VERSION available for upgrade" + $STD apt update + $STD apt install --only-upgrade -y mysql-server + msg_ok "MySQL upgraded" + fi + return + fi else - if ! curl -fsSL "$url" | pv -s "$content_length" >"$output"; then - msg_error "Download failed" + msg_info "Setup MySQL $MYSQL_VERSION" + NEED_INSTALL=true + fi + + if [[ "$NEED_INSTALL" == true ]]; then + $STD systemctl stop mysql || true + $STD apt purge -y "^mysql-server.*" "^mysql-client.*" "^mysql-common.*" || true + + # Cleanup old repository files + cleanup_old_repo_files "mysql" + + # Use helper function to get fallback suite + local SUITE + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://repo.mysql.com/apt/${DISTRO_ID}") + + # Use standardized repo setup + setup_deb822_repo \ + "mysql" \ + "https://repo.mysql.com/RPM-GPG-KEY-mysql-2023" \ + "https://repo.mysql.com/apt/${DISTRO_ID}" \ + "$SUITE" \ + "mysql-${MYSQL_VERSION}" \ + "amd64 arm64" + + export DEBIAN_FRONTEND=noninteractive + $STD apt install -y mysql-server + cache_installed_version "mysql" "$MYSQL_VERSION" + msg_ok "Installed MySQL $MYSQL_VERSION" + fi +} + +# ------------------------------------------------------------------------------ +# Installs Node.js and optional global modules. +# +# Description: +# - Installs specified Node.js version using NodeSource APT repo +# - Optionally installs or updates global npm modules +# +# Variables: +# NODE_VERSION - Node.js version to install (default: 22) +# NODE_MODULE - Comma-separated list of global modules (e.g. "yarn,@vue/cli@5.0.0") +# ------------------------------------------------------------------------------ + +function setup_nodejs() { + local NODE_VERSION="${NODE_VERSION:-22}" + local NODE_MODULE="${NODE_MODULE:-}" + local CURRENT_NODE_VERSION="" + local NEED_NODE_INSTALL=false + local DISTRO_ID DISTRO_CODENAME + DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + + if command -v node >/dev/null; then + CURRENT_NODE_VERSION="$(node -v | grep -oP '^v\K[0-9]+')" + if [[ "$CURRENT_NODE_VERSION" != "$NODE_VERSION" ]]; then + msg_info "Old Node.js $CURRENT_NODE_VERSION found, replacing with $NODE_VERSION" + NEED_NODE_INSTALL=true + fi + else + msg_info "Setup Node.js $NODE_VERSION" + NEED_NODE_INSTALL=true + fi + + ensure_dependencies jq + + if [[ "$NEED_NODE_INSTALL" == true ]]; then + $STD apt purge -y nodejs + + # Cleanup old repository files + cleanup_old_repo_files "nodesource" + + # NodeSource uses 'nodistro' for all distributions - no fallback needed + # Use standardized repo setup + setup_deb822_repo \ + "nodesource" \ + "https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key" \ + "https://deb.nodesource.com/node_${NODE_VERSION}.x" \ + "nodistro" \ + "main" \ + "amd64 arm64" + + if ! $STD apt install -y nodejs; then + msg_error "Failed to install Node.js ${NODE_VERSION} from NodeSource" return 1 fi + + $STD npm install -g npm@latest || { + msg_warn "Failed to update npm to latest version" + } + + cache_installed_version "nodejs" "$NODE_VERSION" + msg_ok "Installed Node.js ${NODE_VERSION}" + fi + + export NODE_OPTIONS="--max-old-space-size=4096" + + if [[ ! -d /opt ]]; then + mkdir -p /opt + fi + cd /opt || { + msg_error "Failed to set safe working directory before npm install" + return 1 + } + + if [[ -n "$NODE_MODULE" ]]; then + IFS=',' read -ra MODULES <<<"$NODE_MODULE" + for mod in "${MODULES[@]}"; do + local MODULE_NAME MODULE_REQ_VERSION MODULE_INSTALLED_VERSION + if [[ "$mod" == @*/*@* ]]; then + MODULE_NAME="${mod%@*}" + MODULE_REQ_VERSION="${mod##*@}" + elif [[ "$mod" == *"@"* ]]; then + MODULE_NAME="${mod%@*}" + MODULE_REQ_VERSION="${mod##*@}" + else + MODULE_NAME="$mod" + MODULE_REQ_VERSION="latest" + fi + + if npm list -g --depth=0 "$MODULE_NAME" >/dev/null 2>&1; then + MODULE_INSTALLED_VERSION="$(npm list -g --depth=0 "$MODULE_NAME" | grep "$MODULE_NAME@" | awk -F@ '{print $2}' | tr -d '[:space:]')" + if [[ "$MODULE_REQ_VERSION" != "latest" && "$MODULE_REQ_VERSION" != "$MODULE_INSTALLED_VERSION" ]]; then + msg_info "Updating $MODULE_NAME from v$MODULE_INSTALLED_VERSION to v$MODULE_REQ_VERSION" + if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}"; then + msg_error "Failed to update $MODULE_NAME to version $MODULE_REQ_VERSION" + return 1 + fi + elif [[ "$MODULE_REQ_VERSION" == "latest" ]]; then + msg_info "Updating $MODULE_NAME to latest version" + if ! $STD npm install -g "${MODULE_NAME}@latest"; then + msg_error "Failed to update $MODULE_NAME to latest version" + return 1 + fi + fi + else + msg_info "Installing $MODULE_NAME@$MODULE_REQ_VERSION" + if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}"; then + msg_error "Failed to install $MODULE_NAME@$MODULE_REQ_VERSION" + return 1 + fi + fi + done + msg_ok "Installed Node.js modules: $NODE_MODULE" + fi +} + +# ------------------------------------------------------------------------------ +# Installs PHP with selected modules and configures Apache/FPM support. +# +# Description: +# - Adds Sury PHP repo if needed +# - Installs default and user-defined modules +# - Patches php.ini for CLI, Apache, and FPM as needed +# +# Variables: +# PHP_VERSION - PHP version to install (default: 8.4) +# PHP_MODULE - Additional comma-separated modules +# PHP_APACHE - Set YES to enable PHP with Apache +# PHP_FPM - Set YES to enable PHP-FPM +# PHP_MEMORY_LIMIT - (default: 512M) +# PHP_UPLOAD_MAX_FILESIZE - (default: 128M) +# PHP_POST_MAX_SIZE - (default: 128M) +# PHP_MAX_EXECUTION_TIME - (default: 300) +# ------------------------------------------------------------------------------ + +function setup_php() { + local PHP_VERSION="${PHP_VERSION:-8.4}" + local PHP_MODULE="${PHP_MODULE:-}" + local PHP_APACHE="${PHP_APACHE:-NO}" + local PHP_FPM="${PHP_FPM:-NO}" + local DISTRO_ID DISTRO_CODENAME + DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + + local DEFAULT_MODULES="bcmath,cli,curl,gd,intl,mbstring,opcache,readline,xml,zip" + local COMBINED_MODULES + + local PHP_MEMORY_LIMIT="${PHP_MEMORY_LIMIT:-512M}" + local PHP_UPLOAD_MAX_FILESIZE="${PHP_UPLOAD_MAX_FILESIZE:-128M}" + local PHP_POST_MAX_SIZE="${PHP_POST_MAX_SIZE:-128M}" + local PHP_MAX_EXECUTION_TIME="${PHP_MAX_EXECUTION_TIME:-300}" + + # Merge default + user-defined modules + if [[ -n "$PHP_MODULE" ]]; then + COMBINED_MODULES="${DEFAULT_MODULES},${PHP_MODULE}" + else + COMBINED_MODULES="${DEFAULT_MODULES}" + fi + + # Deduplicate + COMBINED_MODULES=$(echo "$COMBINED_MODULES" | tr ',' '\n' | awk '!seen[$0]++' | paste -sd, -) + + # Get current PHP-CLI version + local CURRENT_PHP="" + if command -v php >/dev/null 2>&1; then + CURRENT_PHP=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2) + fi + + if [[ -z "$CURRENT_PHP" ]]; then + msg_info "Setup PHP $PHP_VERSION" + elif [[ "$CURRENT_PHP" != "$PHP_VERSION" ]]; then + msg_info "Old PHP $CURRENT_PHP detected, Setup new PHP $PHP_VERSION" + $STD apt purge -y "php${CURRENT_PHP//./}"* || true + fi + + # Ensure Sury repo is available + if [[ ! -f /etc/apt/sources.list.d/php.sources ]]; then + # Cleanup old repository files + cleanup_old_repo_files "php" + + $STD curl -fsSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb + $STD dpkg -i /tmp/debsuryorg-archive-keyring.deb + + # Use helper function to get fallback suite + local SUITE + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://packages.sury.org/php") + + cat </etc/apt/sources.list.d/php.sources +Types: deb +URIs: https://packages.sury.org/php/ +Suites: $SUITE +Components: main +Architectures: amd64 arm64 +Signed-By: /usr/share/keyrings/deb.sury.org-php.gpg +EOF + $STD apt update + fi + + # Build module list + local MODULE_LIST="php${PHP_VERSION}" + IFS=',' read -ra MODULES <<<"$COMBINED_MODULES" + for mod in "${MODULES[@]}"; do + if apt-cache show "php${PHP_VERSION}-${mod}" >/dev/null 2>&1; then + MODULE_LIST+=" php${PHP_VERSION}-${mod}" + else + msg_warn "PHP-Module ${mod} for PHP ${PHP_VERSION} not found – skipping" + fi + done + if [[ "$PHP_FPM" == "YES" ]]; then + MODULE_LIST+=" php${PHP_VERSION}-fpm" + fi + + # install apache2 with PHP support if requested + if [[ "$PHP_APACHE" == "YES" ]]; then + if ! dpkg -l | grep -q "libapache2-mod-php${PHP_VERSION}"; then + msg_info "Installing Apache with PHP${PHP_VERSION} support" + $STD apt install -y apache2 libapache2-mod-php${PHP_VERSION} + else + msg_info "Apache with PHP${PHP_VERSION} already installed – skipping install" + fi + fi + + # setup / update PHP modules + $STD apt install -y $MODULE_LIST + cache_installed_version "php" "$PHP_VERSION" + msg_ok "Installed PHP $PHP_VERSION" + + # optional stop old PHP-FPM service + if [[ "$PHP_FPM" == "YES" && -n "$CURRENT_PHP" && "$CURRENT_PHP" != "$PHP_VERSION" ]]; then + $STD systemctl stop php"${CURRENT_PHP}"-fpm || true + $STD systemctl disable php"${CURRENT_PHP}"-fpm || true + fi + + # Patch all relevant php.ini files (silent) + local PHP_INI_PATHS=("/etc/php/${PHP_VERSION}/cli/php.ini") + [[ "$PHP_FPM" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/fpm/php.ini") + [[ "$PHP_APACHE" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/apache2/php.ini") + for ini in "${PHP_INI_PATHS[@]}"; do + if [[ -f "$ini" ]]; then + $STD sed -i "s|^memory_limit = .*|memory_limit = ${PHP_MEMORY_LIMIT}|" "$ini" + $STD sed -i "s|^upload_max_filesize = .*|upload_max_filesize = ${PHP_UPLOAD_MAX_FILESIZE}|" "$ini" + $STD sed -i "s|^post_max_size = .*|post_max_size = ${PHP_POST_MAX_SIZE}|" "$ini" + $STD sed -i "s|^max_execution_time = .*|max_execution_time = ${PHP_MAX_EXECUTION_TIME}|" "$ini" + fi + done + + # patch Apache configuration if needed + if [[ "$PHP_APACHE" == "YES" ]]; then + for mod in $(ls /etc/apache2/mods-enabled/ 2>/dev/null | grep -E '^php[0-9]\.[0-9]\.conf$' | sed 's/\.conf//'); do + if [[ "$mod" != "php${PHP_VERSION}" ]]; then + $STD a2dismod "$mod" || true + fi + done + $STD a2enmod mpm_prefork + $STD a2enmod "php${PHP_VERSION}" + safe_service_restart apache2 || true + fi + + # enable and restart PHP-FPM if requested + if [[ "$PHP_FPM" == "YES" ]]; then + if systemctl list-unit-files | grep -q "php${PHP_VERSION}-fpm.service"; then + $STD systemctl enable php${PHP_VERSION}-fpm + safe_service_restart php${PHP_VERSION}-fpm + else + msg_warn "FPM requested but service php${PHP_VERSION}-fpm not found" + fi + fi +} + +# ------------------------------------------------------------------------------ +# Installs or upgrades PostgreSQL and optional extensions/modules. +# +# Description: +# - Detects existing PostgreSQL version +# - Dumps all databases before upgrade +# - Adds PGDG repo and installs specified version +# - Installs optional PG_MODULES (e.g. postgis, contrib) +# - Restores dumped data post-upgrade +# +# Variables: +# PG_VERSION - Major PostgreSQL version (e.g. 15, 16) (default: 16) +# PG_MODULES - Comma-separated list of extensions (e.g. "postgis,contrib") +# ------------------------------------------------------------------------------ +function setup_postgresql() { + local PG_VERSION="${PG_VERSION:-16}" + local PG_MODULES="${PG_MODULES:-}" + local CURRENT_PG_VERSION="" + local DISTRO_ID DISTRO_CODENAME + local NEED_PG_INSTALL=false + DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + + if command -v psql >/dev/null; then + CURRENT_PG_VERSION="$(psql -V | awk '{print $3}' | cut -d. -f1)" + [[ "$CURRENT_PG_VERSION" != "$PG_VERSION" ]] && NEED_PG_INSTALL=true + else + NEED_PG_INSTALL=true + fi + + if [[ "$NEED_PG_INSTALL" == true ]]; then + msg_info "Installing PostgreSQL $PG_VERSION" + + if [[ -n "$CURRENT_PG_VERSION" ]]; then + $STD su - postgres -c "pg_dumpall > /var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql" + $STD systemctl stop postgresql + fi + + # Cleanup old repository files + cleanup_old_repo_files "pgdg" + + # Use helper function to get fallback suite + local SUITE + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://apt.postgresql.org/pub/repos/apt") + + # PGDG uses special suite naming: ${SUITE}-pgdg + SUITE="${SUITE}-pgdg" + + # Use standardized repo setup + setup_deb822_repo \ + "pgdg" \ + "https://www.postgresql.org/media/keys/ACCC4CF8.asc" \ + "https://apt.postgresql.org/pub/repos/apt" \ + "$SUITE" \ + "main" \ + "amd64 arm64" + + $STD apt install -y "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}" + + if [[ -n "$CURRENT_PG_VERSION" ]]; then + $STD apt purge -y "postgresql-${CURRENT_PG_VERSION}" "postgresql-client-${CURRENT_PG_VERSION}" || true + $STD su - postgres -c "psql < /var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql" + fi + + $STD systemctl enable --now postgresql + cache_installed_version "postgresql" "$PG_VERSION" + msg_ok "Installed PostgreSQL $PG_VERSION" + fi + + if [[ -n "$PG_MODULES" ]]; then + msg_info "Installing PostgreSQL modules" + IFS=',' read -ra MODULES <<<"$PG_MODULES" + for module in "${MODULES[@]}"; do + $STD apt install -y "postgresql-${PG_VERSION}-${module}" || { + msg_warn "Failed to install postgresql-${PG_VERSION}-${module}" + continue + } + done + msg_ok "Installed PostgreSQL modules" + fi +} + +# ------------------------------------------------------------------------------ +# Installs rbenv and ruby-build, installs Ruby and optionally Rails. +# +# Description: +# - Downloads rbenv and ruby-build from GitHub +# - Compiles and installs target Ruby version +# - Optionally installs Rails via gem +# +# Variables: +# RUBY_VERSION - Ruby version to install (default: 3.4.4) +# RUBY_INSTALL_RAILS - true/false to install Rails (default: true) +# ------------------------------------------------------------------------------ + +function setup_ruby() { + local RUBY_VERSION="${RUBY_VERSION:-3.4.4}" + local RUBY_INSTALL_RAILS="${RUBY_INSTALL_RAILS:-true}" + local RBENV_DIR="$HOME/.rbenv" + local RBENV_BIN="$RBENV_DIR/bin/rbenv" + local PROFILE_FILE="$HOME/.profile" + local TMP_DIR=$(mktemp -d) + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "ruby") + + msg_info "Installing Ruby $RUBY_VERSION" + + ensure_dependencies jq + + local RBENV_RELEASE + RBENV_RELEASE=$(curl -fsSL https://api.github.com/repos/rbenv/rbenv/releases/latest | jq -r '.tag_name' | sed 's/^v//') + if [[ -z "$RBENV_RELEASE" ]]; then + msg_error "Failed to fetch latest rbenv version" + rm -rf "$TMP_DIR" + return 1 + fi + + curl -fsSL "https://github.com/rbenv/rbenv/archive/refs/tags/v${RBENV_RELEASE}.tar.gz" -o "$TMP_DIR/rbenv.tar.gz" || { + msg_error "Failed to download rbenv" + rm -rf "$TMP_DIR" + return 1 + } + + tar -xzf "$TMP_DIR/rbenv.tar.gz" -C "$TMP_DIR" || { + msg_error "Failed to extract rbenv" + rm -rf "$TMP_DIR" + return 1 + } + + mkdir -p "$RBENV_DIR" + cp -r "$TMP_DIR/rbenv-${RBENV_RELEASE}/." "$RBENV_DIR/" + cd "$RBENV_DIR" && src/configure && $STD make -C src + + local RUBY_BUILD_RELEASE + RUBY_BUILD_RELEASE=$(curl -fsSL https://api.github.com/repos/rbenv/ruby-build/releases/latest | jq -r '.tag_name' | sed 's/^v//') + if [[ -z "$RUBY_BUILD_RELEASE" ]]; then + msg_error "Failed to fetch latest ruby-build version" + rm -rf "$TMP_DIR" + return 1 + fi + + curl -fsSL "https://github.com/rbenv/ruby-build/archive/refs/tags/v${RUBY_BUILD_RELEASE}.tar.gz" -o "$TMP_DIR/ruby-build.tar.gz" || { + msg_error "Failed to download ruby-build" + rm -rf "$TMP_DIR" + return 1 + } + + tar -xzf "$TMP_DIR/ruby-build.tar.gz" -C "$TMP_DIR" || { + msg_error "Failed to extract ruby-build" + rm -rf "$TMP_DIR" + return 1 + } + + mkdir -p "$RBENV_DIR/plugins/ruby-build" + cp -r "$TMP_DIR/ruby-build-${RUBY_BUILD_RELEASE}/." "$RBENV_DIR/plugins/ruby-build/" + echo "$RUBY_BUILD_RELEASE" >"$RBENV_DIR/plugins/ruby-build/RUBY_BUILD_version.txt" + + if ! grep -q 'rbenv init' "$PROFILE_FILE"; then + echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >>"$PROFILE_FILE" + echo 'eval "$(rbenv init -)"' >>"$PROFILE_FILE" + fi + + export PATH="$RBENV_DIR/bin:$PATH" + eval "$("$RBENV_BIN" init - bash)" + + if ! "$RBENV_BIN" versions --bare | grep -qx "$RUBY_VERSION"; then + $STD "$RBENV_BIN" install "$RUBY_VERSION" + fi + + "$RBENV_BIN" global "$RUBY_VERSION" + hash -r + + if [[ "$RUBY_INSTALL_RAILS" == "true" ]]; then + $STD gem install rails + local RAILS_VERSION=$(rails -v 2>/dev/null | awk '{print $2}') + msg_ok "Installed Rails $RAILS_VERSION" + fi + + rm -rf "$TMP_DIR" + cache_installed_version "ruby" "$RUBY_VERSION" + msg_ok "Installed Ruby $RUBY_VERSION" +} + +# ------------------------------------------------------------------------------ +# Installs or upgrades ClickHouse database server. +# +# Description: +# - Adds ClickHouse official repository +# - Installs specified version +# - Configures systemd service +# - Supports Debian/Ubuntu with fallback mechanism +# +# Variables: +# CLICKHOUSE_VERSION - ClickHouse version to install (default: latest) +# ------------------------------------------------------------------------------ + +function setup_clickhouse() { + local CLICKHOUSE_VERSION="${CLICKHOUSE_VERSION:-latest}" + local DISTRO_ID DISTRO_CODENAME + DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + + # Determine latest version if needed + if [[ "$CLICKHOUSE_VERSION" == "latest" ]]; then + CLICKHOUSE_VERSION=$(curl -fsSL https://packages.clickhouse.com/tgz/stable/ | + grep -oP 'clickhouse-common-static-\K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | + sort -V | + tail -n1) + if [[ -z "$CLICKHOUSE_VERSION" ]]; then + msg_error "Could not determine latest ClickHouse version" + return 1 + fi + fi + + local CURRENT_VERSION="" + if command -v clickhouse-server >/dev/null 2>&1; then + CURRENT_VERSION=$(clickhouse-server --version 2>/dev/null | grep -oP 'version \K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -n1) + fi + + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "clickhouse") + + # Check if already at target version + if [[ "$CURRENT_VERSION" == "$CLICKHOUSE_VERSION" ]]; then + if [[ "$CACHED_VERSION" == "$CLICKHOUSE_VERSION" ]]; then + upgrade_package clickhouse-server + upgrade_package clickhouse-client + else + msg_info "Upgrading ClickHouse $CLICKHOUSE_VERSION" + $STD apt update + $STD apt install --only-upgrade -y clickhouse-server clickhouse-client + cache_installed_version "clickhouse" "$CLICKHOUSE_VERSION" + msg_ok "Upgraded ClickHouse $CLICKHOUSE_VERSION" + fi + return 0 + fi + + msg_info "Installing ClickHouse $CLICKHOUSE_VERSION" + + # Stop existing service if upgrading + if [[ -n "$CURRENT_VERSION" ]]; then + $STD systemctl stop clickhouse-server || true + fi + + # Cleanup old repository files + cleanup_old_repo_files "clickhouse" + + # Ensure dependencies + ensure_dependencies apt-transport-https ca-certificates dirmngr gnupg + + # Use helper function to get fallback suite + local SUITE + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://packages.clickhouse.com/deb") + + # Use standardized repo setup + setup_deb822_repo \ + "clickhouse" \ + "https://packages.clickhouse.com/rpm/lts/repodata/repomd.xml.key" \ + "https://packages.clickhouse.com/deb" \ + "$SUITE" \ + "main" \ + "amd64 arm64" + + # Install ClickHouse packages + export DEBIAN_FRONTEND=noninteractive + $STD apt install -y clickhouse-server clickhouse-client + + # Create data directory if it doesn't exist + mkdir -p /var/lib/clickhouse + chown -R clickhouse:clickhouse /var/lib/clickhouse + + # Enable and start service + $STD systemctl enable clickhouse-server + safe_service_restart clickhouse-server + + cache_installed_version "clickhouse" "$CLICKHOUSE_VERSION" + msg_ok "Installed ClickHouse $CLICKHOUSE_VERSION" +} + +# ------------------------------------------------------------------------------ +# Installs Rust toolchain and optional global crates via cargo. +# +# Description: +# - Installs rustup (if missing) +# - Installs or updates desired Rust toolchain (stable, nightly, or versioned) +# - Installs or updates specified global crates using `cargo install` +# +# Notes: +# - Skips crate install if exact version is already present +# - Updates crate if newer version or different version is requested +# +# Variables: +# RUST_TOOLCHAIN - Rust toolchain to install (default: stable) +# RUST_CRATES - Comma-separated list of crates (e.g. "cargo-edit,wasm-pack@0.12.1") +# ------------------------------------------------------------------------------ + +function setup_rust() { + local RUST_TOOLCHAIN="${RUST_TOOLCHAIN:-stable}" + local RUST_CRATES="${RUST_CRATES:-}" + local CARGO_BIN="${HOME}/.cargo/bin" + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "rust") + + if ! command -v rustup &>/dev/null; then + msg_info "Installing Rust" + curl -fsSL https://sh.rustup.rs | $STD sh -s -- -y --default-toolchain "$RUST_TOOLCHAIN" || { + msg_error "Failed to install Rust" + return 1 + } + export PATH="$CARGO_BIN:$PATH" + echo 'export PATH="$HOME/.cargo/bin:$PATH"' >>"$HOME/.profile" + local RUST_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}') + cache_installed_version "rust" "$RUST_VERSION" + msg_ok "Installed Rust $RUST_VERSION" + else + $STD rustup install "$RUST_TOOLCHAIN" + $STD rustup default "$RUST_TOOLCHAIN" + $STD rustup update "$RUST_TOOLCHAIN" + local RUST_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}') + cache_installed_version "rust" "$RUST_VERSION" + msg_ok "Updated Rust toolchain to $RUST_TOOLCHAIN ($RUST_VERSION)" + fi + + if [[ -n "$RUST_CRATES" ]]; then + IFS=',' read -ra CRATES <<<"$RUST_CRATES" + for crate in "${CRATES[@]}"; do + local NAME VER INSTALLED_VER + if [[ "$crate" == *"@"* ]]; then + NAME="${crate%@*}" + VER="${crate##*@}" + else + NAME="$crate" + VER="" + fi + + INSTALLED_VER=$(cargo install --list 2>/dev/null | awk "/^$NAME v[0-9]/ {print \$2}" | tr -d 'v') + + if [[ -n "$INSTALLED_VER" ]]; then + if [[ -n "$VER" && "$VER" != "$INSTALLED_VER" ]]; then + msg_info "Updating $NAME: $INSTALLED_VER → $VER" + $STD cargo install "$NAME" --version "$VER" --force + msg_ok "Updated $NAME to $VER" + elif [[ -z "$VER" ]]; then + msg_info "Updating $NAME: $INSTALLED_VER → latest" + $STD cargo install "$NAME" --force + local NEW_VER=$(cargo install --list 2>/dev/null | awk "/^$NAME v[0-9]/ {print \$2}" | tr -d 'v') + msg_ok "Updated $NAME to $NEW_VER" + fi + else + msg_info "Installing $NAME ${VER:+$VER}" + $STD cargo install "$NAME" ${VER:+--version "$VER"} + msg_ok "Installed $NAME ${VER:-latest}" + fi + done fi } @@ -1298,12 +2861,11 @@ function download_with_progress() { function setup_uv() { local UV_BIN="/usr/local/bin/uv" - local TMP_DIR - TMP_DIR=$(mktemp -d) + local TMP_DIR=$(mktemp -d) + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "uv") - # Determine system architecture - local ARCH - ARCH=$(uname -m) + local ARCH=$(uname -m) local UV_TAR case "$ARCH" in @@ -1328,45 +2890,48 @@ function setup_uv() { ;; esac - # Get latest version from GitHub + ensure_dependencies jq + local LATEST_VERSION LATEST_VERSION=$(curl -fsSL https://api.github.com/repos/astral-sh/uv/releases/latest | - grep '"tag_name":' | cut -d '"' -f4 | sed 's/^v//') + jq -r '.tag_name' | sed 's/^v//') if [[ -z "$LATEST_VERSION" ]]; then - msg_error "Could not fetch latest uv version from GitHub." + msg_error "Could not fetch latest uv version" rm -rf "$TMP_DIR" return 1 fi - # Check if uv is already up to date if [[ -x "$UV_BIN" ]]; then local INSTALLED_VERSION - INSTALLED_VERSION=$($UV_BIN -V | awk '{print $2}') + INSTALLED_VERSION=$($UV_BIN -V 2>/dev/null | awk '{print $2}') if [[ "$INSTALLED_VERSION" == "$LATEST_VERSION" ]]; then + if [[ "$CACHED_VERSION" == "$LATEST_VERSION" ]]; then + rm -rf "$TMP_DIR" + return 0 + fi + cache_installed_version "uv" "$LATEST_VERSION" rm -rf "$TMP_DIR" - [[ ":$PATH:" != *":/usr/local/bin:"* ]] && export PATH="/usr/local/bin:$PATH" return 0 else msg_info "Updating uv from $INSTALLED_VERSION to $LATEST_VERSION" fi else - msg_info "Setup uv $LATEST_VERSION" + msg_info "Installing uv $LATEST_VERSION" fi - # Download and install manually local UV_URL="https://github.com/astral-sh/uv/releases/latest/download/${UV_TAR}" - if ! curl -fsSL "$UV_URL" -o "$TMP_DIR/uv.tar.gz"; then - msg_error "Failed to download $UV_URL" + curl -fsSL "$UV_URL" -o "$TMP_DIR/uv.tar.gz" || { + msg_error "Failed to download uv" rm -rf "$TMP_DIR" return 1 - fi + } - if ! tar -xzf "$TMP_DIR/uv.tar.gz" -C "$TMP_DIR"; then - msg_error "Failed to extract uv archive" + tar -xzf "$TMP_DIR/uv.tar.gz" -C "$TMP_DIR" || { + msg_error "Failed to extract uv" rm -rf "$TMP_DIR" return 1 - fi + } install -m 755 "$TMP_DIR"/*/uv "$UV_BIN" || { msg_error "Failed to install uv binary" @@ -1375,14 +2940,13 @@ function setup_uv() { } rm -rf "$TMP_DIR" - #ensure_usr_local_bin_persist - $STD uv python update-shell || { - msg_error "Failed to update uv shell integration" - return 1 - } - msg_ok "Setup uv $LATEST_VERSION" + ensure_usr_local_bin_persist + export PATH="/usr/local/bin:$PATH" + + $STD uv python update-shell || true + cache_installed_version "uv" "$LATEST_VERSION" + msg_ok "Installed uv $LATEST_VERSION" - # Optional: install specific Python version if [[ -n "${PYTHON_VERSION:-}" ]]; then local VERSION_MATCH VERSION_MATCH=$(uv python list --only-downloads | @@ -1390,291 +2954,20 @@ function setup_uv() { cut -d'-' -f2 | sort -V | tail -n1) if [[ -z "$VERSION_MATCH" ]]; then - msg_error "No matching Python $PYTHON_VERSION.x version found via uv" + msg_error "No matching Python $PYTHON_VERSION.x version found" return 1 fi if ! uv python list | grep -q "cpython-${VERSION_MATCH}-linux.*uv/python"; then - if ! $STD uv python install "$VERSION_MATCH"; then - msg_error "Failed to install Python $VERSION_MATCH via uv" + $STD uv python install "$VERSION_MATCH" || { + msg_error "Failed to install Python $VERSION_MATCH" return 1 - fi - msg_ok "Setup Python $VERSION_MATCH via uv" + } + msg_ok "Installed Python $VERSION_MATCH" fi fi } -# ------------------------------------------------------------------------------ -# Ensures /usr/local/bin is permanently in system PATH. -# -# Description: -# - Adds to /etc/profile.d if not present -# ------------------------------------------------------------------------------ - -function ensure_usr_local_bin_persist() { - local PROFILE_FILE="/etc/profile.d/custom_path.sh" - - if [[ ! -f "$PROFILE_FILE" ]] && ! command -v pveversion &>/dev/null; then - echo 'export PATH="/usr/local/bin:$PATH"' >"$PROFILE_FILE" - chmod +x "$PROFILE_FILE" - fi -} - -# ------------------------------------------------------------------------------ -# Installs or updates Ghostscript (gs) from source. -# -# Description: -# - Fetches latest release -# - Builds and installs system-wide -# ------------------------------------------------------------------------------ - -function setup_gs() { - mkdir -p /tmp - TMP_DIR=$(mktemp -d) - CURRENT_VERSION=$(gs --version 2>/dev/null || echo "0") - - RELEASE_JSON=$(curl -fsSL https://api.github.com/repos/ArtifexSoftware/ghostpdl-downloads/releases/latest) - LATEST_VERSION=$(echo "$RELEASE_JSON" | grep '"tag_name":' | head -n1 | cut -d '"' -f4 | sed 's/^gs//') - LATEST_VERSION_DOTTED=$(echo "$RELEASE_JSON" | grep '"name":' | head -n1 | grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+') - - if [[ -z "$LATEST_VERSION" ]]; then - msg_error "Could not determine latest Ghostscript version from GitHub." - rm -rf "$TMP_DIR" - return - fi - - if dpkg --compare-versions "$CURRENT_VERSION" ge "$LATEST_VERSION_DOTTED"; then - rm -rf "$TMP_DIR" - return - fi - - msg_info "Setup Ghostscript $LATEST_VERSION_DOTTED (Patience)" - curl -fsSL "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs${LATEST_VERSION}/ghostscript-${LATEST_VERSION_DOTTED}.tar.gz" -o "$TMP_DIR/ghostscript.tar.gz" - - if ! tar -xzf "$TMP_DIR/ghostscript.tar.gz" -C "$TMP_DIR"; then - msg_error "Failed to extract Ghostscript archive." - rm -rf "$TMP_DIR" - return - fi - - cd "$TMP_DIR/ghostscript-${LATEST_VERSION_DOTTED}" || { - msg_error "Failed to enter Ghostscript source directory." - rm -rf "$TMP_DIR" - } - $STD apt-get install -y build-essential libpng-dev zlib1g-dev - $STD ./configure >/dev/null - $STD make - $STD sudo make install - local EXIT_CODE=$? - hash -r - if [[ ! -x "$(command -v gs)" ]]; then - if [[ -x /usr/local/bin/gs ]]; then - ln -sf /usr/local/bin/gs /usr/bin/gs - fi - fi - - rm -rf "$TMP_DIR" - - if [[ $EXIT_CODE -eq 0 ]]; then - msg_ok "Setup Ghostscript $LATEST_VERSION_DOTTED" - else - msg_error "Ghostscript installation failed" - fi -} - -# ------------------------------------------------------------------------------ -# Installs rbenv and ruby-build, installs Ruby and optionally Rails. -# -# Description: -# - Downloads rbenv and ruby-build from GitHub -# - Compiles and installs target Ruby version -# - Optionally installs Rails via gem -# -# Variables: -# RUBY_VERSION - Ruby version to install (default: 3.4.4) -# RUBY_INSTALL_RAILS - true/false to install Rails (default: true) -# ------------------------------------------------------------------------------ - -function setup_ruby() { - local RUBY_VERSION="${RUBY_VERSION:-3.4.4}" - local RUBY_INSTALL_RAILS="${RUBY_INSTALL_RAILS:-true}" - - local RBENV_DIR="$HOME/.rbenv" - local RBENV_BIN="$RBENV_DIR/bin/rbenv" - local PROFILE_FILE="$HOME/.profile" - local TMP_DIR - TMP_DIR=$(mktemp -d) - - msg_info "Setup Ruby $RUBY_VERSION" - - local RBENV_RELEASE - RBENV_RELEASE=$(curl -fsSL https://api.github.com/repos/rbenv/rbenv/releases/latest | grep '"tag_name":' | cut -d '"' -f4 | sed 's/^v//') - if [[ -z "$RBENV_RELEASE" ]]; then - msg_error "Failed to fetch latest rbenv version" - rm -rf "$TMP_DIR" - return 1 - fi - - curl -fsSL "https://github.com/rbenv/rbenv/archive/refs/tags/v${RBENV_RELEASE}.tar.gz" -o "$TMP_DIR/rbenv.tar.gz" - tar -xzf "$TMP_DIR/rbenv.tar.gz" -C "$TMP_DIR" - mkdir -p "$RBENV_DIR" - cp -r "$TMP_DIR/rbenv-${RBENV_RELEASE}/." "$RBENV_DIR/" - cd "$RBENV_DIR" && src/configure && $STD make -C src - - local RUBY_BUILD_RELEASE - RUBY_BUILD_RELEASE=$(curl -fsSL https://api.github.com/repos/rbenv/ruby-build/releases/latest | grep '"tag_name":' | cut -d '"' -f4 | sed 's/^v//') - if [[ -z "$RUBY_BUILD_RELEASE" ]]; then - msg_error "Failed to fetch latest ruby-build version" - rm -rf "$TMP_DIR" - return 1 - fi - - curl -fsSL "https://github.com/rbenv/ruby-build/archive/refs/tags/v${RUBY_BUILD_RELEASE}.tar.gz" -o "$TMP_DIR/ruby-build.tar.gz" - tar -xzf "$TMP_DIR/ruby-build.tar.gz" -C "$TMP_DIR" - mkdir -p "$RBENV_DIR/plugins/ruby-build" - cp -r "$TMP_DIR/ruby-build-${RUBY_BUILD_RELEASE}/." "$RBENV_DIR/plugins/ruby-build/" - echo "$RUBY_BUILD_RELEASE" >"$RBENV_DIR/plugins/ruby-build/RUBY_BUILD_version.txt" - - if ! grep -q 'rbenv init' "$PROFILE_FILE"; then - echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >>"$PROFILE_FILE" - echo 'eval "$(rbenv init -)"' >>"$PROFILE_FILE" - fi - - export PATH="$RBENV_DIR/bin:$PATH" - eval "$("$RBENV_BIN" init - bash)" - - if ! "$RBENV_BIN" versions --bare | grep -qx "$RUBY_VERSION"; then - $STD "$RBENV_BIN" install "$RUBY_VERSION" - fi - - "$RBENV_BIN" global "$RUBY_VERSION" - hash -r - - if [[ "$RUBY_INSTALL_RAILS" == "true" ]]; then - msg_info "Setup Rails via gem" - gem install rails - msg_ok "Setup Rails $(rails -v)" - fi - - rm -rf "$TMP_DIR" - msg_ok "Setup Ruby $RUBY_VERSION" -} - -# ------------------------------------------------------------------------------ -# Creates and installs self-signed certificates. -# -# Description: -# - Create a self-signed certificate with option to override application name -# -# Variables: -# APP - Application name (default: $APPLICATION variable) -# ------------------------------------------------------------------------------ -function create_selfsigned_certs() { - local app=${APP:-$(echo "${APPLICATION,,}" | tr -d ' ')} - $STD openssl req -x509 -nodes -days 365 -newkey rsa:4096 \ - -keyout /etc/ssl/private/"$app"-selfsigned.key \ - -out /etc/ssl/certs/"$app"-selfsigned.crt \ - -subj "/C=US/O=$app/OU=Domain Control Validated/CN=localhost" -} - -# ------------------------------------------------------------------------------ -# Installs Rust toolchain and optional global crates via cargo. -# -# Description: -# - Installs rustup (if missing) -# - Installs or updates desired Rust toolchain (stable, nightly, or versioned) -# - Installs or updates specified global crates using `cargo install` -# -# Notes: -# - Skips crate install if exact version is already present -# - Updates crate if newer version or different version is requested -# -# Variables: -# RUST_TOOLCHAIN - Rust toolchain to install (default: stable) -# RUST_CRATES - Comma-separated list of crates (e.g. "cargo-edit,wasm-pack@0.12.1") -# ------------------------------------------------------------------------------ - -function setup_rust() { - local RUST_TOOLCHAIN="${RUST_TOOLCHAIN:-stable}" - local RUST_CRATES="${RUST_CRATES:-}" - local CARGO_BIN="${HOME}/.cargo/bin" - - # rustup & toolchain - if ! command -v rustup &>/dev/null; then - msg_info "Setup Rust" - curl -fsSL https://sh.rustup.rs | $STD sh -s -- -y --default-toolchain "$RUST_TOOLCHAIN" - export PATH="$CARGO_BIN:$PATH" - echo 'export PATH="$HOME/.cargo/bin:$PATH"' >>"$HOME/.profile" - msg_ok "Setup Rust" - else - $STD rustup install "$RUST_TOOLCHAIN" - $STD rustup default "$RUST_TOOLCHAIN" - $STD rustup update "$RUST_TOOLCHAIN" - msg_ok "Rust toolchain set to $RUST_TOOLCHAIN" - fi - - # install/update crates - if [[ -n "$RUST_CRATES" ]]; then - IFS=',' read -ra CRATES <<<"$RUST_CRATES" - for crate in "${CRATES[@]}"; do - local NAME VER INSTALLED_VER - if [[ "$crate" == *"@"* ]]; then - NAME="${crate%@*}" - VER="${crate##*@}" - else - NAME="$crate" - VER="" - fi - - INSTALLED_VER=$(cargo install --list 2>/dev/null | awk "/^$NAME v[0-9]/ {print \$2}" | tr -d 'v') - - if [[ -n "$INSTALLED_VER" ]]; then - if [[ -n "$VER" && "$VER" != "$INSTALLED_VER" ]]; then - msg_info "Update $NAME: $INSTALLED_VER → $VER" - $STD cargo install "$NAME" --version "$VER" --force - msg_ok "Updated $NAME to $VER" - elif [[ -z "$VER" ]]; then - msg_info "Update $NAME: $INSTALLED_VER → latest" - $STD cargo install "$NAME" --force - msg_ok "Updated $NAME to latest" - fi - else - msg_info "Setup $NAME ${VER:+($VER)}" - $STD cargo install "$NAME" ${VER:+--version "$VER"} - msg_ok "Setup $NAME ${VER:-latest}" - fi - done - msg_ok "Setup Rust" - fi -} - -# ------------------------------------------------------------------------------ -# Installs Adminer (Debian/Ubuntu via APT, Alpine via direct download). -# -# Description: -# - Adds Adminer to Apache or web root -# - Supports Alpine and Debian-based systems -# ------------------------------------------------------------------------------ - -function setup_adminer() { - if grep -qi alpine /etc/os-release; then - msg_info "Setup Adminer (Alpine)" - mkdir -p /var/www/localhost/htdocs/adminer - if ! curl -fsSL https://github.com/vrana/adminer/releases/latest/download/adminer.php \ - -o /var/www/localhost/htdocs/adminer/index.php; then - msg_error "Failed to download Adminer" - return 1 - fi - msg_ok "Adminer available at /adminer (Alpine)" - else - msg_info "Setup Adminer (Debian/Ubuntu)" - $STD apt-get install -y adminer - $STD a2enconf adminer - $STD systemctl reload apache2 - msg_ok "Adminer available at /adminer (Debian/Ubuntu)" - fi -} - # ------------------------------------------------------------------------------ # Installs or updates yq (mikefarah/yq - Go version). # @@ -1685,51 +2978,56 @@ function setup_adminer() { # ------------------------------------------------------------------------------ function setup_yq() { - local TMP_DIR - TMP_DIR=$(mktemp -d) - local CURRENT_VERSION="" + local TMP_DIR=$(mktemp -d) local BINARY_PATH="/usr/local/bin/yq" local GITHUB_REPO="mikefarah/yq" + local CURRENT_VERSION="" + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "yq") - if ! command -v jq &>/dev/null; then - $STD apt-get update - $STD apt-get install -y jq || { - msg_error "Failed to install jq" - rm -rf "$TMP_DIR" - return 1 - } - fi + ensure_dependencies jq + ensure_usr_local_bin_persist if command -v yq &>/dev/null; then if ! yq --version 2>&1 | grep -q 'mikefarah'; then rm -f "$(command -v yq)" else - CURRENT_VERSION=$(yq --version | awk '{print $NF}' | sed 's/^v//') + CURRENT_VERSION=$(yq --version 2>/dev/null | awk '{print $NF}' | sed 's/^v//') fi fi - local RELEASE_JSON - RELEASE_JSON=$(curl -fsSL "https://api.github.com/repos/${GITHUB_REPO}/releases/latest") local LATEST_VERSION - LATEST_VERSION=$(echo "$RELEASE_JSON" | jq -r '.tag_name' | sed 's/^v//') + LATEST_VERSION=$(curl -fsSL "https://api.github.com/repos/${GITHUB_REPO}/releases/latest" | + jq -r '.tag_name' | sed 's/^v//') if [[ -z "$LATEST_VERSION" ]]; then - msg_error "Could not determine latest yq version from GitHub." + msg_error "Could not determine latest yq version" rm -rf "$TMP_DIR" return 1 fi if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$LATEST_VERSION" ]]; then - return + if [[ "$CACHED_VERSION" == "$LATEST_VERSION" ]]; then + rm -rf "$TMP_DIR" + return 0 + fi + cache_installed_version "yq" "$LATEST_VERSION" + rm -rf "$TMP_DIR" + return 0 fi - msg_info "Setup yq ($LATEST_VERSION)" - curl -fsSL "https://github.com/${GITHUB_REPO}/releases/download/v${LATEST_VERSION}/yq_linux_amd64" -o "$TMP_DIR/yq" + msg_info "Installing yq $LATEST_VERSION" + curl -fsSL "https://github.com/${GITHUB_REPO}/releases/download/v${LATEST_VERSION}/yq_linux_amd64" -o "$TMP_DIR/yq" || { + msg_error "Failed to download yq" + rm -rf "$TMP_DIR" + return 1 + } + chmod +x "$TMP_DIR/yq" mv "$TMP_DIR/yq" "$BINARY_PATH" if [[ ! -x "$BINARY_PATH" ]]; then - msg_error "Failed to install yq to $BINARY_PATH" + msg_error "Failed to install yq" rm -rf "$TMP_DIR" return 1 fi @@ -1738,471 +3036,7 @@ function setup_yq() { hash -r local FINAL_VERSION - FINAL_VERSION=$("$BINARY_PATH" --version 2>/dev/null | awk '{print $NF}') - if [[ "$FINAL_VERSION" == "v$LATEST_VERSION" ]]; then - msg_ok "Setup yq ($LATEST_VERSION)" - else - msg_error "yq installation incomplete or version mismatch" - fi -} - -# ------------------------------------------------------------------------------ -# Installs ImageMagick 7 from source (Debian/Ubuntu only). -# -# Description: -# - Downloads the latest ImageMagick source tarball -# - Builds and installs ImageMagick to /usr/local -# - Configures dynamic linker (ldconfig) -# -# Notes: -# - Requires: build-essential, libtool, libjpeg-dev, libpng-dev, etc. -# ------------------------------------------------------------------------------ -function setup_imagemagick() { - local TMP_DIR - TMP_DIR=$(mktemp -d) - local VERSION="" - local BINARY_PATH="/usr/local/bin/magick" - - if command -v magick &>/dev/null; then - VERSION=$(magick -version | awk '/^Version/ {print $3}') - msg_ok "ImageMagick already installed ($VERSION)" - return 0 - fi - - msg_info "Setup ImageMagick (Patience)" - $STD apt-get update - $STD apt-get install -y \ - build-essential \ - libtool \ - libjpeg-dev \ - libpng-dev \ - libtiff-dev \ - libwebp-dev \ - libheif-dev \ - libde265-dev \ - libopenjp2-7-dev \ - libxml2-dev \ - liblcms2-dev \ - libfreetype6-dev \ - libraw-dev \ - libfftw3-dev \ - liblqr-1-0-dev \ - libgsl-dev \ - pkg-config \ - ghostscript - - curl -fsSL https://imagemagick.org/archive/ImageMagick.tar.gz -o "$TMP_DIR/ImageMagick.tar.gz" - tar -xzf "$TMP_DIR/ImageMagick.tar.gz" -C "$TMP_DIR" - cd "$TMP_DIR"/ImageMagick-* || { - msg_error "Source extraction failed" - rm -rf "$TMP_DIR" - return 1 - } - - ./configure --disable-static >/dev/null - $STD make - $STD make install - $STD ldconfig /usr/local/lib - - if [[ ! -x "$BINARY_PATH" ]]; then - msg_error "ImageMagick installation failed" - rm -rf "$TMP_DIR" - return 1 - fi - - VERSION=$("$BINARY_PATH" -version | awk '/^Version/ {print $3}') - rm -rf "$TMP_DIR" - ensure_usr_local_bin_persist - msg_ok "Setup ImageMagick $VERSION" -} - -# ------------------------------------------------------------------------------ -# Installs FFmpeg from source or prebuilt binary (Debian/Ubuntu only). -# -# Description: -# - Downloads and builds FFmpeg from GitHub (https://github.com/FFmpeg/FFmpeg) -# - Supports specific version override via FFMPEG_VERSION (e.g. n7.1.1) -# - Supports build profile via FFMPEG_TYPE: -# - minimal : x264, vpx, mp3 only -# - medium : adds subtitles, fonts, opus, vorbis -# - full : adds dav1d, svt-av1, zlib, numa -# - binary : downloads static build (johnvansickle.com) -# - Defaults to latest stable version and full feature set -# -# Notes: -# - Requires: curl, jq, build-essential, and matching codec libraries -# - Result is installed to /usr/local/bin/ffmpeg -# ------------------------------------------------------------------------------ - -function setup_ffmpeg() { - local TMP_DIR - TMP_DIR=$(mktemp -d) - local GITHUB_REPO="FFmpeg/FFmpeg" - local VERSION="${FFMPEG_VERSION:-latest}" - local TYPE="${FFMPEG_TYPE:-full}" - local BIN_PATH="/usr/local/bin/ffmpeg" - - # Binary fallback mode - if [[ "$TYPE" == "binary" ]]; then - msg_info "Installing FFmpeg (static binary)" - curl -fsSL https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz -o "$TMP_DIR/ffmpeg.tar.xz" - tar -xf "$TMP_DIR/ffmpeg.tar.xz" -C "$TMP_DIR" - local EXTRACTED_DIR - EXTRACTED_DIR=$(find "$TMP_DIR" -maxdepth 1 -type d -name "ffmpeg-*") - cp "$EXTRACTED_DIR/ffmpeg" "$BIN_PATH" - cp "$EXTRACTED_DIR/ffprobe" /usr/local/bin/ffprobe - chmod +x "$BIN_PATH" /usr/local/bin/ffprobe - rm -rf "$TMP_DIR" - msg_ok "Installed FFmpeg binary ($($BIN_PATH -version | head -n1))" - return - fi - - if ! command -v jq &>/dev/null; then - $STD apt-get update - $STD apt-get install -y jq - fi - - # Auto-detect latest stable version if none specified - if [[ "$VERSION" == "latest" || -z "$VERSION" ]]; then - msg_info "Resolving latest FFmpeg tag" - VERSION=$(curl -fsSL "https://api.github.com/repos/${GITHUB_REPO}/tags" | - jq -r '.[].name' | - grep -E '^n[0-9]+\.[0-9]+\.[0-9]+$' | - sort -V | tail -n1) - fi - - if [[ -z "$VERSION" ]]; then - msg_error "Could not determine FFmpeg version" - rm -rf "$TMP_DIR" - return 1 - fi - - msg_info "Installing FFmpeg ${VERSION} ($TYPE)" - - # Dependency selection - local DEPS=(build-essential yasm nasm pkg-config) - case "$TYPE" in - minimal) - DEPS+=(libx264-dev libvpx-dev libmp3lame-dev) - ;; - medium) - DEPS+=(libx264-dev libvpx-dev libmp3lame-dev libfreetype6-dev libass-dev libopus-dev libvorbis-dev) - ;; - full) - DEPS+=( - libx264-dev libx265-dev libvpx-dev libmp3lame-dev - libfreetype6-dev libass-dev libopus-dev libvorbis-dev - libdav1d-dev libsvtav1-dev zlib1g-dev libnuma-dev - ) - ;; - *) - msg_error "Invalid FFMPEG_TYPE: $TYPE" - rm -rf "$TMP_DIR" - return 1 - ;; - esac - - $STD apt-get update - $STD apt-get install -y "${DEPS[@]}" - - curl -fsSL "https://github.com/${GITHUB_REPO}/archive/refs/tags/${VERSION}.tar.gz" -o "$TMP_DIR/ffmpeg.tar.gz" - tar -xzf "$TMP_DIR/ffmpeg.tar.gz" -C "$TMP_DIR" - cd "$TMP_DIR/FFmpeg-"* || { - msg_error "Source extraction failed" - rm -rf "$TMP_DIR" - return 1 - } - - local args=( - --enable-gpl - --enable-shared - --enable-nonfree - --disable-static - --enable-libx264 - --enable-libvpx - --enable-libmp3lame - ) - - if [[ "$TYPE" != "minimal" ]]; then - args+=(--enable-libfreetype --enable-libass --enable-libopus --enable-libvorbis) - fi - - if [[ "$TYPE" == "full" ]]; then - args+=(--enable-libx265 --enable-libdav1d --enable-zlib) - fi - - if [[ ${#args[@]} -eq 0 ]]; then - msg_error "FFmpeg configure args array is empty – aborting." - rm -rf "$TMP_DIR" - return 1 - fi - - ./configure "${args[@]}" >"$TMP_DIR/configure.log" 2>&1 || { - msg_error "FFmpeg ./configure failed (see $TMP_DIR/configure.log)" - cat "$TMP_DIR/configure.log" | tail -n 20 - rm -rf "$TMP_DIR" - return 1 - } - - $STD make -j"$(nproc)" - $STD make install - echo "/usr/local/lib" >/etc/ld.so.conf.d/ffmpeg.conf - ldconfig - - ldconfig -p | grep libavdevice >/dev/null || { - msg_error "libavdevice not registered with dynamic linker" - return 1 - } - - if ! command -v ffmpeg &>/dev/null; then - msg_error "FFmpeg installation failed" - rm -rf "$TMP_DIR" - return 1 - fi - - local FINAL_VERSION - FINAL_VERSION=$(ffmpeg -version | head -n1 | awk '{print $3}') - rm -rf "$TMP_DIR" - ensure_usr_local_bin_persist - msg_ok "Setup FFmpeg $FINAL_VERSION" -} - -# ------------------------------------------------------------------------------ -# Installs ClickHouse server and client, sets up DB/user with credentials. -# -# Description: -# - Adds official ClickHouse APT repo with GPG key -# - Installs clickhouse-server and clickhouse-client -# - Creates database and user (credentials optionally overrideable via env) -# -# Variables: -# CLICKHOUSE_DB - Database name (default: analytics) -# CLICKHOUSE_USER - Username (default: analytics_user) -# CLICKHOUSE_PASS - Password (default: auto-generated) -# ------------------------------------------------------------------------------ - -function setup_clickhouse() { - local CLICKHOUSE_DB="${CLICKHOUSE_DB:-analytics}" - local CLICKHOUSE_USER="${CLICKHOUSE_USER:-analytics_user}" - local CLICKHOUSE_PASS="${CLICKHOUSE_PASS:-$(openssl rand -base64 18 | cut -c1-13)}" - local GPG_URL="https://packages.clickhouse.com/rpm/lts/repodata/repomd.xml.key" - local GPG_KEY_PATH="/usr/share/keyrings/clickhouse-keyring.gpg" - local ARCH - ARCH=$(dpkg --print-architecture) - - if ! command -v clickhouse >/dev/null; then - msg_info "Setup ClickHouse" - - if ! curl -fsSL --connect-timeout 5 https://packages.clickhouse.com >/dev/null 2>&1; then - msg_error "Connection to packages.clickhouse.com:443 failed – possibly blocked" - echo "💡 Check AdGuard/Pi-hole or firewall rules" - return 1 - fi - - if ! curl -fsSL --retry 3 --connect-timeout 10 "$GPG_URL" | - gpg --dearmor -o "$GPG_KEY_PATH"; then - msg_error "Failed to fetch ClickHouse GPG key" - return 1 - fi - - echo "deb [signed-by=$GPG_KEY_PATH arch=$ARCH] https://packages.clickhouse.com/deb stable main" \ - >/etc/apt/sources.list.d/clickhouse.list - - env -u CLICKHOUSE_USER $STD apt-get update - env -u CLICKHOUSE_USER DEBIAN_FRONTEND=noninteractive $STD apt-get install -y clickhouse-server clickhouse-client - - $STD systemctl enable --now clickhouse-server - - msg_info "Waiting for ClickHouse to be ready" - for i in {1..10}; do - if clickhouse client --query "SELECT 1" &>/dev/null; then break; fi - sleep 1 - done - - # User anlegen - clickhouse client --query "CREATE DATABASE IF NOT EXISTS $CLICKHOUSE_DB" - clickhouse client --query "CREATE USER IF NOT EXISTS $CLICKHOUSE_USER IDENTIFIED WITH plaintext_password BY '$CLICKHOUSE_PASS'" - clickhouse client --query "GRANT ALL ON $CLICKHOUSE_DB.* TO $CLICKHOUSE_USER" - - # Default-User ggf. deaktivieren - cat </etc/clickhouse-server/users.d/disable-default.xml - - - - - -EOF - systemctl restart clickhouse-server - - msg_ok "Setup ClickHouse (DB: $CLICKHOUSE_DB, User: $CLICKHOUSE_USER)" - - { - echo "ClickHouse DB: $CLICKHOUSE_DB" - echo "ClickHouse User: $CLICKHOUSE_USER" - echo "ClickHouse Pass: $CLICKHOUSE_PASS" - } >>~/clickhouse.creds - else - msg_info "Updating ClickHouse packages" - env -u CLICKHOUSE_USER $STD apt-get update - env -u CLICKHOUSE_USER $STD apt-get install -y --only-upgrade clickhouse-server clickhouse-client - msg_ok "ClickHouse updated" - fi -} - -# ------------------------------------------------------------------------------ -# Checks for new GitHub release (latest tag). -# -# Description: -# - Queries the GitHub API for the latest release tag -# - Compares it to a local cached version (~/.) -# - If newer, sets global CHECK_UPDATE_RELEASE and returns 0 -# -# Usage: -# check_for_gh_release "AppName" "user/repo" -# if [[ $? -eq 0 ]]; then -# echo "New version available: $CHECK_UPDATE_RELEASE" -# # trigger update... -# fi -# -# Notes: -# - Requires `jq` (auto-installed if missing) -# - Does not modify anything, only checks version state -# - Does not support pre-releases -# ------------------------------------------------------------------------------ -check_for_gh_release() { - local app="$1" - local source="$2" - local pinned_version="${3:-}" # optional - local current_file="$HOME/.${app,,}" - - msg_info "Check for update: ${app}" - - # DNS check for GitHub - if ! getent hosts api.github.com >/dev/null 2>&1; then - msg_error "Network error: cannot resolve api.github.com" - return 1 - fi - - # jq check - if ! command -v jq &>/dev/null; then - apt-get update -qq &>/dev/null && apt-get install -y jq &>/dev/null || { - msg_error "Failed to install jq" - return 1 - } - fi - - # get latest release - local release - release=$(curl -fsSL "https://api.github.com/repos/${source}/releases/latest" | - jq -r '.tag_name' | sed 's/^v//') - - if [[ -z "$release" ]]; then - msg_error "Unable to determine latest release for ${app}" - return 1 - fi - - local current="" - [[ -f "$current_file" ]] && current=$(<"$current_file") - - # PINNED Releases - if [[ -n "$pinned_version" ]]; then - if [[ "$pinned_version" == "$release" ]]; then - msg_ok "${app} pinned to v${pinned_version} (no update needed)" - return 1 - else - if [[ "$current" == "$pinned_version" ]]; then - msg_ok "${app} pinned to v${pinned_version} (already installed, upstream v${release})" - return 1 - fi - msg_info "${app} pinned to v${pinned_version} (upstream v${release}) → update/downgrade required" - CHECK_UPDATE_RELEASE="$pinned_version" - return 0 - fi - fi - - if [[ "$release" != "$current" ]] || [[ ! -f "$current_file" ]]; then - CHECK_UPDATE_RELEASE="$release" - msg_info "New release available: v${release} (current: v${current:-none})" - return 0 - else - msg_ok "${app} is up to date (v${release})" - return 1 - fi -} - -# ------------------------------------------------------------------------------ -# Hardware acceleration setup inside container -# Works with: Debian 12 (bookworm), Debian 13 (trixie), Ubuntu 24.04 (noble) -# Usage: hwaccel_setup_in_ct [--nonfree-intel] -# ------------------------------------------------------------------------------ - -hwaccel_setup_in_ct() { - local CTTYPE="$1" NONFREE=0 - [[ "$2" == "--nonfree-intel" ]] && NONFREE=1 - - # Detect OS info inside the container - local ID VERSION_CODENAME - if [[ -r /etc/os-release ]]; then - . /etc/os-release - fi - ID="${ID:-debian}" - VERSION_CODENAME="${VERSION_CODENAME:-bookworm}" - - msg_info "Setting up hardware acceleration for ${ID^} ($VERSION_CODENAME)" - - case "$ID" in - debian | ubuntu) - if ((NONFREE)) && [[ "$VERSION_CODENAME" =~ (trixie|noble) ]]; then - # Debian 13 / Ubuntu 24.04 → non-free Intel driver - cat >/etc/apt/sources.list.d/non-free.sources <<'SRC' -Types: deb deb-src -URIs: http://deb.debian.org/debian -Suites: trixie -Components: non-free non-free-firmware - -Types: deb deb-src -URIs: http://deb.debian.org/debian-security -Suites: trixie-security -Components: non-free non-free-firmware - -Types: deb deb-src -URIs: http://deb.debian.org/debian -Suites: trixie-updates -Components: non-free non-free-firmware -SRC - - $STD apt-get update - $STD apt-get install -y \ - intel-media-va-driver-non-free \ - ocl-icd-libopencl1 \ - mesa-opencl-icd \ - mesa-va-drivers \ - libvpl2 \ - vainfo \ - intel-gpu-tools - else - # Debian 12 (bookworm) and fallback for Debian 13/Ubuntu 24.04 without non-free - $STD apt-get update - $STD apt-get install -y \ - va-driver-all \ - ocl-icd-libopencl1 \ - mesa-opencl-icd \ - mesa-va-drivers \ - vainfo \ - intel-gpu-tools - fi - ;; - *) - msg_warn "Unsupported distribution ($ID $VERSION_CODENAME) – skipping HW accel setup" - return 0 - ;; - esac - - # Add current user to video/render groups (only for privileged CTs) - if [[ "$CTTYPE" == "0" ]]; then - $STD adduser "$(id -un)" video || true - $STD adduser "$(id -un)" render || true - fi - - msg_ok "Hardware acceleration is ready" + FINAL_VERSION=$("$BINARY_PATH" --version 2>/dev/null | awk '{print $NF}' | sed 's/^v//') + cache_installed_version "yq" "$FINAL_VERSION" + msg_ok "Installed yq $FINAL_VERSION" } diff --git a/misc/tools.func.bak b/misc/tools.func.bak new file mode 100644 index 000000000..e98332517 --- /dev/null +++ b/misc/tools.func.bak @@ -0,0 +1,2208 @@ +#!/bin/bash + +# ------------------------------------------------------------------------------ +# Installs Microsoft .NET +# +# Description: +# - Installs specified .NET version using Microsoft APT repo +# +# Variables: +# NET_VERSION - .NET version to install (default: "8.0") +# ------------------------------------------------------------------------------ + +function setup_dotnet() { + local NET_VERSION="${NET_VERSION:-8.0}" + local DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{ print $2 }' /etc/os-release) + local NEED_NET_INSTALL=false + + if command -v dotnet >/dev/null; then + CURRENT_NET_VERSION=$(dotnet --version | awk -F. '{print $1 "." $2}') + if [[ "$CURRENT_NET_VERSION" == "$NET_VERSION" ]]; then + : + fi + else + msg_info "Setup .NET $NET_VERSION" + NEED_NET_INSTALL=true + fi + + if [[ "$NEED_NET_INSTALL" == true ]]; then + curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor -o /usr/share/keyrings/microsoft-prod.gpg + if [[ "$DISTRO_CODENAME" != "trixie" ]]; then + curl -fsSL https://packages.microsoft.com/config/debian/12/prod.list -o /etc/apt/sources.list.d/msprod.list + else + curl -fsSL https://packages.microsoft.com/config/debian/13/prod.list -o /etc/apt/sources.list.d/msprod.list + fi + $STD apt-get update + $STD apt-get install -y dotnet-sdk-$NET_VERSION + msg_ok "Setup .NET ${NET_VERSION}" + fi +} + +# ------------------------------------------------------------------------------ +# Installs Node.js and optional global modules. +# +# Description: +# - Installs specified Node.js version using NodeSource APT repo +# - Optionally installs or updates global npm modules +# +# Variables: +# NODE_VERSION - Node.js version to install (default: 22) +# NODE_MODULE - Comma-separated list of global modules (e.g. "yarn,@vue/cli@5.0.0") +# ------------------------------------------------------------------------------ + +function setup_nodejs() { + local NODE_VERSION="${NODE_VERSION:-22}" + local NODE_MODULE="${NODE_MODULE:-}" + local CURRENT_NODE_VERSION="" + local NEED_NODE_INSTALL=false + + if command -v node >/dev/null; then + CURRENT_NODE_VERSION="$(node -v | grep -oP '^v\K[0-9]+')" + if [[ "$CURRENT_NODE_VERSION" != "$NODE_VERSION" ]]; then + msg_info "Old Node.js $CURRENT_NODE_VERSION found, replacing with $NODE_VERSION" + NEED_NODE_INSTALL=true + fi + else + msg_info "Setup Node.js $NODE_VERSION" + NEED_NODE_INSTALL=true + fi + + if ! command -v jq &>/dev/null; then + $STD apt-get update + $STD apt-get install -y jq || { + msg_error "Failed to install jq" + return 1 + } + fi + + if [[ "$NEED_NODE_INSTALL" == true ]]; then + $STD apt-get purge -y nodejs + rm -f /etc/apt/sources.list.d/nodesource.list /usr/share/keyrings/nodesource.gpg + + mkdir -p /usr/share/keyrings + curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | + gpg --dearmor -o /usr/share/keyrings/nodesource.gpg || { + msg_error "Failed to import NodeSource GPG key" + exit 1 + } + chmod 644 /usr/share/keyrings/nodesource.gpg + + local ARCH + ARCH=$(dpkg --print-architecture) + if ! [[ "$ARCH" =~ ^(amd64|arm64|armhf)$ ]]; then + msg_error "Unsupported architecture: $ARCH" + exit 1 + fi + + echo "deb [arch=$ARCH signed-by=/usr/share/keyrings/nodesource.gpg] https://deb.nodesource.com/node_${NODE_VERSION}.x nodistro main" >/etc/apt/sources.list.d/nodesource.list + + cat </etc/apt/preferences.d/nodejs +Package: nodejs +Pin: origin deb.nodesource.com +Pin-Priority: 700 +EOF + + sleep 2 + if ! apt-get update >/dev/null 2>&1; then + msg_warn "APT update failed – retrying in 5s" + sleep 5 + if ! apt-get update >/dev/null 2>&1; then + msg_error "Failed to update APT repositories after adding NodeSource" + exit 1 + fi + fi + + if ! apt-get install -y -t nodistro nodejs >/dev/null 2>&1; then + msg_error "Failed to install Node.js ${NODE_VERSION} from NodeSource" + apt-cache policy nodejs | tee "$STD" + exit 1 + fi + + $STD npm install -g npm@latest || { + msg_error "Failed to update npm to latest version" + } + msg_ok "Setup Node.js ${NODE_VERSION}" + fi + + export NODE_OPTIONS="--max-old-space-size=4096" + + [[ -d /opt ]] || mkdir -p /opt + cd /opt || { + msg_error "Failed to set safe working directory before npm install" + exit 1 + } + + if [[ -n "$NODE_MODULE" ]]; then + IFS=',' read -ra MODULES <<<"$NODE_MODULE" + for mod in "${MODULES[@]}"; do + local MODULE_NAME MODULE_REQ_VERSION MODULE_INSTALLED_VERSION + if [[ "$mod" == @*/*@* ]]; then + MODULE_NAME="${mod%@*}" + MODULE_REQ_VERSION="${mod##*@}" + elif [[ "$mod" == *"@"* ]]; then + MODULE_NAME="${mod%@*}" + MODULE_REQ_VERSION="${mod##*@}" + else + MODULE_NAME="$mod" + MODULE_REQ_VERSION="latest" + fi + + if npm list -g --depth=0 "$MODULE_NAME" >/dev/null 2>&1; then + MODULE_INSTALLED_VERSION="$(npm list -g --depth=0 "$MODULE_NAME" | grep "$MODULE_NAME@" | awk -F@ '{print $2}' | tr -d '[:space:]')" + if [[ "$MODULE_REQ_VERSION" != "latest" && "$MODULE_REQ_VERSION" != "$MODULE_INSTALLED_VERSION" ]]; then + msg_info "Updating $MODULE_NAME from v$MODULE_INSTALLED_VERSION to v$MODULE_REQ_VERSION" + $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}" || { + msg_error "Failed to update $MODULE_NAME to version $MODULE_REQ_VERSION" + exit 1 + } + elif [[ "$MODULE_REQ_VERSION" == "latest" ]]; then + msg_info "Updating $MODULE_NAME to latest version" + $STD npm install -g "${MODULE_NAME}@latest" || { + msg_error "Failed to update $MODULE_NAME to latest version" + exit 1 + } + fi + else + msg_info "Installing $MODULE_NAME@$MODULE_REQ_VERSION" + $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}" || { + msg_error "Failed to install $MODULE_NAME@$MODULE_REQ_VERSION" + exit 1 + } + fi + done + msg_ok "Installed Node.js modules: $NODE_MODULE" + fi +} + +# ------------------------------------------------------------------------------ +# Installs or upgrades PostgreSQL and optional extensions/modules. +# +# Description: +# - Detects existing PostgreSQL version +# - Dumps all databases before upgrade +# - Adds PGDG repo and installs specified version +# - Installs optional PG_MODULES (e.g. postgis, contrib) +# - Restores dumped data post-upgrade +# +# Variables: +# PG_VERSION - Major PostgreSQL version (e.g. 15, 16) (default: 16) +# PG_MODULES - Comma-separated list of extensions (e.g. "postgis,contrib") +# ------------------------------------------------------------------------------ +function setup_postgresql() { + local PG_VERSION="${PG_VERSION:-16}" + local PG_MODULES="${PG_MODULES:-}" + local CURRENT_PG_VERSION="" + local DISTRO + local NEED_PG_INSTALL=false + DISTRO="$(awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release)" + + if command -v psql >/dev/null; then + CURRENT_PG_VERSION="$(psql -V | awk '{print $3}' | cut -d. -f1)" + if [[ "$CURRENT_PG_VERSION" == "$PG_VERSION" ]]; then + : # PostgreSQL is already at the desired version – no action needed + else + $STD msg_info "Detected PostgreSQL $CURRENT_PG_VERSION, preparing upgrade to $PG_VERSION" + NEED_PG_INSTALL=true + fi + else + NEED_PG_INSTALL=true + fi + + if [[ "$NEED_PG_INSTALL" == true ]]; then + if [[ -n "$CURRENT_PG_VERSION" ]]; then + $STD msg_info "Dumping PostgreSQL $CURRENT_PG_VERSION data" + su - postgres -c "pg_dumpall > /var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql" + $STD msg_ok "Data dump completed" + + systemctl stop postgresql + fi + + rm -f /etc/apt/sources.list.d/pgdg.list /etc/apt/trusted.gpg.d/postgresql.gpg + + $STD msg_info "Adding PostgreSQL PGDG repository" + curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | + gpg --dearmor -o /etc/apt/trusted.gpg.d/postgresql.gpg + + echo "deb https://apt.postgresql.org/pub/repos/apt ${DISTRO}-pgdg main" \ + >/etc/apt/sources.list.d/pgdg.list + $STD apt-get update + $STD msg_ok "Repository added" + + msg_info "Setup PostgreSQL $PG_VERSION" + $STD apt-get install -y "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}" + + if [[ -n "$CURRENT_PG_VERSION" ]]; then + $STD apt-get purge -y "postgresql-${CURRENT_PG_VERSION}" "postgresql-client-${CURRENT_PG_VERSION}" || true + fi + systemctl enable -q --now postgresql + + if [[ -n "$CURRENT_PG_VERSION" ]]; then + $STD msg_info "Restoring dumped data" + su - postgres -c "psql < /var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql" + $STD msg_ok "Data restored" + fi + + $STD msg_ok "PostgreSQL $PG_VERSION installed" + fi + + # Install optional PostgreSQL modules + if [[ -n "$PG_MODULES" ]]; then + IFS=',' read -ra MODULES <<<"$PG_MODULES" + for module in "${MODULES[@]}"; do + local pkg="postgresql-${PG_VERSION}-${module}" + $STD msg_info "Setup PostgreSQL module/s: $pkg" + $STD apt-get install -y "$pkg" || { + msg_error "Failed to install $pkg" + continue + } + done + $STD msg_ok "Setup PostgreSQL modules" + fi +} + +# ------------------------------------------------------------------------------ +# Installs or updates MariaDB from official repo. +# +# Description: +# - Detects current MariaDB version and replaces it if necessary +# - Preserves existing database data +# - Dynamically determines latest GA version if "latest" is given +# +# Variables: +# MARIADB_VERSION - MariaDB version to install (e.g. 10.11, latest) (default: latest) +# ------------------------------------------------------------------------------ test + +setup_mariadb() { + local MARIADB_VERSION="${MARIADB_VERSION:-latest}" + local DISTRO_CODENAME + DISTRO_CODENAME="$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release)" + CURRENT_OS="$(awk -F= '/^ID=/{print $2}' /etc/os-release)" + + if ! curl -fsI http://mirror.mariadb.org/repo/ >/dev/null; then + msg_error "MariaDB mirror not reachable" + return 1 + fi + + msg_info "Setting up MariaDB $MARIADB_VERSION" + # Grab dynamic latest LTS version + if [[ "$MARIADB_VERSION" == "latest" ]]; then + MARIADB_VERSION=$(curl -fsSL http://mirror.mariadb.org/repo/ | + grep -Eo '[0-9]+\.[0-9]+\.[0-9]+/' | + grep -vE 'rc/|rolling/' | + sed 's|/||' | + sort -Vr | + head -n1) + if [[ -z "$MARIADB_VERSION" ]]; then + msg_error "Could not determine latest GA MariaDB version" + return 1 + fi + fi + + local CURRENT_VERSION="" + if command -v mariadb >/dev/null; then + CURRENT_VERSION=$(mariadb --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+') + fi + + if [[ "$CURRENT_VERSION" == "$MARIADB_VERSION" ]]; then + $STD msg_info "MariaDB $MARIADB_VERSION, upgrading" + $STD apt-get update + $STD apt-get install --only-upgrade -y mariadb-server mariadb-client + $STD msg_ok "MariaDB upgraded to $MARIADB_VERSION" + return 0 + fi + + if [[ -n "$CURRENT_VERSION" ]]; then + $STD msg_info "Upgrading MariaDB $CURRENT_VERSION to $MARIADB_VERSION" + $STD systemctl stop mariadb >/dev/null 2>&1 || true + $STD apt-get purge -y 'mariadb*' || true + rm -f /etc/apt/sources.list.d/mariadb.list /etc/apt/trusted.gpg.d/mariadb.gpg + else + $STD msg_info "Setup MariaDB $MARIADB_VERSION" + fi + + curl -fsSL "https://mariadb.org/mariadb_release_signing_key.asc" | + gpg --dearmor -o /etc/apt/trusted.gpg.d/mariadb.gpg + + echo "deb [signed-by=/etc/apt/trusted.gpg.d/mariadb.gpg] http://mirror.mariadb.org/repo/${MARIADB_VERSION}/${CURRENT_OS} ${DISTRO_CODENAME} main" \ + >/etc/apt/sources.list.d/mariadb.list + + $STD apt-get update + + local MARIADB_MAJOR_MINOR + MARIADB_MAJOR_MINOR=$(echo "$MARIADB_VERSION" | awk -F. '{print $1"."$2}') + if [[ -n "$MARIADB_MAJOR_MINOR" ]]; then + echo "mariadb-server-$MARIADB_MAJOR_MINOR mariadb-server/feedback boolean false" | debconf-set-selections + else + for ver in 12.1 12.0 11.4 11.3 11.2 11.1 11.0 10.11 10.6 10.5 10.4 10.3; do + echo "mariadb-server-$ver mariadb-server/feedback boolean false" | debconf-set-selections + done + fi + DEBIAN_FRONTEND=noninteractive $STD apt-get install -y mariadb-server mariadb-client || { + msg_warn "Failed to install MariaDB ${MARIADB_VERSION} from upstream repo – trying distro package as fallback..." + # Cleanup, remove upstream repo to avoid conflicts + rm -f /etc/apt/sources.list.d/mariadb.list /etc/apt/trusted.gpg.d/mariadb.gpg + $STD apt-get update + # Final fallback: distro package + DEBIAN_FRONTEND=noninteractive $STD apt-get install -y mariadb-server mariadb-client || { + msg_error "MariaDB installation failed even with distro fallback!" + return 1 + } + msg_ok "Setup MariaDB (distro fallback)" + return 0 + } + + msg_ok "Setup MariaDB $MARIADB_VERSION" +} + +# ------------------------------------------------------------------------------ +# Installs or upgrades MySQL and configures APT repo. +# +# Description: +# - Detects existing MySQL installation +# - Purges conflicting packages before installation +# - Supports clean upgrade +# +# Variables: +# MYSQL_VERSION - MySQL version to install (e.g. 5.7, 8.0) (default: 8.0) +# ------------------------------------------------------------------------------ + +function setup_mysql() { + local MYSQL_VERSION="${MYSQL_VERSION:-8.0}" + local CURRENT_VERSION="" + local NEED_INSTALL=false + CURRENT_OS="$(awk -F= '/^ID=/{print $2}' /etc/os-release)" + + if command -v mysql >/dev/null; then + CURRENT_VERSION="$(mysql --version | grep -oP 'Distrib\s+\K[0-9]+\.[0-9]+')" + if [[ "$CURRENT_VERSION" != "$MYSQL_VERSION" ]]; then + $STD msg_info "MySQL $CURRENT_VERSION will be upgraded to $MYSQL_VERSION" + NEED_INSTALL=true + else + # Check for patch-level updates + if apt list --upgradable 2>/dev/null | grep -q '^mysql-server/'; then + $STD msg_info "MySQL $CURRENT_VERSION available for upgrade" + $STD apt-get update + $STD apt-get install --only-upgrade -y mysql-server + $STD msg_ok "MySQL upgraded" + fi + return + fi + else + msg_info "Setup MySQL $MYSQL_VERSION" + NEED_INSTALL=true + fi + + if [[ "$NEED_INSTALL" == true ]]; then + $STD systemctl stop mysql || true + $STD apt-get purge -y "^mysql-server.*" "^mysql-client.*" "^mysql-common.*" || true + rm -f /etc/apt/sources.list.d/mysql.list /etc/apt/trusted.gpg.d/mysql.gpg + + local DISTRO_CODENAME + DISTRO_CODENAME="$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release)" + curl -fsSL https://repo.mysql.com/RPM-GPG-KEY-mysql-2023 | gpg --dearmor -o /etc/apt/trusted.gpg.d/mysql.gpg + echo "deb [signed-by=/etc/apt/trusted.gpg.d/mysql.gpg] https://repo.mysql.com/apt/${CURRENT_OS}/ ${DISTRO_CODENAME} mysql-${MYSQL_VERSION}" \ + >/etc/apt/sources.list.d/mysql.list + + export DEBIAN_FRONTEND=noninteractive + $STD apt-get update + $STD apt-get install -y mysql-server + msg_ok "Setup MySQL $MYSQL_VERSION" + fi +} + +# ------------------------------------------------------------------------------ +# Installs PHP with selected modules and configures Apache/FPM support. +# +# Description: +# - Adds Sury PHP repo if needed +# - Installs default and user-defined modules +# - Patches php.ini for CLI, Apache, and FPM as needed +# +# Variables: +# PHP_VERSION - PHP version to install (default: 8.4) +# PHP_MODULE - Additional comma-separated modules +# PHP_APACHE - Set YES to enable PHP with Apache +# PHP_FPM - Set YES to enable PHP-FPM +# PHP_MEMORY_LIMIT - (default: 512M) +# PHP_UPLOAD_MAX_FILESIZE - (default: 128M) +# PHP_POST_MAX_SIZE - (default: 128M) +# PHP_MAX_EXECUTION_TIME - (default: 300) +# ------------------------------------------------------------------------------ + +function setup_php() { + local PHP_VERSION="${PHP_VERSION:-8.4}" + local PHP_MODULE="${PHP_MODULE:-}" + local PHP_APACHE="${PHP_APACHE:-NO}" + local PHP_FPM="${PHP_FPM:-NO}" + local DISTRO_CODENAME + DISTRO_CODENAME=$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release) + + local DEFAULT_MODULES="bcmath,cli,curl,gd,intl,mbstring,opcache,readline,xml,zip" + local COMBINED_MODULES + + local PHP_MEMORY_LIMIT="${PHP_MEMORY_LIMIT:-512M}" + local PHP_UPLOAD_MAX_FILESIZE="${PHP_UPLOAD_MAX_FILESIZE:-128M}" + local PHP_POST_MAX_SIZE="${PHP_POST_MAX_SIZE:-128M}" + local PHP_MAX_EXECUTION_TIME="${PHP_MAX_EXECUTION_TIME:-300}" + + # Merge default + user-defined modules + if [[ -n "$PHP_MODULE" ]]; then + COMBINED_MODULES="${DEFAULT_MODULES},${PHP_MODULE}" + else + COMBINED_MODULES="${DEFAULT_MODULES}" + fi + + # Deduplicate + COMBINED_MODULES=$(echo "$COMBINED_MODULES" | tr ',' '\n' | awk '!seen[$0]++' | paste -sd, -) + + # Get current PHP-CLI version + local CURRENT_PHP="" + if command -v php >/dev/null 2>&1; then + CURRENT_PHP=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2) + fi + + if [[ -z "$CURRENT_PHP" ]]; then + msg_info "Setup PHP $PHP_VERSION" + elif [[ "$CURRENT_PHP" != "$PHP_VERSION" ]]; then + msg_info "Old PHP $CURRENT_PHP detected, Setup new PHP $PHP_VERSION" + $STD apt-get purge -y "php${CURRENT_PHP//./}"* || true + fi + + # Ensure Sury repo is available + if [[ ! -f /etc/apt/sources.list.d/php.list ]]; then + $STD curl -fsSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb + $STD dpkg -i /tmp/debsuryorg-archive-keyring.deb + echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ ${DISTRO_CODENAME} main" \ + >/etc/apt/sources.list.d/php.list + $STD apt-get update + fi + + # Build module list + local MODULE_LIST="php${PHP_VERSION}" + IFS=',' read -ra MODULES <<<"$COMBINED_MODULES" + for mod in "${MODULES[@]}"; do + if apt-cache show "php${PHP_VERSION}-${mod}" >/dev/null 2>&1; then + MODULE_LIST+=" php${PHP_VERSION}-${mod}" + else + msg_warn "PHP-Module ${mod} for PHP ${PHP_VERSION} not found – skipping" + fi + done + if [[ "$PHP_FPM" == "YES" ]]; then + MODULE_LIST+=" php${PHP_VERSION}-fpm" + fi + + # install apache2 with PHP support if requested + if [[ "$PHP_APACHE" == "YES" ]]; then + if ! dpkg -l | grep -q "libapache2-mod-php${PHP_VERSION}"; then + msg_info "Installing Apache with PHP${PHP_VERSION} support" + $STD apt-get install -y apache2 libapache2-mod-php${PHP_VERSION} + else + msg_info "Apache with PHP${PHP_VERSION} already installed – skipping install" + fi + fi + + # setup / update PHP modules + $STD apt-get install -y $MODULE_LIST + msg_ok "Setup PHP $PHP_VERSION" + + # optional stop old PHP-FPM service + if [[ "$PHP_FPM" == "YES" && -n "$CURRENT_PHP" && "$CURRENT_PHP" != "$PHP_VERSION" ]]; then + $STD systemctl stop php"${CURRENT_PHP}"-fpm || true + $STD systemctl disable php"${CURRENT_PHP}"-fpm || true + fi + + # Patch all relevant php.ini files + local PHP_INI_PATHS=("/etc/php/${PHP_VERSION}/cli/php.ini") + [[ "$PHP_FPM" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/fpm/php.ini") + [[ "$PHP_APACHE" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/apache2/php.ini") + for ini in "${PHP_INI_PATHS[@]}"; do + if [[ -f "$ini" ]]; then + $STD msg_info "Patching $ini" + sed -i "s|^memory_limit = .*|memory_limit = ${PHP_MEMORY_LIMIT}|" "$ini" + sed -i "s|^upload_max_filesize = .*|upload_max_filesize = ${PHP_UPLOAD_MAX_FILESIZE}|" "$ini" + sed -i "s|^post_max_size = .*|post_max_size = ${PHP_POST_MAX_SIZE}|" "$ini" + sed -i "s|^max_execution_time = .*|max_execution_time = ${PHP_MAX_EXECUTION_TIME}|" "$ini" + $STD msg_ok "Patched $ini" + fi + done + + # patch Apache configuration if needed + if [[ "$PHP_APACHE" == "YES" ]]; then + for mod in $(ls /etc/apache2/mods-enabled/ 2>/dev/null | grep -E '^php[0-9]\.[0-9]\.conf$' | sed 's/\.conf//'); do + if [[ "$mod" != "php${PHP_VERSION}" ]]; then + $STD a2dismod "$mod" || true + fi + done + $STD a2enmod mpm_prefork + $STD a2enmod "php${PHP_VERSION}" + $STD systemctl restart apache2 || true + fi + + # enable and restart PHP-FPM if requested + if [[ "$PHP_FPM" == "YES" ]]; then + if systemctl list-unit-files | grep -q "php${PHP_VERSION}-fpm.service"; then + $STD systemctl enable php${PHP_VERSION}-fpm + $STD systemctl restart php${PHP_VERSION}-fpm + else + msg_warn "FPM requested but service php${PHP_VERSION}-fpm not found" + fi + fi +} + +# ------------------------------------------------------------------------------ +# Installs or updates Composer globally. +# +# Description: +# - Downloads latest version from getcomposer.org +# - Installs to /usr/local/bin/composer +# ------------------------------------------------------------------------------ + +function setup_composer() { + local COMPOSER_BIN="/usr/local/bin/composer" + export COMPOSER_ALLOW_SUPERUSER=1 + + # Check if composer is already installed + if [[ -x "$COMPOSER_BIN" ]]; then + local CURRENT_VERSION + CURRENT_VERSION=$("$COMPOSER_BIN" --version | awk '{print $3}') + $STD msg_info "Old Composer $CURRENT_VERSION found, updating to latest" + else + msg_info "Setup Composer" + fi + + # Download and install latest composer + curl -fsSL https://getcomposer.org/installer -o /tmp/composer-setup.php + php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer >/dev/null 2>&1 + + if [[ $? -ne 0 ]]; then + msg_error "Failed to install Composer" + return 1 + fi + + chmod +x "$COMPOSER_BIN" + $STD composer diagnose + msg_ok "Setup Composer" +} + +# ------------------------------------------------------------------------------ +# Installs Go (Golang) from official tarball. +# +# Description: +# - Determines system architecture +# - Downloads latest version if GO_VERSION not set +# +# Variables: +# GO_VERSION - Version to install (e.g. 1.22.2 or latest) +# ------------------------------------------------------------------------------ + +function setup_go() { + local ARCH + case "$(uname -m)" in + x86_64) ARCH="amd64" ;; + aarch64) ARCH="arm64" ;; + *) + msg_error "Unsupported architecture: $(uname -m)" + return 1 + ;; + esac + + # Determine version + if [[ -z "${GO_VERSION:-}" || "${GO_VERSION}" == "latest" ]]; then + GO_VERSION=$(curl -fsSL https://go.dev/VERSION?m=text | head -n1 | sed 's/^go//') + if [[ -z "$GO_VERSION" ]]; then + msg_error "Could not determine latest Go version" + return 1 + fi + fi + + local GO_BIN="/usr/local/bin/go" + local GO_INSTALL_DIR="/usr/local/go" + + if [[ -x "$GO_BIN" ]]; then + local CURRENT_VERSION + CURRENT_VERSION=$("$GO_BIN" version | awk '{print $3}' | sed 's/go//') + if [[ "$CURRENT_VERSION" == "$GO_VERSION" ]]; then + return 0 + else + $STD msg_info "Old Go Installation ($CURRENT_VERSION) found, upgrading to $GO_VERSION" + rm -rf "$GO_INSTALL_DIR" + fi + else + msg_info "Setup Go $GO_VERSION" + fi + + local TARBALL="go${GO_VERSION}.linux-${ARCH}.tar.gz" + local URL="https://go.dev/dl/${TARBALL}" + local TMP_TAR=$(mktemp) + + curl -fsSL "$URL" -o "$TMP_TAR" || { + msg_error "Failed to download $TARBALL" + return 1 + } + + tar -C /usr/local -xzf "$TMP_TAR" + ln -sf /usr/local/go/bin/go /usr/local/bin/go + ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt + rm -f "$TMP_TAR" + + msg_ok "Setup Go $GO_VERSION" +} + +# ------------------------------------------------------------------------------ +# Installs Temurin JDK via Adoptium APT repository. +# +# Description: +# - Removes previous JDK if version mismatch +# - Installs or upgrades to specified JAVA_VERSION +# +# Variables: +# JAVA_VERSION - Temurin JDK version to install (e.g. 17, 21) +# ------------------------------------------------------------------------------ + +function setup_java() { + local JAVA_VERSION="${JAVA_VERSION:-21}" + local DISTRO_CODENAME + DISTRO_CODENAME=$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release) + local DESIRED_PACKAGE="temurin-${JAVA_VERSION}-jdk" + + # Add Adoptium repo if missing + if [[ ! -f /etc/apt/sources.list.d/adoptium.list ]]; then + $STD msg_info "Setting up Adoptium Repository" + mkdir -p /etc/apt/keyrings + curl -fsSL "https://packages.adoptium.net/artifactory/api/gpg/key/public" | gpg --dearmor -o /etc/apt/trusted.gpg.d/adoptium.gpg + echo "deb [signed-by=/etc/apt/trusted.gpg.d/adoptium.gpg] https://packages.adoptium.net/artifactory/deb ${DISTRO_CODENAME} main" \ + >/etc/apt/sources.list.d/adoptium.list + $STD apt-get update + $STD msg_ok "Set up Adoptium Repository" + fi + + # Detect currently installed temurin version + local INSTALLED_VERSION="" + if dpkg -l | grep -q "temurin-.*-jdk"; then + INSTALLED_VERSION=$(dpkg -l | awk '/temurin-.*-jdk/{print $2}' | grep -oP 'temurin-\K[0-9]+') + fi + + if [[ "$INSTALLED_VERSION" == "$JAVA_VERSION" ]]; then + $STD msg_info "Upgrading Temurin JDK $JAVA_VERSION" + $STD apt-get update + $STD apt-get install --only-upgrade -y "$DESIRED_PACKAGE" + $STD msg_ok "Upgraded Temurin JDK $JAVA_VERSION" + else + if [[ -n "$INSTALLED_VERSION" ]]; then + $STD msg_info "Removing Temurin JDK $INSTALLED_VERSION" + $STD apt-get purge -y "temurin-${INSTALLED_VERSION}-jdk" + fi + + msg_info "Setup Temurin JDK $JAVA_VERSION" + $STD apt-get install -y "$DESIRED_PACKAGE" + msg_ok "Setup Temurin JDK $JAVA_VERSION" + fi +} + +# ------------------------------------------------------------------------------ +# Installs or updates MongoDB to specified major version. +# +# Description: +# - Preserves data across installations +# - Adds official MongoDB repo +# +# Variables: +# MONGO_VERSION - MongoDB major version to install (e.g. 7.0, 8.0) +# ------------------------------------------------------------------------------ + +function setup_mongodb() { + local MONGO_VERSION="${MONGO_VERSION:-8.0}" + local DISTRO_ID DISTRO_CODENAME MONGO_BASE_URL + DISTRO_ID=$(awk -F= '/^ID=/{ gsub(/"/,"",$2); print $2 }' /etc/os-release) + DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{ print $2 }' /etc/os-release) + + # Check AVX support + if ! grep -qm1 'avx[^ ]*' /proc/cpuinfo; then + local major="${MONGO_VERSION%%.*}" + if ((major > 5)); then + msg_error "MongoDB ${MONGO_VERSION} requires AVX support, which is not available on this system." + return 1 + fi + fi + + case "$DISTRO_ID" in + ubuntu) + MONGO_BASE_URL="https://repo.mongodb.org/apt/ubuntu" + REPO_COMPONENT="multiverse" + ;; + debian) + MONGO_BASE_URL="https://repo.mongodb.org/apt/debian" + REPO_COMPONENT="main" + ;; + *) + msg_error "Unsupported distribution: $DISTRO_ID" + return 1 + ;; + esac + + local REPO_LIST="/etc/apt/sources.list.d/mongodb-org-${MONGO_VERSION}.list" + + local INSTALLED_VERSION="" + if command -v mongod >/dev/null; then + INSTALLED_VERSION=$(mongod --version | awk '/db version/{print $3}' | cut -d. -f1,2) + fi + + if [[ "$INSTALLED_VERSION" == "$MONGO_VERSION" ]]; then + $STD msg_info "Upgrading MongoDB $MONGO_VERSION" + $STD apt-get update + $STD apt-get install --only-upgrade -y mongodb-org + $STD msg_ok "Upgraded MongoDB $MONGO_VERSION" + return 0 + fi + + if [[ -n "$INSTALLED_VERSION" ]]; then + $STD systemctl stop mongod || true + $STD apt-get purge -y mongodb-org || true + rm -f /etc/apt/sources.list.d/mongodb-org-*.list + rm -f /etc/apt/trusted.gpg.d/mongodb-*.gpg + else + msg_info "Setup MongoDB $MONGO_VERSION" + fi + + curl -fsSL "https://pgp.mongodb.com/server-${MONGO_VERSION}.asc" | gpg --dearmor -o "/etc/apt/trusted.gpg.d/mongodb-${MONGO_VERSION}.gpg" + echo "deb [signed-by=/etc/apt/trusted.gpg.d/mongodb-${MONGO_VERSION}.gpg] ${MONGO_BASE_URL} ${DISTRO_CODENAME}/mongodb-org/${MONGO_VERSION} main" \ + >"$REPO_LIST" + + $STD apt-get update || { + msg_error "APT update failed — invalid MongoDB repo for ${DISTRO_ID}-${DISTRO_CODENAME}?" + return 1 + } + + $STD apt-get install -y mongodb-org + + mkdir -p /var/lib/mongodb + chown -R mongodb:mongodb /var/lib/mongodb + + $STD systemctl enable mongod + $STD systemctl start mongod + msg_ok "Setup MongoDB $MONGO_VERSION" +} + +# ------------------------------------------------------------------------------ +# Downloads and deploys latest GitHub release (source, binary, tarball, asset). +# +# Description: +# - Fetches latest release metadata from GitHub API +# - Supports the following modes: +# - tarball: Source code tarball (default if omitted) +# - source: Alias for tarball (same behavior) +# - binary: .deb package install (arch-dependent) +# - prebuild: Prebuilt .tar.gz archive (e.g. Go binaries) +# - singlefile: Standalone binary (no archive, direct chmod +x install) +# - Handles download, extraction/installation and version tracking in ~/. +# +# Parameters: +# $1 APP - Application name (used for install path and version file) +# $2 REPO - GitHub repository in form user/repo +# $3 MODE - Release type: +# tarball → source tarball (.tar.gz) +# binary → .deb file (auto-arch matched) +# prebuild → prebuilt archive (e.g. tar.gz) +# singlefile→ standalone binary (chmod +x) +# $4 VERSION - Optional release tag (default: latest) +# $5 TARGET_DIR - Optional install path (default: /opt/) +# $6 ASSET_FILENAME - Required for: +# - prebuild → archive filename or pattern +# - singlefile→ binary filename or pattern +# +# Optional: +# - Set GITHUB_TOKEN env var to increase API rate limit (recommended for CI/CD). +# +# Examples: +# # 1. Minimal: Fetch and deploy source tarball +# fetch_and_deploy_gh_release "myapp" "myuser/myapp" +# +# # 2. Binary install via .deb asset (architecture auto-detected) +# fetch_and_deploy_gh_release "myapp" "myuser/myapp" "binary" +# +# # 3. Prebuilt archive (.tar.gz) with asset filename match +# fetch_and_deploy_gh_release "hanko" "teamhanko/hanko" "prebuild" "latest" "/opt/hanko" "hanko_Linux_x86_64.tar.gz" +# +# # 4. Single binary (chmod +x) like Argus, Promtail etc. +# fetch_and_deploy_gh_release "argus" "release-argus/Argus" "singlefile" "0.26.3" "/opt/argus" "Argus-.*linux-amd64" +# ------------------------------------------------------------------------------ + +function fetch_and_deploy_gh_release() { + local app="$1" + local repo="$2" + local mode="${3:-tarball}" # tarball | binary | prebuild | singlefile + local version="${4:-latest}" + local target="${5:-/opt/$app}" + local asset_pattern="${6:-}" + + local app_lc=$(echo "${app,,}" | tr -d ' ') + local version_file="$HOME/.${app_lc}" + + local api_timeout="--connect-timeout 10 --max-time 60" + local download_timeout="--connect-timeout 15 --max-time 900" + + local current_version="" + [[ -f "$version_file" ]] && current_version=$(<"$version_file") + + if ! command -v jq &>/dev/null; then + $STD apt-get install -y jq &>/dev/null + fi + + local api_url="https://api.github.com/repos/$repo/releases" + [[ "$version" != "latest" ]] && api_url="$api_url/tags/$version" || api_url="$api_url/latest" + local header=() + [[ -n "${GITHUB_TOKEN:-}" ]] && header=(-H "Authorization: token $GITHUB_TOKEN") + + # dns pre check + local gh_host + gh_host=$(awk -F/ '{print $3}' <<<"$api_url") + if ! getent hosts "$gh_host" &>/dev/null; then + msg_error "DNS resolution failed for $gh_host – check /etc/resolv.conf or networking" + return 1 + fi + + local max_retries=3 retry_delay=2 attempt=1 success=false resp http_code + + while ((attempt <= max_retries)); do + resp=$(curl $api_timeout -fsSL -w "%{http_code}" -o /tmp/gh_rel.json "${header[@]}" "$api_url") && success=true && break + sleep "$retry_delay" + ((attempt++)) + done + + if ! $success; then + msg_error "Failed to fetch release metadata from $api_url after $max_retries attempts" + return 1 + fi + + http_code="${resp:(-3)}" + [[ "$http_code" != "200" ]] && { + msg_error "GitHub API returned HTTP $http_code" + return 1 + } + + local json tag_name + json=$(/dev/null || uname -m) + [[ "$arch" == "x86_64" ]] && arch="amd64" + [[ "$arch" == "aarch64" ]] && arch="arm64" + + local assets url_match="" + assets=$(echo "$json" | jq -r '.assets[].browser_download_url') + + # If explicit filename pattern is provided (param $6), match that first + if [[ -n "$asset_pattern" ]]; then + for u in $assets; do + case "${u##*/}" in + $asset_pattern) + url_match="$u" + break + ;; + esac + done + fi + + # If no match via explicit pattern, fall back to architecture heuristic + if [[ -z "$url_match" ]]; then + for u in $assets; do + if [[ "$u" =~ ($arch|amd64|x86_64|aarch64|arm64).*\.deb$ ]]; then + url_match="$u" + break + fi + done + fi + + # Fallback: any .deb file + if [[ -z "$url_match" ]]; then + for u in $assets; do + [[ "$u" =~ \.deb$ ]] && url_match="$u" && break + done + fi + + if [[ -z "$url_match" ]]; then + msg_error "No suitable .deb asset found for $app" + rm -rf "$tmpdir" + return 1 + fi + + filename="${url_match##*/}" + curl $download_timeout -fsSL -o "$tmpdir/$filename" "$url_match" || { + msg_error "Download failed: $url_match" + rm -rf "$tmpdir" + return 1 + } + + chmod 644 "$tmpdir/$filename" + $STD apt-get install -y "$tmpdir/$filename" || { + $STD dpkg -i "$tmpdir/$filename" || { + msg_error "Both apt and dpkg installation failed" + rm -rf "$tmpdir" + return 1 + } + } + + ### Prebuild Mode ### + elif [[ "$mode" == "prebuild" ]]; then + local pattern="${6%\"}" + pattern="${pattern#\"}" + [[ -z "$pattern" ]] && { + msg_error "Mode 'prebuild' requires 6th parameter (asset filename pattern)" + rm -rf "$tmpdir" + return 1 + } + + local asset_url="" + for u in $(echo "$json" | jq -r '.assets[].browser_download_url'); do + filename_candidate="${u##*/}" + case "$filename_candidate" in + $pattern) + asset_url="$u" + break + ;; + esac + done + + [[ -z "$asset_url" ]] && { + msg_error "No asset matching '$pattern' found" + rm -rf "$tmpdir" + return 1 + } + + filename="${asset_url##*/}" + curl $download_timeout -fsSL -o "$tmpdir/$filename" "$asset_url" || { + msg_error "Download failed: $asset_url" + rm -rf "$tmpdir" + return 1 + } + + local unpack_tmp + unpack_tmp=$(mktemp -d) + mkdir -p "$target" + if [[ "${CLEAN_INSTALL:-0}" == "1" ]]; then + rm -rf "${target:?}/"* + fi + + if [[ "$filename" == *.zip ]]; then + if ! command -v unzip &>/dev/null; then + $STD apt-get install -y unzip + fi + unzip -q "$tmpdir/$filename" -d "$unpack_tmp" + elif [[ "$filename" == *.tar.* || "$filename" == *.tgz ]]; then + tar -xf "$tmpdir/$filename" -C "$unpack_tmp" + else + msg_error "Unsupported archive format: $filename" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + fi + + local top_dirs + top_dirs=$(find "$unpack_tmp" -mindepth 1 -maxdepth 1 -type d | wc -l) + local top_entries inner_dir + top_entries=$(find "$unpack_tmp" -mindepth 1 -maxdepth 1) + if [[ "$(echo "$top_entries" | wc -l)" -eq 1 && -d "$top_entries" ]]; then + # Strip leading folder + inner_dir="$top_entries" + shopt -s dotglob nullglob + if compgen -G "$inner_dir/*" >/dev/null; then + cp -r "$inner_dir"/* "$target/" || { + msg_error "Failed to copy contents from $inner_dir to $target" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + } + else + msg_error "Inner directory is empty: $inner_dir" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + fi + shopt -u dotglob nullglob + else + # Copy all contents + shopt -s dotglob nullglob + if compgen -G "$unpack_tmp/*" >/dev/null; then + cp -r "$unpack_tmp"/* "$target/" || { + msg_error "Failed to copy contents to $target" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + } + else + msg_error "Unpacked archive is empty" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + fi + shopt -u dotglob nullglob + fi + + ### Singlefile Mode ### + elif [[ "$mode" == "singlefile" ]]; then + local pattern="${6%\"}" + pattern="${pattern#\"}" + [[ -z "$pattern" ]] && { + msg_error "Mode 'singlefile' requires 6th parameter (asset filename pattern)" + rm -rf "$tmpdir" + return 1 + } + + local asset_url="" + for u in $(echo "$json" | jq -r '.assets[].browser_download_url'); do + filename_candidate="${u##*/}" + case "$filename_candidate" in + $pattern) + asset_url="$u" + break + ;; + esac + done + + [[ -z "$asset_url" ]] && { + msg_error "No asset matching '$pattern' found" + rm -rf "$tmpdir" + return 1 + } + + filename="${asset_url##*/}" + mkdir -p "$target" + + local use_filename="${USE_ORIGINAL_FILENAME:-false}" + local target_file="$app" + [[ "$use_filename" == "true" ]] && target_file="$filename" + + curl $download_timeout -fsSL -o "$target/$target_file" "$asset_url" || { + msg_error "Download failed: $asset_url" + rm -rf "$tmpdir" + return 1 + } + + if [[ "$target_file" != *.jar && -f "$target/$target_file" ]]; then + chmod +x "$target/$target_file" + fi + + else + msg_error "Unknown mode: $mode" + rm -rf "$tmpdir" + return 1 + fi + + echo "$version" >"$version_file" + msg_ok "Deployed: $app ($version)" + rm -rf "$tmpdir" +} + +# ------------------------------------------------------------------------------ +# Installs a local IP updater script using networkd-dispatcher. +# +# Description: +# - Stores current IP in /run/local-ip.env +# - Automatically runs on network changes +# ------------------------------------------------------------------------------ + +function setup_local_ip_helper() { + local BASE_DIR="/usr/local/community-scripts/ip-management" + local SCRIPT_PATH="$BASE_DIR/update_local_ip.sh" + local IP_FILE="/run/local-ip.env" + local DISPATCHER_SCRIPT="/etc/networkd-dispatcher/routable.d/10-update-local-ip.sh" + + mkdir -p "$BASE_DIR" + + # Install networkd-dispatcher if not present + if ! dpkg -s networkd-dispatcher >/dev/null 2>&1; then + $STD apt-get update + $STD apt-get install -y networkd-dispatcher + fi + + # Write update_local_ip.sh + cat <<'EOF' >"$SCRIPT_PATH" +#!/bin/bash +set -euo pipefail + +IP_FILE="/run/local-ip.env" +mkdir -p "$(dirname "$IP_FILE")" + +get_current_ip() { + local targets=("8.8.8.8" "1.1.1.1" "192.168.1.1" "10.0.0.1" "172.16.0.1" "default") + local ip + + for target in "${targets[@]}"; do + if [[ "$target" == "default" ]]; then + ip=$(ip route get 1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') + else + ip=$(ip route get "$target" 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') + fi + if [[ -n "$ip" ]]; then + echo "$ip" + return 0 + fi + done + + return 1 +} + +current_ip="$(get_current_ip)" + +if [[ -z "$current_ip" ]]; then + echo "[ERROR] Could not detect local IP" >&2 + exit 1 +fi + +if [[ -f "$IP_FILE" ]]; then + source "$IP_FILE" + [[ "$LOCAL_IP" == "$current_ip" ]] && exit 0 +fi + +echo "LOCAL_IP=$current_ip" > "$IP_FILE" +echo "[INFO] LOCAL_IP updated to $current_ip" +EOF + + chmod +x "$SCRIPT_PATH" + + # Install dispatcher hook + mkdir -p "$(dirname "$DISPATCHER_SCRIPT")" + cat <"$DISPATCHER_SCRIPT" +#!/bin/bash +$SCRIPT_PATH +EOF + + chmod +x "$DISPATCHER_SCRIPT" + systemctl enable -q --now networkd-dispatcher.service +} + +# ------------------------------------------------------------------------------ +# Loads LOCAL_IP from persistent store or detects if missing. +# +# Description: +# - Loads from /run/local-ip.env or performs runtime lookup +# ------------------------------------------------------------------------------ + +function import_local_ip() { + local IP_FILE="/run/local-ip.env" + if [[ -f "$IP_FILE" ]]; then + # shellcheck disable=SC1090 + source "$IP_FILE" + fi + + if [[ -z "${LOCAL_IP:-}" ]]; then + get_current_ip() { + local targets=("8.8.8.8" "1.1.1.1" "192.168.1.1" "10.0.0.1" "172.16.0.1" "default") + local ip + + for target in "${targets[@]}"; do + if [[ "$target" == "default" ]]; then + ip=$(ip route get 1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') + else + ip=$(ip route get "$target" 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') + fi + if [[ -n "$ip" ]]; then + echo "$ip" + return 0 + fi + done + + return 1 + } + + LOCAL_IP="$(get_current_ip || true)" + if [[ -z "$LOCAL_IP" ]]; then + msg_error "Could not determine LOCAL_IP" + return 1 + fi + fi + + export LOCAL_IP +} + +# ------------------------------------------------------------------------------ +# Downloads file with optional progress indicator using pv. +# +# Arguments: +# $1 - URL +# $2 - Destination path +# ------------------------------------------------------------------------------ + +function download_with_progress() { + local url="$1" + local output="$2" + if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi + + if ! command -v pv &>/dev/null; then + $STD apt-get install -y pv + fi + set -o pipefail + + # Content-Length aus HTTP-Header holen + local content_length + content_length=$(curl -fsSLI "$url" | awk '/Content-Length/ {print $2}' | tr -d '\r' || true) + + if [[ -z "$content_length" ]]; then + if ! curl -fL# -o "$output" "$url"; then + msg_error "Download failed" + return 1 + fi + else + if ! curl -fsSL "$url" | pv -s "$content_length" >"$output"; then + msg_error "Download failed" + return 1 + fi + fi +} + +# ------------------------------------------------------------------------------ +# Installs or upgrades uv (Python package manager) from GitHub releases. +# - Downloads platform-specific tarball (no install.sh!) +# - Extracts uv binary +# - Places it in /usr/local/bin +# - Optionally installs a specific Python version via uv +# ------------------------------------------------------------------------------ + +function setup_uv() { + local UV_BIN="/usr/local/bin/uv" + local TMP_DIR + TMP_DIR=$(mktemp -d) + + # Determine system architecture + local ARCH + ARCH=$(uname -m) + local UV_TAR + + case "$ARCH" in + x86_64) + if grep -qi "alpine" /etc/os-release; then + UV_TAR="uv-x86_64-unknown-linux-musl.tar.gz" + else + UV_TAR="uv-x86_64-unknown-linux-gnu.tar.gz" + fi + ;; + aarch64) + if grep -qi "alpine" /etc/os-release; then + UV_TAR="uv-aarch64-unknown-linux-musl.tar.gz" + else + UV_TAR="uv-aarch64-unknown-linux-gnu.tar.gz" + fi + ;; + *) + msg_error "Unsupported architecture: $ARCH" + rm -rf "$TMP_DIR" + return 1 + ;; + esac + + # Get latest version from GitHub + local LATEST_VERSION + LATEST_VERSION=$(curl -fsSL https://api.github.com/repos/astral-sh/uv/releases/latest | + grep '"tag_name":' | cut -d '"' -f4 | sed 's/^v//') + + if [[ -z "$LATEST_VERSION" ]]; then + msg_error "Could not fetch latest uv version from GitHub." + rm -rf "$TMP_DIR" + return 1 + fi + + # Check if uv is already up to date + if [[ -x "$UV_BIN" ]]; then + local INSTALLED_VERSION + INSTALLED_VERSION=$($UV_BIN -V | awk '{print $2}') + if [[ "$INSTALLED_VERSION" == "$LATEST_VERSION" ]]; then + rm -rf "$TMP_DIR" + [[ ":$PATH:" != *":/usr/local/bin:"* ]] && export PATH="/usr/local/bin:$PATH" + return 0 + else + msg_info "Updating uv from $INSTALLED_VERSION to $LATEST_VERSION" + fi + else + msg_info "Setup uv $LATEST_VERSION" + fi + + # Download and install manually + local UV_URL="https://github.com/astral-sh/uv/releases/latest/download/${UV_TAR}" + if ! curl -fsSL "$UV_URL" -o "$TMP_DIR/uv.tar.gz"; then + msg_error "Failed to download $UV_URL" + rm -rf "$TMP_DIR" + return 1 + fi + + if ! tar -xzf "$TMP_DIR/uv.tar.gz" -C "$TMP_DIR"; then + msg_error "Failed to extract uv archive" + rm -rf "$TMP_DIR" + return 1 + fi + + install -m 755 "$TMP_DIR"/*/uv "$UV_BIN" || { + msg_error "Failed to install uv binary" + rm -rf "$TMP_DIR" + return 1 + } + + rm -rf "$TMP_DIR" + #ensure_usr_local_bin_persist + $STD uv python update-shell || { + msg_error "Failed to update uv shell integration" + return 1 + } + msg_ok "Setup uv $LATEST_VERSION" + + # Optional: install specific Python version + if [[ -n "${PYTHON_VERSION:-}" ]]; then + local VERSION_MATCH + VERSION_MATCH=$(uv python list --only-downloads | + grep -E "^cpython-${PYTHON_VERSION//./\\.}\.[0-9]+-linux" | + cut -d'-' -f2 | sort -V | tail -n1) + + if [[ -z "$VERSION_MATCH" ]]; then + msg_error "No matching Python $PYTHON_VERSION.x version found via uv" + return 1 + fi + + if ! uv python list | grep -q "cpython-${VERSION_MATCH}-linux.*uv/python"; then + if ! $STD uv python install "$VERSION_MATCH"; then + msg_error "Failed to install Python $VERSION_MATCH via uv" + return 1 + fi + msg_ok "Setup Python $VERSION_MATCH via uv" + fi + fi +} + +# ------------------------------------------------------------------------------ +# Ensures /usr/local/bin is permanently in system PATH. +# +# Description: +# - Adds to /etc/profile.d if not present +# ------------------------------------------------------------------------------ + +function ensure_usr_local_bin_persist() { + local PROFILE_FILE="/etc/profile.d/custom_path.sh" + + if [[ ! -f "$PROFILE_FILE" ]] && ! command -v pveversion &>/dev/null; then + echo 'export PATH="/usr/local/bin:$PATH"' >"$PROFILE_FILE" + chmod +x "$PROFILE_FILE" + fi +} + +# ------------------------------------------------------------------------------ +# Installs or updates Ghostscript (gs) from source. +# +# Description: +# - Fetches latest release +# - Builds and installs system-wide +# ------------------------------------------------------------------------------ + +function setup_gs() { + mkdir -p /tmp + TMP_DIR=$(mktemp -d) + CURRENT_VERSION=$(gs --version 2>/dev/null || echo "0") + + RELEASE_JSON=$(curl -fsSL https://api.github.com/repos/ArtifexSoftware/ghostpdl-downloads/releases/latest) + LATEST_VERSION=$(echo "$RELEASE_JSON" | grep '"tag_name":' | head -n1 | cut -d '"' -f4 | sed 's/^gs//') + LATEST_VERSION_DOTTED=$(echo "$RELEASE_JSON" | grep '"name":' | head -n1 | grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+') + + if [[ -z "$LATEST_VERSION" ]]; then + msg_error "Could not determine latest Ghostscript version from GitHub." + rm -rf "$TMP_DIR" + return + fi + + if dpkg --compare-versions "$CURRENT_VERSION" ge "$LATEST_VERSION_DOTTED"; then + rm -rf "$TMP_DIR" + return + fi + + msg_info "Setup Ghostscript $LATEST_VERSION_DOTTED (Patience)" + curl -fsSL "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs${LATEST_VERSION}/ghostscript-${LATEST_VERSION_DOTTED}.tar.gz" -o "$TMP_DIR/ghostscript.tar.gz" + + if ! tar -xzf "$TMP_DIR/ghostscript.tar.gz" -C "$TMP_DIR"; then + msg_error "Failed to extract Ghostscript archive." + rm -rf "$TMP_DIR" + return + fi + + cd "$TMP_DIR/ghostscript-${LATEST_VERSION_DOTTED}" || { + msg_error "Failed to enter Ghostscript source directory." + rm -rf "$TMP_DIR" + } + $STD apt-get install -y build-essential libpng-dev zlib1g-dev + $STD ./configure >/dev/null + $STD make + $STD sudo make install + local EXIT_CODE=$? + hash -r + if [[ ! -x "$(command -v gs)" ]]; then + if [[ -x /usr/local/bin/gs ]]; then + ln -sf /usr/local/bin/gs /usr/bin/gs + fi + fi + + rm -rf "$TMP_DIR" + + if [[ $EXIT_CODE -eq 0 ]]; then + msg_ok "Setup Ghostscript $LATEST_VERSION_DOTTED" + else + msg_error "Ghostscript installation failed" + fi +} + +# ------------------------------------------------------------------------------ +# Installs rbenv and ruby-build, installs Ruby and optionally Rails. +# +# Description: +# - Downloads rbenv and ruby-build from GitHub +# - Compiles and installs target Ruby version +# - Optionally installs Rails via gem +# +# Variables: +# RUBY_VERSION - Ruby version to install (default: 3.4.4) +# RUBY_INSTALL_RAILS - true/false to install Rails (default: true) +# ------------------------------------------------------------------------------ + +function setup_ruby() { + local RUBY_VERSION="${RUBY_VERSION:-3.4.4}" + local RUBY_INSTALL_RAILS="${RUBY_INSTALL_RAILS:-true}" + + local RBENV_DIR="$HOME/.rbenv" + local RBENV_BIN="$RBENV_DIR/bin/rbenv" + local PROFILE_FILE="$HOME/.profile" + local TMP_DIR + TMP_DIR=$(mktemp -d) + + msg_info "Setup Ruby $RUBY_VERSION" + + local RBENV_RELEASE + RBENV_RELEASE=$(curl -fsSL https://api.github.com/repos/rbenv/rbenv/releases/latest | grep '"tag_name":' | cut -d '"' -f4 | sed 's/^v//') + if [[ -z "$RBENV_RELEASE" ]]; then + msg_error "Failed to fetch latest rbenv version" + rm -rf "$TMP_DIR" + return 1 + fi + + curl -fsSL "https://github.com/rbenv/rbenv/archive/refs/tags/v${RBENV_RELEASE}.tar.gz" -o "$TMP_DIR/rbenv.tar.gz" + tar -xzf "$TMP_DIR/rbenv.tar.gz" -C "$TMP_DIR" + mkdir -p "$RBENV_DIR" + cp -r "$TMP_DIR/rbenv-${RBENV_RELEASE}/." "$RBENV_DIR/" + cd "$RBENV_DIR" && src/configure && $STD make -C src + + local RUBY_BUILD_RELEASE + RUBY_BUILD_RELEASE=$(curl -fsSL https://api.github.com/repos/rbenv/ruby-build/releases/latest | grep '"tag_name":' | cut -d '"' -f4 | sed 's/^v//') + if [[ -z "$RUBY_BUILD_RELEASE" ]]; then + msg_error "Failed to fetch latest ruby-build version" + rm -rf "$TMP_DIR" + return 1 + fi + + curl -fsSL "https://github.com/rbenv/ruby-build/archive/refs/tags/v${RUBY_BUILD_RELEASE}.tar.gz" -o "$TMP_DIR/ruby-build.tar.gz" + tar -xzf "$TMP_DIR/ruby-build.tar.gz" -C "$TMP_DIR" + mkdir -p "$RBENV_DIR/plugins/ruby-build" + cp -r "$TMP_DIR/ruby-build-${RUBY_BUILD_RELEASE}/." "$RBENV_DIR/plugins/ruby-build/" + echo "$RUBY_BUILD_RELEASE" >"$RBENV_DIR/plugins/ruby-build/RUBY_BUILD_version.txt" + + if ! grep -q 'rbenv init' "$PROFILE_FILE"; then + echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >>"$PROFILE_FILE" + echo 'eval "$(rbenv init -)"' >>"$PROFILE_FILE" + fi + + export PATH="$RBENV_DIR/bin:$PATH" + eval "$("$RBENV_BIN" init - bash)" + + if ! "$RBENV_BIN" versions --bare | grep -qx "$RUBY_VERSION"; then + $STD "$RBENV_BIN" install "$RUBY_VERSION" + fi + + "$RBENV_BIN" global "$RUBY_VERSION" + hash -r + + if [[ "$RUBY_INSTALL_RAILS" == "true" ]]; then + msg_info "Setup Rails via gem" + gem install rails + msg_ok "Setup Rails $(rails -v)" + fi + + rm -rf "$TMP_DIR" + msg_ok "Setup Ruby $RUBY_VERSION" +} + +# ------------------------------------------------------------------------------ +# Creates and installs self-signed certificates. +# +# Description: +# - Create a self-signed certificate with option to override application name +# +# Variables: +# APP - Application name (default: $APPLICATION variable) +# ------------------------------------------------------------------------------ +function create_selfsigned_certs() { + local app=${APP:-$(echo "${APPLICATION,,}" | tr -d ' ')} + $STD openssl req -x509 -nodes -days 365 -newkey rsa:4096 \ + -keyout /etc/ssl/private/"$app"-selfsigned.key \ + -out /etc/ssl/certs/"$app"-selfsigned.crt \ + -subj "/C=US/O=$app/OU=Domain Control Validated/CN=localhost" +} + +# ------------------------------------------------------------------------------ +# Installs Rust toolchain and optional global crates via cargo. +# +# Description: +# - Installs rustup (if missing) +# - Installs or updates desired Rust toolchain (stable, nightly, or versioned) +# - Installs or updates specified global crates using `cargo install` +# +# Notes: +# - Skips crate install if exact version is already present +# - Updates crate if newer version or different version is requested +# +# Variables: +# RUST_TOOLCHAIN - Rust toolchain to install (default: stable) +# RUST_CRATES - Comma-separated list of crates (e.g. "cargo-edit,wasm-pack@0.12.1") +# ------------------------------------------------------------------------------ + +function setup_rust() { + local RUST_TOOLCHAIN="${RUST_TOOLCHAIN:-stable}" + local RUST_CRATES="${RUST_CRATES:-}" + local CARGO_BIN="${HOME}/.cargo/bin" + + # rustup & toolchain + if ! command -v rustup &>/dev/null; then + msg_info "Setup Rust" + curl -fsSL https://sh.rustup.rs | $STD sh -s -- -y --default-toolchain "$RUST_TOOLCHAIN" + export PATH="$CARGO_BIN:$PATH" + echo 'export PATH="$HOME/.cargo/bin:$PATH"' >>"$HOME/.profile" + msg_ok "Setup Rust" + else + $STD rustup install "$RUST_TOOLCHAIN" + $STD rustup default "$RUST_TOOLCHAIN" + $STD rustup update "$RUST_TOOLCHAIN" + msg_ok "Rust toolchain set to $RUST_TOOLCHAIN" + fi + + # install/update crates + if [[ -n "$RUST_CRATES" ]]; then + IFS=',' read -ra CRATES <<<"$RUST_CRATES" + for crate in "${CRATES[@]}"; do + local NAME VER INSTALLED_VER + if [[ "$crate" == *"@"* ]]; then + NAME="${crate%@*}" + VER="${crate##*@}" + else + NAME="$crate" + VER="" + fi + + INSTALLED_VER=$(cargo install --list 2>/dev/null | awk "/^$NAME v[0-9]/ {print \$2}" | tr -d 'v') + + if [[ -n "$INSTALLED_VER" ]]; then + if [[ -n "$VER" && "$VER" != "$INSTALLED_VER" ]]; then + msg_info "Update $NAME: $INSTALLED_VER → $VER" + $STD cargo install "$NAME" --version "$VER" --force + msg_ok "Updated $NAME to $VER" + elif [[ -z "$VER" ]]; then + msg_info "Update $NAME: $INSTALLED_VER → latest" + $STD cargo install "$NAME" --force + msg_ok "Updated $NAME to latest" + fi + else + msg_info "Setup $NAME ${VER:+($VER)}" + $STD cargo install "$NAME" ${VER:+--version "$VER"} + msg_ok "Setup $NAME ${VER:-latest}" + fi + done + msg_ok "Setup Rust" + fi +} + +# ------------------------------------------------------------------------------ +# Installs Adminer (Debian/Ubuntu via APT, Alpine via direct download). +# +# Description: +# - Adds Adminer to Apache or web root +# - Supports Alpine and Debian-based systems +# ------------------------------------------------------------------------------ + +function setup_adminer() { + if grep -qi alpine /etc/os-release; then + msg_info "Setup Adminer (Alpine)" + mkdir -p /var/www/localhost/htdocs/adminer + if ! curl -fsSL https://github.com/vrana/adminer/releases/latest/download/adminer.php \ + -o /var/www/localhost/htdocs/adminer/index.php; then + msg_error "Failed to download Adminer" + return 1 + fi + msg_ok "Adminer available at /adminer (Alpine)" + else + msg_info "Setup Adminer (Debian/Ubuntu)" + $STD apt-get install -y adminer + $STD a2enconf adminer + $STD systemctl reload apache2 + msg_ok "Adminer available at /adminer (Debian/Ubuntu)" + fi +} + +# ------------------------------------------------------------------------------ +# Installs or updates yq (mikefarah/yq - Go version). +# +# Description: +# - Checks if yq is installed and from correct source +# - Compares with latest release on GitHub +# - Updates if outdated or wrong implementation +# ------------------------------------------------------------------------------ + +function setup_yq() { + local TMP_DIR + TMP_DIR=$(mktemp -d) + local CURRENT_VERSION="" + local BINARY_PATH="/usr/local/bin/yq" + local GITHUB_REPO="mikefarah/yq" + + if ! command -v jq &>/dev/null; then + $STD apt-get update + $STD apt-get install -y jq || { + msg_error "Failed to install jq" + rm -rf "$TMP_DIR" + return 1 + } + fi + + if command -v yq &>/dev/null; then + if ! yq --version 2>&1 | grep -q 'mikefarah'; then + rm -f "$(command -v yq)" + else + CURRENT_VERSION=$(yq --version | awk '{print $NF}' | sed 's/^v//') + fi + fi + + local RELEASE_JSON + RELEASE_JSON=$(curl -fsSL "https://api.github.com/repos/${GITHUB_REPO}/releases/latest") + local LATEST_VERSION + LATEST_VERSION=$(echo "$RELEASE_JSON" | jq -r '.tag_name' | sed 's/^v//') + + if [[ -z "$LATEST_VERSION" ]]; then + msg_error "Could not determine latest yq version from GitHub." + rm -rf "$TMP_DIR" + return 1 + fi + + if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$LATEST_VERSION" ]]; then + return + fi + + msg_info "Setup yq ($LATEST_VERSION)" + curl -fsSL "https://github.com/${GITHUB_REPO}/releases/download/v${LATEST_VERSION}/yq_linux_amd64" -o "$TMP_DIR/yq" + chmod +x "$TMP_DIR/yq" + mv "$TMP_DIR/yq" "$BINARY_PATH" + + if [[ ! -x "$BINARY_PATH" ]]; then + msg_error "Failed to install yq to $BINARY_PATH" + rm -rf "$TMP_DIR" + return 1 + fi + + rm -rf "$TMP_DIR" + hash -r + + local FINAL_VERSION + FINAL_VERSION=$("$BINARY_PATH" --version 2>/dev/null | awk '{print $NF}') + if [[ "$FINAL_VERSION" == "v$LATEST_VERSION" ]]; then + msg_ok "Setup yq ($LATEST_VERSION)" + else + msg_error "yq installation incomplete or version mismatch" + fi +} + +# ------------------------------------------------------------------------------ +# Installs ImageMagick 7 from source (Debian/Ubuntu only). +# +# Description: +# - Downloads the latest ImageMagick source tarball +# - Builds and installs ImageMagick to /usr/local +# - Configures dynamic linker (ldconfig) +# +# Notes: +# - Requires: build-essential, libtool, libjpeg-dev, libpng-dev, etc. +# ------------------------------------------------------------------------------ +function setup_imagemagick() { + local TMP_DIR + TMP_DIR=$(mktemp -d) + local VERSION="" + local BINARY_PATH="/usr/local/bin/magick" + + if command -v magick &>/dev/null; then + VERSION=$(magick -version | awk '/^Version/ {print $3}') + msg_ok "ImageMagick already installed ($VERSION)" + return 0 + fi + + msg_info "Setup ImageMagick (Patience)" + $STD apt-get update + $STD apt-get install -y \ + build-essential \ + libtool \ + libjpeg-dev \ + libpng-dev \ + libtiff-dev \ + libwebp-dev \ + libheif-dev \ + libde265-dev \ + libopenjp2-7-dev \ + libxml2-dev \ + liblcms2-dev \ + libfreetype6-dev \ + libraw-dev \ + libfftw3-dev \ + liblqr-1-0-dev \ + libgsl-dev \ + pkg-config \ + ghostscript + + curl -fsSL https://imagemagick.org/archive/ImageMagick.tar.gz -o "$TMP_DIR/ImageMagick.tar.gz" + tar -xzf "$TMP_DIR/ImageMagick.tar.gz" -C "$TMP_DIR" + cd "$TMP_DIR"/ImageMagick-* || { + msg_error "Source extraction failed" + rm -rf "$TMP_DIR" + return 1 + } + + ./configure --disable-static >/dev/null + $STD make + $STD make install + $STD ldconfig /usr/local/lib + + if [[ ! -x "$BINARY_PATH" ]]; then + msg_error "ImageMagick installation failed" + rm -rf "$TMP_DIR" + return 1 + fi + + VERSION=$("$BINARY_PATH" -version | awk '/^Version/ {print $3}') + rm -rf "$TMP_DIR" + ensure_usr_local_bin_persist + msg_ok "Setup ImageMagick $VERSION" +} + +# ------------------------------------------------------------------------------ +# Installs FFmpeg from source or prebuilt binary (Debian/Ubuntu only). +# +# Description: +# - Downloads and builds FFmpeg from GitHub (https://github.com/FFmpeg/FFmpeg) +# - Supports specific version override via FFMPEG_VERSION (e.g. n7.1.1) +# - Supports build profile via FFMPEG_TYPE: +# - minimal : x264, vpx, mp3 only +# - medium : adds subtitles, fonts, opus, vorbis +# - full : adds dav1d, svt-av1, zlib, numa +# - binary : downloads static build (johnvansickle.com) +# - Defaults to latest stable version and full feature set +# +# Notes: +# - Requires: curl, jq, build-essential, and matching codec libraries +# - Result is installed to /usr/local/bin/ffmpeg +# ------------------------------------------------------------------------------ + +function setup_ffmpeg() { + local TMP_DIR + TMP_DIR=$(mktemp -d) + local GITHUB_REPO="FFmpeg/FFmpeg" + local VERSION="${FFMPEG_VERSION:-latest}" + local TYPE="${FFMPEG_TYPE:-full}" + local BIN_PATH="/usr/local/bin/ffmpeg" + + # Binary fallback mode + if [[ "$TYPE" == "binary" ]]; then + msg_info "Installing FFmpeg (static binary)" + curl -fsSL https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz -o "$TMP_DIR/ffmpeg.tar.xz" + tar -xf "$TMP_DIR/ffmpeg.tar.xz" -C "$TMP_DIR" + local EXTRACTED_DIR + EXTRACTED_DIR=$(find "$TMP_DIR" -maxdepth 1 -type d -name "ffmpeg-*") + cp "$EXTRACTED_DIR/ffmpeg" "$BIN_PATH" + cp "$EXTRACTED_DIR/ffprobe" /usr/local/bin/ffprobe + chmod +x "$BIN_PATH" /usr/local/bin/ffprobe + rm -rf "$TMP_DIR" + msg_ok "Installed FFmpeg binary ($($BIN_PATH -version | head -n1))" + return + fi + + if ! command -v jq &>/dev/null; then + $STD apt-get update + $STD apt-get install -y jq + fi + + # Auto-detect latest stable version if none specified + if [[ "$VERSION" == "latest" || -z "$VERSION" ]]; then + msg_info "Resolving latest FFmpeg tag" + VERSION=$(curl -fsSL "https://api.github.com/repos/${GITHUB_REPO}/tags" | + jq -r '.[].name' | + grep -E '^n[0-9]+\.[0-9]+\.[0-9]+$' | + sort -V | tail -n1) + fi + + if [[ -z "$VERSION" ]]; then + msg_error "Could not determine FFmpeg version" + rm -rf "$TMP_DIR" + return 1 + fi + + msg_info "Installing FFmpeg ${VERSION} ($TYPE)" + + # Dependency selection + local DEPS=(build-essential yasm nasm pkg-config) + case "$TYPE" in + minimal) + DEPS+=(libx264-dev libvpx-dev libmp3lame-dev) + ;; + medium) + DEPS+=(libx264-dev libvpx-dev libmp3lame-dev libfreetype6-dev libass-dev libopus-dev libvorbis-dev) + ;; + full) + DEPS+=( + libx264-dev libx265-dev libvpx-dev libmp3lame-dev + libfreetype6-dev libass-dev libopus-dev libvorbis-dev + libdav1d-dev libsvtav1-dev zlib1g-dev libnuma-dev + ) + ;; + *) + msg_error "Invalid FFMPEG_TYPE: $TYPE" + rm -rf "$TMP_DIR" + return 1 + ;; + esac + + $STD apt-get update + $STD apt-get install -y "${DEPS[@]}" + + curl -fsSL "https://github.com/${GITHUB_REPO}/archive/refs/tags/${VERSION}.tar.gz" -o "$TMP_DIR/ffmpeg.tar.gz" + tar -xzf "$TMP_DIR/ffmpeg.tar.gz" -C "$TMP_DIR" + cd "$TMP_DIR/FFmpeg-"* || { + msg_error "Source extraction failed" + rm -rf "$TMP_DIR" + return 1 + } + + local args=( + --enable-gpl + --enable-shared + --enable-nonfree + --disable-static + --enable-libx264 + --enable-libvpx + --enable-libmp3lame + ) + + if [[ "$TYPE" != "minimal" ]]; then + args+=(--enable-libfreetype --enable-libass --enable-libopus --enable-libvorbis) + fi + + if [[ "$TYPE" == "full" ]]; then + args+=(--enable-libx265 --enable-libdav1d --enable-zlib) + fi + + if [[ ${#args[@]} -eq 0 ]]; then + msg_error "FFmpeg configure args array is empty – aborting." + rm -rf "$TMP_DIR" + return 1 + fi + + ./configure "${args[@]}" >"$TMP_DIR/configure.log" 2>&1 || { + msg_error "FFmpeg ./configure failed (see $TMP_DIR/configure.log)" + cat "$TMP_DIR/configure.log" | tail -n 20 + rm -rf "$TMP_DIR" + return 1 + } + + $STD make -j"$(nproc)" + $STD make install + echo "/usr/local/lib" >/etc/ld.so.conf.d/ffmpeg.conf + ldconfig + + ldconfig -p | grep libavdevice >/dev/null || { + msg_error "libavdevice not registered with dynamic linker" + return 1 + } + + if ! command -v ffmpeg &>/dev/null; then + msg_error "FFmpeg installation failed" + rm -rf "$TMP_DIR" + return 1 + fi + + local FINAL_VERSION + FINAL_VERSION=$(ffmpeg -version | head -n1 | awk '{print $3}') + rm -rf "$TMP_DIR" + ensure_usr_local_bin_persist + msg_ok "Setup FFmpeg $FINAL_VERSION" +} + +# ------------------------------------------------------------------------------ +# Installs ClickHouse server and client, sets up DB/user with credentials. +# +# Description: +# - Adds official ClickHouse APT repo with GPG key +# - Installs clickhouse-server and clickhouse-client +# - Creates database and user (credentials optionally overrideable via env) +# +# Variables: +# CLICKHOUSE_DB - Database name (default: analytics) +# CLICKHOUSE_USER - Username (default: analytics_user) +# CLICKHOUSE_PASS - Password (default: auto-generated) +# ------------------------------------------------------------------------------ + +function setup_clickhouse() { + local CLICKHOUSE_DB="${CLICKHOUSE_DB:-analytics}" + local CLICKHOUSE_USER="${CLICKHOUSE_USER:-analytics_user}" + local CLICKHOUSE_PASS="${CLICKHOUSE_PASS:-$(openssl rand -base64 18 | cut -c1-13)}" + local GPG_URL="https://packages.clickhouse.com/rpm/lts/repodata/repomd.xml.key" + local GPG_KEY_PATH="/usr/share/keyrings/clickhouse-keyring.gpg" + local ARCH + ARCH=$(dpkg --print-architecture) + + if ! command -v clickhouse >/dev/null; then + msg_info "Setup ClickHouse" + + if ! curl -fsSL --connect-timeout 5 https://packages.clickhouse.com >/dev/null 2>&1; then + msg_error "Connection to packages.clickhouse.com:443 failed – possibly blocked" + echo "💡 Check AdGuard/Pi-hole or firewall rules" + return 1 + fi + + if ! curl -fsSL --retry 3 --connect-timeout 10 "$GPG_URL" | + gpg --dearmor -o "$GPG_KEY_PATH"; then + msg_error "Failed to fetch ClickHouse GPG key" + return 1 + fi + + echo "deb [signed-by=$GPG_KEY_PATH arch=$ARCH] https://packages.clickhouse.com/deb stable main" \ + >/etc/apt/sources.list.d/clickhouse.list + + env -u CLICKHOUSE_USER $STD apt-get update + env -u CLICKHOUSE_USER DEBIAN_FRONTEND=noninteractive $STD apt-get install -y clickhouse-server clickhouse-client + + $STD systemctl enable --now clickhouse-server + + msg_info "Waiting for ClickHouse to be ready" + for i in {1..10}; do + if clickhouse client --query "SELECT 1" &>/dev/null; then break; fi + sleep 1 + done + + # User anlegen + clickhouse client --query "CREATE DATABASE IF NOT EXISTS $CLICKHOUSE_DB" + clickhouse client --query "CREATE USER IF NOT EXISTS $CLICKHOUSE_USER IDENTIFIED WITH plaintext_password BY '$CLICKHOUSE_PASS'" + clickhouse client --query "GRANT ALL ON $CLICKHOUSE_DB.* TO $CLICKHOUSE_USER" + + # Default-User ggf. deaktivieren + cat </etc/clickhouse-server/users.d/disable-default.xml + + + + + +EOF + systemctl restart clickhouse-server + + msg_ok "Setup ClickHouse (DB: $CLICKHOUSE_DB, User: $CLICKHOUSE_USER)" + + { + echo "ClickHouse DB: $CLICKHOUSE_DB" + echo "ClickHouse User: $CLICKHOUSE_USER" + echo "ClickHouse Pass: $CLICKHOUSE_PASS" + } >>~/clickhouse.creds + else + msg_info "Updating ClickHouse packages" + env -u CLICKHOUSE_USER $STD apt-get update + env -u CLICKHOUSE_USER $STD apt-get install -y --only-upgrade clickhouse-server clickhouse-client + msg_ok "ClickHouse updated" + fi +} + +# ------------------------------------------------------------------------------ +# Checks for new GitHub release (latest tag). +# +# Description: +# - Queries the GitHub API for the latest release tag +# - Compares it to a local cached version (~/.) +# - If newer, sets global CHECK_UPDATE_RELEASE and returns 0 +# +# Usage: +# check_for_gh_release "AppName" "user/repo" +# if [[ $? -eq 0 ]]; then +# echo "New version available: $CHECK_UPDATE_RELEASE" +# # trigger update... +# fi +# +# Notes: +# - Requires `jq` (auto-installed if missing) +# - Does not modify anything, only checks version state +# - Does not support pre-releases +# ------------------------------------------------------------------------------ +check_for_gh_release() { + local app="$1" + local source="$2" + local pinned_version="${3:-}" # optional + local current_file="$HOME/.${app,,}" + + msg_info "Check for update: ${app}" + + # DNS check for GitHub + if ! getent hosts api.github.com >/dev/null 2>&1; then + msg_error "Network error: cannot resolve api.github.com" + return 1 + fi + + # jq check + if ! command -v jq &>/dev/null; then + apt-get update -qq &>/dev/null && apt-get install -y jq &>/dev/null || { + msg_error "Failed to install jq" + return 1 + } + fi + + # get latest release + local release + release=$(curl -fsSL "https://api.github.com/repos/${source}/releases/latest" | + jq -r '.tag_name' | sed 's/^v//') + + if [[ -z "$release" ]]; then + msg_error "Unable to determine latest release for ${app}" + return 1 + fi + + local current="" + [[ -f "$current_file" ]] && current=$(<"$current_file") + + # PINNED Releases + if [[ -n "$pinned_version" ]]; then + if [[ "$pinned_version" == "$release" ]]; then + msg_ok "${app} pinned to v${pinned_version} (no update needed)" + return 1 + else + if [[ "$current" == "$pinned_version" ]]; then + msg_ok "${app} pinned to v${pinned_version} (already installed, upstream v${release})" + return 1 + fi + msg_info "${app} pinned to v${pinned_version} (upstream v${release}) → update/downgrade required" + CHECK_UPDATE_RELEASE="$pinned_version" + return 0 + fi + fi + + if [[ "$release" != "$current" ]] || [[ ! -f "$current_file" ]]; then + CHECK_UPDATE_RELEASE="$release" + msg_info "New release available: v${release} (current: v${current:-none})" + return 0 + else + msg_ok "${app} is up to date (v${release})" + return 1 + fi +} + +# ------------------------------------------------------------------------------ +# Hardware acceleration setup inside container +# Works with: Debian 12 (bookworm), Debian 13 (trixie), Ubuntu 24.04 (noble) +# Usage: hwaccel_setup_in_ct [--nonfree-intel] +# ------------------------------------------------------------------------------ + +hwaccel_setup_in_ct() { + local CTTYPE="$1" NONFREE=0 + [[ "$2" == "--nonfree-intel" ]] && NONFREE=1 + + # Detect OS info inside the container + local ID VERSION_CODENAME + if [[ -r /etc/os-release ]]; then + . /etc/os-release + fi + ID="${ID:-debian}" + VERSION_CODENAME="${VERSION_CODENAME:-bookworm}" + + msg_info "Setting up hardware acceleration for ${ID^} ($VERSION_CODENAME)" + + case "$ID" in + debian | ubuntu) + if ((NONFREE)) && [[ "$VERSION_CODENAME" =~ (trixie|noble) ]]; then + # Debian 13 / Ubuntu 24.04 → non-free Intel driver + cat >/etc/apt/sources.list.d/non-free.sources <<'SRC' +Types: deb deb-src +URIs: http://deb.debian.org/debian +Suites: trixie +Components: non-free non-free-firmware + +Types: deb deb-src +URIs: http://deb.debian.org/debian-security +Suites: trixie-security +Components: non-free non-free-firmware + +Types: deb deb-src +URIs: http://deb.debian.org/debian +Suites: trixie-updates +Components: non-free non-free-firmware +SRC + + $STD apt-get update + $STD apt-get install -y \ + intel-media-va-driver-non-free \ + ocl-icd-libopencl1 \ + mesa-opencl-icd \ + mesa-va-drivers \ + libvpl2 \ + vainfo \ + intel-gpu-tools + else + # Debian 12 (bookworm) and fallback for Debian 13/Ubuntu 24.04 without non-free + $STD apt-get update + $STD apt-get install -y \ + va-driver-all \ + ocl-icd-libopencl1 \ + mesa-opencl-icd \ + mesa-va-drivers \ + vainfo \ + intel-gpu-tools + fi + ;; + *) + msg_warn "Unsupported distribution ($ID $VERSION_CODENAME) – skipping HW accel setup" + return 0 + ;; + esac + + # Add current user to video/render groups (only for privileged CTs) + if [[ "$CTTYPE" == "0" ]]; then + $STD adduser "$(id -un)" video || true + $STD adduser "$(id -un)" render || true + fi + + msg_ok "Hardware acceleration is ready" +} From f6130d653ed91e65333a8792b45afc7db621e312 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 16 Oct 2025 22:05:41 +0200 Subject: [PATCH 1419/1733] testscript --- install/debian-install.sh | 282 +++++++++++++++++++++++++++++++++++--- 1 file changed, 263 insertions(+), 19 deletions(-) diff --git a/install/debian-install.sh b/install/debian-install.sh index 5f83d54af..96774cb4a 100644 --- a/install/debian-install.sh +++ b/install/debian-install.sh @@ -1,9 +1,10 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Test Suite for tools.func # License: MIT # https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Purpose: Comprehensive test of all setup_* functions from tools.func source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color @@ -13,31 +14,274 @@ setting_up_container network_check update_os -msg_info "Installing Dependencies" -$STD apt-get install -y gpg -msg_ok "Installed Dependencies" +msg_info "Installing Base Dependencies" +$STD apt-get install -y curl wget gpg jq git build-essential +msg_ok "Installed Base Dependencies" -#install_vaapi_userland_interactive +# Helper function to test and validate installation +test_and_validate() { + local test_name="$1" + local command_check="$2" + local version_cmd="$3" -#setup_mariadb + echo -e "\n${GN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}" + echo -e "${GN}Testing: ${test_name}${CL}" + echo -e "${GN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}\n" -#FFMPEG_VERSION="n7.1.1" FFMPEG_TYPE="full" setup_ffmpeg + if command -v "$command_check" &>/dev/null; then + local version_output + version_output=$($version_cmd 2>&1 | head -n1) + msg_ok "${test_name} installed: ${version_output}" + return 0 + else + msg_error "${test_name} validation FAILED - command not found: $command_check" + return 1 + fi +} -#fetch_and_deploy_gh_release "argus" "release-argus/Argus" "singlefile" "latest" "/opt/argus" "Argus-.*linux-amd64" -#fetch_and_deploy_gh_release "planka" "plankanban/planka" "prebuild" "latest" "/opt/planka" "planka-prebuild.zip" +# ============================================================================== +# 1. YQ - YAML Processor +# ============================================================================== +echo -e "\n${YW}[1/20] Testing: YQ${CL}" +setup_yq +test_and_validate "yq" "yq" "yq --version" -#PYTHON_VERSION="3.12" setup_uv +# ============================================================================== +# 2. ADMINER - Database Management Tool +# ============================================================================== +echo -e "\n${YW}[2/20] Testing: Adminer${CL}" +setup_adminer +if [ -f "/usr/share/adminer/latest.php" ]; then + msg_ok "Adminer installed at /usr/share/adminer/latest.php" +else + msg_error "Adminer installation FAILED" +fi -#PHP_VERSION=8.2 PHP_FPM=YES setup_php -#setup_composer +# ============================================================================== +# 3. LOCAL IP HELPER +# ============================================================================== +echo -e "\n${YW}[3/20] Testing: Local IP Helper${CL}" +setup_local_ip_helper +if systemctl is-enabled local-ip-helper.service &>/dev/null; then + msg_ok "Local IP Helper service enabled" +else + msg_error "Local IP Helper service NOT enabled" +fi -# Example Setting for Test -#NODE_MODULE="pnpm@10.1,yarn" -#RELEASE=$(curl_handler -fsSL https://api.github.com/repos/babybuddy/babybuddy/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') -#msg_ok "Get Release $RELEASE" -#NODE_VERSION="24" NODE_MODULE="yarn" setup_nodejs +# ============================================================================== +# 4. CLICKHOUSE - Columnar Database +# ============================================================================== +echo -e "\n${YW}[4/20] Testing: ClickHouse${CL}" +setup_clickhouse +test_and_validate "ClickHouse" "clickhouse-server" "clickhouse-server --version" +systemctl status clickhouse-server --no-pager | head -n5 -#PG_VERSION="16" setup_postgresql +# ============================================================================== +# 5. POSTGRESQL - Relational Database (Version 17) +# ============================================================================== +echo -e "\n${YW}[5/20] Testing: PostgreSQL 17${CL}" +PG_VERSION=17 setup_postgresql +test_and_validate "PostgreSQL" "psql" "psql --version" +sudo -u postgres psql -c "SELECT version();" | head -n3 + +# ============================================================================== +# 6. MARIADB - MySQL Fork (Version 11.4) +# ============================================================================== +echo -e "\n${YW}[6/20] Testing: MariaDB 11.4${CL}" +MARIADB_VERSION="11.4" setup_mariadb +test_and_validate "MariaDB" "mariadb" "mariadb --version" +mariadb -e "SELECT VERSION();" + +# ============================================================================== +# 7. MYSQL - Remove MariaDB first, then install MySQL 8.0 +# ============================================================================== +echo -e "\n${YW}[7/20] Testing: MySQL 8.0 (removing MariaDB first)${CL}" +msg_info "Removing MariaDB to avoid conflicts" +$STD systemctl stop mariadb +$STD apt-get purge -y mariadb-server mariadb-client mariadb-common +$STD apt-get autoremove -y +$STD rm -rf /etc/mysql /var/lib/mysql +msg_ok "MariaDB removed" + +MYSQL_VERSION="8.0" setup_mysql +test_and_validate "MySQL" "mysql" "mysql --version" +mysql -e "SELECT VERSION();" + +# ============================================================================== +# 8. MONGODB - NoSQL Database (Version 8.0 - requires AVX CPU) +# ============================================================================== +echo -e "\n${YW}[8/20] Testing: MongoDB 8.0${CL}" +if grep -q avx /proc/cpuinfo; then + MONGO_VERSION="8.0" setup_mongodb + test_and_validate "MongoDB" "mongod" "mongod --version" + systemctl status mongod --no-pager | head -n5 +else + msg_info "Skipping MongoDB - CPU does not support AVX" +fi + +# ============================================================================== +# 9. NODE.JS - JavaScript Runtime (Version 22 with modules) +# ============================================================================== +echo -e "\n${YW}[9/20] Testing: Node.js 22 with yarn, pnpm, pm2${CL}" +NODE_VERSION="22" NODE_MODULE="yarn,pnpm@10.1.0,pm2" setup_nodejs +test_and_validate "Node.js" "node" "node --version" +test_and_validate "npm" "npm" "npm --version" +test_and_validate "yarn" "yarn" "yarn --version" +test_and_validate "pnpm" "pnpm" "pnpm --version" +test_and_validate "pm2" "pm2" "pm2 --version" + +# ============================================================================== +# 10. PYTHON (via UV) - Version 3.12 +# ============================================================================== +echo -e "\n${YW}[10/20] Testing: Python 3.12 via uv${CL}" +PYTHON_VERSION="3.12" setup_uv +test_and_validate "uv" "uv" "uv --version" +if [ -d "/opt/venv" ]; then + source /opt/venv/bin/activate + test_and_validate "Python" "python" "python --version" + deactivate +fi + +# ============================================================================== +# 11. PHP - Version 8.3 with FPM and modules +# ============================================================================== +echo -e "\n${YW}[11/20] Testing: PHP 8.3 with FPM${CL}" +PHP_VERSION="8.3" PHP_FPM="YES" PHP_MODULE="redis,imagick,apcu,zip,mbstring" setup_php +test_and_validate "PHP" "php" "php --version" +php -m | grep -E "redis|imagick|apcu|zip|mbstring" +systemctl status php8.3-fpm --no-pager | head -n5 + +# ============================================================================== +# 12. COMPOSER - PHP Dependency Manager +# ============================================================================== +echo -e "\n${YW}[12/20] Testing: Composer${CL}" +setup_composer +test_and_validate "Composer" "composer" "composer --version" + +# ============================================================================== +# 13. JAVA - Temurin JDK 21 +# ============================================================================== +echo -e "\n${YW}[13/20] Testing: Java (Temurin 21)${CL}" +JAVA_VERSION="21" setup_java +test_and_validate "Java" "java" "java --version" +echo -e "\nJava Home: $JAVA_HOME" + +# ============================================================================== +# 14. GO - Golang (latest) +# ============================================================================== +echo -e "\n${YW}[14/20] Testing: Go (latest)${CL}" +GO_VERSION="latest" setup_go +test_and_validate "Go" "go" "go version" + +# ============================================================================== +# 15. RUBY - Version 3.4.1 with Rails +# ============================================================================== +echo -e "\n${YW}[15/20] Testing: Ruby 3.4.1 with Rails${CL}" +RUBY_VERSION="3.4.1" RUBY_INSTALL_RAILS="true" setup_ruby +test_and_validate "Ruby" "ruby" "ruby --version" +test_and_validate "Rails" "rails" "rails --version" + +# ============================================================================== +# 16. RUST - Stable toolchain with cargo-edit +# ============================================================================== +echo -e "\n${YW}[16/20] Testing: Rust (stable)${CL}" +RUST_TOOLCHAIN="stable" RUST_CRATES="cargo-edit" setup_rust +source "$HOME/.cargo/env" +test_and_validate "Rust" "rustc" "rustc --version" +test_and_validate "Cargo" "cargo" "cargo --version" + +# ============================================================================== +# 17. GHOSTSCRIPT - PDF/PostScript processor +# ============================================================================== +echo -e "\n${YW}[17/20] Testing: Ghostscript${CL}" +setup_gs +test_and_validate "Ghostscript" "gs" "gs --version" + +# ============================================================================== +# 18. IMAGEMAGICK - Image processing from source +# ============================================================================== +echo -e "\n${YW}[18/20] Testing: ImageMagick${CL}" +setup_imagemagick +test_and_validate "ImageMagick" "magick" "magick --version" + +# ============================================================================== +# 19. FFMPEG - Full build (n7.1.1) +# ============================================================================== +echo -e "\n${YW}[19/20] Testing: FFmpeg (full build)${CL}" +FFMPEG_VERSION="n7.1.1" FFMPEG_TYPE="full" setup_ffmpeg +test_and_validate "FFmpeg" "ffmpeg" "ffmpeg -version" +ffmpeg -encoders 2>/dev/null | grep -E "libx264|libvpx|libmp3lame" + +# ============================================================================== +# 20. GITHUB RELEASE DEPLOYMENTS +# ============================================================================== +echo -e "\n${YW}[20/20] Testing: GitHub Release Deployments${CL}" + +# Test 1: Tarball deployment +msg_info "Testing: Tarball deployment (Hanko)" +fetch_and_deploy_gh_release "hanko" "teamhanko/hanko" "tarball" "latest" "/opt/hanko-test" +if [ -d "/opt/hanko-test" ]; then + msg_ok "Hanko tarball deployed to /opt/hanko-test" + ls -lah /opt/hanko-test | head -n10 +else + msg_error "Hanko tarball deployment FAILED" +fi + +# Test 2: Single binary deployment +msg_info "Testing: Single binary deployment (Argus)" +fetch_and_deploy_gh_release "argus" "release-argus/Argus" "singlefile" "latest" "/opt/argus" "Argus-.*linux-amd64" +if [ -f "/opt/argus/argus" ]; then + msg_ok "Argus binary deployed" + /opt/argus/argus version 2>&1 || echo "Binary exists at /opt/argus/argus" +else + msg_error "Argus binary deployment FAILED" +fi + +# ============================================================================== +# FINAL SUMMARY +# ============================================================================== +echo -e "\n${GN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}" +echo -e "${GN} TEST SUITE SUMMARY${CL}" +echo -e "${GN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}\n" + +msg_info "Generating installation report" +{ + echo "=== tools.func Test Suite Report ===" + echo "Date: $(date)" + echo "Hostname: $(hostname)" + echo "" + echo "--- Installed Versions ---" + command -v yq &>/dev/null && echo "yq: $(yq --version 2>&1)" + command -v clickhouse-server &>/dev/null && echo "ClickHouse: $(clickhouse-server --version 2>&1 | head -n1)" + command -v psql &>/dev/null && echo "PostgreSQL: $(psql --version)" + command -v mysql &>/dev/null && echo "MySQL: $(mysql --version)" + command -v mongod &>/dev/null && echo "MongoDB: $(mongod --version 2>&1 | head -n1)" + command -v node &>/dev/null && echo "Node.js: $(node --version)" + command -v npm &>/dev/null && echo "npm: $(npm --version)" + command -v yarn &>/dev/null && echo "yarn: $(yarn --version)" + command -v pnpm &>/dev/null && echo "pnpm: $(pnpm --version)" + command -v uv &>/dev/null && echo "uv: $(uv --version)" + command -v php &>/dev/null && echo "PHP: $(php --version | head -n1)" + command -v composer &>/dev/null && echo "Composer: $(composer --version)" + command -v java &>/dev/null && echo "Java: $(java --version 2>&1 | head -n1)" + command -v go &>/dev/null && echo "Go: $(go version)" + command -v ruby &>/dev/null && echo "Ruby: $(ruby --version)" + command -v rustc &>/dev/null && echo "Rust: $(rustc --version)" + command -v gs &>/dev/null && echo "Ghostscript: $(gs --version)" + command -v magick &>/dev/null && echo "ImageMagick: $(magick --version | head -n1)" + command -v ffmpeg &>/dev/null && echo "FFmpeg: $(ffmpeg -version 2>&1 | head -n1)" + echo "" + echo "--- Service Status ---" + systemctl is-active clickhouse-server &>/dev/null && echo "ClickHouse: Active" + systemctl is-active postgresql &>/dev/null && echo "PostgreSQL: Active" + systemctl is-active mysql &>/dev/null && echo "MySQL: Active" + systemctl is-active mongod &>/dev/null && echo "MongoDB: Active" + systemctl is-active php8.3-fpm &>/dev/null && echo "PHP-FPM: Active" + systemctl is-active local-ip-helper &>/dev/null && echo "Local IP Helper: Active" +} > ~/tools-func-test-report.txt + +cat ~/tools-func-test-report.txt +msg_ok "Test report saved to ~/tools-func-test-report.txt" motd_ssh customize From 6f8d13cf4145e76aa38f335aa7056e0f0b4931f1 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 16 Oct 2025 22:06:16 +0200 Subject: [PATCH 1420/1733] testct --- .vscode/settings.json | 2 +- ct/debian.sh | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 47009d646..8f17c7ff9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -18,7 +18,7 @@ "editor.minimap.enabled": false, "terminal.integrated.scrollback": 10000, "[shellscript]": { - "editor.defaultFormatter": "mads-hartmann.bash-ide-vscode", + "editor.defaultFormatter": "foxundermoon.shell-format", "editor.tabSize": 4, "editor.insertSpaces": true, }, diff --git a/ct/debian.sh b/ct/debian.sh index 7798f275b..78010dc3d 100644 --- a/ct/debian.sh +++ b/ct/debian.sh @@ -8,8 +8,8 @@ source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxV APP="Debian" var_tags="${var_tags:-}" var_cpu="${var_cpu:-4}" -var_ram="${var_ram:-4096}" -var_disk="${var_disk:-15}" +var_ram="${var_ram:-8192}" +var_disk="${var_disk:-20}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" @@ -22,18 +22,18 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /var ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - msg_info "Updating $APP LXC" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "Updated $APP LXC" + header_info + check_container_storage + check_container_resources + if [[ ! -d /var ]]; then + msg_error "No ${APP} Installation Found!" exit + fi + msg_info "Updating $APP LXC" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Updated $APP LXC" + exit } start From 43e35f6f99be917625f820c90d7bea2bd7338a0c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 16 Oct 2025 22:10:49 +0200 Subject: [PATCH 1421/1733] fixes --- install/debian-install.sh | 2 +- misc/tools.func | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/install/debian-install.sh b/install/debian-install.sh index 96774cb4a..57ce16580 100644 --- a/install/debian-install.sh +++ b/install/debian-install.sh @@ -278,7 +278,7 @@ msg_info "Generating installation report" systemctl is-active mongod &>/dev/null && echo "MongoDB: Active" systemctl is-active php8.3-fpm &>/dev/null && echo "PHP-FPM: Active" systemctl is-active local-ip-helper &>/dev/null && echo "Local IP Helper: Active" -} > ~/tools-func-test-report.txt +} >~/tools-func-test-report.txt cat ~/tools-func-test-report.txt msg_ok "Test report saved to ~/tools-func-test-report.txt" diff --git a/misc/tools.func b/misc/tools.func index a9a385cdd..c3f1ed709 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -16,6 +16,7 @@ cache_installed_version() { get_cached_version() { local app="$1" + mkdir -p /var/cache/app-versions [[ -f "/var/cache/app-versions/${app}_version.txt" ]] && cat "/var/cache/app-versions/${app}_version.txt" } From e03359fc80c78e21237a62b4f5f3b6d8dba6e2a4 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 16 Oct 2025 22:11:15 +0200 Subject: [PATCH 1422/1733] fixes --- misc/tools.func | 4190 +++++++++++++++++++++++------------------------ 1 file changed, 2095 insertions(+), 2095 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index c3f1ed709..525d28ed6 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -8,550 +8,550 @@ # Cache installed version to avoid repeated checks # ------------------------------------------------------------------------------ cache_installed_version() { - local app="$1" - local version="$2" - mkdir -p /var/cache/app-versions - echo "$version" >"/var/cache/app-versions/${app}_version.txt" + local app="$1" + local version="$2" + mkdir -p /var/cache/app-versions + echo "$version" >"/var/cache/app-versions/${app}_version.txt" } get_cached_version() { - local app="$1" - mkdir -p /var/cache/app-versions - [[ -f "/var/cache/app-versions/${app}_version.txt" ]] && cat "/var/cache/app-versions/${app}_version.txt" + local app="$1" + mkdir -p /var/cache/app-versions + [[ -f "/var/cache/app-versions/${app}_version.txt" ]] && cat "/var/cache/app-versions/${app}_version.txt" } # ------------------------------------------------------------------------------ # Unified package upgrade function (with apt update caching) # ------------------------------------------------------------------------------ upgrade_package() { - local package="$1" - msg_info "Upgrading $package" + local package="$1" + msg_info "Upgrading $package" - # Use same caching logic as ensure_dependencies - local apt_cache_file="/var/cache/apt-update-timestamp" - local current_time=$(date +%s) - local last_update=0 + # Use same caching logic as ensure_dependencies + local apt_cache_file="/var/cache/apt-update-timestamp" + local current_time=$(date +%s) + local last_update=0 - if [[ -f "$apt_cache_file" ]]; then - last_update=$(cat "$apt_cache_file" 2>/dev/null || echo 0) - fi + if [[ -f "$apt_cache_file" ]]; then + last_update=$(cat "$apt_cache_file" 2>/dev/null || echo 0) + fi - if ((current_time - last_update > 300)); then - $STD apt update - echo "$current_time" >"$apt_cache_file" - fi + if ((current_time - last_update > 300)); then + $STD apt update + echo "$current_time" >"$apt_cache_file" + fi - $STD apt install --only-upgrade -y "$package" - msg_ok "Upgraded $package" + $STD apt install --only-upgrade -y "$package" + msg_ok "Upgraded $package" } # ------------------------------------------------------------------------------ # Repository availability check # ------------------------------------------------------------------------------ verify_repo_available() { - local repo_url="$1" - local suite="$2" + local repo_url="$1" + local suite="$2" - if curl -fsSL --max-time 10 "${repo_url}/dists/${suite}/Release" &>/dev/null; then - return 0 - fi - return 1 + if curl -fsSL --max-time 10 "${repo_url}/dists/${suite}/Release" &>/dev/null; then + return 0 + fi + return 1 } # ------------------------------------------------------------------------------ # Ensure dependencies are installed (with apt update caching) # ------------------------------------------------------------------------------ ensure_dependencies() { - local deps=("$@") - local missing=() + local deps=("$@") + local missing=() - for dep in "${deps[@]}"; do - if ! command -v "$dep" &>/dev/null && ! is_package_installed "$dep"; then - missing+=("$dep") + for dep in "${deps[@]}"; do + if ! command -v "$dep" &>/dev/null && ! is_package_installed "$dep"; then + missing+=("$dep") + fi + done + + if [[ ${#missing[@]} -gt 0 ]]; then + msg_info "Installing dependencies: ${missing[*]}" + + # Only run apt update if not done recently (within last 5 minutes) + local apt_cache_file="/var/cache/apt-update-timestamp" + local current_time=$(date +%s) + local last_update=0 + + if [[ -f "$apt_cache_file" ]]; then + last_update=$(cat "$apt_cache_file" 2>/dev/null || echo 0) + fi + + if ((current_time - last_update > 300)); then + $STD apt update + echo "$current_time" >"$apt_cache_file" + fi + + $STD apt install -y "${missing[@]}" + msg_ok "Installed dependencies" fi - done - - if [[ ${#missing[@]} -gt 0 ]]; then - msg_info "Installing dependencies: ${missing[*]}" - - # Only run apt update if not done recently (within last 5 minutes) - local apt_cache_file="/var/cache/apt-update-timestamp" - local current_time=$(date +%s) - local last_update=0 - - if [[ -f "$apt_cache_file" ]]; then - last_update=$(cat "$apt_cache_file" 2>/dev/null || echo 0) - fi - - if ((current_time - last_update > 300)); then - $STD apt update - echo "$current_time" >"$apt_cache_file" - fi - - $STD apt install -y "${missing[@]}" - msg_ok "Installed dependencies" - fi } # ------------------------------------------------------------------------------ # Smart version comparison # ------------------------------------------------------------------------------ version_gt() { - test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1" + test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1" } # ------------------------------------------------------------------------------ # Get system architecture (normalized) # ------------------------------------------------------------------------------ get_system_arch() { - local arch_type="${1:-dpkg}" # dpkg, uname, or both - local arch + local arch_type="${1:-dpkg}" # dpkg, uname, or both + local arch - case "$arch_type" in - dpkg) - arch=$(dpkg --print-architecture 2>/dev/null) - ;; - uname) - arch=$(uname -m) - [[ "$arch" == "x86_64" ]] && arch="amd64" - [[ "$arch" == "aarch64" ]] && arch="arm64" - ;; - both | *) - arch=$(dpkg --print-architecture 2>/dev/null || uname -m) - [[ "$arch" == "x86_64" ]] && arch="amd64" - [[ "$arch" == "aarch64" ]] && arch="arm64" - ;; - esac + case "$arch_type" in + dpkg) + arch=$(dpkg --print-architecture 2>/dev/null) + ;; + uname) + arch=$(uname -m) + [[ "$arch" == "x86_64" ]] && arch="amd64" + [[ "$arch" == "aarch64" ]] && arch="arm64" + ;; + both | *) + arch=$(dpkg --print-architecture 2>/dev/null || uname -m) + [[ "$arch" == "x86_64" ]] && arch="amd64" + [[ "$arch" == "aarch64" ]] && arch="arm64" + ;; + esac - echo "$arch" + echo "$arch" } # ------------------------------------------------------------------------------ # Create temporary directory with automatic cleanup # ------------------------------------------------------------------------------ create_temp_dir() { - local tmp_dir=$(mktemp -d) - # Set trap to cleanup on EXIT, ERR, INT, TERM - trap "rm -rf '$tmp_dir'" EXIT ERR INT TERM - echo "$tmp_dir" + local tmp_dir=$(mktemp -d) + # Set trap to cleanup on EXIT, ERR, INT, TERM + trap "rm -rf '$tmp_dir'" EXIT ERR INT TERM + echo "$tmp_dir" } # ------------------------------------------------------------------------------ # Check if package is installed (faster than dpkg -l | grep) # ------------------------------------------------------------------------------ is_package_installed() { - local package="$1" - dpkg-query -W -f='${Status}' "$package" 2>/dev/null | grep -q "^install ok installed$" + local package="$1" + dpkg-query -W -f='${Status}' "$package" 2>/dev/null | grep -q "^install ok installed$" } # ------------------------------------------------------------------------------ # GitHub API call with authentication and rate limit handling # ------------------------------------------------------------------------------ github_api_call() { - local url="$1" - local output_file="${2:-/dev/stdout}" - local max_retries=3 - local retry_delay=2 + local url="$1" + local output_file="${2:-/dev/stdout}" + local max_retries=3 + local retry_delay=2 - local header_args=() - [[ -n "${GITHUB_TOKEN:-}" ]] && header_args=(-H "Authorization: Bearer $GITHUB_TOKEN") + local header_args=() + [[ -n "${GITHUB_TOKEN:-}" ]] && header_args=(-H "Authorization: Bearer $GITHUB_TOKEN") - for attempt in $(seq 1 $max_retries); do - local http_code - http_code=$(curl -fsSL -w "%{http_code}" -o "$output_file" \ - -H "Accept: application/vnd.github+json" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ - "${header_args[@]}" \ - "$url" 2>/dev/null || echo "000") + for attempt in $(seq 1 $max_retries); do + local http_code + http_code=$(curl -fsSL -w "%{http_code}" -o "$output_file" \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "${header_args[@]}" \ + "$url" 2>/dev/null || echo "000") - case "$http_code" in - 200) - return 0 - ;; - 403) - # Rate limit - check if we can retry - if [[ $attempt -lt $max_retries ]]; then - msg_warn "GitHub API rate limit, waiting ${retry_delay}s... (attempt $attempt/$max_retries)" - sleep "$retry_delay" - retry_delay=$((retry_delay * 2)) - continue - fi - msg_error "GitHub API rate limit exceeded. Set GITHUB_TOKEN to increase limits." - return 1 - ;; - 404) - msg_error "GitHub API endpoint not found: $url" - return 1 - ;; - *) - if [[ $attempt -lt $max_retries ]]; then - sleep "$retry_delay" - continue - fi - msg_error "GitHub API call failed with HTTP $http_code" - return 1 - ;; - esac - done + case "$http_code" in + 200) + return 0 + ;; + 403) + # Rate limit - check if we can retry + if [[ $attempt -lt $max_retries ]]; then + msg_warn "GitHub API rate limit, waiting ${retry_delay}s... (attempt $attempt/$max_retries)" + sleep "$retry_delay" + retry_delay=$((retry_delay * 2)) + continue + fi + msg_error "GitHub API rate limit exceeded. Set GITHUB_TOKEN to increase limits." + return 1 + ;; + 404) + msg_error "GitHub API endpoint not found: $url" + return 1 + ;; + *) + if [[ $attempt -lt $max_retries ]]; then + sleep "$retry_delay" + continue + fi + msg_error "GitHub API call failed with HTTP $http_code" + return 1 + ;; + esac + done - return 1 + return 1 } should_upgrade() { - local current="$1" - local target="$2" + local current="$1" + local target="$2" - [[ -z "$current" ]] && return 0 - version_gt "$target" "$current" && return 0 - return 1 + [[ -z "$current" ]] && return 0 + version_gt "$target" "$current" && return 0 + return 1 } # ------------------------------------------------------------------------------ # Get OS information (cached for performance) # ------------------------------------------------------------------------------ get_os_info() { - local field="${1:-all}" # id, codename, version, version_id, all + local field="${1:-all}" # id, codename, version, version_id, all - # Cache OS info to avoid repeated file reads - if [[ -z "${_OS_ID:-}" ]]; then - export _OS_ID=$(awk -F= '/^ID=/{gsub(/"/,"",$2); print $2}' /etc/os-release) - export _OS_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{gsub(/"/,"",$2); print $2}' /etc/os-release) - export _OS_VERSION=$(awk -F= '/^VERSION_ID=/{gsub(/"/,"",$2); print $2}' /etc/os-release) - export _OS_VERSION_FULL=$(awk -F= '/^VERSION=/{gsub(/"/,"",$2); print $2}' /etc/os-release) - fi + # Cache OS info to avoid repeated file reads + if [[ -z "${_OS_ID:-}" ]]; then + export _OS_ID=$(awk -F= '/^ID=/{gsub(/"/,"",$2); print $2}' /etc/os-release) + export _OS_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{gsub(/"/,"",$2); print $2}' /etc/os-release) + export _OS_VERSION=$(awk -F= '/^VERSION_ID=/{gsub(/"/,"",$2); print $2}' /etc/os-release) + export _OS_VERSION_FULL=$(awk -F= '/^VERSION=/{gsub(/"/,"",$2); print $2}' /etc/os-release) + fi - case "$field" in - id) echo "$_OS_ID" ;; - codename) echo "$_OS_CODENAME" ;; - version) echo "$_OS_VERSION" ;; - version_id) echo "$_OS_VERSION" ;; - version_full) echo "$_OS_VERSION_FULL" ;; - all) echo "ID=$_OS_ID CODENAME=$_OS_CODENAME VERSION=$_OS_VERSION" ;; - *) echo "$_OS_ID" ;; - esac + case "$field" in + id) echo "$_OS_ID" ;; + codename) echo "$_OS_CODENAME" ;; + version) echo "$_OS_VERSION" ;; + version_id) echo "$_OS_VERSION" ;; + version_full) echo "$_OS_VERSION_FULL" ;; + all) echo "ID=$_OS_ID CODENAME=$_OS_CODENAME VERSION=$_OS_VERSION" ;; + *) echo "$_OS_ID" ;; + esac } # ------------------------------------------------------------------------------ # Check if running on specific OS # ------------------------------------------------------------------------------ is_debian() { - [[ "$(get_os_info id)" == "debian" ]] + [[ "$(get_os_info id)" == "debian" ]] } is_ubuntu() { - [[ "$(get_os_info id)" == "ubuntu" ]] + [[ "$(get_os_info id)" == "ubuntu" ]] } is_alpine() { - [[ "$(get_os_info id)" == "alpine" ]] + [[ "$(get_os_info id)" == "alpine" ]] } # ------------------------------------------------------------------------------ # Get Debian/Ubuntu major version # ------------------------------------------------------------------------------ get_os_version_major() { - local version=$(get_os_info version) - echo "${version%%.*}" + local version=$(get_os_info version) + echo "${version%%.*}" } # ------------------------------------------------------------------------------ # Download file with retry logic and progress # ------------------------------------------------------------------------------ download_file() { - local url="$1" - local output="$2" - local max_retries="${3:-3}" - local show_progress="${4:-false}" + local url="$1" + local output="$2" + local max_retries="${3:-3}" + local show_progress="${4:-false}" - local curl_opts=(-fsSL) - [[ "$show_progress" == "true" ]] && curl_opts=(-fL#) + local curl_opts=(-fsSL) + [[ "$show_progress" == "true" ]] && curl_opts=(-fL#) - for attempt in $(seq 1 $max_retries); do - if curl "${curl_opts[@]}" -o "$output" "$url"; then - return 0 - fi + for attempt in $(seq 1 $max_retries); do + if curl "${curl_opts[@]}" -o "$output" "$url"; then + return 0 + fi - if [[ $attempt -lt $max_retries ]]; then - msg_warn "Download failed, retrying... (attempt $attempt/$max_retries)" - sleep 2 - fi - done + if [[ $attempt -lt $max_retries ]]; then + msg_warn "Download failed, retrying... (attempt $attempt/$max_retries)" + sleep 2 + fi + done - msg_error "Failed to download: $url" - return 1 + msg_error "Failed to download: $url" + return 1 } # ------------------------------------------------------------------------------ # Get fallback suite for repository (comprehensive mapping) # ------------------------------------------------------------------------------ get_fallback_suite() { - local distro_id="$1" - local distro_codename="$2" - local repo_base_url="$3" + local distro_id="$1" + local distro_codename="$2" + local repo_base_url="$3" - # Check if current codename works - if verify_repo_available "$repo_base_url" "$distro_codename"; then - echo "$distro_codename" - return 0 - fi + # Check if current codename works + if verify_repo_available "$repo_base_url" "$distro_codename"; then + echo "$distro_codename" + return 0 + fi - # Comprehensive fallback mappings - case "$distro_id" in - debian) - case "$distro_codename" in - # Debian 13 (Trixie) → Debian 12 (Bookworm) - trixie | forky | sid) - echo "bookworm" - ;; - # Debian 12 (Bookworm) stays - bookworm) - echo "bookworm" - ;; - # Debian 11 (Bullseye) stays - bullseye) - echo "bullseye" - ;; - # Unknown → latest stable + # Comprehensive fallback mappings + case "$distro_id" in + debian) + case "$distro_codename" in + # Debian 13 (Trixie) → Debian 12 (Bookworm) + trixie | forky | sid) + echo "bookworm" + ;; + # Debian 12 (Bookworm) stays + bookworm) + echo "bookworm" + ;; + # Debian 11 (Bullseye) stays + bullseye) + echo "bullseye" + ;; + # Unknown → latest stable + *) + echo "bookworm" + ;; + esac + ;; + ubuntu) + case "$distro_codename" in + # Ubuntu 24.10 (Oracular) → 24.04 LTS (Noble) + oracular | plucky) + echo "noble" + ;; + # Ubuntu 24.04 LTS (Noble) stays + noble) + echo "noble" + ;; + # Ubuntu 23.10 (Mantic) → 22.04 LTS (Jammy) + mantic | lunar) + echo "jammy" + ;; + # Ubuntu 22.04 LTS (Jammy) stays + jammy) + echo "jammy" + ;; + # Ubuntu 20.04 LTS (Focal) stays + focal) + echo "focal" + ;; + # Unknown → latest LTS + *) + echo "jammy" + ;; + esac + ;; *) - echo "bookworm" - ;; + echo "$distro_codename" + ;; esac - ;; - ubuntu) - case "$distro_codename" in - # Ubuntu 24.10 (Oracular) → 24.04 LTS (Noble) - oracular | plucky) - echo "noble" - ;; - # Ubuntu 24.04 LTS (Noble) stays - noble) - echo "noble" - ;; - # Ubuntu 23.10 (Mantic) → 22.04 LTS (Jammy) - mantic | lunar) - echo "jammy" - ;; - # Ubuntu 22.04 LTS (Jammy) stays - jammy) - echo "jammy" - ;; - # Ubuntu 20.04 LTS (Focal) stays - focal) - echo "focal" - ;; - # Unknown → latest LTS - *) - echo "jammy" - ;; - esac - ;; - *) - echo "$distro_codename" - ;; - esac } # ------------------------------------------------------------------------------ # Verify package source and version # ------------------------------------------------------------------------------ verify_package_source() { - local package="$1" - local expected_version="$2" + local package="$1" + local expected_version="$2" - if apt-cache policy "$package" 2>/dev/null | grep -q "$expected_version"; then - return 0 - fi - return 1 + if apt-cache policy "$package" 2>/dev/null | grep -q "$expected_version"; then + return 0 + fi + return 1 } # ------------------------------------------------------------------------------ # Check if running on LTS version # ------------------------------------------------------------------------------ is_lts_version() { - local os_id=$(get_os_info id) - local codename=$(get_os_info codename) + local os_id=$(get_os_info id) + local codename=$(get_os_info codename) - if [[ "$os_id" == "ubuntu" ]]; then - case "$codename" in - focal | jammy | noble) return 0 ;; # 20.04, 22.04, 24.04 - *) return 1 ;; - esac - elif [[ "$os_id" == "debian" ]]; then - # Debian releases are all "stable" - case "$codename" in - bullseye | bookworm | trixie) return 0 ;; - *) return 1 ;; - esac - fi + if [[ "$os_id" == "ubuntu" ]]; then + case "$codename" in + focal | jammy | noble) return 0 ;; # 20.04, 22.04, 24.04 + *) return 1 ;; + esac + elif [[ "$os_id" == "debian" ]]; then + # Debian releases are all "stable" + case "$codename" in + bullseye | bookworm | trixie) return 0 ;; + *) return 1 ;; + esac + fi - return 1 + return 1 } # ------------------------------------------------------------------------------ # Get optimal number of parallel jobs (cached) # ------------------------------------------------------------------------------ get_parallel_jobs() { - if [[ -z "${_PARALLEL_JOBS:-}" ]]; then - local cpu_count=$(nproc 2>/dev/null || echo 1) - local mem_gb=$(free -g | awk '/^Mem:/{print $2}') + if [[ -z "${_PARALLEL_JOBS:-}" ]]; then + local cpu_count=$(nproc 2>/dev/null || echo 1) + local mem_gb=$(free -g | awk '/^Mem:/{print $2}') - # Limit by available memory (assume 1GB per job for compilation) - local max_by_mem=$((mem_gb > 0 ? mem_gb : 1)) - local max_jobs=$((cpu_count < max_by_mem ? cpu_count : max_by_mem)) + # Limit by available memory (assume 1GB per job for compilation) + local max_by_mem=$((mem_gb > 0 ? mem_gb : 1)) + local max_jobs=$((cpu_count < max_by_mem ? cpu_count : max_by_mem)) - # At least 1, at most cpu_count - export _PARALLEL_JOBS=$((max_jobs > 0 ? max_jobs : 1)) - fi - echo "$_PARALLEL_JOBS" + # At least 1, at most cpu_count + export _PARALLEL_JOBS=$((max_jobs > 0 ? max_jobs : 1)) + fi + echo "$_PARALLEL_JOBS" } # ------------------------------------------------------------------------------ # Get default PHP version for OS # ------------------------------------------------------------------------------ get_default_php_version() { - local os_id=$(get_os_info id) - local os_version=$(get_os_version_major) + local os_id=$(get_os_info id) + local os_version=$(get_os_version_major) - case "$os_id" in - debian) - case "$os_version" in - 13) echo "8.3" ;; # Debian 13 (Trixie) - 12) echo "8.2" ;; # Debian 12 (Bookworm) - 11) echo "7.4" ;; # Debian 11 (Bullseye) - *) echo "8.2" ;; + case "$os_id" in + debian) + case "$os_version" in + 13) echo "8.3" ;; # Debian 13 (Trixie) + 12) echo "8.2" ;; # Debian 12 (Bookworm) + 11) echo "7.4" ;; # Debian 11 (Bullseye) + *) echo "8.2" ;; + esac + ;; + ubuntu) + case "$os_version" in + 24) echo "8.3" ;; # Ubuntu 24.04 LTS (Noble) + 22) echo "8.1" ;; # Ubuntu 22.04 LTS (Jammy) + 20) echo "7.4" ;; # Ubuntu 20.04 LTS (Focal) + *) echo "8.1" ;; + esac + ;; + *) + echo "8.2" + ;; esac - ;; - ubuntu) - case "$os_version" in - 24) echo "8.3" ;; # Ubuntu 24.04 LTS (Noble) - 22) echo "8.1" ;; # Ubuntu 22.04 LTS (Jammy) - 20) echo "7.4" ;; # Ubuntu 20.04 LTS (Focal) - *) echo "8.1" ;; - esac - ;; - *) - echo "8.2" - ;; - esac } # ------------------------------------------------------------------------------ # Get default Python version for OS # ------------------------------------------------------------------------------ get_default_python_version() { - local os_id=$(get_os_info id) - local os_version=$(get_os_version_major) + local os_id=$(get_os_info id) + local os_version=$(get_os_version_major) - case "$os_id" in - debian) - case "$os_version" in - 13) echo "3.12" ;; # Debian 13 (Trixie) - 12) echo "3.11" ;; # Debian 12 (Bookworm) - 11) echo "3.9" ;; # Debian 11 (Bullseye) - *) echo "3.11" ;; + case "$os_id" in + debian) + case "$os_version" in + 13) echo "3.12" ;; # Debian 13 (Trixie) + 12) echo "3.11" ;; # Debian 12 (Bookworm) + 11) echo "3.9" ;; # Debian 11 (Bullseye) + *) echo "3.11" ;; + esac + ;; + ubuntu) + case "$os_version" in + 24) echo "3.12" ;; # Ubuntu 24.04 LTS + 22) echo "3.10" ;; # Ubuntu 22.04 LTS + 20) echo "3.8" ;; # Ubuntu 20.04 LTS + *) echo "3.10" ;; + esac + ;; + *) + echo "3.11" + ;; esac - ;; - ubuntu) - case "$os_version" in - 24) echo "3.12" ;; # Ubuntu 24.04 LTS - 22) echo "3.10" ;; # Ubuntu 22.04 LTS - 20) echo "3.8" ;; # Ubuntu 20.04 LTS - *) echo "3.10" ;; - esac - ;; - *) - echo "3.11" - ;; - esac } # ------------------------------------------------------------------------------ # Get default Node.js LTS version # ------------------------------------------------------------------------------ get_default_nodejs_version() { - # Always return current LTS (as of 2025) - echo "20" + # Always return current LTS (as of 2025) + echo "20" } # ------------------------------------------------------------------------------ # Check if package manager is locked # ------------------------------------------------------------------------------ is_apt_locked() { - if fuser /var/lib/dpkg/lock-frontend &>/dev/null || - fuser /var/lib/apt/lists/lock &>/dev/null || - fuser /var/cache/apt/archives/lock &>/dev/null; then - return 0 - fi - return 1 + if fuser /var/lib/dpkg/lock-frontend &>/dev/null || + fuser /var/lib/apt/lists/lock &>/dev/null || + fuser /var/cache/apt/archives/lock &>/dev/null; then + return 0 + fi + return 1 } # ------------------------------------------------------------------------------ # Wait for apt to be available # ------------------------------------------------------------------------------ wait_for_apt() { - local max_wait="${1:-300}" # 5 minutes default - local waited=0 + local max_wait="${1:-300}" # 5 minutes default + local waited=0 - while is_apt_locked; do - if [[ $waited -ge $max_wait ]]; then - msg_error "Timeout waiting for apt to be available" - return 1 - fi + while is_apt_locked; do + if [[ $waited -ge $max_wait ]]; then + msg_error "Timeout waiting for apt to be available" + return 1 + fi - debug_log "Waiting for apt to be available... (${waited}s)" - sleep 5 - waited=$((waited + 5)) - done + debug_log "Waiting for apt to be available... (${waited}s)" + sleep 5 + waited=$((waited + 5)) + done - return 0 + return 0 } # ------------------------------------------------------------------------------ # Cleanup old repository files (migration helper) # ------------------------------------------------------------------------------ cleanup_old_repo_files() { - local app="$1" + local app="$1" - # Remove old-style .list files (including backups) - rm -f /etc/apt/sources.list.d/"${app}"*.list - rm -f /etc/apt/sources.list.d/"${app}"*.list.save - rm -f /etc/apt/sources.list.d/"${app}"*.list.distUpgrade - rm -f /etc/apt/sources.list.d/"${app}"*.list.dpkg-* + # Remove old-style .list files (including backups) + rm -f /etc/apt/sources.list.d/"${app}"*.list + rm -f /etc/apt/sources.list.d/"${app}"*.list.save + rm -f /etc/apt/sources.list.d/"${app}"*.list.distUpgrade + rm -f /etc/apt/sources.list.d/"${app}"*.list.dpkg-* - # Remove old GPG keys from trusted.gpg.d - rm -f /etc/apt/trusted.gpg.d/"${app}"*.gpg + # Remove old GPG keys from trusted.gpg.d + rm -f /etc/apt/trusted.gpg.d/"${app}"*.gpg - # Remove duplicate .sources files (keep only the main one) - local sources_file="/etc/apt/sources.list.d/${app}.sources" - if [[ -f "$sources_file" ]]; then - find /etc/apt/sources.list.d/ -name "${app}*.sources" ! -name "${app}.sources" -delete 2>/dev/null || true - fi + # Remove duplicate .sources files (keep only the main one) + local sources_file="/etc/apt/sources.list.d/${app}.sources" + if [[ -f "$sources_file" ]]; then + find /etc/apt/sources.list.d/ -name "${app}*.sources" ! -name "${app}.sources" -delete 2>/dev/null || true + fi } # ------------------------------------------------------------------------------ # Standardized deb822 repository setup # ------------------------------------------------------------------------------ setup_deb822_repo() { - local name="$1" - local gpg_url="$2" - local repo_url="$3" - local suite="$4" - local component="${5:-main}" - local architectures="${6:-amd64 arm64}" + local name="$1" + local gpg_url="$2" + local repo_url="$3" + local suite="$4" + local component="${5:-main}" + local architectures="${6:-amd64 arm64}" - msg_info "Setting up $name repository" + msg_info "Setting up $name repository" - # Cleanup old configs - cleanup_old_repo_files "$name" + # Cleanup old configs + cleanup_old_repo_files "$name" - # Ensure keyring directory exists - mkdir -p /etc/apt/keyrings + # Ensure keyring directory exists + mkdir -p /etc/apt/keyrings - # Download GPG key - curl -fsSL "$gpg_url" | gpg --dearmor -o "/etc/apt/keyrings/${name}.gpg" + # Download GPG key + curl -fsSL "$gpg_url" | gpg --dearmor -o "/etc/apt/keyrings/${name}.gpg" - # Create deb822 sources file - cat </etc/apt/sources.list.d/${name}.sources + # Create deb822 sources file + cat </etc/apt/sources.list.d/${name}.sources Types: deb URIs: $repo_url Suites: $suite @@ -560,178 +560,178 @@ Architectures: $architectures Signed-By: /etc/apt/keyrings/${name}.gpg EOF - # Use cached apt update - local apt_cache_file="/var/cache/apt-update-timestamp" - local current_time=$(date +%s) - local last_update=0 + # Use cached apt update + local apt_cache_file="/var/cache/apt-update-timestamp" + local current_time=$(date +%s) + local last_update=0 - if [[ -f "$apt_cache_file" ]]; then - last_update=$(cat "$apt_cache_file" 2>/dev/null || echo 0) - fi + if [[ -f "$apt_cache_file" ]]; then + last_update=$(cat "$apt_cache_file" 2>/dev/null || echo 0) + fi - # For repo changes, always update but respect short-term cache (30s) - if ((current_time - last_update > 30)); then - $STD apt update - echo "$current_time" >"$apt_cache_file" - fi + # For repo changes, always update but respect short-term cache (30s) + if ((current_time - last_update > 30)); then + $STD apt update + echo "$current_time" >"$apt_cache_file" + fi - msg_ok "Set up $name repository" + msg_ok "Set up $name repository" } # ------------------------------------------------------------------------------ # Package version hold/unhold helpers # ------------------------------------------------------------------------------ hold_package_version() { - local package="$1" - $STD apt-mark hold "$package" + local package="$1" + $STD apt-mark hold "$package" } unhold_package_version() { - local package="$1" - $STD apt-mark unhold "$package" + local package="$1" + $STD apt-mark unhold "$package" } # ------------------------------------------------------------------------------ # Safe service restart with verification # ------------------------------------------------------------------------------ safe_service_restart() { - local service="$1" + local service="$1" - if systemctl is-active --quiet "$service"; then - $STD systemctl restart "$service" - else - $STD systemctl start "$service" - fi + if systemctl is-active --quiet "$service"; then + $STD systemctl restart "$service" + else + $STD systemctl start "$service" + fi - if ! systemctl is-active --quiet "$service"; then - msg_error "Failed to start $service" - systemctl status "$service" --no-pager - return 1 - fi - return 0 + if ! systemctl is-active --quiet "$service"; then + msg_error "Failed to start $service" + systemctl status "$service" --no-pager + return 1 + fi + return 0 } # ------------------------------------------------------------------------------ # Enable and start service (with error handling) # ------------------------------------------------------------------------------ enable_and_start_service() { - local service="$1" + local service="$1" - if ! systemctl enable "$service" &>/dev/null; then - msg_warn "Could not enable $service (may not be installed yet)" - fi + if ! systemctl enable "$service" &>/dev/null; then + msg_warn "Could not enable $service (may not be installed yet)" + fi - if ! systemctl start "$service" &>/dev/null; then - msg_error "Failed to start $service" - systemctl status "$service" --no-pager - return 1 - fi + if ! systemctl start "$service" &>/dev/null; then + msg_error "Failed to start $service" + systemctl status "$service" --no-pager + return 1 + fi - return 0 + return 0 } # ------------------------------------------------------------------------------ # Check if service is enabled # ------------------------------------------------------------------------------ is_service_enabled() { - local service="$1" - systemctl is-enabled --quiet "$service" 2>/dev/null + local service="$1" + systemctl is-enabled --quiet "$service" 2>/dev/null } # ------------------------------------------------------------------------------ # Check if service is running # ------------------------------------------------------------------------------ is_service_running() { - local service="$1" - systemctl is-active --quiet "$service" 2>/dev/null + local service="$1" + systemctl is-active --quiet "$service" 2>/dev/null } # ------------------------------------------------------------------------------ # Extract version from JSON (GitHub releases) # ------------------------------------------------------------------------------ extract_version_from_json() { - local json="$1" - local field="${2:-tag_name}" - local strip_v="${3:-true}" + local json="$1" + local field="${2:-tag_name}" + local strip_v="${3:-true}" - ensure_dependencies jq + ensure_dependencies jq - local version - version=$(echo "$json" | jq -r ".${field} // empty") + local version + version=$(echo "$json" | jq -r ".${field} // empty") - if [[ -z "$version" ]]; then - return 1 - fi + if [[ -z "$version" ]]; then + return 1 + fi - if [[ "$strip_v" == "true" ]]; then - echo "${version#v}" - else - echo "$version" - fi + if [[ "$strip_v" == "true" ]]; then + echo "${version#v}" + else + echo "$version" + fi } # ------------------------------------------------------------------------------ # Get latest GitHub release version # ------------------------------------------------------------------------------ get_latest_github_release() { - local repo="$1" - local strip_v="${2:-true}" - local temp_file=$(mktemp) + local repo="$1" + local strip_v="${2:-true}" + local temp_file=$(mktemp) - if ! github_api_call "https://api.github.com/repos/${repo}/releases/latest" "$temp_file"; then + if ! github_api_call "https://api.github.com/repos/${repo}/releases/latest" "$temp_file"; then + rm -f "$temp_file" + return 1 + fi + + local version + version=$(extract_version_from_json "$(cat "$temp_file")" "tag_name" "$strip_v") rm -f "$temp_file" - return 1 - fi - local version - version=$(extract_version_from_json "$(cat "$temp_file")" "tag_name" "$strip_v") - rm -f "$temp_file" + if [[ -z "$version" ]]; then + return 1 + fi - if [[ -z "$version" ]]; then - return 1 - fi - - echo "$version" + echo "$version" } # ------------------------------------------------------------------------------ # Debug logging (only if DEBUG=1) # ------------------------------------------------------------------------------ debug_log() { - [[ "${DEBUG:-0}" == "1" ]] && echo "[DEBUG] $*" >&2 + [[ "${DEBUG:-0}" == "1" ]] && echo "[DEBUG] $*" >&2 } # ------------------------------------------------------------------------------ # Performance timing helper # ------------------------------------------------------------------------------ start_timer() { - echo $(date +%s) + echo $(date +%s) } end_timer() { - local start_time="$1" - local label="${2:-Operation}" - local end_time=$(date +%s) - local duration=$((end_time - start_time)) - debug_log "$label took ${duration}s" + local start_time="$1" + local label="${2:-Operation}" + local end_time=$(date +%s) + local duration=$((end_time - start_time)) + debug_log "$label took ${duration}s" } # ------------------------------------------------------------------------------ # GPG key fingerprint verification # ------------------------------------------------------------------------------ verify_gpg_fingerprint() { - local key_file="$1" - local expected_fingerprint="$2" + local key_file="$1" + local expected_fingerprint="$2" - local actual_fingerprint - actual_fingerprint=$(gpg --show-keys --with-fingerprint --with-colons "$key_file" 2>&1 | grep -m1 '^fpr:' | cut -d: -f10) + local actual_fingerprint + actual_fingerprint=$(gpg --show-keys --with-fingerprint --with-colons "$key_file" 2>&1 | grep -m1 '^fpr:' | cut -d: -f10) - if [[ "$actual_fingerprint" == "$expected_fingerprint" ]]; then - return 0 - fi + if [[ "$actual_fingerprint" == "$expected_fingerprint" ]]; then + return 0 + fi - msg_error "GPG fingerprint mismatch! Expected: $expected_fingerprint, Got: $actual_fingerprint" - return 1 + msg_error "GPG fingerprint mismatch! Expected: $expected_fingerprint, Got: $actual_fingerprint" + return 1 } # ============================================================================== @@ -759,101 +759,101 @@ verify_gpg_fingerprint() { # - Does not support pre-releases # ------------------------------------------------------------------------------ check_for_gh_release() { - local app="$1" - local source="$2" - local pinned_version_in="${3:-}" # optional - local app_lc="${app,,}" - local current_file="$HOME/.${app_lc}" + local app="$1" + local source="$2" + local pinned_version_in="${3:-}" # optional + local app_lc="${app,,}" + local current_file="$HOME/.${app_lc}" - msg_info "Checking for update: ${app}" + msg_info "Checking for update: ${app}" - # DNS check - if ! getent hosts api.github.com >/dev/null 2>&1; then - msg_error "Network error: cannot resolve api.github.com" - return 1 - fi - - ensure_dependencies jq - - # Fetch releases and exclude drafts/prereleases - local releases_json - releases_json=$(curl -fsSL --max-time 20 \ - -H 'Accept: application/vnd.github+json' \ - -H 'X-GitHub-Api-Version: 2022-11-28' \ - "https://api.github.com/repos/${source}/releases") || { - msg_error "Unable to fetch releases for ${app}" - return 1 - } - - mapfile -t raw_tags < <(jq -r '.[] | select(.draft==false and .prerelease==false) | .tag_name' <<<"$releases_json") - if ((${#raw_tags[@]} == 0)); then - msg_error "No stable releases found for ${app}" - return 1 - fi - - local clean_tags=() - for t in "${raw_tags[@]}"; do - clean_tags+=("${t#v}") - done - - local latest_raw="${raw_tags[0]}" - local latest_clean="${clean_tags[0]}" - - # current installed (stored without v) - local current="" - if [[ -f "$current_file" ]]; then - current="$(<"$current_file")" - else - # Migration: search for any /opt/*_version.txt - local legacy_files - mapfile -t legacy_files < <(find /opt -maxdepth 1 -type f -name "*_version.txt" 2>/dev/null) - if ((${#legacy_files[@]} == 1)); then - current="$(<"${legacy_files[0]}")" - echo "${current#v}" >"$current_file" - rm -f "${legacy_files[0]}" + # DNS check + if ! getent hosts api.github.com >/dev/null 2>&1; then + msg_error "Network error: cannot resolve api.github.com" + return 1 fi - fi - current="${current#v}" - # Pinned version handling - if [[ -n "$pinned_version_in" ]]; then - local pin_clean="${pinned_version_in#v}" - local match_raw="" - for i in "${!clean_tags[@]}"; do - if [[ "${clean_tags[$i]}" == "$pin_clean" ]]; then - match_raw="${raw_tags[$i]}" - break - fi + ensure_dependencies jq + + # Fetch releases and exclude drafts/prereleases + local releases_json + releases_json=$(curl -fsSL --max-time 20 \ + -H 'Accept: application/vnd.github+json' \ + -H 'X-GitHub-Api-Version: 2022-11-28' \ + "https://api.github.com/repos/${source}/releases") || { + msg_error "Unable to fetch releases for ${app}" + return 1 + } + + mapfile -t raw_tags < <(jq -r '.[] | select(.draft==false and .prerelease==false) | .tag_name' <<<"$releases_json") + if ((${#raw_tags[@]} == 0)); then + msg_error "No stable releases found for ${app}" + return 1 + fi + + local clean_tags=() + for t in "${raw_tags[@]}"; do + clean_tags+=("${t#v}") done - if [[ -z "$match_raw" ]]; then - msg_error "Pinned version ${pinned_version_in} not found upstream" - return 1 - fi + local latest_raw="${raw_tags[0]}" + local latest_clean="${clean_tags[0]}" - if [[ "$current" != "$pin_clean" ]]; then - msg_info "${app} pinned to ${pinned_version_in} (installed ${current:-none}) → update required" - CHECK_UPDATE_RELEASE="$match_raw" - return 0 - fi - - if [[ "$pin_clean" == "$latest_clean" ]]; then - msg_ok "${app} pinned to ${pinned_version_in} (up to date)" + # current installed (stored without v) + local current="" + if [[ -f "$current_file" ]]; then + current="$(<"$current_file")" else - msg_ok "${app} pinned to ${pinned_version_in} (already installed, upstream ${latest_raw})" + # Migration: search for any /opt/*_version.txt + local legacy_files + mapfile -t legacy_files < <(find /opt -maxdepth 1 -type f -name "*_version.txt" 2>/dev/null) + if ((${#legacy_files[@]} == 1)); then + current="$(<"${legacy_files[0]}")" + echo "${current#v}" >"$current_file" + rm -f "${legacy_files[0]}" + fi fi + current="${current#v}" + + # Pinned version handling + if [[ -n "$pinned_version_in" ]]; then + local pin_clean="${pinned_version_in#v}" + local match_raw="" + for i in "${!clean_tags[@]}"; do + if [[ "${clean_tags[$i]}" == "$pin_clean" ]]; then + match_raw="${raw_tags[$i]}" + break + fi + done + + if [[ -z "$match_raw" ]]; then + msg_error "Pinned version ${pinned_version_in} not found upstream" + return 1 + fi + + if [[ "$current" != "$pin_clean" ]]; then + msg_info "${app} pinned to ${pinned_version_in} (installed ${current:-none}) → update required" + CHECK_UPDATE_RELEASE="$match_raw" + return 0 + fi + + if [[ "$pin_clean" == "$latest_clean" ]]; then + msg_ok "${app} pinned to ${pinned_version_in} (up to date)" + else + msg_ok "${app} pinned to ${pinned_version_in} (already installed, upstream ${latest_raw})" + fi + return 1 + fi + + # No pinning → use latest + if [[ -z "$current" || "$current" != "$latest_clean" ]]; then + CHECK_UPDATE_RELEASE="$latest_raw" + msg_info "New release available: ${latest_raw} (current: v${current:-none})" + return 0 + fi + + msg_ok "${app} is up to date (${latest_raw})" return 1 - fi - - # No pinning → use latest - if [[ -z "$current" || "$current" != "$latest_clean" ]]; then - CHECK_UPDATE_RELEASE="$latest_raw" - msg_info "New release available: ${latest_raw} (current: v${current:-none})" - return 0 - fi - - msg_ok "${app} is up to date (${latest_raw})" - return 1 } # ------------------------------------------------------------------------------ @@ -866,26 +866,26 @@ check_for_gh_release() { # APP - Application name (default: $APPLICATION variable) # ------------------------------------------------------------------------------ create_self_signed_cert() { - local APP_NAME="${1:-${APPLICATION}}" - local CERT_DIR="/etc/ssl/${APP_NAME}" - local CERT_KEY="${CERT_DIR}/${APP_NAME}.key" - local CERT_CRT="${CERT_DIR}/${APP_NAME}.crt" + local APP_NAME="${1:-${APPLICATION}}" + local CERT_DIR="/etc/ssl/${APP_NAME}" + local CERT_KEY="${CERT_DIR}/${APP_NAME}.key" + local CERT_CRT="${CERT_DIR}/${APP_NAME}.crt" - if [[ -f "$CERT_CRT" && -f "$CERT_KEY" ]]; then - return 0 - fi + if [[ -f "$CERT_CRT" && -f "$CERT_KEY" ]]; then + return 0 + fi - $STD apt update - $STD apt install -y openssl + $STD apt update + $STD apt install -y openssl - mkdir -p "$CERT_DIR" - $STD openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 \ - -subj "/C=US/ST=State/L=City/O=Organization/CN=${APP_NAME}" \ - -keyout "$CERT_KEY" \ - -out "$CERT_CRT" + mkdir -p "$CERT_DIR" + $STD openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 \ + -subj "/C=US/ST=State/L=City/O=Organization/CN=${APP_NAME}" \ + -keyout "$CERT_KEY" \ + -out "$CERT_CRT" - chmod 600 "$CERT_KEY" - chmod 644 "$CERT_CRT" + chmod 600 "$CERT_KEY" + chmod 644 "$CERT_CRT" } # ------------------------------------------------------------------------------ @@ -897,28 +897,28 @@ create_self_signed_cert() { # ------------------------------------------------------------------------------ function download_with_progress() { - local url="$1" - local output="$2" - if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi + local url="$1" + local output="$2" + if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi - ensure_dependencies pv - set -o pipefail + ensure_dependencies pv + set -o pipefail - # Content-Length aus HTTP-Header holen - local content_length - content_length=$(curl -fsSLI "$url" | awk '/Content-Length/ {print $2}' | tr -d '\r' || true) + # Content-Length aus HTTP-Header holen + local content_length + content_length=$(curl -fsSLI "$url" | awk '/Content-Length/ {print $2}' | tr -d '\r' || true) - if [[ -z "$content_length" ]]; then - if ! curl -fL# -o "$output" "$url"; then - msg_error "Download failed" - return 1 + if [[ -z "$content_length" ]]; then + if ! curl -fL# -o "$output" "$url"; then + msg_error "Download failed" + return 1 + fi + else + if ! curl -fsSL "$url" | pv -s "$content_length" >"$output"; then + msg_error "Download failed" + return 1 + fi fi - else - if ! curl -fsSL "$url" | pv -s "$content_length" >"$output"; then - msg_error "Download failed" - return 1 - fi - fi } # ------------------------------------------------------------------------------ @@ -929,12 +929,12 @@ function download_with_progress() { # ------------------------------------------------------------------------------ function ensure_usr_local_bin_persist() { - local PROFILE_FILE="/etc/profile.d/custom_path.sh" + local PROFILE_FILE="/etc/profile.d/custom_path.sh" - if [[ ! -f "$PROFILE_FILE" ]] && ! command -v pveversion &>/dev/null; then - echo 'export PATH="/usr/local/bin:$PATH"' >"$PROFILE_FILE" - chmod +x "$PROFILE_FILE" - fi + if [[ ! -f "$PROFILE_FILE" ]] && ! command -v pveversion &>/dev/null; then + echo 'export PATH="/usr/local/bin:$PATH"' >"$PROFILE_FILE" + chmod +x "$PROFILE_FILE" + fi } # ------------------------------------------------------------------------------ @@ -982,303 +982,303 @@ function ensure_usr_local_bin_persist() { # ------------------------------------------------------------------------------ function fetch_and_deploy_gh_release() { - local app="$1" - local repo="$2" - local mode="${3:-tarball}" # tarball | binary | prebuild | singlefile - local version="${4:-latest}" - local target="${5:-/opt/$app}" - local asset_pattern="${6:-}" + local app="$1" + local repo="$2" + local mode="${3:-tarball}" # tarball | binary | prebuild | singlefile + local version="${4:-latest}" + local target="${5:-/opt/$app}" + local asset_pattern="${6:-}" - local app_lc=$(echo "${app,,}" | tr -d ' ') - local version_file="$HOME/.${app_lc}" + local app_lc=$(echo "${app,,}" | tr -d ' ') + local version_file="$HOME/.${app_lc}" - local api_timeout="--connect-timeout 10 --max-time 60" - local download_timeout="--connect-timeout 15 --max-time 900" + local api_timeout="--connect-timeout 10 --max-time 60" + local download_timeout="--connect-timeout 15 --max-time 900" - local current_version="" - [[ -f "$version_file" ]] && current_version=$(<"$version_file") + local current_version="" + [[ -f "$version_file" ]] && current_version=$(<"$version_file") - ensure_dependencies jq + ensure_dependencies jq - local api_url="https://api.github.com/repos/$repo/releases" - [[ "$version" != "latest" ]] && api_url="$api_url/tags/$version" || api_url="$api_url/latest" - local header=() - [[ -n "${GITHUB_TOKEN:-}" ]] && header=(-H "Authorization: token $GITHUB_TOKEN") + local api_url="https://api.github.com/repos/$repo/releases" + [[ "$version" != "latest" ]] && api_url="$api_url/tags/$version" || api_url="$api_url/latest" + local header=() + [[ -n "${GITHUB_TOKEN:-}" ]] && header=(-H "Authorization: token $GITHUB_TOKEN") - # dns pre check - local gh_host - gh_host=$(awk -F/ '{print $3}' <<<"$api_url") - if ! getent hosts "$gh_host" &>/dev/null; then - msg_error "DNS resolution failed for $gh_host – check /etc/resolv.conf or networking" - return 1 - fi + # dns pre check + local gh_host + gh_host=$(awk -F/ '{print $3}' <<<"$api_url") + if ! getent hosts "$gh_host" &>/dev/null; then + msg_error "DNS resolution failed for $gh_host – check /etc/resolv.conf or networking" + return 1 + fi - local max_retries=3 retry_delay=2 attempt=1 success=false resp http_code + local max_retries=3 retry_delay=2 attempt=1 success=false resp http_code - while ((attempt <= max_retries)); do - resp=$(curl $api_timeout -fsSL -w "%{http_code}" -o /tmp/gh_rel.json "${header[@]}" "$api_url") && success=true && break - sleep "$retry_delay" - ((attempt++)) - done + while ((attempt <= max_retries)); do + resp=$(curl $api_timeout -fsSL -w "%{http_code}" -o /tmp/gh_rel.json "${header[@]}" "$api_url") && success=true && break + sleep "$retry_delay" + ((attempt++)) + done - if ! $success; then - msg_error "Failed to fetch release metadata from $api_url after $max_retries attempts" - return 1 - fi + if ! $success; then + msg_error "Failed to fetch release metadata from $api_url after $max_retries attempts" + return 1 + fi - http_code="${resp:(-3)}" - [[ "$http_code" != "200" ]] && { - msg_error "GitHub API returned HTTP $http_code" - return 1 - } - - local json tag_name - json=$(/dev/null || uname -m) - [[ "$arch" == "x86_64" ]] && arch="amd64" - [[ "$arch" == "aarch64" ]] && arch="arm64" + local clean_install=false + [[ -n "${CLEAN_INSTALL:-}" && "$CLEAN_INSTALL" == "1" ]] && clean_install=true - local assets url_match="" - assets=$(echo "$json" | jq -r '.assets[].browser_download_url') + ### Tarball Mode ### + if [[ "$mode" == "tarball" || "$mode" == "source" ]]; then + url=$(echo "$json" | jq -r '.tarball_url // empty') + [[ -z "$url" ]] && url="https://github.com/$repo/archive/refs/tags/v$version.tar.gz" + filename="${app_lc}-${version}.tar.gz" - # If explicit filename pattern is provided (param $6), match that first - if [[ -n "$asset_pattern" ]]; then - for u in $assets; do - case "${u##*/}" in - $asset_pattern) - url_match="$u" - break - ;; - esac - done - fi + curl $download_timeout -fsSL -o "$tmpdir/$filename" "$url" || { + msg_error "Download failed: $url" + rm -rf "$tmpdir" + return 1 + } - # If no match via explicit pattern, fall back to architecture heuristic - if [[ -z "$url_match" ]]; then - for u in $assets; do - if [[ "$u" =~ ($arch|amd64|x86_64|aarch64|arm64).*\.deb$ ]]; then - url_match="$u" - break + mkdir -p "$target" + if [[ "${CLEAN_INSTALL:-0}" == "1" ]]; then + rm -rf "${target:?}/"* fi - done - fi - # Fallback: any .deb file - if [[ -z "$url_match" ]]; then - for u in $assets; do - [[ "$u" =~ \.deb$ ]] && url_match="$u" && break - done - fi + tar -xzf "$tmpdir/$filename" -C "$tmpdir" + local unpack_dir + unpack_dir=$(find "$tmpdir" -mindepth 1 -maxdepth 1 -type d | head -n1) - if [[ -z "$url_match" ]]; then - msg_error "No suitable .deb asset found for $app" - rm -rf "$tmpdir" - return 1 - fi + shopt -s dotglob nullglob + cp -r "$unpack_dir"/* "$target/" + shopt -u dotglob nullglob - filename="${url_match##*/}" - curl $download_timeout -fsSL -o "$tmpdir/$filename" "$url_match" || { - msg_error "Download failed: $url_match" - rm -rf "$tmpdir" - return 1 - } + ### Binary Mode ### + elif [[ "$mode" == "binary" ]]; then + local arch + arch=$(dpkg --print-architecture 2>/dev/null || uname -m) + [[ "$arch" == "x86_64" ]] && arch="amd64" + [[ "$arch" == "aarch64" ]] && arch="arm64" - chmod 644 "$tmpdir/$filename" - $STD apt install -y "$tmpdir/$filename" || { - $STD dpkg -i "$tmpdir/$filename" || { - msg_error "Both apt and dpkg installation failed" + local assets url_match="" + assets=$(echo "$json" | jq -r '.assets[].browser_download_url') + + # If explicit filename pattern is provided (param $6), match that first + if [[ -n "$asset_pattern" ]]; then + for u in $assets; do + case "${u##*/}" in + $asset_pattern) + url_match="$u" + break + ;; + esac + done + fi + + # If no match via explicit pattern, fall back to architecture heuristic + if [[ -z "$url_match" ]]; then + for u in $assets; do + if [[ "$u" =~ ($arch|amd64|x86_64|aarch64|arm64).*\.deb$ ]]; then + url_match="$u" + break + fi + done + fi + + # Fallback: any .deb file + if [[ -z "$url_match" ]]; then + for u in $assets; do + [[ "$u" =~ \.deb$ ]] && url_match="$u" && break + done + fi + + if [[ -z "$url_match" ]]; then + msg_error "No suitable .deb asset found for $app" + rm -rf "$tmpdir" + return 1 + fi + + filename="${url_match##*/}" + curl $download_timeout -fsSL -o "$tmpdir/$filename" "$url_match" || { + msg_error "Download failed: $url_match" + rm -rf "$tmpdir" + return 1 + } + + chmod 644 "$tmpdir/$filename" + $STD apt install -y "$tmpdir/$filename" || { + $STD dpkg -i "$tmpdir/$filename" || { + msg_error "Both apt and dpkg installation failed" + rm -rf "$tmpdir" + return 1 + } + } + + ### Prebuild Mode ### + elif [[ "$mode" == "prebuild" ]]; then + local pattern="${6%\"}" + pattern="${pattern#\"}" + [[ -z "$pattern" ]] && { + msg_error "Mode 'prebuild' requires 6th parameter (asset filename pattern)" + rm -rf "$tmpdir" + return 1 + } + + local asset_url="" + for u in $(echo "$json" | jq -r '.assets[].browser_download_url'); do + filename_candidate="${u##*/}" + case "$filename_candidate" in + $pattern) + asset_url="$u" + break + ;; + esac + done + + [[ -z "$asset_url" ]] && { + msg_error "No asset matching '$pattern' found" + rm -rf "$tmpdir" + return 1 + } + + filename="${asset_url##*/}" + curl $download_timeout -fsSL -o "$tmpdir/$filename" "$asset_url" || { + msg_error "Download failed: $asset_url" + rm -rf "$tmpdir" + return 1 + } + + local unpack_tmp + unpack_tmp=$(mktemp -d) + mkdir -p "$target" + if [[ "${CLEAN_INSTALL:-0}" == "1" ]]; then + rm -rf "${target:?}/"* + fi + + if [[ "$filename" == *.zip ]]; then + ensure_dependencies unzip + unzip -q "$tmpdir/$filename" -d "$unpack_tmp" + elif [[ "$filename" == *.tar.* || "$filename" == *.tgz ]]; then + tar -xf "$tmpdir/$filename" -C "$unpack_tmp" + else + msg_error "Unsupported archive format: $filename" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + fi + + local top_dirs + top_dirs=$(find "$unpack_tmp" -mindepth 1 -maxdepth 1 -type d | wc -l) + local top_entries inner_dir + top_entries=$(find "$unpack_tmp" -mindepth 1 -maxdepth 1) + if [[ "$(echo "$top_entries" | wc -l)" -eq 1 && -d "$top_entries" ]]; then + # Strip leading folder + inner_dir="$top_entries" + shopt -s dotglob nullglob + if compgen -G "$inner_dir/*" >/dev/null; then + cp -r "$inner_dir"/* "$target/" || { + msg_error "Failed to copy contents from $inner_dir to $target" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + } + else + msg_error "Inner directory is empty: $inner_dir" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + fi + shopt -u dotglob nullglob + else + # Copy all contents + shopt -s dotglob nullglob + if compgen -G "$unpack_tmp/*" >/dev/null; then + cp -r "$unpack_tmp"/* "$target/" || { + msg_error "Failed to copy contents to $target" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + } + else + msg_error "Unpacked archive is empty" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + fi + shopt -u dotglob nullglob + fi + + ### Singlefile Mode ### + elif [[ "$mode" == "singlefile" ]]; then + local pattern="${6%\"}" + pattern="${pattern#\"}" + [[ -z "$pattern" ]] && { + msg_error "Mode 'singlefile' requires 6th parameter (asset filename pattern)" + rm -rf "$tmpdir" + return 1 + } + + local asset_url="" + for u in $(echo "$json" | jq -r '.assets[].browser_download_url'); do + filename_candidate="${u##*/}" + case "$filename_candidate" in + $pattern) + asset_url="$u" + break + ;; + esac + done + + [[ -z "$asset_url" ]] && { + msg_error "No asset matching '$pattern' found" + rm -rf "$tmpdir" + return 1 + } + + filename="${asset_url##*/}" + mkdir -p "$target" + + local use_filename="${USE_ORIGINAL_FILENAME:-false}" + local target_file="$app" + [[ "$use_filename" == "true" ]] && target_file="$filename" + + curl $download_timeout -fsSL -o "$target/$target_file" "$asset_url" || { + msg_error "Download failed: $asset_url" + rm -rf "$tmpdir" + return 1 + } + + if [[ "$target_file" != *.jar && -f "$target/$target_file" ]]; then + chmod +x "$target/$target_file" + fi + + else + msg_error "Unknown mode: $mode" rm -rf "$tmpdir" return 1 - } - } - - ### Prebuild Mode ### - elif [[ "$mode" == "prebuild" ]]; then - local pattern="${6%\"}" - pattern="${pattern#\"}" - [[ -z "$pattern" ]] && { - msg_error "Mode 'prebuild' requires 6th parameter (asset filename pattern)" - rm -rf "$tmpdir" - return 1 - } - - local asset_url="" - for u in $(echo "$json" | jq -r '.assets[].browser_download_url'); do - filename_candidate="${u##*/}" - case "$filename_candidate" in - $pattern) - asset_url="$u" - break - ;; - esac - done - - [[ -z "$asset_url" ]] && { - msg_error "No asset matching '$pattern' found" - rm -rf "$tmpdir" - return 1 - } - - filename="${asset_url##*/}" - curl $download_timeout -fsSL -o "$tmpdir/$filename" "$asset_url" || { - msg_error "Download failed: $asset_url" - rm -rf "$tmpdir" - return 1 - } - - local unpack_tmp - unpack_tmp=$(mktemp -d) - mkdir -p "$target" - if [[ "${CLEAN_INSTALL:-0}" == "1" ]]; then - rm -rf "${target:?}/"* fi - if [[ "$filename" == *.zip ]]; then - ensure_dependencies unzip - unzip -q "$tmpdir/$filename" -d "$unpack_tmp" - elif [[ "$filename" == *.tar.* || "$filename" == *.tgz ]]; then - tar -xf "$tmpdir/$filename" -C "$unpack_tmp" - else - msg_error "Unsupported archive format: $filename" - rm -rf "$tmpdir" "$unpack_tmp" - return 1 - fi - - local top_dirs - top_dirs=$(find "$unpack_tmp" -mindepth 1 -maxdepth 1 -type d | wc -l) - local top_entries inner_dir - top_entries=$(find "$unpack_tmp" -mindepth 1 -maxdepth 1) - if [[ "$(echo "$top_entries" | wc -l)" -eq 1 && -d "$top_entries" ]]; then - # Strip leading folder - inner_dir="$top_entries" - shopt -s dotglob nullglob - if compgen -G "$inner_dir/*" >/dev/null; then - cp -r "$inner_dir"/* "$target/" || { - msg_error "Failed to copy contents from $inner_dir to $target" - rm -rf "$tmpdir" "$unpack_tmp" - return 1 - } - else - msg_error "Inner directory is empty: $inner_dir" - rm -rf "$tmpdir" "$unpack_tmp" - return 1 - fi - shopt -u dotglob nullglob - else - # Copy all contents - shopt -s dotglob nullglob - if compgen -G "$unpack_tmp/*" >/dev/null; then - cp -r "$unpack_tmp"/* "$target/" || { - msg_error "Failed to copy contents to $target" - rm -rf "$tmpdir" "$unpack_tmp" - return 1 - } - else - msg_error "Unpacked archive is empty" - rm -rf "$tmpdir" "$unpack_tmp" - return 1 - fi - shopt -u dotglob nullglob - fi - - ### Singlefile Mode ### - elif [[ "$mode" == "singlefile" ]]; then - local pattern="${6%\"}" - pattern="${pattern#\"}" - [[ -z "$pattern" ]] && { - msg_error "Mode 'singlefile' requires 6th parameter (asset filename pattern)" - rm -rf "$tmpdir" - return 1 - } - - local asset_url="" - for u in $(echo "$json" | jq -r '.assets[].browser_download_url'); do - filename_candidate="${u##*/}" - case "$filename_candidate" in - $pattern) - asset_url="$u" - break - ;; - esac - done - - [[ -z "$asset_url" ]] && { - msg_error "No asset matching '$pattern' found" - rm -rf "$tmpdir" - return 1 - } - - filename="${asset_url##*/}" - mkdir -p "$target" - - local use_filename="${USE_ORIGINAL_FILENAME:-false}" - local target_file="$app" - [[ "$use_filename" == "true" ]] && target_file="$filename" - - curl $download_timeout -fsSL -o "$target/$target_file" "$asset_url" || { - msg_error "Download failed: $asset_url" - rm -rf "$tmpdir" - return 1 - } - - if [[ "$target_file" != *.jar && -f "$target/$target_file" ]]; then - chmod +x "$target/$target_file" - fi - - else - msg_error "Unknown mode: $mode" + echo "$version" >"$version_file" + msg_ok "Deployed: $app ($version)" rm -rf "$tmpdir" - return 1 - fi - - echo "$version" >"$version_file" - msg_ok "Deployed: $app ($version)" - rm -rf "$tmpdir" } # ------------------------------------------------------------------------------ @@ -1289,40 +1289,40 @@ function fetch_and_deploy_gh_release() { # ------------------------------------------------------------------------------ function import_local_ip() { - local IP_FILE="/run/local-ip.env" - if [[ -f "$IP_FILE" ]]; then - # shellcheck disable=SC1090 - source "$IP_FILE" - fi - - if [[ -z "${LOCAL_IP:-}" ]]; then - get_current_ip() { - local targets=("8.8.8.8" "1.1.1.1" "192.168.1.1" "10.0.0.1" "172.16.0.1" "default") - local ip - - for target in "${targets[@]}"; do - if [[ "$target" == "default" ]]; then - ip=$(ip route get 1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') - else - ip=$(ip route get "$target" 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') - fi - if [[ -n "$ip" ]]; then - echo "$ip" - return 0 - fi - done - - return 1 - } - - LOCAL_IP="$(get_current_ip || true)" - if [[ -z "$LOCAL_IP" ]]; then - msg_error "Could not determine LOCAL_IP" - return 1 + local IP_FILE="/run/local-ip.env" + if [[ -f "$IP_FILE" ]]; then + # shellcheck disable=SC1090 + source "$IP_FILE" fi - fi - export LOCAL_IP + if [[ -z "${LOCAL_IP:-}" ]]; then + get_current_ip() { + local targets=("8.8.8.8" "1.1.1.1" "192.168.1.1" "10.0.0.1" "172.16.0.1" "default") + local ip + + for target in "${targets[@]}"; do + if [[ "$target" == "default" ]]; then + ip=$(ip route get 1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') + else + ip=$(ip route get "$target" 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') + fi + if [[ -n "$ip" ]]; then + echo "$ip" + return 0 + fi + done + + return 1 + } + + LOCAL_IP="$(get_current_ip || true)" + if [[ -z "$LOCAL_IP" ]]; then + msg_error "Could not determine LOCAL_IP" + return 1 + fi + fi + + export LOCAL_IP } # ------------------------------------------------------------------------------ @@ -1334,29 +1334,29 @@ function import_local_ip() { # ------------------------------------------------------------------------------ function setup_adminer() { - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "adminer") + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "adminer") - if grep -qi alpine /etc/os-release; then - msg_info "Installing Adminer (Alpine)" - mkdir -p /var/www/localhost/htdocs/adminer - curl -fsSL https://github.com/vrana/adminer/releases/latest/download/adminer.php \ - -o /var/www/localhost/htdocs/adminer/index.php || { - msg_error "Failed to download Adminer" - return 1 - } - cache_installed_version "adminer" "latest-alpine" - msg_ok "Installed Adminer (Alpine)" - else - msg_info "Installing Adminer (Debian/Ubuntu)" - ensure_dependencies adminer - $STD a2enconf adminer - $STD systemctl reload apache2 - local VERSION - VERSION=$(dpkg -s adminer 2>/dev/null | grep '^Version:' | awk '{print $2}') - cache_installed_version "adminer" "${VERSION:-unknown}" - msg_ok "Installed Adminer (Debian/Ubuntu)" - fi + if grep -qi alpine /etc/os-release; then + msg_info "Installing Adminer (Alpine)" + mkdir -p /var/www/localhost/htdocs/adminer + curl -fsSL https://github.com/vrana/adminer/releases/latest/download/adminer.php \ + -o /var/www/localhost/htdocs/adminer/index.php || { + msg_error "Failed to download Adminer" + return 1 + } + cache_installed_version "adminer" "latest-alpine" + msg_ok "Installed Adminer (Alpine)" + else + msg_info "Installing Adminer (Debian/Ubuntu)" + ensure_dependencies adminer + $STD a2enconf adminer + $STD systemctl reload apache2 + local VERSION + VERSION=$(dpkg -s adminer 2>/dev/null | grep '^Version:' | awk '{print $2}') + cache_installed_version "adminer" "${VERSION:-unknown}" + msg_ok "Installed Adminer (Debian/Ubuntu)" + fi } # ------------------------------------------------------------------------------ @@ -1368,46 +1368,46 @@ function setup_adminer() { # ------------------------------------------------------------------------------ function setup_composer() { - local COMPOSER_BIN="/usr/local/bin/composer" - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "composer") - export COMPOSER_ALLOW_SUPERUSER=1 + local COMPOSER_BIN="/usr/local/bin/composer" + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "composer") + export COMPOSER_ALLOW_SUPERUSER=1 - for old in /usr/bin/composer /bin/composer /root/.composer/vendor/bin/composer; do - [[ -e "$old" && "$old" != "$COMPOSER_BIN" ]] && rm -f "$old" - done + for old in /usr/bin/composer /bin/composer /root/.composer/vendor/bin/composer; do + [[ -e "$old" && "$old" != "$COMPOSER_BIN" ]] && rm -f "$old" + done - ensure_usr_local_bin_persist - export PATH="/usr/local/bin:$PATH" + ensure_usr_local_bin_persist + export PATH="/usr/local/bin:$PATH" - if [[ -x "$COMPOSER_BIN" ]]; then - local CURRENT_VERSION - CURRENT_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}') - msg_info "Updating Composer from $CURRENT_VERSION to latest" - else - msg_info "Installing Composer" - fi + if [[ -x "$COMPOSER_BIN" ]]; then + local CURRENT_VERSION + CURRENT_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}') + msg_info "Updating Composer from $CURRENT_VERSION to latest" + else + msg_info "Installing Composer" + fi - curl -fsSL https://getcomposer.org/installer -o /tmp/composer-setup.php || { - msg_error "Failed to download Composer installer" - return 1 - } + curl -fsSL https://getcomposer.org/installer -o /tmp/composer-setup.php || { + msg_error "Failed to download Composer installer" + return 1 + } - $STD php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer - rm -f /tmp/composer-setup.php + $STD php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer + rm -f /tmp/composer-setup.php - if [[ ! -x "$COMPOSER_BIN" ]]; then - msg_error "Composer installation failed" - return 1 - fi + if [[ ! -x "$COMPOSER_BIN" ]]; then + msg_error "Composer installation failed" + return 1 + fi - chmod +x "$COMPOSER_BIN" - $STD "$COMPOSER_BIN" self-update --no-interaction || true + chmod +x "$COMPOSER_BIN" + $STD "$COMPOSER_BIN" self-update --no-interaction || true - local FINAL_VERSION - FINAL_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}') - cache_installed_version "composer" "$FINAL_VERSION" - msg_ok "Installed Composer $FINAL_VERSION" + local FINAL_VERSION + FINAL_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}') + cache_installed_version "composer" "$FINAL_VERSION" + msg_ok "Installed Composer $FINAL_VERSION" } # ------------------------------------------------------------------------------ @@ -1429,147 +1429,147 @@ function setup_composer() { # ------------------------------------------------------------------------------ function setup_ffmpeg() { - local TMP_DIR=$(mktemp -d) - local GITHUB_REPO="FFmpeg/FFmpeg" - local VERSION="${FFMPEG_VERSION:-latest}" - local TYPE="${FFMPEG_TYPE:-full}" - local BIN_PATH="/usr/local/bin/ffmpeg" - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "ffmpeg") + local TMP_DIR=$(mktemp -d) + local GITHUB_REPO="FFmpeg/FFmpeg" + local VERSION="${FFMPEG_VERSION:-latest}" + local TYPE="${FFMPEG_TYPE:-full}" + local BIN_PATH="/usr/local/bin/ffmpeg" + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "ffmpeg") - # Binary fallback mode - if [[ "$TYPE" == "binary" ]]; then - msg_info "Installing FFmpeg (static binary)" - curl -fsSL https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz -o "$TMP_DIR/ffmpeg.tar.xz" || { - msg_error "Failed to download FFmpeg binary" - rm -rf "$TMP_DIR" - return 1 + # Binary fallback mode + if [[ "$TYPE" == "binary" ]]; then + msg_info "Installing FFmpeg (static binary)" + curl -fsSL https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz -o "$TMP_DIR/ffmpeg.tar.xz" || { + msg_error "Failed to download FFmpeg binary" + rm -rf "$TMP_DIR" + return 1 + } + tar -xf "$TMP_DIR/ffmpeg.tar.xz" -C "$TMP_DIR" + local EXTRACTED_DIR + EXTRACTED_DIR=$(find "$TMP_DIR" -maxdepth 1 -type d -name "ffmpeg-*") + cp "$EXTRACTED_DIR/ffmpeg" "$BIN_PATH" + cp "$EXTRACTED_DIR/ffprobe" /usr/local/bin/ffprobe + chmod +x "$BIN_PATH" /usr/local/bin/ffprobe + local FINAL_VERSION=$($BIN_PATH -version | head -n1 | awk '{print $3}') + rm -rf "$TMP_DIR" + cache_installed_version "ffmpeg" "$FINAL_VERSION" + ensure_usr_local_bin_persist + msg_ok "Installed FFmpeg binary $FINAL_VERSION" + return 0 + fi + + ensure_dependencies jq + + # Auto-detect latest stable version if none specified + if [[ "$VERSION" == "latest" || -z "$VERSION" ]]; then + VERSION=$(curl -fsSL "https://api.github.com/repos/${GITHUB_REPO}/tags" | + jq -r '.[].name' | + grep -E '^n[0-9]+\.[0-9]+\.[0-9]+$' | + sort -V | tail -n1) + fi + + if [[ -z "$VERSION" ]]; then + msg_error "Could not determine FFmpeg version" + rm -rf "$TMP_DIR" + return 1 + fi + + msg_info "Installing FFmpeg ${VERSION} ($TYPE)" + + # Dependency selection + local DEPS=(build-essential yasm nasm pkg-config) + case "$TYPE" in + minimal) + DEPS+=(libx264-dev libvpx-dev libmp3lame-dev) + ;; + medium) + DEPS+=(libx264-dev libvpx-dev libmp3lame-dev libfreetype6-dev libass-dev libopus-dev libvorbis-dev) + ;; + full) + DEPS+=( + libx264-dev libx265-dev libvpx-dev libmp3lame-dev + libfreetype6-dev libass-dev libopus-dev libvorbis-dev + libdav1d-dev libsvtav1-dev zlib1g-dev libnuma-dev + libva-dev libdrm-dev + ) + ;; + *) + msg_error "Invalid FFMPEG_TYPE: $TYPE" + rm -rf "$TMP_DIR" + return 1 + ;; + esac + + ensure_dependencies "${DEPS[@]}" + + curl -fsSL "https://github.com/${GITHUB_REPO}/archive/refs/tags/${VERSION}.tar.gz" -o "$TMP_DIR/ffmpeg.tar.gz" || { + msg_error "Failed to download FFmpeg source" + rm -rf "$TMP_DIR" + return 1 } - tar -xf "$TMP_DIR/ffmpeg.tar.xz" -C "$TMP_DIR" - local EXTRACTED_DIR - EXTRACTED_DIR=$(find "$TMP_DIR" -maxdepth 1 -type d -name "ffmpeg-*") - cp "$EXTRACTED_DIR/ffmpeg" "$BIN_PATH" - cp "$EXTRACTED_DIR/ffprobe" /usr/local/bin/ffprobe - chmod +x "$BIN_PATH" /usr/local/bin/ffprobe - local FINAL_VERSION=$($BIN_PATH -version | head -n1 | awk '{print $3}') + + tar -xzf "$TMP_DIR/ffmpeg.tar.gz" -C "$TMP_DIR" || { + msg_error "Failed to extract FFmpeg" + rm -rf "$TMP_DIR" + return 1 + } + + cd "$TMP_DIR/FFmpeg-"* || { + msg_error "Source extraction failed" + rm -rf "$TMP_DIR" + return 1 + } + + local args=( + --enable-gpl + --enable-shared + --enable-nonfree + --disable-static + --enable-libx264 + --enable-libvpx + --enable-libmp3lame + ) + + if [[ "$TYPE" != "minimal" ]]; then + args+=(--enable-libfreetype --enable-libass --enable-libopus --enable-libvorbis) + fi + + if [[ "$TYPE" == "full" ]]; then + args+=(--enable-libx265 --enable-libdav1d --enable-zlib) + args+=(--enable-vaapi --enable-libdrm) + fi + + if [[ ${#args[@]} -eq 0 ]]; then + msg_error "FFmpeg configure args array is empty" + rm -rf "$TMP_DIR" + return 1 + fi + + $STD ./configure "${args[@]}" + $STD make -j"$(nproc)" + $STD make install + echo "/usr/local/lib" >/etc/ld.so.conf.d/ffmpeg.conf + $STD ldconfig + + ldconfig -p | grep libavdevice >/dev/null || { + msg_error "libavdevice not registered with dynamic linker" + rm -rf "$TMP_DIR" + return 1 + } + + if ! command -v ffmpeg &>/dev/null; then + msg_error "FFmpeg installation failed" + rm -rf "$TMP_DIR" + return 1 + fi + + local FINAL_VERSION + FINAL_VERSION=$(ffmpeg -version | head -n1 | awk '{print $3}') rm -rf "$TMP_DIR" cache_installed_version "ffmpeg" "$FINAL_VERSION" ensure_usr_local_bin_persist - msg_ok "Installed FFmpeg binary $FINAL_VERSION" - return 0 - fi - - ensure_dependencies jq - - # Auto-detect latest stable version if none specified - if [[ "$VERSION" == "latest" || -z "$VERSION" ]]; then - VERSION=$(curl -fsSL "https://api.github.com/repos/${GITHUB_REPO}/tags" | - jq -r '.[].name' | - grep -E '^n[0-9]+\.[0-9]+\.[0-9]+$' | - sort -V | tail -n1) - fi - - if [[ -z "$VERSION" ]]; then - msg_error "Could not determine FFmpeg version" - rm -rf "$TMP_DIR" - return 1 - fi - - msg_info "Installing FFmpeg ${VERSION} ($TYPE)" - - # Dependency selection - local DEPS=(build-essential yasm nasm pkg-config) - case "$TYPE" in - minimal) - DEPS+=(libx264-dev libvpx-dev libmp3lame-dev) - ;; - medium) - DEPS+=(libx264-dev libvpx-dev libmp3lame-dev libfreetype6-dev libass-dev libopus-dev libvorbis-dev) - ;; - full) - DEPS+=( - libx264-dev libx265-dev libvpx-dev libmp3lame-dev - libfreetype6-dev libass-dev libopus-dev libvorbis-dev - libdav1d-dev libsvtav1-dev zlib1g-dev libnuma-dev - libva-dev libdrm-dev - ) - ;; - *) - msg_error "Invalid FFMPEG_TYPE: $TYPE" - rm -rf "$TMP_DIR" - return 1 - ;; - esac - - ensure_dependencies "${DEPS[@]}" - - curl -fsSL "https://github.com/${GITHUB_REPO}/archive/refs/tags/${VERSION}.tar.gz" -o "$TMP_DIR/ffmpeg.tar.gz" || { - msg_error "Failed to download FFmpeg source" - rm -rf "$TMP_DIR" - return 1 - } - - tar -xzf "$TMP_DIR/ffmpeg.tar.gz" -C "$TMP_DIR" || { - msg_error "Failed to extract FFmpeg" - rm -rf "$TMP_DIR" - return 1 - } - - cd "$TMP_DIR/FFmpeg-"* || { - msg_error "Source extraction failed" - rm -rf "$TMP_DIR" - return 1 - } - - local args=( - --enable-gpl - --enable-shared - --enable-nonfree - --disable-static - --enable-libx264 - --enable-libvpx - --enable-libmp3lame - ) - - if [[ "$TYPE" != "minimal" ]]; then - args+=(--enable-libfreetype --enable-libass --enable-libopus --enable-libvorbis) - fi - - if [[ "$TYPE" == "full" ]]; then - args+=(--enable-libx265 --enable-libdav1d --enable-zlib) - args+=(--enable-vaapi --enable-libdrm) - fi - - if [[ ${#args[@]} -eq 0 ]]; then - msg_error "FFmpeg configure args array is empty" - rm -rf "$TMP_DIR" - return 1 - fi - - $STD ./configure "${args[@]}" - $STD make -j"$(nproc)" - $STD make install - echo "/usr/local/lib" >/etc/ld.so.conf.d/ffmpeg.conf - $STD ldconfig - - ldconfig -p | grep libavdevice >/dev/null || { - msg_error "libavdevice not registered with dynamic linker" - rm -rf "$TMP_DIR" - return 1 - } - - if ! command -v ffmpeg &>/dev/null; then - msg_error "FFmpeg installation failed" - rm -rf "$TMP_DIR" - return 1 - fi - - local FINAL_VERSION - FINAL_VERSION=$(ffmpeg -version | head -n1 | awk '{print $3}') - rm -rf "$TMP_DIR" - cache_installed_version "ffmpeg" "$FINAL_VERSION" - ensure_usr_local_bin_persist - msg_ok "Installed FFmpeg $FINAL_VERSION" + msg_ok "Installed FFmpeg $FINAL_VERSION" } # ------------------------------------------------------------------------------ @@ -1584,65 +1584,65 @@ function setup_ffmpeg() { # ------------------------------------------------------------------------------ function setup_go() { - local ARCH - case "$(uname -m)" in - x86_64) ARCH="amd64" ;; - aarch64) ARCH="arm64" ;; - *) - msg_error "Unsupported architecture: $(uname -m)" - return 1 - ;; - esac + local ARCH + case "$(uname -m)" in + x86_64) ARCH="amd64" ;; + aarch64) ARCH="arm64" ;; + *) + msg_error "Unsupported architecture: $(uname -m)" + return 1 + ;; + esac - # Determine version - if [[ -z "${GO_VERSION:-}" || "${GO_VERSION}" == "latest" ]]; then - GO_VERSION=$(curl -fsSL https://go.dev/VERSION?m=text | head -n1 | sed 's/^go//') - if [[ -z "$GO_VERSION" ]]; then - msg_error "Could not determine latest Go version" - return 1 + # Determine version + if [[ -z "${GO_VERSION:-}" || "${GO_VERSION}" == "latest" ]]; then + GO_VERSION=$(curl -fsSL https://go.dev/VERSION?m=text | head -n1 | sed 's/^go//') + if [[ -z "$GO_VERSION" ]]; then + msg_error "Could not determine latest Go version" + return 1 + fi fi - fi - local GO_BIN="/usr/local/bin/go" - local GO_INSTALL_DIR="/usr/local/go" - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "go") + local GO_BIN="/usr/local/bin/go" + local GO_INSTALL_DIR="/usr/local/go" + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "go") - if [[ -x "$GO_BIN" ]]; then - local CURRENT_VERSION - CURRENT_VERSION=$("$GO_BIN" version | awk '{print $3}' | sed 's/go//') - if [[ "$CURRENT_VERSION" == "$GO_VERSION" ]]; then - if [[ "$CACHED_VERSION" == "$GO_VERSION" ]]; then - return 0 - fi - cache_installed_version "go" "$GO_VERSION" - return 0 + if [[ -x "$GO_BIN" ]]; then + local CURRENT_VERSION + CURRENT_VERSION=$("$GO_BIN" version | awk '{print $3}' | sed 's/go//') + if [[ "$CURRENT_VERSION" == "$GO_VERSION" ]]; then + if [[ "$CACHED_VERSION" == "$GO_VERSION" ]]; then + return 0 + fi + cache_installed_version "go" "$GO_VERSION" + return 0 + else + msg_info "Upgrading Go from $CURRENT_VERSION to $GO_VERSION" + rm -rf "$GO_INSTALL_DIR" + fi else - msg_info "Upgrading Go from $CURRENT_VERSION to $GO_VERSION" - rm -rf "$GO_INSTALL_DIR" + msg_info "Installing Go $GO_VERSION" fi - else - msg_info "Installing Go $GO_VERSION" - fi - local TARBALL="go${GO_VERSION}.linux-${ARCH}.tar.gz" - local URL="https://go.dev/dl/${TARBALL}" - local TMP_TAR=$(mktemp) + local TARBALL="go${GO_VERSION}.linux-${ARCH}.tar.gz" + local URL="https://go.dev/dl/${TARBALL}" + local TMP_TAR=$(mktemp) - curl -fsSL "$URL" -o "$TMP_TAR" || { - msg_error "Failed to download $TARBALL" + curl -fsSL "$URL" -o "$TMP_TAR" || { + msg_error "Failed to download $TARBALL" + rm -f "$TMP_TAR" + return 1 + } + + $STD tar -C /usr/local -xzf "$TMP_TAR" + ln -sf /usr/local/go/bin/go /usr/local/bin/go + ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt rm -f "$TMP_TAR" - return 1 - } - $STD tar -C /usr/local -xzf "$TMP_TAR" - ln -sf /usr/local/go/bin/go /usr/local/bin/go - ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt - rm -f "$TMP_TAR" - - cache_installed_version "go" "$GO_VERSION" - ensure_usr_local_bin_persist - msg_ok "Installed Go $GO_VERSION" + cache_installed_version "go" "$GO_VERSION" + ensure_usr_local_bin_persist + msg_ok "Installed Go $GO_VERSION" } # ------------------------------------------------------------------------------ @@ -1654,73 +1654,73 @@ function setup_go() { # ------------------------------------------------------------------------------ function setup_gs() { - local TMP_DIR=$(mktemp -d) - local CURRENT_VERSION=$(gs --version 2>/dev/null || echo "0") - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "ghostscript") + local TMP_DIR=$(mktemp -d) + local CURRENT_VERSION=$(gs --version 2>/dev/null || echo "0") + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "ghostscript") - ensure_dependencies jq + ensure_dependencies jq - local RELEASE_JSON - RELEASE_JSON=$(curl -fsSL https://api.github.com/repos/ArtifexSoftware/ghostpdl-downloads/releases/latest) - local LATEST_VERSION - LATEST_VERSION=$(echo "$RELEASE_JSON" | jq -r '.tag_name' | sed 's/^gs//') - local LATEST_VERSION_DOTTED - LATEST_VERSION_DOTTED=$(echo "$RELEASE_JSON" | jq -r '.name' | grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+') + local RELEASE_JSON + RELEASE_JSON=$(curl -fsSL https://api.github.com/repos/ArtifexSoftware/ghostpdl-downloads/releases/latest) + local LATEST_VERSION + LATEST_VERSION=$(echo "$RELEASE_JSON" | jq -r '.tag_name' | sed 's/^gs//') + local LATEST_VERSION_DOTTED + LATEST_VERSION_DOTTED=$(echo "$RELEASE_JSON" | jq -r '.name' | grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+') - if [[ -z "$LATEST_VERSION" || -z "$LATEST_VERSION_DOTTED" ]]; then - msg_error "Could not determine latest Ghostscript version" - rm -rf "$TMP_DIR" - return 1 - fi - - if dpkg --compare-versions "$CURRENT_VERSION" ge "$LATEST_VERSION_DOTTED" 2>/dev/null; then - if [[ "$CACHED_VERSION" == "$LATEST_VERSION_DOTTED" ]]; then - rm -rf "$TMP_DIR" - return 0 + if [[ -z "$LATEST_VERSION" || -z "$LATEST_VERSION_DOTTED" ]]; then + msg_error "Could not determine latest Ghostscript version" + rm -rf "$TMP_DIR" + return 1 fi + + if dpkg --compare-versions "$CURRENT_VERSION" ge "$LATEST_VERSION_DOTTED" 2>/dev/null; then + if [[ "$CACHED_VERSION" == "$LATEST_VERSION_DOTTED" ]]; then + rm -rf "$TMP_DIR" + return 0 + fi + cache_installed_version "ghostscript" "$LATEST_VERSION_DOTTED" + rm -rf "$TMP_DIR" + return 0 + fi + + msg_info "Installing Ghostscript $LATEST_VERSION_DOTTED" + + curl -fsSL "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs${LATEST_VERSION}/ghostscript-${LATEST_VERSION_DOTTED}.tar.gz" -o "$TMP_DIR/ghostscript.tar.gz" || { + msg_error "Failed to download Ghostscript" + rm -rf "$TMP_DIR" + return 1 + } + + if ! tar -xzf "$TMP_DIR/ghostscript.tar.gz" -C "$TMP_DIR"; then + msg_error "Failed to extract Ghostscript archive" + rm -rf "$TMP_DIR" + return 1 + fi + + cd "$TMP_DIR/ghostscript-${LATEST_VERSION_DOTTED}" || { + msg_error "Failed to enter Ghostscript source directory" + rm -rf "$TMP_DIR" + return 1 + } + + ensure_dependencies build-essential libpng-dev zlib1g-dev + + $STD ./configure + $STD make -j"$(nproc)" + $STD make install + + hash -r + if [[ ! -x "$(command -v gs)" ]]; then + if [[ -x /usr/local/bin/gs ]]; then + ln -sf /usr/local/bin/gs /usr/bin/gs + fi + fi + + rm -rf "$TMP_DIR" cache_installed_version "ghostscript" "$LATEST_VERSION_DOTTED" - rm -rf "$TMP_DIR" - return 0 - fi - - msg_info "Installing Ghostscript $LATEST_VERSION_DOTTED" - - curl -fsSL "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs${LATEST_VERSION}/ghostscript-${LATEST_VERSION_DOTTED}.tar.gz" -o "$TMP_DIR/ghostscript.tar.gz" || { - msg_error "Failed to download Ghostscript" - rm -rf "$TMP_DIR" - return 1 - } - - if ! tar -xzf "$TMP_DIR/ghostscript.tar.gz" -C "$TMP_DIR"; then - msg_error "Failed to extract Ghostscript archive" - rm -rf "$TMP_DIR" - return 1 - fi - - cd "$TMP_DIR/ghostscript-${LATEST_VERSION_DOTTED}" || { - msg_error "Failed to enter Ghostscript source directory" - rm -rf "$TMP_DIR" - return 1 - } - - ensure_dependencies build-essential libpng-dev zlib1g-dev - - $STD ./configure - $STD make -j"$(nproc)" - $STD make install - - hash -r - if [[ ! -x "$(command -v gs)" ]]; then - if [[ -x /usr/local/bin/gs ]]; then - ln -sf /usr/local/bin/gs /usr/bin/gs - fi - fi - - rm -rf "$TMP_DIR" - cache_installed_version "ghostscript" "$LATEST_VERSION_DOTTED" - ensure_usr_local_bin_persist - msg_ok "Installed Ghostscript $LATEST_VERSION_DOTTED" + ensure_usr_local_bin_persist + msg_ok "Installed Ghostscript $LATEST_VERSION_DOTTED" } # ------------------------------------------------------------------------------ @@ -1735,79 +1735,79 @@ function setup_gs() { # - Requires: build-essential, libtool, libjpeg-dev, libpng-dev, etc. # ------------------------------------------------------------------------------ function setup_imagemagick() { - local TMP_DIR=$(mktemp -d) - local VERSION="" - local BINARY_PATH="/usr/local/bin/magick" - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "imagemagick") + local TMP_DIR=$(mktemp -d) + local VERSION="" + local BINARY_PATH="/usr/local/bin/magick" + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "imagemagick") - if command -v magick &>/dev/null; then - VERSION=$(magick -version | awk '/^Version/ {print $3}') - if [[ "$CACHED_VERSION" == "$VERSION" ]]; then - rm -rf "$TMP_DIR" - return 0 + if command -v magick &>/dev/null; then + VERSION=$(magick -version | awk '/^Version/ {print $3}') + if [[ "$CACHED_VERSION" == "$VERSION" ]]; then + rm -rf "$TMP_DIR" + return 0 + fi + cache_installed_version "imagemagick" "$VERSION" + rm -rf "$TMP_DIR" + return 0 fi + + msg_info "Installing ImageMagick (Patience)" + + ensure_dependencies \ + build-essential \ + libtool \ + libjpeg-dev \ + libpng-dev \ + libtiff-dev \ + libwebp-dev \ + libheif-dev \ + libde265-dev \ + libopenjp2-7-dev \ + libxml2-dev \ + liblcms2-dev \ + libfreetype6-dev \ + libraw-dev \ + libfftw3-dev \ + liblqr-1-0-dev \ + libgsl-dev \ + pkg-config \ + ghostscript + + curl -fsSL https://imagemagick.org/archive/ImageMagick.tar.gz -o "$TMP_DIR/ImageMagick.tar.gz" || { + msg_error "Failed to download ImageMagick" + rm -rf "$TMP_DIR" + return 1 + } + + tar -xzf "$TMP_DIR/ImageMagick.tar.gz" -C "$TMP_DIR" || { + msg_error "Failed to extract ImageMagick" + rm -rf "$TMP_DIR" + return 1 + } + + cd "$TMP_DIR"/ImageMagick-* || { + msg_error "Source extraction failed" + rm -rf "$TMP_DIR" + return 1 + } + + $STD ./configure --disable-static + $STD make -j"$(nproc)" + $STD make install + $STD ldconfig /usr/local/lib + + if [[ ! -x "$BINARY_PATH" ]]; then + msg_error "ImageMagick installation failed" + rm -rf "$TMP_DIR" + return 1 + fi + + VERSION=$("$BINARY_PATH" -version | awk '/^Version/ {print $3}') + rm -rf "$TMP_DIR" cache_installed_version "imagemagick" "$VERSION" - rm -rf "$TMP_DIR" - return 0 - fi - - msg_info "Installing ImageMagick (Patience)" - - ensure_dependencies \ - build-essential \ - libtool \ - libjpeg-dev \ - libpng-dev \ - libtiff-dev \ - libwebp-dev \ - libheif-dev \ - libde265-dev \ - libopenjp2-7-dev \ - libxml2-dev \ - liblcms2-dev \ - libfreetype6-dev \ - libraw-dev \ - libfftw3-dev \ - liblqr-1-0-dev \ - libgsl-dev \ - pkg-config \ - ghostscript - - curl -fsSL https://imagemagick.org/archive/ImageMagick.tar.gz -o "$TMP_DIR/ImageMagick.tar.gz" || { - msg_error "Failed to download ImageMagick" - rm -rf "$TMP_DIR" - return 1 - } - - tar -xzf "$TMP_DIR/ImageMagick.tar.gz" -C "$TMP_DIR" || { - msg_error "Failed to extract ImageMagick" - rm -rf "$TMP_DIR" - return 1 - } - - cd "$TMP_DIR"/ImageMagick-* || { - msg_error "Source extraction failed" - rm -rf "$TMP_DIR" - return 1 - } - - $STD ./configure --disable-static - $STD make -j"$(nproc)" - $STD make install - $STD ldconfig /usr/local/lib - - if [[ ! -x "$BINARY_PATH" ]]; then - msg_error "ImageMagick installation failed" - rm -rf "$TMP_DIR" - return 1 - fi - - VERSION=$("$BINARY_PATH" -version | awk '/^Version/ {print $3}') - rm -rf "$TMP_DIR" - cache_installed_version "imagemagick" "$VERSION" - ensure_usr_local_bin_persist - msg_ok "Installed ImageMagick $VERSION" + ensure_usr_local_bin_persist + msg_ok "Installed ImageMagick $VERSION" } # ------------------------------------------------------------------------------ @@ -1822,63 +1822,63 @@ function setup_imagemagick() { # ------------------------------------------------------------------------------ function setup_java() { - local JAVA_VERSION="${JAVA_VERSION:-21}" - local DISTRO_ID DISTRO_CODENAME - DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') - DISTRO_CODENAME=$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release) - local DESIRED_PACKAGE="temurin-${JAVA_VERSION}-jdk" + local JAVA_VERSION="${JAVA_VERSION:-21}" + local DISTRO_ID DISTRO_CODENAME + DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + DISTRO_CODENAME=$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release) + local DESIRED_PACKAGE="temurin-${JAVA_VERSION}-jdk" - # Check cached version - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "temurin-jdk") + # Check cached version + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "temurin-jdk") - # Add repo nur wenn nötig - if [[ ! -f /etc/apt/sources.list.d/adoptium.sources ]]; then - # Cleanup old repository files - cleanup_old_repo_files "adoptium" + # Add repo nur wenn nötig + if [[ ! -f /etc/apt/sources.list.d/adoptium.sources ]]; then + # Cleanup old repository files + cleanup_old_repo_files "adoptium" - # Use helper function to get fallback suite - local SUITE - SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://packages.adoptium.net/artifactory/deb") + # Use helper function to get fallback suite + local SUITE + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://packages.adoptium.net/artifactory/deb") - # Use standardized repo setup - setup_deb822_repo \ - "adoptium" \ - "https://packages.adoptium.net/artifactory/api/gpg/key/public" \ - "https://packages.adoptium.net/artifactory/deb" \ - "$SUITE" \ - "main" \ - "amd64 arm64" - fi + # Use standardized repo setup + setup_deb822_repo \ + "adoptium" \ + "https://packages.adoptium.net/artifactory/api/gpg/key/public" \ + "https://packages.adoptium.net/artifactory/deb" \ + "$SUITE" \ + "main" \ + "amd64 arm64" + fi - local INSTALLED_VERSION="" - if dpkg -l | grep -q "temurin-.*-jdk"; then - INSTALLED_VERSION=$(dpkg -l | awk '/temurin-.*-jdk/{print $2}' | grep -oP 'temurin-\K[0-9]+') - fi + local INSTALLED_VERSION="" + if dpkg -l | grep -q "temurin-.*-jdk"; then + INSTALLED_VERSION=$(dpkg -l | awk '/temurin-.*-jdk/{print $2}' | grep -oP 'temurin-\K[0-9]+') + fi - if [[ "$INSTALLED_VERSION" == "$JAVA_VERSION" ]]; then - if [[ "$CACHED_VERSION" == "$JAVA_VERSION" ]]; then - # Already at correct version, just upgrade if available - upgrade_package "$DESIRED_PACKAGE" + if [[ "$INSTALLED_VERSION" == "$JAVA_VERSION" ]]; then + if [[ "$CACHED_VERSION" == "$JAVA_VERSION" ]]; then + # Already at correct version, just upgrade if available + upgrade_package "$DESIRED_PACKAGE" + else + msg_info "Upgrading Temurin JDK $JAVA_VERSION" + $STD apt update + $STD apt install --only-upgrade -y "$DESIRED_PACKAGE" + cache_installed_version "temurin-jdk" "$JAVA_VERSION" + msg_ok "Upgraded Temurin JDK $JAVA_VERSION" + fi else - msg_info "Upgrading Temurin JDK $JAVA_VERSION" - $STD apt update - $STD apt install --only-upgrade -y "$DESIRED_PACKAGE" - cache_installed_version "temurin-jdk" "$JAVA_VERSION" - msg_ok "Upgraded Temurin JDK $JAVA_VERSION" - fi - else - if [[ -n "$INSTALLED_VERSION" ]]; then - msg_info "Removing old Temurin JDK $INSTALLED_VERSION" - $STD apt purge -y "temurin-${INSTALLED_VERSION}-jdk" - msg_ok "Removed old Temurin JDK" - fi + if [[ -n "$INSTALLED_VERSION" ]]; then + msg_info "Removing old Temurin JDK $INSTALLED_VERSION" + $STD apt purge -y "temurin-${INSTALLED_VERSION}-jdk" + msg_ok "Removed old Temurin JDK" + fi - msg_info "Installing Temurin JDK $JAVA_VERSION" - $STD apt install -y "$DESIRED_PACKAGE" - cache_installed_version "temurin-jdk" "$JAVA_VERSION" - msg_ok "Installed Temurin JDK $JAVA_VERSION" - fi + msg_info "Installing Temurin JDK $JAVA_VERSION" + $STD apt install -y "$DESIRED_PACKAGE" + cache_installed_version "temurin-jdk" "$JAVA_VERSION" + msg_ok "Installed Temurin JDK $JAVA_VERSION" + fi } # ------------------------------------------------------------------------------ @@ -1890,21 +1890,21 @@ function setup_java() { # ------------------------------------------------------------------------------ function setup_local_ip_helper() { - local BASE_DIR="/usr/local/community-scripts/ip-management" - local SCRIPT_PATH="$BASE_DIR/update_local_ip.sh" - local IP_FILE="/run/local-ip.env" - local DISPATCHER_SCRIPT="/etc/networkd-dispatcher/routable.d/10-update-local-ip.sh" + local BASE_DIR="/usr/local/community-scripts/ip-management" + local SCRIPT_PATH="$BASE_DIR/update_local_ip.sh" + local IP_FILE="/run/local-ip.env" + local DISPATCHER_SCRIPT="/etc/networkd-dispatcher/routable.d/10-update-local-ip.sh" - mkdir -p "$BASE_DIR" + mkdir -p "$BASE_DIR" - # Install networkd-dispatcher if not present - if ! dpkg -s networkd-dispatcher >/dev/null 2>&1; then - $STD apt update - $STD apt install -y networkd-dispatcher - fi + # Install networkd-dispatcher if not present + if ! dpkg -s networkd-dispatcher >/dev/null 2>&1; then + $STD apt update + $STD apt install -y networkd-dispatcher + fi - # Write update_local_ip.sh - cat <<'EOF' >"$SCRIPT_PATH" + # Write update_local_ip.sh + cat <<'EOF' >"$SCRIPT_PATH" #!/bin/bash set -euo pipefail @@ -1946,17 +1946,17 @@ echo "LOCAL_IP=$current_ip" > "$IP_FILE" echo "[INFO] LOCAL_IP updated to $current_ip" EOF - chmod +x "$SCRIPT_PATH" + chmod +x "$SCRIPT_PATH" - # Install dispatcher hook - mkdir -p "$(dirname "$DISPATCHER_SCRIPT")" - cat <"$DISPATCHER_SCRIPT" + # Install dispatcher hook + mkdir -p "$(dirname "$DISPATCHER_SCRIPT")" + cat <"$DISPATCHER_SCRIPT" #!/bin/bash $SCRIPT_PATH EOF - chmod +x "$DISPATCHER_SCRIPT" - systemctl enable -q --now networkd-dispatcher.service + chmod +x "$DISPATCHER_SCRIPT" + systemctl enable -q --now networkd-dispatcher.service } # ------------------------------------------------------------------------------ @@ -1972,88 +1972,88 @@ EOF # ------------------------------------------------------------------------------ setup_mariadb() { - local MARIADB_VERSION="${MARIADB_VERSION:-latest}" - local DISTRO_ID DISTRO_CODENAME - DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') - DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + local MARIADB_VERSION="${MARIADB_VERSION:-latest}" + local DISTRO_ID DISTRO_CODENAME + DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - if ! curl -fsI http://mirror.mariadb.org/repo/ >/dev/null; then - msg_error "MariaDB mirror not reachable" - return 1 - fi - - if [[ "$MARIADB_VERSION" == "latest" ]]; then - MARIADB_VERSION=$(curl -fsSL http://mirror.mariadb.org/repo/ | - grep -Eo '[0-9]+\.[0-9]+\.[0-9]+/' | - grep -vE 'rc/|rolling/' | - sed 's|/||' | - sort -Vr | - head -n1) - if [[ -z "$MARIADB_VERSION" ]]; then - msg_error "Could not determine latest GA MariaDB version" - return 1 + if ! curl -fsI http://mirror.mariadb.org/repo/ >/dev/null; then + msg_error "MariaDB mirror not reachable" + return 1 fi - fi - local CURRENT_VERSION="" - if command -v mariadb >/dev/null; then - CURRENT_VERSION=$(mariadb --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+') - fi - - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "mariadb") - - if [[ "$CURRENT_VERSION" == "$MARIADB_VERSION" ]]; then - if [[ "$CACHED_VERSION" == "$MARIADB_VERSION" ]]; then - upgrade_package mariadb-server - upgrade_package mariadb-client - else - msg_info "Upgrading MariaDB $MARIADB_VERSION" - $STD apt update - $STD apt install --only-upgrade -y mariadb-server mariadb-client - cache_installed_version "mariadb" "$MARIADB_VERSION" - msg_ok "Upgraded MariaDB $MARIADB_VERSION" + if [[ "$MARIADB_VERSION" == "latest" ]]; then + MARIADB_VERSION=$(curl -fsSL http://mirror.mariadb.org/repo/ | + grep -Eo '[0-9]+\.[0-9]+\.[0-9]+/' | + grep -vE 'rc/|rolling/' | + sed 's|/||' | + sort -Vr | + head -n1) + if [[ -z "$MARIADB_VERSION" ]]; then + msg_error "Could not determine latest GA MariaDB version" + return 1 + fi fi - return 0 - fi - msg_info "Installing MariaDB $MARIADB_VERSION" + local CURRENT_VERSION="" + if command -v mariadb >/dev/null; then + CURRENT_VERSION=$(mariadb --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+') + fi - if [[ -n "$CURRENT_VERSION" ]]; then - $STD systemctl stop mariadb >/dev/null 2>&1 || true - $STD apt purge -y 'mariadb*' || true - fi + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "mariadb") - # Cleanup old repository files - cleanup_old_repo_files "mariadb" + if [[ "$CURRENT_VERSION" == "$MARIADB_VERSION" ]]; then + if [[ "$CACHED_VERSION" == "$MARIADB_VERSION" ]]; then + upgrade_package mariadb-server + upgrade_package mariadb-client + else + msg_info "Upgrading MariaDB $MARIADB_VERSION" + $STD apt update + $STD apt install --only-upgrade -y mariadb-server mariadb-client + cache_installed_version "mariadb" "$MARIADB_VERSION" + msg_ok "Upgraded MariaDB $MARIADB_VERSION" + fi + return 0 + fi - # Use helper function to get fallback suite - local SUITE - SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "http://mirror.mariadb.org/repo/${MARIADB_VERSION}/${DISTRO_ID}") + msg_info "Installing MariaDB $MARIADB_VERSION" - # Use standardized repo setup - setup_deb822_repo \ - "mariadb" \ - "https://mariadb.org/mariadb_release_signing_key.asc" \ - "http://mirror.mariadb.org/repo/${MARIADB_VERSION}/${DISTRO_ID}" \ - "$SUITE" \ - "main" \ - "amd64 arm64" + if [[ -n "$CURRENT_VERSION" ]]; then + $STD systemctl stop mariadb >/dev/null 2>&1 || true + $STD apt purge -y 'mariadb*' || true + fi - local MARIADB_MAJOR_MINOR - MARIADB_MAJOR_MINOR=$(echo "$MARIADB_VERSION" | awk -F. '{print $1"."$2}') - if [[ -n "$MARIADB_MAJOR_MINOR" ]]; then - echo "mariadb-server-$MARIADB_MAJOR_MINOR mariadb-server/feedback boolean false" | debconf-set-selections - fi - - DEBIAN_FRONTEND=noninteractive $STD apt install -y mariadb-server mariadb-client || { + # Cleanup old repository files cleanup_old_repo_files "mariadb" - $STD apt update - DEBIAN_FRONTEND=noninteractive $STD apt install -y mariadb-server mariadb-client - } - cache_installed_version "mariadb" "$MARIADB_VERSION" - msg_ok "Installed MariaDB $MARIADB_VERSION" + # Use helper function to get fallback suite + local SUITE + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "http://mirror.mariadb.org/repo/${MARIADB_VERSION}/${DISTRO_ID}") + + # Use standardized repo setup + setup_deb822_repo \ + "mariadb" \ + "https://mariadb.org/mariadb_release_signing_key.asc" \ + "http://mirror.mariadb.org/repo/${MARIADB_VERSION}/${DISTRO_ID}" \ + "$SUITE" \ + "main" \ + "amd64 arm64" + + local MARIADB_MAJOR_MINOR + MARIADB_MAJOR_MINOR=$(echo "$MARIADB_VERSION" | awk -F. '{print $1"."$2}') + if [[ -n "$MARIADB_MAJOR_MINOR" ]]; then + echo "mariadb-server-$MARIADB_MAJOR_MINOR mariadb-server/feedback boolean false" | debconf-set-selections + fi + + DEBIAN_FRONTEND=noninteractive $STD apt install -y mariadb-server mariadb-client || { + cleanup_old_repo_files "mariadb" + $STD apt update + DEBIAN_FRONTEND=noninteractive $STD apt install -y mariadb-server mariadb-client + } + + cache_installed_version "mariadb" "$MARIADB_VERSION" + msg_ok "Installed MariaDB $MARIADB_VERSION" } # ------------------------------------------------------------------------------ @@ -2068,75 +2068,75 @@ setup_mariadb() { # ------------------------------------------------------------------------------ function setup_mongodb() { - local MONGO_VERSION="${MONGO_VERSION:-8.0}" - local DISTRO_ID DISTRO_CODENAME - DISTRO_ID=$(awk -F= '/^ID=/{ gsub(/"/,"",$2); print $2 }' /etc/os-release) - DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{ print $2 }' /etc/os-release) + local MONGO_VERSION="${MONGO_VERSION:-8.0}" + local DISTRO_ID DISTRO_CODENAME + DISTRO_ID=$(awk -F= '/^ID=/{ gsub(/"/,"",$2); print $2 }' /etc/os-release) + DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{ print $2 }' /etc/os-release) - # Check AVX support - if ! grep -qm1 'avx[^ ]*' /proc/cpuinfo; then - local major="${MONGO_VERSION%%.*}" - if ((major > 5)); then - msg_error "MongoDB ${MONGO_VERSION} requires AVX support, which is not available on this system." - return 1 + # Check AVX support + if ! grep -qm1 'avx[^ ]*' /proc/cpuinfo; then + local major="${MONGO_VERSION%%.*}" + if ((major > 5)); then + msg_error "MongoDB ${MONGO_VERSION} requires AVX support, which is not available on this system." + return 1 + fi fi - fi - case "$DISTRO_ID" in - ubuntu) - MONGO_BASE_URL="https://repo.mongodb.org/apt/ubuntu" - REPO_COMPONENT="multiverse" - ;; - debian) - MONGO_BASE_URL="https://repo.mongodb.org/apt/debian" - REPO_COMPONENT="main" - ;; - *) - msg_error "Unsupported distribution: $DISTRO_ID" - return 1 - ;; - esac + case "$DISTRO_ID" in + ubuntu) + MONGO_BASE_URL="https://repo.mongodb.org/apt/ubuntu" + REPO_COMPONENT="multiverse" + ;; + debian) + MONGO_BASE_URL="https://repo.mongodb.org/apt/debian" + REPO_COMPONENT="main" + ;; + *) + msg_error "Unsupported distribution: $DISTRO_ID" + return 1 + ;; + esac - local INSTALLED_VERSION="" - if command -v mongod >/dev/null; then - INSTALLED_VERSION=$(mongod --version | awk '/db version/{print $3}' | cut -d. -f1,2) - fi - - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "mongodb") - - if [[ "$INSTALLED_VERSION" == "$MONGO_VERSION" ]]; then - if [[ "$CACHED_VERSION" == "$MONGO_VERSION" ]]; then - upgrade_package mongodb-org - else - msg_info "Upgrading MongoDB $MONGO_VERSION" - $STD apt update - $STD apt install --only-upgrade -y mongodb-org - cache_installed_version "mongodb" "$MONGO_VERSION" - msg_ok "Upgraded MongoDB $MONGO_VERSION" + local INSTALLED_VERSION="" + if command -v mongod >/dev/null; then + INSTALLED_VERSION=$(mongod --version | awk '/db version/{print $3}' | cut -d. -f1,2) fi - return 0 - fi - msg_info "Installing MongoDB $MONGO_VERSION" + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "mongodb") - if [[ -n "$INSTALLED_VERSION" ]]; then - $STD systemctl stop mongod || true - $STD apt purge -y mongodb-org || true - fi + if [[ "$INSTALLED_VERSION" == "$MONGO_VERSION" ]]; then + if [[ "$CACHED_VERSION" == "$MONGO_VERSION" ]]; then + upgrade_package mongodb-org + else + msg_info "Upgrading MongoDB $MONGO_VERSION" + $STD apt update + $STD apt install --only-upgrade -y mongodb-org + cache_installed_version "mongodb" "$MONGO_VERSION" + msg_ok "Upgraded MongoDB $MONGO_VERSION" + fi + return 0 + fi - # Cleanup old repository files - cleanup_old_repo_files "mongodb-org-${MONGO_VERSION}" + msg_info "Installing MongoDB $MONGO_VERSION" - # Use helper function to get fallback suite - local SUITE - SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "$MONGO_BASE_URL") + if [[ -n "$INSTALLED_VERSION" ]]; then + $STD systemctl stop mongod || true + $STD apt purge -y mongodb-org || true + fi - # Use standardized repo setup - mkdir -p /etc/apt/keyrings - curl -fsSL "https://pgp.mongodb.com/server-${MONGO_VERSION}.asc" | gpg --dearmor -o "/etc/apt/keyrings/mongodb-${MONGO_VERSION}.gpg" + # Cleanup old repository files + cleanup_old_repo_files "mongodb-org-${MONGO_VERSION}" - cat </etc/apt/sources.list.d/mongodb-org-${MONGO_VERSION}.sources + # Use helper function to get fallback suite + local SUITE + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "$MONGO_BASE_URL") + + # Use standardized repo setup + mkdir -p /etc/apt/keyrings + curl -fsSL "https://pgp.mongodb.com/server-${MONGO_VERSION}.asc" | gpg --dearmor -o "/etc/apt/keyrings/mongodb-${MONGO_VERSION}.gpg" + + cat </etc/apt/sources.list.d/mongodb-org-${MONGO_VERSION}.sources Types: deb URIs: ${MONGO_BASE_URL} Suites: ${SUITE}/mongodb-org/${MONGO_VERSION} @@ -2145,20 +2145,20 @@ Architectures: amd64 arm64 Signed-By: /etc/apt/keyrings/mongodb-${MONGO_VERSION}.gpg EOF - $STD apt update || { - msg_error "APT update failed — invalid MongoDB repo for ${DISTRO_ID}-${DISTRO_CODENAME}?" - return 1 - } + $STD apt update || { + msg_error "APT update failed — invalid MongoDB repo for ${DISTRO_ID}-${DISTRO_CODENAME}?" + return 1 + } - $STD apt install -y mongodb-org + $STD apt install -y mongodb-org - mkdir -p /var/lib/mongodb - chown -R mongodb:mongodb /var/lib/mongodb + mkdir -p /var/lib/mongodb + chown -R mongodb:mongodb /var/lib/mongodb - $STD systemctl enable mongod - safe_service_restart mongod - cache_installed_version "mongodb" "$MONGO_VERSION" - msg_ok "Installed MongoDB $MONGO_VERSION" + $STD systemctl enable mongod + safe_service_restart mongod + cache_installed_version "mongodb" "$MONGO_VERSION" + msg_ok "Installed MongoDB $MONGO_VERSION" } # ------------------------------------------------------------------------------ @@ -2174,57 +2174,57 @@ EOF # ------------------------------------------------------------------------------ function setup_mysql() { - local MYSQL_VERSION="${MYSQL_VERSION:-8.0}" - local CURRENT_VERSION="" - local NEED_INSTALL=false - local DISTRO_ID DISTRO_CODENAME - DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') - DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + local MYSQL_VERSION="${MYSQL_VERSION:-8.0}" + local CURRENT_VERSION="" + local NEED_INSTALL=false + local DISTRO_ID DISTRO_CODENAME + DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - if command -v mysql >/dev/null; then - CURRENT_VERSION="$(mysql --version | grep -oP '[0-9]+\.[0-9]+' | head -n1)" - if [[ "$CURRENT_VERSION" != "$MYSQL_VERSION" ]]; then - msg_info "MySQL $CURRENT_VERSION will be upgraded to $MYSQL_VERSION" - NEED_INSTALL=true + if command -v mysql >/dev/null; then + CURRENT_VERSION="$(mysql --version | grep -oP '[0-9]+\.[0-9]+' | head -n1)" + if [[ "$CURRENT_VERSION" != "$MYSQL_VERSION" ]]; then + msg_info "MySQL $CURRENT_VERSION will be upgraded to $MYSQL_VERSION" + NEED_INSTALL=true + else + if apt list --upgradable 2>/dev/null | grep -q '^mysql-server/'; then + msg_info "MySQL $CURRENT_VERSION available for upgrade" + $STD apt update + $STD apt install --only-upgrade -y mysql-server + msg_ok "MySQL upgraded" + fi + return + fi else - if apt list --upgradable 2>/dev/null | grep -q '^mysql-server/'; then - msg_info "MySQL $CURRENT_VERSION available for upgrade" - $STD apt update - $STD apt install --only-upgrade -y mysql-server - msg_ok "MySQL upgraded" - fi - return + msg_info "Setup MySQL $MYSQL_VERSION" + NEED_INSTALL=true fi - else - msg_info "Setup MySQL $MYSQL_VERSION" - NEED_INSTALL=true - fi - if [[ "$NEED_INSTALL" == true ]]; then - $STD systemctl stop mysql || true - $STD apt purge -y "^mysql-server.*" "^mysql-client.*" "^mysql-common.*" || true + if [[ "$NEED_INSTALL" == true ]]; then + $STD systemctl stop mysql || true + $STD apt purge -y "^mysql-server.*" "^mysql-client.*" "^mysql-common.*" || true - # Cleanup old repository files - cleanup_old_repo_files "mysql" + # Cleanup old repository files + cleanup_old_repo_files "mysql" - # Use helper function to get fallback suite - local SUITE - SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://repo.mysql.com/apt/${DISTRO_ID}") + # Use helper function to get fallback suite + local SUITE + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://repo.mysql.com/apt/${DISTRO_ID}") - # Use standardized repo setup - setup_deb822_repo \ - "mysql" \ - "https://repo.mysql.com/RPM-GPG-KEY-mysql-2023" \ - "https://repo.mysql.com/apt/${DISTRO_ID}" \ - "$SUITE" \ - "mysql-${MYSQL_VERSION}" \ - "amd64 arm64" + # Use standardized repo setup + setup_deb822_repo \ + "mysql" \ + "https://repo.mysql.com/RPM-GPG-KEY-mysql-2023" \ + "https://repo.mysql.com/apt/${DISTRO_ID}" \ + "$SUITE" \ + "mysql-${MYSQL_VERSION}" \ + "amd64 arm64" - export DEBIAN_FRONTEND=noninteractive - $STD apt install -y mysql-server - cache_installed_version "mysql" "$MYSQL_VERSION" - msg_ok "Installed MySQL $MYSQL_VERSION" - fi + export DEBIAN_FRONTEND=noninteractive + $STD apt install -y mysql-server + cache_installed_version "mysql" "$MYSQL_VERSION" + msg_ok "Installed MySQL $MYSQL_VERSION" + fi } # ------------------------------------------------------------------------------ @@ -2240,106 +2240,106 @@ function setup_mysql() { # ------------------------------------------------------------------------------ function setup_nodejs() { - local NODE_VERSION="${NODE_VERSION:-22}" - local NODE_MODULE="${NODE_MODULE:-}" - local CURRENT_NODE_VERSION="" - local NEED_NODE_INSTALL=false - local DISTRO_ID DISTRO_CODENAME - DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') - DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + local NODE_VERSION="${NODE_VERSION:-22}" + local NODE_MODULE="${NODE_MODULE:-}" + local CURRENT_NODE_VERSION="" + local NEED_NODE_INSTALL=false + local DISTRO_ID DISTRO_CODENAME + DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - if command -v node >/dev/null; then - CURRENT_NODE_VERSION="$(node -v | grep -oP '^v\K[0-9]+')" - if [[ "$CURRENT_NODE_VERSION" != "$NODE_VERSION" ]]; then - msg_info "Old Node.js $CURRENT_NODE_VERSION found, replacing with $NODE_VERSION" - NEED_NODE_INSTALL=true - fi - else - msg_info "Setup Node.js $NODE_VERSION" - NEED_NODE_INSTALL=true - fi - - ensure_dependencies jq - - if [[ "$NEED_NODE_INSTALL" == true ]]; then - $STD apt purge -y nodejs - - # Cleanup old repository files - cleanup_old_repo_files "nodesource" - - # NodeSource uses 'nodistro' for all distributions - no fallback needed - # Use standardized repo setup - setup_deb822_repo \ - "nodesource" \ - "https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key" \ - "https://deb.nodesource.com/node_${NODE_VERSION}.x" \ - "nodistro" \ - "main" \ - "amd64 arm64" - - if ! $STD apt install -y nodejs; then - msg_error "Failed to install Node.js ${NODE_VERSION} from NodeSource" - return 1 + if command -v node >/dev/null; then + CURRENT_NODE_VERSION="$(node -v | grep -oP '^v\K[0-9]+')" + if [[ "$CURRENT_NODE_VERSION" != "$NODE_VERSION" ]]; then + msg_info "Old Node.js $CURRENT_NODE_VERSION found, replacing with $NODE_VERSION" + NEED_NODE_INSTALL=true + fi + else + msg_info "Setup Node.js $NODE_VERSION" + NEED_NODE_INSTALL=true fi - $STD npm install -g npm@latest || { - msg_warn "Failed to update npm to latest version" + ensure_dependencies jq + + if [[ "$NEED_NODE_INSTALL" == true ]]; then + $STD apt purge -y nodejs + + # Cleanup old repository files + cleanup_old_repo_files "nodesource" + + # NodeSource uses 'nodistro' for all distributions - no fallback needed + # Use standardized repo setup + setup_deb822_repo \ + "nodesource" \ + "https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key" \ + "https://deb.nodesource.com/node_${NODE_VERSION}.x" \ + "nodistro" \ + "main" \ + "amd64 arm64" + + if ! $STD apt install -y nodejs; then + msg_error "Failed to install Node.js ${NODE_VERSION} from NodeSource" + return 1 + fi + + $STD npm install -g npm@latest || { + msg_warn "Failed to update npm to latest version" + } + + cache_installed_version "nodejs" "$NODE_VERSION" + msg_ok "Installed Node.js ${NODE_VERSION}" + fi + + export NODE_OPTIONS="--max-old-space-size=4096" + + if [[ ! -d /opt ]]; then + mkdir -p /opt + fi + cd /opt || { + msg_error "Failed to set safe working directory before npm install" + return 1 } - cache_installed_version "nodejs" "$NODE_VERSION" - msg_ok "Installed Node.js ${NODE_VERSION}" - fi + if [[ -n "$NODE_MODULE" ]]; then + IFS=',' read -ra MODULES <<<"$NODE_MODULE" + for mod in "${MODULES[@]}"; do + local MODULE_NAME MODULE_REQ_VERSION MODULE_INSTALLED_VERSION + if [[ "$mod" == @*/*@* ]]; then + MODULE_NAME="${mod%@*}" + MODULE_REQ_VERSION="${mod##*@}" + elif [[ "$mod" == *"@"* ]]; then + MODULE_NAME="${mod%@*}" + MODULE_REQ_VERSION="${mod##*@}" + else + MODULE_NAME="$mod" + MODULE_REQ_VERSION="latest" + fi - export NODE_OPTIONS="--max-old-space-size=4096" - - if [[ ! -d /opt ]]; then - mkdir -p /opt - fi - cd /opt || { - msg_error "Failed to set safe working directory before npm install" - return 1 - } - - if [[ -n "$NODE_MODULE" ]]; then - IFS=',' read -ra MODULES <<<"$NODE_MODULE" - for mod in "${MODULES[@]}"; do - local MODULE_NAME MODULE_REQ_VERSION MODULE_INSTALLED_VERSION - if [[ "$mod" == @*/*@* ]]; then - MODULE_NAME="${mod%@*}" - MODULE_REQ_VERSION="${mod##*@}" - elif [[ "$mod" == *"@"* ]]; then - MODULE_NAME="${mod%@*}" - MODULE_REQ_VERSION="${mod##*@}" - else - MODULE_NAME="$mod" - MODULE_REQ_VERSION="latest" - fi - - if npm list -g --depth=0 "$MODULE_NAME" >/dev/null 2>&1; then - MODULE_INSTALLED_VERSION="$(npm list -g --depth=0 "$MODULE_NAME" | grep "$MODULE_NAME@" | awk -F@ '{print $2}' | tr -d '[:space:]')" - if [[ "$MODULE_REQ_VERSION" != "latest" && "$MODULE_REQ_VERSION" != "$MODULE_INSTALLED_VERSION" ]]; then - msg_info "Updating $MODULE_NAME from v$MODULE_INSTALLED_VERSION to v$MODULE_REQ_VERSION" - if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}"; then - msg_error "Failed to update $MODULE_NAME to version $MODULE_REQ_VERSION" - return 1 - fi - elif [[ "$MODULE_REQ_VERSION" == "latest" ]]; then - msg_info "Updating $MODULE_NAME to latest version" - if ! $STD npm install -g "${MODULE_NAME}@latest"; then - msg_error "Failed to update $MODULE_NAME to latest version" - return 1 - fi - fi - else - msg_info "Installing $MODULE_NAME@$MODULE_REQ_VERSION" - if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}"; then - msg_error "Failed to install $MODULE_NAME@$MODULE_REQ_VERSION" - return 1 - fi - fi - done - msg_ok "Installed Node.js modules: $NODE_MODULE" - fi + if npm list -g --depth=0 "$MODULE_NAME" >/dev/null 2>&1; then + MODULE_INSTALLED_VERSION="$(npm list -g --depth=0 "$MODULE_NAME" | grep "$MODULE_NAME@" | awk -F@ '{print $2}' | tr -d '[:space:]')" + if [[ "$MODULE_REQ_VERSION" != "latest" && "$MODULE_REQ_VERSION" != "$MODULE_INSTALLED_VERSION" ]]; then + msg_info "Updating $MODULE_NAME from v$MODULE_INSTALLED_VERSION to v$MODULE_REQ_VERSION" + if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}"; then + msg_error "Failed to update $MODULE_NAME to version $MODULE_REQ_VERSION" + return 1 + fi + elif [[ "$MODULE_REQ_VERSION" == "latest" ]]; then + msg_info "Updating $MODULE_NAME to latest version" + if ! $STD npm install -g "${MODULE_NAME}@latest"; then + msg_error "Failed to update $MODULE_NAME to latest version" + return 1 + fi + fi + else + msg_info "Installing $MODULE_NAME@$MODULE_REQ_VERSION" + if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}"; then + msg_error "Failed to install $MODULE_NAME@$MODULE_REQ_VERSION" + return 1 + fi + fi + done + msg_ok "Installed Node.js modules: $NODE_MODULE" + fi } # ------------------------------------------------------------------------------ @@ -2362,58 +2362,58 @@ function setup_nodejs() { # ------------------------------------------------------------------------------ function setup_php() { - local PHP_VERSION="${PHP_VERSION:-8.4}" - local PHP_MODULE="${PHP_MODULE:-}" - local PHP_APACHE="${PHP_APACHE:-NO}" - local PHP_FPM="${PHP_FPM:-NO}" - local DISTRO_ID DISTRO_CODENAME - DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') - DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + local PHP_VERSION="${PHP_VERSION:-8.4}" + local PHP_MODULE="${PHP_MODULE:-}" + local PHP_APACHE="${PHP_APACHE:-NO}" + local PHP_FPM="${PHP_FPM:-NO}" + local DISTRO_ID DISTRO_CODENAME + DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - local DEFAULT_MODULES="bcmath,cli,curl,gd,intl,mbstring,opcache,readline,xml,zip" - local COMBINED_MODULES + local DEFAULT_MODULES="bcmath,cli,curl,gd,intl,mbstring,opcache,readline,xml,zip" + local COMBINED_MODULES - local PHP_MEMORY_LIMIT="${PHP_MEMORY_LIMIT:-512M}" - local PHP_UPLOAD_MAX_FILESIZE="${PHP_UPLOAD_MAX_FILESIZE:-128M}" - local PHP_POST_MAX_SIZE="${PHP_POST_MAX_SIZE:-128M}" - local PHP_MAX_EXECUTION_TIME="${PHP_MAX_EXECUTION_TIME:-300}" + local PHP_MEMORY_LIMIT="${PHP_MEMORY_LIMIT:-512M}" + local PHP_UPLOAD_MAX_FILESIZE="${PHP_UPLOAD_MAX_FILESIZE:-128M}" + local PHP_POST_MAX_SIZE="${PHP_POST_MAX_SIZE:-128M}" + local PHP_MAX_EXECUTION_TIME="${PHP_MAX_EXECUTION_TIME:-300}" - # Merge default + user-defined modules - if [[ -n "$PHP_MODULE" ]]; then - COMBINED_MODULES="${DEFAULT_MODULES},${PHP_MODULE}" - else - COMBINED_MODULES="${DEFAULT_MODULES}" - fi + # Merge default + user-defined modules + if [[ -n "$PHP_MODULE" ]]; then + COMBINED_MODULES="${DEFAULT_MODULES},${PHP_MODULE}" + else + COMBINED_MODULES="${DEFAULT_MODULES}" + fi - # Deduplicate - COMBINED_MODULES=$(echo "$COMBINED_MODULES" | tr ',' '\n' | awk '!seen[$0]++' | paste -sd, -) + # Deduplicate + COMBINED_MODULES=$(echo "$COMBINED_MODULES" | tr ',' '\n' | awk '!seen[$0]++' | paste -sd, -) - # Get current PHP-CLI version - local CURRENT_PHP="" - if command -v php >/dev/null 2>&1; then - CURRENT_PHP=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2) - fi + # Get current PHP-CLI version + local CURRENT_PHP="" + if command -v php >/dev/null 2>&1; then + CURRENT_PHP=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2) + fi - if [[ -z "$CURRENT_PHP" ]]; then - msg_info "Setup PHP $PHP_VERSION" - elif [[ "$CURRENT_PHP" != "$PHP_VERSION" ]]; then - msg_info "Old PHP $CURRENT_PHP detected, Setup new PHP $PHP_VERSION" - $STD apt purge -y "php${CURRENT_PHP//./}"* || true - fi + if [[ -z "$CURRENT_PHP" ]]; then + msg_info "Setup PHP $PHP_VERSION" + elif [[ "$CURRENT_PHP" != "$PHP_VERSION" ]]; then + msg_info "Old PHP $CURRENT_PHP detected, Setup new PHP $PHP_VERSION" + $STD apt purge -y "php${CURRENT_PHP//./}"* || true + fi - # Ensure Sury repo is available - if [[ ! -f /etc/apt/sources.list.d/php.sources ]]; then - # Cleanup old repository files - cleanup_old_repo_files "php" + # Ensure Sury repo is available + if [[ ! -f /etc/apt/sources.list.d/php.sources ]]; then + # Cleanup old repository files + cleanup_old_repo_files "php" - $STD curl -fsSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb - $STD dpkg -i /tmp/debsuryorg-archive-keyring.deb + $STD curl -fsSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb + $STD dpkg -i /tmp/debsuryorg-archive-keyring.deb - # Use helper function to get fallback suite - local SUITE - SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://packages.sury.org/php") + # Use helper function to get fallback suite + local SUITE + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://packages.sury.org/php") - cat </etc/apt/sources.list.d/php.sources + cat </etc/apt/sources.list.d/php.sources Types: deb URIs: https://packages.sury.org/php/ Suites: $SUITE @@ -2421,78 +2421,78 @@ Components: main Architectures: amd64 arm64 Signed-By: /usr/share/keyrings/deb.sury.org-php.gpg EOF - $STD apt update - fi - - # Build module list - local MODULE_LIST="php${PHP_VERSION}" - IFS=',' read -ra MODULES <<<"$COMBINED_MODULES" - for mod in "${MODULES[@]}"; do - if apt-cache show "php${PHP_VERSION}-${mod}" >/dev/null 2>&1; then - MODULE_LIST+=" php${PHP_VERSION}-${mod}" - else - msg_warn "PHP-Module ${mod} for PHP ${PHP_VERSION} not found – skipping" + $STD apt update fi - done - if [[ "$PHP_FPM" == "YES" ]]; then - MODULE_LIST+=" php${PHP_VERSION}-fpm" - fi - # install apache2 with PHP support if requested - if [[ "$PHP_APACHE" == "YES" ]]; then - if ! dpkg -l | grep -q "libapache2-mod-php${PHP_VERSION}"; then - msg_info "Installing Apache with PHP${PHP_VERSION} support" - $STD apt install -y apache2 libapache2-mod-php${PHP_VERSION} - else - msg_info "Apache with PHP${PHP_VERSION} already installed – skipping install" - fi - fi - - # setup / update PHP modules - $STD apt install -y $MODULE_LIST - cache_installed_version "php" "$PHP_VERSION" - msg_ok "Installed PHP $PHP_VERSION" - - # optional stop old PHP-FPM service - if [[ "$PHP_FPM" == "YES" && -n "$CURRENT_PHP" && "$CURRENT_PHP" != "$PHP_VERSION" ]]; then - $STD systemctl stop php"${CURRENT_PHP}"-fpm || true - $STD systemctl disable php"${CURRENT_PHP}"-fpm || true - fi - - # Patch all relevant php.ini files (silent) - local PHP_INI_PATHS=("/etc/php/${PHP_VERSION}/cli/php.ini") - [[ "$PHP_FPM" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/fpm/php.ini") - [[ "$PHP_APACHE" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/apache2/php.ini") - for ini in "${PHP_INI_PATHS[@]}"; do - if [[ -f "$ini" ]]; then - $STD sed -i "s|^memory_limit = .*|memory_limit = ${PHP_MEMORY_LIMIT}|" "$ini" - $STD sed -i "s|^upload_max_filesize = .*|upload_max_filesize = ${PHP_UPLOAD_MAX_FILESIZE}|" "$ini" - $STD sed -i "s|^post_max_size = .*|post_max_size = ${PHP_POST_MAX_SIZE}|" "$ini" - $STD sed -i "s|^max_execution_time = .*|max_execution_time = ${PHP_MAX_EXECUTION_TIME}|" "$ini" - fi - done - - # patch Apache configuration if needed - if [[ "$PHP_APACHE" == "YES" ]]; then - for mod in $(ls /etc/apache2/mods-enabled/ 2>/dev/null | grep -E '^php[0-9]\.[0-9]\.conf$' | sed 's/\.conf//'); do - if [[ "$mod" != "php${PHP_VERSION}" ]]; then - $STD a2dismod "$mod" || true - fi + # Build module list + local MODULE_LIST="php${PHP_VERSION}" + IFS=',' read -ra MODULES <<<"$COMBINED_MODULES" + for mod in "${MODULES[@]}"; do + if apt-cache show "php${PHP_VERSION}-${mod}" >/dev/null 2>&1; then + MODULE_LIST+=" php${PHP_VERSION}-${mod}" + else + msg_warn "PHP-Module ${mod} for PHP ${PHP_VERSION} not found – skipping" + fi done - $STD a2enmod mpm_prefork - $STD a2enmod "php${PHP_VERSION}" - safe_service_restart apache2 || true - fi - - # enable and restart PHP-FPM if requested - if [[ "$PHP_FPM" == "YES" ]]; then - if systemctl list-unit-files | grep -q "php${PHP_VERSION}-fpm.service"; then - $STD systemctl enable php${PHP_VERSION}-fpm - safe_service_restart php${PHP_VERSION}-fpm - else - msg_warn "FPM requested but service php${PHP_VERSION}-fpm not found" + if [[ "$PHP_FPM" == "YES" ]]; then + MODULE_LIST+=" php${PHP_VERSION}-fpm" + fi + + # install apache2 with PHP support if requested + if [[ "$PHP_APACHE" == "YES" ]]; then + if ! dpkg -l | grep -q "libapache2-mod-php${PHP_VERSION}"; then + msg_info "Installing Apache with PHP${PHP_VERSION} support" + $STD apt install -y apache2 libapache2-mod-php${PHP_VERSION} + else + msg_info "Apache with PHP${PHP_VERSION} already installed – skipping install" + fi + fi + + # setup / update PHP modules + $STD apt install -y $MODULE_LIST + cache_installed_version "php" "$PHP_VERSION" + msg_ok "Installed PHP $PHP_VERSION" + + # optional stop old PHP-FPM service + if [[ "$PHP_FPM" == "YES" && -n "$CURRENT_PHP" && "$CURRENT_PHP" != "$PHP_VERSION" ]]; then + $STD systemctl stop php"${CURRENT_PHP}"-fpm || true + $STD systemctl disable php"${CURRENT_PHP}"-fpm || true + fi + + # Patch all relevant php.ini files (silent) + local PHP_INI_PATHS=("/etc/php/${PHP_VERSION}/cli/php.ini") + [[ "$PHP_FPM" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/fpm/php.ini") + [[ "$PHP_APACHE" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/apache2/php.ini") + for ini in "${PHP_INI_PATHS[@]}"; do + if [[ -f "$ini" ]]; then + $STD sed -i "s|^memory_limit = .*|memory_limit = ${PHP_MEMORY_LIMIT}|" "$ini" + $STD sed -i "s|^upload_max_filesize = .*|upload_max_filesize = ${PHP_UPLOAD_MAX_FILESIZE}|" "$ini" + $STD sed -i "s|^post_max_size = .*|post_max_size = ${PHP_POST_MAX_SIZE}|" "$ini" + $STD sed -i "s|^max_execution_time = .*|max_execution_time = ${PHP_MAX_EXECUTION_TIME}|" "$ini" + fi + done + + # patch Apache configuration if needed + if [[ "$PHP_APACHE" == "YES" ]]; then + for mod in $(ls /etc/apache2/mods-enabled/ 2>/dev/null | grep -E '^php[0-9]\.[0-9]\.conf$' | sed 's/\.conf//'); do + if [[ "$mod" != "php${PHP_VERSION}" ]]; then + $STD a2dismod "$mod" || true + fi + done + $STD a2enmod mpm_prefork + $STD a2enmod "php${PHP_VERSION}" + safe_service_restart apache2 || true + fi + + # enable and restart PHP-FPM if requested + if [[ "$PHP_FPM" == "YES" ]]; then + if systemctl list-unit-files | grep -q "php${PHP_VERSION}-fpm.service"; then + $STD systemctl enable php${PHP_VERSION}-fpm + safe_service_restart php${PHP_VERSION}-fpm + else + msg_warn "FPM requested but service php${PHP_VERSION}-fpm not found" + fi fi - fi } # ------------------------------------------------------------------------------ @@ -2510,71 +2510,71 @@ EOF # PG_MODULES - Comma-separated list of extensions (e.g. "postgis,contrib") # ------------------------------------------------------------------------------ function setup_postgresql() { - local PG_VERSION="${PG_VERSION:-16}" - local PG_MODULES="${PG_MODULES:-}" - local CURRENT_PG_VERSION="" - local DISTRO_ID DISTRO_CODENAME - local NEED_PG_INSTALL=false - DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') - DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + local PG_VERSION="${PG_VERSION:-16}" + local PG_MODULES="${PG_MODULES:-}" + local CURRENT_PG_VERSION="" + local DISTRO_ID DISTRO_CODENAME + local NEED_PG_INSTALL=false + DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - if command -v psql >/dev/null; then - CURRENT_PG_VERSION="$(psql -V | awk '{print $3}' | cut -d. -f1)" - [[ "$CURRENT_PG_VERSION" != "$PG_VERSION" ]] && NEED_PG_INSTALL=true - else - NEED_PG_INSTALL=true - fi - - if [[ "$NEED_PG_INSTALL" == true ]]; then - msg_info "Installing PostgreSQL $PG_VERSION" - - if [[ -n "$CURRENT_PG_VERSION" ]]; then - $STD su - postgres -c "pg_dumpall > /var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql" - $STD systemctl stop postgresql + if command -v psql >/dev/null; then + CURRENT_PG_VERSION="$(psql -V | awk '{print $3}' | cut -d. -f1)" + [[ "$CURRENT_PG_VERSION" != "$PG_VERSION" ]] && NEED_PG_INSTALL=true + else + NEED_PG_INSTALL=true fi - # Cleanup old repository files - cleanup_old_repo_files "pgdg" + if [[ "$NEED_PG_INSTALL" == true ]]; then + msg_info "Installing PostgreSQL $PG_VERSION" - # Use helper function to get fallback suite - local SUITE - SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://apt.postgresql.org/pub/repos/apt") + if [[ -n "$CURRENT_PG_VERSION" ]]; then + $STD su - postgres -c "pg_dumpall > /var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql" + $STD systemctl stop postgresql + fi - # PGDG uses special suite naming: ${SUITE}-pgdg - SUITE="${SUITE}-pgdg" + # Cleanup old repository files + cleanup_old_repo_files "pgdg" - # Use standardized repo setup - setup_deb822_repo \ - "pgdg" \ - "https://www.postgresql.org/media/keys/ACCC4CF8.asc" \ - "https://apt.postgresql.org/pub/repos/apt" \ - "$SUITE" \ - "main" \ - "amd64 arm64" + # Use helper function to get fallback suite + local SUITE + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://apt.postgresql.org/pub/repos/apt") - $STD apt install -y "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}" + # PGDG uses special suite naming: ${SUITE}-pgdg + SUITE="${SUITE}-pgdg" - if [[ -n "$CURRENT_PG_VERSION" ]]; then - $STD apt purge -y "postgresql-${CURRENT_PG_VERSION}" "postgresql-client-${CURRENT_PG_VERSION}" || true - $STD su - postgres -c "psql < /var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql" + # Use standardized repo setup + setup_deb822_repo \ + "pgdg" \ + "https://www.postgresql.org/media/keys/ACCC4CF8.asc" \ + "https://apt.postgresql.org/pub/repos/apt" \ + "$SUITE" \ + "main" \ + "amd64 arm64" + + $STD apt install -y "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}" + + if [[ -n "$CURRENT_PG_VERSION" ]]; then + $STD apt purge -y "postgresql-${CURRENT_PG_VERSION}" "postgresql-client-${CURRENT_PG_VERSION}" || true + $STD su - postgres -c "psql < /var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql" + fi + + $STD systemctl enable --now postgresql + cache_installed_version "postgresql" "$PG_VERSION" + msg_ok "Installed PostgreSQL $PG_VERSION" fi - $STD systemctl enable --now postgresql - cache_installed_version "postgresql" "$PG_VERSION" - msg_ok "Installed PostgreSQL $PG_VERSION" - fi - - if [[ -n "$PG_MODULES" ]]; then - msg_info "Installing PostgreSQL modules" - IFS=',' read -ra MODULES <<<"$PG_MODULES" - for module in "${MODULES[@]}"; do - $STD apt install -y "postgresql-${PG_VERSION}-${module}" || { - msg_warn "Failed to install postgresql-${PG_VERSION}-${module}" - continue - } - done - msg_ok "Installed PostgreSQL modules" - fi + if [[ -n "$PG_MODULES" ]]; then + msg_info "Installing PostgreSQL modules" + IFS=',' read -ra MODULES <<<"$PG_MODULES" + for module in "${MODULES[@]}"; do + $STD apt install -y "postgresql-${PG_VERSION}-${module}" || { + msg_warn "Failed to install postgresql-${PG_VERSION}-${module}" + continue + } + done + msg_ok "Installed PostgreSQL modules" + fi } # ------------------------------------------------------------------------------ @@ -2591,91 +2591,91 @@ function setup_postgresql() { # ------------------------------------------------------------------------------ function setup_ruby() { - local RUBY_VERSION="${RUBY_VERSION:-3.4.4}" - local RUBY_INSTALL_RAILS="${RUBY_INSTALL_RAILS:-true}" - local RBENV_DIR="$HOME/.rbenv" - local RBENV_BIN="$RBENV_DIR/bin/rbenv" - local PROFILE_FILE="$HOME/.profile" - local TMP_DIR=$(mktemp -d) - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "ruby") + local RUBY_VERSION="${RUBY_VERSION:-3.4.4}" + local RUBY_INSTALL_RAILS="${RUBY_INSTALL_RAILS:-true}" + local RBENV_DIR="$HOME/.rbenv" + local RBENV_BIN="$RBENV_DIR/bin/rbenv" + local PROFILE_FILE="$HOME/.profile" + local TMP_DIR=$(mktemp -d) + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "ruby") - msg_info "Installing Ruby $RUBY_VERSION" + msg_info "Installing Ruby $RUBY_VERSION" - ensure_dependencies jq + ensure_dependencies jq + + local RBENV_RELEASE + RBENV_RELEASE=$(curl -fsSL https://api.github.com/repos/rbenv/rbenv/releases/latest | jq -r '.tag_name' | sed 's/^v//') + if [[ -z "$RBENV_RELEASE" ]]; then + msg_error "Failed to fetch latest rbenv version" + rm -rf "$TMP_DIR" + return 1 + fi + + curl -fsSL "https://github.com/rbenv/rbenv/archive/refs/tags/v${RBENV_RELEASE}.tar.gz" -o "$TMP_DIR/rbenv.tar.gz" || { + msg_error "Failed to download rbenv" + rm -rf "$TMP_DIR" + return 1 + } + + tar -xzf "$TMP_DIR/rbenv.tar.gz" -C "$TMP_DIR" || { + msg_error "Failed to extract rbenv" + rm -rf "$TMP_DIR" + return 1 + } + + mkdir -p "$RBENV_DIR" + cp -r "$TMP_DIR/rbenv-${RBENV_RELEASE}/." "$RBENV_DIR/" + cd "$RBENV_DIR" && src/configure && $STD make -C src + + local RUBY_BUILD_RELEASE + RUBY_BUILD_RELEASE=$(curl -fsSL https://api.github.com/repos/rbenv/ruby-build/releases/latest | jq -r '.tag_name' | sed 's/^v//') + if [[ -z "$RUBY_BUILD_RELEASE" ]]; then + msg_error "Failed to fetch latest ruby-build version" + rm -rf "$TMP_DIR" + return 1 + fi + + curl -fsSL "https://github.com/rbenv/ruby-build/archive/refs/tags/v${RUBY_BUILD_RELEASE}.tar.gz" -o "$TMP_DIR/ruby-build.tar.gz" || { + msg_error "Failed to download ruby-build" + rm -rf "$TMP_DIR" + return 1 + } + + tar -xzf "$TMP_DIR/ruby-build.tar.gz" -C "$TMP_DIR" || { + msg_error "Failed to extract ruby-build" + rm -rf "$TMP_DIR" + return 1 + } + + mkdir -p "$RBENV_DIR/plugins/ruby-build" + cp -r "$TMP_DIR/ruby-build-${RUBY_BUILD_RELEASE}/." "$RBENV_DIR/plugins/ruby-build/" + echo "$RUBY_BUILD_RELEASE" >"$RBENV_DIR/plugins/ruby-build/RUBY_BUILD_version.txt" + + if ! grep -q 'rbenv init' "$PROFILE_FILE"; then + echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >>"$PROFILE_FILE" + echo 'eval "$(rbenv init -)"' >>"$PROFILE_FILE" + fi + + export PATH="$RBENV_DIR/bin:$PATH" + eval "$("$RBENV_BIN" init - bash)" + + if ! "$RBENV_BIN" versions --bare | grep -qx "$RUBY_VERSION"; then + $STD "$RBENV_BIN" install "$RUBY_VERSION" + fi + + "$RBENV_BIN" global "$RUBY_VERSION" + hash -r + + if [[ "$RUBY_INSTALL_RAILS" == "true" ]]; then + $STD gem install rails + local RAILS_VERSION=$(rails -v 2>/dev/null | awk '{print $2}') + msg_ok "Installed Rails $RAILS_VERSION" + fi - local RBENV_RELEASE - RBENV_RELEASE=$(curl -fsSL https://api.github.com/repos/rbenv/rbenv/releases/latest | jq -r '.tag_name' | sed 's/^v//') - if [[ -z "$RBENV_RELEASE" ]]; then - msg_error "Failed to fetch latest rbenv version" rm -rf "$TMP_DIR" - return 1 - fi - - curl -fsSL "https://github.com/rbenv/rbenv/archive/refs/tags/v${RBENV_RELEASE}.tar.gz" -o "$TMP_DIR/rbenv.tar.gz" || { - msg_error "Failed to download rbenv" - rm -rf "$TMP_DIR" - return 1 - } - - tar -xzf "$TMP_DIR/rbenv.tar.gz" -C "$TMP_DIR" || { - msg_error "Failed to extract rbenv" - rm -rf "$TMP_DIR" - return 1 - } - - mkdir -p "$RBENV_DIR" - cp -r "$TMP_DIR/rbenv-${RBENV_RELEASE}/." "$RBENV_DIR/" - cd "$RBENV_DIR" && src/configure && $STD make -C src - - local RUBY_BUILD_RELEASE - RUBY_BUILD_RELEASE=$(curl -fsSL https://api.github.com/repos/rbenv/ruby-build/releases/latest | jq -r '.tag_name' | sed 's/^v//') - if [[ -z "$RUBY_BUILD_RELEASE" ]]; then - msg_error "Failed to fetch latest ruby-build version" - rm -rf "$TMP_DIR" - return 1 - fi - - curl -fsSL "https://github.com/rbenv/ruby-build/archive/refs/tags/v${RUBY_BUILD_RELEASE}.tar.gz" -o "$TMP_DIR/ruby-build.tar.gz" || { - msg_error "Failed to download ruby-build" - rm -rf "$TMP_DIR" - return 1 - } - - tar -xzf "$TMP_DIR/ruby-build.tar.gz" -C "$TMP_DIR" || { - msg_error "Failed to extract ruby-build" - rm -rf "$TMP_DIR" - return 1 - } - - mkdir -p "$RBENV_DIR/plugins/ruby-build" - cp -r "$TMP_DIR/ruby-build-${RUBY_BUILD_RELEASE}/." "$RBENV_DIR/plugins/ruby-build/" - echo "$RUBY_BUILD_RELEASE" >"$RBENV_DIR/plugins/ruby-build/RUBY_BUILD_version.txt" - - if ! grep -q 'rbenv init' "$PROFILE_FILE"; then - echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >>"$PROFILE_FILE" - echo 'eval "$(rbenv init -)"' >>"$PROFILE_FILE" - fi - - export PATH="$RBENV_DIR/bin:$PATH" - eval "$("$RBENV_BIN" init - bash)" - - if ! "$RBENV_BIN" versions --bare | grep -qx "$RUBY_VERSION"; then - $STD "$RBENV_BIN" install "$RUBY_VERSION" - fi - - "$RBENV_BIN" global "$RUBY_VERSION" - hash -r - - if [[ "$RUBY_INSTALL_RAILS" == "true" ]]; then - $STD gem install rails - local RAILS_VERSION=$(rails -v 2>/dev/null | awk '{print $2}') - msg_ok "Installed Rails $RAILS_VERSION" - fi - - rm -rf "$TMP_DIR" - cache_installed_version "ruby" "$RUBY_VERSION" - msg_ok "Installed Ruby $RUBY_VERSION" + cache_installed_version "ruby" "$RUBY_VERSION" + msg_ok "Installed Ruby $RUBY_VERSION" } # ------------------------------------------------------------------------------ @@ -2692,86 +2692,86 @@ function setup_ruby() { # ------------------------------------------------------------------------------ function setup_clickhouse() { - local CLICKHOUSE_VERSION="${CLICKHOUSE_VERSION:-latest}" - local DISTRO_ID DISTRO_CODENAME - DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') - DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + local CLICKHOUSE_VERSION="${CLICKHOUSE_VERSION:-latest}" + local DISTRO_ID DISTRO_CODENAME + DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - # Determine latest version if needed - if [[ "$CLICKHOUSE_VERSION" == "latest" ]]; then - CLICKHOUSE_VERSION=$(curl -fsSL https://packages.clickhouse.com/tgz/stable/ | - grep -oP 'clickhouse-common-static-\K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | - sort -V | - tail -n1) - if [[ -z "$CLICKHOUSE_VERSION" ]]; then - msg_error "Could not determine latest ClickHouse version" - return 1 + # Determine latest version if needed + if [[ "$CLICKHOUSE_VERSION" == "latest" ]]; then + CLICKHOUSE_VERSION=$(curl -fsSL https://packages.clickhouse.com/tgz/stable/ | + grep -oP 'clickhouse-common-static-\K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | + sort -V | + tail -n1) + if [[ -z "$CLICKHOUSE_VERSION" ]]; then + msg_error "Could not determine latest ClickHouse version" + return 1 + fi fi - fi - local CURRENT_VERSION="" - if command -v clickhouse-server >/dev/null 2>&1; then - CURRENT_VERSION=$(clickhouse-server --version 2>/dev/null | grep -oP 'version \K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -n1) - fi - - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "clickhouse") - - # Check if already at target version - if [[ "$CURRENT_VERSION" == "$CLICKHOUSE_VERSION" ]]; then - if [[ "$CACHED_VERSION" == "$CLICKHOUSE_VERSION" ]]; then - upgrade_package clickhouse-server - upgrade_package clickhouse-client - else - msg_info "Upgrading ClickHouse $CLICKHOUSE_VERSION" - $STD apt update - $STD apt install --only-upgrade -y clickhouse-server clickhouse-client - cache_installed_version "clickhouse" "$CLICKHOUSE_VERSION" - msg_ok "Upgraded ClickHouse $CLICKHOUSE_VERSION" + local CURRENT_VERSION="" + if command -v clickhouse-server >/dev/null 2>&1; then + CURRENT_VERSION=$(clickhouse-server --version 2>/dev/null | grep -oP 'version \K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -n1) fi - return 0 - fi - msg_info "Installing ClickHouse $CLICKHOUSE_VERSION" + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "clickhouse") - # Stop existing service if upgrading - if [[ -n "$CURRENT_VERSION" ]]; then - $STD systemctl stop clickhouse-server || true - fi + # Check if already at target version + if [[ "$CURRENT_VERSION" == "$CLICKHOUSE_VERSION" ]]; then + if [[ "$CACHED_VERSION" == "$CLICKHOUSE_VERSION" ]]; then + upgrade_package clickhouse-server + upgrade_package clickhouse-client + else + msg_info "Upgrading ClickHouse $CLICKHOUSE_VERSION" + $STD apt update + $STD apt install --only-upgrade -y clickhouse-server clickhouse-client + cache_installed_version "clickhouse" "$CLICKHOUSE_VERSION" + msg_ok "Upgraded ClickHouse $CLICKHOUSE_VERSION" + fi + return 0 + fi - # Cleanup old repository files - cleanup_old_repo_files "clickhouse" + msg_info "Installing ClickHouse $CLICKHOUSE_VERSION" - # Ensure dependencies - ensure_dependencies apt-transport-https ca-certificates dirmngr gnupg + # Stop existing service if upgrading + if [[ -n "$CURRENT_VERSION" ]]; then + $STD systemctl stop clickhouse-server || true + fi - # Use helper function to get fallback suite - local SUITE - SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://packages.clickhouse.com/deb") + # Cleanup old repository files + cleanup_old_repo_files "clickhouse" - # Use standardized repo setup - setup_deb822_repo \ - "clickhouse" \ - "https://packages.clickhouse.com/rpm/lts/repodata/repomd.xml.key" \ - "https://packages.clickhouse.com/deb" \ - "$SUITE" \ - "main" \ - "amd64 arm64" + # Ensure dependencies + ensure_dependencies apt-transport-https ca-certificates dirmngr gnupg - # Install ClickHouse packages - export DEBIAN_FRONTEND=noninteractive - $STD apt install -y clickhouse-server clickhouse-client + # Use helper function to get fallback suite + local SUITE + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://packages.clickhouse.com/deb") - # Create data directory if it doesn't exist - mkdir -p /var/lib/clickhouse - chown -R clickhouse:clickhouse /var/lib/clickhouse + # Use standardized repo setup + setup_deb822_repo \ + "clickhouse" \ + "https://packages.clickhouse.com/rpm/lts/repodata/repomd.xml.key" \ + "https://packages.clickhouse.com/deb" \ + "$SUITE" \ + "main" \ + "amd64 arm64" - # Enable and start service - $STD systemctl enable clickhouse-server - safe_service_restart clickhouse-server + # Install ClickHouse packages + export DEBIAN_FRONTEND=noninteractive + $STD apt install -y clickhouse-server clickhouse-client - cache_installed_version "clickhouse" "$CLICKHOUSE_VERSION" - msg_ok "Installed ClickHouse $CLICKHOUSE_VERSION" + # Create data directory if it doesn't exist + mkdir -p /var/lib/clickhouse + chown -R clickhouse:clickhouse /var/lib/clickhouse + + # Enable and start service + $STD systemctl enable clickhouse-server + safe_service_restart clickhouse-server + + cache_installed_version "clickhouse" "$CLICKHOUSE_VERSION" + msg_ok "Installed ClickHouse $CLICKHOUSE_VERSION" } # ------------------------------------------------------------------------------ @@ -2792,64 +2792,64 @@ function setup_clickhouse() { # ------------------------------------------------------------------------------ function setup_rust() { - local RUST_TOOLCHAIN="${RUST_TOOLCHAIN:-stable}" - local RUST_CRATES="${RUST_CRATES:-}" - local CARGO_BIN="${HOME}/.cargo/bin" - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "rust") + local RUST_TOOLCHAIN="${RUST_TOOLCHAIN:-stable}" + local RUST_CRATES="${RUST_CRATES:-}" + local CARGO_BIN="${HOME}/.cargo/bin" + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "rust") - if ! command -v rustup &>/dev/null; then - msg_info "Installing Rust" - curl -fsSL https://sh.rustup.rs | $STD sh -s -- -y --default-toolchain "$RUST_TOOLCHAIN" || { - msg_error "Failed to install Rust" - return 1 - } - export PATH="$CARGO_BIN:$PATH" - echo 'export PATH="$HOME/.cargo/bin:$PATH"' >>"$HOME/.profile" - local RUST_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}') - cache_installed_version "rust" "$RUST_VERSION" - msg_ok "Installed Rust $RUST_VERSION" - else - $STD rustup install "$RUST_TOOLCHAIN" - $STD rustup default "$RUST_TOOLCHAIN" - $STD rustup update "$RUST_TOOLCHAIN" - local RUST_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}') - cache_installed_version "rust" "$RUST_VERSION" - msg_ok "Updated Rust toolchain to $RUST_TOOLCHAIN ($RUST_VERSION)" - fi + if ! command -v rustup &>/dev/null; then + msg_info "Installing Rust" + curl -fsSL https://sh.rustup.rs | $STD sh -s -- -y --default-toolchain "$RUST_TOOLCHAIN" || { + msg_error "Failed to install Rust" + return 1 + } + export PATH="$CARGO_BIN:$PATH" + echo 'export PATH="$HOME/.cargo/bin:$PATH"' >>"$HOME/.profile" + local RUST_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}') + cache_installed_version "rust" "$RUST_VERSION" + msg_ok "Installed Rust $RUST_VERSION" + else + $STD rustup install "$RUST_TOOLCHAIN" + $STD rustup default "$RUST_TOOLCHAIN" + $STD rustup update "$RUST_TOOLCHAIN" + local RUST_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}') + cache_installed_version "rust" "$RUST_VERSION" + msg_ok "Updated Rust toolchain to $RUST_TOOLCHAIN ($RUST_VERSION)" + fi - if [[ -n "$RUST_CRATES" ]]; then - IFS=',' read -ra CRATES <<<"$RUST_CRATES" - for crate in "${CRATES[@]}"; do - local NAME VER INSTALLED_VER - if [[ "$crate" == *"@"* ]]; then - NAME="${crate%@*}" - VER="${crate##*@}" - else - NAME="$crate" - VER="" - fi + if [[ -n "$RUST_CRATES" ]]; then + IFS=',' read -ra CRATES <<<"$RUST_CRATES" + for crate in "${CRATES[@]}"; do + local NAME VER INSTALLED_VER + if [[ "$crate" == *"@"* ]]; then + NAME="${crate%@*}" + VER="${crate##*@}" + else + NAME="$crate" + VER="" + fi - INSTALLED_VER=$(cargo install --list 2>/dev/null | awk "/^$NAME v[0-9]/ {print \$2}" | tr -d 'v') + INSTALLED_VER=$(cargo install --list 2>/dev/null | awk "/^$NAME v[0-9]/ {print \$2}" | tr -d 'v') - if [[ -n "$INSTALLED_VER" ]]; then - if [[ -n "$VER" && "$VER" != "$INSTALLED_VER" ]]; then - msg_info "Updating $NAME: $INSTALLED_VER → $VER" - $STD cargo install "$NAME" --version "$VER" --force - msg_ok "Updated $NAME to $VER" - elif [[ -z "$VER" ]]; then - msg_info "Updating $NAME: $INSTALLED_VER → latest" - $STD cargo install "$NAME" --force - local NEW_VER=$(cargo install --list 2>/dev/null | awk "/^$NAME v[0-9]/ {print \$2}" | tr -d 'v') - msg_ok "Updated $NAME to $NEW_VER" - fi - else - msg_info "Installing $NAME ${VER:+$VER}" - $STD cargo install "$NAME" ${VER:+--version "$VER"} - msg_ok "Installed $NAME ${VER:-latest}" - fi - done - fi + if [[ -n "$INSTALLED_VER" ]]; then + if [[ -n "$VER" && "$VER" != "$INSTALLED_VER" ]]; then + msg_info "Updating $NAME: $INSTALLED_VER → $VER" + $STD cargo install "$NAME" --version "$VER" --force + msg_ok "Updated $NAME to $VER" + elif [[ -z "$VER" ]]; then + msg_info "Updating $NAME: $INSTALLED_VER → latest" + $STD cargo install "$NAME" --force + local NEW_VER=$(cargo install --list 2>/dev/null | awk "/^$NAME v[0-9]/ {print \$2}" | tr -d 'v') + msg_ok "Updated $NAME to $NEW_VER" + fi + else + msg_info "Installing $NAME ${VER:+$VER}" + $STD cargo install "$NAME" ${VER:+--version "$VER"} + msg_ok "Installed $NAME ${VER:-latest}" + fi + done + fi } # ------------------------------------------------------------------------------ @@ -2861,112 +2861,112 @@ function setup_rust() { # ------------------------------------------------------------------------------ function setup_uv() { - local UV_BIN="/usr/local/bin/uv" - local TMP_DIR=$(mktemp -d) - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "uv") + local UV_BIN="/usr/local/bin/uv" + local TMP_DIR=$(mktemp -d) + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "uv") - local ARCH=$(uname -m) - local UV_TAR + local ARCH=$(uname -m) + local UV_TAR - case "$ARCH" in - x86_64) - if grep -qi "alpine" /etc/os-release; then - UV_TAR="uv-x86_64-unknown-linux-musl.tar.gz" - else - UV_TAR="uv-x86_64-unknown-linux-gnu.tar.gz" - fi - ;; - aarch64) - if grep -qi "alpine" /etc/os-release; then - UV_TAR="uv-aarch64-unknown-linux-musl.tar.gz" - else - UV_TAR="uv-aarch64-unknown-linux-gnu.tar.gz" - fi - ;; - *) - msg_error "Unsupported architecture: $ARCH" - rm -rf "$TMP_DIR" - return 1 - ;; - esac - - ensure_dependencies jq - - local LATEST_VERSION - LATEST_VERSION=$(curl -fsSL https://api.github.com/repos/astral-sh/uv/releases/latest | - jq -r '.tag_name' | sed 's/^v//') - - if [[ -z "$LATEST_VERSION" ]]; then - msg_error "Could not fetch latest uv version" - rm -rf "$TMP_DIR" - return 1 - fi - - if [[ -x "$UV_BIN" ]]; then - local INSTALLED_VERSION - INSTALLED_VERSION=$($UV_BIN -V 2>/dev/null | awk '{print $2}') - if [[ "$INSTALLED_VERSION" == "$LATEST_VERSION" ]]; then - if [[ "$CACHED_VERSION" == "$LATEST_VERSION" ]]; then + case "$ARCH" in + x86_64) + if grep -qi "alpine" /etc/os-release; then + UV_TAR="uv-x86_64-unknown-linux-musl.tar.gz" + else + UV_TAR="uv-x86_64-unknown-linux-gnu.tar.gz" + fi + ;; + aarch64) + if grep -qi "alpine" /etc/os-release; then + UV_TAR="uv-aarch64-unknown-linux-musl.tar.gz" + else + UV_TAR="uv-aarch64-unknown-linux-gnu.tar.gz" + fi + ;; + *) + msg_error "Unsupported architecture: $ARCH" + rm -rf "$TMP_DIR" + return 1 + ;; + esac + + ensure_dependencies jq + + local LATEST_VERSION + LATEST_VERSION=$(curl -fsSL https://api.github.com/repos/astral-sh/uv/releases/latest | + jq -r '.tag_name' | sed 's/^v//') + + if [[ -z "$LATEST_VERSION" ]]; then + msg_error "Could not fetch latest uv version" rm -rf "$TMP_DIR" - return 0 - fi - cache_installed_version "uv" "$LATEST_VERSION" - rm -rf "$TMP_DIR" - return 0 - else - msg_info "Updating uv from $INSTALLED_VERSION to $LATEST_VERSION" - fi - else - msg_info "Installing uv $LATEST_VERSION" - fi - - local UV_URL="https://github.com/astral-sh/uv/releases/latest/download/${UV_TAR}" - curl -fsSL "$UV_URL" -o "$TMP_DIR/uv.tar.gz" || { - msg_error "Failed to download uv" - rm -rf "$TMP_DIR" - return 1 - } - - tar -xzf "$TMP_DIR/uv.tar.gz" -C "$TMP_DIR" || { - msg_error "Failed to extract uv" - rm -rf "$TMP_DIR" - return 1 - } - - install -m 755 "$TMP_DIR"/*/uv "$UV_BIN" || { - msg_error "Failed to install uv binary" - rm -rf "$TMP_DIR" - return 1 - } - - rm -rf "$TMP_DIR" - ensure_usr_local_bin_persist - export PATH="/usr/local/bin:$PATH" - - $STD uv python update-shell || true - cache_installed_version "uv" "$LATEST_VERSION" - msg_ok "Installed uv $LATEST_VERSION" - - if [[ -n "${PYTHON_VERSION:-}" ]]; then - local VERSION_MATCH - VERSION_MATCH=$(uv python list --only-downloads | - grep -E "^cpython-${PYTHON_VERSION//./\\.}\.[0-9]+-linux" | - cut -d'-' -f2 | sort -V | tail -n1) - - if [[ -z "$VERSION_MATCH" ]]; then - msg_error "No matching Python $PYTHON_VERSION.x version found" - return 1 - fi - - if ! uv python list | grep -q "cpython-${VERSION_MATCH}-linux.*uv/python"; then - $STD uv python install "$VERSION_MATCH" || { - msg_error "Failed to install Python $VERSION_MATCH" return 1 - } - msg_ok "Installed Python $VERSION_MATCH" fi - fi + + if [[ -x "$UV_BIN" ]]; then + local INSTALLED_VERSION + INSTALLED_VERSION=$($UV_BIN -V 2>/dev/null | awk '{print $2}') + if [[ "$INSTALLED_VERSION" == "$LATEST_VERSION" ]]; then + if [[ "$CACHED_VERSION" == "$LATEST_VERSION" ]]; then + rm -rf "$TMP_DIR" + return 0 + fi + cache_installed_version "uv" "$LATEST_VERSION" + rm -rf "$TMP_DIR" + return 0 + else + msg_info "Updating uv from $INSTALLED_VERSION to $LATEST_VERSION" + fi + else + msg_info "Installing uv $LATEST_VERSION" + fi + + local UV_URL="https://github.com/astral-sh/uv/releases/latest/download/${UV_TAR}" + curl -fsSL "$UV_URL" -o "$TMP_DIR/uv.tar.gz" || { + msg_error "Failed to download uv" + rm -rf "$TMP_DIR" + return 1 + } + + tar -xzf "$TMP_DIR/uv.tar.gz" -C "$TMP_DIR" || { + msg_error "Failed to extract uv" + rm -rf "$TMP_DIR" + return 1 + } + + install -m 755 "$TMP_DIR"/*/uv "$UV_BIN" || { + msg_error "Failed to install uv binary" + rm -rf "$TMP_DIR" + return 1 + } + + rm -rf "$TMP_DIR" + ensure_usr_local_bin_persist + export PATH="/usr/local/bin:$PATH" + + $STD uv python update-shell || true + cache_installed_version "uv" "$LATEST_VERSION" + msg_ok "Installed uv $LATEST_VERSION" + + if [[ -n "${PYTHON_VERSION:-}" ]]; then + local VERSION_MATCH + VERSION_MATCH=$(uv python list --only-downloads | + grep -E "^cpython-${PYTHON_VERSION//./\\.}\.[0-9]+-linux" | + cut -d'-' -f2 | sort -V | tail -n1) + + if [[ -z "$VERSION_MATCH" ]]; then + msg_error "No matching Python $PYTHON_VERSION.x version found" + return 1 + fi + + if ! uv python list | grep -q "cpython-${VERSION_MATCH}-linux.*uv/python"; then + $STD uv python install "$VERSION_MATCH" || { + msg_error "Failed to install Python $VERSION_MATCH" + return 1 + } + msg_ok "Installed Python $VERSION_MATCH" + fi + fi } # ------------------------------------------------------------------------------ @@ -2979,65 +2979,65 @@ function setup_uv() { # ------------------------------------------------------------------------------ function setup_yq() { - local TMP_DIR=$(mktemp -d) - local BINARY_PATH="/usr/local/bin/yq" - local GITHUB_REPO="mikefarah/yq" - local CURRENT_VERSION="" - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "yq") + local TMP_DIR=$(mktemp -d) + local BINARY_PATH="/usr/local/bin/yq" + local GITHUB_REPO="mikefarah/yq" + local CURRENT_VERSION="" + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "yq") - ensure_dependencies jq - ensure_usr_local_bin_persist + ensure_dependencies jq + ensure_usr_local_bin_persist - if command -v yq &>/dev/null; then - if ! yq --version 2>&1 | grep -q 'mikefarah'; then - rm -f "$(command -v yq)" - else - CURRENT_VERSION=$(yq --version 2>/dev/null | awk '{print $NF}' | sed 's/^v//') + if command -v yq &>/dev/null; then + if ! yq --version 2>&1 | grep -q 'mikefarah'; then + rm -f "$(command -v yq)" + else + CURRENT_VERSION=$(yq --version 2>/dev/null | awk '{print $NF}' | sed 's/^v//') + fi fi - fi - local LATEST_VERSION - LATEST_VERSION=$(curl -fsSL "https://api.github.com/repos/${GITHUB_REPO}/releases/latest" | - jq -r '.tag_name' | sed 's/^v//') + local LATEST_VERSION + LATEST_VERSION=$(curl -fsSL "https://api.github.com/repos/${GITHUB_REPO}/releases/latest" | + jq -r '.tag_name' | sed 's/^v//') - if [[ -z "$LATEST_VERSION" ]]; then - msg_error "Could not determine latest yq version" - rm -rf "$TMP_DIR" - return 1 - fi - - if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$LATEST_VERSION" ]]; then - if [[ "$CACHED_VERSION" == "$LATEST_VERSION" ]]; then - rm -rf "$TMP_DIR" - return 0 + if [[ -z "$LATEST_VERSION" ]]; then + msg_error "Could not determine latest yq version" + rm -rf "$TMP_DIR" + return 1 fi - cache_installed_version "yq" "$LATEST_VERSION" + + if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$LATEST_VERSION" ]]; then + if [[ "$CACHED_VERSION" == "$LATEST_VERSION" ]]; then + rm -rf "$TMP_DIR" + return 0 + fi + cache_installed_version "yq" "$LATEST_VERSION" + rm -rf "$TMP_DIR" + return 0 + fi + + msg_info "Installing yq $LATEST_VERSION" + curl -fsSL "https://github.com/${GITHUB_REPO}/releases/download/v${LATEST_VERSION}/yq_linux_amd64" -o "$TMP_DIR/yq" || { + msg_error "Failed to download yq" + rm -rf "$TMP_DIR" + return 1 + } + + chmod +x "$TMP_DIR/yq" + mv "$TMP_DIR/yq" "$BINARY_PATH" + + if [[ ! -x "$BINARY_PATH" ]]; then + msg_error "Failed to install yq" + rm -rf "$TMP_DIR" + return 1 + fi + rm -rf "$TMP_DIR" - return 0 - fi + hash -r - msg_info "Installing yq $LATEST_VERSION" - curl -fsSL "https://github.com/${GITHUB_REPO}/releases/download/v${LATEST_VERSION}/yq_linux_amd64" -o "$TMP_DIR/yq" || { - msg_error "Failed to download yq" - rm -rf "$TMP_DIR" - return 1 - } - - chmod +x "$TMP_DIR/yq" - mv "$TMP_DIR/yq" "$BINARY_PATH" - - if [[ ! -x "$BINARY_PATH" ]]; then - msg_error "Failed to install yq" - rm -rf "$TMP_DIR" - return 1 - fi - - rm -rf "$TMP_DIR" - hash -r - - local FINAL_VERSION - FINAL_VERSION=$("$BINARY_PATH" --version 2>/dev/null | awk '{print $NF}' | sed 's/^v//') - cache_installed_version "yq" "$FINAL_VERSION" - msg_ok "Installed yq $FINAL_VERSION" + local FINAL_VERSION + FINAL_VERSION=$("$BINARY_PATH" --version 2>/dev/null | awk '{print $NF}' | sed 's/^v//') + cache_installed_version "yq" "$FINAL_VERSION" + msg_ok "Installed yq $FINAL_VERSION" } From 5d9a7b472dfa95c4baad79c3b15274cbac83f22a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 16 Oct 2025 22:22:10 +0200 Subject: [PATCH 1423/1733] testing --- misc/tools.func | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/misc/tools.func b/misc/tools.func index 525d28ed6..dc95a8380 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -17,7 +17,11 @@ cache_installed_version() { get_cached_version() { local app="$1" mkdir -p /var/cache/app-versions - [[ -f "/var/cache/app-versions/${app}_version.txt" ]] && cat "/var/cache/app-versions/${app}_version.txt" + if [[ -f "/var/cache/app-versions/${app}_version.txt" ]]; then + cat "/var/cache/app-versions/${app}_version.txt" + return 0 + fi + return 0 } # ------------------------------------------------------------------------------ From f3009620f19c839891df9062c3d75319b860aeb6 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 16 Oct 2025 22:38:21 +0200 Subject: [PATCH 1424/1733] push testcase --- install/debian-install.sh | 31 ++-- misc/test-tools-func.sh | 302 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 313 insertions(+), 20 deletions(-) create mode 100644 misc/test-tools-func.sh diff --git a/install/debian-install.sh b/install/debian-install.sh index 57ce16580..4ee6dbab1 100644 --- a/install/debian-install.sh +++ b/install/debian-install.sh @@ -4,7 +4,7 @@ # Author: Test Suite for tools.func # License: MIT # https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Purpose: Comprehensive test of all setup_* functions from tools.func +# Purpose: Run comprehensive test suite for all setup_* functions from tools.func source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color @@ -15,29 +15,20 @@ network_check update_os msg_info "Installing Base Dependencies" -$STD apt-get install -y curl wget gpg jq git build-essential +$STD apt-get install -y curl wget ca-certificates msg_ok "Installed Base Dependencies" -# Helper function to test and validate installation -test_and_validate() { - local test_name="$1" - local command_check="$2" - local version_cmd="$3" +msg_info "Downloading and executing tools.func test suite" +bash <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/test-tools-func.sh) +msg_ok "Test suite completed" - echo -e "\n${GN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}" - echo -e "${GN}Testing: ${test_name}${CL}" - echo -e "${GN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}\n" +motd_ssh +customize - if command -v "$command_check" &>/dev/null; then - local version_output - version_output=$($version_cmd 2>&1 | head -n1) - msg_ok "${test_name} installed: ${version_output}" - return 0 - else - msg_error "${test_name} validation FAILED - command not found: $command_check" - return 1 - fi -} +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" # ============================================================================== # 1. YQ - YAML Processor diff --git a/misc/test-tools-func.sh b/misc/test-tools-func.sh new file mode 100644 index 000000000..b843f39e6 --- /dev/null +++ b/misc/test-tools-func.sh @@ -0,0 +1,302 @@ +#!/usr/bin/env bash + +# ============================================================================== +# TEST SUITE FOR tools.func +# ============================================================================== +# This script tests all setup_* functions from tools.func +# Can be run standalone in any Debian-based system +# +# Usage: +# bash <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/test-tools-func.sh) +# ============================================================================== + +set -euo pipefail + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +CYAN='\033[0;36m' +NC='\033[0m' # No Color + +# Counters +TESTS_PASSED=0 +TESTS_FAILED=0 +TESTS_SKIPPED=0 + +# Log file +TEST_LOG="/tmp/tools-func-test-$(date +%Y%m%d-%H%M%S).log" + +echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}" +echo -e "${CYAN} TOOLS.FUNC TEST SUITE${NC}" +echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}" +echo -e "Log file: ${TEST_LOG}\n" + +# Source tools.func from repository +echo -e "${BLUE}► Sourcing tools.func from repository...${NC}" +if ! source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func); then + echo -e "${RED}✖ Failed to source tools.func${NC}" + exit 1 +fi +echo -e "${GREEN}✔ tools.func loaded${NC}\n" + +# Source core functions if available +if curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func &>/dev/null; then + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) || true +fi + +# Define STD if not already defined +STD="${STD:-&>/dev/null}" + +# Helper functions +msg_info() { echo -e "${BLUE}ℹ ${1}${NC}"; } +msg_ok() { echo -e "${GREEN}✔ ${1}${NC}"; } +msg_error() { echo -e "${RED}✖ ${1}${NC}"; } +msg_warn() { echo -e "${YELLOW}⚠ ${1}${NC}"; } + +# Test validation function +test_function() { + local test_name="$1" + local test_command="$2" + local validation_cmd="${3:-}" + + echo -e "\n${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo -e "${CYAN}Testing: ${test_name}${NC}" + echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + + { + echo "=== Test: ${test_name} ===" + echo "Command: ${test_command}" + echo "Started: $(date)" + } >>"$TEST_LOG" + + if eval "$test_command" >>"$TEST_LOG" 2>&1; then + if [[ -n "$validation_cmd" ]]; then + if eval "$validation_cmd" >>"$TEST_LOG" 2>&1; then + local output + output=$(eval "$validation_cmd" 2>&1 | head -n1) + msg_ok "${test_name} - ${output}" + ((TESTS_PASSED++)) + else + msg_error "${test_name} - Installation succeeded but validation failed" + ((TESTS_FAILED++)) + fi + else + msg_ok "${test_name}" + ((TESTS_PASSED++)) + fi + else + msg_error "${test_name} - Installation failed" + ((TESTS_FAILED++)) + fi + + echo "Completed: $(date)" >>"$TEST_LOG" + echo "" >>"$TEST_LOG" +} + +# Skip test with reason +skip_test() { + local test_name="$1" + local reason="$2" + + echo -e "\n${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo -e "${CYAN}Testing: ${test_name}${NC}" + echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + msg_warn "Skipped: ${reason}" + ((TESTS_SKIPPED++)) +} + +# Update system +msg_info "Updating system packages" +apt-get update &>/dev/null && msg_ok "System updated" + +# Install base dependencies +msg_info "Installing base dependencies" +apt-get install -y curl wget gpg jq git build-essential ca-certificates &>/dev/null && msg_ok "Base dependencies installed" + +# ============================================================================== +# TEST 1: YQ - YAML Processor +# ============================================================================== +test_function "YQ" \ + "setup_yq" \ + "yq --version" + +# ============================================================================== +# TEST 2: ADMINER - Database Management +# ============================================================================== +test_function "Adminer" \ + "setup_adminer" \ + "test -f /usr/share/adminer/latest.php && echo 'Adminer installed'" + +# ============================================================================== +# TEST 3: LOCAL IP HELPER +# ============================================================================== +test_function "Local IP Helper" \ + "setup_local_ip_helper" \ + "systemctl is-enabled local-ip-helper.service &>/dev/null && echo 'Service enabled'" + +# ============================================================================== +# TEST 4: CLICKHOUSE +# ============================================================================== +test_function "ClickHouse" \ + "setup_clickhouse" \ + "clickhouse-server --version" + +# ============================================================================== +# TEST 5: POSTGRESQL +# ============================================================================== +test_function "PostgreSQL 17" \ + "PG_VERSION=17 setup_postgresql" \ + "psql --version" + +# ============================================================================== +# TEST 6: MARIADB +# ============================================================================== +test_function "MariaDB 11.4" \ + "MARIADB_VERSION=11.4 setup_mariadb" \ + "mariadb --version" + +# ============================================================================== +# TEST 7: MYSQL (Remove MariaDB first) +# ============================================================================== +msg_info "Removing MariaDB before MySQL installation" +systemctl stop mariadb &>/dev/null || true +apt-get purge -y mariadb-server mariadb-client mariadb-common &>/dev/null || true +apt-get autoremove -y &>/dev/null +rm -rf /etc/mysql /var/lib/mysql +msg_ok "MariaDB removed" + +test_function "MySQL 8.0" \ + "MYSQL_VERSION=8.0 setup_mysql" \ + "mysql --version" + +# ============================================================================== +# TEST 8: MONGODB (Check AVX support) +# ============================================================================== +if grep -q avx /proc/cpuinfo; then + test_function "MongoDB 8.0" \ + "MONGO_VERSION=8.0 setup_mongodb" \ + "mongod --version" +else + skip_test "MongoDB 8.0" "CPU does not support AVX" +fi + +# ============================================================================== +# TEST 9: NODE.JS +# ============================================================================== +test_function "Node.js 22 with modules" \ + "NODE_VERSION=22 NODE_MODULE='yarn,pnpm@10.1.0,pm2' setup_nodejs" \ + "node --version && npm --version && yarn --version && pnpm --version && pm2 --version" + +# ============================================================================== +# TEST 10: PYTHON (UV) +# ============================================================================== +test_function "Python 3.12 via uv" \ + "PYTHON_VERSION=3.12 setup_uv" \ + "uv --version" + +# ============================================================================== +# TEST 11: PHP +# ============================================================================== +test_function "PHP 8.3 with FPM" \ + "PHP_VERSION=8.3 PHP_FPM=YES PHP_MODULE='redis,imagick,apcu,zip,mbstring' setup_php" \ + "php --version" + +# ============================================================================== +# TEST 12: COMPOSER +# ============================================================================== +test_function "Composer" \ + "setup_composer" \ + "composer --version" + +# ============================================================================== +# TEST 13: JAVA +# ============================================================================== +test_function "Java Temurin 21" \ + "JAVA_VERSION=21 setup_java" \ + "java --version" + +# ============================================================================== +# TEST 14: GO +# ============================================================================== +test_function "Go (latest)" \ + "GO_VERSION=latest setup_go" \ + "go version" + +# ============================================================================== +# TEST 15: RUBY +# ============================================================================== +test_function "Ruby 3.4.1 with Rails" \ + "RUBY_VERSION=3.4.1 RUBY_INSTALL_RAILS=true setup_ruby" \ + "ruby --version" + +# ============================================================================== +# TEST 16: RUST +# ============================================================================== +test_function "Rust (stable)" \ + "RUST_TOOLCHAIN=stable RUST_CRATES='cargo-edit' setup_rust" \ + "source \$HOME/.cargo/env && rustc --version" + +# ============================================================================== +# TEST 17: GHOSTSCRIPT +# ============================================================================== +test_function "Ghostscript" \ + "setup_gs" \ + "gs --version" + +# ============================================================================== +# TEST 18: IMAGEMAGICK +# ============================================================================== +test_function "ImageMagick" \ + "setup_imagemagick" \ + "magick --version" + +# ============================================================================== +# TEST 19: FFMPEG +# ============================================================================== +test_function "FFmpeg n7.1.1 (full)" \ + "FFMPEG_VERSION=n7.1.1 FFMPEG_TYPE=full setup_ffmpeg" \ + "ffmpeg -version" + +# ============================================================================== +# FINAL SUMMARY +# ============================================================================== +echo -e "\n${CYAN}═══════════════════════════════════════════════════════════${NC}" +echo -e "${CYAN} TEST SUMMARY${NC}" +echo -e "${CYAN}═══════════════════════════════════════════════════════════${NC}" +echo -e "${GREEN}✔ Passed: ${TESTS_PASSED}${NC}" +echo -e "${RED}✖ Failed: ${TESTS_FAILED}${NC}" +echo -e "${YELLOW}⚠ Skipped: ${TESTS_SKIPPED}${NC}" +echo -e "\nDetailed log: ${TEST_LOG}" + +# Generate summary report +{ + echo "" + echo "=== FINAL SUMMARY ===" + echo "Tests Passed: ${TESTS_PASSED}" + echo "Tests Failed: ${TESTS_FAILED}" + echo "Tests Skipped: ${TESTS_SKIPPED}" + echo "" + echo "=== Installed Versions ===" + command -v yq &>/dev/null && echo "yq: $(yq --version 2>&1)" + command -v clickhouse-server &>/dev/null && echo "ClickHouse: $(clickhouse-server --version 2>&1 | head -n1)" + command -v psql &>/dev/null && echo "PostgreSQL: $(psql --version)" + command -v mysql &>/dev/null && echo "MySQL: $(mysql --version)" + command -v mongod &>/dev/null && echo "MongoDB: $(mongod --version 2>&1 | head -n1)" + command -v node &>/dev/null && echo "Node.js: $(node --version)" + command -v php &>/dev/null && echo "PHP: $(php --version | head -n1)" + command -v java &>/dev/null && echo "Java: $(java --version 2>&1 | head -n1)" + command -v go &>/dev/null && echo "Go: $(go version)" + command -v ruby &>/dev/null && echo "Ruby: $(ruby --version)" + command -v rustc &>/dev/null && echo "Rust: $(rustc --version)" + command -v ffmpeg &>/dev/null && echo "FFmpeg: $(ffmpeg -version 2>&1 | head -n1)" +} >>"$TEST_LOG" + +if [ $TESTS_FAILED -eq 0 ]; then + echo -e "\n${GREEN}All tests completed successfully!${NC}" + exit 0 +else + echo -e "\n${RED}Some tests failed. Check the log for details.${NC}" + exit 1 +fi From fedda9aaadefdafcbc1e5682318e46ad4ad08de9 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 16 Oct 2025 22:42:05 +0200 Subject: [PATCH 1425/1733] testing --- install/debian-install.sh | 252 -------------------------------------- misc/test-tools-func.sh | 12 +- 2 files changed, 7 insertions(+), 257 deletions(-) diff --git a/install/debian-install.sh b/install/debian-install.sh index 4ee6dbab1..b5864b09c 100644 --- a/install/debian-install.sh +++ b/install/debian-install.sh @@ -29,255 +29,3 @@ msg_info "Cleaning up" $STD apt-get -y autoremove $STD apt-get -y autoclean msg_ok "Cleaned" - -# ============================================================================== -# 1. YQ - YAML Processor -# ============================================================================== -echo -e "\n${YW}[1/20] Testing: YQ${CL}" -setup_yq -test_and_validate "yq" "yq" "yq --version" - -# ============================================================================== -# 2. ADMINER - Database Management Tool -# ============================================================================== -echo -e "\n${YW}[2/20] Testing: Adminer${CL}" -setup_adminer -if [ -f "/usr/share/adminer/latest.php" ]; then - msg_ok "Adminer installed at /usr/share/adminer/latest.php" -else - msg_error "Adminer installation FAILED" -fi - -# ============================================================================== -# 3. LOCAL IP HELPER -# ============================================================================== -echo -e "\n${YW}[3/20] Testing: Local IP Helper${CL}" -setup_local_ip_helper -if systemctl is-enabled local-ip-helper.service &>/dev/null; then - msg_ok "Local IP Helper service enabled" -else - msg_error "Local IP Helper service NOT enabled" -fi - -# ============================================================================== -# 4. CLICKHOUSE - Columnar Database -# ============================================================================== -echo -e "\n${YW}[4/20] Testing: ClickHouse${CL}" -setup_clickhouse -test_and_validate "ClickHouse" "clickhouse-server" "clickhouse-server --version" -systemctl status clickhouse-server --no-pager | head -n5 - -# ============================================================================== -# 5. POSTGRESQL - Relational Database (Version 17) -# ============================================================================== -echo -e "\n${YW}[5/20] Testing: PostgreSQL 17${CL}" -PG_VERSION=17 setup_postgresql -test_and_validate "PostgreSQL" "psql" "psql --version" -sudo -u postgres psql -c "SELECT version();" | head -n3 - -# ============================================================================== -# 6. MARIADB - MySQL Fork (Version 11.4) -# ============================================================================== -echo -e "\n${YW}[6/20] Testing: MariaDB 11.4${CL}" -MARIADB_VERSION="11.4" setup_mariadb -test_and_validate "MariaDB" "mariadb" "mariadb --version" -mariadb -e "SELECT VERSION();" - -# ============================================================================== -# 7. MYSQL - Remove MariaDB first, then install MySQL 8.0 -# ============================================================================== -echo -e "\n${YW}[7/20] Testing: MySQL 8.0 (removing MariaDB first)${CL}" -msg_info "Removing MariaDB to avoid conflicts" -$STD systemctl stop mariadb -$STD apt-get purge -y mariadb-server mariadb-client mariadb-common -$STD apt-get autoremove -y -$STD rm -rf /etc/mysql /var/lib/mysql -msg_ok "MariaDB removed" - -MYSQL_VERSION="8.0" setup_mysql -test_and_validate "MySQL" "mysql" "mysql --version" -mysql -e "SELECT VERSION();" - -# ============================================================================== -# 8. MONGODB - NoSQL Database (Version 8.0 - requires AVX CPU) -# ============================================================================== -echo -e "\n${YW}[8/20] Testing: MongoDB 8.0${CL}" -if grep -q avx /proc/cpuinfo; then - MONGO_VERSION="8.0" setup_mongodb - test_and_validate "MongoDB" "mongod" "mongod --version" - systemctl status mongod --no-pager | head -n5 -else - msg_info "Skipping MongoDB - CPU does not support AVX" -fi - -# ============================================================================== -# 9. NODE.JS - JavaScript Runtime (Version 22 with modules) -# ============================================================================== -echo -e "\n${YW}[9/20] Testing: Node.js 22 with yarn, pnpm, pm2${CL}" -NODE_VERSION="22" NODE_MODULE="yarn,pnpm@10.1.0,pm2" setup_nodejs -test_and_validate "Node.js" "node" "node --version" -test_and_validate "npm" "npm" "npm --version" -test_and_validate "yarn" "yarn" "yarn --version" -test_and_validate "pnpm" "pnpm" "pnpm --version" -test_and_validate "pm2" "pm2" "pm2 --version" - -# ============================================================================== -# 10. PYTHON (via UV) - Version 3.12 -# ============================================================================== -echo -e "\n${YW}[10/20] Testing: Python 3.12 via uv${CL}" -PYTHON_VERSION="3.12" setup_uv -test_and_validate "uv" "uv" "uv --version" -if [ -d "/opt/venv" ]; then - source /opt/venv/bin/activate - test_and_validate "Python" "python" "python --version" - deactivate -fi - -# ============================================================================== -# 11. PHP - Version 8.3 with FPM and modules -# ============================================================================== -echo -e "\n${YW}[11/20] Testing: PHP 8.3 with FPM${CL}" -PHP_VERSION="8.3" PHP_FPM="YES" PHP_MODULE="redis,imagick,apcu,zip,mbstring" setup_php -test_and_validate "PHP" "php" "php --version" -php -m | grep -E "redis|imagick|apcu|zip|mbstring" -systemctl status php8.3-fpm --no-pager | head -n5 - -# ============================================================================== -# 12. COMPOSER - PHP Dependency Manager -# ============================================================================== -echo -e "\n${YW}[12/20] Testing: Composer${CL}" -setup_composer -test_and_validate "Composer" "composer" "composer --version" - -# ============================================================================== -# 13. JAVA - Temurin JDK 21 -# ============================================================================== -echo -e "\n${YW}[13/20] Testing: Java (Temurin 21)${CL}" -JAVA_VERSION="21" setup_java -test_and_validate "Java" "java" "java --version" -echo -e "\nJava Home: $JAVA_HOME" - -# ============================================================================== -# 14. GO - Golang (latest) -# ============================================================================== -echo -e "\n${YW}[14/20] Testing: Go (latest)${CL}" -GO_VERSION="latest" setup_go -test_and_validate "Go" "go" "go version" - -# ============================================================================== -# 15. RUBY - Version 3.4.1 with Rails -# ============================================================================== -echo -e "\n${YW}[15/20] Testing: Ruby 3.4.1 with Rails${CL}" -RUBY_VERSION="3.4.1" RUBY_INSTALL_RAILS="true" setup_ruby -test_and_validate "Ruby" "ruby" "ruby --version" -test_and_validate "Rails" "rails" "rails --version" - -# ============================================================================== -# 16. RUST - Stable toolchain with cargo-edit -# ============================================================================== -echo -e "\n${YW}[16/20] Testing: Rust (stable)${CL}" -RUST_TOOLCHAIN="stable" RUST_CRATES="cargo-edit" setup_rust -source "$HOME/.cargo/env" -test_and_validate "Rust" "rustc" "rustc --version" -test_and_validate "Cargo" "cargo" "cargo --version" - -# ============================================================================== -# 17. GHOSTSCRIPT - PDF/PostScript processor -# ============================================================================== -echo -e "\n${YW}[17/20] Testing: Ghostscript${CL}" -setup_gs -test_and_validate "Ghostscript" "gs" "gs --version" - -# ============================================================================== -# 18. IMAGEMAGICK - Image processing from source -# ============================================================================== -echo -e "\n${YW}[18/20] Testing: ImageMagick${CL}" -setup_imagemagick -test_and_validate "ImageMagick" "magick" "magick --version" - -# ============================================================================== -# 19. FFMPEG - Full build (n7.1.1) -# ============================================================================== -echo -e "\n${YW}[19/20] Testing: FFmpeg (full build)${CL}" -FFMPEG_VERSION="n7.1.1" FFMPEG_TYPE="full" setup_ffmpeg -test_and_validate "FFmpeg" "ffmpeg" "ffmpeg -version" -ffmpeg -encoders 2>/dev/null | grep -E "libx264|libvpx|libmp3lame" - -# ============================================================================== -# 20. GITHUB RELEASE DEPLOYMENTS -# ============================================================================== -echo -e "\n${YW}[20/20] Testing: GitHub Release Deployments${CL}" - -# Test 1: Tarball deployment -msg_info "Testing: Tarball deployment (Hanko)" -fetch_and_deploy_gh_release "hanko" "teamhanko/hanko" "tarball" "latest" "/opt/hanko-test" -if [ -d "/opt/hanko-test" ]; then - msg_ok "Hanko tarball deployed to /opt/hanko-test" - ls -lah /opt/hanko-test | head -n10 -else - msg_error "Hanko tarball deployment FAILED" -fi - -# Test 2: Single binary deployment -msg_info "Testing: Single binary deployment (Argus)" -fetch_and_deploy_gh_release "argus" "release-argus/Argus" "singlefile" "latest" "/opt/argus" "Argus-.*linux-amd64" -if [ -f "/opt/argus/argus" ]; then - msg_ok "Argus binary deployed" - /opt/argus/argus version 2>&1 || echo "Binary exists at /opt/argus/argus" -else - msg_error "Argus binary deployment FAILED" -fi - -# ============================================================================== -# FINAL SUMMARY -# ============================================================================== -echo -e "\n${GN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}" -echo -e "${GN} TEST SUITE SUMMARY${CL}" -echo -e "${GN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${CL}\n" - -msg_info "Generating installation report" -{ - echo "=== tools.func Test Suite Report ===" - echo "Date: $(date)" - echo "Hostname: $(hostname)" - echo "" - echo "--- Installed Versions ---" - command -v yq &>/dev/null && echo "yq: $(yq --version 2>&1)" - command -v clickhouse-server &>/dev/null && echo "ClickHouse: $(clickhouse-server --version 2>&1 | head -n1)" - command -v psql &>/dev/null && echo "PostgreSQL: $(psql --version)" - command -v mysql &>/dev/null && echo "MySQL: $(mysql --version)" - command -v mongod &>/dev/null && echo "MongoDB: $(mongod --version 2>&1 | head -n1)" - command -v node &>/dev/null && echo "Node.js: $(node --version)" - command -v npm &>/dev/null && echo "npm: $(npm --version)" - command -v yarn &>/dev/null && echo "yarn: $(yarn --version)" - command -v pnpm &>/dev/null && echo "pnpm: $(pnpm --version)" - command -v uv &>/dev/null && echo "uv: $(uv --version)" - command -v php &>/dev/null && echo "PHP: $(php --version | head -n1)" - command -v composer &>/dev/null && echo "Composer: $(composer --version)" - command -v java &>/dev/null && echo "Java: $(java --version 2>&1 | head -n1)" - command -v go &>/dev/null && echo "Go: $(go version)" - command -v ruby &>/dev/null && echo "Ruby: $(ruby --version)" - command -v rustc &>/dev/null && echo "Rust: $(rustc --version)" - command -v gs &>/dev/null && echo "Ghostscript: $(gs --version)" - command -v magick &>/dev/null && echo "ImageMagick: $(magick --version | head -n1)" - command -v ffmpeg &>/dev/null && echo "FFmpeg: $(ffmpeg -version 2>&1 | head -n1)" - echo "" - echo "--- Service Status ---" - systemctl is-active clickhouse-server &>/dev/null && echo "ClickHouse: Active" - systemctl is-active postgresql &>/dev/null && echo "PostgreSQL: Active" - systemctl is-active mysql &>/dev/null && echo "MySQL: Active" - systemctl is-active mongod &>/dev/null && echo "MongoDB: Active" - systemctl is-active php8.3-fpm &>/dev/null && echo "PHP-FPM: Active" - systemctl is-active local-ip-helper &>/dev/null && echo "Local IP Helper: Active" -} >~/tools-func-test-report.txt - -cat ~/tools-func-test-report.txt -msg_ok "Test report saved to ~/tools-func-test-report.txt" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" diff --git a/misc/test-tools-func.sh b/misc/test-tools-func.sh index b843f39e6..c9d2c219d 100644 --- a/misc/test-tools-func.sh +++ b/misc/test-tools-func.sh @@ -10,7 +10,7 @@ # bash <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/test-tools-func.sh) # ============================================================================== -set -euo pipefail +set -uo pipefail # Colors RED='\033[0;31m' @@ -73,13 +73,14 @@ test_function() { if eval "$test_command" >>"$TEST_LOG" 2>&1; then if [[ -n "$validation_cmd" ]]; then - if eval "$validation_cmd" >>"$TEST_LOG" 2>&1; then - local output - output=$(eval "$validation_cmd" 2>&1 | head -n1) - msg_ok "${test_name} - ${output}" + local output + if output=$(eval "$validation_cmd" 2>&1); then + msg_ok "${test_name} - $(echo "$output" | head -n1)" ((TESTS_PASSED++)) else msg_error "${test_name} - Installation succeeded but validation failed" + echo "Validation command: $validation_cmd" >>"$TEST_LOG" + echo "Validation failed" >>"$TEST_LOG" ((TESTS_FAILED++)) fi else @@ -88,6 +89,7 @@ test_function() { fi else msg_error "${test_name} - Installation failed" + echo "Installation failed" >>"$TEST_LOG" ((TESTS_FAILED++)) fi From 6775dc41ddc9467dbcf6aa4ee142ba82754a0115 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 16 Oct 2025 22:47:01 +0200 Subject: [PATCH 1426/1733] fix STD issue --- misc/test-tools-func.sh | 46 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/misc/test-tools-func.sh b/misc/test-tools-func.sh index c9d2c219d..d58253fbc 100644 --- a/misc/test-tools-func.sh +++ b/misc/test-tools-func.sh @@ -46,8 +46,33 @@ if curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) || true fi -# Define STD if not already defined -STD="${STD:-&>/dev/null}" +# Override STD to show all output for debugging +export STD='' + +# Update PATH to include common installation directories +export PATH="/usr/local/bin:/usr/local/go/bin:/root/.cargo/bin:/root/.rbenv/bin:/root/.rbenv/shims:/opt/java/bin:$PATH" + +# Helper functions (override if needed from core.func) +msg_info() { echo -e "${BLUE}ℹ ${1}${CL:-${NC}}"; } +msg_ok() { echo -e "${GREEN}✔ ${1}${CL:-${NC}}"; } +msg_error() { echo -e "${RED}✖ ${1}${CL:-${NC}}"; } +msg_warn() { echo -e "${YELLOW}⚠ ${1}${CL:-${NC}}"; } + +# Color definitions if not already set +GN="${GN:-${GREEN}}" +BL="${BL:-${BLUE}}" +RD="${RD:-${RED}}" +YW="${YW:-${YELLOW}}" +CL="${CL:-${NC}}" + +# Reload environment helper +reload_path() { + export PATH="/usr/local/bin:/usr/local/go/bin:/root/.cargo/bin:/root/.rbenv/bin:/root/.rbenv/shims:/opt/java/bin:$PATH" + # Source profile files if they exist + [ -f "/root/.bashrc" ] && source /root/.bashrc 2>/dev/null || true + [ -f "/root/.profile" ] && source /root/.profile 2>/dev/null || true + [ -f "/root/.cargo/env" ] && source /root/.cargo/env 2>/dev/null || true +} # Helper functions msg_info() { echo -e "${BLUE}ℹ ${1}${NC}"; } @@ -55,6 +80,15 @@ msg_ok() { echo -e "${GREEN}✔ ${1}${NC}"; } msg_error() { echo -e "${RED}✖ ${1}${NC}"; } msg_warn() { echo -e "${YELLOW}⚠ ${1}${NC}"; } +# Reload environment helper +reload_path() { + export PATH="/usr/local/bin:/usr/local/go/bin:/root/.cargo/bin:/root/.rbenv/bin:/root/.rbenv/shims:/opt/java/bin:$PATH" + # Source profile files if they exist + [ -f "/root/.bashrc" ] && source /root/.bashrc 2>/dev/null || true + [ -f "/root/.profile" ] && source /root/.profile 2>/dev/null || true + [ -f "/root/.cargo/env" ] && source /root/.cargo/env 2>/dev/null || true +} + # Test validation function test_function() { local test_name="$1" @@ -72,15 +106,19 @@ test_function() { } >>"$TEST_LOG" if eval "$test_command" >>"$TEST_LOG" 2>&1; then + # Reload PATH after installation + reload_path + if [[ -n "$validation_cmd" ]]; then local output - if output=$(eval "$validation_cmd" 2>&1); then + if output=$(bash -c "$validation_cmd" 2>&1); then msg_ok "${test_name} - $(echo "$output" | head -n1)" ((TESTS_PASSED++)) else msg_error "${test_name} - Installation succeeded but validation failed" echo "Validation command: $validation_cmd" >>"$TEST_LOG" - echo "Validation failed" >>"$TEST_LOG" + echo "Validation output: $output" >>"$TEST_LOG" + echo "PATH: $PATH" >>"$TEST_LOG" ((TESTS_FAILED++)) fi else From f9191b4437b11c574c16d82897c3afac716f6fd5 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 16 Oct 2025 22:50:53 +0200 Subject: [PATCH 1427/1733] output --- misc/test-tools-func.sh | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/misc/test-tools-func.sh b/misc/test-tools-func.sh index d58253fbc..5b272bd7f 100644 --- a/misc/test-tools-func.sh +++ b/misc/test-tools-func.sh @@ -103,9 +103,10 @@ test_function() { echo "=== Test: ${test_name} ===" echo "Command: ${test_command}" echo "Started: $(date)" - } >>"$TEST_LOG" + } | tee -a "$TEST_LOG" - if eval "$test_command" >>"$TEST_LOG" 2>&1; then + # Execute installation with output visible AND logged + if eval "$test_command" 2>&1 | tee -a "$TEST_LOG"; then # Reload PATH after installation reload_path @@ -116,9 +117,11 @@ test_function() { ((TESTS_PASSED++)) else msg_error "${test_name} - Installation succeeded but validation failed" - echo "Validation command: $validation_cmd" >>"$TEST_LOG" - echo "Validation output: $output" >>"$TEST_LOG" - echo "PATH: $PATH" >>"$TEST_LOG" + { + echo "Validation command: $validation_cmd" + echo "Validation output: $output" + echo "PATH: $PATH" + } | tee -a "$TEST_LOG" ((TESTS_FAILED++)) fi else @@ -127,12 +130,12 @@ test_function() { fi else msg_error "${test_name} - Installation failed" - echo "Installation failed" >>"$TEST_LOG" + echo "Installation failed" | tee -a "$TEST_LOG" ((TESTS_FAILED++)) fi - echo "Completed: $(date)" >>"$TEST_LOG" - echo "" >>"$TEST_LOG" + echo "Completed: $(date)" | tee -a "$TEST_LOG" + echo "" | tee -a "$TEST_LOG" } # Skip test with reason From 100d3be0267c81321dd468f96378c2c3a36c96ee Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 16 Oct 2025 22:56:22 +0200 Subject: [PATCH 1428/1733] various fixes --- misc/test-tools-func.sh | 42 +++++++++++++++++++++-------------------- misc/tools.func | 6 +++--- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/misc/test-tools-func.sh b/misc/test-tools-func.sh index 5b272bd7f..a4bed67e4 100644 --- a/misc/test-tools-func.sh +++ b/misc/test-tools-func.sh @@ -49,6 +49,9 @@ fi # Override STD to show all output for debugging export STD='' +# Force non-interactive mode for all apt operations +export DEBIAN_FRONTEND=noninteractive + # Update PATH to include common installation directories export PATH="/usr/local/bin:/usr/local/go/bin:/root/.cargo/bin:/root/.rbenv/bin:/root/.rbenv/shims:/opt/java/bin:$PATH" @@ -74,17 +77,20 @@ reload_path() { [ -f "/root/.cargo/env" ] && source /root/.cargo/env 2>/dev/null || true } -# Helper functions -msg_info() { echo -e "${BLUE}ℹ ${1}${NC}"; } -msg_ok() { echo -e "${GREEN}✔ ${1}${NC}"; } -msg_error() { echo -e "${RED}✖ ${1}${NC}"; } -msg_warn() { echo -e "${YELLOW}⚠ ${1}${NC}"; } - -# Reload environment helper -reload_path() { - export PATH="/usr/local/bin:/usr/local/go/bin:/root/.cargo/bin:/root/.rbenv/bin:/root/.rbenv/shims:/opt/java/bin:$PATH" - # Source profile files if they exist - [ -f "/root/.bashrc" ] && source /root/.bashrc 2>/dev/null || true +# Clean up before test to avoid interactive prompts and locks +cleanup_before_test() { + # Kill any hanging apt processes + killall apt-get apt 2>/dev/null || true + + # Remove apt locks + rm -f /var/lib/dpkg/lock-frontend /var/lib/dpkg/lock /var/cache/apt/archives/lock 2>/dev/null || true + + # Remove existing keyrings to avoid overwrite prompts + rm -f /etc/apt/keyrings/*.gpg 2>/dev/null || true + + # Wait a moment for processes to clean up + sleep 1 +} [ -f "/root/.profile" ] && source /root/.profile 2>/dev/null || true [ -f "/root/.cargo/env" ] && source /root/.cargo/env 2>/dev/null || true } @@ -95,6 +101,9 @@ test_function() { local test_command="$2" local validation_cmd="${3:-}" + # Clean up before starting test + cleanup_before_test + echo -e "\n${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" echo -e "${CYAN}Testing: ${test_name}${NC}" echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" @@ -173,21 +182,14 @@ test_function "Adminer" \ "test -f /usr/share/adminer/latest.php && echo 'Adminer installed'" # ============================================================================== -# TEST 3: LOCAL IP HELPER -# ============================================================================== -test_function "Local IP Helper" \ - "setup_local_ip_helper" \ - "systemctl is-enabled local-ip-helper.service &>/dev/null && echo 'Service enabled'" - -# ============================================================================== -# TEST 4: CLICKHOUSE +# TEST 3: CLICKHOUSE # ============================================================================== test_function "ClickHouse" \ "setup_clickhouse" \ "clickhouse-server --version" # ============================================================================== -# TEST 5: POSTGRESQL +# TEST 4: POSTGRESQL # ============================================================================== test_function "PostgreSQL 17" \ "PG_VERSION=17 setup_postgresql" \ diff --git a/misc/tools.func b/misc/tools.func index dc95a8380..d7f5dda2b 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -551,8 +551,8 @@ setup_deb822_repo() { # Ensure keyring directory exists mkdir -p /etc/apt/keyrings - # Download GPG key - curl -fsSL "$gpg_url" | gpg --dearmor -o "/etc/apt/keyrings/${name}.gpg" + # Download GPG key (with --yes to avoid interactive prompts) + curl -fsSL "$gpg_url" | gpg --dearmor --yes -o "/etc/apt/keyrings/${name}.gpg" # Create deb822 sources file cat </etc/apt/sources.list.d/${name}.sources @@ -2138,7 +2138,7 @@ function setup_mongodb() { # Use standardized repo setup mkdir -p /etc/apt/keyrings - curl -fsSL "https://pgp.mongodb.com/server-${MONGO_VERSION}.asc" | gpg --dearmor -o "/etc/apt/keyrings/mongodb-${MONGO_VERSION}.gpg" + curl -fsSL "https://pgp.mongodb.com/server-${MONGO_VERSION}.asc" | gpg --dearmor --yes -o "/etc/apt/keyrings/mongodb-${MONGO_VERSION}.gpg" cat </etc/apt/sources.list.d/mongodb-org-${MONGO_VERSION}.sources Types: deb From 26ec62beee568ff16830bb45c7ef7ae2bcc60778 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 16 Oct 2025 22:56:28 +0200 Subject: [PATCH 1429/1733] fixes --- misc/test-tools-func.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/misc/test-tools-func.sh b/misc/test-tools-func.sh index a4bed67e4..dd18f5896 100644 --- a/misc/test-tools-func.sh +++ b/misc/test-tools-func.sh @@ -81,13 +81,13 @@ reload_path() { cleanup_before_test() { # Kill any hanging apt processes killall apt-get apt 2>/dev/null || true - + # Remove apt locks rm -f /var/lib/dpkg/lock-frontend /var/lib/dpkg/lock /var/cache/apt/archives/lock 2>/dev/null || true - - # Remove existing keyrings to avoid overwrite prompts + + # Remove existing keyrings to avoid overwrite prompts rm -f /etc/apt/keyrings/*.gpg 2>/dev/null || true - + # Wait a moment for processes to clean up sleep 1 } From 5605449dfdedc04c2d02e2fada9cee11f2a5e004 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 16 Oct 2025 23:00:40 +0200 Subject: [PATCH 1430/1733] missing char --- misc/test-tools-func.sh | 253 ++++++++++++++++++++-------------------- 1 file changed, 126 insertions(+), 127 deletions(-) diff --git a/misc/test-tools-func.sh b/misc/test-tools-func.sh index dd18f5896..0718bbd66 100644 --- a/misc/test-tools-func.sh +++ b/misc/test-tools-func.sh @@ -36,14 +36,14 @@ echo -e "Log file: ${TEST_LOG}\n" # Source tools.func from repository echo -e "${BLUE}► Sourcing tools.func from repository...${NC}" if ! source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func); then - echo -e "${RED}✖ Failed to source tools.func${NC}" - exit 1 + echo -e "${RED}✖ Failed to source tools.func${NC}" + exit 1 fi echo -e "${GREEN}✔ tools.func loaded${NC}\n" # Source core functions if available if curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func &>/dev/null; then - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) || true + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) || true fi # Override STD to show all output for debugging @@ -70,93 +70,92 @@ CL="${CL:-${NC}}" # Reload environment helper reload_path() { - export PATH="/usr/local/bin:/usr/local/go/bin:/root/.cargo/bin:/root/.rbenv/bin:/root/.rbenv/shims:/opt/java/bin:$PATH" - # Source profile files if they exist - [ -f "/root/.bashrc" ] && source /root/.bashrc 2>/dev/null || true - [ -f "/root/.profile" ] && source /root/.profile 2>/dev/null || true - [ -f "/root/.cargo/env" ] && source /root/.cargo/env 2>/dev/null || true + export PATH="/usr/local/bin:/usr/local/go/bin:/root/.cargo/bin:/root/.rbenv/bin:/root/.rbenv/shims:/opt/java/bin:$PATH" + # Source profile files if they exist + [ -f "/root/.bashrc" ] && source /root/.bashrc 2>/dev/null || true + [ -f "/root/.profile" ] && source /root/.profile 2>/dev/null || true + [ -f "/root/.cargo/env" ] && source /root/.cargo/env 2>/dev/null || true } # Clean up before test to avoid interactive prompts and locks cleanup_before_test() { - # Kill any hanging apt processes - killall apt-get apt 2>/dev/null || true + # Kill any hanging apt processes + killall apt-get apt 2>/dev/null || true - # Remove apt locks - rm -f /var/lib/dpkg/lock-frontend /var/lib/dpkg/lock /var/cache/apt/archives/lock 2>/dev/null || true + # Remove apt locks + rm -f /var/lib/dpkg/lock-frontend /var/lib/dpkg/lock /var/cache/apt/archives/lock 2>/dev/null || true - # Remove existing keyrings to avoid overwrite prompts - rm -f /etc/apt/keyrings/*.gpg 2>/dev/null || true + # Remove existing keyrings to avoid overwrite prompts + rm -f /etc/apt/keyrings/*.gpg 2>/dev/null || true - # Wait a moment for processes to clean up - sleep 1 -} - [ -f "/root/.profile" ] && source /root/.profile 2>/dev/null || true - [ -f "/root/.cargo/env" ] && source /root/.cargo/env 2>/dev/null || true + # Wait a moment for processes to clean up + sleep 1 } +[ -f "/root/.profile" ] && source /root/.profile 2>/dev/null || true +[ -f "/root/.cargo/env" ] && source /root/.cargo/env 2>/dev/null || true # Test validation function test_function() { - local test_name="$1" - local test_command="$2" - local validation_cmd="${3:-}" + local test_name="$1" + local test_command="$2" + local validation_cmd="${3:-}" - # Clean up before starting test - cleanup_before_test + # Clean up before starting test + cleanup_before_test - echo -e "\n${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" - echo -e "${CYAN}Testing: ${test_name}${NC}" - echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo -e "\n${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo -e "${CYAN}Testing: ${test_name}${NC}" + echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" - { - echo "=== Test: ${test_name} ===" - echo "Command: ${test_command}" - echo "Started: $(date)" - } | tee -a "$TEST_LOG" + { + echo "=== Test: ${test_name} ===" + echo "Command: ${test_command}" + echo "Started: $(date)" + } | tee -a "$TEST_LOG" - # Execute installation with output visible AND logged - if eval "$test_command" 2>&1 | tee -a "$TEST_LOG"; then - # Reload PATH after installation - reload_path + # Execute installation with output visible AND logged + if eval "$test_command" 2>&1 | tee -a "$TEST_LOG"; then + # Reload PATH after installation + reload_path - if [[ -n "$validation_cmd" ]]; then - local output - if output=$(bash -c "$validation_cmd" 2>&1); then - msg_ok "${test_name} - $(echo "$output" | head -n1)" - ((TESTS_PASSED++)) - else - msg_error "${test_name} - Installation succeeded but validation failed" - { - echo "Validation command: $validation_cmd" - echo "Validation output: $output" - echo "PATH: $PATH" - } | tee -a "$TEST_LOG" - ((TESTS_FAILED++)) - fi - else - msg_ok "${test_name}" - ((TESTS_PASSED++)) - fi - else - msg_error "${test_name} - Installation failed" - echo "Installation failed" | tee -a "$TEST_LOG" + if [[ -n "$validation_cmd" ]]; then + local output + if output=$(bash -c "$validation_cmd" 2>&1); then + msg_ok "${test_name} - $(echo "$output" | head -n1)" + ((TESTS_PASSED++)) + else + msg_error "${test_name} - Installation succeeded but validation failed" + { + echo "Validation command: $validation_cmd" + echo "Validation output: $output" + echo "PATH: $PATH" + } | tee -a "$TEST_LOG" ((TESTS_FAILED++)) + fi + else + msg_ok "${test_name}" + ((TESTS_PASSED++)) fi + else + msg_error "${test_name} - Installation failed" + echo "Installation failed" | tee -a "$TEST_LOG" + ((TESTS_FAILED++)) + fi - echo "Completed: $(date)" | tee -a "$TEST_LOG" - echo "" | tee -a "$TEST_LOG" + echo "Completed: $(date)" | tee -a "$TEST_LOG" + echo "" | tee -a "$TEST_LOG" } # Skip test with reason skip_test() { - local test_name="$1" - local reason="$2" + local test_name="$1" + local reason="$2" - echo -e "\n${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" - echo -e "${CYAN}Testing: ${test_name}${NC}" - echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" - msg_warn "Skipped: ${reason}" - ((TESTS_SKIPPED++)) + echo -e "\n${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + echo -e "${CYAN}Testing: ${test_name}${NC}" + echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" + msg_warn "Skipped: ${reason}" + ((TESTS_SKIPPED++)) } # Update system @@ -171,36 +170,36 @@ apt-get install -y curl wget gpg jq git build-essential ca-certificates &>/dev/n # TEST 1: YQ - YAML Processor # ============================================================================== test_function "YQ" \ - "setup_yq" \ - "yq --version" + "setup_yq" \ + "yq --version" # ============================================================================== # TEST 2: ADMINER - Database Management # ============================================================================== test_function "Adminer" \ - "setup_adminer" \ - "test -f /usr/share/adminer/latest.php && echo 'Adminer installed'" + "setup_adminer" \ + "test -f /usr/share/adminer/latest.php && echo 'Adminer installed'" # ============================================================================== # TEST 3: CLICKHOUSE # ============================================================================== test_function "ClickHouse" \ - "setup_clickhouse" \ - "clickhouse-server --version" + "setup_clickhouse" \ + "clickhouse-server --version" # ============================================================================== # TEST 4: POSTGRESQL # ============================================================================== test_function "PostgreSQL 17" \ - "PG_VERSION=17 setup_postgresql" \ - "psql --version" + "PG_VERSION=17 setup_postgresql" \ + "psql --version" # ============================================================================== # TEST 6: MARIADB # ============================================================================== test_function "MariaDB 11.4" \ - "MARIADB_VERSION=11.4 setup_mariadb" \ - "mariadb --version" + "MARIADB_VERSION=11.4 setup_mariadb" \ + "mariadb --version" # ============================================================================== # TEST 7: MYSQL (Remove MariaDB first) @@ -213,96 +212,96 @@ rm -rf /etc/mysql /var/lib/mysql msg_ok "MariaDB removed" test_function "MySQL 8.0" \ - "MYSQL_VERSION=8.0 setup_mysql" \ - "mysql --version" + "MYSQL_VERSION=8.0 setup_mysql" \ + "mysql --version" # ============================================================================== # TEST 8: MONGODB (Check AVX support) # ============================================================================== if grep -q avx /proc/cpuinfo; then - test_function "MongoDB 8.0" \ - "MONGO_VERSION=8.0 setup_mongodb" \ - "mongod --version" + test_function "MongoDB 8.0" \ + "MONGO_VERSION=8.0 setup_mongodb" \ + "mongod --version" else - skip_test "MongoDB 8.0" "CPU does not support AVX" + skip_test "MongoDB 8.0" "CPU does not support AVX" fi # ============================================================================== # TEST 9: NODE.JS # ============================================================================== test_function "Node.js 22 with modules" \ - "NODE_VERSION=22 NODE_MODULE='yarn,pnpm@10.1.0,pm2' setup_nodejs" \ - "node --version && npm --version && yarn --version && pnpm --version && pm2 --version" + "NODE_VERSION=22 NODE_MODULE='yarn,pnpm@10.1.0,pm2' setup_nodejs" \ + "node --version && npm --version && yarn --version && pnpm --version && pm2 --version" # ============================================================================== # TEST 10: PYTHON (UV) # ============================================================================== test_function "Python 3.12 via uv" \ - "PYTHON_VERSION=3.12 setup_uv" \ - "uv --version" + "PYTHON_VERSION=3.12 setup_uv" \ + "uv --version" # ============================================================================== # TEST 11: PHP # ============================================================================== test_function "PHP 8.3 with FPM" \ - "PHP_VERSION=8.3 PHP_FPM=YES PHP_MODULE='redis,imagick,apcu,zip,mbstring' setup_php" \ - "php --version" + "PHP_VERSION=8.3 PHP_FPM=YES PHP_MODULE='redis,imagick,apcu,zip,mbstring' setup_php" \ + "php --version" # ============================================================================== # TEST 12: COMPOSER # ============================================================================== test_function "Composer" \ - "setup_composer" \ - "composer --version" + "setup_composer" \ + "composer --version" # ============================================================================== # TEST 13: JAVA # ============================================================================== test_function "Java Temurin 21" \ - "JAVA_VERSION=21 setup_java" \ - "java --version" + "JAVA_VERSION=21 setup_java" \ + "java --version" # ============================================================================== # TEST 14: GO # ============================================================================== test_function "Go (latest)" \ - "GO_VERSION=latest setup_go" \ - "go version" + "GO_VERSION=latest setup_go" \ + "go version" # ============================================================================== # TEST 15: RUBY # ============================================================================== test_function "Ruby 3.4.1 with Rails" \ - "RUBY_VERSION=3.4.1 RUBY_INSTALL_RAILS=true setup_ruby" \ - "ruby --version" + "RUBY_VERSION=3.4.1 RUBY_INSTALL_RAILS=true setup_ruby" \ + "ruby --version" # ============================================================================== # TEST 16: RUST # ============================================================================== test_function "Rust (stable)" \ - "RUST_TOOLCHAIN=stable RUST_CRATES='cargo-edit' setup_rust" \ - "source \$HOME/.cargo/env && rustc --version" + "RUST_TOOLCHAIN=stable RUST_CRATES='cargo-edit' setup_rust" \ + "source \$HOME/.cargo/env && rustc --version" # ============================================================================== # TEST 17: GHOSTSCRIPT # ============================================================================== test_function "Ghostscript" \ - "setup_gs" \ - "gs --version" + "setup_gs" \ + "gs --version" # ============================================================================== # TEST 18: IMAGEMAGICK # ============================================================================== test_function "ImageMagick" \ - "setup_imagemagick" \ - "magick --version" + "setup_imagemagick" \ + "magick --version" # ============================================================================== # TEST 19: FFMPEG # ============================================================================== test_function "FFmpeg n7.1.1 (full)" \ - "FFMPEG_VERSION=n7.1.1 FFMPEG_TYPE=full setup_ffmpeg" \ - "ffmpeg -version" + "FFMPEG_VERSION=n7.1.1 FFMPEG_TYPE=full setup_ffmpeg" \ + "ffmpeg -version" # ============================================================================== # FINAL SUMMARY @@ -317,31 +316,31 @@ echo -e "\nDetailed log: ${TEST_LOG}" # Generate summary report { - echo "" - echo "=== FINAL SUMMARY ===" - echo "Tests Passed: ${TESTS_PASSED}" - echo "Tests Failed: ${TESTS_FAILED}" - echo "Tests Skipped: ${TESTS_SKIPPED}" - echo "" - echo "=== Installed Versions ===" - command -v yq &>/dev/null && echo "yq: $(yq --version 2>&1)" - command -v clickhouse-server &>/dev/null && echo "ClickHouse: $(clickhouse-server --version 2>&1 | head -n1)" - command -v psql &>/dev/null && echo "PostgreSQL: $(psql --version)" - command -v mysql &>/dev/null && echo "MySQL: $(mysql --version)" - command -v mongod &>/dev/null && echo "MongoDB: $(mongod --version 2>&1 | head -n1)" - command -v node &>/dev/null && echo "Node.js: $(node --version)" - command -v php &>/dev/null && echo "PHP: $(php --version | head -n1)" - command -v java &>/dev/null && echo "Java: $(java --version 2>&1 | head -n1)" - command -v go &>/dev/null && echo "Go: $(go version)" - command -v ruby &>/dev/null && echo "Ruby: $(ruby --version)" - command -v rustc &>/dev/null && echo "Rust: $(rustc --version)" - command -v ffmpeg &>/dev/null && echo "FFmpeg: $(ffmpeg -version 2>&1 | head -n1)" + echo "" + echo "=== FINAL SUMMARY ===" + echo "Tests Passed: ${TESTS_PASSED}" + echo "Tests Failed: ${TESTS_FAILED}" + echo "Tests Skipped: ${TESTS_SKIPPED}" + echo "" + echo "=== Installed Versions ===" + command -v yq &>/dev/null && echo "yq: $(yq --version 2>&1)" + command -v clickhouse-server &>/dev/null && echo "ClickHouse: $(clickhouse-server --version 2>&1 | head -n1)" + command -v psql &>/dev/null && echo "PostgreSQL: $(psql --version)" + command -v mysql &>/dev/null && echo "MySQL: $(mysql --version)" + command -v mongod &>/dev/null && echo "MongoDB: $(mongod --version 2>&1 | head -n1)" + command -v node &>/dev/null && echo "Node.js: $(node --version)" + command -v php &>/dev/null && echo "PHP: $(php --version | head -n1)" + command -v java &>/dev/null && echo "Java: $(java --version 2>&1 | head -n1)" + command -v go &>/dev/null && echo "Go: $(go version)" + command -v ruby &>/dev/null && echo "Ruby: $(ruby --version)" + command -v rustc &>/dev/null && echo "Rust: $(rustc --version)" + command -v ffmpeg &>/dev/null && echo "FFmpeg: $(ffmpeg -version 2>&1 | head -n1)" } >>"$TEST_LOG" if [ $TESTS_FAILED -eq 0 ]; then - echo -e "\n${GREEN}All tests completed successfully!${NC}" - exit 0 + echo -e "\n${GREEN}All tests completed successfully!${NC}" + exit 0 else - echo -e "\n${RED}Some tests failed. Check the log for details.${NC}" - exit 1 + echo -e "\n${RED}Some tests failed. Check the log for details.${NC}" + exit 1 fi From b3be6d0b91bb095053cb1516cc92c54669a65bdf Mon Sep 17 00:00:00 2001 From: tremor021 Date: Fri, 17 Oct 2025 23:17:38 +0200 Subject: [PATCH 1431/1733] GLPI TEST --- ct/glpi.sh | 47 ++++++++++++ install/glpi-install.sh | 161 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 208 insertions(+) create mode 100644 ct/glpi.sh create mode 100644 install/glpi-install.sh diff --git a/ct/glpi.sh b/ct/glpi.sh new file mode 100644 index 000000000..3ffa7be49 --- /dev/null +++ b/ct/glpi.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Nícolas Pastorello (opastorello) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://www.glpi-project.org/ + +APP="GLPI" +var_tags="${var_tags:-asset-management;foss}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-10}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -d /opt/glpi ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + RELEASE=$(curl -fsSL https://api.github.com/repos/glpi-project/glpi/releases/latest | grep '"tag_name"' | sed -E 's/.*"tag_name": "([^"]+)".*/\1/') + if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then + msg_error "Currently we don't provide an update function for this ${APP}." + else + msg_ok "No update required. ${APP} is already at v${RELEASE}." + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:80${CL}" diff --git a/install/glpi-install.sh b/install/glpi-install.sh new file mode 100644 index 000000000..7751b9e23 --- /dev/null +++ b/install/glpi-install.sh @@ -0,0 +1,161 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Nícolas Pastorello (opastorello) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://www.glpi-project.org/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt-get install -y \ + git \ + apache2 \ + php8.4-{apcu,cli,common,curl,gd,ldap,mysql,xmlrpc,xml,mbstring,bcmath,intl,zip,redis,bz2,soap} \ + php-cas \ + libapache2-mod-php +msg_ok "Installed Dependencies" + +setup_mariadb + +msg_info "Setting up database" +DB_NAME=glpi_db +DB_USER=glpi +DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) +mariadb-tzinfo-to-sql /usr/share/zoneinfo | mariadb mysql +$STD mariadb -u root -e "CREATE DATABASE $DB_NAME;" +$STD mariadb -u root -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';" +$STD mariadb -u root -e "GRANT ALL PRIVILEGES ON $DB_NAME.* TO '$DB_USER'@'localhost';" +$STD mariadb -u root -e "GRANT SELECT ON \`mysql\`.\`time_zone_name\` TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" +{ + echo "GLPI Database Credentials" + echo "Database: $DB_NAME" + echo "Username: $DB_USER" + echo "Password: $DB_PASS" +} >>~/glpi_db.creds +msg_ok "Set up database" + +msg_info "Installing GLPi" +cd /opt +RELEASE=$(curl -fsSL https://api.github.com/repos/glpi-project/glpi/releases/latest | grep '"tag_name"' | sed -E 's/.*"tag_name": "([^"]+)".*/\1/') +curl -fsSL "https://github.com/glpi-project/glpi/releases/download/${RELEASE}/glpi-${RELEASE}.tgz" -o $(basename "https://github.com/glpi-project/glpi/releases/download/${RELEASE}/glpi-${RELEASE}.tgz") +$STD tar -xzvf glpi-${RELEASE}.tgz +cd /opt/glpi +echo "${RELEASE}" >/opt/${APPLICATION}_version.txt +msg_ok "Installed GLPi" + +msg_info "Setting Downstream file" +cat </opt/glpi/inc/downstream.php +/etc/glpi/local_define.php +/etc/apache2/sites-available/glpi.conf + + ServerName localhost + DocumentRoot /opt/glpi/public + + + Require all granted + RewriteEngine On + RewriteCond %{HTTP:Authorization} ^(.+)$ + RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^(.*)$ index.php [QSA,L] + + + ErrorLog \${APACHE_LOG_DIR}/glpi_error.log + CustomLog \${APACHE_LOG_DIR}/glpi_access.log combined + +EOF +$STD a2dissite 000-default.conf +$STD a2enmod rewrite +$STD a2ensite glpi.conf +msg_ok "Setup Service" + +msg_info "Setup Cronjob" +echo "* * * * * php /opt/glpi/front/cron.php" | crontab - +msg_ok "Setup Cronjob" + +msg_info "Update PHP Params" +PHP_VERSION=$(ls /etc/php/ | grep -E '^[0-9]+\.[0-9]+$' | head -n 1) +PHP_INI="/etc/php/$PHP_VERSION/apache2/php.ini" +sed -i 's/^upload_max_filesize = .*/upload_max_filesize = 20M/' $PHP_INI +sed -i 's/^post_max_size = .*/post_max_size = 20M/' $PHP_INI +sed -i 's/^max_execution_time = .*/max_execution_time = 60/' $PHP_INI +sed -i 's/^max_input_vars = .*/max_input_vars = 5000/' $PHP_INI +sed -i 's/^memory_limit = .*/memory_limit = 256M/' $PHP_INI +sed -i 's/^;\?\s*session.cookie_httponly\s*=.*/session.cookie_httponly = On/' $PHP_INI +systemctl restart apache2 +msg_ok "Update PHP Params" + +motd_ssh +customize + +msg_info "Cleaning up" +rm -rf /opt/glpi/install +rm -rf /opt/glpi-${RELEASE}.tgz +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 5a2eff112fc42260e0619870e96c4c1d9d47202f Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 17 Oct 2025 21:17:57 +0000 Subject: [PATCH 1432/1733] Update .app files --- ct/headers/glpi | 6 ++++++ ct/headers/open-archiver | 6 ++++++ ct/headers/openarchiver | 6 ------ 3 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 ct/headers/glpi create mode 100644 ct/headers/open-archiver delete mode 100644 ct/headers/openarchiver diff --git a/ct/headers/glpi b/ct/headers/glpi new file mode 100644 index 000000000..789b62590 --- /dev/null +++ b/ct/headers/glpi @@ -0,0 +1,6 @@ + ________ ____ ____ + / ____/ / / __ \/ _/ + / / __/ / / /_/ // / +/ /_/ / /___/ ____// / +\____/_____/_/ /___/ + diff --git a/ct/headers/open-archiver b/ct/headers/open-archiver new file mode 100644 index 000000000..eaf65ae7b --- /dev/null +++ b/ct/headers/open-archiver @@ -0,0 +1,6 @@ + ____ ___ __ _ + / __ \____ ___ ____ / | __________/ /_ (_) _____ _____ + / / / / __ \/ _ \/ __ \______/ /| | / ___/ ___/ __ \/ / | / / _ \/ ___/ +/ /_/ / /_/ / __/ / / /_____/ ___ |/ / / /__/ / / / /| |/ / __/ / +\____/ .___/\___/_/ /_/ /_/ |_/_/ \___/_/ /_/_/ |___/\___/_/ + /_/ diff --git a/ct/headers/openarchiver b/ct/headers/openarchiver deleted file mode 100644 index cad7937d3..000000000 --- a/ct/headers/openarchiver +++ /dev/null @@ -1,6 +0,0 @@ - ____ ___ __ _ - / __ \____ ___ ____ / | __________/ /_ (_) _____ _____ - / / / / __ \/ _ \/ __ \ / /| | / ___/ ___/ __ \/ / | / / _ \/ ___/ -/ /_/ / /_/ / __/ / / / / ___ |/ / / /__/ / / / /| |/ / __/ / -\____/ .___/\___/_/ /_/ /_/ |_/_/ \___/_/ /_/_/ |___/\___/_/ - /_/ From fed6a8dbc48c943aa0cffc3fd3e57be1387c76f3 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Fri, 17 Oct 2025 23:41:41 +0200 Subject: [PATCH 1433/1733] Remove GLPI test files --- ct/glpi.sh | 47 ------------ install/glpi-install.sh | 161 ---------------------------------------- 2 files changed, 208 deletions(-) delete mode 100644 ct/glpi.sh delete mode 100644 install/glpi-install.sh diff --git a/ct/glpi.sh b/ct/glpi.sh deleted file mode 100644 index 3ffa7be49..000000000 --- a/ct/glpi.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Nícolas Pastorello (opastorello) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://www.glpi-project.org/ - -APP="GLPI" -var_tags="${var_tags:-asset-management;foss}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-10}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - - if [[ ! -d /opt/glpi ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - RELEASE=$(curl -fsSL https://api.github.com/repos/glpi-project/glpi/releases/latest | grep '"tag_name"' | sed -E 's/.*"tag_name": "([^"]+)".*/\1/') - if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then - msg_error "Currently we don't provide an update function for this ${APP}." - else - msg_ok "No update required. ${APP} is already at v${RELEASE}." - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:80${CL}" diff --git a/install/glpi-install.sh b/install/glpi-install.sh deleted file mode 100644 index 7751b9e23..000000000 --- a/install/glpi-install.sh +++ /dev/null @@ -1,161 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Nícolas Pastorello (opastorello) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://www.glpi-project.org/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt-get install -y \ - git \ - apache2 \ - php8.4-{apcu,cli,common,curl,gd,ldap,mysql,xmlrpc,xml,mbstring,bcmath,intl,zip,redis,bz2,soap} \ - php-cas \ - libapache2-mod-php -msg_ok "Installed Dependencies" - -setup_mariadb - -msg_info "Setting up database" -DB_NAME=glpi_db -DB_USER=glpi -DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) -mariadb-tzinfo-to-sql /usr/share/zoneinfo | mariadb mysql -$STD mariadb -u root -e "CREATE DATABASE $DB_NAME;" -$STD mariadb -u root -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';" -$STD mariadb -u root -e "GRANT ALL PRIVILEGES ON $DB_NAME.* TO '$DB_USER'@'localhost';" -$STD mariadb -u root -e "GRANT SELECT ON \`mysql\`.\`time_zone_name\` TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" -{ - echo "GLPI Database Credentials" - echo "Database: $DB_NAME" - echo "Username: $DB_USER" - echo "Password: $DB_PASS" -} >>~/glpi_db.creds -msg_ok "Set up database" - -msg_info "Installing GLPi" -cd /opt -RELEASE=$(curl -fsSL https://api.github.com/repos/glpi-project/glpi/releases/latest | grep '"tag_name"' | sed -E 's/.*"tag_name": "([^"]+)".*/\1/') -curl -fsSL "https://github.com/glpi-project/glpi/releases/download/${RELEASE}/glpi-${RELEASE}.tgz" -o $(basename "https://github.com/glpi-project/glpi/releases/download/${RELEASE}/glpi-${RELEASE}.tgz") -$STD tar -xzvf glpi-${RELEASE}.tgz -cd /opt/glpi -echo "${RELEASE}" >/opt/${APPLICATION}_version.txt -msg_ok "Installed GLPi" - -msg_info "Setting Downstream file" -cat </opt/glpi/inc/downstream.php -/etc/glpi/local_define.php -/etc/apache2/sites-available/glpi.conf - - ServerName localhost - DocumentRoot /opt/glpi/public - - - Require all granted - RewriteEngine On - RewriteCond %{HTTP:Authorization} ^(.+)$ - RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] - RewriteCond %{REQUEST_FILENAME} !-f - RewriteRule ^(.*)$ index.php [QSA,L] - - - ErrorLog \${APACHE_LOG_DIR}/glpi_error.log - CustomLog \${APACHE_LOG_DIR}/glpi_access.log combined - -EOF -$STD a2dissite 000-default.conf -$STD a2enmod rewrite -$STD a2ensite glpi.conf -msg_ok "Setup Service" - -msg_info "Setup Cronjob" -echo "* * * * * php /opt/glpi/front/cron.php" | crontab - -msg_ok "Setup Cronjob" - -msg_info "Update PHP Params" -PHP_VERSION=$(ls /etc/php/ | grep -E '^[0-9]+\.[0-9]+$' | head -n 1) -PHP_INI="/etc/php/$PHP_VERSION/apache2/php.ini" -sed -i 's/^upload_max_filesize = .*/upload_max_filesize = 20M/' $PHP_INI -sed -i 's/^post_max_size = .*/post_max_size = 20M/' $PHP_INI -sed -i 's/^max_execution_time = .*/max_execution_time = 60/' $PHP_INI -sed -i 's/^max_input_vars = .*/max_input_vars = 5000/' $PHP_INI -sed -i 's/^memory_limit = .*/memory_limit = 256M/' $PHP_INI -sed -i 's/^;\?\s*session.cookie_httponly\s*=.*/session.cookie_httponly = On/' $PHP_INI -systemctl restart apache2 -msg_ok "Update PHP Params" - -motd_ssh -customize - -msg_info "Cleaning up" -rm -rf /opt/glpi/install -rm -rf /opt/glpi-${RELEASE}.tgz -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" From acbdf9306f75760292c18ce41a00a9a14d88c2f7 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 17 Oct 2025 21:42:10 +0000 Subject: [PATCH 1434/1733] Update .app files --- ct/headers/glpi | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 ct/headers/glpi diff --git a/ct/headers/glpi b/ct/headers/glpi deleted file mode 100644 index 789b62590..000000000 --- a/ct/headers/glpi +++ /dev/null @@ -1,6 +0,0 @@ - ________ ____ ____ - / ____/ / / __ \/ _/ - / / __/ / / /_/ // / -/ /_/ / /___/ ____// / -\____/_____/_/ /___/ - From 79c05ab52c5756646be595fc8990cec208b3d57d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Sat, 18 Oct 2025 11:47:52 +0200 Subject: [PATCH 1435/1733] nginx.test --- ct/nginxproxymanager.sh | 163 +++++++++++++++++++++++ install/nginxproxymanager-install.sh | 185 +++++++++++++++++++++++++++ 2 files changed, 348 insertions(+) create mode 100644 ct/nginxproxymanager.sh create mode 100644 install/nginxproxymanager-install.sh diff --git a/ct/nginxproxymanager.sh b/ct/nginxproxymanager.sh new file mode 100644 index 000000000..7607e0c8d --- /dev/null +++ b/ct/nginxproxymanager.sh @@ -0,0 +1,163 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://nginxproxymanager.com/ + +APP="Nginx Proxy Manager" +var_tags="${var_tags:-proxy}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-1024}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -f /lib/systemd/system/npm.service ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if ! command -v pnpm &>/dev/null; then + msg_info "Installing pnpm" + #export NODE_OPTIONS=--openssl-legacy-provider + $STD npm install -g pnpm@8.15 + msg_ok "Installed pnpm" + fi + + RELEASE=$(curl -fsSL https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/releases/latest | + grep "tag_name" | + awk '{print substr($2, 3, length($2)-4) }') + + msg_info "Downloading NPM v${RELEASE}" + curl -fsSL "https://codeload.github.com/NginxProxyManager/nginx-proxy-manager/tar.gz/v${RELEASE}" | tar -xz + cd nginx-proxy-manager-"${RELEASE}" || exit + msg_ok "Downloaded NPM v${RELEASE}" + + msg_info "Building Frontend" + ( + sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" backend/package.json + sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" frontend/package.json + cd ./frontend || exit + $STD pnpm install + $STD pnpm upgrade + $STD pnpm run build + ) + msg_ok "Built Frontend" + + msg_info "Stopping Services" + systemctl stop openresty + systemctl stop npm + msg_ok "Stopped Services" + + msg_info "Cleaning Old Files" + rm -rf /app \ + /var/www/html \ + /etc/nginx \ + /var/log/nginx \ + /var/lib/nginx \ + "$STD" /var/cache/nginx + msg_ok "Cleaned Old Files" + + msg_info "Setting up Environment" + ln -sf /usr/bin/python3 /usr/bin/python + ln -sf /usr/bin/certbot /opt/certbot/bin/certbot + ln -sf /usr/local/openresty/nginx/sbin/nginx /usr/sbin/nginx + ln -sf /usr/local/openresty/nginx/ /etc/nginx + sed -i 's+^daemon+#daemon+g' docker/rootfs/etc/nginx/nginx.conf + NGINX_CONFS=$(find "$(pwd)" -type f -name "*.conf") + for NGINX_CONF in $NGINX_CONFS; do + sed -i 's+include conf.d+include /etc/nginx/conf.d+g' "$NGINX_CONF" + done + mkdir -p /var/www/html /etc/nginx/logs + cp -r docker/rootfs/var/www/html/* /var/www/html/ + cp -r docker/rootfs/etc/nginx/* /etc/nginx/ + cp docker/rootfs/etc/letsencrypt.ini /etc/letsencrypt.ini + cp docker/rootfs/etc/logrotate.d/nginx-proxy-manager /etc/logrotate.d/nginx-proxy-manager + ln -sf /etc/nginx/nginx.conf /etc/nginx/conf/nginx.conf + rm -f /etc/nginx/conf.d/dev.conf + mkdir -p /tmp/nginx/body \ + /run/nginx \ + /data/nginx \ + /data/custom_ssl \ + /data/logs \ + /data/access \ + /data/nginx/default_host \ + /data/nginx/default_www \ + /data/nginx/proxy_host \ + /data/nginx/redirection_host \ + /data/nginx/stream \ + /data/nginx/dead_host \ + /data/nginx/temp \ + /var/lib/nginx/cache/public \ + /var/lib/nginx/cache/private \ + /var/cache/nginx/proxy_temp + chmod -R 777 /var/cache/nginx + chown root /tmp/nginx + echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" {print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf);" >/etc/nginx/conf.d/include/resolvers.conf + if [ ! -f /data/nginx/dummycert.pem ] || [ ! -f /data/nginx/dummykey.pem ]; then + $STD openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 -subj "/O=Nginx Proxy Manager/OU=Dummy Certificate/CN=localhost" -keyout /data/nginx/dummykey.pem -out /data/nginx/dummycert.pem + fi + mkdir -p /app/global /app/frontend/images + cp -r frontend/dist/* /app/frontend + cp -r frontend/app-images/* /app/frontend/images + cp -r backend/* /app + cp -r global/* /app/global + $STD python3 -m pip install --no-cache-dir --break-system-packages certbot-dns-cloudflare + msg_ok "Setup Environment" + + msg_info "Initializing Backend" + $STD rm -rf /app/config/default.json + if [ ! -f /app/config/production.json ]; then + cat <<'EOF' >/app/config/production.json +{ + "database": { + "engine": "knex-native", + "knex": { + "client": "sqlite3", + "connection": { + "filename": "/data/database.sqlite" + } + } + } +} +EOF + fi + cd /app || exit + $STD pnpm install + msg_ok "Initialized Backend" + + msg_info "Starting Services" + sed -i 's/user npm/user root/g; s/^pid/#pid/g' /usr/local/openresty/nginx/conf/nginx.conf + sed -i 's/su npm npm/su root root/g' /etc/logrotate.d/nginx-proxy-manager + sed -i 's/include-system-site-packages = false/include-system-site-packages = true/g' /opt/certbot/pyvenv.cfg + systemctl enable -q --now openresty + systemctl enable -q --now npm + msg_ok "Started Services" + + msg_info "Cleaning up" + rm -rf ~/nginx-proxy-manager-* + msg_ok "Cleaned" + + msg_ok "Updated Successfully" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:81${CL}" diff --git a/install/nginxproxymanager-install.sh b/install/nginxproxymanager-install.sh new file mode 100644 index 000000000..5958239de --- /dev/null +++ b/install/nginxproxymanager-install.sh @@ -0,0 +1,185 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://nginxproxymanager.com/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +setup_go + +msg_info "Installing Dependencies" +$STD apt update +$STD apt -y install \ + ca-certificates \ + apache2-utils \ + logrotate \ + build-essential \ + git +msg_ok "Installed Dependencies" + +msg_info "Installing Python Dependencies" +$STD apt install -y \ + python3 \ + python3-dev \ + python3-pip \ + python3-venv \ + python3-cffi \ + python3-certbot \ + python3-certbot-dns-cloudflare +$STD pip3 install --break-system-packages certbot-dns-multi +msg_ok "Installed Python Dependencies" + +VERSION="$(awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release)" + +msg_info "Installing Openresty" +curl -fsSL "https://openresty.org/package/pubkey.gpg" | gpg --dearmor -o /etc/apt/trusted.gpg.d/openresty-archive-keyring.gpg +case "$VERSION" in +trixie) + echo -e "deb http://openresty.org/package/debian bookworm openresty" >/etc/apt/sources.list.d/openresty.list + ;; +*) + echo -e "deb http://openresty.org/package/debian $VERSION openresty" >/etc/apt/sources.list.d/openresty.list + ;; +esac +$STD apt update +$STD apt -y install openresty +msg_ok "Installed Openresty" + +NODE_VERSION="22" NODE_MODULE="pnpm@latest" setup_nodejs + +RELEASE=$(curl -fsSL https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/releases/latest | + grep "tag_name" | + awk '{print substr($2, 3, length($2)-4) }') + +msg_info "Downloading Nginx Proxy Manager v${RELEASE}" +curl -fsSL "https://codeload.github.com/NginxProxyManager/nginx-proxy-manager/tar.gz/v${RELEASE}" | tar -xz +cd ./nginx-proxy-manager-"${RELEASE}" +msg_ok "Downloaded Nginx Proxy Manager v${RELEASE}" + +msg_info "Setting up Environment" +ln -sf /usr/bin/python3 /usr/bin/python +ln -sf /usr/bin/certbot /usr/local/bin/certbot +ln -sf /usr/local/openresty/nginx/sbin/nginx /usr/sbin/nginx +ln -sf /usr/local/openresty/nginx/ /etc/nginx +sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" backend/package.json +sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" frontend/package.json +sed -i 's+^daemon+#daemon+g' docker/rootfs/etc/nginx/nginx.conf +NGINX_CONFS=$(find "$(pwd)" -type f -name "*.conf") +for NGINX_CONF in $NGINX_CONFS; do + sed -i 's+include conf.d+include /etc/nginx/conf.d+g' "$NGINX_CONF" +done + +mkdir -p /var/www/html /etc/nginx/logs +cp -r docker/rootfs/var/www/html/* /var/www/html/ +cp -r docker/rootfs/etc/nginx/* /etc/nginx/ +cp docker/rootfs/etc/letsencrypt.ini /etc/letsencrypt.ini +cp docker/rootfs/etc/logrotate.d/nginx-proxy-manager /etc/logrotate.d/nginx-proxy-manager +ln -sf /etc/nginx/nginx.conf /etc/nginx/conf/nginx.conf +rm -f /etc/nginx/conf.d/dev.conf + +mkdir -p /tmp/nginx/body \ + /run/nginx \ + /data/nginx \ + /data/custom_ssl \ + /data/logs \ + /data/access \ + /data/nginx/default_host \ + /data/nginx/default_www \ + /data/nginx/proxy_host \ + /data/nginx/redirection_host \ + /data/nginx/stream \ + /data/nginx/dead_host \ + /data/nginx/temp \ + /var/lib/nginx/cache/public \ + /var/lib/nginx/cache/private \ + /var/cache/nginx/proxy_temp + +chmod -R 777 /var/cache/nginx +chown root /tmp/nginx + +echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" {print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf);" >/etc/nginx/conf.d/include/resolvers.conf + +if [ ! -f /data/nginx/dummycert.pem ] || [ ! -f /data/nginx/dummykey.pem ]; then + openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 -subj "/O=Nginx Proxy Manager/OU=Dummy Certificate/CN=localhost" -keyout /data/nginx/dummykey.pem -out /data/nginx/dummycert.pem &>/dev/null +fi + +mkdir -p /app/global /app/frontend/images +cp -r backend/* /app +cp -r global/* /app/global +msg_ok "Set up Environment" + +msg_info "Building Frontend" +cd ./frontend +$STD pnpm install +$STD pnpm upgrade +$STD pnpm run build +cp -r dist/* /app/frontend +cp -r app-images/* /app/frontend/images +msg_ok "Built Frontend" + +msg_info "Initializing Backend" +rm -rf /app/config/default.json +if [ ! -f /app/config/production.json ]; then + cat <<'EOF' >/app/config/production.json +{ + "database": { + "engine": "knex-native", + "knex": { + "client": "sqlite3", + "connection": { + "filename": "/data/database.sqlite" + } + } + } +} +EOF +fi +cd /app +$STD pnpm install +msg_ok "Initialized Backend" + +msg_info "Creating Service" +cat <<'EOF' >/lib/systemd/system/npm.service +[Unit] +Description=Nginx Proxy Manager +After=network.target +Wants=openresty.service + +[Service] +Type=simple +Environment=NODE_ENV=production +ExecStartPre=-mkdir -p /tmp/nginx/body /data/letsencrypt-acme-challenge +ExecStart=/usr/bin/node index.js --abort_on_uncaught_exception --max_old_space_size=250 +WorkingDirectory=/app +Restart=on-failure + +[Install] +WantedBy=multi-user.target +EOF +msg_ok "Created Service" + +motd_ssh +customize + +msg_info "Starting Services" +sed -i 's/user npm/user root/g; s/^pid/#pid/g' /usr/local/openresty/nginx/conf/nginx.conf +sed -r -i 's/^([[:space:]]*)su npm npm/\1#su npm npm/g;' /etc/logrotate.d/nginx-proxy-manager +systemctl enable -q --now openresty +systemctl enable -q --now npm +msg_ok "Started Services" + +msg_info "Cleaning up" +rm -rf ../nginx-proxy-manager-* +systemctl restart openresty +$STD apt -y autoremove +$STD apt -y autoclean +$STD apt -y clean +msg_ok "Cleaned" From c3b3020025d45ca6a437c6cf5daecb01f1b71108 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Sat, 18 Oct 2025 11:48:12 +0200 Subject: [PATCH 1436/1733] fix certbot --- install/nginxproxymanager-install.sh | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/install/nginxproxymanager-install.sh b/install/nginxproxymanager-install.sh index 5958239de..94a1d6d52 100644 --- a/install/nginxproxymanager-install.sh +++ b/install/nginxproxymanager-install.sh @@ -13,8 +13,6 @@ setting_up_container network_check update_os -setup_go - msg_info "Installing Dependencies" $STD apt update $STD apt -y install \ @@ -34,7 +32,6 @@ $STD apt install -y \ python3-cffi \ python3-certbot \ python3-certbot-dns-cloudflare -$STD pip3 install --break-system-packages certbot-dns-multi msg_ok "Installed Python Dependencies" VERSION="$(awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release)" @@ -53,7 +50,7 @@ $STD apt update $STD apt -y install openresty msg_ok "Installed Openresty" -NODE_VERSION="22" NODE_MODULE="pnpm@latest" setup_nodejs +NODE_VERSION="20" NODE_MODULE="pnpm@latest" setup_nodejs RELEASE=$(curl -fsSL https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/releases/latest | grep "tag_name" | From 16cff16207793bedeb6c8f5d68f6b2f1299a15c8 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Sat, 18 Oct 2025 12:01:46 +0200 Subject: [PATCH 1437/1733] var. bugfixes --- ct/nginxproxymanager.sh | 18 +++++++++--------- install/nginxproxymanager-install.sh | 11 ++++++----- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/ct/nginxproxymanager.sh b/ct/nginxproxymanager.sh index 7607e0c8d..067092126 100644 --- a/ct/nginxproxymanager.sh +++ b/ct/nginxproxymanager.sh @@ -28,13 +28,14 @@ function update_script() { exit fi - if ! command -v pnpm &>/dev/null; then - msg_info "Installing pnpm" - #export NODE_OPTIONS=--openssl-legacy-provider - $STD npm install -g pnpm@8.15 - msg_ok "Installed pnpm" + if ! command -v yarn &>/dev/null; then + msg_info "Installing Yarn" + $STD npm install -g yarn + msg_ok "Installed Yarn" fi + export NODE_OPTIONS="--openssl-legacy-provider" + RELEASE=$(curl -fsSL https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') @@ -49,9 +50,8 @@ function update_script() { sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" backend/package.json sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" frontend/package.json cd ./frontend || exit - $STD pnpm install - $STD pnpm upgrade - $STD pnpm run build + $STD yarn install --network-timeout 600000 + $STD yarn build ) msg_ok "Built Frontend" @@ -134,7 +134,7 @@ function update_script() { EOF fi cd /app || exit - $STD pnpm install + $STD yarn install --network-timeout 600000 msg_ok "Initialized Backend" msg_info "Starting Services" diff --git a/install/nginxproxymanager-install.sh b/install/nginxproxymanager-install.sh index 94a1d6d52..fc7662369 100644 --- a/install/nginxproxymanager-install.sh +++ b/install/nginxproxymanager-install.sh @@ -50,7 +50,7 @@ $STD apt update $STD apt -y install openresty msg_ok "Installed Openresty" -NODE_VERSION="20" NODE_MODULE="pnpm@latest" setup_nodejs +NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs RELEASE=$(curl -fsSL https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/releases/latest | grep "tag_name" | @@ -115,9 +115,9 @@ msg_ok "Set up Environment" msg_info "Building Frontend" cd ./frontend -$STD pnpm install -$STD pnpm upgrade -$STD pnpm run build +export NODE_OPTIONS="--openssl-legacy-provider" +$STD yarn install --network-timeout 600000 +$STD yarn build cp -r dist/* /app/frontend cp -r app-images/* /app/frontend/images msg_ok "Built Frontend" @@ -140,7 +140,8 @@ if [ ! -f /app/config/production.json ]; then EOF fi cd /app -$STD pnpm install +export NODE_OPTIONS="--openssl-legacy-provider" +$STD yarn install --network-timeout 600000 msg_ok "Initialized Backend" msg_info "Creating Service" From 7406afb43bd43a637038c66d910397c8d74fa8ff Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sat, 18 Oct 2025 10:02:15 +0000 Subject: [PATCH 1438/1733] Update .app files --- ct/headers/nginxproxymanager | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/nginxproxymanager diff --git a/ct/headers/nginxproxymanager b/ct/headers/nginxproxymanager new file mode 100644 index 000000000..d68d0c9d8 --- /dev/null +++ b/ct/headers/nginxproxymanager @@ -0,0 +1,6 @@ + _ __ _ ____ __ ___ + / | / /___ _(_)___ _ __ / __ \_________ _ ____ __ / |/ /___ _____ ____ _____ ____ _____ + / |/ / __ `/ / __ \| |/_/ / /_/ / ___/ __ \| |/_/ / / / / /|_/ / __ `/ __ \/ __ `/ __ `/ _ \/ ___/ + / /| / /_/ / / / / /> < / ____/ / / /_/ /> Date: Sat, 18 Oct 2025 12:06:20 +0200 Subject: [PATCH 1439/1733] fix open archiver slug --- frontend/public/json/open-archiver.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/public/json/open-archiver.json b/frontend/public/json/open-archiver.json index 66d1f8091..b203d38c1 100644 --- a/frontend/public/json/open-archiver.json +++ b/frontend/public/json/open-archiver.json @@ -1,6 +1,6 @@ { - "name": "Open Archiver", - "slug": "openarchiver", + "name": "Open-Archiver", + "slug": "open-archiver", "categories": [ 7 ], From 55d0596b5358971b482397485a8d0168decd4bde Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Sat, 18 Oct 2025 12:12:41 +0200 Subject: [PATCH 1440/1733] fix yarn --- ct/nginxproxymanager.sh | 5 +++++ install/nginxproxymanager-install.sh | 3 +++ 2 files changed, 8 insertions(+) diff --git a/ct/nginxproxymanager.sh b/ct/nginxproxymanager.sh index 067092126..98248394d 100644 --- a/ct/nginxproxymanager.sh +++ b/ct/nginxproxymanager.sh @@ -50,6 +50,11 @@ function update_script() { sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" backend/package.json sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" frontend/package.json cd ./frontend || exit + # Remove yarn.lock to force fresh dependency resolution + rm -f yarn.lock + # Replace node-sass with sass (Dart Sass) for Node.js compatibility + $STD yarn remove node-sass 2>/dev/null || true + $STD yarn add -D sass $STD yarn install --network-timeout 600000 $STD yarn build ) diff --git a/install/nginxproxymanager-install.sh b/install/nginxproxymanager-install.sh index fc7662369..f470638c2 100644 --- a/install/nginxproxymanager-install.sh +++ b/install/nginxproxymanager-install.sh @@ -116,6 +116,9 @@ msg_ok "Set up Environment" msg_info "Building Frontend" cd ./frontend export NODE_OPTIONS="--openssl-legacy-provider" +rm -f yarn.lock +$STD yarn remove node-sass 2>/dev/null || true +$STD yarn add -D sass $STD yarn install --network-timeout 600000 $STD yarn build cp -r dist/* /app/frontend From e83360d2af56ffa01a105cb58c0334e9f5cfb6d6 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Sat, 18 Oct 2025 12:18:04 +0200 Subject: [PATCH 1441/1733] test --- ct/nginxproxymanager.sh | 8 +++----- install/nginxproxymanager-install.sh | 6 +++--- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/ct/nginxproxymanager.sh b/ct/nginxproxymanager.sh index 98248394d..2c4259d2a 100644 --- a/ct/nginxproxymanager.sh +++ b/ct/nginxproxymanager.sh @@ -50,12 +50,10 @@ function update_script() { sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" backend/package.json sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" frontend/package.json cd ./frontend || exit - # Remove yarn.lock to force fresh dependency resolution - rm -f yarn.lock - # Replace node-sass with sass (Dart Sass) for Node.js compatibility - $STD yarn remove node-sass 2>/dev/null || true - $STD yarn add -D sass + # First install to generate yarn.lock, then swap node-sass for sass $STD yarn install --network-timeout 600000 + $STD yarn remove node-sass + $STD yarn add -D sass $STD yarn build ) msg_ok "Built Frontend" diff --git a/install/nginxproxymanager-install.sh b/install/nginxproxymanager-install.sh index f470638c2..a40297e03 100644 --- a/install/nginxproxymanager-install.sh +++ b/install/nginxproxymanager-install.sh @@ -116,10 +116,10 @@ msg_ok "Set up Environment" msg_info "Building Frontend" cd ./frontend export NODE_OPTIONS="--openssl-legacy-provider" -rm -f yarn.lock -$STD yarn remove node-sass 2>/dev/null || true -$STD yarn add -D sass +# First install to generate yarn.lock, then swap node-sass for sass $STD yarn install --network-timeout 600000 +$STD yarn remove node-sass +$STD yarn add -D sass $STD yarn build cp -r dist/* /app/frontend cp -r app-images/* /app/frontend/images From a1719cf5f7bb56d05fdd5e84290160442eae1aa8 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Sat, 18 Oct 2025 12:29:01 +0200 Subject: [PATCH 1442/1733] test --- ct/nginxproxymanager.sh | 5 ++--- install/nginxproxymanager-install.sh | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/ct/nginxproxymanager.sh b/ct/nginxproxymanager.sh index 2c4259d2a..073bd0a5f 100644 --- a/ct/nginxproxymanager.sh +++ b/ct/nginxproxymanager.sh @@ -50,10 +50,9 @@ function update_script() { sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" backend/package.json sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" frontend/package.json cd ./frontend || exit - # First install to generate yarn.lock, then swap node-sass for sass + # Replace node-sass with sass in package.json before installation + sed -i 's/"node-sass".*$/"sass": "^1.92.1",/g' package.json $STD yarn install --network-timeout 600000 - $STD yarn remove node-sass - $STD yarn add -D sass $STD yarn build ) msg_ok "Built Frontend" diff --git a/install/nginxproxymanager-install.sh b/install/nginxproxymanager-install.sh index a40297e03..91060eb4b 100644 --- a/install/nginxproxymanager-install.sh +++ b/install/nginxproxymanager-install.sh @@ -116,10 +116,9 @@ msg_ok "Set up Environment" msg_info "Building Frontend" cd ./frontend export NODE_OPTIONS="--openssl-legacy-provider" -# First install to generate yarn.lock, then swap node-sass for sass +# Replace node-sass with sass in package.json before installation +sed -i 's/"node-sass".*$/"sass": "^1.92.1",/g' package.json $STD yarn install --network-timeout 600000 -$STD yarn remove node-sass -$STD yarn add -D sass $STD yarn build cp -r dist/* /app/frontend cp -r app-images/* /app/frontend/images From 41776eb78ad660916975541cdc8cdfa1ad450590 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Sat, 18 Oct 2025 12:38:39 +0200 Subject: [PATCH 1443/1733] fix certbot --- ct/nginxproxymanager.sh | 6 +++++- install/nginxproxymanager-install.sh | 12 ++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/ct/nginxproxymanager.sh b/ct/nginxproxymanager.sh index 073bd0a5f..6992c5273 100644 --- a/ct/nginxproxymanager.sh +++ b/ct/nginxproxymanager.sh @@ -115,7 +115,11 @@ function update_script() { cp -r frontend/app-images/* /app/frontend/images cp -r backend/* /app cp -r global/* /app/global - $STD python3 -m pip install --no-cache-dir --break-system-packages certbot-dns-cloudflare + + # Update Certbot and plugins in virtual environment + if [ -d /opt/certbot ]; then + $STD /opt/certbot/bin/pip install --upgrade certbot certbot-dns-cloudflare + fi msg_ok "Setup Environment" msg_info "Initializing Backend" diff --git a/install/nginxproxymanager-install.sh b/install/nginxproxymanager-install.sh index 91060eb4b..50f5a0315 100644 --- a/install/nginxproxymanager-install.sh +++ b/install/nginxproxymanager-install.sh @@ -29,11 +29,16 @@ $STD apt install -y \ python3-dev \ python3-pip \ python3-venv \ - python3-cffi \ - python3-certbot \ - python3-certbot-dns-cloudflare + python3-cffi msg_ok "Installed Python Dependencies" +msg_info "Setting up Certbot" +$STD python3 -m venv /opt/certbot +$STD /opt/certbot/bin/pip install --upgrade pip +$STD /opt/certbot/bin/pip install certbot certbot-dns-cloudflare +ln -sf /opt/certbot/bin/certbot /usr/local/bin/certbot +msg_ok "Set up Certbot" + VERSION="$(awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release)" msg_info "Installing Openresty" @@ -63,7 +68,6 @@ msg_ok "Downloaded Nginx Proxy Manager v${RELEASE}" msg_info "Setting up Environment" ln -sf /usr/bin/python3 /usr/bin/python -ln -sf /usr/bin/certbot /usr/local/bin/certbot ln -sf /usr/local/openresty/nginx/sbin/nginx /usr/sbin/nginx ln -sf /usr/local/openresty/nginx/ /etc/nginx sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" backend/package.json From 11250ce7d925cef28a5728ef4dc5503c5c308fd3 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Sat, 18 Oct 2025 12:38:42 +0200 Subject: [PATCH 1444/1733] fix certbot --- ct/nginxproxymanager.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/nginxproxymanager.sh b/ct/nginxproxymanager.sh index 6992c5273..96a107480 100644 --- a/ct/nginxproxymanager.sh +++ b/ct/nginxproxymanager.sh @@ -115,7 +115,7 @@ function update_script() { cp -r frontend/app-images/* /app/frontend/images cp -r backend/* /app cp -r global/* /app/global - + # Update Certbot and plugins in virtual environment if [ -d /opt/certbot ]; then $STD /opt/certbot/bin/pip install --upgrade certbot certbot-dns-cloudflare From d36a2595cdd437aad8a40cf2c6e93aad8b03d45e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Sat, 18 Oct 2025 12:40:40 +0200 Subject: [PATCH 1445/1733] fix wheel --- ct/nginxproxymanager.sh | 1 + install/nginxproxymanager-install.sh | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ct/nginxproxymanager.sh b/ct/nginxproxymanager.sh index 96a107480..ab4cb2ffd 100644 --- a/ct/nginxproxymanager.sh +++ b/ct/nginxproxymanager.sh @@ -118,6 +118,7 @@ function update_script() { # Update Certbot and plugins in virtual environment if [ -d /opt/certbot ]; then + $STD /opt/certbot/bin/pip install --upgrade pip setuptools wheel $STD /opt/certbot/bin/pip install --upgrade certbot certbot-dns-cloudflare fi msg_ok "Setup Environment" diff --git a/install/nginxproxymanager-install.sh b/install/nginxproxymanager-install.sh index 50f5a0315..664878130 100644 --- a/install/nginxproxymanager-install.sh +++ b/install/nginxproxymanager-install.sh @@ -34,7 +34,7 @@ msg_ok "Installed Python Dependencies" msg_info "Setting up Certbot" $STD python3 -m venv /opt/certbot -$STD /opt/certbot/bin/pip install --upgrade pip +$STD /opt/certbot/bin/pip install --upgrade pip setuptools wheel $STD /opt/certbot/bin/pip install certbot certbot-dns-cloudflare ln -sf /opt/certbot/bin/certbot /usr/local/bin/certbot msg_ok "Set up Certbot" From 5a68d7a708c5c090de9304e962fdd1497d37c284 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Sat, 18 Oct 2025 12:52:41 +0200 Subject: [PATCH 1446/1733] fix update --- ct/nginxproxymanager.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ct/nginxproxymanager.sh b/ct/nginxproxymanager.sh index ab4cb2ffd..82eef7146 100644 --- a/ct/nginxproxymanager.sh +++ b/ct/nginxproxymanager.sh @@ -73,7 +73,7 @@ function update_script() { msg_info "Setting up Environment" ln -sf /usr/bin/python3 /usr/bin/python - ln -sf /usr/bin/certbot /opt/certbot/bin/certbot + ln -sf /opt/certbot/bin/certbot /usr/local/bin/certbot ln -sf /usr/local/openresty/nginx/sbin/nginx /usr/sbin/nginx ln -sf /usr/local/openresty/nginx/ /etc/nginx sed -i 's+^daemon+#daemon+g' docker/rootfs/etc/nginx/nginx.conf @@ -141,6 +141,7 @@ function update_script() { EOF fi cd /app || exit + export NODE_OPTIONS="--openssl-legacy-provider" $STD yarn install --network-timeout 600000 msg_ok "Initialized Backend" From faf3bc57bd9790993f6ee52e1eb7119a4f66dab6 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Sun, 19 Oct 2025 13:01:57 +0200 Subject: [PATCH 1447/1733] test --- misc/build.func | 599 +++++++++++++++++++----------------------------- 1 file changed, 234 insertions(+), 365 deletions(-) diff --git a/misc/build.func b/misc/build.func index c24798689..24bb790f2 100644 --- a/misc/build.func +++ b/misc/build.func @@ -220,7 +220,6 @@ get_current_ip() { echo "$CURRENT_IP" } - # ------------------------------------------------------------------------------ # update_motd_ip() # @@ -626,7 +625,7 @@ advanced_settings() { BRIDGE_MENU_OPTIONS+=("$bridge" " ") fi fi - done <<< "$BRIDGES" + done <<<"$BRIDGES" BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge: " 18 55 6 "${BRIDGE_MENU_OPTIONS[@]}" 3>&1 1>&2 2>&3) if [[ -z "$BRG" ]]; then @@ -859,92 +858,9 @@ advanced_settings() { exit_script fi - # --- SSH key provisioning (one dialog) --- - SSH_KEYS_FILE="$(mktemp)" - : >"$SSH_KEYS_FILE" - - IFS=$'\0' read -r -d '' -a _def_files < <(ssh_discover_default_files && printf '\0') - ssh_build_choices_from_files "${_def_files[@]}" - DEF_KEYS_COUNT="$COUNT" - - if [[ "$DEF_KEYS_COUNT" -gt 0 ]]; then - SSH_KEY_MODE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ - "Provision SSH keys for root:" 14 72 4 \ - "found" "Select from detected keys (${DEF_KEYS_COUNT})" \ - "manual" "Paste a single public key" \ - "folder" "Scan another folder (path or glob)" \ - "none" "No keys" 3>&1 1>&2 2>&3) || exit_script - else - SSH_KEY_MODE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ - "No host keys detected; choose manual/none:" 12 72 2 \ - "manual" "Paste a single public key" \ - "none" "No keys" 3>&1 1>&2 2>&3) || exit_script - fi - - case "$SSH_KEY_MODE" in - found) - SEL=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT HOST KEYS" \ - --checklist "Select one or more keys to import:" 20 140 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script - for tag in $SEL; do - tag="${tag%\"}" - tag="${tag#\"}" - line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) - [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" - done - ;; - manual) - SSH_AUTHORIZED_KEY="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --inputbox "Paste one SSH public key line (ssh-ed25519/ssh-rsa/...)" 10 72 --title "SSH Public Key" 3>&1 1>&2 2>&3)" - [[ -n "$SSH_AUTHORIZED_KEY" ]] && printf '%s\n' "$SSH_AUTHORIZED_KEY" >>"$SSH_KEYS_FILE" - ;; - folder) - GLOB_PATH="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --inputbox "Enter a folder or glob to scan (e.g. /root/.ssh/*.pub)" 10 72 --title "Scan Folder/Glob" 3>&1 1>&2 2>&3)" - if [[ -n "$GLOB_PATH" ]]; then - shopt -s nullglob - read -r -a _scan_files <<<"$GLOB_PATH" - shopt -u nullglob - if [[ "${#_scan_files[@]}" -gt 0 ]]; then - ssh_build_choices_from_files "${_scan_files[@]}" - if [[ "$COUNT" -gt 0 ]]; then - SEL=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT FOLDER KEYS" \ - --checklist "Select key(s) to import:" 20 78 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script - for tag in $SEL; do - tag="${tag%\"}" - tag="${tag#\"}" - line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) - [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" - done - else - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "No keys found in: $GLOB_PATH" 8 60 - fi - else - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "Path/glob returned no files." 8 60 - fi - fi - ;; - none) : ;; - esac - - # Dedupe + clean EOF - if [[ -s "$SSH_KEYS_FILE" ]]; then - sort -u -o "$SSH_KEYS_FILE" "$SSH_KEYS_FILE" - printf '\n' >>"$SSH_KEYS_FILE" - fi - - # SSH activate, if keys found or password set - if [[ -s "$SSH_KEYS_FILE" || "$PW" == -password* ]]; then - if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable root SSH access?" 10 58); then - SSH="yes" - else - SSH="no" - fi - else - SSH="no" - fi - echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}" - + configure_ssh_settings export SSH_KEYS_FILE + echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}" if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "FUSE Support" --yesno "Enable FUSE support?\nRequired for tools like rclone, mergerfs, AppImage, etc." 10 58); then ENABLE_FUSE="yes" else @@ -1552,32 +1468,11 @@ ensure_storage_selection_for_vars_file() { return 0 fi - # --- Erstmalige Auswahl: beide Abfragen --- - select_storage template - local tpl_sel="$STORAGE_RESULT" + # --- Erstmalige Auswahl: beide Abfragen (nutze existierende Helper) --- + choose_and_set_storage_for_file "$vf" template + choose_and_set_storage_for_file "$vf" container - select_storage container - local ct_sel="$STORAGE_RESULT" - - # --- Zusammenfassung + Nachfrage --- - if whiptail --backtitle "Community Scripts" --title "Default Storage" \ - --yesno "Template-Storage --> $tpl_sel\nContainer-Storage --> $ct_sel\n\nSave as global defaults?" \ - 12 70; then - sed -i '/^[#[:space:]]*var_template_storage=/d' "$vf" - sed -i '/^[#[:space:]]*var_container_storage=/d' "$vf" - echo "var_template_storage=$tpl_sel" >>"$vf" - echo "var_container_storage=$ct_sel" >>"$vf" - else - sed -i '/^[#[:space:]]*var_template_storage=/d' "$vf" - sed -i '/^[#[:space:]]*var_container_storage=/d' "$vf" - echo "# var_template_storage=$tpl_sel" >>"$vf" - echo "# var_container_storage=$ct_sel" >>"$vf" - fi - - TEMPLATE_STORAGE="$tpl_sel" - CONTAINER_STORAGE="$ct_sel" - msg_ok "Using Template-Storage → $tpl_sel" - msg_ok "Using Container-Storage → $ct_sel" + msg_ok "Storage configuration saved to $(basename "$vf")" } diagnostics_menu() { @@ -1602,6 +1497,15 @@ diagnostics_menu() { fi } +ensure_global_default_vars_file() { + local vars_path="/usr/local/community-scripts/default.vars" + if [[ ! -f "$vars_path" ]]; then + mkdir -p "$(dirname "$vars_path")" + touch "$vars_path" + fi + echo "$vars_path" +} + # ------------------------------------------------------------------------------ # install_script() # @@ -1633,19 +1537,26 @@ install_script() { CHOICE="${mode:-${1:-}}" # If no CLI argument → show whiptail menu - if [ -z "$CHOICE" ]; then - local menu_items=( - "1" "Default Install" - "2" "Advanced Install" - "3" "My Defaults" - ) + # Build menu dynamically based on available options + local appdefaults_option="" + local settings_option="" + local menu_items=( + "1" "Default Install" + "2" "Advanced Install" + "3" "My Defaults" + ) - if [ -f "$(get_app_defaults_path)" ]; then - menu_items+=("4" "App Defaults for ${APP}") - menu_items+=("5" "Settings") - else - menu_items+=("4" "Settings") - fi + if [ -f "$(get_app_defaults_path)" ]; then + appdefaults_option="4" + menu_items+=("4" "App Defaults for ${APP}") + settings_option="5" + menu_items+=("5" "Settings") + else + settings_option="4" + menu_items+=("4" "Settings") + fi + + if [ -z "$CHOICE" ]; then TMP_CHOICE=$(whiptail \ --backtitle "Proxmox VE Helper Scripts" \ @@ -1660,7 +1571,12 @@ install_script() { CHOICE="$TMP_CHOICE" fi + APPDEFAULTS_OPTION="$appdefaults_option" + SETTINGS_OPTION="$settings_option" + # --- Main case --- + local defaults_target="" + local run_maybe_offer="no" case "$CHOICE" in 1 | default | DEFAULT) header_info @@ -1669,12 +1585,7 @@ install_script() { METHOD="default" base_settings "$VERBOSE" echo_default - [[ -f /usr/local/community-scripts/default.vars ]] || { - mkdir -p /usr/local/community-scripts - touch /usr/local/community-scripts/default.vars - } - ensure_storage_selection_for_vars_file "/usr/local/community-scripts/default.vars" - + defaults_target="$(ensure_global_default_vars_file)" ;; 2 | advanced | ADVANCED) header_info @@ -1682,22 +1593,17 @@ install_script() { METHOD="advanced" base_settings advanced_settings - [[ -f /usr/local/community-scripts/default.vars ]] || { - mkdir -p /usr/local/community-scripts - touch /usr/local/community-scripts/default.vars - } - ensure_storage_selection_for_vars_file "/usr/local/community-scripts/default.vars" - - maybe_offer_save_app_defaults + defaults_target="$(ensure_global_default_vars_file)" + run_maybe_offer="yes" ;; 3 | mydefaults | MYDEFAULTS) default_var_settings || { msg_error "Failed to apply default.vars" exit 1 } - ensure_storage_selection_for_vars_file "/usr/local/community-scripts/default.vars" + defaults_target="/usr/local/community-scripts/default.vars" ;; - 4 | appdefaults | APPDEFAULTS) + "$APPDEFAULTS_OPTION" | appdefaults | APPDEFAULTS) if [ -f "$(get_app_defaults_path)" ]; then header_info echo -e "${DEFAULT}${BOLD}${BL}Using App Defaults for ${APP} on node $PVEHOST_NAME${CL}" @@ -1705,41 +1611,41 @@ install_script() { base_settings _load_vars_file "$(get_app_defaults_path)" echo_default - ensure_storage_selection_for_vars_file "$(get_app_defaults_path)" + defaults_target="$(get_app_defaults_path)" else msg_error "No App Defaults available for ${APP}" exit 1 fi ;; - 5 | settings | SETTINGS) + "$SETTINGS_OPTION" | settings | SETTINGS) settings_menu + defaults_target="" ;; *) echo -e "${CROSS}${RD}Invalid option: $CHOICE${CL}" exit 1 ;; esac + + if [[ -n "$defaults_target" ]]; then + ensure_storage_selection_for_vars_file "$defaults_target" + fi + + if [[ "$run_maybe_offer" == "yes" ]]; then + maybe_offer_save_app_defaults + fi } edit_default_storage() { local vf="/usr/local/community-scripts/default.vars" - # make sure file exists + # Ensure file exists if [[ ! -f "$vf" ]]; then - # still create mkdir -p "$(dirname "$vf")" touch "$vf" - - if select_storage template; then - echo "var_template_storage=$STORAGE_RESULT" >>"$vf" - fi - - if select_storage container; then - echo "var_container_storage=$STORAGE_RESULT" >>"$vf" - fi fi - # reuse the same Whiptail selection we already have + # Let ensure_storage_selection_for_vars_file handle everything ensure_storage_selection_for_vars_file "$vf" } @@ -1834,19 +1740,24 @@ choose_and_set_storage_for_file() { echo_storage_summary_from_file() { local vars_file="$1" - local ct_store tpl_store - ct_store=$(awk -F= '/^var_container_storage=/ {print $2; exit}' "$vars_file") + local tpl_store ct_store tpl_store=$(awk -F= '/^var_template_storage=/ {print $2; exit}' "$vars_file") - if [ -n "$tpl" ]; then - TEMPLATE_STORAGE="$tpl" + ct_store=$(awk -F= '/^var_container_storage=/ {print $2; exit}' "$vars_file") + + if [[ -n "$tpl_store" ]] && resolve_storage_preselect template "$tpl_store"; then + TEMPLATE_STORAGE="$STORAGE_RESULT" + TEMPLATE_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Using Template-Storage → $TEMPLATE_STORAGE${TEMPLATE_STORAGE_INFO:+ ($TEMPLATE_STORAGE_INFO)}" else - choose_and_set_storage_for_file "$vf" template + choose_and_set_storage_for_file "$vars_file" template fi - if [ -n "$ct" ]; then - CONTAINER_STORAGE="$ct" + if [[ -n "$ct_store" ]] && resolve_storage_preselect container "$ct_store"; then + CONTAINER_STORAGE="$STORAGE_RESULT" + CONTAINER_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Using Container-Storage → $CONTAINER_STORAGE${CONTAINER_STORAGE_INFO:+ ($CONTAINER_STORAGE_INFO)}" else - choose_and_set_storage_for_file "$vf" container + choose_and_set_storage_for_file "$vars_file" container fi } @@ -1981,6 +1892,97 @@ ssh_discover_default_files() { printf '%s\0' "${cand[@]}" } +configure_ssh_settings() { + SSH_KEYS_FILE="$(mktemp)" + : >"$SSH_KEYS_FILE" + + IFS=$'\0' read -r -d '' -a _def_files < <(ssh_discover_default_files && printf '\0') + ssh_build_choices_from_files "${_def_files[@]}" + local default_key_count="$COUNT" + + local ssh_key_mode + if [[ "$default_key_count" -gt 0 ]]; then + ssh_key_mode=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ + "Provision SSH keys for root:" 14 72 4 \ + "found" "Select from detected keys (${default_key_count})" \ + "manual" "Paste a single public key" \ + "folder" "Scan another folder (path or glob)" \ + "none" "No keys" 3>&1 1>&2 2>&3) || exit_script + else + ssh_key_mode=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ + "No host keys detected; choose manual/none:" 12 72 2 \ + "manual" "Paste a single public key" \ + "none" "No keys" 3>&1 1>&2 2>&3) || exit_script + fi + + case "$ssh_key_mode" in + found) + local selection + selection=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT HOST KEYS" \ + --checklist "Select one or more keys to import:" 20 140 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script + for tag in $selection; do + tag="${tag%\"}" + tag="${tag#\"}" + local line + line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) + [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" + done + ;; + manual) + SSH_AUTHORIZED_KEY="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Paste one SSH public key line (ssh-ed25519/ssh-rsa/...)" 10 72 --title "SSH Public Key" 3>&1 1>&2 2>&3)" + [[ -n "$SSH_AUTHORIZED_KEY" ]] && printf '%s\n' "$SSH_AUTHORIZED_KEY" >>"$SSH_KEYS_FILE" + ;; + folder) + local glob_path + glob_path=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Enter a folder or glob to scan (e.g. /root/.ssh/*.pub)" 10 72 --title "Scan Folder/Glob" 3>&1 1>&2 2>&3) + if [[ -n "$glob_path" ]]; then + shopt -s nullglob + read -r -a _scan_files <<<"$glob_path" + shopt -u nullglob + if [[ "${#_scan_files[@]}" -gt 0 ]]; then + ssh_build_choices_from_files "${_scan_files[@]}" + if [[ "$COUNT" -gt 0 ]]; then + local folder_selection + folder_selection=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT FOLDER KEYS" \ + --checklist "Select key(s) to import:" 20 78 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script + for tag in $folder_selection; do + tag="${tag%\"}" + tag="${tag#\"}" + local line + line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) + [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" + done + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "No keys found in: $glob_path" 8 60 + fi + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "Path/glob returned no files." 8 60 + fi + fi + ;; + none) + : + ;; + esac + + if [[ -s "$SSH_KEYS_FILE" ]]; then + sort -u -o "$SSH_KEYS_FILE" "$SSH_KEYS_FILE" + printf '\n' >>"$SSH_KEYS_FILE" + fi + + if [[ -s "$SSH_KEYS_FILE" || "$PW" == -password* ]]; then + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable root SSH access?" 10 58); then + SSH="yes" + else + SSH="no" + fi + else + SSH="no" + fi +} + # ------------------------------------------------------------------------------ # start() # @@ -2143,7 +2145,7 @@ build_container() { # ============================================================================ # List of applications that benefit from GPU acceleration - GPU_APPS=( + GPU_APPS=( "immich" "channels" "emby" "ersatztv" "frigate" "jellyfin" "plex" "scrypted" "tdarr" "unmanic" "ollama" "fileflows" "open-webui" "tunarr" "debian" @@ -2234,12 +2236,11 @@ EOF fi fi - # Debug output - msg_debug "Intel devices: ${INTEL_DEVICES[*]}" - msg_debug "AMD devices: ${AMD_DEVICES[*]}" - msg_debug "NVIDIA devices: ${NVIDIA_DEVICES[*]}" - } - + # Debug output + msg_debug "Intel devices: ${INTEL_DEVICES[*]}" + msg_debug "AMD devices: ${AMD_DEVICES[*]}" + msg_debug "NVIDIA devices: ${NVIDIA_DEVICES[*]}" + } # Configure USB passthrough for privileged containers configure_usb_passthrough() { @@ -2264,70 +2265,70 @@ EOF } # Configure GPU passthrough -configure_gpu_passthrough() { - # Skip if not a GPU app and not privileged - if [[ "$CT_TYPE" != "0" ]] && ! is_gpu_app "$APP"; then - return 0 - fi - - detect_gpu_devices - - # Count available GPU types - local gpu_count=0 - local available_gpus=() - - if [[ ${#INTEL_DEVICES[@]} -gt 0 ]]; then - available_gpus+=("INTEL") - gpu_count=$((gpu_count + 1)) - fi - - if [[ ${#AMD_DEVICES[@]} -gt 0 ]]; then - available_gpus+=("AMD") - gpu_count=$((gpu_count + 1)) - fi - - if [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then - available_gpus+=("NVIDIA") - gpu_count=$((gpu_count + 1)) - fi - - if [[ $gpu_count -eq 0 ]]; then - msg_info "No GPU devices found for passthrough" - return 0 - fi - - local selected_gpu="" - - if [[ $gpu_count -eq 1 ]]; then - # Automatic selection for single GPU - selected_gpu="${available_gpus[0]}" - msg_info "Automatically configuring ${selected_gpu} GPU passthrough" - else - # Multiple GPUs - ask user - echo -e "\n${INFO} Multiple GPU types detected:" - for gpu in "${available_gpus[@]}"; do - echo " - $gpu" - done - read -rp "Which GPU type to passthrough? (${available_gpus[*]}): " selected_gpu - selected_gpu="${selected_gpu^^}" - - # Validate selection - local valid=0 - for gpu in "${available_gpus[@]}"; do - [[ "$selected_gpu" == "$gpu" ]] && valid=1 - done - - if [[ $valid -eq 0 ]]; then - msg_warn "Invalid selection. Skipping GPU passthrough." + configure_gpu_passthrough() { + # Skip if not a GPU app and not privileged + if [[ "$CT_TYPE" != "0" ]] && ! is_gpu_app "$APP"; then return 0 fi - fi - # Apply passthrough configuration based on selection - local dev_idx=0 + detect_gpu_devices - case "$selected_gpu" in - INTEL|AMD) + # Count available GPU types + local gpu_count=0 + local available_gpus=() + + if [[ ${#INTEL_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("INTEL") + gpu_count=$((gpu_count + 1)) + fi + + if [[ ${#AMD_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("AMD") + gpu_count=$((gpu_count + 1)) + fi + + if [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("NVIDIA") + gpu_count=$((gpu_count + 1)) + fi + + if [[ $gpu_count -eq 0 ]]; then + msg_info "No GPU devices found for passthrough" + return 0 + fi + + local selected_gpu="" + + if [[ $gpu_count -eq 1 ]]; then + # Automatic selection for single GPU + selected_gpu="${available_gpus[0]}" + msg_info "Automatically configuring ${selected_gpu} GPU passthrough" + else + # Multiple GPUs - ask user + echo -e "\n${INFO} Multiple GPU types detected:" + for gpu in "${available_gpus[@]}"; do + echo " - $gpu" + done + read -rp "Which GPU type to passthrough? (${available_gpus[*]}): " selected_gpu + selected_gpu="${selected_gpu^^}" + + # Validate selection + local valid=0 + for gpu in "${available_gpus[@]}"; do + [[ "$selected_gpu" == "$gpu" ]] && valid=1 + done + + if [[ $valid -eq 0 ]]; then + msg_warn "Invalid selection. Skipping GPU passthrough." + return 0 + fi + fi + + # Apply passthrough configuration based on selection + local dev_idx=0 + + case "$selected_gpu" in + INTEL | AMD) local devices=() [[ "$selected_gpu" == "INTEL" ]] && devices=("${INTEL_DEVICES[@]}") [[ "$selected_gpu" == "AMD" ]] && devices=("${AMD_DEVICES[@]}") @@ -2392,8 +2393,8 @@ configure_gpu_passthrough() { export GPU_TYPE="NVIDIA" msg_ok "NVIDIA GPU passthrough configured (${dev_idx} devices)" ;; - esac -} + esac + } # Additional device passthrough configure_additional_devices() { @@ -2473,143 +2474,11 @@ EOF get_container_gid() { local group="$1" local gid=$(pct exec "$CTID" -- getent group "$group" 2>/dev/null | cut -d: -f3) - echo "${gid:-44}" # Default to 44 if not found + echo "${gid:-44}" # Default to 44 if not found } fix_gpu_gids -# Configure GPU passthrough -configure_gpu_passthrough() { - # Skip if not a GPU app and not privileged - if [[ "$CT_TYPE" != "0" ]] && ! is_gpu_app "$APP"; then - return 0 - fi - - detect_gpu_devices - - # Count available GPU types - local gpu_count=0 - local available_gpus=() - - if [[ ${#INTEL_DEVICES[@]} -gt 0 ]]; then - available_gpus+=("INTEL") - gpu_count=$((gpu_count + 1)) - fi - - if [[ ${#AMD_DEVICES[@]} -gt 0 ]]; then - available_gpus+=("AMD") - gpu_count=$((gpu_count + 1)) - fi - - if [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then - available_gpus+=("NVIDIA") - gpu_count=$((gpu_count + 1)) - fi - - if [[ $gpu_count -eq 0 ]]; then - msg_info "No GPU devices found for passthrough" - return 0 - fi - - local selected_gpu="" - - if [[ $gpu_count -eq 1 ]]; then - # Automatic selection for single GPU - selected_gpu="${available_gpus[0]}" - msg_info "Automatically configuring ${selected_gpu} GPU passthrough" - else - # Multiple GPUs - ask user - echo -e "\n${INFO} Multiple GPU types detected:" - for gpu in "${available_gpus[@]}"; do - echo " - $gpu" - done - read -rp "Which GPU type to passthrough? (${available_gpus[*]}): " selected_gpu - selected_gpu="${selected_gpu^^}" - - # Validate selection - local valid=0 - for gpu in "${available_gpus[@]}"; do - [[ "$selected_gpu" == "$gpu" ]] && valid=1 - done - - if [[ $valid -eq 0 ]]; then - msg_warn "Invalid selection. Skipping GPU passthrough." - return 0 - fi - fi - - # Apply passthrough configuration based on selection - local dev_idx=0 - - case "$selected_gpu" in - INTEL|AMD) - local devices=() - [[ "$selected_gpu" == "INTEL" ]] && devices=("${INTEL_DEVICES[@]}") - [[ "$selected_gpu" == "AMD" ]] && devices=("${AMD_DEVICES[@]}") - - # For Proxmox WebUI visibility, add as dev0, dev1 etc. - for dev in "${devices[@]}"; do - if [[ "$CT_TYPE" == "0" ]]; then - # Privileged container - use dev entries for WebUI visibility - # Use initial GID 104 (render) for renderD*, 44 (video) for card* - if [[ "$dev" =~ renderD ]]; then - echo "dev${dev_idx}: $dev,gid=104" >>"$LXC_CONFIG" - else - echo "dev${dev_idx}: $dev,gid=44" >>"$LXC_CONFIG" - fi - dev_idx=$((dev_idx + 1)) - - # Also add cgroup allows for privileged containers - local major minor - major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") - minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") - - if [[ "$major" != "0" && "$minor" != "0" ]]; then - echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" - fi - else - # Unprivileged container - if [[ "$dev" =~ renderD ]]; then - echo "dev${dev_idx}: $dev,uid=0,gid=104" >>"$LXC_CONFIG" - else - echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG" - fi - dev_idx=$((dev_idx + 1)) - fi - done - - export GPU_TYPE="$selected_gpu" - msg_ok "${selected_gpu} GPU passthrough configured (${dev_idx} devices)" - ;; - - NVIDIA) - if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then - msg_error "NVIDIA drivers not installed on host. Please install: apt install nvidia-driver" - return 1 - fi - - for dev in "${NVIDIA_DEVICES[@]}"; do - # NVIDIA devices typically need different handling - echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG" - dev_idx=$((dev_idx + 1)) - - if [[ "$CT_TYPE" == "0" ]]; then - local major minor - major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") - minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") - - if [[ "$major" != "0" && "$minor" != "0" ]]; then - echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" - fi - fi - done - - export GPU_TYPE="NVIDIA" - msg_ok "NVIDIA GPU passthrough configured (${dev_idx} devices)" - ;; - esac -} - # Continue with standard container setup msg_info "Customizing LXC Container" @@ -2767,14 +2636,14 @@ fix_gpu_gids() { # Versuche die video Gruppe zu erstellen pct exec "$CTID" -- sh -c "groupadd -r video 2>/dev/null || true" video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") - [[ -z "$video_gid" ]] && video_gid="44" # Ultimate fallback + [[ -z "$video_gid" ]] && video_gid="44" # Ultimate fallback fi if [[ -z "$render_gid" ]]; then # Versuche die render Gruppe zu erstellen pct exec "$CTID" -- sh -c "groupadd -r render 2>/dev/null || true" render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") - [[ -z "$render_gid" ]] && render_gid="104" # Ultimate fallback + [[ -z "$render_gid" ]] && render_gid="104" # Ultimate fallback fi msg_info "Container GIDs detected - video:${video_gid}, render:${render_gid}" @@ -2816,7 +2685,7 @@ fix_gpu_gids() { # Keep non-dev lines echo "$line" fi - done < "$LXC_CONFIG" > "${LXC_CONFIG}.new" + done <"$LXC_CONFIG" >"${LXC_CONFIG}.new" mv "${LXC_CONFIG}.new" "$LXC_CONFIG" From 21525006d0164bcbd59621426734a8c8317e6c16 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 20 Oct 2025 08:15:43 +0200 Subject: [PATCH 1448/1733] fixes --- misc/test-tools-func.sh | 70 +- misc/tools.func | 4228 ++++++++++++++++++++------------------- 2 files changed, 2170 insertions(+), 2128 deletions(-) diff --git a/misc/test-tools-func.sh b/misc/test-tools-func.sh index 0718bbd66..744c25e7c 100644 --- a/misc/test-tools-func.sh +++ b/misc/test-tools-func.sh @@ -169,16 +169,16 @@ apt-get install -y curl wget gpg jq git build-essential ca-certificates &>/dev/n # ============================================================================== # TEST 1: YQ - YAML Processor # ============================================================================== -test_function "YQ" \ - "setup_yq" \ - "yq --version" +# test_function "YQ" \ +# "setup_yq" \ +# "yq --version" # ============================================================================== # TEST 2: ADMINER - Database Management # ============================================================================== test_function "Adminer" \ "setup_adminer" \ - "test -f /usr/share/adminer/latest.php && echo 'Adminer installed'" + "dpkg -l adminer 2>/dev/null | grep -q '^ii' && a2query -c adminer 2>/dev/null && echo 'Adminer installed'" # ============================================================================== # TEST 3: CLICKHOUSE @@ -190,9 +190,9 @@ test_function "ClickHouse" \ # ============================================================================== # TEST 4: POSTGRESQL # ============================================================================== -test_function "PostgreSQL 17" \ - "PG_VERSION=17 setup_postgresql" \ - "psql --version" +# test_function "PostgreSQL 17" \ +# "PG_VERSION=17 setup_postgresql" \ +# "psql --version" # ============================================================================== # TEST 6: MARIADB @@ -229,44 +229,44 @@ fi # ============================================================================== # TEST 9: NODE.JS # ============================================================================== -test_function "Node.js 22 with modules" \ - "NODE_VERSION=22 NODE_MODULE='yarn,pnpm@10.1.0,pm2' setup_nodejs" \ - "node --version && npm --version && yarn --version && pnpm --version && pm2 --version" +# test_function "Node.js 22 with modules" \ +# "NODE_VERSION=22 NODE_MODULE='yarn,pnpm@10.1.0,pm2' setup_nodejs" \ +# "node --version && npm --version && yarn --version && pnpm --version && pm2 --version" # ============================================================================== # TEST 10: PYTHON (UV) # ============================================================================== -test_function "Python 3.12 via uv" \ - "PYTHON_VERSION=3.12 setup_uv" \ - "uv --version" +# test_function "Python 3.12 via uv" \ +# "PYTHON_VERSION=3.12 setup_uv" \ +# "uv --version" # ============================================================================== # TEST 11: PHP # ============================================================================== -test_function "PHP 8.3 with FPM" \ - "PHP_VERSION=8.3 PHP_FPM=YES PHP_MODULE='redis,imagick,apcu,zip,mbstring' setup_php" \ - "php --version" +# test_function "PHP 8.3 with FPM" \ +# "PHP_VERSION=8.3 PHP_FPM=YES PHP_MODULE='redis,imagick,apcu,zip,mbstring' setup_php" \ +# "php --version" # ============================================================================== # TEST 12: COMPOSER -# ============================================================================== -test_function "Composer" \ - "setup_composer" \ - "composer --version" +# # ============================================================================== +# test_function "Composer" \ +# "setup_composer" \ +# "composer --version" # ============================================================================== # TEST 13: JAVA # ============================================================================== -test_function "Java Temurin 21" \ - "JAVA_VERSION=21 setup_java" \ - "java --version" +# test_function "Java Temurin 21" \ +# "JAVA_VERSION=21 setup_java" \ +# "java --version" # ============================================================================== # TEST 14: GO # ============================================================================== -test_function "Go (latest)" \ - "GO_VERSION=latest setup_go" \ - "go version" +# test_function "Go (latest)" \ +# "GO_VERSION=latest setup_go" \ +# "go version" # ============================================================================== # TEST 15: RUBY @@ -285,23 +285,23 @@ test_function "Rust (stable)" \ # ============================================================================== # TEST 17: GHOSTSCRIPT # ============================================================================== -test_function "Ghostscript" \ - "setup_gs" \ - "gs --version" +# test_function "Ghostscript" \ +# "setup_gs" \ +# "gs --version" # ============================================================================== # TEST 18: IMAGEMAGICK # ============================================================================== -test_function "ImageMagick" \ - "setup_imagemagick" \ - "magick --version" +# test_function "ImageMagick" \ +# "setup_imagemagick" \ +# "magick --version" # ============================================================================== # TEST 19: FFMPEG # ============================================================================== -test_function "FFmpeg n7.1.1 (full)" \ - "FFMPEG_VERSION=n7.1.1 FFMPEG_TYPE=full setup_ffmpeg" \ - "ffmpeg -version" +# test_function "FFmpeg n7.1.1 (full)" \ +# "FFMPEG_VERSION=n7.1.1 FFMPEG_TYPE=full setup_ffmpeg" \ +# "ffmpeg -version" # ============================================================================== # FINAL SUMMARY diff --git a/misc/tools.func b/misc/tools.func index d7f5dda2b..097f86df4 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -8,554 +8,557 @@ # Cache installed version to avoid repeated checks # ------------------------------------------------------------------------------ cache_installed_version() { - local app="$1" - local version="$2" - mkdir -p /var/cache/app-versions - echo "$version" >"/var/cache/app-versions/${app}_version.txt" + local app="$1" + local version="$2" + mkdir -p /var/cache/app-versions + echo "$version" >"/var/cache/app-versions/${app}_version.txt" } get_cached_version() { - local app="$1" - mkdir -p /var/cache/app-versions - if [[ -f "/var/cache/app-versions/${app}_version.txt" ]]; then - cat "/var/cache/app-versions/${app}_version.txt" - return 0 - fi + local app="$1" + mkdir -p /var/cache/app-versions + if [[ -f "/var/cache/app-versions/${app}_version.txt" ]]; then + cat "/var/cache/app-versions/${app}_version.txt" return 0 + fi + return 0 } # ------------------------------------------------------------------------------ # Unified package upgrade function (with apt update caching) # ------------------------------------------------------------------------------ upgrade_package() { - local package="$1" - msg_info "Upgrading $package" + local package="$1" + msg_info "Upgrading $package" - # Use same caching logic as ensure_dependencies - local apt_cache_file="/var/cache/apt-update-timestamp" - local current_time=$(date +%s) - local last_update=0 + # Use same caching logic as ensure_dependencies + local apt_cache_file="/var/cache/apt-update-timestamp" + local current_time=$(date +%s) + local last_update=0 - if [[ -f "$apt_cache_file" ]]; then - last_update=$(cat "$apt_cache_file" 2>/dev/null || echo 0) - fi + if [[ -f "$apt_cache_file" ]]; then + last_update=$(cat "$apt_cache_file" 2>/dev/null || echo 0) + fi - if ((current_time - last_update > 300)); then - $STD apt update - echo "$current_time" >"$apt_cache_file" - fi + if ((current_time - last_update > 300)); then + $STD apt update + echo "$current_time" >"$apt_cache_file" + fi - $STD apt install --only-upgrade -y "$package" - msg_ok "Upgraded $package" + $STD apt install --only-upgrade -y "$package" + msg_ok "Upgraded $package" } # ------------------------------------------------------------------------------ # Repository availability check # ------------------------------------------------------------------------------ verify_repo_available() { - local repo_url="$1" - local suite="$2" + local repo_url="$1" + local suite="$2" - if curl -fsSL --max-time 10 "${repo_url}/dists/${suite}/Release" &>/dev/null; then - return 0 - fi - return 1 + if curl -fsSL --max-time 10 "${repo_url}/dists/${suite}/Release" &>/dev/null; then + return 0 + fi + return 1 } # ------------------------------------------------------------------------------ # Ensure dependencies are installed (with apt update caching) # ------------------------------------------------------------------------------ ensure_dependencies() { - local deps=("$@") - local missing=() + local deps=("$@") + local missing=() - for dep in "${deps[@]}"; do - if ! command -v "$dep" &>/dev/null && ! is_package_installed "$dep"; then - missing+=("$dep") - fi - done - - if [[ ${#missing[@]} -gt 0 ]]; then - msg_info "Installing dependencies: ${missing[*]}" - - # Only run apt update if not done recently (within last 5 minutes) - local apt_cache_file="/var/cache/apt-update-timestamp" - local current_time=$(date +%s) - local last_update=0 - - if [[ -f "$apt_cache_file" ]]; then - last_update=$(cat "$apt_cache_file" 2>/dev/null || echo 0) - fi - - if ((current_time - last_update > 300)); then - $STD apt update - echo "$current_time" >"$apt_cache_file" - fi - - $STD apt install -y "${missing[@]}" - msg_ok "Installed dependencies" + for dep in "${deps[@]}"; do + if ! command -v "$dep" &>/dev/null && ! is_package_installed "$dep"; then + missing+=("$dep") fi + done + + if [[ ${#missing[@]} -gt 0 ]]; then + msg_info "Installing dependencies: ${missing[*]}" + + # Only run apt update if not done recently (within last 5 minutes) + local apt_cache_file="/var/cache/apt-update-timestamp" + local current_time=$(date +%s) + local last_update=0 + + if [[ -f "$apt_cache_file" ]]; then + last_update=$(cat "$apt_cache_file" 2>/dev/null || echo 0) + fi + + if ((current_time - last_update > 300)); then + $STD apt update + echo "$current_time" >"$apt_cache_file" + fi + + $STD apt install -y "${missing[@]}" + msg_ok "Installed dependencies" + fi } # ------------------------------------------------------------------------------ # Smart version comparison # ------------------------------------------------------------------------------ version_gt() { - test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1" + test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1" } # ------------------------------------------------------------------------------ # Get system architecture (normalized) # ------------------------------------------------------------------------------ get_system_arch() { - local arch_type="${1:-dpkg}" # dpkg, uname, or both - local arch + local arch_type="${1:-dpkg}" # dpkg, uname, or both + local arch - case "$arch_type" in - dpkg) - arch=$(dpkg --print-architecture 2>/dev/null) - ;; - uname) - arch=$(uname -m) - [[ "$arch" == "x86_64" ]] && arch="amd64" - [[ "$arch" == "aarch64" ]] && arch="arm64" - ;; - both | *) - arch=$(dpkg --print-architecture 2>/dev/null || uname -m) - [[ "$arch" == "x86_64" ]] && arch="amd64" - [[ "$arch" == "aarch64" ]] && arch="arm64" - ;; - esac + case "$arch_type" in + dpkg) + arch=$(dpkg --print-architecture 2>/dev/null) + ;; + uname) + arch=$(uname -m) + [[ "$arch" == "x86_64" ]] && arch="amd64" + [[ "$arch" == "aarch64" ]] && arch="arm64" + ;; + both | *) + arch=$(dpkg --print-architecture 2>/dev/null || uname -m) + [[ "$arch" == "x86_64" ]] && arch="amd64" + [[ "$arch" == "aarch64" ]] && arch="arm64" + ;; + esac - echo "$arch" + echo "$arch" } # ------------------------------------------------------------------------------ # Create temporary directory with automatic cleanup # ------------------------------------------------------------------------------ create_temp_dir() { - local tmp_dir=$(mktemp -d) - # Set trap to cleanup on EXIT, ERR, INT, TERM - trap "rm -rf '$tmp_dir'" EXIT ERR INT TERM - echo "$tmp_dir" + local tmp_dir=$(mktemp -d) + # Set trap to cleanup on EXIT, ERR, INT, TERM + trap "rm -rf '$tmp_dir'" EXIT ERR INT TERM + echo "$tmp_dir" } # ------------------------------------------------------------------------------ # Check if package is installed (faster than dpkg -l | grep) # ------------------------------------------------------------------------------ is_package_installed() { - local package="$1" - dpkg-query -W -f='${Status}' "$package" 2>/dev/null | grep -q "^install ok installed$" + local package="$1" + dpkg-query -W -f='${Status}' "$package" 2>/dev/null | grep -q "^install ok installed$" } # ------------------------------------------------------------------------------ # GitHub API call with authentication and rate limit handling # ------------------------------------------------------------------------------ github_api_call() { - local url="$1" - local output_file="${2:-/dev/stdout}" - local max_retries=3 - local retry_delay=2 + local url="$1" + local output_file="${2:-/dev/stdout}" + local max_retries=3 + local retry_delay=2 - local header_args=() - [[ -n "${GITHUB_TOKEN:-}" ]] && header_args=(-H "Authorization: Bearer $GITHUB_TOKEN") + local header_args=() + [[ -n "${GITHUB_TOKEN:-}" ]] && header_args=(-H "Authorization: Bearer $GITHUB_TOKEN") - for attempt in $(seq 1 $max_retries); do - local http_code - http_code=$(curl -fsSL -w "%{http_code}" -o "$output_file" \ - -H "Accept: application/vnd.github+json" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ - "${header_args[@]}" \ - "$url" 2>/dev/null || echo "000") + for attempt in $(seq 1 $max_retries); do + local http_code + http_code=$(curl -fsSL -w "%{http_code}" -o "$output_file" \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "${header_args[@]}" \ + "$url" 2>/dev/null || echo "000") - case "$http_code" in - 200) - return 0 - ;; - 403) - # Rate limit - check if we can retry - if [[ $attempt -lt $max_retries ]]; then - msg_warn "GitHub API rate limit, waiting ${retry_delay}s... (attempt $attempt/$max_retries)" - sleep "$retry_delay" - retry_delay=$((retry_delay * 2)) - continue - fi - msg_error "GitHub API rate limit exceeded. Set GITHUB_TOKEN to increase limits." - return 1 - ;; - 404) - msg_error "GitHub API endpoint not found: $url" - return 1 - ;; - *) - if [[ $attempt -lt $max_retries ]]; then - sleep "$retry_delay" - continue - fi - msg_error "GitHub API call failed with HTTP $http_code" - return 1 - ;; - esac - done + case "$http_code" in + 200) + return 0 + ;; + 403) + # Rate limit - check if we can retry + if [[ $attempt -lt $max_retries ]]; then + msg_warn "GitHub API rate limit, waiting ${retry_delay}s... (attempt $attempt/$max_retries)" + sleep "$retry_delay" + retry_delay=$((retry_delay * 2)) + continue + fi + msg_error "GitHub API rate limit exceeded. Set GITHUB_TOKEN to increase limits." + return 1 + ;; + 404) + msg_error "GitHub API endpoint not found: $url" + return 1 + ;; + *) + if [[ $attempt -lt $max_retries ]]; then + sleep "$retry_delay" + continue + fi + msg_error "GitHub API call failed with HTTP $http_code" + return 1 + ;; + esac + done - return 1 + return 1 } should_upgrade() { - local current="$1" - local target="$2" + local current="$1" + local target="$2" - [[ -z "$current" ]] && return 0 - version_gt "$target" "$current" && return 0 - return 1 + [[ -z "$current" ]] && return 0 + version_gt "$target" "$current" && return 0 + return 1 } # ------------------------------------------------------------------------------ # Get OS information (cached for performance) # ------------------------------------------------------------------------------ get_os_info() { - local field="${1:-all}" # id, codename, version, version_id, all + local field="${1:-all}" # id, codename, version, version_id, all - # Cache OS info to avoid repeated file reads - if [[ -z "${_OS_ID:-}" ]]; then - export _OS_ID=$(awk -F= '/^ID=/{gsub(/"/,"",$2); print $2}' /etc/os-release) - export _OS_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{gsub(/"/,"",$2); print $2}' /etc/os-release) - export _OS_VERSION=$(awk -F= '/^VERSION_ID=/{gsub(/"/,"",$2); print $2}' /etc/os-release) - export _OS_VERSION_FULL=$(awk -F= '/^VERSION=/{gsub(/"/,"",$2); print $2}' /etc/os-release) - fi + # Cache OS info to avoid repeated file reads + if [[ -z "${_OS_ID:-}" ]]; then + export _OS_ID=$(awk -F= '/^ID=/{gsub(/"/,"",$2); print $2}' /etc/os-release) + export _OS_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{gsub(/"/,"",$2); print $2}' /etc/os-release) + export _OS_VERSION=$(awk -F= '/^VERSION_ID=/{gsub(/"/,"",$2); print $2}' /etc/os-release) + export _OS_VERSION_FULL=$(awk -F= '/^VERSION=/{gsub(/"/,"",$2); print $2}' /etc/os-release) + fi - case "$field" in - id) echo "$_OS_ID" ;; - codename) echo "$_OS_CODENAME" ;; - version) echo "$_OS_VERSION" ;; - version_id) echo "$_OS_VERSION" ;; - version_full) echo "$_OS_VERSION_FULL" ;; - all) echo "ID=$_OS_ID CODENAME=$_OS_CODENAME VERSION=$_OS_VERSION" ;; - *) echo "$_OS_ID" ;; - esac + case "$field" in + id) echo "$_OS_ID" ;; + codename) echo "$_OS_CODENAME" ;; + version) echo "$_OS_VERSION" ;; + version_id) echo "$_OS_VERSION" ;; + version_full) echo "$_OS_VERSION_FULL" ;; + all) echo "ID=$_OS_ID CODENAME=$_OS_CODENAME VERSION=$_OS_VERSION" ;; + *) echo "$_OS_ID" ;; + esac } # ------------------------------------------------------------------------------ # Check if running on specific OS # ------------------------------------------------------------------------------ is_debian() { - [[ "$(get_os_info id)" == "debian" ]] + [[ "$(get_os_info id)" == "debian" ]] } is_ubuntu() { - [[ "$(get_os_info id)" == "ubuntu" ]] + [[ "$(get_os_info id)" == "ubuntu" ]] } is_alpine() { - [[ "$(get_os_info id)" == "alpine" ]] + [[ "$(get_os_info id)" == "alpine" ]] } # ------------------------------------------------------------------------------ # Get Debian/Ubuntu major version # ------------------------------------------------------------------------------ get_os_version_major() { - local version=$(get_os_info version) - echo "${version%%.*}" + local version=$(get_os_info version) + echo "${version%%.*}" } # ------------------------------------------------------------------------------ # Download file with retry logic and progress # ------------------------------------------------------------------------------ download_file() { - local url="$1" - local output="$2" - local max_retries="${3:-3}" - local show_progress="${4:-false}" + local url="$1" + local output="$2" + local max_retries="${3:-3}" + local show_progress="${4:-false}" - local curl_opts=(-fsSL) - [[ "$show_progress" == "true" ]] && curl_opts=(-fL#) + local curl_opts=(-fsSL) + [[ "$show_progress" == "true" ]] && curl_opts=(-fL#) - for attempt in $(seq 1 $max_retries); do - if curl "${curl_opts[@]}" -o "$output" "$url"; then - return 0 - fi + for attempt in $(seq 1 $max_retries); do + if curl "${curl_opts[@]}" -o "$output" "$url"; then + return 0 + fi - if [[ $attempt -lt $max_retries ]]; then - msg_warn "Download failed, retrying... (attempt $attempt/$max_retries)" - sleep 2 - fi - done + if [[ $attempt -lt $max_retries ]]; then + msg_warn "Download failed, retrying... (attempt $attempt/$max_retries)" + sleep 2 + fi + done - msg_error "Failed to download: $url" - return 1 + msg_error "Failed to download: $url" + return 1 } # ------------------------------------------------------------------------------ # Get fallback suite for repository (comprehensive mapping) # ------------------------------------------------------------------------------ get_fallback_suite() { - local distro_id="$1" - local distro_codename="$2" - local repo_base_url="$3" + local distro_id="$1" + local distro_codename="$2" + local repo_base_url="$3" - # Check if current codename works - if verify_repo_available "$repo_base_url" "$distro_codename"; then - echo "$distro_codename" - return 0 - fi + # Check if current codename works + if verify_repo_available "$repo_base_url" "$distro_codename"; then + echo "$distro_codename" + return 0 + fi - # Comprehensive fallback mappings - case "$distro_id" in - debian) - case "$distro_codename" in - # Debian 13 (Trixie) → Debian 12 (Bookworm) - trixie | forky | sid) - echo "bookworm" - ;; - # Debian 12 (Bookworm) stays - bookworm) - echo "bookworm" - ;; - # Debian 11 (Bullseye) stays - bullseye) - echo "bullseye" - ;; - # Unknown → latest stable - *) - echo "bookworm" - ;; - esac - ;; - ubuntu) - case "$distro_codename" in - # Ubuntu 24.10 (Oracular) → 24.04 LTS (Noble) - oracular | plucky) - echo "noble" - ;; - # Ubuntu 24.04 LTS (Noble) stays - noble) - echo "noble" - ;; - # Ubuntu 23.10 (Mantic) → 22.04 LTS (Jammy) - mantic | lunar) - echo "jammy" - ;; - # Ubuntu 22.04 LTS (Jammy) stays - jammy) - echo "jammy" - ;; - # Ubuntu 20.04 LTS (Focal) stays - focal) - echo "focal" - ;; - # Unknown → latest LTS - *) - echo "jammy" - ;; - esac - ;; + # Comprehensive fallback mappings + case "$distro_id" in + debian) + case "$distro_codename" in + # Debian 13 (Trixie) → Debian 12 (Bookworm) + trixie | forky | sid) + echo "bookworm" + ;; + # Debian 12 (Bookworm) stays + bookworm) + echo "bookworm" + ;; + # Debian 11 (Bullseye) stays + bullseye) + echo "bullseye" + ;; + # Unknown → latest stable *) - echo "$distro_codename" - ;; + echo "bookworm" + ;; esac + ;; + ubuntu) + case "$distro_codename" in + # Ubuntu 24.10 (Oracular) → 24.04 LTS (Noble) + oracular | plucky) + echo "noble" + ;; + # Ubuntu 24.04 LTS (Noble) stays + noble) + echo "noble" + ;; + # Ubuntu 23.10 (Mantic) → 22.04 LTS (Jammy) + mantic | lunar) + echo "jammy" + ;; + # Ubuntu 22.04 LTS (Jammy) stays + jammy) + echo "jammy" + ;; + # Ubuntu 20.04 LTS (Focal) stays + focal) + echo "focal" + ;; + # Unknown → latest LTS + *) + echo "jammy" + ;; + esac + ;; + *) + echo "$distro_codename" + ;; + esac } # ------------------------------------------------------------------------------ # Verify package source and version # ------------------------------------------------------------------------------ verify_package_source() { - local package="$1" - local expected_version="$2" + local package="$1" + local expected_version="$2" - if apt-cache policy "$package" 2>/dev/null | grep -q "$expected_version"; then - return 0 - fi - return 1 + if apt-cache policy "$package" 2>/dev/null | grep -q "$expected_version"; then + return 0 + fi + return 1 } # ------------------------------------------------------------------------------ # Check if running on LTS version # ------------------------------------------------------------------------------ is_lts_version() { - local os_id=$(get_os_info id) - local codename=$(get_os_info codename) + local os_id=$(get_os_info id) + local codename=$(get_os_info codename) - if [[ "$os_id" == "ubuntu" ]]; then - case "$codename" in - focal | jammy | noble) return 0 ;; # 20.04, 22.04, 24.04 - *) return 1 ;; - esac - elif [[ "$os_id" == "debian" ]]; then - # Debian releases are all "stable" - case "$codename" in - bullseye | bookworm | trixie) return 0 ;; - *) return 1 ;; - esac - fi + if [[ "$os_id" == "ubuntu" ]]; then + case "$codename" in + focal | jammy | noble) return 0 ;; # 20.04, 22.04, 24.04 + *) return 1 ;; + esac + elif [[ "$os_id" == "debian" ]]; then + # Debian releases are all "stable" + case "$codename" in + bullseye | bookworm | trixie) return 0 ;; + *) return 1 ;; + esac + fi - return 1 + return 1 } # ------------------------------------------------------------------------------ # Get optimal number of parallel jobs (cached) # ------------------------------------------------------------------------------ get_parallel_jobs() { - if [[ -z "${_PARALLEL_JOBS:-}" ]]; then - local cpu_count=$(nproc 2>/dev/null || echo 1) - local mem_gb=$(free -g | awk '/^Mem:/{print $2}') + if [[ -z "${_PARALLEL_JOBS:-}" ]]; then + local cpu_count=$(nproc 2>/dev/null || echo 1) + local mem_gb=$(free -g | awk '/^Mem:/{print $2}') - # Limit by available memory (assume 1GB per job for compilation) - local max_by_mem=$((mem_gb > 0 ? mem_gb : 1)) - local max_jobs=$((cpu_count < max_by_mem ? cpu_count : max_by_mem)) + # Limit by available memory (assume 1GB per job for compilation) + local max_by_mem=$((mem_gb > 0 ? mem_gb : 1)) + local max_jobs=$((cpu_count < max_by_mem ? cpu_count : max_by_mem)) - # At least 1, at most cpu_count - export _PARALLEL_JOBS=$((max_jobs > 0 ? max_jobs : 1)) - fi - echo "$_PARALLEL_JOBS" + # At least 1, at most cpu_count + export _PARALLEL_JOBS=$((max_jobs > 0 ? max_jobs : 1)) + fi + echo "$_PARALLEL_JOBS" } # ------------------------------------------------------------------------------ # Get default PHP version for OS # ------------------------------------------------------------------------------ get_default_php_version() { - local os_id=$(get_os_info id) - local os_version=$(get_os_version_major) + local os_id=$(get_os_info id) + local os_version=$(get_os_version_major) - case "$os_id" in - debian) - case "$os_version" in - 13) echo "8.3" ;; # Debian 13 (Trixie) - 12) echo "8.2" ;; # Debian 12 (Bookworm) - 11) echo "7.4" ;; # Debian 11 (Bullseye) - *) echo "8.2" ;; - esac - ;; - ubuntu) - case "$os_version" in - 24) echo "8.3" ;; # Ubuntu 24.04 LTS (Noble) - 22) echo "8.1" ;; # Ubuntu 22.04 LTS (Jammy) - 20) echo "7.4" ;; # Ubuntu 20.04 LTS (Focal) - *) echo "8.1" ;; - esac - ;; - *) - echo "8.2" - ;; + case "$os_id" in + debian) + case "$os_version" in + 13) echo "8.3" ;; # Debian 13 (Trixie) + 12) echo "8.2" ;; # Debian 12 (Bookworm) + 11) echo "7.4" ;; # Debian 11 (Bullseye) + *) echo "8.2" ;; esac + ;; + ubuntu) + case "$os_version" in + 24) echo "8.3" ;; # Ubuntu 24.04 LTS (Noble) + 22) echo "8.1" ;; # Ubuntu 22.04 LTS (Jammy) + 20) echo "7.4" ;; # Ubuntu 20.04 LTS (Focal) + *) echo "8.1" ;; + esac + ;; + *) + echo "8.2" + ;; + esac } # ------------------------------------------------------------------------------ # Get default Python version for OS # ------------------------------------------------------------------------------ get_default_python_version() { - local os_id=$(get_os_info id) - local os_version=$(get_os_version_major) + local os_id=$(get_os_info id) + local os_version=$(get_os_version_major) - case "$os_id" in - debian) - case "$os_version" in - 13) echo "3.12" ;; # Debian 13 (Trixie) - 12) echo "3.11" ;; # Debian 12 (Bookworm) - 11) echo "3.9" ;; # Debian 11 (Bullseye) - *) echo "3.11" ;; - esac - ;; - ubuntu) - case "$os_version" in - 24) echo "3.12" ;; # Ubuntu 24.04 LTS - 22) echo "3.10" ;; # Ubuntu 22.04 LTS - 20) echo "3.8" ;; # Ubuntu 20.04 LTS - *) echo "3.10" ;; - esac - ;; - *) - echo "3.11" - ;; + case "$os_id" in + debian) + case "$os_version" in + 13) echo "3.12" ;; # Debian 13 (Trixie) + 12) echo "3.11" ;; # Debian 12 (Bookworm) + 11) echo "3.9" ;; # Debian 11 (Bullseye) + *) echo "3.11" ;; esac + ;; + ubuntu) + case "$os_version" in + 24) echo "3.12" ;; # Ubuntu 24.04 LTS + 22) echo "3.10" ;; # Ubuntu 22.04 LTS + 20) echo "3.8" ;; # Ubuntu 20.04 LTS + *) echo "3.10" ;; + esac + ;; + *) + echo "3.11" + ;; + esac } # ------------------------------------------------------------------------------ # Get default Node.js LTS version # ------------------------------------------------------------------------------ get_default_nodejs_version() { - # Always return current LTS (as of 2025) - echo "20" + # Always return current LTS (as of 2025) + echo "20" } # ------------------------------------------------------------------------------ # Check if package manager is locked # ------------------------------------------------------------------------------ is_apt_locked() { - if fuser /var/lib/dpkg/lock-frontend &>/dev/null || - fuser /var/lib/apt/lists/lock &>/dev/null || - fuser /var/cache/apt/archives/lock &>/dev/null; then - return 0 - fi - return 1 + if fuser /var/lib/dpkg/lock-frontend &>/dev/null || + fuser /var/lib/apt/lists/lock &>/dev/null || + fuser /var/cache/apt/archives/lock &>/dev/null; then + return 0 + fi + return 1 } # ------------------------------------------------------------------------------ # Wait for apt to be available # ------------------------------------------------------------------------------ wait_for_apt() { - local max_wait="${1:-300}" # 5 minutes default - local waited=0 + local max_wait="${1:-300}" # 5 minutes default + local waited=0 - while is_apt_locked; do - if [[ $waited -ge $max_wait ]]; then - msg_error "Timeout waiting for apt to be available" - return 1 - fi + while is_apt_locked; do + if [[ $waited -ge $max_wait ]]; then + msg_error "Timeout waiting for apt to be available" + return 1 + fi - debug_log "Waiting for apt to be available... (${waited}s)" - sleep 5 - waited=$((waited + 5)) - done + debug_log "Waiting for apt to be available... (${waited}s)" + sleep 5 + waited=$((waited + 5)) + done - return 0 + return 0 } # ------------------------------------------------------------------------------ # Cleanup old repository files (migration helper) # ------------------------------------------------------------------------------ cleanup_old_repo_files() { - local app="$1" + local app="$1" - # Remove old-style .list files (including backups) - rm -f /etc/apt/sources.list.d/"${app}"*.list - rm -f /etc/apt/sources.list.d/"${app}"*.list.save - rm -f /etc/apt/sources.list.d/"${app}"*.list.distUpgrade - rm -f /etc/apt/sources.list.d/"${app}"*.list.dpkg-* + # Remove old-style .list files (including backups) + rm -f /etc/apt/sources.list.d/"${app}"*.list + rm -f /etc/apt/sources.list.d/"${app}"*.list.save + rm -f /etc/apt/sources.list.d/"${app}"*.list.distUpgrade + rm -f /etc/apt/sources.list.d/"${app}"*.list.dpkg-* - # Remove old GPG keys from trusted.gpg.d - rm -f /etc/apt/trusted.gpg.d/"${app}"*.gpg + # Remove old GPG keys from trusted.gpg.d + rm -f /etc/apt/trusted.gpg.d/"${app}"*.gpg - # Remove duplicate .sources files (keep only the main one) - local sources_file="/etc/apt/sources.list.d/${app}.sources" - if [[ -f "$sources_file" ]]; then - find /etc/apt/sources.list.d/ -name "${app}*.sources" ! -name "${app}.sources" -delete 2>/dev/null || true - fi + # Remove keyrings from /etc/apt/keyrings (FIX: This was missing!) + rm -f /etc/apt/keyrings/"${app}"*.gpg + + # Remove duplicate .sources files (keep only the main one) + local sources_file="/etc/apt/sources.list.d/${app}.sources" + if [[ -f "$sources_file" ]]; then + find /etc/apt/sources.list.d/ -name "${app}*.sources" ! -name "${app}.sources" -delete 2>/dev/null || true + fi } # ------------------------------------------------------------------------------ # Standardized deb822 repository setup # ------------------------------------------------------------------------------ setup_deb822_repo() { - local name="$1" - local gpg_url="$2" - local repo_url="$3" - local suite="$4" - local component="${5:-main}" - local architectures="${6:-amd64 arm64}" + local name="$1" + local gpg_url="$2" + local repo_url="$3" + local suite="$4" + local component="${5:-main}" + local architectures="${6:-amd64 arm64}" - msg_info "Setting up $name repository" + msg_info "Setting up $name repository" - # Cleanup old configs - cleanup_old_repo_files "$name" + # Cleanup old configs + cleanup_old_repo_files "$name" - # Ensure keyring directory exists - mkdir -p /etc/apt/keyrings + # Ensure keyring directory exists + mkdir -p /etc/apt/keyrings - # Download GPG key (with --yes to avoid interactive prompts) - curl -fsSL "$gpg_url" | gpg --dearmor --yes -o "/etc/apt/keyrings/${name}.gpg" + # Download GPG key (with --yes to avoid interactive prompts) + curl -fsSL "$gpg_url" | gpg --dearmor --yes -o "/etc/apt/keyrings/${name}.gpg" - # Create deb822 sources file - cat </etc/apt/sources.list.d/${name}.sources + # Create deb822 sources file + cat </etc/apt/sources.list.d/${name}.sources Types: deb URIs: $repo_url Suites: $suite @@ -564,178 +567,178 @@ Architectures: $architectures Signed-By: /etc/apt/keyrings/${name}.gpg EOF - # Use cached apt update - local apt_cache_file="/var/cache/apt-update-timestamp" - local current_time=$(date +%s) - local last_update=0 + # Use cached apt update + local apt_cache_file="/var/cache/apt-update-timestamp" + local current_time=$(date +%s) + local last_update=0 - if [[ -f "$apt_cache_file" ]]; then - last_update=$(cat "$apt_cache_file" 2>/dev/null || echo 0) - fi + if [[ -f "$apt_cache_file" ]]; then + last_update=$(cat "$apt_cache_file" 2>/dev/null || echo 0) + fi - # For repo changes, always update but respect short-term cache (30s) - if ((current_time - last_update > 30)); then - $STD apt update - echo "$current_time" >"$apt_cache_file" - fi + # For repo changes, always update but respect short-term cache (30s) + if ((current_time - last_update > 30)); then + $STD apt update + echo "$current_time" >"$apt_cache_file" + fi - msg_ok "Set up $name repository" + msg_ok "Set up $name repository" } # ------------------------------------------------------------------------------ # Package version hold/unhold helpers # ------------------------------------------------------------------------------ hold_package_version() { - local package="$1" - $STD apt-mark hold "$package" + local package="$1" + $STD apt-mark hold "$package" } unhold_package_version() { - local package="$1" - $STD apt-mark unhold "$package" + local package="$1" + $STD apt-mark unhold "$package" } # ------------------------------------------------------------------------------ # Safe service restart with verification # ------------------------------------------------------------------------------ safe_service_restart() { - local service="$1" + local service="$1" - if systemctl is-active --quiet "$service"; then - $STD systemctl restart "$service" - else - $STD systemctl start "$service" - fi + if systemctl is-active --quiet "$service"; then + $STD systemctl restart "$service" + else + $STD systemctl start "$service" + fi - if ! systemctl is-active --quiet "$service"; then - msg_error "Failed to start $service" - systemctl status "$service" --no-pager - return 1 - fi - return 0 + if ! systemctl is-active --quiet "$service"; then + msg_error "Failed to start $service" + systemctl status "$service" --no-pager + return 1 + fi + return 0 } # ------------------------------------------------------------------------------ # Enable and start service (with error handling) # ------------------------------------------------------------------------------ enable_and_start_service() { - local service="$1" + local service="$1" - if ! systemctl enable "$service" &>/dev/null; then - msg_warn "Could not enable $service (may not be installed yet)" - fi + if ! systemctl enable "$service" &>/dev/null; then + msg_warn "Could not enable $service (may not be installed yet)" + fi - if ! systemctl start "$service" &>/dev/null; then - msg_error "Failed to start $service" - systemctl status "$service" --no-pager - return 1 - fi + if ! systemctl start "$service" &>/dev/null; then + msg_error "Failed to start $service" + systemctl status "$service" --no-pager + return 1 + fi - return 0 + return 0 } # ------------------------------------------------------------------------------ # Check if service is enabled # ------------------------------------------------------------------------------ is_service_enabled() { - local service="$1" - systemctl is-enabled --quiet "$service" 2>/dev/null + local service="$1" + systemctl is-enabled --quiet "$service" 2>/dev/null } # ------------------------------------------------------------------------------ # Check if service is running # ------------------------------------------------------------------------------ is_service_running() { - local service="$1" - systemctl is-active --quiet "$service" 2>/dev/null + local service="$1" + systemctl is-active --quiet "$service" 2>/dev/null } # ------------------------------------------------------------------------------ # Extract version from JSON (GitHub releases) # ------------------------------------------------------------------------------ extract_version_from_json() { - local json="$1" - local field="${2:-tag_name}" - local strip_v="${3:-true}" + local json="$1" + local field="${2:-tag_name}" + local strip_v="${3:-true}" - ensure_dependencies jq + ensure_dependencies jq - local version - version=$(echo "$json" | jq -r ".${field} // empty") + local version + version=$(echo "$json" | jq -r ".${field} // empty") - if [[ -z "$version" ]]; then - return 1 - fi + if [[ -z "$version" ]]; then + return 1 + fi - if [[ "$strip_v" == "true" ]]; then - echo "${version#v}" - else - echo "$version" - fi + if [[ "$strip_v" == "true" ]]; then + echo "${version#v}" + else + echo "$version" + fi } # ------------------------------------------------------------------------------ # Get latest GitHub release version # ------------------------------------------------------------------------------ get_latest_github_release() { - local repo="$1" - local strip_v="${2:-true}" - local temp_file=$(mktemp) + local repo="$1" + local strip_v="${2:-true}" + local temp_file=$(mktemp) - if ! github_api_call "https://api.github.com/repos/${repo}/releases/latest" "$temp_file"; then - rm -f "$temp_file" - return 1 - fi - - local version - version=$(extract_version_from_json "$(cat "$temp_file")" "tag_name" "$strip_v") + if ! github_api_call "https://api.github.com/repos/${repo}/releases/latest" "$temp_file"; then rm -f "$temp_file" + return 1 + fi - if [[ -z "$version" ]]; then - return 1 - fi + local version + version=$(extract_version_from_json "$(cat "$temp_file")" "tag_name" "$strip_v") + rm -f "$temp_file" - echo "$version" + if [[ -z "$version" ]]; then + return 1 + fi + + echo "$version" } # ------------------------------------------------------------------------------ # Debug logging (only if DEBUG=1) # ------------------------------------------------------------------------------ debug_log() { - [[ "${DEBUG:-0}" == "1" ]] && echo "[DEBUG] $*" >&2 + [[ "${DEBUG:-0}" == "1" ]] && echo "[DEBUG] $*" >&2 } # ------------------------------------------------------------------------------ # Performance timing helper # ------------------------------------------------------------------------------ start_timer() { - echo $(date +%s) + echo $(date +%s) } end_timer() { - local start_time="$1" - local label="${2:-Operation}" - local end_time=$(date +%s) - local duration=$((end_time - start_time)) - debug_log "$label took ${duration}s" + local start_time="$1" + local label="${2:-Operation}" + local end_time=$(date +%s) + local duration=$((end_time - start_time)) + debug_log "$label took ${duration}s" } # ------------------------------------------------------------------------------ # GPG key fingerprint verification # ------------------------------------------------------------------------------ verify_gpg_fingerprint() { - local key_file="$1" - local expected_fingerprint="$2" + local key_file="$1" + local expected_fingerprint="$2" - local actual_fingerprint - actual_fingerprint=$(gpg --show-keys --with-fingerprint --with-colons "$key_file" 2>&1 | grep -m1 '^fpr:' | cut -d: -f10) + local actual_fingerprint + actual_fingerprint=$(gpg --show-keys --with-fingerprint --with-colons "$key_file" 2>&1 | grep -m1 '^fpr:' | cut -d: -f10) - if [[ "$actual_fingerprint" == "$expected_fingerprint" ]]; then - return 0 - fi + if [[ "$actual_fingerprint" == "$expected_fingerprint" ]]; then + return 0 + fi - msg_error "GPG fingerprint mismatch! Expected: $expected_fingerprint, Got: $actual_fingerprint" - return 1 + msg_error "GPG fingerprint mismatch! Expected: $expected_fingerprint, Got: $actual_fingerprint" + return 1 } # ============================================================================== @@ -763,101 +766,101 @@ verify_gpg_fingerprint() { # - Does not support pre-releases # ------------------------------------------------------------------------------ check_for_gh_release() { - local app="$1" - local source="$2" - local pinned_version_in="${3:-}" # optional - local app_lc="${app,,}" - local current_file="$HOME/.${app_lc}" + local app="$1" + local source="$2" + local pinned_version_in="${3:-}" # optional + local app_lc="${app,,}" + local current_file="$HOME/.${app_lc}" - msg_info "Checking for update: ${app}" + msg_info "Checking for update: ${app}" - # DNS check - if ! getent hosts api.github.com >/dev/null 2>&1; then - msg_error "Network error: cannot resolve api.github.com" - return 1 + # DNS check + if ! getent hosts api.github.com >/dev/null 2>&1; then + msg_error "Network error: cannot resolve api.github.com" + return 1 + fi + + ensure_dependencies jq + + # Fetch releases and exclude drafts/prereleases + local releases_json + releases_json=$(curl -fsSL --max-time 20 \ + -H 'Accept: application/vnd.github+json' \ + -H 'X-GitHub-Api-Version: 2022-11-28' \ + "https://api.github.com/repos/${source}/releases") || { + msg_error "Unable to fetch releases for ${app}" + return 1 + } + + mapfile -t raw_tags < <(jq -r '.[] | select(.draft==false and .prerelease==false) | .tag_name' <<<"$releases_json") + if ((${#raw_tags[@]} == 0)); then + msg_error "No stable releases found for ${app}" + return 1 + fi + + local clean_tags=() + for t in "${raw_tags[@]}"; do + clean_tags+=("${t#v}") + done + + local latest_raw="${raw_tags[0]}" + local latest_clean="${clean_tags[0]}" + + # current installed (stored without v) + local current="" + if [[ -f "$current_file" ]]; then + current="$(<"$current_file")" + else + # Migration: search for any /opt/*_version.txt + local legacy_files + mapfile -t legacy_files < <(find /opt -maxdepth 1 -type f -name "*_version.txt" 2>/dev/null) + if ((${#legacy_files[@]} == 1)); then + current="$(<"${legacy_files[0]}")" + echo "${current#v}" >"$current_file" + rm -f "${legacy_files[0]}" fi + fi + current="${current#v}" - ensure_dependencies jq - - # Fetch releases and exclude drafts/prereleases - local releases_json - releases_json=$(curl -fsSL --max-time 20 \ - -H 'Accept: application/vnd.github+json' \ - -H 'X-GitHub-Api-Version: 2022-11-28' \ - "https://api.github.com/repos/${source}/releases") || { - msg_error "Unable to fetch releases for ${app}" - return 1 - } - - mapfile -t raw_tags < <(jq -r '.[] | select(.draft==false and .prerelease==false) | .tag_name' <<<"$releases_json") - if ((${#raw_tags[@]} == 0)); then - msg_error "No stable releases found for ${app}" - return 1 - fi - - local clean_tags=() - for t in "${raw_tags[@]}"; do - clean_tags+=("${t#v}") + # Pinned version handling + if [[ -n "$pinned_version_in" ]]; then + local pin_clean="${pinned_version_in#v}" + local match_raw="" + for i in "${!clean_tags[@]}"; do + if [[ "${clean_tags[$i]}" == "$pin_clean" ]]; then + match_raw="${raw_tags[$i]}" + break + fi done - local latest_raw="${raw_tags[0]}" - local latest_clean="${clean_tags[0]}" + if [[ -z "$match_raw" ]]; then + msg_error "Pinned version ${pinned_version_in} not found upstream" + return 1 + fi - # current installed (stored without v) - local current="" - if [[ -f "$current_file" ]]; then - current="$(<"$current_file")" + if [[ "$current" != "$pin_clean" ]]; then + msg_info "${app} pinned to ${pinned_version_in} (installed ${current:-none}) → update required" + CHECK_UPDATE_RELEASE="$match_raw" + return 0 + fi + + if [[ "$pin_clean" == "$latest_clean" ]]; then + msg_ok "${app} pinned to ${pinned_version_in} (up to date)" else - # Migration: search for any /opt/*_version.txt - local legacy_files - mapfile -t legacy_files < <(find /opt -maxdepth 1 -type f -name "*_version.txt" 2>/dev/null) - if ((${#legacy_files[@]} == 1)); then - current="$(<"${legacy_files[0]}")" - echo "${current#v}" >"$current_file" - rm -f "${legacy_files[0]}" - fi + msg_ok "${app} pinned to ${pinned_version_in} (already installed, upstream ${latest_raw})" fi - current="${current#v}" - - # Pinned version handling - if [[ -n "$pinned_version_in" ]]; then - local pin_clean="${pinned_version_in#v}" - local match_raw="" - for i in "${!clean_tags[@]}"; do - if [[ "${clean_tags[$i]}" == "$pin_clean" ]]; then - match_raw="${raw_tags[$i]}" - break - fi - done - - if [[ -z "$match_raw" ]]; then - msg_error "Pinned version ${pinned_version_in} not found upstream" - return 1 - fi - - if [[ "$current" != "$pin_clean" ]]; then - msg_info "${app} pinned to ${pinned_version_in} (installed ${current:-none}) → update required" - CHECK_UPDATE_RELEASE="$match_raw" - return 0 - fi - - if [[ "$pin_clean" == "$latest_clean" ]]; then - msg_ok "${app} pinned to ${pinned_version_in} (up to date)" - else - msg_ok "${app} pinned to ${pinned_version_in} (already installed, upstream ${latest_raw})" - fi - return 1 - fi - - # No pinning → use latest - if [[ -z "$current" || "$current" != "$latest_clean" ]]; then - CHECK_UPDATE_RELEASE="$latest_raw" - msg_info "New release available: ${latest_raw} (current: v${current:-none})" - return 0 - fi - - msg_ok "${app} is up to date (${latest_raw})" return 1 + fi + + # No pinning → use latest + if [[ -z "$current" || "$current" != "$latest_clean" ]]; then + CHECK_UPDATE_RELEASE="$latest_raw" + msg_info "New release available: ${latest_raw} (current: v${current:-none})" + return 0 + fi + + msg_ok "${app} is up to date (${latest_raw})" + return 1 } # ------------------------------------------------------------------------------ @@ -870,26 +873,26 @@ check_for_gh_release() { # APP - Application name (default: $APPLICATION variable) # ------------------------------------------------------------------------------ create_self_signed_cert() { - local APP_NAME="${1:-${APPLICATION}}" - local CERT_DIR="/etc/ssl/${APP_NAME}" - local CERT_KEY="${CERT_DIR}/${APP_NAME}.key" - local CERT_CRT="${CERT_DIR}/${APP_NAME}.crt" + local APP_NAME="${1:-${APPLICATION}}" + local CERT_DIR="/etc/ssl/${APP_NAME}" + local CERT_KEY="${CERT_DIR}/${APP_NAME}.key" + local CERT_CRT="${CERT_DIR}/${APP_NAME}.crt" - if [[ -f "$CERT_CRT" && -f "$CERT_KEY" ]]; then - return 0 - fi + if [[ -f "$CERT_CRT" && -f "$CERT_KEY" ]]; then + return 0 + fi - $STD apt update - $STD apt install -y openssl + $STD apt update + $STD apt install -y openssl - mkdir -p "$CERT_DIR" - $STD openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 \ - -subj "/C=US/ST=State/L=City/O=Organization/CN=${APP_NAME}" \ - -keyout "$CERT_KEY" \ - -out "$CERT_CRT" + mkdir -p "$CERT_DIR" + $STD openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 \ + -subj "/C=US/ST=State/L=City/O=Organization/CN=${APP_NAME}" \ + -keyout "$CERT_KEY" \ + -out "$CERT_CRT" - chmod 600 "$CERT_KEY" - chmod 644 "$CERT_CRT" + chmod 600 "$CERT_KEY" + chmod 644 "$CERT_CRT" } # ------------------------------------------------------------------------------ @@ -901,28 +904,28 @@ create_self_signed_cert() { # ------------------------------------------------------------------------------ function download_with_progress() { - local url="$1" - local output="$2" - if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi + local url="$1" + local output="$2" + if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi - ensure_dependencies pv - set -o pipefail + ensure_dependencies pv + set -o pipefail - # Content-Length aus HTTP-Header holen - local content_length - content_length=$(curl -fsSLI "$url" | awk '/Content-Length/ {print $2}' | tr -d '\r' || true) + # Content-Length aus HTTP-Header holen + local content_length + content_length=$(curl -fsSLI "$url" | awk '/Content-Length/ {print $2}' | tr -d '\r' || true) - if [[ -z "$content_length" ]]; then - if ! curl -fL# -o "$output" "$url"; then - msg_error "Download failed" - return 1 - fi - else - if ! curl -fsSL "$url" | pv -s "$content_length" >"$output"; then - msg_error "Download failed" - return 1 - fi + if [[ -z "$content_length" ]]; then + if ! curl -fL# -o "$output" "$url"; then + msg_error "Download failed" + return 1 fi + else + if ! curl -fsSL "$url" | pv -s "$content_length" >"$output"; then + msg_error "Download failed" + return 1 + fi + fi } # ------------------------------------------------------------------------------ @@ -933,12 +936,12 @@ function download_with_progress() { # ------------------------------------------------------------------------------ function ensure_usr_local_bin_persist() { - local PROFILE_FILE="/etc/profile.d/custom_path.sh" + local PROFILE_FILE="/etc/profile.d/custom_path.sh" - if [[ ! -f "$PROFILE_FILE" ]] && ! command -v pveversion &>/dev/null; then - echo 'export PATH="/usr/local/bin:$PATH"' >"$PROFILE_FILE" - chmod +x "$PROFILE_FILE" - fi + if [[ ! -f "$PROFILE_FILE" ]] && ! command -v pveversion &>/dev/null; then + echo 'export PATH="/usr/local/bin:$PATH"' >"$PROFILE_FILE" + chmod +x "$PROFILE_FILE" + fi } # ------------------------------------------------------------------------------ @@ -986,303 +989,303 @@ function ensure_usr_local_bin_persist() { # ------------------------------------------------------------------------------ function fetch_and_deploy_gh_release() { - local app="$1" - local repo="$2" - local mode="${3:-tarball}" # tarball | binary | prebuild | singlefile - local version="${4:-latest}" - local target="${5:-/opt/$app}" - local asset_pattern="${6:-}" + local app="$1" + local repo="$2" + local mode="${3:-tarball}" # tarball | binary | prebuild | singlefile + local version="${4:-latest}" + local target="${5:-/opt/$app}" + local asset_pattern="${6:-}" - local app_lc=$(echo "${app,,}" | tr -d ' ') - local version_file="$HOME/.${app_lc}" + local app_lc=$(echo "${app,,}" | tr -d ' ') + local version_file="$HOME/.${app_lc}" - local api_timeout="--connect-timeout 10 --max-time 60" - local download_timeout="--connect-timeout 15 --max-time 900" + local api_timeout="--connect-timeout 10 --max-time 60" + local download_timeout="--connect-timeout 15 --max-time 900" - local current_version="" - [[ -f "$version_file" ]] && current_version=$(<"$version_file") + local current_version="" + [[ -f "$version_file" ]] && current_version=$(<"$version_file") - ensure_dependencies jq + ensure_dependencies jq - local api_url="https://api.github.com/repos/$repo/releases" - [[ "$version" != "latest" ]] && api_url="$api_url/tags/$version" || api_url="$api_url/latest" - local header=() - [[ -n "${GITHUB_TOKEN:-}" ]] && header=(-H "Authorization: token $GITHUB_TOKEN") + local api_url="https://api.github.com/repos/$repo/releases" + [[ "$version" != "latest" ]] && api_url="$api_url/tags/$version" || api_url="$api_url/latest" + local header=() + [[ -n "${GITHUB_TOKEN:-}" ]] && header=(-H "Authorization: token $GITHUB_TOKEN") - # dns pre check - local gh_host - gh_host=$(awk -F/ '{print $3}' <<<"$api_url") - if ! getent hosts "$gh_host" &>/dev/null; then - msg_error "DNS resolution failed for $gh_host – check /etc/resolv.conf or networking" - return 1 - fi + # dns pre check + local gh_host + gh_host=$(awk -F/ '{print $3}' <<<"$api_url") + if ! getent hosts "$gh_host" &>/dev/null; then + msg_error "DNS resolution failed for $gh_host – check /etc/resolv.conf or networking" + return 1 + fi - local max_retries=3 retry_delay=2 attempt=1 success=false resp http_code + local max_retries=3 retry_delay=2 attempt=1 success=false resp http_code - while ((attempt <= max_retries)); do - resp=$(curl $api_timeout -fsSL -w "%{http_code}" -o /tmp/gh_rel.json "${header[@]}" "$api_url") && success=true && break - sleep "$retry_delay" - ((attempt++)) - done + while ((attempt <= max_retries)); do + resp=$(curl $api_timeout -fsSL -w "%{http_code}" -o /tmp/gh_rel.json "${header[@]}" "$api_url") && success=true && break + sleep "$retry_delay" + ((attempt++)) + done - if ! $success; then - msg_error "Failed to fetch release metadata from $api_url after $max_retries attempts" - return 1 - fi + if ! $success; then + msg_error "Failed to fetch release metadata from $api_url after $max_retries attempts" + return 1 + fi - http_code="${resp:(-3)}" - [[ "$http_code" != "200" ]] && { - msg_error "GitHub API returned HTTP $http_code" - return 1 + http_code="${resp:(-3)}" + [[ "$http_code" != "200" ]] && { + msg_error "GitHub API returned HTTP $http_code" + return 1 + } + + local json tag_name + json=$(/dev/null || uname -m) + [[ "$arch" == "x86_64" ]] && arch="amd64" + [[ "$arch" == "aarch64" ]] && arch="arm64" - ### Tarball Mode ### - if [[ "$mode" == "tarball" || "$mode" == "source" ]]; then - url=$(echo "$json" | jq -r '.tarball_url // empty') - [[ -z "$url" ]] && url="https://github.com/$repo/archive/refs/tags/v$version.tar.gz" - filename="${app_lc}-${version}.tar.gz" + local assets url_match="" + assets=$(echo "$json" | jq -r '.assets[].browser_download_url') - curl $download_timeout -fsSL -o "$tmpdir/$filename" "$url" || { - msg_error "Download failed: $url" - rm -rf "$tmpdir" - return 1 - } + # If explicit filename pattern is provided (param $6), match that first + if [[ -n "$asset_pattern" ]]; then + for u in $assets; do + case "${u##*/}" in + $asset_pattern) + url_match="$u" + break + ;; + esac + done + fi - mkdir -p "$target" - if [[ "${CLEAN_INSTALL:-0}" == "1" ]]; then - rm -rf "${target:?}/"* + # If no match via explicit pattern, fall back to architecture heuristic + if [[ -z "$url_match" ]]; then + for u in $assets; do + if [[ "$u" =~ ($arch|amd64|x86_64|aarch64|arm64).*\.deb$ ]]; then + url_match="$u" + break fi + done + fi - tar -xzf "$tmpdir/$filename" -C "$tmpdir" - local unpack_dir - unpack_dir=$(find "$tmpdir" -mindepth 1 -maxdepth 1 -type d | head -n1) + # Fallback: any .deb file + if [[ -z "$url_match" ]]; then + for u in $assets; do + [[ "$u" =~ \.deb$ ]] && url_match="$u" && break + done + fi - shopt -s dotglob nullglob - cp -r "$unpack_dir"/* "$target/" - shopt -u dotglob nullglob + if [[ -z "$url_match" ]]; then + msg_error "No suitable .deb asset found for $app" + rm -rf "$tmpdir" + return 1 + fi - ### Binary Mode ### - elif [[ "$mode" == "binary" ]]; then - local arch - arch=$(dpkg --print-architecture 2>/dev/null || uname -m) - [[ "$arch" == "x86_64" ]] && arch="amd64" - [[ "$arch" == "aarch64" ]] && arch="arm64" + filename="${url_match##*/}" + curl $download_timeout -fsSL -o "$tmpdir/$filename" "$url_match" || { + msg_error "Download failed: $url_match" + rm -rf "$tmpdir" + return 1 + } - local assets url_match="" - assets=$(echo "$json" | jq -r '.assets[].browser_download_url') - - # If explicit filename pattern is provided (param $6), match that first - if [[ -n "$asset_pattern" ]]; then - for u in $assets; do - case "${u##*/}" in - $asset_pattern) - url_match="$u" - break - ;; - esac - done - fi - - # If no match via explicit pattern, fall back to architecture heuristic - if [[ -z "$url_match" ]]; then - for u in $assets; do - if [[ "$u" =~ ($arch|amd64|x86_64|aarch64|arm64).*\.deb$ ]]; then - url_match="$u" - break - fi - done - fi - - # Fallback: any .deb file - if [[ -z "$url_match" ]]; then - for u in $assets; do - [[ "$u" =~ \.deb$ ]] && url_match="$u" && break - done - fi - - if [[ -z "$url_match" ]]; then - msg_error "No suitable .deb asset found for $app" - rm -rf "$tmpdir" - return 1 - fi - - filename="${url_match##*/}" - curl $download_timeout -fsSL -o "$tmpdir/$filename" "$url_match" || { - msg_error "Download failed: $url_match" - rm -rf "$tmpdir" - return 1 - } - - chmod 644 "$tmpdir/$filename" - $STD apt install -y "$tmpdir/$filename" || { - $STD dpkg -i "$tmpdir/$filename" || { - msg_error "Both apt and dpkg installation failed" - rm -rf "$tmpdir" - return 1 - } - } - - ### Prebuild Mode ### - elif [[ "$mode" == "prebuild" ]]; then - local pattern="${6%\"}" - pattern="${pattern#\"}" - [[ -z "$pattern" ]] && { - msg_error "Mode 'prebuild' requires 6th parameter (asset filename pattern)" - rm -rf "$tmpdir" - return 1 - } - - local asset_url="" - for u in $(echo "$json" | jq -r '.assets[].browser_download_url'); do - filename_candidate="${u##*/}" - case "$filename_candidate" in - $pattern) - asset_url="$u" - break - ;; - esac - done - - [[ -z "$asset_url" ]] && { - msg_error "No asset matching '$pattern' found" - rm -rf "$tmpdir" - return 1 - } - - filename="${asset_url##*/}" - curl $download_timeout -fsSL -o "$tmpdir/$filename" "$asset_url" || { - msg_error "Download failed: $asset_url" - rm -rf "$tmpdir" - return 1 - } - - local unpack_tmp - unpack_tmp=$(mktemp -d) - mkdir -p "$target" - if [[ "${CLEAN_INSTALL:-0}" == "1" ]]; then - rm -rf "${target:?}/"* - fi - - if [[ "$filename" == *.zip ]]; then - ensure_dependencies unzip - unzip -q "$tmpdir/$filename" -d "$unpack_tmp" - elif [[ "$filename" == *.tar.* || "$filename" == *.tgz ]]; then - tar -xf "$tmpdir/$filename" -C "$unpack_tmp" - else - msg_error "Unsupported archive format: $filename" - rm -rf "$tmpdir" "$unpack_tmp" - return 1 - fi - - local top_dirs - top_dirs=$(find "$unpack_tmp" -mindepth 1 -maxdepth 1 -type d | wc -l) - local top_entries inner_dir - top_entries=$(find "$unpack_tmp" -mindepth 1 -maxdepth 1) - if [[ "$(echo "$top_entries" | wc -l)" -eq 1 && -d "$top_entries" ]]; then - # Strip leading folder - inner_dir="$top_entries" - shopt -s dotglob nullglob - if compgen -G "$inner_dir/*" >/dev/null; then - cp -r "$inner_dir"/* "$target/" || { - msg_error "Failed to copy contents from $inner_dir to $target" - rm -rf "$tmpdir" "$unpack_tmp" - return 1 - } - else - msg_error "Inner directory is empty: $inner_dir" - rm -rf "$tmpdir" "$unpack_tmp" - return 1 - fi - shopt -u dotglob nullglob - else - # Copy all contents - shopt -s dotglob nullglob - if compgen -G "$unpack_tmp/*" >/dev/null; then - cp -r "$unpack_tmp"/* "$target/" || { - msg_error "Failed to copy contents to $target" - rm -rf "$tmpdir" "$unpack_tmp" - return 1 - } - else - msg_error "Unpacked archive is empty" - rm -rf "$tmpdir" "$unpack_tmp" - return 1 - fi - shopt -u dotglob nullglob - fi - - ### Singlefile Mode ### - elif [[ "$mode" == "singlefile" ]]; then - local pattern="${6%\"}" - pattern="${pattern#\"}" - [[ -z "$pattern" ]] && { - msg_error "Mode 'singlefile' requires 6th parameter (asset filename pattern)" - rm -rf "$tmpdir" - return 1 - } - - local asset_url="" - for u in $(echo "$json" | jq -r '.assets[].browser_download_url'); do - filename_candidate="${u##*/}" - case "$filename_candidate" in - $pattern) - asset_url="$u" - break - ;; - esac - done - - [[ -z "$asset_url" ]] && { - msg_error "No asset matching '$pattern' found" - rm -rf "$tmpdir" - return 1 - } - - filename="${asset_url##*/}" - mkdir -p "$target" - - local use_filename="${USE_ORIGINAL_FILENAME:-false}" - local target_file="$app" - [[ "$use_filename" == "true" ]] && target_file="$filename" - - curl $download_timeout -fsSL -o "$target/$target_file" "$asset_url" || { - msg_error "Download failed: $asset_url" - rm -rf "$tmpdir" - return 1 - } - - if [[ "$target_file" != *.jar && -f "$target/$target_file" ]]; then - chmod +x "$target/$target_file" - fi - - else - msg_error "Unknown mode: $mode" + chmod 644 "$tmpdir/$filename" + $STD apt install -y "$tmpdir/$filename" || { + $STD dpkg -i "$tmpdir/$filename" || { + msg_error "Both apt and dpkg installation failed" rm -rf "$tmpdir" return 1 + } + } + + ### Prebuild Mode ### + elif [[ "$mode" == "prebuild" ]]; then + local pattern="${6%\"}" + pattern="${pattern#\"}" + [[ -z "$pattern" ]] && { + msg_error "Mode 'prebuild' requires 6th parameter (asset filename pattern)" + rm -rf "$tmpdir" + return 1 + } + + local asset_url="" + for u in $(echo "$json" | jq -r '.assets[].browser_download_url'); do + filename_candidate="${u##*/}" + case "$filename_candidate" in + $pattern) + asset_url="$u" + break + ;; + esac + done + + [[ -z "$asset_url" ]] && { + msg_error "No asset matching '$pattern' found" + rm -rf "$tmpdir" + return 1 + } + + filename="${asset_url##*/}" + curl $download_timeout -fsSL -o "$tmpdir/$filename" "$asset_url" || { + msg_error "Download failed: $asset_url" + rm -rf "$tmpdir" + return 1 + } + + local unpack_tmp + unpack_tmp=$(mktemp -d) + mkdir -p "$target" + if [[ "${CLEAN_INSTALL:-0}" == "1" ]]; then + rm -rf "${target:?}/"* fi - echo "$version" >"$version_file" - msg_ok "Deployed: $app ($version)" + if [[ "$filename" == *.zip ]]; then + ensure_dependencies unzip + unzip -q "$tmpdir/$filename" -d "$unpack_tmp" + elif [[ "$filename" == *.tar.* || "$filename" == *.tgz ]]; then + tar -xf "$tmpdir/$filename" -C "$unpack_tmp" + else + msg_error "Unsupported archive format: $filename" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + fi + + local top_dirs + top_dirs=$(find "$unpack_tmp" -mindepth 1 -maxdepth 1 -type d | wc -l) + local top_entries inner_dir + top_entries=$(find "$unpack_tmp" -mindepth 1 -maxdepth 1) + if [[ "$(echo "$top_entries" | wc -l)" -eq 1 && -d "$top_entries" ]]; then + # Strip leading folder + inner_dir="$top_entries" + shopt -s dotglob nullglob + if compgen -G "$inner_dir/*" >/dev/null; then + cp -r "$inner_dir"/* "$target/" || { + msg_error "Failed to copy contents from $inner_dir to $target" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + } + else + msg_error "Inner directory is empty: $inner_dir" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + fi + shopt -u dotglob nullglob + else + # Copy all contents + shopt -s dotglob nullglob + if compgen -G "$unpack_tmp/*" >/dev/null; then + cp -r "$unpack_tmp"/* "$target/" || { + msg_error "Failed to copy contents to $target" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + } + else + msg_error "Unpacked archive is empty" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + fi + shopt -u dotglob nullglob + fi + + ### Singlefile Mode ### + elif [[ "$mode" == "singlefile" ]]; then + local pattern="${6%\"}" + pattern="${pattern#\"}" + [[ -z "$pattern" ]] && { + msg_error "Mode 'singlefile' requires 6th parameter (asset filename pattern)" + rm -rf "$tmpdir" + return 1 + } + + local asset_url="" + for u in $(echo "$json" | jq -r '.assets[].browser_download_url'); do + filename_candidate="${u##*/}" + case "$filename_candidate" in + $pattern) + asset_url="$u" + break + ;; + esac + done + + [[ -z "$asset_url" ]] && { + msg_error "No asset matching '$pattern' found" + rm -rf "$tmpdir" + return 1 + } + + filename="${asset_url##*/}" + mkdir -p "$target" + + local use_filename="${USE_ORIGINAL_FILENAME:-false}" + local target_file="$app" + [[ "$use_filename" == "true" ]] && target_file="$filename" + + curl $download_timeout -fsSL -o "$target/$target_file" "$asset_url" || { + msg_error "Download failed: $asset_url" + rm -rf "$tmpdir" + return 1 + } + + if [[ "$target_file" != *.jar && -f "$target/$target_file" ]]; then + chmod +x "$target/$target_file" + fi + + else + msg_error "Unknown mode: $mode" rm -rf "$tmpdir" + return 1 + fi + + echo "$version" >"$version_file" + msg_ok "Deployed: $app ($version)" + rm -rf "$tmpdir" } # ------------------------------------------------------------------------------ @@ -1293,40 +1296,40 @@ function fetch_and_deploy_gh_release() { # ------------------------------------------------------------------------------ function import_local_ip() { - local IP_FILE="/run/local-ip.env" - if [[ -f "$IP_FILE" ]]; then - # shellcheck disable=SC1090 - source "$IP_FILE" - fi + local IP_FILE="/run/local-ip.env" + if [[ -f "$IP_FILE" ]]; then + # shellcheck disable=SC1090 + source "$IP_FILE" + fi - if [[ -z "${LOCAL_IP:-}" ]]; then - get_current_ip() { - local targets=("8.8.8.8" "1.1.1.1" "192.168.1.1" "10.0.0.1" "172.16.0.1" "default") - local ip + if [[ -z "${LOCAL_IP:-}" ]]; then + get_current_ip() { + local targets=("8.8.8.8" "1.1.1.1" "192.168.1.1" "10.0.0.1" "172.16.0.1" "default") + local ip - for target in "${targets[@]}"; do - if [[ "$target" == "default" ]]; then - ip=$(ip route get 1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') - else - ip=$(ip route get "$target" 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') - fi - if [[ -n "$ip" ]]; then - echo "$ip" - return 0 - fi - done - - return 1 - } - - LOCAL_IP="$(get_current_ip || true)" - if [[ -z "$LOCAL_IP" ]]; then - msg_error "Could not determine LOCAL_IP" - return 1 + for target in "${targets[@]}"; do + if [[ "$target" == "default" ]]; then + ip=$(ip route get 1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') + else + ip=$(ip route get "$target" 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') fi - fi + if [[ -n "$ip" ]]; then + echo "$ip" + return 0 + fi + done - export LOCAL_IP + return 1 + } + + LOCAL_IP="$(get_current_ip || true)" + if [[ -z "$LOCAL_IP" ]]; then + msg_error "Could not determine LOCAL_IP" + return 1 + fi + fi + + export LOCAL_IP } # ------------------------------------------------------------------------------ @@ -1338,29 +1341,29 @@ function import_local_ip() { # ------------------------------------------------------------------------------ function setup_adminer() { - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "adminer") + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "adminer") - if grep -qi alpine /etc/os-release; then - msg_info "Installing Adminer (Alpine)" - mkdir -p /var/www/localhost/htdocs/adminer - curl -fsSL https://github.com/vrana/adminer/releases/latest/download/adminer.php \ - -o /var/www/localhost/htdocs/adminer/index.php || { - msg_error "Failed to download Adminer" - return 1 - } - cache_installed_version "adminer" "latest-alpine" - msg_ok "Installed Adminer (Alpine)" - else - msg_info "Installing Adminer (Debian/Ubuntu)" - ensure_dependencies adminer - $STD a2enconf adminer - $STD systemctl reload apache2 - local VERSION - VERSION=$(dpkg -s adminer 2>/dev/null | grep '^Version:' | awk '{print $2}') - cache_installed_version "adminer" "${VERSION:-unknown}" - msg_ok "Installed Adminer (Debian/Ubuntu)" - fi + if grep -qi alpine /etc/os-release; then + msg_info "Installing Adminer (Alpine)" + mkdir -p /var/www/localhost/htdocs/adminer + curl -fsSL https://github.com/vrana/adminer/releases/latest/download/adminer.php \ + -o /var/www/localhost/htdocs/adminer/index.php || { + msg_error "Failed to download Adminer" + return 1 + } + cache_installed_version "adminer" "latest-alpine" + msg_ok "Installed Adminer (Alpine)" + else + msg_info "Installing Adminer (Debian/Ubuntu)" + ensure_dependencies adminer + $STD a2enconf adminer + $STD systemctl reload apache2 + local VERSION + VERSION=$(dpkg -s adminer 2>/dev/null | grep '^Version:' | awk '{print $2}') + cache_installed_version "adminer" "${VERSION:-unknown}" + msg_ok "Installed Adminer (Debian/Ubuntu)" + fi } # ------------------------------------------------------------------------------ @@ -1372,46 +1375,46 @@ function setup_adminer() { # ------------------------------------------------------------------------------ function setup_composer() { - local COMPOSER_BIN="/usr/local/bin/composer" - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "composer") - export COMPOSER_ALLOW_SUPERUSER=1 + local COMPOSER_BIN="/usr/local/bin/composer" + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "composer") + export COMPOSER_ALLOW_SUPERUSER=1 - for old in /usr/bin/composer /bin/composer /root/.composer/vendor/bin/composer; do - [[ -e "$old" && "$old" != "$COMPOSER_BIN" ]] && rm -f "$old" - done + for old in /usr/bin/composer /bin/composer /root/.composer/vendor/bin/composer; do + [[ -e "$old" && "$old" != "$COMPOSER_BIN" ]] && rm -f "$old" + done - ensure_usr_local_bin_persist - export PATH="/usr/local/bin:$PATH" + ensure_usr_local_bin_persist + export PATH="/usr/local/bin:$PATH" - if [[ -x "$COMPOSER_BIN" ]]; then - local CURRENT_VERSION - CURRENT_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}') - msg_info "Updating Composer from $CURRENT_VERSION to latest" - else - msg_info "Installing Composer" - fi + if [[ -x "$COMPOSER_BIN" ]]; then + local CURRENT_VERSION + CURRENT_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}') + msg_info "Updating Composer from $CURRENT_VERSION to latest" + else + msg_info "Installing Composer" + fi - curl -fsSL https://getcomposer.org/installer -o /tmp/composer-setup.php || { - msg_error "Failed to download Composer installer" - return 1 - } + curl -fsSL https://getcomposer.org/installer -o /tmp/composer-setup.php || { + msg_error "Failed to download Composer installer" + return 1 + } - $STD php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer - rm -f /tmp/composer-setup.php + $STD php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer + rm -f /tmp/composer-setup.php - if [[ ! -x "$COMPOSER_BIN" ]]; then - msg_error "Composer installation failed" - return 1 - fi + if [[ ! -x "$COMPOSER_BIN" ]]; then + msg_error "Composer installation failed" + return 1 + fi - chmod +x "$COMPOSER_BIN" - $STD "$COMPOSER_BIN" self-update --no-interaction || true + chmod +x "$COMPOSER_BIN" + $STD "$COMPOSER_BIN" self-update --no-interaction || true - local FINAL_VERSION - FINAL_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}') - cache_installed_version "composer" "$FINAL_VERSION" - msg_ok "Installed Composer $FINAL_VERSION" + local FINAL_VERSION + FINAL_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}') + cache_installed_version "composer" "$FINAL_VERSION" + msg_ok "Installed Composer $FINAL_VERSION" } # ------------------------------------------------------------------------------ @@ -1433,147 +1436,147 @@ function setup_composer() { # ------------------------------------------------------------------------------ function setup_ffmpeg() { - local TMP_DIR=$(mktemp -d) - local GITHUB_REPO="FFmpeg/FFmpeg" - local VERSION="${FFMPEG_VERSION:-latest}" - local TYPE="${FFMPEG_TYPE:-full}" - local BIN_PATH="/usr/local/bin/ffmpeg" - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "ffmpeg") + local TMP_DIR=$(mktemp -d) + local GITHUB_REPO="FFmpeg/FFmpeg" + local VERSION="${FFMPEG_VERSION:-latest}" + local TYPE="${FFMPEG_TYPE:-full}" + local BIN_PATH="/usr/local/bin/ffmpeg" + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "ffmpeg") - # Binary fallback mode - if [[ "$TYPE" == "binary" ]]; then - msg_info "Installing FFmpeg (static binary)" - curl -fsSL https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz -o "$TMP_DIR/ffmpeg.tar.xz" || { - msg_error "Failed to download FFmpeg binary" - rm -rf "$TMP_DIR" - return 1 - } - tar -xf "$TMP_DIR/ffmpeg.tar.xz" -C "$TMP_DIR" - local EXTRACTED_DIR - EXTRACTED_DIR=$(find "$TMP_DIR" -maxdepth 1 -type d -name "ffmpeg-*") - cp "$EXTRACTED_DIR/ffmpeg" "$BIN_PATH" - cp "$EXTRACTED_DIR/ffprobe" /usr/local/bin/ffprobe - chmod +x "$BIN_PATH" /usr/local/bin/ffprobe - local FINAL_VERSION=$($BIN_PATH -version | head -n1 | awk '{print $3}') - rm -rf "$TMP_DIR" - cache_installed_version "ffmpeg" "$FINAL_VERSION" - ensure_usr_local_bin_persist - msg_ok "Installed FFmpeg binary $FINAL_VERSION" - return 0 - fi - - ensure_dependencies jq - - # Auto-detect latest stable version if none specified - if [[ "$VERSION" == "latest" || -z "$VERSION" ]]; then - VERSION=$(curl -fsSL "https://api.github.com/repos/${GITHUB_REPO}/tags" | - jq -r '.[].name' | - grep -E '^n[0-9]+\.[0-9]+\.[0-9]+$' | - sort -V | tail -n1) - fi - - if [[ -z "$VERSION" ]]; then - msg_error "Could not determine FFmpeg version" - rm -rf "$TMP_DIR" - return 1 - fi - - msg_info "Installing FFmpeg ${VERSION} ($TYPE)" - - # Dependency selection - local DEPS=(build-essential yasm nasm pkg-config) - case "$TYPE" in - minimal) - DEPS+=(libx264-dev libvpx-dev libmp3lame-dev) - ;; - medium) - DEPS+=(libx264-dev libvpx-dev libmp3lame-dev libfreetype6-dev libass-dev libopus-dev libvorbis-dev) - ;; - full) - DEPS+=( - libx264-dev libx265-dev libvpx-dev libmp3lame-dev - libfreetype6-dev libass-dev libopus-dev libvorbis-dev - libdav1d-dev libsvtav1-dev zlib1g-dev libnuma-dev - libva-dev libdrm-dev - ) - ;; - *) - msg_error "Invalid FFMPEG_TYPE: $TYPE" - rm -rf "$TMP_DIR" - return 1 - ;; - esac - - ensure_dependencies "${DEPS[@]}" - - curl -fsSL "https://github.com/${GITHUB_REPO}/archive/refs/tags/${VERSION}.tar.gz" -o "$TMP_DIR/ffmpeg.tar.gz" || { - msg_error "Failed to download FFmpeg source" - rm -rf "$TMP_DIR" - return 1 + # Binary fallback mode + if [[ "$TYPE" == "binary" ]]; then + msg_info "Installing FFmpeg (static binary)" + curl -fsSL https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz -o "$TMP_DIR/ffmpeg.tar.xz" || { + msg_error "Failed to download FFmpeg binary" + rm -rf "$TMP_DIR" + return 1 } - - tar -xzf "$TMP_DIR/ffmpeg.tar.gz" -C "$TMP_DIR" || { - msg_error "Failed to extract FFmpeg" - rm -rf "$TMP_DIR" - return 1 - } - - cd "$TMP_DIR/FFmpeg-"* || { - msg_error "Source extraction failed" - rm -rf "$TMP_DIR" - return 1 - } - - local args=( - --enable-gpl - --enable-shared - --enable-nonfree - --disable-static - --enable-libx264 - --enable-libvpx - --enable-libmp3lame - ) - - if [[ "$TYPE" != "minimal" ]]; then - args+=(--enable-libfreetype --enable-libass --enable-libopus --enable-libvorbis) - fi - - if [[ "$TYPE" == "full" ]]; then - args+=(--enable-libx265 --enable-libdav1d --enable-zlib) - args+=(--enable-vaapi --enable-libdrm) - fi - - if [[ ${#args[@]} -eq 0 ]]; then - msg_error "FFmpeg configure args array is empty" - rm -rf "$TMP_DIR" - return 1 - fi - - $STD ./configure "${args[@]}" - $STD make -j"$(nproc)" - $STD make install - echo "/usr/local/lib" >/etc/ld.so.conf.d/ffmpeg.conf - $STD ldconfig - - ldconfig -p | grep libavdevice >/dev/null || { - msg_error "libavdevice not registered with dynamic linker" - rm -rf "$TMP_DIR" - return 1 - } - - if ! command -v ffmpeg &>/dev/null; then - msg_error "FFmpeg installation failed" - rm -rf "$TMP_DIR" - return 1 - fi - - local FINAL_VERSION - FINAL_VERSION=$(ffmpeg -version | head -n1 | awk '{print $3}') + tar -xf "$TMP_DIR/ffmpeg.tar.xz" -C "$TMP_DIR" + local EXTRACTED_DIR + EXTRACTED_DIR=$(find "$TMP_DIR" -maxdepth 1 -type d -name "ffmpeg-*") + cp "$EXTRACTED_DIR/ffmpeg" "$BIN_PATH" + cp "$EXTRACTED_DIR/ffprobe" /usr/local/bin/ffprobe + chmod +x "$BIN_PATH" /usr/local/bin/ffprobe + local FINAL_VERSION=$($BIN_PATH -version | head -n1 | awk '{print $3}') rm -rf "$TMP_DIR" cache_installed_version "ffmpeg" "$FINAL_VERSION" ensure_usr_local_bin_persist - msg_ok "Installed FFmpeg $FINAL_VERSION" + msg_ok "Installed FFmpeg binary $FINAL_VERSION" + return 0 + fi + + ensure_dependencies jq + + # Auto-detect latest stable version if none specified + if [[ "$VERSION" == "latest" || -z "$VERSION" ]]; then + VERSION=$(curl -fsSL "https://api.github.com/repos/${GITHUB_REPO}/tags" | + jq -r '.[].name' | + grep -E '^n[0-9]+\.[0-9]+\.[0-9]+$' | + sort -V | tail -n1) + fi + + if [[ -z "$VERSION" ]]; then + msg_error "Could not determine FFmpeg version" + rm -rf "$TMP_DIR" + return 1 + fi + + msg_info "Installing FFmpeg ${VERSION} ($TYPE)" + + # Dependency selection + local DEPS=(build-essential yasm nasm pkg-config) + case "$TYPE" in + minimal) + DEPS+=(libx264-dev libvpx-dev libmp3lame-dev) + ;; + medium) + DEPS+=(libx264-dev libvpx-dev libmp3lame-dev libfreetype6-dev libass-dev libopus-dev libvorbis-dev) + ;; + full) + DEPS+=( + libx264-dev libx265-dev libvpx-dev libmp3lame-dev + libfreetype6-dev libass-dev libopus-dev libvorbis-dev + libdav1d-dev libsvtav1-dev zlib1g-dev libnuma-dev + libva-dev libdrm-dev + ) + ;; + *) + msg_error "Invalid FFMPEG_TYPE: $TYPE" + rm -rf "$TMP_DIR" + return 1 + ;; + esac + + ensure_dependencies "${DEPS[@]}" + + curl -fsSL "https://github.com/${GITHUB_REPO}/archive/refs/tags/${VERSION}.tar.gz" -o "$TMP_DIR/ffmpeg.tar.gz" || { + msg_error "Failed to download FFmpeg source" + rm -rf "$TMP_DIR" + return 1 + } + + tar -xzf "$TMP_DIR/ffmpeg.tar.gz" -C "$TMP_DIR" || { + msg_error "Failed to extract FFmpeg" + rm -rf "$TMP_DIR" + return 1 + } + + cd "$TMP_DIR/FFmpeg-"* || { + msg_error "Source extraction failed" + rm -rf "$TMP_DIR" + return 1 + } + + local args=( + --enable-gpl + --enable-shared + --enable-nonfree + --disable-static + --enable-libx264 + --enable-libvpx + --enable-libmp3lame + ) + + if [[ "$TYPE" != "minimal" ]]; then + args+=(--enable-libfreetype --enable-libass --enable-libopus --enable-libvorbis) + fi + + if [[ "$TYPE" == "full" ]]; then + args+=(--enable-libx265 --enable-libdav1d --enable-zlib) + args+=(--enable-vaapi --enable-libdrm) + fi + + if [[ ${#args[@]} -eq 0 ]]; then + msg_error "FFmpeg configure args array is empty" + rm -rf "$TMP_DIR" + return 1 + fi + + $STD ./configure "${args[@]}" + $STD make -j"$(nproc)" + $STD make install + echo "/usr/local/lib" >/etc/ld.so.conf.d/ffmpeg.conf + $STD ldconfig + + ldconfig -p | grep libavdevice >/dev/null || { + msg_error "libavdevice not registered with dynamic linker" + rm -rf "$TMP_DIR" + return 1 + } + + if ! command -v ffmpeg &>/dev/null; then + msg_error "FFmpeg installation failed" + rm -rf "$TMP_DIR" + return 1 + fi + + local FINAL_VERSION + FINAL_VERSION=$(ffmpeg -version | head -n1 | awk '{print $3}') + rm -rf "$TMP_DIR" + cache_installed_version "ffmpeg" "$FINAL_VERSION" + ensure_usr_local_bin_persist + msg_ok "Installed FFmpeg $FINAL_VERSION" } # ------------------------------------------------------------------------------ @@ -1588,65 +1591,65 @@ function setup_ffmpeg() { # ------------------------------------------------------------------------------ function setup_go() { - local ARCH - case "$(uname -m)" in - x86_64) ARCH="amd64" ;; - aarch64) ARCH="arm64" ;; - *) - msg_error "Unsupported architecture: $(uname -m)" - return 1 - ;; - esac + local ARCH + case "$(uname -m)" in + x86_64) ARCH="amd64" ;; + aarch64) ARCH="arm64" ;; + *) + msg_error "Unsupported architecture: $(uname -m)" + return 1 + ;; + esac - # Determine version - if [[ -z "${GO_VERSION:-}" || "${GO_VERSION}" == "latest" ]]; then - GO_VERSION=$(curl -fsSL https://go.dev/VERSION?m=text | head -n1 | sed 's/^go//') - if [[ -z "$GO_VERSION" ]]; then - msg_error "Could not determine latest Go version" - return 1 - fi + # Determine version + if [[ -z "${GO_VERSION:-}" || "${GO_VERSION}" == "latest" ]]; then + GO_VERSION=$(curl -fsSL https://go.dev/VERSION?m=text | head -n1 | sed 's/^go//') + if [[ -z "$GO_VERSION" ]]; then + msg_error "Could not determine latest Go version" + return 1 fi + fi - local GO_BIN="/usr/local/bin/go" - local GO_INSTALL_DIR="/usr/local/go" - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "go") + local GO_BIN="/usr/local/bin/go" + local GO_INSTALL_DIR="/usr/local/go" + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "go") - if [[ -x "$GO_BIN" ]]; then - local CURRENT_VERSION - CURRENT_VERSION=$("$GO_BIN" version | awk '{print $3}' | sed 's/go//') - if [[ "$CURRENT_VERSION" == "$GO_VERSION" ]]; then - if [[ "$CACHED_VERSION" == "$GO_VERSION" ]]; then - return 0 - fi - cache_installed_version "go" "$GO_VERSION" - return 0 - else - msg_info "Upgrading Go from $CURRENT_VERSION to $GO_VERSION" - rm -rf "$GO_INSTALL_DIR" - fi + if [[ -x "$GO_BIN" ]]; then + local CURRENT_VERSION + CURRENT_VERSION=$("$GO_BIN" version | awk '{print $3}' | sed 's/go//') + if [[ "$CURRENT_VERSION" == "$GO_VERSION" ]]; then + if [[ "$CACHED_VERSION" == "$GO_VERSION" ]]; then + return 0 + fi + cache_installed_version "go" "$GO_VERSION" + return 0 else - msg_info "Installing Go $GO_VERSION" + msg_info "Upgrading Go from $CURRENT_VERSION to $GO_VERSION" + rm -rf "$GO_INSTALL_DIR" fi + else + msg_info "Installing Go $GO_VERSION" + fi - local TARBALL="go${GO_VERSION}.linux-${ARCH}.tar.gz" - local URL="https://go.dev/dl/${TARBALL}" - local TMP_TAR=$(mktemp) + local TARBALL="go${GO_VERSION}.linux-${ARCH}.tar.gz" + local URL="https://go.dev/dl/${TARBALL}" + local TMP_TAR=$(mktemp) - curl -fsSL "$URL" -o "$TMP_TAR" || { - msg_error "Failed to download $TARBALL" - rm -f "$TMP_TAR" - return 1 - } - - $STD tar -C /usr/local -xzf "$TMP_TAR" - ln -sf /usr/local/go/bin/go /usr/local/bin/go - ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt + curl -fsSL "$URL" -o "$TMP_TAR" || { + msg_error "Failed to download $TARBALL" rm -f "$TMP_TAR" + return 1 + } - cache_installed_version "go" "$GO_VERSION" - ensure_usr_local_bin_persist - msg_ok "Installed Go $GO_VERSION" + $STD tar -C /usr/local -xzf "$TMP_TAR" + ln -sf /usr/local/go/bin/go /usr/local/bin/go + ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt + rm -f "$TMP_TAR" + + cache_installed_version "go" "$GO_VERSION" + ensure_usr_local_bin_persist + msg_ok "Installed Go $GO_VERSION" } # ------------------------------------------------------------------------------ @@ -1658,73 +1661,73 @@ function setup_go() { # ------------------------------------------------------------------------------ function setup_gs() { - local TMP_DIR=$(mktemp -d) - local CURRENT_VERSION=$(gs --version 2>/dev/null || echo "0") - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "ghostscript") + local TMP_DIR=$(mktemp -d) + local CURRENT_VERSION=$(gs --version 2>/dev/null || echo "0") + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "ghostscript") - ensure_dependencies jq + ensure_dependencies jq - local RELEASE_JSON - RELEASE_JSON=$(curl -fsSL https://api.github.com/repos/ArtifexSoftware/ghostpdl-downloads/releases/latest) - local LATEST_VERSION - LATEST_VERSION=$(echo "$RELEASE_JSON" | jq -r '.tag_name' | sed 's/^gs//') - local LATEST_VERSION_DOTTED - LATEST_VERSION_DOTTED=$(echo "$RELEASE_JSON" | jq -r '.name' | grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+') - - if [[ -z "$LATEST_VERSION" || -z "$LATEST_VERSION_DOTTED" ]]; then - msg_error "Could not determine latest Ghostscript version" - rm -rf "$TMP_DIR" - return 1 - fi - - if dpkg --compare-versions "$CURRENT_VERSION" ge "$LATEST_VERSION_DOTTED" 2>/dev/null; then - if [[ "$CACHED_VERSION" == "$LATEST_VERSION_DOTTED" ]]; then - rm -rf "$TMP_DIR" - return 0 - fi - cache_installed_version "ghostscript" "$LATEST_VERSION_DOTTED" - rm -rf "$TMP_DIR" - return 0 - fi - - msg_info "Installing Ghostscript $LATEST_VERSION_DOTTED" - - curl -fsSL "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs${LATEST_VERSION}/ghostscript-${LATEST_VERSION_DOTTED}.tar.gz" -o "$TMP_DIR/ghostscript.tar.gz" || { - msg_error "Failed to download Ghostscript" - rm -rf "$TMP_DIR" - return 1 - } - - if ! tar -xzf "$TMP_DIR/ghostscript.tar.gz" -C "$TMP_DIR"; then - msg_error "Failed to extract Ghostscript archive" - rm -rf "$TMP_DIR" - return 1 - fi - - cd "$TMP_DIR/ghostscript-${LATEST_VERSION_DOTTED}" || { - msg_error "Failed to enter Ghostscript source directory" - rm -rf "$TMP_DIR" - return 1 - } - - ensure_dependencies build-essential libpng-dev zlib1g-dev - - $STD ./configure - $STD make -j"$(nproc)" - $STD make install - - hash -r - if [[ ! -x "$(command -v gs)" ]]; then - if [[ -x /usr/local/bin/gs ]]; then - ln -sf /usr/local/bin/gs /usr/bin/gs - fi - fi + local RELEASE_JSON + RELEASE_JSON=$(curl -fsSL https://api.github.com/repos/ArtifexSoftware/ghostpdl-downloads/releases/latest) + local LATEST_VERSION + LATEST_VERSION=$(echo "$RELEASE_JSON" | jq -r '.tag_name' | sed 's/^gs//') + local LATEST_VERSION_DOTTED + LATEST_VERSION_DOTTED=$(echo "$RELEASE_JSON" | jq -r '.name' | grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+') + if [[ -z "$LATEST_VERSION" || -z "$LATEST_VERSION_DOTTED" ]]; then + msg_error "Could not determine latest Ghostscript version" rm -rf "$TMP_DIR" + return 1 + fi + + if dpkg --compare-versions "$CURRENT_VERSION" ge "$LATEST_VERSION_DOTTED" 2>/dev/null; then + if [[ "$CACHED_VERSION" == "$LATEST_VERSION_DOTTED" ]]; then + rm -rf "$TMP_DIR" + return 0 + fi cache_installed_version "ghostscript" "$LATEST_VERSION_DOTTED" - ensure_usr_local_bin_persist - msg_ok "Installed Ghostscript $LATEST_VERSION_DOTTED" + rm -rf "$TMP_DIR" + return 0 + fi + + msg_info "Installing Ghostscript $LATEST_VERSION_DOTTED" + + curl -fsSL "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs${LATEST_VERSION}/ghostscript-${LATEST_VERSION_DOTTED}.tar.gz" -o "$TMP_DIR/ghostscript.tar.gz" || { + msg_error "Failed to download Ghostscript" + rm -rf "$TMP_DIR" + return 1 + } + + if ! tar -xzf "$TMP_DIR/ghostscript.tar.gz" -C "$TMP_DIR"; then + msg_error "Failed to extract Ghostscript archive" + rm -rf "$TMP_DIR" + return 1 + fi + + cd "$TMP_DIR/ghostscript-${LATEST_VERSION_DOTTED}" || { + msg_error "Failed to enter Ghostscript source directory" + rm -rf "$TMP_DIR" + return 1 + } + + ensure_dependencies build-essential libpng-dev zlib1g-dev + + $STD ./configure + $STD make -j"$(nproc)" + $STD make install + + hash -r + if [[ ! -x "$(command -v gs)" ]]; then + if [[ -x /usr/local/bin/gs ]]; then + ln -sf /usr/local/bin/gs /usr/bin/gs + fi + fi + + rm -rf "$TMP_DIR" + cache_installed_version "ghostscript" "$LATEST_VERSION_DOTTED" + ensure_usr_local_bin_persist + msg_ok "Installed Ghostscript $LATEST_VERSION_DOTTED" } # ------------------------------------------------------------------------------ @@ -1739,79 +1742,79 @@ function setup_gs() { # - Requires: build-essential, libtool, libjpeg-dev, libpng-dev, etc. # ------------------------------------------------------------------------------ function setup_imagemagick() { - local TMP_DIR=$(mktemp -d) - local VERSION="" - local BINARY_PATH="/usr/local/bin/magick" - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "imagemagick") + local TMP_DIR=$(mktemp -d) + local VERSION="" + local BINARY_PATH="/usr/local/bin/magick" + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "imagemagick") - if command -v magick &>/dev/null; then - VERSION=$(magick -version | awk '/^Version/ {print $3}') - if [[ "$CACHED_VERSION" == "$VERSION" ]]; then - rm -rf "$TMP_DIR" - return 0 - fi - cache_installed_version "imagemagick" "$VERSION" - rm -rf "$TMP_DIR" - return 0 + if command -v magick &>/dev/null; then + VERSION=$(magick -version | awk '/^Version/ {print $3}') + if [[ "$CACHED_VERSION" == "$VERSION" ]]; then + rm -rf "$TMP_DIR" + return 0 fi - - msg_info "Installing ImageMagick (Patience)" - - ensure_dependencies \ - build-essential \ - libtool \ - libjpeg-dev \ - libpng-dev \ - libtiff-dev \ - libwebp-dev \ - libheif-dev \ - libde265-dev \ - libopenjp2-7-dev \ - libxml2-dev \ - liblcms2-dev \ - libfreetype6-dev \ - libraw-dev \ - libfftw3-dev \ - liblqr-1-0-dev \ - libgsl-dev \ - pkg-config \ - ghostscript - - curl -fsSL https://imagemagick.org/archive/ImageMagick.tar.gz -o "$TMP_DIR/ImageMagick.tar.gz" || { - msg_error "Failed to download ImageMagick" - rm -rf "$TMP_DIR" - return 1 - } - - tar -xzf "$TMP_DIR/ImageMagick.tar.gz" -C "$TMP_DIR" || { - msg_error "Failed to extract ImageMagick" - rm -rf "$TMP_DIR" - return 1 - } - - cd "$TMP_DIR"/ImageMagick-* || { - msg_error "Source extraction failed" - rm -rf "$TMP_DIR" - return 1 - } - - $STD ./configure --disable-static - $STD make -j"$(nproc)" - $STD make install - $STD ldconfig /usr/local/lib - - if [[ ! -x "$BINARY_PATH" ]]; then - msg_error "ImageMagick installation failed" - rm -rf "$TMP_DIR" - return 1 - fi - - VERSION=$("$BINARY_PATH" -version | awk '/^Version/ {print $3}') - rm -rf "$TMP_DIR" cache_installed_version "imagemagick" "$VERSION" - ensure_usr_local_bin_persist - msg_ok "Installed ImageMagick $VERSION" + rm -rf "$TMP_DIR" + return 0 + fi + + msg_info "Installing ImageMagick (Patience)" + + ensure_dependencies \ + build-essential \ + libtool \ + libjpeg-dev \ + libpng-dev \ + libtiff-dev \ + libwebp-dev \ + libheif-dev \ + libde265-dev \ + libopenjp2-7-dev \ + libxml2-dev \ + liblcms2-dev \ + libfreetype6-dev \ + libraw-dev \ + libfftw3-dev \ + liblqr-1-0-dev \ + libgsl-dev \ + pkg-config \ + ghostscript + + curl -fsSL https://imagemagick.org/archive/ImageMagick.tar.gz -o "$TMP_DIR/ImageMagick.tar.gz" || { + msg_error "Failed to download ImageMagick" + rm -rf "$TMP_DIR" + return 1 + } + + tar -xzf "$TMP_DIR/ImageMagick.tar.gz" -C "$TMP_DIR" || { + msg_error "Failed to extract ImageMagick" + rm -rf "$TMP_DIR" + return 1 + } + + cd "$TMP_DIR"/ImageMagick-* || { + msg_error "Source extraction failed" + rm -rf "$TMP_DIR" + return 1 + } + + $STD ./configure --disable-static + $STD make -j"$(nproc)" + $STD make install + $STD ldconfig /usr/local/lib + + if [[ ! -x "$BINARY_PATH" ]]; then + msg_error "ImageMagick installation failed" + rm -rf "$TMP_DIR" + return 1 + fi + + VERSION=$("$BINARY_PATH" -version | awk '/^Version/ {print $3}') + rm -rf "$TMP_DIR" + cache_installed_version "imagemagick" "$VERSION" + ensure_usr_local_bin_persist + msg_ok "Installed ImageMagick $VERSION" } # ------------------------------------------------------------------------------ @@ -1826,63 +1829,63 @@ function setup_imagemagick() { # ------------------------------------------------------------------------------ function setup_java() { - local JAVA_VERSION="${JAVA_VERSION:-21}" - local DISTRO_ID DISTRO_CODENAME - DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') - DISTRO_CODENAME=$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release) - local DESIRED_PACKAGE="temurin-${JAVA_VERSION}-jdk" + local JAVA_VERSION="${JAVA_VERSION:-21}" + local DISTRO_ID DISTRO_CODENAME + DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + DISTRO_CODENAME=$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release) + local DESIRED_PACKAGE="temurin-${JAVA_VERSION}-jdk" - # Check cached version - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "temurin-jdk") + # Check cached version + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "temurin-jdk") - # Add repo nur wenn nötig - if [[ ! -f /etc/apt/sources.list.d/adoptium.sources ]]; then - # Cleanup old repository files - cleanup_old_repo_files "adoptium" + # Add repo nur wenn nötig + if [[ ! -f /etc/apt/sources.list.d/adoptium.sources ]]; then + # Cleanup old repository files + cleanup_old_repo_files "adoptium" - # Use helper function to get fallback suite - local SUITE - SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://packages.adoptium.net/artifactory/deb") + # Use helper function to get fallback suite + local SUITE + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://packages.adoptium.net/artifactory/deb") - # Use standardized repo setup - setup_deb822_repo \ - "adoptium" \ - "https://packages.adoptium.net/artifactory/api/gpg/key/public" \ - "https://packages.adoptium.net/artifactory/deb" \ - "$SUITE" \ - "main" \ - "amd64 arm64" - fi + # Use standardized repo setup + setup_deb822_repo \ + "adoptium" \ + "https://packages.adoptium.net/artifactory/api/gpg/key/public" \ + "https://packages.adoptium.net/artifactory/deb" \ + "$SUITE" \ + "main" \ + "amd64 arm64" + fi - local INSTALLED_VERSION="" - if dpkg -l | grep -q "temurin-.*-jdk"; then - INSTALLED_VERSION=$(dpkg -l | awk '/temurin-.*-jdk/{print $2}' | grep -oP 'temurin-\K[0-9]+') - fi + local INSTALLED_VERSION="" + if dpkg -l | grep -q "temurin-.*-jdk"; then + INSTALLED_VERSION=$(dpkg -l | awk '/temurin-.*-jdk/{print $2}' | grep -oP 'temurin-\K[0-9]+') + fi - if [[ "$INSTALLED_VERSION" == "$JAVA_VERSION" ]]; then - if [[ "$CACHED_VERSION" == "$JAVA_VERSION" ]]; then - # Already at correct version, just upgrade if available - upgrade_package "$DESIRED_PACKAGE" - else - msg_info "Upgrading Temurin JDK $JAVA_VERSION" - $STD apt update - $STD apt install --only-upgrade -y "$DESIRED_PACKAGE" - cache_installed_version "temurin-jdk" "$JAVA_VERSION" - msg_ok "Upgraded Temurin JDK $JAVA_VERSION" - fi + if [[ "$INSTALLED_VERSION" == "$JAVA_VERSION" ]]; then + if [[ "$CACHED_VERSION" == "$JAVA_VERSION" ]]; then + # Already at correct version, just upgrade if available + upgrade_package "$DESIRED_PACKAGE" else - if [[ -n "$INSTALLED_VERSION" ]]; then - msg_info "Removing old Temurin JDK $INSTALLED_VERSION" - $STD apt purge -y "temurin-${INSTALLED_VERSION}-jdk" - msg_ok "Removed old Temurin JDK" - fi - - msg_info "Installing Temurin JDK $JAVA_VERSION" - $STD apt install -y "$DESIRED_PACKAGE" - cache_installed_version "temurin-jdk" "$JAVA_VERSION" - msg_ok "Installed Temurin JDK $JAVA_VERSION" + msg_info "Upgrading Temurin JDK $JAVA_VERSION" + $STD apt update + $STD apt install --only-upgrade -y "$DESIRED_PACKAGE" + cache_installed_version "temurin-jdk" "$JAVA_VERSION" + msg_ok "Upgraded Temurin JDK $JAVA_VERSION" fi + else + if [[ -n "$INSTALLED_VERSION" ]]; then + msg_info "Removing old Temurin JDK $INSTALLED_VERSION" + $STD apt purge -y "temurin-${INSTALLED_VERSION}-jdk" + msg_ok "Removed old Temurin JDK" + fi + + msg_info "Installing Temurin JDK $JAVA_VERSION" + $STD apt install -y "$DESIRED_PACKAGE" + cache_installed_version "temurin-jdk" "$JAVA_VERSION" + msg_ok "Installed Temurin JDK $JAVA_VERSION" + fi } # ------------------------------------------------------------------------------ @@ -1894,21 +1897,21 @@ function setup_java() { # ------------------------------------------------------------------------------ function setup_local_ip_helper() { - local BASE_DIR="/usr/local/community-scripts/ip-management" - local SCRIPT_PATH="$BASE_DIR/update_local_ip.sh" - local IP_FILE="/run/local-ip.env" - local DISPATCHER_SCRIPT="/etc/networkd-dispatcher/routable.d/10-update-local-ip.sh" + local BASE_DIR="/usr/local/community-scripts/ip-management" + local SCRIPT_PATH="$BASE_DIR/update_local_ip.sh" + local IP_FILE="/run/local-ip.env" + local DISPATCHER_SCRIPT="/etc/networkd-dispatcher/routable.d/10-update-local-ip.sh" - mkdir -p "$BASE_DIR" + mkdir -p "$BASE_DIR" - # Install networkd-dispatcher if not present - if ! dpkg -s networkd-dispatcher >/dev/null 2>&1; then - $STD apt update - $STD apt install -y networkd-dispatcher - fi + # Install networkd-dispatcher if not present + if ! dpkg -s networkd-dispatcher >/dev/null 2>&1; then + $STD apt update + $STD apt install -y networkd-dispatcher + fi - # Write update_local_ip.sh - cat <<'EOF' >"$SCRIPT_PATH" + # Write update_local_ip.sh + cat <<'EOF' >"$SCRIPT_PATH" #!/bin/bash set -euo pipefail @@ -1950,17 +1953,17 @@ echo "LOCAL_IP=$current_ip" > "$IP_FILE" echo "[INFO] LOCAL_IP updated to $current_ip" EOF - chmod +x "$SCRIPT_PATH" + chmod +x "$SCRIPT_PATH" - # Install dispatcher hook - mkdir -p "$(dirname "$DISPATCHER_SCRIPT")" - cat <"$DISPATCHER_SCRIPT" + # Install dispatcher hook + mkdir -p "$(dirname "$DISPATCHER_SCRIPT")" + cat <"$DISPATCHER_SCRIPT" #!/bin/bash $SCRIPT_PATH EOF - chmod +x "$DISPATCHER_SCRIPT" - systemctl enable -q --now networkd-dispatcher.service + chmod +x "$DISPATCHER_SCRIPT" + systemctl enable -q --now networkd-dispatcher.service } # ------------------------------------------------------------------------------ @@ -1976,88 +1979,88 @@ EOF # ------------------------------------------------------------------------------ setup_mariadb() { - local MARIADB_VERSION="${MARIADB_VERSION:-latest}" - local DISTRO_ID DISTRO_CODENAME - DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') - DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + local MARIADB_VERSION="${MARIADB_VERSION:-latest}" + local DISTRO_ID DISTRO_CODENAME + DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - if ! curl -fsI http://mirror.mariadb.org/repo/ >/dev/null; then - msg_error "MariaDB mirror not reachable" - return 1 + if ! curl -fsI http://mirror.mariadb.org/repo/ >/dev/null; then + msg_error "MariaDB mirror not reachable" + return 1 + fi + + if [[ "$MARIADB_VERSION" == "latest" ]]; then + MARIADB_VERSION=$(curl -fsSL http://mirror.mariadb.org/repo/ | + grep -Eo '[0-9]+\.[0-9]+\.[0-9]+/' | + grep -vE 'rc/|rolling/' | + sed 's|/||' | + sort -Vr | + head -n1) + if [[ -z "$MARIADB_VERSION" ]]; then + msg_error "Could not determine latest GA MariaDB version" + return 1 fi + fi - if [[ "$MARIADB_VERSION" == "latest" ]]; then - MARIADB_VERSION=$(curl -fsSL http://mirror.mariadb.org/repo/ | - grep -Eo '[0-9]+\.[0-9]+\.[0-9]+/' | - grep -vE 'rc/|rolling/' | - sed 's|/||' | - sort -Vr | - head -n1) - if [[ -z "$MARIADB_VERSION" ]]; then - msg_error "Could not determine latest GA MariaDB version" - return 1 - fi + local CURRENT_VERSION="" + if command -v mariadb >/dev/null; then + CURRENT_VERSION=$(mariadb --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+') + fi + + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "mariadb") + + if [[ "$CURRENT_VERSION" == "$MARIADB_VERSION" ]]; then + if [[ "$CACHED_VERSION" == "$MARIADB_VERSION" ]]; then + upgrade_package mariadb-server + upgrade_package mariadb-client + else + msg_info "Upgrading MariaDB $MARIADB_VERSION" + $STD apt update + $STD apt install --only-upgrade -y mariadb-server mariadb-client + cache_installed_version "mariadb" "$MARIADB_VERSION" + msg_ok "Upgraded MariaDB $MARIADB_VERSION" fi + return 0 + fi - local CURRENT_VERSION="" - if command -v mariadb >/dev/null; then - CURRENT_VERSION=$(mariadb --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+') - fi + msg_info "Installing MariaDB $MARIADB_VERSION" - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "mariadb") + if [[ -n "$CURRENT_VERSION" ]]; then + $STD systemctl stop mariadb >/dev/null 2>&1 || true + $STD apt purge -y 'mariadb*' || true + fi - if [[ "$CURRENT_VERSION" == "$MARIADB_VERSION" ]]; then - if [[ "$CACHED_VERSION" == "$MARIADB_VERSION" ]]; then - upgrade_package mariadb-server - upgrade_package mariadb-client - else - msg_info "Upgrading MariaDB $MARIADB_VERSION" - $STD apt update - $STD apt install --only-upgrade -y mariadb-server mariadb-client - cache_installed_version "mariadb" "$MARIADB_VERSION" - msg_ok "Upgraded MariaDB $MARIADB_VERSION" - fi - return 0 - fi + # Cleanup old repository files + cleanup_old_repo_files "mariadb" - msg_info "Installing MariaDB $MARIADB_VERSION" + # Use helper function to get fallback suite + local SUITE + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "http://mirror.mariadb.org/repo/${MARIADB_VERSION}/${DISTRO_ID}") - if [[ -n "$CURRENT_VERSION" ]]; then - $STD systemctl stop mariadb >/dev/null 2>&1 || true - $STD apt purge -y 'mariadb*' || true - fi + # Use standardized repo setup + setup_deb822_repo \ + "mariadb" \ + "https://mariadb.org/mariadb_release_signing_key.asc" \ + "http://mirror.mariadb.org/repo/${MARIADB_VERSION}/${DISTRO_ID}" \ + "$SUITE" \ + "main" \ + "amd64 arm64" - # Cleanup old repository files + local MARIADB_MAJOR_MINOR + MARIADB_MAJOR_MINOR=$(echo "$MARIADB_VERSION" | awk -F. '{print $1"."$2}') + if [[ -n "$MARIADB_MAJOR_MINOR" ]]; then + echo "mariadb-server-$MARIADB_MAJOR_MINOR mariadb-server/feedback boolean false" | debconf-set-selections + fi + + DEBIAN_FRONTEND=noninteractive $STD apt install -y mariadb-server mariadb-client || { cleanup_old_repo_files "mariadb" + $STD apt update + DEBIAN_FRONTEND=noninteractive $STD apt install -y mariadb-server mariadb-client + } - # Use helper function to get fallback suite - local SUITE - SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "http://mirror.mariadb.org/repo/${MARIADB_VERSION}/${DISTRO_ID}") - - # Use standardized repo setup - setup_deb822_repo \ - "mariadb" \ - "https://mariadb.org/mariadb_release_signing_key.asc" \ - "http://mirror.mariadb.org/repo/${MARIADB_VERSION}/${DISTRO_ID}" \ - "$SUITE" \ - "main" \ - "amd64 arm64" - - local MARIADB_MAJOR_MINOR - MARIADB_MAJOR_MINOR=$(echo "$MARIADB_VERSION" | awk -F. '{print $1"."$2}') - if [[ -n "$MARIADB_MAJOR_MINOR" ]]; then - echo "mariadb-server-$MARIADB_MAJOR_MINOR mariadb-server/feedback boolean false" | debconf-set-selections - fi - - DEBIAN_FRONTEND=noninteractive $STD apt install -y mariadb-server mariadb-client || { - cleanup_old_repo_files "mariadb" - $STD apt update - DEBIAN_FRONTEND=noninteractive $STD apt install -y mariadb-server mariadb-client - } - - cache_installed_version "mariadb" "$MARIADB_VERSION" - msg_ok "Installed MariaDB $MARIADB_VERSION" + cache_installed_version "mariadb" "$MARIADB_VERSION" + msg_ok "Installed MariaDB $MARIADB_VERSION" } # ------------------------------------------------------------------------------ @@ -2072,75 +2075,75 @@ setup_mariadb() { # ------------------------------------------------------------------------------ function setup_mongodb() { - local MONGO_VERSION="${MONGO_VERSION:-8.0}" - local DISTRO_ID DISTRO_CODENAME - DISTRO_ID=$(awk -F= '/^ID=/{ gsub(/"/,"",$2); print $2 }' /etc/os-release) - DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{ print $2 }' /etc/os-release) + local MONGO_VERSION="${MONGO_VERSION:-8.0}" + local DISTRO_ID DISTRO_CODENAME + DISTRO_ID=$(awk -F= '/^ID=/{ gsub(/"/,"",$2); print $2 }' /etc/os-release) + DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{ print $2 }' /etc/os-release) - # Check AVX support - if ! grep -qm1 'avx[^ ]*' /proc/cpuinfo; then - local major="${MONGO_VERSION%%.*}" - if ((major > 5)); then - msg_error "MongoDB ${MONGO_VERSION} requires AVX support, which is not available on this system." - return 1 - fi + # Check AVX support + if ! grep -qm1 'avx[^ ]*' /proc/cpuinfo; then + local major="${MONGO_VERSION%%.*}" + if ((major > 5)); then + msg_error "MongoDB ${MONGO_VERSION} requires AVX support, which is not available on this system." + return 1 fi + fi - case "$DISTRO_ID" in - ubuntu) - MONGO_BASE_URL="https://repo.mongodb.org/apt/ubuntu" - REPO_COMPONENT="multiverse" - ;; - debian) - MONGO_BASE_URL="https://repo.mongodb.org/apt/debian" - REPO_COMPONENT="main" - ;; - *) - msg_error "Unsupported distribution: $DISTRO_ID" - return 1 - ;; - esac + case "$DISTRO_ID" in + ubuntu) + MONGO_BASE_URL="https://repo.mongodb.org/apt/ubuntu" + REPO_COMPONENT="multiverse" + ;; + debian) + MONGO_BASE_URL="https://repo.mongodb.org/apt/debian" + REPO_COMPONENT="main" + ;; + *) + msg_error "Unsupported distribution: $DISTRO_ID" + return 1 + ;; + esac - local INSTALLED_VERSION="" - if command -v mongod >/dev/null; then - INSTALLED_VERSION=$(mongod --version | awk '/db version/{print $3}' | cut -d. -f1,2) + local INSTALLED_VERSION="" + if command -v mongod >/dev/null; then + INSTALLED_VERSION=$(mongod --version | awk '/db version/{print $3}' | cut -d. -f1,2) + fi + + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "mongodb") + + if [[ "$INSTALLED_VERSION" == "$MONGO_VERSION" ]]; then + if [[ "$CACHED_VERSION" == "$MONGO_VERSION" ]]; then + upgrade_package mongodb-org + else + msg_info "Upgrading MongoDB $MONGO_VERSION" + $STD apt update + $STD apt install --only-upgrade -y mongodb-org + cache_installed_version "mongodb" "$MONGO_VERSION" + msg_ok "Upgraded MongoDB $MONGO_VERSION" fi + return 0 + fi - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "mongodb") + msg_info "Installing MongoDB $MONGO_VERSION" - if [[ "$INSTALLED_VERSION" == "$MONGO_VERSION" ]]; then - if [[ "$CACHED_VERSION" == "$MONGO_VERSION" ]]; then - upgrade_package mongodb-org - else - msg_info "Upgrading MongoDB $MONGO_VERSION" - $STD apt update - $STD apt install --only-upgrade -y mongodb-org - cache_installed_version "mongodb" "$MONGO_VERSION" - msg_ok "Upgraded MongoDB $MONGO_VERSION" - fi - return 0 - fi + if [[ -n "$INSTALLED_VERSION" ]]; then + $STD systemctl stop mongod || true + $STD apt purge -y mongodb-org || true + fi - msg_info "Installing MongoDB $MONGO_VERSION" + # Cleanup old repository files + cleanup_old_repo_files "mongodb-org-${MONGO_VERSION}" - if [[ -n "$INSTALLED_VERSION" ]]; then - $STD systemctl stop mongod || true - $STD apt purge -y mongodb-org || true - fi + # Use helper function to get fallback suite + local SUITE + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "$MONGO_BASE_URL") - # Cleanup old repository files - cleanup_old_repo_files "mongodb-org-${MONGO_VERSION}" + # Use standardized repo setup + mkdir -p /etc/apt/keyrings + curl -fsSL "https://pgp.mongodb.com/server-${MONGO_VERSION}.asc" | gpg --dearmor --yes -o "/etc/apt/keyrings/mongodb-${MONGO_VERSION}.gpg" - # Use helper function to get fallback suite - local SUITE - SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "$MONGO_BASE_URL") - - # Use standardized repo setup - mkdir -p /etc/apt/keyrings - curl -fsSL "https://pgp.mongodb.com/server-${MONGO_VERSION}.asc" | gpg --dearmor --yes -o "/etc/apt/keyrings/mongodb-${MONGO_VERSION}.gpg" - - cat </etc/apt/sources.list.d/mongodb-org-${MONGO_VERSION}.sources + cat </etc/apt/sources.list.d/mongodb-org-${MONGO_VERSION}.sources Types: deb URIs: ${MONGO_BASE_URL} Suites: ${SUITE}/mongodb-org/${MONGO_VERSION} @@ -2149,20 +2152,20 @@ Architectures: amd64 arm64 Signed-By: /etc/apt/keyrings/mongodb-${MONGO_VERSION}.gpg EOF - $STD apt update || { - msg_error "APT update failed — invalid MongoDB repo for ${DISTRO_ID}-${DISTRO_CODENAME}?" - return 1 - } + $STD apt update || { + msg_error "APT update failed — invalid MongoDB repo for ${DISTRO_ID}-${DISTRO_CODENAME}?" + return 1 + } - $STD apt install -y mongodb-org + $STD apt install -y mongodb-org - mkdir -p /var/lib/mongodb - chown -R mongodb:mongodb /var/lib/mongodb + mkdir -p /var/lib/mongodb + chown -R mongodb:mongodb /var/lib/mongodb - $STD systemctl enable mongod - safe_service_restart mongod - cache_installed_version "mongodb" "$MONGO_VERSION" - msg_ok "Installed MongoDB $MONGO_VERSION" + $STD systemctl enable mongod + safe_service_restart mongod + cache_installed_version "mongodb" "$MONGO_VERSION" + msg_ok "Installed MongoDB $MONGO_VERSION" } # ------------------------------------------------------------------------------ @@ -2178,57 +2181,79 @@ EOF # ------------------------------------------------------------------------------ function setup_mysql() { - local MYSQL_VERSION="${MYSQL_VERSION:-8.0}" - local CURRENT_VERSION="" - local NEED_INSTALL=false - local DISTRO_ID DISTRO_CODENAME - DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') - DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + local MYSQL_VERSION="${MYSQL_VERSION:-8.0}" + local CURRENT_VERSION="" + local NEED_INSTALL=false + local DISTRO_ID DISTRO_CODENAME + DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - if command -v mysql >/dev/null; then - CURRENT_VERSION="$(mysql --version | grep -oP '[0-9]+\.[0-9]+' | head -n1)" - if [[ "$CURRENT_VERSION" != "$MYSQL_VERSION" ]]; then - msg_info "MySQL $CURRENT_VERSION will be upgraded to $MYSQL_VERSION" - NEED_INSTALL=true - else - if apt list --upgradable 2>/dev/null | grep -q '^mysql-server/'; then - msg_info "MySQL $CURRENT_VERSION available for upgrade" - $STD apt update - $STD apt install --only-upgrade -y mysql-server - msg_ok "MySQL upgraded" - fi - return - fi + if command -v mysql >/dev/null; then + CURRENT_VERSION="$(mysql --version | grep -oP '[0-9]+\.[0-9]+' | head -n1)" + if [[ "$CURRENT_VERSION" != "$MYSQL_VERSION" ]]; then + msg_info "MySQL $CURRENT_VERSION will be upgraded to $MYSQL_VERSION" + NEED_INSTALL=true else - msg_info "Setup MySQL $MYSQL_VERSION" - NEED_INSTALL=true + if apt list --upgradable 2>/dev/null | grep -q '^mysql-server/'; then + msg_info "MySQL $CURRENT_VERSION available for upgrade" + $STD apt update + $STD apt install --only-upgrade -y mysql-server + msg_ok "MySQL upgraded" + fi + return + fi + else + msg_info "Setup MySQL $MYSQL_VERSION" + NEED_INSTALL=true + fi + + if [[ "$NEED_INSTALL" == true ]]; then + $STD systemctl stop mysql || true + $STD apt purge -y "^mysql-server.*" "^mysql-client.*" "^mysql-common.*" || true + + # Cleanup old repository files + cleanup_old_repo_files "mysql" + + # Use helper function to get fallback suite + local SUITE + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://repo.mysql.com/apt/${DISTRO_ID}") + + # Use standardized repo setup + setup_deb822_repo \ + "mysql" \ + "https://repo.mysql.com/RPM-GPG-KEY-mysql-2023" \ + "https://repo.mysql.com/apt/${DISTRO_ID}" \ + "$SUITE" \ + "mysql-${MYSQL_VERSION}" \ + "amd64 arm64" + + export DEBIAN_FRONTEND=noninteractive + + # Update apt and check if package is available + if ! $STD apt update; then + msg_error "APT update failed for MySQL repository" + return 1 fi - if [[ "$NEED_INSTALL" == true ]]; then - $STD systemctl stop mysql || true - $STD apt purge -y "^mysql-server.*" "^mysql-client.*" "^mysql-common.*" || true - - # Cleanup old repository files - cleanup_old_repo_files "mysql" - - # Use helper function to get fallback suite - local SUITE - SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://repo.mysql.com/apt/${DISTRO_ID}") - - # Use standardized repo setup - setup_deb822_repo \ - "mysql" \ - "https://repo.mysql.com/RPM-GPG-KEY-mysql-2023" \ - "https://repo.mysql.com/apt/${DISTRO_ID}" \ - "$SUITE" \ - "mysql-${MYSQL_VERSION}" \ - "amd64 arm64" - - export DEBIAN_FRONTEND=noninteractive - $STD apt install -y mysql-server - cache_installed_version "mysql" "$MYSQL_VERSION" - msg_ok "Installed MySQL $MYSQL_VERSION" + if ! apt-cache policy mysql-server | grep -q 'Candidate:'; then + msg_error "MySQL ${MYSQL_VERSION} package not available for ${DISTRO_ID}-${DISTRO_CODENAME}" + return 1 fi + + if ! $STD apt install -y mysql-server; then + msg_error "Failed to install MySQL ${MYSQL_VERSION}" + return 1 + fi + + # Verify installation + if ! command -v mysql >/dev/null 2>&1; then + msg_error "MySQL installation completed but mysql command not found" + return 1 + fi + + cache_installed_version "mysql" "$MYSQL_VERSION" + msg_ok "Installed MySQL $MYSQL_VERSION" + fi } # ------------------------------------------------------------------------------ @@ -2244,106 +2269,106 @@ function setup_mysql() { # ------------------------------------------------------------------------------ function setup_nodejs() { - local NODE_VERSION="${NODE_VERSION:-22}" - local NODE_MODULE="${NODE_MODULE:-}" - local CURRENT_NODE_VERSION="" - local NEED_NODE_INSTALL=false - local DISTRO_ID DISTRO_CODENAME - DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') - DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + local NODE_VERSION="${NODE_VERSION:-22}" + local NODE_MODULE="${NODE_MODULE:-}" + local CURRENT_NODE_VERSION="" + local NEED_NODE_INSTALL=false + local DISTRO_ID DISTRO_CODENAME + DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - if command -v node >/dev/null; then - CURRENT_NODE_VERSION="$(node -v | grep -oP '^v\K[0-9]+')" - if [[ "$CURRENT_NODE_VERSION" != "$NODE_VERSION" ]]; then - msg_info "Old Node.js $CURRENT_NODE_VERSION found, replacing with $NODE_VERSION" - NEED_NODE_INSTALL=true - fi - else - msg_info "Setup Node.js $NODE_VERSION" - NEED_NODE_INSTALL=true + if command -v node >/dev/null; then + CURRENT_NODE_VERSION="$(node -v | grep -oP '^v\K[0-9]+')" + if [[ "$CURRENT_NODE_VERSION" != "$NODE_VERSION" ]]; then + msg_info "Old Node.js $CURRENT_NODE_VERSION found, replacing with $NODE_VERSION" + NEED_NODE_INSTALL=true + fi + else + msg_info "Setup Node.js $NODE_VERSION" + NEED_NODE_INSTALL=true + fi + + ensure_dependencies jq + + if [[ "$NEED_NODE_INSTALL" == true ]]; then + $STD apt purge -y nodejs + + # Cleanup old repository files + cleanup_old_repo_files "nodesource" + + # NodeSource uses 'nodistro' for all distributions - no fallback needed + # Use standardized repo setup + setup_deb822_repo \ + "nodesource" \ + "https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key" \ + "https://deb.nodesource.com/node_${NODE_VERSION}.x" \ + "nodistro" \ + "main" \ + "amd64 arm64" + + if ! $STD apt install -y nodejs; then + msg_error "Failed to install Node.js ${NODE_VERSION} from NodeSource" + return 1 fi - ensure_dependencies jq - - if [[ "$NEED_NODE_INSTALL" == true ]]; then - $STD apt purge -y nodejs - - # Cleanup old repository files - cleanup_old_repo_files "nodesource" - - # NodeSource uses 'nodistro' for all distributions - no fallback needed - # Use standardized repo setup - setup_deb822_repo \ - "nodesource" \ - "https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key" \ - "https://deb.nodesource.com/node_${NODE_VERSION}.x" \ - "nodistro" \ - "main" \ - "amd64 arm64" - - if ! $STD apt install -y nodejs; then - msg_error "Failed to install Node.js ${NODE_VERSION} from NodeSource" - return 1 - fi - - $STD npm install -g npm@latest || { - msg_warn "Failed to update npm to latest version" - } - - cache_installed_version "nodejs" "$NODE_VERSION" - msg_ok "Installed Node.js ${NODE_VERSION}" - fi - - export NODE_OPTIONS="--max-old-space-size=4096" - - if [[ ! -d /opt ]]; then - mkdir -p /opt - fi - cd /opt || { - msg_error "Failed to set safe working directory before npm install" - return 1 + $STD npm install -g npm@latest || { + msg_warn "Failed to update npm to latest version" } - if [[ -n "$NODE_MODULE" ]]; then - IFS=',' read -ra MODULES <<<"$NODE_MODULE" - for mod in "${MODULES[@]}"; do - local MODULE_NAME MODULE_REQ_VERSION MODULE_INSTALLED_VERSION - if [[ "$mod" == @*/*@* ]]; then - MODULE_NAME="${mod%@*}" - MODULE_REQ_VERSION="${mod##*@}" - elif [[ "$mod" == *"@"* ]]; then - MODULE_NAME="${mod%@*}" - MODULE_REQ_VERSION="${mod##*@}" - else - MODULE_NAME="$mod" - MODULE_REQ_VERSION="latest" - fi + cache_installed_version "nodejs" "$NODE_VERSION" + msg_ok "Installed Node.js ${NODE_VERSION}" + fi - if npm list -g --depth=0 "$MODULE_NAME" >/dev/null 2>&1; then - MODULE_INSTALLED_VERSION="$(npm list -g --depth=0 "$MODULE_NAME" | grep "$MODULE_NAME@" | awk -F@ '{print $2}' | tr -d '[:space:]')" - if [[ "$MODULE_REQ_VERSION" != "latest" && "$MODULE_REQ_VERSION" != "$MODULE_INSTALLED_VERSION" ]]; then - msg_info "Updating $MODULE_NAME from v$MODULE_INSTALLED_VERSION to v$MODULE_REQ_VERSION" - if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}"; then - msg_error "Failed to update $MODULE_NAME to version $MODULE_REQ_VERSION" - return 1 - fi - elif [[ "$MODULE_REQ_VERSION" == "latest" ]]; then - msg_info "Updating $MODULE_NAME to latest version" - if ! $STD npm install -g "${MODULE_NAME}@latest"; then - msg_error "Failed to update $MODULE_NAME to latest version" - return 1 - fi - fi - else - msg_info "Installing $MODULE_NAME@$MODULE_REQ_VERSION" - if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}"; then - msg_error "Failed to install $MODULE_NAME@$MODULE_REQ_VERSION" - return 1 - fi - fi - done - msg_ok "Installed Node.js modules: $NODE_MODULE" - fi + export NODE_OPTIONS="--max-old-space-size=4096" + + if [[ ! -d /opt ]]; then + mkdir -p /opt + fi + cd /opt || { + msg_error "Failed to set safe working directory before npm install" + return 1 + } + + if [[ -n "$NODE_MODULE" ]]; then + IFS=',' read -ra MODULES <<<"$NODE_MODULE" + for mod in "${MODULES[@]}"; do + local MODULE_NAME MODULE_REQ_VERSION MODULE_INSTALLED_VERSION + if [[ "$mod" == @*/*@* ]]; then + MODULE_NAME="${mod%@*}" + MODULE_REQ_VERSION="${mod##*@}" + elif [[ "$mod" == *"@"* ]]; then + MODULE_NAME="${mod%@*}" + MODULE_REQ_VERSION="${mod##*@}" + else + MODULE_NAME="$mod" + MODULE_REQ_VERSION="latest" + fi + + if npm list -g --depth=0 "$MODULE_NAME" >/dev/null 2>&1; then + MODULE_INSTALLED_VERSION="$(npm list -g --depth=0 "$MODULE_NAME" | grep "$MODULE_NAME@" | awk -F@ '{print $2}' | tr -d '[:space:]')" + if [[ "$MODULE_REQ_VERSION" != "latest" && "$MODULE_REQ_VERSION" != "$MODULE_INSTALLED_VERSION" ]]; then + msg_info "Updating $MODULE_NAME from v$MODULE_INSTALLED_VERSION to v$MODULE_REQ_VERSION" + if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}"; then + msg_error "Failed to update $MODULE_NAME to version $MODULE_REQ_VERSION" + return 1 + fi + elif [[ "$MODULE_REQ_VERSION" == "latest" ]]; then + msg_info "Updating $MODULE_NAME to latest version" + if ! $STD npm install -g "${MODULE_NAME}@latest"; then + msg_error "Failed to update $MODULE_NAME to latest version" + return 1 + fi + fi + else + msg_info "Installing $MODULE_NAME@$MODULE_REQ_VERSION" + if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}"; then + msg_error "Failed to install $MODULE_NAME@$MODULE_REQ_VERSION" + return 1 + fi + fi + done + msg_ok "Installed Node.js modules: $NODE_MODULE" + fi } # ------------------------------------------------------------------------------ @@ -2366,58 +2391,58 @@ function setup_nodejs() { # ------------------------------------------------------------------------------ function setup_php() { - local PHP_VERSION="${PHP_VERSION:-8.4}" - local PHP_MODULE="${PHP_MODULE:-}" - local PHP_APACHE="${PHP_APACHE:-NO}" - local PHP_FPM="${PHP_FPM:-NO}" - local DISTRO_ID DISTRO_CODENAME - DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') - DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + local PHP_VERSION="${PHP_VERSION:-8.4}" + local PHP_MODULE="${PHP_MODULE:-}" + local PHP_APACHE="${PHP_APACHE:-NO}" + local PHP_FPM="${PHP_FPM:-NO}" + local DISTRO_ID DISTRO_CODENAME + DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - local DEFAULT_MODULES="bcmath,cli,curl,gd,intl,mbstring,opcache,readline,xml,zip" - local COMBINED_MODULES + local DEFAULT_MODULES="bcmath,cli,curl,gd,intl,mbstring,opcache,readline,xml,zip" + local COMBINED_MODULES - local PHP_MEMORY_LIMIT="${PHP_MEMORY_LIMIT:-512M}" - local PHP_UPLOAD_MAX_FILESIZE="${PHP_UPLOAD_MAX_FILESIZE:-128M}" - local PHP_POST_MAX_SIZE="${PHP_POST_MAX_SIZE:-128M}" - local PHP_MAX_EXECUTION_TIME="${PHP_MAX_EXECUTION_TIME:-300}" + local PHP_MEMORY_LIMIT="${PHP_MEMORY_LIMIT:-512M}" + local PHP_UPLOAD_MAX_FILESIZE="${PHP_UPLOAD_MAX_FILESIZE:-128M}" + local PHP_POST_MAX_SIZE="${PHP_POST_MAX_SIZE:-128M}" + local PHP_MAX_EXECUTION_TIME="${PHP_MAX_EXECUTION_TIME:-300}" - # Merge default + user-defined modules - if [[ -n "$PHP_MODULE" ]]; then - COMBINED_MODULES="${DEFAULT_MODULES},${PHP_MODULE}" - else - COMBINED_MODULES="${DEFAULT_MODULES}" - fi + # Merge default + user-defined modules + if [[ -n "$PHP_MODULE" ]]; then + COMBINED_MODULES="${DEFAULT_MODULES},${PHP_MODULE}" + else + COMBINED_MODULES="${DEFAULT_MODULES}" + fi - # Deduplicate - COMBINED_MODULES=$(echo "$COMBINED_MODULES" | tr ',' '\n' | awk '!seen[$0]++' | paste -sd, -) + # Deduplicate + COMBINED_MODULES=$(echo "$COMBINED_MODULES" | tr ',' '\n' | awk '!seen[$0]++' | paste -sd, -) - # Get current PHP-CLI version - local CURRENT_PHP="" - if command -v php >/dev/null 2>&1; then - CURRENT_PHP=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2) - fi + # Get current PHP-CLI version + local CURRENT_PHP="" + if command -v php >/dev/null 2>&1; then + CURRENT_PHP=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2) + fi - if [[ -z "$CURRENT_PHP" ]]; then - msg_info "Setup PHP $PHP_VERSION" - elif [[ "$CURRENT_PHP" != "$PHP_VERSION" ]]; then - msg_info "Old PHP $CURRENT_PHP detected, Setup new PHP $PHP_VERSION" - $STD apt purge -y "php${CURRENT_PHP//./}"* || true - fi + if [[ -z "$CURRENT_PHP" ]]; then + msg_info "Setup PHP $PHP_VERSION" + elif [[ "$CURRENT_PHP" != "$PHP_VERSION" ]]; then + msg_info "Old PHP $CURRENT_PHP detected, Setup new PHP $PHP_VERSION" + $STD apt purge -y "php${CURRENT_PHP//./}"* || true + fi - # Ensure Sury repo is available - if [[ ! -f /etc/apt/sources.list.d/php.sources ]]; then - # Cleanup old repository files - cleanup_old_repo_files "php" + # Ensure Sury repo is available + if [[ ! -f /etc/apt/sources.list.d/php.sources ]]; then + # Cleanup old repository files + cleanup_old_repo_files "php" - $STD curl -fsSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb - $STD dpkg -i /tmp/debsuryorg-archive-keyring.deb + $STD curl -fsSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb + $STD dpkg -i /tmp/debsuryorg-archive-keyring.deb - # Use helper function to get fallback suite - local SUITE - SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://packages.sury.org/php") + # Use helper function to get fallback suite + local SUITE + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://packages.sury.org/php") - cat </etc/apt/sources.list.d/php.sources + cat </etc/apt/sources.list.d/php.sources Types: deb URIs: https://packages.sury.org/php/ Suites: $SUITE @@ -2425,78 +2450,78 @@ Components: main Architectures: amd64 arm64 Signed-By: /usr/share/keyrings/deb.sury.org-php.gpg EOF - $STD apt update - fi + $STD apt update + fi - # Build module list - local MODULE_LIST="php${PHP_VERSION}" - IFS=',' read -ra MODULES <<<"$COMBINED_MODULES" - for mod in "${MODULES[@]}"; do - if apt-cache show "php${PHP_VERSION}-${mod}" >/dev/null 2>&1; then - MODULE_LIST+=" php${PHP_VERSION}-${mod}" - else - msg_warn "PHP-Module ${mod} for PHP ${PHP_VERSION} not found – skipping" - fi + # Build module list + local MODULE_LIST="php${PHP_VERSION}" + IFS=',' read -ra MODULES <<<"$COMBINED_MODULES" + for mod in "${MODULES[@]}"; do + if apt-cache show "php${PHP_VERSION}-${mod}" >/dev/null 2>&1; then + MODULE_LIST+=" php${PHP_VERSION}-${mod}" + else + msg_warn "PHP-Module ${mod} for PHP ${PHP_VERSION} not found – skipping" + fi + done + if [[ "$PHP_FPM" == "YES" ]]; then + MODULE_LIST+=" php${PHP_VERSION}-fpm" + fi + + # install apache2 with PHP support if requested + if [[ "$PHP_APACHE" == "YES" ]]; then + if ! dpkg -l | grep -q "libapache2-mod-php${PHP_VERSION}"; then + msg_info "Installing Apache with PHP${PHP_VERSION} support" + $STD apt install -y apache2 libapache2-mod-php${PHP_VERSION} + else + msg_info "Apache with PHP${PHP_VERSION} already installed – skipping install" + fi + fi + + # setup / update PHP modules + $STD apt install -y $MODULE_LIST + cache_installed_version "php" "$PHP_VERSION" + msg_ok "Installed PHP $PHP_VERSION" + + # optional stop old PHP-FPM service + if [[ "$PHP_FPM" == "YES" && -n "$CURRENT_PHP" && "$CURRENT_PHP" != "$PHP_VERSION" ]]; then + $STD systemctl stop php"${CURRENT_PHP}"-fpm || true + $STD systemctl disable php"${CURRENT_PHP}"-fpm || true + fi + + # Patch all relevant php.ini files (silent) + local PHP_INI_PATHS=("/etc/php/${PHP_VERSION}/cli/php.ini") + [[ "$PHP_FPM" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/fpm/php.ini") + [[ "$PHP_APACHE" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/apache2/php.ini") + for ini in "${PHP_INI_PATHS[@]}"; do + if [[ -f "$ini" ]]; then + $STD sed -i "s|^memory_limit = .*|memory_limit = ${PHP_MEMORY_LIMIT}|" "$ini" + $STD sed -i "s|^upload_max_filesize = .*|upload_max_filesize = ${PHP_UPLOAD_MAX_FILESIZE}|" "$ini" + $STD sed -i "s|^post_max_size = .*|post_max_size = ${PHP_POST_MAX_SIZE}|" "$ini" + $STD sed -i "s|^max_execution_time = .*|max_execution_time = ${PHP_MAX_EXECUTION_TIME}|" "$ini" + fi + done + + # patch Apache configuration if needed + if [[ "$PHP_APACHE" == "YES" ]]; then + for mod in $(ls /etc/apache2/mods-enabled/ 2>/dev/null | grep -E '^php[0-9]\.[0-9]\.conf$' | sed 's/\.conf//'); do + if [[ "$mod" != "php${PHP_VERSION}" ]]; then + $STD a2dismod "$mod" || true + fi done - if [[ "$PHP_FPM" == "YES" ]]; then - MODULE_LIST+=" php${PHP_VERSION}-fpm" - fi - - # install apache2 with PHP support if requested - if [[ "$PHP_APACHE" == "YES" ]]; then - if ! dpkg -l | grep -q "libapache2-mod-php${PHP_VERSION}"; then - msg_info "Installing Apache with PHP${PHP_VERSION} support" - $STD apt install -y apache2 libapache2-mod-php${PHP_VERSION} - else - msg_info "Apache with PHP${PHP_VERSION} already installed – skipping install" - fi - fi - - # setup / update PHP modules - $STD apt install -y $MODULE_LIST - cache_installed_version "php" "$PHP_VERSION" - msg_ok "Installed PHP $PHP_VERSION" - - # optional stop old PHP-FPM service - if [[ "$PHP_FPM" == "YES" && -n "$CURRENT_PHP" && "$CURRENT_PHP" != "$PHP_VERSION" ]]; then - $STD systemctl stop php"${CURRENT_PHP}"-fpm || true - $STD systemctl disable php"${CURRENT_PHP}"-fpm || true - fi - - # Patch all relevant php.ini files (silent) - local PHP_INI_PATHS=("/etc/php/${PHP_VERSION}/cli/php.ini") - [[ "$PHP_FPM" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/fpm/php.ini") - [[ "$PHP_APACHE" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/apache2/php.ini") - for ini in "${PHP_INI_PATHS[@]}"; do - if [[ -f "$ini" ]]; then - $STD sed -i "s|^memory_limit = .*|memory_limit = ${PHP_MEMORY_LIMIT}|" "$ini" - $STD sed -i "s|^upload_max_filesize = .*|upload_max_filesize = ${PHP_UPLOAD_MAX_FILESIZE}|" "$ini" - $STD sed -i "s|^post_max_size = .*|post_max_size = ${PHP_POST_MAX_SIZE}|" "$ini" - $STD sed -i "s|^max_execution_time = .*|max_execution_time = ${PHP_MAX_EXECUTION_TIME}|" "$ini" - fi - done - - # patch Apache configuration if needed - if [[ "$PHP_APACHE" == "YES" ]]; then - for mod in $(ls /etc/apache2/mods-enabled/ 2>/dev/null | grep -E '^php[0-9]\.[0-9]\.conf$' | sed 's/\.conf//'); do - if [[ "$mod" != "php${PHP_VERSION}" ]]; then - $STD a2dismod "$mod" || true - fi - done - $STD a2enmod mpm_prefork - $STD a2enmod "php${PHP_VERSION}" - safe_service_restart apache2 || true - fi - - # enable and restart PHP-FPM if requested - if [[ "$PHP_FPM" == "YES" ]]; then - if systemctl list-unit-files | grep -q "php${PHP_VERSION}-fpm.service"; then - $STD systemctl enable php${PHP_VERSION}-fpm - safe_service_restart php${PHP_VERSION}-fpm - else - msg_warn "FPM requested but service php${PHP_VERSION}-fpm not found" - fi + $STD a2enmod mpm_prefork + $STD a2enmod "php${PHP_VERSION}" + safe_service_restart apache2 || true + fi + + # enable and restart PHP-FPM if requested + if [[ "$PHP_FPM" == "YES" ]]; then + if systemctl list-unit-files | grep -q "php${PHP_VERSION}-fpm.service"; then + $STD systemctl enable php${PHP_VERSION}-fpm + safe_service_restart php${PHP_VERSION}-fpm + else + msg_warn "FPM requested but service php${PHP_VERSION}-fpm not found" fi + fi } # ------------------------------------------------------------------------------ @@ -2514,71 +2539,71 @@ EOF # PG_MODULES - Comma-separated list of extensions (e.g. "postgis,contrib") # ------------------------------------------------------------------------------ function setup_postgresql() { - local PG_VERSION="${PG_VERSION:-16}" - local PG_MODULES="${PG_MODULES:-}" - local CURRENT_PG_VERSION="" - local DISTRO_ID DISTRO_CODENAME - local NEED_PG_INSTALL=false - DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') - DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + local PG_VERSION="${PG_VERSION:-16}" + local PG_MODULES="${PG_MODULES:-}" + local CURRENT_PG_VERSION="" + local DISTRO_ID DISTRO_CODENAME + local NEED_PG_INSTALL=false + DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - if command -v psql >/dev/null; then - CURRENT_PG_VERSION="$(psql -V | awk '{print $3}' | cut -d. -f1)" - [[ "$CURRENT_PG_VERSION" != "$PG_VERSION" ]] && NEED_PG_INSTALL=true - else - NEED_PG_INSTALL=true + if command -v psql >/dev/null; then + CURRENT_PG_VERSION="$(psql -V | awk '{print $3}' | cut -d. -f1)" + [[ "$CURRENT_PG_VERSION" != "$PG_VERSION" ]] && NEED_PG_INSTALL=true + else + NEED_PG_INSTALL=true + fi + + if [[ "$NEED_PG_INSTALL" == true ]]; then + msg_info "Installing PostgreSQL $PG_VERSION" + + if [[ -n "$CURRENT_PG_VERSION" ]]; then + $STD su - postgres -c "pg_dumpall > /var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql" + $STD systemctl stop postgresql fi - if [[ "$NEED_PG_INSTALL" == true ]]; then - msg_info "Installing PostgreSQL $PG_VERSION" + # Cleanup old repository files + cleanup_old_repo_files "pgdg" - if [[ -n "$CURRENT_PG_VERSION" ]]; then - $STD su - postgres -c "pg_dumpall > /var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql" - $STD systemctl stop postgresql - fi + # Use helper function to get fallback suite + local SUITE + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://apt.postgresql.org/pub/repos/apt") - # Cleanup old repository files - cleanup_old_repo_files "pgdg" + # PGDG uses special suite naming: ${SUITE}-pgdg + SUITE="${SUITE}-pgdg" - # Use helper function to get fallback suite - local SUITE - SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://apt.postgresql.org/pub/repos/apt") + # Use standardized repo setup + setup_deb822_repo \ + "pgdg" \ + "https://www.postgresql.org/media/keys/ACCC4CF8.asc" \ + "https://apt.postgresql.org/pub/repos/apt" \ + "$SUITE" \ + "main" \ + "amd64 arm64" - # PGDG uses special suite naming: ${SUITE}-pgdg - SUITE="${SUITE}-pgdg" + $STD apt install -y "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}" - # Use standardized repo setup - setup_deb822_repo \ - "pgdg" \ - "https://www.postgresql.org/media/keys/ACCC4CF8.asc" \ - "https://apt.postgresql.org/pub/repos/apt" \ - "$SUITE" \ - "main" \ - "amd64 arm64" - - $STD apt install -y "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}" - - if [[ -n "$CURRENT_PG_VERSION" ]]; then - $STD apt purge -y "postgresql-${CURRENT_PG_VERSION}" "postgresql-client-${CURRENT_PG_VERSION}" || true - $STD su - postgres -c "psql < /var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql" - fi - - $STD systemctl enable --now postgresql - cache_installed_version "postgresql" "$PG_VERSION" - msg_ok "Installed PostgreSQL $PG_VERSION" + if [[ -n "$CURRENT_PG_VERSION" ]]; then + $STD apt purge -y "postgresql-${CURRENT_PG_VERSION}" "postgresql-client-${CURRENT_PG_VERSION}" || true + $STD su - postgres -c "psql < /var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql" fi - if [[ -n "$PG_MODULES" ]]; then - msg_info "Installing PostgreSQL modules" - IFS=',' read -ra MODULES <<<"$PG_MODULES" - for module in "${MODULES[@]}"; do - $STD apt install -y "postgresql-${PG_VERSION}-${module}" || { - msg_warn "Failed to install postgresql-${PG_VERSION}-${module}" - continue - } - done - msg_ok "Installed PostgreSQL modules" - fi + $STD systemctl enable --now postgresql + cache_installed_version "postgresql" "$PG_VERSION" + msg_ok "Installed PostgreSQL $PG_VERSION" + fi + + if [[ -n "$PG_MODULES" ]]; then + msg_info "Installing PostgreSQL modules" + IFS=',' read -ra MODULES <<<"$PG_MODULES" + for module in "${MODULES[@]}"; do + $STD apt install -y "postgresql-${PG_VERSION}-${module}" || { + msg_warn "Failed to install postgresql-${PG_VERSION}-${module}" + continue + } + done + msg_ok "Installed PostgreSQL modules" + fi } # ------------------------------------------------------------------------------ @@ -2595,91 +2620,91 @@ function setup_postgresql() { # ------------------------------------------------------------------------------ function setup_ruby() { - local RUBY_VERSION="${RUBY_VERSION:-3.4.4}" - local RUBY_INSTALL_RAILS="${RUBY_INSTALL_RAILS:-true}" - local RBENV_DIR="$HOME/.rbenv" - local RBENV_BIN="$RBENV_DIR/bin/rbenv" - local PROFILE_FILE="$HOME/.profile" - local TMP_DIR=$(mktemp -d) - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "ruby") + local RUBY_VERSION="${RUBY_VERSION:-3.4.4}" + local RUBY_INSTALL_RAILS="${RUBY_INSTALL_RAILS:-true}" + local RBENV_DIR="$HOME/.rbenv" + local RBENV_BIN="$RBENV_DIR/bin/rbenv" + local PROFILE_FILE="$HOME/.profile" + local TMP_DIR=$(mktemp -d) + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "ruby") - msg_info "Installing Ruby $RUBY_VERSION" + msg_info "Installing Ruby $RUBY_VERSION" - ensure_dependencies jq - - local RBENV_RELEASE - RBENV_RELEASE=$(curl -fsSL https://api.github.com/repos/rbenv/rbenv/releases/latest | jq -r '.tag_name' | sed 's/^v//') - if [[ -z "$RBENV_RELEASE" ]]; then - msg_error "Failed to fetch latest rbenv version" - rm -rf "$TMP_DIR" - return 1 - fi - - curl -fsSL "https://github.com/rbenv/rbenv/archive/refs/tags/v${RBENV_RELEASE}.tar.gz" -o "$TMP_DIR/rbenv.tar.gz" || { - msg_error "Failed to download rbenv" - rm -rf "$TMP_DIR" - return 1 - } - - tar -xzf "$TMP_DIR/rbenv.tar.gz" -C "$TMP_DIR" || { - msg_error "Failed to extract rbenv" - rm -rf "$TMP_DIR" - return 1 - } - - mkdir -p "$RBENV_DIR" - cp -r "$TMP_DIR/rbenv-${RBENV_RELEASE}/." "$RBENV_DIR/" - cd "$RBENV_DIR" && src/configure && $STD make -C src - - local RUBY_BUILD_RELEASE - RUBY_BUILD_RELEASE=$(curl -fsSL https://api.github.com/repos/rbenv/ruby-build/releases/latest | jq -r '.tag_name' | sed 's/^v//') - if [[ -z "$RUBY_BUILD_RELEASE" ]]; then - msg_error "Failed to fetch latest ruby-build version" - rm -rf "$TMP_DIR" - return 1 - fi - - curl -fsSL "https://github.com/rbenv/ruby-build/archive/refs/tags/v${RUBY_BUILD_RELEASE}.tar.gz" -o "$TMP_DIR/ruby-build.tar.gz" || { - msg_error "Failed to download ruby-build" - rm -rf "$TMP_DIR" - return 1 - } - - tar -xzf "$TMP_DIR/ruby-build.tar.gz" -C "$TMP_DIR" || { - msg_error "Failed to extract ruby-build" - rm -rf "$TMP_DIR" - return 1 - } - - mkdir -p "$RBENV_DIR/plugins/ruby-build" - cp -r "$TMP_DIR/ruby-build-${RUBY_BUILD_RELEASE}/." "$RBENV_DIR/plugins/ruby-build/" - echo "$RUBY_BUILD_RELEASE" >"$RBENV_DIR/plugins/ruby-build/RUBY_BUILD_version.txt" - - if ! grep -q 'rbenv init' "$PROFILE_FILE"; then - echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >>"$PROFILE_FILE" - echo 'eval "$(rbenv init -)"' >>"$PROFILE_FILE" - fi - - export PATH="$RBENV_DIR/bin:$PATH" - eval "$("$RBENV_BIN" init - bash)" - - if ! "$RBENV_BIN" versions --bare | grep -qx "$RUBY_VERSION"; then - $STD "$RBENV_BIN" install "$RUBY_VERSION" - fi - - "$RBENV_BIN" global "$RUBY_VERSION" - hash -r - - if [[ "$RUBY_INSTALL_RAILS" == "true" ]]; then - $STD gem install rails - local RAILS_VERSION=$(rails -v 2>/dev/null | awk '{print $2}') - msg_ok "Installed Rails $RAILS_VERSION" - fi + ensure_dependencies jq + local RBENV_RELEASE + RBENV_RELEASE=$(curl -fsSL https://api.github.com/repos/rbenv/rbenv/releases/latest | jq -r '.tag_name' | sed 's/^v//') + if [[ -z "$RBENV_RELEASE" ]]; then + msg_error "Failed to fetch latest rbenv version" rm -rf "$TMP_DIR" - cache_installed_version "ruby" "$RUBY_VERSION" - msg_ok "Installed Ruby $RUBY_VERSION" + return 1 + fi + + curl -fsSL "https://github.com/rbenv/rbenv/archive/refs/tags/v${RBENV_RELEASE}.tar.gz" -o "$TMP_DIR/rbenv.tar.gz" || { + msg_error "Failed to download rbenv" + rm -rf "$TMP_DIR" + return 1 + } + + tar -xzf "$TMP_DIR/rbenv.tar.gz" -C "$TMP_DIR" || { + msg_error "Failed to extract rbenv" + rm -rf "$TMP_DIR" + return 1 + } + + mkdir -p "$RBENV_DIR" + cp -r "$TMP_DIR/rbenv-${RBENV_RELEASE}/." "$RBENV_DIR/" + cd "$RBENV_DIR" && src/configure && $STD make -C src + + local RUBY_BUILD_RELEASE + RUBY_BUILD_RELEASE=$(curl -fsSL https://api.github.com/repos/rbenv/ruby-build/releases/latest | jq -r '.tag_name' | sed 's/^v//') + if [[ -z "$RUBY_BUILD_RELEASE" ]]; then + msg_error "Failed to fetch latest ruby-build version" + rm -rf "$TMP_DIR" + return 1 + fi + + curl -fsSL "https://github.com/rbenv/ruby-build/archive/refs/tags/v${RUBY_BUILD_RELEASE}.tar.gz" -o "$TMP_DIR/ruby-build.tar.gz" || { + msg_error "Failed to download ruby-build" + rm -rf "$TMP_DIR" + return 1 + } + + tar -xzf "$TMP_DIR/ruby-build.tar.gz" -C "$TMP_DIR" || { + msg_error "Failed to extract ruby-build" + rm -rf "$TMP_DIR" + return 1 + } + + mkdir -p "$RBENV_DIR/plugins/ruby-build" + cp -r "$TMP_DIR/ruby-build-${RUBY_BUILD_RELEASE}/." "$RBENV_DIR/plugins/ruby-build/" + echo "$RUBY_BUILD_RELEASE" >"$RBENV_DIR/plugins/ruby-build/RUBY_BUILD_version.txt" + + if ! grep -q 'rbenv init' "$PROFILE_FILE"; then + echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >>"$PROFILE_FILE" + echo 'eval "$(rbenv init -)"' >>"$PROFILE_FILE" + fi + + export PATH="$RBENV_DIR/bin:$PATH" + eval "$("$RBENV_BIN" init - bash)" + + if ! "$RBENV_BIN" versions --bare | grep -qx "$RUBY_VERSION"; then + $STD "$RBENV_BIN" install "$RUBY_VERSION" + fi + + "$RBENV_BIN" global "$RUBY_VERSION" + hash -r + + if [[ "$RUBY_INSTALL_RAILS" == "true" ]]; then + $STD gem install rails + local RAILS_VERSION=$(rails -v 2>/dev/null | awk '{print $2}') + msg_ok "Installed Rails $RAILS_VERSION" + fi + + rm -rf "$TMP_DIR" + cache_installed_version "ruby" "$RUBY_VERSION" + msg_ok "Installed Ruby $RUBY_VERSION" } # ------------------------------------------------------------------------------ @@ -2696,86 +2721,103 @@ function setup_ruby() { # ------------------------------------------------------------------------------ function setup_clickhouse() { - local CLICKHOUSE_VERSION="${CLICKHOUSE_VERSION:-latest}" - local DISTRO_ID DISTRO_CODENAME - DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') - DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + local CLICKHOUSE_VERSION="${CLICKHOUSE_VERSION:-latest}" + local DISTRO_ID DISTRO_CODENAME + DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - # Determine latest version if needed - if [[ "$CLICKHOUSE_VERSION" == "latest" ]]; then - CLICKHOUSE_VERSION=$(curl -fsSL https://packages.clickhouse.com/tgz/stable/ | - grep -oP 'clickhouse-common-static-\K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | - sort -V | - tail -n1) - if [[ -z "$CLICKHOUSE_VERSION" ]]; then - msg_error "Could not determine latest ClickHouse version" - return 1 - fi + # Determine latest version if needed + if [[ "$CLICKHOUSE_VERSION" == "latest" ]]; then + # Try multiple methods to get the latest version + CLICKHOUSE_VERSION=$(curl -fsSL https://packages.clickhouse.com/tgz/stable/ 2>/dev/null | + grep -oP 'clickhouse-common-static-\K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | + sort -V | + tail -n1) + + # Fallback: Try GitHub releases API + if [[ -z "$CLICKHOUSE_VERSION" ]]; then + CLICKHOUSE_VERSION=$(curl -fsSL https://api.github.com/repos/ClickHouse/ClickHouse/releases/latest 2>/dev/null | + grep -oP '"tag_name":\s*"v\K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | + head -n1) fi - local CURRENT_VERSION="" - if command -v clickhouse-server >/dev/null 2>&1; then - CURRENT_VERSION=$(clickhouse-server --version 2>/dev/null | grep -oP 'version \K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -n1) + # Final fallback: Parse HTML more liberally + if [[ -z "$CLICKHOUSE_VERSION" ]]; then + CLICKHOUSE_VERSION=$(curl -fsSL https://packages.clickhouse.com/tgz/stable/ 2>/dev/null | + grep -oP '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+(?=/clickhouse)' | + sort -V | + tail -n1) fi - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "clickhouse") - - # Check if already at target version - if [[ "$CURRENT_VERSION" == "$CLICKHOUSE_VERSION" ]]; then - if [[ "$CACHED_VERSION" == "$CLICKHOUSE_VERSION" ]]; then - upgrade_package clickhouse-server - upgrade_package clickhouse-client - else - msg_info "Upgrading ClickHouse $CLICKHOUSE_VERSION" - $STD apt update - $STD apt install --only-upgrade -y clickhouse-server clickhouse-client - cache_installed_version "clickhouse" "$CLICKHOUSE_VERSION" - msg_ok "Upgraded ClickHouse $CLICKHOUSE_VERSION" - fi - return 0 + if [[ -z "$CLICKHOUSE_VERSION" ]]; then + msg_error "Could not determine latest ClickHouse version" + return 1 fi + fi - msg_info "Installing ClickHouse $CLICKHOUSE_VERSION" + local CURRENT_VERSION="" + if command -v clickhouse-server >/dev/null 2>&1; then + CURRENT_VERSION=$(clickhouse-server --version 2>/dev/null | grep -oP 'version \K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -n1) + fi - # Stop existing service if upgrading - if [[ -n "$CURRENT_VERSION" ]]; then - $STD systemctl stop clickhouse-server || true + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "clickhouse") + + # Check if already at target version + if [[ "$CURRENT_VERSION" == "$CLICKHOUSE_VERSION" ]]; then + if [[ "$CACHED_VERSION" == "$CLICKHOUSE_VERSION" ]]; then + upgrade_package clickhouse-server + upgrade_package clickhouse-client + else + msg_info "Upgrading ClickHouse $CLICKHOUSE_VERSION" + $STD apt update + $STD apt install --only-upgrade -y clickhouse-server clickhouse-client + cache_installed_version "clickhouse" "$CLICKHOUSE_VERSION" + msg_ok "Upgraded ClickHouse $CLICKHOUSE_VERSION" fi + return 0 + fi - # Cleanup old repository files - cleanup_old_repo_files "clickhouse" + msg_info "Installing ClickHouse $CLICKHOUSE_VERSION" - # Ensure dependencies - ensure_dependencies apt-transport-https ca-certificates dirmngr gnupg + # Stop existing service if upgrading + if [[ -n "$CURRENT_VERSION" ]]; then + $STD systemctl stop clickhouse-server || true + fi - # Use helper function to get fallback suite - local SUITE - SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://packages.clickhouse.com/deb") + # Cleanup old repository files + cleanup_old_repo_files "clickhouse" - # Use standardized repo setup - setup_deb822_repo \ - "clickhouse" \ - "https://packages.clickhouse.com/rpm/lts/repodata/repomd.xml.key" \ - "https://packages.clickhouse.com/deb" \ - "$SUITE" \ - "main" \ - "amd64 arm64" + # Ensure dependencies + ensure_dependencies apt-transport-https ca-certificates dirmngr gnupg - # Install ClickHouse packages - export DEBIAN_FRONTEND=noninteractive - $STD apt install -y clickhouse-server clickhouse-client + # Use helper function to get fallback suite + local SUITE + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://packages.clickhouse.com/deb") - # Create data directory if it doesn't exist - mkdir -p /var/lib/clickhouse - chown -R clickhouse:clickhouse /var/lib/clickhouse + # Use standardized repo setup + setup_deb822_repo \ + "clickhouse" \ + "https://packages.clickhouse.com/rpm/lts/repodata/repomd.xml.key" \ + "https://packages.clickhouse.com/deb" \ + "$SUITE" \ + "main" \ + "amd64 arm64" - # Enable and start service - $STD systemctl enable clickhouse-server - safe_service_restart clickhouse-server + # Install ClickHouse packages + export DEBIAN_FRONTEND=noninteractive + $STD apt install -y clickhouse-server clickhouse-client - cache_installed_version "clickhouse" "$CLICKHOUSE_VERSION" - msg_ok "Installed ClickHouse $CLICKHOUSE_VERSION" + # Create data directory if it doesn't exist + mkdir -p /var/lib/clickhouse + chown -R clickhouse:clickhouse /var/lib/clickhouse + + # Enable and start service + $STD systemctl enable clickhouse-server + safe_service_restart clickhouse-server + + cache_installed_version "clickhouse" "$CLICKHOUSE_VERSION" + msg_ok "Installed ClickHouse $CLICKHOUSE_VERSION" } # ------------------------------------------------------------------------------ @@ -2796,64 +2838,64 @@ function setup_clickhouse() { # ------------------------------------------------------------------------------ function setup_rust() { - local RUST_TOOLCHAIN="${RUST_TOOLCHAIN:-stable}" - local RUST_CRATES="${RUST_CRATES:-}" - local CARGO_BIN="${HOME}/.cargo/bin" - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "rust") + local RUST_TOOLCHAIN="${RUST_TOOLCHAIN:-stable}" + local RUST_CRATES="${RUST_CRATES:-}" + local CARGO_BIN="${HOME}/.cargo/bin" + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "rust") - if ! command -v rustup &>/dev/null; then - msg_info "Installing Rust" - curl -fsSL https://sh.rustup.rs | $STD sh -s -- -y --default-toolchain "$RUST_TOOLCHAIN" || { - msg_error "Failed to install Rust" - return 1 - } - export PATH="$CARGO_BIN:$PATH" - echo 'export PATH="$HOME/.cargo/bin:$PATH"' >>"$HOME/.profile" - local RUST_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}') - cache_installed_version "rust" "$RUST_VERSION" - msg_ok "Installed Rust $RUST_VERSION" - else - $STD rustup install "$RUST_TOOLCHAIN" - $STD rustup default "$RUST_TOOLCHAIN" - $STD rustup update "$RUST_TOOLCHAIN" - local RUST_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}') - cache_installed_version "rust" "$RUST_VERSION" - msg_ok "Updated Rust toolchain to $RUST_TOOLCHAIN ($RUST_VERSION)" - fi + if ! command -v rustup &>/dev/null; then + msg_info "Installing Rust" + curl -fsSL https://sh.rustup.rs | $STD sh -s -- -y --default-toolchain "$RUST_TOOLCHAIN" || { + msg_error "Failed to install Rust" + return 1 + } + export PATH="$CARGO_BIN:$PATH" + echo 'export PATH="$HOME/.cargo/bin:$PATH"' >>"$HOME/.profile" + local RUST_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}') + cache_installed_version "rust" "$RUST_VERSION" + msg_ok "Installed Rust $RUST_VERSION" + else + $STD rustup install "$RUST_TOOLCHAIN" + $STD rustup default "$RUST_TOOLCHAIN" + $STD rustup update "$RUST_TOOLCHAIN" + local RUST_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}') + cache_installed_version "rust" "$RUST_VERSION" + msg_ok "Updated Rust toolchain to $RUST_TOOLCHAIN ($RUST_VERSION)" + fi - if [[ -n "$RUST_CRATES" ]]; then - IFS=',' read -ra CRATES <<<"$RUST_CRATES" - for crate in "${CRATES[@]}"; do - local NAME VER INSTALLED_VER - if [[ "$crate" == *"@"* ]]; then - NAME="${crate%@*}" - VER="${crate##*@}" - else - NAME="$crate" - VER="" - fi + if [[ -n "$RUST_CRATES" ]]; then + IFS=',' read -ra CRATES <<<"$RUST_CRATES" + for crate in "${CRATES[@]}"; do + local NAME VER INSTALLED_VER + if [[ "$crate" == *"@"* ]]; then + NAME="${crate%@*}" + VER="${crate##*@}" + else + NAME="$crate" + VER="" + fi - INSTALLED_VER=$(cargo install --list 2>/dev/null | awk "/^$NAME v[0-9]/ {print \$2}" | tr -d 'v') + INSTALLED_VER=$(cargo install --list 2>/dev/null | awk "/^$NAME v[0-9]/ {print \$2}" | tr -d 'v') - if [[ -n "$INSTALLED_VER" ]]; then - if [[ -n "$VER" && "$VER" != "$INSTALLED_VER" ]]; then - msg_info "Updating $NAME: $INSTALLED_VER → $VER" - $STD cargo install "$NAME" --version "$VER" --force - msg_ok "Updated $NAME to $VER" - elif [[ -z "$VER" ]]; then - msg_info "Updating $NAME: $INSTALLED_VER → latest" - $STD cargo install "$NAME" --force - local NEW_VER=$(cargo install --list 2>/dev/null | awk "/^$NAME v[0-9]/ {print \$2}" | tr -d 'v') - msg_ok "Updated $NAME to $NEW_VER" - fi - else - msg_info "Installing $NAME ${VER:+$VER}" - $STD cargo install "$NAME" ${VER:+--version "$VER"} - msg_ok "Installed $NAME ${VER:-latest}" - fi - done - fi + if [[ -n "$INSTALLED_VER" ]]; then + if [[ -n "$VER" && "$VER" != "$INSTALLED_VER" ]]; then + msg_info "Updating $NAME: $INSTALLED_VER → $VER" + $STD cargo install "$NAME" --version "$VER" --force + msg_ok "Updated $NAME to $VER" + elif [[ -z "$VER" ]]; then + msg_info "Updating $NAME: $INSTALLED_VER → latest" + $STD cargo install "$NAME" --force + local NEW_VER=$(cargo install --list 2>/dev/null | awk "/^$NAME v[0-9]/ {print \$2}" | tr -d 'v') + msg_ok "Updated $NAME to $NEW_VER" + fi + else + msg_info "Installing $NAME ${VER:+$VER}" + $STD cargo install "$NAME" ${VER:+--version "$VER"} + msg_ok "Installed $NAME ${VER:-latest}" + fi + done + fi } # ------------------------------------------------------------------------------ @@ -2865,112 +2907,112 @@ function setup_rust() { # ------------------------------------------------------------------------------ function setup_uv() { - local UV_BIN="/usr/local/bin/uv" - local TMP_DIR=$(mktemp -d) - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "uv") + local UV_BIN="/usr/local/bin/uv" + local TMP_DIR=$(mktemp -d) + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "uv") - local ARCH=$(uname -m) - local UV_TAR + local ARCH=$(uname -m) + local UV_TAR - case "$ARCH" in - x86_64) - if grep -qi "alpine" /etc/os-release; then - UV_TAR="uv-x86_64-unknown-linux-musl.tar.gz" - else - UV_TAR="uv-x86_64-unknown-linux-gnu.tar.gz" - fi - ;; - aarch64) - if grep -qi "alpine" /etc/os-release; then - UV_TAR="uv-aarch64-unknown-linux-musl.tar.gz" - else - UV_TAR="uv-aarch64-unknown-linux-gnu.tar.gz" - fi - ;; - *) - msg_error "Unsupported architecture: $ARCH" - rm -rf "$TMP_DIR" - return 1 - ;; - esac - - ensure_dependencies jq - - local LATEST_VERSION - LATEST_VERSION=$(curl -fsSL https://api.github.com/repos/astral-sh/uv/releases/latest | - jq -r '.tag_name' | sed 's/^v//') - - if [[ -z "$LATEST_VERSION" ]]; then - msg_error "Could not fetch latest uv version" - rm -rf "$TMP_DIR" - return 1 - fi - - if [[ -x "$UV_BIN" ]]; then - local INSTALLED_VERSION - INSTALLED_VERSION=$($UV_BIN -V 2>/dev/null | awk '{print $2}') - if [[ "$INSTALLED_VERSION" == "$LATEST_VERSION" ]]; then - if [[ "$CACHED_VERSION" == "$LATEST_VERSION" ]]; then - rm -rf "$TMP_DIR" - return 0 - fi - cache_installed_version "uv" "$LATEST_VERSION" - rm -rf "$TMP_DIR" - return 0 - else - msg_info "Updating uv from $INSTALLED_VERSION to $LATEST_VERSION" - fi + case "$ARCH" in + x86_64) + if grep -qi "alpine" /etc/os-release; then + UV_TAR="uv-x86_64-unknown-linux-musl.tar.gz" else - msg_info "Installing uv $LATEST_VERSION" + UV_TAR="uv-x86_64-unknown-linux-gnu.tar.gz" fi - - local UV_URL="https://github.com/astral-sh/uv/releases/latest/download/${UV_TAR}" - curl -fsSL "$UV_URL" -o "$TMP_DIR/uv.tar.gz" || { - msg_error "Failed to download uv" - rm -rf "$TMP_DIR" - return 1 - } - - tar -xzf "$TMP_DIR/uv.tar.gz" -C "$TMP_DIR" || { - msg_error "Failed to extract uv" - rm -rf "$TMP_DIR" - return 1 - } - - install -m 755 "$TMP_DIR"/*/uv "$UV_BIN" || { - msg_error "Failed to install uv binary" - rm -rf "$TMP_DIR" - return 1 - } - + ;; + aarch64) + if grep -qi "alpine" /etc/os-release; then + UV_TAR="uv-aarch64-unknown-linux-musl.tar.gz" + else + UV_TAR="uv-aarch64-unknown-linux-gnu.tar.gz" + fi + ;; + *) + msg_error "Unsupported architecture: $ARCH" rm -rf "$TMP_DIR" - ensure_usr_local_bin_persist - export PATH="/usr/local/bin:$PATH" + return 1 + ;; + esac - $STD uv python update-shell || true - cache_installed_version "uv" "$LATEST_VERSION" - msg_ok "Installed uv $LATEST_VERSION" + ensure_dependencies jq - if [[ -n "${PYTHON_VERSION:-}" ]]; then - local VERSION_MATCH - VERSION_MATCH=$(uv python list --only-downloads | - grep -E "^cpython-${PYTHON_VERSION//./\\.}\.[0-9]+-linux" | - cut -d'-' -f2 | sort -V | tail -n1) + local LATEST_VERSION + LATEST_VERSION=$(curl -fsSL https://api.github.com/repos/astral-sh/uv/releases/latest | + jq -r '.tag_name' | sed 's/^v//') - if [[ -z "$VERSION_MATCH" ]]; then - msg_error "No matching Python $PYTHON_VERSION.x version found" - return 1 - fi + if [[ -z "$LATEST_VERSION" ]]; then + msg_error "Could not fetch latest uv version" + rm -rf "$TMP_DIR" + return 1 + fi - if ! uv python list | grep -q "cpython-${VERSION_MATCH}-linux.*uv/python"; then - $STD uv python install "$VERSION_MATCH" || { - msg_error "Failed to install Python $VERSION_MATCH" - return 1 - } - msg_ok "Installed Python $VERSION_MATCH" - fi + if [[ -x "$UV_BIN" ]]; then + local INSTALLED_VERSION + INSTALLED_VERSION=$($UV_BIN -V 2>/dev/null | awk '{print $2}') + if [[ "$INSTALLED_VERSION" == "$LATEST_VERSION" ]]; then + if [[ "$CACHED_VERSION" == "$LATEST_VERSION" ]]; then + rm -rf "$TMP_DIR" + return 0 + fi + cache_installed_version "uv" "$LATEST_VERSION" + rm -rf "$TMP_DIR" + return 0 + else + msg_info "Updating uv from $INSTALLED_VERSION to $LATEST_VERSION" fi + else + msg_info "Installing uv $LATEST_VERSION" + fi + + local UV_URL="https://github.com/astral-sh/uv/releases/latest/download/${UV_TAR}" + curl -fsSL "$UV_URL" -o "$TMP_DIR/uv.tar.gz" || { + msg_error "Failed to download uv" + rm -rf "$TMP_DIR" + return 1 + } + + tar -xzf "$TMP_DIR/uv.tar.gz" -C "$TMP_DIR" || { + msg_error "Failed to extract uv" + rm -rf "$TMP_DIR" + return 1 + } + + install -m 755 "$TMP_DIR"/*/uv "$UV_BIN" || { + msg_error "Failed to install uv binary" + rm -rf "$TMP_DIR" + return 1 + } + + rm -rf "$TMP_DIR" + ensure_usr_local_bin_persist + export PATH="/usr/local/bin:$PATH" + + $STD uv python update-shell || true + cache_installed_version "uv" "$LATEST_VERSION" + msg_ok "Installed uv $LATEST_VERSION" + + if [[ -n "${PYTHON_VERSION:-}" ]]; then + local VERSION_MATCH + VERSION_MATCH=$(uv python list --only-downloads | + grep -E "^cpython-${PYTHON_VERSION//./\\.}\.[0-9]+-linux" | + cut -d'-' -f2 | sort -V | tail -n1) + + if [[ -z "$VERSION_MATCH" ]]; then + msg_error "No matching Python $PYTHON_VERSION.x version found" + return 1 + fi + + if ! uv python list | grep -q "cpython-${VERSION_MATCH}-linux.*uv/python"; then + $STD uv python install "$VERSION_MATCH" || { + msg_error "Failed to install Python $VERSION_MATCH" + return 1 + } + msg_ok "Installed Python $VERSION_MATCH" + fi + fi } # ------------------------------------------------------------------------------ @@ -2983,65 +3025,65 @@ function setup_uv() { # ------------------------------------------------------------------------------ function setup_yq() { - local TMP_DIR=$(mktemp -d) - local BINARY_PATH="/usr/local/bin/yq" - local GITHUB_REPO="mikefarah/yq" - local CURRENT_VERSION="" - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "yq") + local TMP_DIR=$(mktemp -d) + local BINARY_PATH="/usr/local/bin/yq" + local GITHUB_REPO="mikefarah/yq" + local CURRENT_VERSION="" + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "yq") - ensure_dependencies jq - ensure_usr_local_bin_persist + ensure_dependencies jq + ensure_usr_local_bin_persist - if command -v yq &>/dev/null; then - if ! yq --version 2>&1 | grep -q 'mikefarah'; then - rm -f "$(command -v yq)" - else - CURRENT_VERSION=$(yq --version 2>/dev/null | awk '{print $NF}' | sed 's/^v//') - fi + if command -v yq &>/dev/null; then + if ! yq --version 2>&1 | grep -q 'mikefarah'; then + rm -f "$(command -v yq)" + else + CURRENT_VERSION=$(yq --version 2>/dev/null | awk '{print $NF}' | sed 's/^v//') fi + fi - local LATEST_VERSION - LATEST_VERSION=$(curl -fsSL "https://api.github.com/repos/${GITHUB_REPO}/releases/latest" | - jq -r '.tag_name' | sed 's/^v//') - - if [[ -z "$LATEST_VERSION" ]]; then - msg_error "Could not determine latest yq version" - rm -rf "$TMP_DIR" - return 1 - fi - - if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$LATEST_VERSION" ]]; then - if [[ "$CACHED_VERSION" == "$LATEST_VERSION" ]]; then - rm -rf "$TMP_DIR" - return 0 - fi - cache_installed_version "yq" "$LATEST_VERSION" - rm -rf "$TMP_DIR" - return 0 - fi - - msg_info "Installing yq $LATEST_VERSION" - curl -fsSL "https://github.com/${GITHUB_REPO}/releases/download/v${LATEST_VERSION}/yq_linux_amd64" -o "$TMP_DIR/yq" || { - msg_error "Failed to download yq" - rm -rf "$TMP_DIR" - return 1 - } - - chmod +x "$TMP_DIR/yq" - mv "$TMP_DIR/yq" "$BINARY_PATH" - - if [[ ! -x "$BINARY_PATH" ]]; then - msg_error "Failed to install yq" - rm -rf "$TMP_DIR" - return 1 - fi + local LATEST_VERSION + LATEST_VERSION=$(curl -fsSL "https://api.github.com/repos/${GITHUB_REPO}/releases/latest" | + jq -r '.tag_name' | sed 's/^v//') + if [[ -z "$LATEST_VERSION" ]]; then + msg_error "Could not determine latest yq version" rm -rf "$TMP_DIR" - hash -r + return 1 + fi - local FINAL_VERSION - FINAL_VERSION=$("$BINARY_PATH" --version 2>/dev/null | awk '{print $NF}' | sed 's/^v//') - cache_installed_version "yq" "$FINAL_VERSION" - msg_ok "Installed yq $FINAL_VERSION" + if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$LATEST_VERSION" ]]; then + if [[ "$CACHED_VERSION" == "$LATEST_VERSION" ]]; then + rm -rf "$TMP_DIR" + return 0 + fi + cache_installed_version "yq" "$LATEST_VERSION" + rm -rf "$TMP_DIR" + return 0 + fi + + msg_info "Installing yq $LATEST_VERSION" + curl -fsSL "https://github.com/${GITHUB_REPO}/releases/download/v${LATEST_VERSION}/yq_linux_amd64" -o "$TMP_DIR/yq" || { + msg_error "Failed to download yq" + rm -rf "$TMP_DIR" + return 1 + } + + chmod +x "$TMP_DIR/yq" + mv "$TMP_DIR/yq" "$BINARY_PATH" + + if [[ ! -x "$BINARY_PATH" ]]; then + msg_error "Failed to install yq" + rm -rf "$TMP_DIR" + return 1 + fi + + rm -rf "$TMP_DIR" + hash -r + + local FINAL_VERSION + FINAL_VERSION=$("$BINARY_PATH" --version 2>/dev/null | awk '{print $NF}' | sed 's/^v//') + cache_installed_version "yq" "$FINAL_VERSION" + msg_ok "Installed yq $FINAL_VERSION" } From 703b0ec5f267450af506cb21478427c3ce62d812 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Mon, 20 Oct 2025 08:17:00 +0200 Subject: [PATCH 1449/1733] deb13 --- ct/jellyfin.sh | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 ct/jellyfin.sh diff --git a/ct/jellyfin.sh b/ct/jellyfin.sh new file mode 100644 index 000000000..b230c7cd3 --- /dev/null +++ b/ct/jellyfin.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 Community-Scripts ORG +# Author: tteck (tteckster) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://jellyfin.org/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Setting Up Hardware Acceleration" +if [[ ! -d /etc/apt/keyrings ]]; then + mkdir -p /etc/apt/keyrings +fi +curl -fsSL https://repositories.intel.com/graphics/intel-graphics.key | gpg --dearmor --yes -o /etc/apt/keyrings/intel-graphics.gpg +cat <<'EOF' | sudo tee /etc/apt/sources.list.d/intel-gpu.sources > /dev/null +Types: deb +URIs: https://repositories.intel.com/graphics/ubuntu +Suites: stable +Components: main +Architectures: amd64 +Signed-By: /etc/apt/keyrings/intel-graphics.gpg +EOF + +$STD apt-get -y install {va-driver-all,ocl-icd-libopencl1,intel-opencl-icd,vainfo,intel-gpu-tools} +if [[ "$CTTYPE" == "0" ]]; then + chgrp video /dev/dri + chmod 755 /dev/dri + chmod 660 /dev/dri/* + $STD adduser $(id -u -n) video + $STD adduser $(id -u -n) render +fi +msg_ok "Set Up Hardware Acceleration" + +msg_info "Installing Jellyfin" +VERSION="$(awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release)" +# Download the repository signing key and install it to the keyring directory +curl -fsSL https://repo.jellyfin.org/jellyfin_team.gpg.key | gpg --dearmor --yes --output /etc/apt/keyrings/jellyfin.gpg +# Install the Deb822 format jellyfin.sources entry +cat </etc/apt/sources.list.d/jellyfin.sources +Types: deb +URIs: https://repo.jellyfin.org/${PCT_OSTYPE} +Suites: ${VERSION} +Components: main +Architectures: amd64 +Signed-By: /etc/apt/keyrings/jellyfin.gpg +EOF +# Install Jellyfin using the metapackage (which will fetch jellyfin-server, jellyfin-web, and jellyfin-ffmpeg5) +$STD apt-get update +$STD apt-get install -y jellyfin +sed -i 's/"MinimumLevel": "Information"/"MinimumLevel": "Error"/g' /etc/jellyfin/logging.json +chown -R jellyfin:adm /etc/jellyfin +sleep 10 +systemctl restart jellyfin +if [[ "$CTTYPE" == "0" ]]; then + sed -i -e 's/^ssl-cert:x:104:$/render:x:104:root,jellyfin/' -e 's/^render:x:108:root,jellyfin$/ssl-cert:x:108:/' /etc/group +else + sed -i -e 's/^ssl-cert:x:104:$/render:x:104:jellyfin/' -e 's/^render:x:108:jellyfin$/ssl-cert:x:108:/' /etc/group +fi +msg_ok "Installed Jellyfin" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt -y autoremove +$STD apt -y autoclean +$STD apt -y clean +msg_ok "Cleaned" From 189f90d9a7cab8147230df4a113b25a4c5e2844a Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Mon, 20 Oct 2025 08:17:56 +0200 Subject: [PATCH 1450/1733] deb13 --- install/jellyfin-install.sh | 74 +++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 install/jellyfin-install.sh diff --git a/install/jellyfin-install.sh b/install/jellyfin-install.sh new file mode 100644 index 000000000..b230c7cd3 --- /dev/null +++ b/install/jellyfin-install.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 Community-Scripts ORG +# Author: tteck (tteckster) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://jellyfin.org/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Setting Up Hardware Acceleration" +if [[ ! -d /etc/apt/keyrings ]]; then + mkdir -p /etc/apt/keyrings +fi +curl -fsSL https://repositories.intel.com/graphics/intel-graphics.key | gpg --dearmor --yes -o /etc/apt/keyrings/intel-graphics.gpg +cat <<'EOF' | sudo tee /etc/apt/sources.list.d/intel-gpu.sources > /dev/null +Types: deb +URIs: https://repositories.intel.com/graphics/ubuntu +Suites: stable +Components: main +Architectures: amd64 +Signed-By: /etc/apt/keyrings/intel-graphics.gpg +EOF + +$STD apt-get -y install {va-driver-all,ocl-icd-libopencl1,intel-opencl-icd,vainfo,intel-gpu-tools} +if [[ "$CTTYPE" == "0" ]]; then + chgrp video /dev/dri + chmod 755 /dev/dri + chmod 660 /dev/dri/* + $STD adduser $(id -u -n) video + $STD adduser $(id -u -n) render +fi +msg_ok "Set Up Hardware Acceleration" + +msg_info "Installing Jellyfin" +VERSION="$(awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release)" +# Download the repository signing key and install it to the keyring directory +curl -fsSL https://repo.jellyfin.org/jellyfin_team.gpg.key | gpg --dearmor --yes --output /etc/apt/keyrings/jellyfin.gpg +# Install the Deb822 format jellyfin.sources entry +cat </etc/apt/sources.list.d/jellyfin.sources +Types: deb +URIs: https://repo.jellyfin.org/${PCT_OSTYPE} +Suites: ${VERSION} +Components: main +Architectures: amd64 +Signed-By: /etc/apt/keyrings/jellyfin.gpg +EOF +# Install Jellyfin using the metapackage (which will fetch jellyfin-server, jellyfin-web, and jellyfin-ffmpeg5) +$STD apt-get update +$STD apt-get install -y jellyfin +sed -i 's/"MinimumLevel": "Information"/"MinimumLevel": "Error"/g' /etc/jellyfin/logging.json +chown -R jellyfin:adm /etc/jellyfin +sleep 10 +systemctl restart jellyfin +if [[ "$CTTYPE" == "0" ]]; then + sed -i -e 's/^ssl-cert:x:104:$/render:x:104:root,jellyfin/' -e 's/^render:x:108:root,jellyfin$/ssl-cert:x:108:/' /etc/group +else + sed -i -e 's/^ssl-cert:x:104:$/render:x:104:jellyfin/' -e 's/^render:x:108:jellyfin$/ssl-cert:x:108:/' /etc/group +fi +msg_ok "Installed Jellyfin" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt -y autoremove +$STD apt -y autoclean +$STD apt -y clean +msg_ok "Cleaned" From d0a3137185372ae989d3c276c3eb4338fe2d0dd7 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Mon, 20 Oct 2025 08:18:37 +0200 Subject: [PATCH 1451/1733] deb13 --- ct/jellyfin.sh | 99 ++++++++++++++++++-------------------------------- 1 file changed, 35 insertions(+), 64 deletions(-) diff --git a/ct/jellyfin.sh b/ct/jellyfin.sh index b230c7cd3..54fb1af56 100644 --- a/ct/jellyfin.sh +++ b/ct/jellyfin.sh @@ -1,74 +1,45 @@ #!/usr/bin/env bash - -# Copyright (c) 2021-2025 Community-Scripts ORG +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 tteck # Author: tteck (tteckster) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://jellyfin.org/ -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +APP="Jellyfin" +var_tags="${var_tags:-media}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-8}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables color -verb_ip6 catch_errors -setting_up_container -network_check -update_os -msg_info "Setting Up Hardware Acceleration" -if [[ ! -d /etc/apt/keyrings ]]; then - mkdir -p /etc/apt/keyrings -fi -curl -fsSL https://repositories.intel.com/graphics/intel-graphics.key | gpg --dearmor --yes -o /etc/apt/keyrings/intel-graphics.gpg -cat <<'EOF' | sudo tee /etc/apt/sources.list.d/intel-gpu.sources > /dev/null -Types: deb -URIs: https://repositories.intel.com/graphics/ubuntu -Suites: stable -Components: main -Architectures: amd64 -Signed-By: /etc/apt/keyrings/intel-graphics.gpg -EOF +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /usr/lib/jellyfin ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating ${APP} LXC" + $STD apt-get update + $STD apt-get -y upgrade + $STD apt-get -y --with-new-pkgs upgrade jellyfin jellyfin-server + msg_ok "Updated ${APP} LXC" + exit +} -$STD apt-get -y install {va-driver-all,ocl-icd-libopencl1,intel-opencl-icd,vainfo,intel-gpu-tools} -if [[ "$CTTYPE" == "0" ]]; then - chgrp video /dev/dri - chmod 755 /dev/dri - chmod 660 /dev/dri/* - $STD adduser $(id -u -n) video - $STD adduser $(id -u -n) render -fi -msg_ok "Set Up Hardware Acceleration" +start +build_container +description -msg_info "Installing Jellyfin" -VERSION="$(awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release)" -# Download the repository signing key and install it to the keyring directory -curl -fsSL https://repo.jellyfin.org/jellyfin_team.gpg.key | gpg --dearmor --yes --output /etc/apt/keyrings/jellyfin.gpg -# Install the Deb822 format jellyfin.sources entry -cat </etc/apt/sources.list.d/jellyfin.sources -Types: deb -URIs: https://repo.jellyfin.org/${PCT_OSTYPE} -Suites: ${VERSION} -Components: main -Architectures: amd64 -Signed-By: /etc/apt/keyrings/jellyfin.gpg -EOF -# Install Jellyfin using the metapackage (which will fetch jellyfin-server, jellyfin-web, and jellyfin-ffmpeg5) -$STD apt-get update -$STD apt-get install -y jellyfin -sed -i 's/"MinimumLevel": "Information"/"MinimumLevel": "Error"/g' /etc/jellyfin/logging.json -chown -R jellyfin:adm /etc/jellyfin -sleep 10 -systemctl restart jellyfin -if [[ "$CTTYPE" == "0" ]]; then - sed -i -e 's/^ssl-cert:x:104:$/render:x:104:root,jellyfin/' -e 's/^render:x:108:root,jellyfin$/ssl-cert:x:108:/' /etc/group -else - sed -i -e 's/^ssl-cert:x:104:$/render:x:104:jellyfin/' -e 's/^render:x:108:jellyfin$/ssl-cert:x:108:/' /etc/group -fi -msg_ok "Installed Jellyfin" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt -y autoremove -$STD apt -y autoclean -$STD apt -y clean -msg_ok "Cleaned" +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8096${CL}" From b70a6338617da72545339554f1a6bec7f6953f7d Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 20 Oct 2025 06:18:58 +0000 Subject: [PATCH 1452/1733] Update .app files --- ct/headers/jellyfin | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/jellyfin diff --git a/ct/headers/jellyfin b/ct/headers/jellyfin new file mode 100644 index 000000000..d905c4dba --- /dev/null +++ b/ct/headers/jellyfin @@ -0,0 +1,6 @@ + __ ____ _____ + / /__ / / /_ __/ __(_)___ + __ / / _ \/ / / / / / /_/ / __ \ +/ /_/ / __/ / / /_/ / __/ / / / / +\____/\___/_/_/\__, /_/ /_/_/ /_/ + /____/ From c65e49811d9aa52eedd87399fc40809026aff745 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Mon, 20 Oct 2025 08:39:52 +0200 Subject: [PATCH 1453/1733] Update source command to use curl with -fsSL --- ct/dispatcharr.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/dispatcharr.sh b/ct/dispatcharr.sh index e2a2971ee..11b0f4b6d 100644 --- a/ct/dispatcharr.sh +++ b/ct/dispatcharr.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: ekke85 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE From 57b9a609cba629eff97a3e0bb90ef7c8d1fc8329 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 20 Oct 2025 08:43:02 +0200 Subject: [PATCH 1454/1733] Update dispatcharr-install.sh --- install/dispatcharr-install.sh | 80 +++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 30 deletions(-) diff --git a/install/dispatcharr-install.sh b/install/dispatcharr-install.sh index cf7b5d630..1079dbfb8 100644 --- a/install/dispatcharr-install.sh +++ b/install/dispatcharr-install.sh @@ -15,15 +15,15 @@ update_os msg_info "Installing Dependencies" $STD apt install -y \ - build-essential \ - gcc \ - libpcre3-dev \ - libpq-dev \ - nginx \ - redis-server \ - ffmpeg \ - procps \ - streamlink + build-essential \ + gcc \ + libpcre3-dev \ + libpq-dev \ + nginx \ + redis-server \ + ffmpeg \ + procps \ + streamlink msg_ok "Installed Dependencies" PYTHON_VERSION="3.13" setup_uv @@ -34,35 +34,39 @@ msg_info "Set up PostgreSQL Database" DB_NAME=dispatcharr_db DB_USER=dispatcharr_usr DB_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" -DB_URL="postgresql://${DB_USER}:${DB_PASS}@localhost:5432/${DB_NAME}" $STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" { - echo "Dispatcharr-Credentials" - echo "Dispatcharr Database Name: $DB_NAME" - echo "Dispatcharr Database User: $DB_USER" - echo "Dispatcharr Database Password: $DB_PASS" + echo "Dispatcharr-Credentials" + echo "Dispatcharr Database Name: $DB_NAME" + echo "Dispatcharr Database User: $DB_USER" + echo "Dispatcharr Database Password: $DB_PASS" } >>~/dispatcharr.creds msg_ok "Set up PostgreSQL Database" fetch_and_deploy_gh_release "dispatcharr" "Dispatcharr/Dispatcharr" -msg_info "Setup Python (uv) requirements (system)" +msg_info "Setup Python virtual environment" cd /opt/dispatcharr +# WICHTIG: Erstelle ein echtes venv statt --system +$STD uv venv env +msg_ok "Virtual environment created" + +msg_info "Installing Python requirements" PYPI_URL="https://pypi.org/simple" mapfile -t EXTRA_INDEX_URLS < <(grep -E '^(--(extra-)?index-url|-i)\s' requirements.txt 2>/dev/null | awk '{print $2}' | sed 's#/*$##') UV_INDEX_ARGS=(--index-url "$PYPI_URL" --index-strategy unsafe-best-match) for u in "${EXTRA_INDEX_URLS[@]}"; do - [[ -n "$u" && "$u" != "$PYPI_URL" ]] && UV_INDEX_ARGS+=(--extra-index-url "$u") + [[ -n "$u" && "$u" != "$PYPI_URL" ]] && UV_INDEX_ARGS+=(--extra-index-url "$u") done if [[ -f requirements.txt ]]; then - $STD uv pip install --system "${UV_INDEX_ARGS[@]}" -r requirements.txt + $STD uv pip install "${UV_INDEX_ARGS[@]}" -r requirements.txt fi -$STD uv pip install --system "${UV_INDEX_ARGS[@]}" gunicorn gevent celery daphne +$STD uv pip install "${UV_INDEX_ARGS[@]}" gunicorn gevent celery daphne ln -sf /usr/bin/ffmpeg /opt/dispatcharr/env/bin/ffmpeg msg_ok "Python Requirements Installed" @@ -74,12 +78,14 @@ msg_ok "Built Frontend" msg_info "Running Django Migrations" cd /opt/dispatcharr -set -o allexport -source /etc/dispatcharr/dispatcharr.env -set +o allexport +# WICHTIG: Setze Umgebungsvariablen für Django/PostgreSQL +export POSTGRES_DB="$DB_NAME" +export POSTGRES_USER="$DB_USER" +export POSTGRES_PASSWORD="$DB_PASS" +export POSTGRES_HOST="localhost" -$STD ./.venv/bin/python manage.py migrate --noinput -$STD ./.venv/bin/python manage.py collectstatic --noinput +$STD ./env/bin/python manage.py migrate --noinput +$STD ./env/bin/python manage.py collectstatic --noinput msg_ok "Migrations Complete" msg_info "Configuring Nginx" @@ -116,7 +122,7 @@ EOF ln -sf /etc/nginx/sites-available/dispatcharr.conf /etc/nginx/sites-enabled/dispatcharr.conf rm -f /etc/nginx/sites-enabled/default -nginx -t +$STD nginx -t systemctl restart nginx systemctl enable nginx msg_ok "Configured Nginx" @@ -133,12 +139,15 @@ WorkingDirectory=/opt/dispatcharr RuntimeDirectory=dispatcharr RuntimeDirectoryMode=0775 Environment="PATH=/opt/dispatcharr/env/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin" -EnvironmentFile=/etc/dispatcharr/dispatcharr.env +Environment="POSTGRES_DB=$DB_NAME" +Environment="POSTGRES_USER=$DB_USER" +Environment="POSTGRES_PASSWORD=$DB_PASS" +Environment="POSTGRES_HOST=localhost" ExecStart=/opt/dispatcharr/env/bin/gunicorn \\ --workers=4 \\ --worker-class=gevent \\ --timeout=300 \\ - --bind 0.0.0.0:5656 \ + --bind 0.0.0.0:5656 \\ dispatcharr.wsgi:application Restart=always KillMode=mixed @@ -156,7 +165,10 @@ Requires=dispatcharr.service [Service] WorkingDirectory=/opt/dispatcharr Environment="PATH=/opt/dispatcharr/env/bin" -EnvironmentFile=/etc/dispatcharr/dispatcharr.env +Environment="POSTGRES_DB=$DB_NAME" +Environment="POSTGRES_USER=$DB_USER" +Environment="POSTGRES_PASSWORD=$DB_PASS" +Environment="POSTGRES_HOST=localhost" Environment="CELERY_BROKER_URL=redis://localhost:6379/0" ExecStart=/opt/dispatcharr/env/bin/celery -A dispatcharr worker -l info -c 4 Restart=always @@ -175,7 +187,10 @@ Requires=dispatcharr.service [Service] WorkingDirectory=/opt/dispatcharr Environment="PATH=/opt/dispatcharr/env/bin" -EnvironmentFile=/etc/dispatcharr/dispatcharr.env +Environment="POSTGRES_DB=$DB_NAME" +Environment="POSTGRES_USER=$DB_USER" +Environment="POSTGRES_PASSWORD=$DB_PASS" +Environment="POSTGRES_HOST=localhost" Environment="CELERY_BROKER_URL=redis://localhost:6379/0" ExecStart=/opt/dispatcharr/env/bin/celery -A dispatcharr beat -l info Restart=always @@ -194,7 +209,10 @@ Requires=dispatcharr.service [Service] WorkingDirectory=/opt/dispatcharr Environment="PATH=/opt/dispatcharr/env/bin" -EnvironmentFile=/etc/dispatcharr/dispatcharr.env +Environment="POSTGRES_DB=$DB_NAME" +Environment="POSTGRES_USER=$DB_USER" +Environment="POSTGRES_PASSWORD=$DB_PASS" +Environment="POSTGRES_HOST=localhost" ExecStart=/opt/dispatcharr/env/bin/daphne -b 0.0.0.0 -p 8001 dispatcharr.asgi:application Restart=always KillMode=mixed @@ -202,7 +220,9 @@ KillMode=mixed [Install] WantedBy=multi-user.target EOF -systemctl enable -q --now dispatcharr dispatcharr-celery dispatcharr-celerybeat dispatcharr-daphne + +systemctl daemon-reload +systemctl enable --now dispatcharr dispatcharr-celery dispatcharr-celerybeat dispatcharr-daphne msg_ok "Started Dispatcharr Services" motd_ssh From 5022388d000b8715264885cc009759a2bd07dad9 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 20 Oct 2025 09:19:47 +0200 Subject: [PATCH 1455/1733] Update tools.func --- misc/tools.func | 122 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 101 insertions(+), 21 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 097f86df4..b7c3e450b 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -525,14 +525,35 @@ cleanup_old_repo_files() { # Remove old GPG keys from trusted.gpg.d rm -f /etc/apt/trusted.gpg.d/"${app}"*.gpg - # Remove keyrings from /etc/apt/keyrings (FIX: This was missing!) + # Remove keyrings from /etc/apt/keyrings rm -f /etc/apt/keyrings/"${app}"*.gpg - # Remove duplicate .sources files (keep only the main one) - local sources_file="/etc/apt/sources.list.d/${app}.sources" - if [[ -f "$sources_file" ]]; then - find /etc/apt/sources.list.d/ -name "${app}*.sources" ! -name "${app}.sources" -delete 2>/dev/null || true - fi + # Remove ALL .sources files for this app (including the main one) + # This ensures no orphaned .sources files reference deleted keyrings + rm -f /etc/apt/sources.list.d/"${app}"*.sources +} + +# ------------------------------------------------------------------------------ +# Cleanup orphaned .sources files that reference missing keyrings +# This prevents APT signature verification errors +# ------------------------------------------------------------------------------ +cleanup_orphaned_sources() { + local sources_dir="/etc/apt/sources.list.d" + local keyrings_dir="/etc/apt/keyrings" + + [[ ! -d "$sources_dir" ]] && return 0 + + while IFS= read -r -d '' sources_file; do + # Extract Signed-By path from .sources file + local keyring_path + keyring_path=$(grep -E '^Signed-By:' "$sources_file" 2>/dev/null | awk '{print $2}') + + # If keyring doesn't exist, remove the .sources file + if [[ -n "$keyring_path" ]] && [[ ! -f "$keyring_path" ]]; then + msg_warn "Removing orphaned sources file: $(basename "$sources_file") (missing keyring: $(basename "$keyring_path"))" + rm -f "$sources_file" + fi + done < <(find "$sources_dir" -name "*.sources" -print0 2>/dev/null) } # ------------------------------------------------------------------------------ @@ -548,9 +569,12 @@ setup_deb822_repo() { msg_info "Setting up $name repository" - # Cleanup old configs + # Cleanup old configs for this app cleanup_old_repo_files "$name" + # Cleanup any orphaned .sources files from other apps + cleanup_orphaned_sources + # Ensure keyring directory exists mkdir -p /etc/apt/keyrings @@ -2135,6 +2159,9 @@ function setup_mongodb() { # Cleanup old repository files cleanup_old_repo_files "mongodb-org-${MONGO_VERSION}" + # Cleanup any orphaned .sources files from other apps + cleanup_orphaned_sources + # Use helper function to get fallback suite local SUITE SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "$MONGO_BASE_URL") @@ -2565,12 +2592,25 @@ function setup_postgresql() { # Cleanup old repository files cleanup_old_repo_files "pgdg" - # Use helper function to get fallback suite + # PostgreSQL PGDG repository uses special suite naming + # For unstable/testing Debian, we need to check what's actually available local SUITE - SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://apt.postgresql.org/pub/repos/apt") - - # PGDG uses special suite naming: ${SUITE}-pgdg - SUITE="${SUITE}-pgdg" + case "$DISTRO_CODENAME" in + trixie | forky | sid) + # Try trixie-pgdg first, fallback to bookworm-pgdg if not available + if verify_repo_available "https://apt.postgresql.org/pub/repos/apt" "trixie-pgdg"; then + SUITE="trixie-pgdg" + else + SUITE="bookworm-pgdg" + fi + ;; + *) + # Use helper function for stable releases + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://apt.postgresql.org/pub/repos/apt") + # PGDG uses special suite naming: ${SUITE}-pgdg + SUITE="${SUITE}-pgdg" + ;; + esac # Use standardized repo setup setup_deb822_repo \ @@ -2581,7 +2621,23 @@ function setup_postgresql() { "main" \ "amd64 arm64" - $STD apt install -y "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}" + # Update apt and verify package availability + if ! $STD apt update; then + msg_error "APT update failed for PostgreSQL repository" + return 1 + fi + + if ! apt-cache policy "postgresql-${PG_VERSION}" | grep -q 'Candidate:'; then + msg_error "PostgreSQL ${PG_VERSION} package not available for suite ${SUITE}" + msg_info "Available PostgreSQL versions:" + apt-cache search "^postgresql-[0-9]" | grep "^postgresql-" | sed 's/^/ /' + return 1 + fi + + if ! $STD apt install -y "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}"; then + msg_error "Failed to install PostgreSQL ${PG_VERSION}" + return 1 + fi if [[ -n "$CURRENT_PG_VERSION" ]]; then $STD apt purge -y "postgresql-${CURRENT_PG_VERSION}" "postgresql-client-${CURRENT_PG_VERSION}" || true @@ -2791,9 +2847,8 @@ function setup_clickhouse() { # Ensure dependencies ensure_dependencies apt-transport-https ca-certificates dirmngr gnupg - # Use helper function to get fallback suite - local SUITE - SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://packages.clickhouse.com/deb") + # ClickHouse uses 'stable' instead of distro codenames + local SUITE="stable" # Use standardized repo setup setup_deb822_repo \ @@ -2804,17 +2859,42 @@ function setup_clickhouse() { "main" \ "amd64 arm64" - # Install ClickHouse packages + # Update and install ClickHouse packages export DEBIAN_FRONTEND=noninteractive - $STD apt install -y clickhouse-server clickhouse-client + if ! $STD apt update; then + msg_error "APT update failed for ClickHouse repository" + return 1 + fi + + if ! $STD apt install -y clickhouse-server clickhouse-client; then + msg_error "Failed to install ClickHouse packages" + return 1 + fi + + # Verify installation + if ! command -v clickhouse-server >/dev/null 2>&1; then + msg_error "ClickHouse installation completed but clickhouse-server command not found" + return 1 + fi # Create data directory if it doesn't exist mkdir -p /var/lib/clickhouse - chown -R clickhouse:clickhouse /var/lib/clickhouse + + # Check if clickhouse user exists before chown + if id clickhouse >/dev/null 2>&1; then + chown -R clickhouse:clickhouse /var/lib/clickhouse + else + msg_warn "ClickHouse user not found, skipping chown" + fi # Enable and start service - $STD systemctl enable clickhouse-server - safe_service_restart clickhouse-server + if ! $STD systemctl enable clickhouse-server; then + msg_warn "Failed to enable clickhouse-server service" + fi + + if ! safe_service_restart clickhouse-server; then + msg_warn "Failed to start clickhouse-server service (this may be normal on first install)" + fi cache_installed_version "clickhouse" "$CLICKHOUSE_VERSION" msg_ok "Installed ClickHouse $CLICKHOUSE_VERSION" From 647d05be1f21dce0f947036720357c7b8ecf912f Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 20 Oct 2025 09:35:23 +0200 Subject: [PATCH 1456/1733] Improve database setup scripts and dependency handling Adds explicit installation of MariaDB dependencies before setup. Updates MySQL setup to handle unsupported Debian testing/unstable suites by falling back to bookworm if available, with improved error messaging. Ensures PostgreSQL binaries are added to PATH during installation. Expands Ruby setup to install all required build dependencies. --- misc/tools.func | 43 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index b7c3e450b..2f55f57d4 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -2055,6 +2055,14 @@ setup_mariadb() { $STD apt purge -y 'mariadb*' || true fi + # Install required dependencies first (MariaDB needs these from main repos) + msg_info "Installing MariaDB dependencies" + $STD apt update + $STD apt install -y gawk rsync socat libdbi-perl pv || { + msg_error "Failed to install MariaDB dependencies" + return 1 + } + # Cleanup old repository files cleanup_old_repo_files "mariadb" @@ -2241,9 +2249,25 @@ function setup_mysql() { # Cleanup old repository files cleanup_old_repo_files "mysql" - # Use helper function to get fallback suite + # MySQL doesn't support Debian testing/unstable - try bookworm as fallback local SUITE - SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://repo.mysql.com/apt/${DISTRO_ID}") + case "$DISTRO_CODENAME" in + trixie | forky | sid) + # MySQL doesn't publish packages for Debian testing/unstable + # Try bookworm as fallback + if verify_repo_available "https://repo.mysql.com/apt/${DISTRO_ID}" "bookworm"; then + SUITE="bookworm" + msg_warn "MySQL ${MYSQL_VERSION} not available for ${DISTRO_CODENAME}, using bookworm packages" + else + msg_error "MySQL ${MYSQL_VERSION} package not available for ${DISTRO_ID}-${DISTRO_CODENAME}" + msg_info "MySQL typically doesn't support Debian testing/unstable. Consider using MariaDB instead." + return 1 + fi + ;; + *) + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://repo.mysql.com/apt/${DISTRO_ID}") + ;; + esac # Use standardized repo setup setup_deb822_repo \ @@ -2263,7 +2287,9 @@ function setup_mysql() { fi if ! apt-cache policy mysql-server | grep -q 'Candidate:'; then - msg_error "MySQL ${MYSQL_VERSION} package not available for ${DISTRO_ID}-${DISTRO_CODENAME}" + msg_error "MySQL ${MYSQL_VERSION} package not available for suite ${SUITE}" + msg_info "Available MySQL components:" + apt-cache search "^mysql-" | grep "^mysql-" | head -20 | sed 's/^/ /' return 1 fi @@ -2645,6 +2671,12 @@ function setup_postgresql() { fi $STD systemctl enable --now postgresql + + # Add PostgreSQL binaries to PATH for the install script + if ! grep -q '/usr/lib/postgresql' /etc/environment 2>/dev/null; then + echo 'PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/postgresql/'"${PG_VERSION}"'/bin"' >/etc/environment + fi + cache_installed_version "postgresql" "$PG_VERSION" msg_ok "Installed PostgreSQL $PG_VERSION" fi @@ -2687,7 +2719,10 @@ function setup_ruby() { msg_info "Installing Ruby $RUBY_VERSION" - ensure_dependencies jq + # Install all required build dependencies + ensure_dependencies jq autoconf patch build-essential rustc libssl-dev libyaml-dev \ + libreadline6-dev zlib1g-dev libgmp-dev libncurses5-dev libffi-dev libgdbm6 \ + libgdbm-dev libdb-dev uuid-dev local RBENV_RELEASE RBENV_RELEASE=$(curl -fsSL https://api.github.com/repos/rbenv/rbenv/releases/latest | jq -r '.tag_name' | sed 's/^v//') From 0c12ac59bb1a77e1466c8f7f08392c8281547a4b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 20 Oct 2025 09:51:30 +0200 Subject: [PATCH 1457/1733] fixes --- misc/test-tools-func.sh | 49 +++++++++++++++---------- misc/tools.func | 81 ++++++++++++++++++++++++++++++++++------- 2 files changed, 97 insertions(+), 33 deletions(-) diff --git a/misc/test-tools-func.sh b/misc/test-tools-func.sh index 744c25e7c..a1655219e 100644 --- a/misc/test-tools-func.sh +++ b/misc/test-tools-func.sh @@ -85,8 +85,17 @@ cleanup_before_test() { # Remove apt locks rm -f /var/lib/dpkg/lock-frontend /var/lib/dpkg/lock /var/cache/apt/archives/lock 2>/dev/null || true - # Remove existing keyrings to avoid overwrite prompts + # Clean up broken repository files from previous tests + # Remove all custom sources files + rm -f /etc/apt/sources.list.d/*.sources 2>/dev/null || true + rm -f /etc/apt/sources.list.d/*.list 2>/dev/null || true + + # Remove all keyrings rm -f /etc/apt/keyrings/*.gpg 2>/dev/null || true + rm -f /etc/apt/keyrings/*.asc 2>/dev/null || true + + # Update package lists to ensure clean state + apt-get update -qq 2>/dev/null || true # Wait a moment for processes to clean up sleep 1 @@ -176,23 +185,23 @@ apt-get install -y curl wget gpg jq git build-essential ca-certificates &>/dev/n # ============================================================================== # TEST 2: ADMINER - Database Management # ============================================================================== -test_function "Adminer" \ - "setup_adminer" \ - "dpkg -l adminer 2>/dev/null | grep -q '^ii' && a2query -c adminer 2>/dev/null && echo 'Adminer installed'" +# test_function "Adminer" \ +# "setup_adminer" \ +# "dpkg -l adminer 2>/dev/null | grep -q '^ii' && a2query -c adminer 2>/dev/null && echo 'Adminer installed'" # ============================================================================== # TEST 3: CLICKHOUSE # ============================================================================== -test_function "ClickHouse" \ - "setup_clickhouse" \ - "clickhouse-server --version" +# test_function "ClickHouse" \ +# "setup_clickhouse" \ +# "clickhouse-server --version" # ============================================================================== # TEST 4: POSTGRESQL # ============================================================================== -# test_function "PostgreSQL 17" \ -# "PG_VERSION=17 setup_postgresql" \ -# "psql --version" +test_function "PostgreSQL 16" \ + "PG_VERSION=16 setup_postgresql" \ + "psql --version" # ============================================================================== # TEST 6: MARIADB @@ -218,13 +227,13 @@ test_function "MySQL 8.0" \ # ============================================================================== # TEST 8: MONGODB (Check AVX support) # ============================================================================== -if grep -q avx /proc/cpuinfo; then - test_function "MongoDB 8.0" \ - "MONGO_VERSION=8.0 setup_mongodb" \ - "mongod --version" -else - skip_test "MongoDB 8.0" "CPU does not support AVX" -fi +# if grep -q avx /proc/cpuinfo; then +# test_function "MongoDB 8.0" \ +# "MONGO_VERSION=8.0 setup_mongodb" \ +# "mongod --version" +# else +# skip_test "MongoDB 8.0" "CPU does not support AVX" +# fi # ============================================================================== # TEST 9: NODE.JS @@ -278,9 +287,9 @@ test_function "Ruby 3.4.1 with Rails" \ # ============================================================================== # TEST 16: RUST # ============================================================================== -test_function "Rust (stable)" \ - "RUST_TOOLCHAIN=stable RUST_CRATES='cargo-edit' setup_rust" \ - "source \$HOME/.cargo/env && rustc --version" +# test_function "Rust (stable)" \ +# "RUST_TOOLCHAIN=stable RUST_CRATES='cargo-edit' setup_rust" \ +# "source \$HOME/.cargo/env && rustc --version" # ============================================================================== # TEST 17: GHOSTSCRIPT diff --git a/misc/tools.func b/misc/tools.func index 2f55f57d4..539cd8e3d 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -88,11 +88,20 @@ ensure_dependencies() { fi if ((current_time - last_update > 300)); then - $STD apt update + # Ensure orphaned sources are cleaned before updating + cleanup_orphaned_sources 2>/dev/null || true + + if ! $STD apt update; then + msg_warn "apt update failed, attempting to fix..." + ensure_apt_working || return 1 + fi echo "$current_time" >"$apt_cache_file" fi - $STD apt install -y "${missing[@]}" + $STD apt install -y "${missing[@]}" || { + msg_error "Failed to install dependencies: ${missing[*]}" + return 1 + } msg_ok "Installed dependencies" fi } @@ -536,6 +545,7 @@ cleanup_old_repo_files() { # ------------------------------------------------------------------------------ # Cleanup orphaned .sources files that reference missing keyrings # This prevents APT signature verification errors +# Call this at the start of any setup function to ensure APT is in a clean state # ------------------------------------------------------------------------------ cleanup_orphaned_sources() { local sources_dir="/etc/apt/sources.list.d" @@ -554,6 +564,37 @@ cleanup_orphaned_sources() { rm -f "$sources_file" fi done < <(find "$sources_dir" -name "*.sources" -print0 2>/dev/null) + + # Also check for broken symlinks in keyrings directory + if [[ -d "$keyrings_dir" ]]; then + find "$keyrings_dir" -type l ! -exec test -e {} \; -delete 2>/dev/null || true + fi +} + +# ------------------------------------------------------------------------------ +# Ensure APT is in a working state before installing packages +# This should be called at the start of any setup function +# ------------------------------------------------------------------------------ +ensure_apt_working() { + # Clean up orphaned sources first + cleanup_orphaned_sources + + # Try to update package lists + if ! apt-get update -qq 2>/dev/null; then + msg_warn "APT update failed, attempting to fix..." + + # More aggressive cleanup + rm -f /etc/apt/sources.list.d/*.sources 2>/dev/null || true + cleanup_orphaned_sources + + # Try again + if ! apt-get update -qq 2>/dev/null; then + msg_error "Cannot update package lists - APT is critically broken" + return 1 + fi + fi + + return 0 } # ------------------------------------------------------------------------------ @@ -2055,17 +2096,19 @@ setup_mariadb() { $STD apt purge -y 'mariadb*' || true fi - # Install required dependencies first (MariaDB needs these from main repos) - msg_info "Installing MariaDB dependencies" - $STD apt update - $STD apt install -y gawk rsync socat libdbi-perl pv || { - msg_error "Failed to install MariaDB dependencies" - return 1 - } + # Ensure APT is working before proceeding + ensure_apt_working || return 1 # Cleanup old repository files cleanup_old_repo_files "mariadb" + # Install required dependencies first (MariaDB needs these from main repos) + msg_info "Installing MariaDB dependencies" + ensure_dependencies gawk rsync socat libdbi-perl pv || { + msg_error "Failed to install MariaDB dependencies" + return 1 + } + # Use helper function to get fallback suite local SUITE SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "http://mirror.mariadb.org/repo/${MARIADB_VERSION}/${DISTRO_ID}") @@ -2286,16 +2329,25 @@ function setup_mysql() { return 1 fi - if ! apt-cache policy mysql-server | grep -q 'Candidate:'; then + # Check for either mysql-server meta package or mysql-community-server + if ! apt-cache policy mysql-server mysql-community-server 2>/dev/null | grep -q 'Candidate:'; then msg_error "MySQL ${MYSQL_VERSION} package not available for suite ${SUITE}" msg_info "Available MySQL components:" apt-cache search "^mysql-" | grep "^mysql-" | head -20 | sed 's/^/ /' return 1 fi - if ! $STD apt install -y mysql-server; then - msg_error "Failed to install MySQL ${MYSQL_VERSION}" - return 1 + # Try to install mysql-server first (meta package), fallback to mysql-community-server + if apt-cache policy mysql-server 2>/dev/null | grep -q 'Candidate:'; then + if ! $STD apt install -y mysql-server mysql-client; then + msg_error "Failed to install MySQL ${MYSQL_VERSION}" + return 1 + fi + else + if ! $STD apt install -y mysql-community-server mysql-community-client; then + msg_error "Failed to install MySQL ${MYSQL_VERSION}" + return 1 + fi fi # Verify installation @@ -2719,6 +2771,9 @@ function setup_ruby() { msg_info "Installing Ruby $RUBY_VERSION" + # Ensure APT is working before installing dependencies + ensure_apt_working || return 1 + # Install all required build dependencies ensure_dependencies jq autoconf patch build-essential rustc libssl-dev libyaml-dev \ libreadline6-dev zlib1g-dev libgmp-dev libncurses5-dev libffi-dev libgdbm6 \ From 2d393c72379e22b582963f3f89059c103df1cf68 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 20 Oct 2025 10:10:12 +0200 Subject: [PATCH 1458/1733] Update tools.func --- misc/tools.func | 163 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 125 insertions(+), 38 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 539cd8e3d..70d5a79c4 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -2104,10 +2104,21 @@ setup_mariadb() { # Install required dependencies first (MariaDB needs these from main repos) msg_info "Installing MariaDB dependencies" - ensure_dependencies gawk rsync socat libdbi-perl pv || { - msg_error "Failed to install MariaDB dependencies" - return 1 - } + + local mariadb_deps=() + for dep in gawk rsync socat libdbi-perl pv; do + if apt-cache search "^${dep}$" 2>/dev/null | grep -q .; then + mariadb_deps+=("$dep") + else + msg_warn "Package $dep not available, skipping" + fi + done + + if [[ ${#mariadb_deps[@]} -gt 0 ]]; then + if ! $STD apt install -y "${mariadb_deps[@]}" 2>/dev/null; then + msg_warn "Some MariaDB dependencies failed to install - continuing anyway" + fi + fi # Use helper function to get fallback suite local SUITE @@ -2323,37 +2334,45 @@ function setup_mysql() { export DEBIAN_FRONTEND=noninteractive - # Update apt and check if package is available + # Update apt if ! $STD apt update; then msg_error "APT update failed for MySQL repository" return 1 fi - # Check for either mysql-server meta package or mysql-community-server - if ! apt-cache policy mysql-server mysql-community-server 2>/dev/null | grep -q 'Candidate:'; then - msg_error "MySQL ${MYSQL_VERSION} package not available for suite ${SUITE}" - msg_info "Available MySQL components:" - apt-cache search "^mysql-" | grep "^mysql-" | head -20 | sed 's/^/ /' - return 1 + # Try multiple MySQL package patterns + local mysql_install_success=false + + # First try: mysql-server (most common) + if apt-cache search "^mysql-server$" 2>/dev/null | grep -q . && $STD apt install -y mysql-server mysql-client 2>/dev/null; then + mysql_install_success=true fi - # Try to install mysql-server first (meta package), fallback to mysql-community-server - if apt-cache policy mysql-server 2>/dev/null | grep -q 'Candidate:'; then - if ! $STD apt install -y mysql-server mysql-client; then - msg_error "Failed to install MySQL ${MYSQL_VERSION}" - return 1 - fi - else - if ! $STD apt install -y mysql-community-server mysql-community-client; then - msg_error "Failed to install MySQL ${MYSQL_VERSION}" - return 1 - fi + # Second try: mysql-community-server (when official repo) + if [[ "$mysql_install_success" == false ]] && apt-cache search "^mysql-community-server$" 2>/dev/null | grep -q . && $STD apt install -y mysql-community-server mysql-community-client 2>/dev/null; then + mysql_install_success=true + fi + + # Third try: just mysql meta package + if [[ "$mysql_install_success" == false ]] && apt-cache search "^mysql$" 2>/dev/null | grep -q . && $STD apt install -y mysql 2>/dev/null; then + mysql_install_success=true + fi + + if [[ "$mysql_install_success" == false ]]; then + msg_error "MySQL ${MYSQL_VERSION} package not available for suite ${SUITE}" + msg_info "Available MySQL components:" + apt-cache search "mysql" | grep "^mysql" | head -20 | sed 's/^/ /' + return 1 fi # Verify installation if ! command -v mysql >/dev/null 2>&1; then - msg_error "MySQL installation completed but mysql command not found" - return 1 + msg_warn "MySQL installed but mysql command not immediately available - retrying after shell refresh" + hash -r + if ! command -v mysql >/dev/null 2>&1; then + msg_error "MySQL installed but mysql command still not found" + return 1 + fi fi cache_installed_version "mysql" "$MYSQL_VERSION" @@ -2699,30 +2718,50 @@ function setup_postgresql() { "main" \ "amd64 arm64" - # Update apt and verify package availability + # Update apt if ! $STD apt update; then msg_error "APT update failed for PostgreSQL repository" return 1 fi - if ! apt-cache policy "postgresql-${PG_VERSION}" | grep -q 'Candidate:'; then - msg_error "PostgreSQL ${PG_VERSION} package not available for suite ${SUITE}" - msg_info "Available PostgreSQL versions:" - apt-cache search "^postgresql-[0-9]" | grep "^postgresql-" | sed 's/^/ /' + # Try multiple PostgreSQL package patterns + local pg_install_success=false + + # First try: postgresql-X (common in most repos) + if apt-cache search "^postgresql-${PG_VERSION}$" 2>/dev/null | grep -q . && $STD apt install -y "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}" 2>/dev/null; then + pg_install_success=true + fi + + # Second try: postgresql-server-X (some repos use this) + if [[ "$pg_install_success" == false ]] && apt-cache search "^postgresql-server-${PG_VERSION}$" 2>/dev/null | grep -q . && $STD apt install -y "postgresql-server-${PG_VERSION}" "postgresql-client-${PG_VERSION}" 2>/dev/null; then + pg_install_success=true + fi + + # Third try: just postgresql (any version) + if [[ "$pg_install_success" == false ]] && apt-cache search "^postgresql$" 2>/dev/null | grep -q . && $STD apt install -y postgresql postgresql-client 2>/dev/null; then + pg_install_success=true + msg_warn "PostgreSQL ${PG_VERSION} not available, installed latest available version" + fi + + if [[ "$pg_install_success" == false ]]; then + msg_error "PostgreSQL package not available for suite ${SUITE}" + msg_info "Available PostgreSQL packages:" + apt-cache search "postgresql" | grep "^postgresql" | head -20 | sed 's/^/ /' return 1 fi - if ! $STD apt install -y "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}"; then - msg_error "Failed to install PostgreSQL ${PG_VERSION}" + # Verify installation + if ! command -v psql >/dev/null 2>&1; then + msg_error "PostgreSQL installed but psql command not found" return 1 fi if [[ -n "$CURRENT_PG_VERSION" ]]; then - $STD apt purge -y "postgresql-${CURRENT_PG_VERSION}" "postgresql-client-${CURRENT_PG_VERSION}" || true - $STD su - postgres -c "psql < /var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql" + $STD apt purge -y "postgresql-${CURRENT_PG_VERSION}" "postgresql-client-${CURRENT_PG_VERSION}" 2>/dev/null || true + $STD su - postgres -c "psql < /var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql" 2>/dev/null || true fi - $STD systemctl enable --now postgresql + $STD systemctl enable --now postgresql 2>/dev/null || true # Add PostgreSQL binaries to PATH for the install script if ! grep -q '/usr/lib/postgresql' /etc/environment 2>/dev/null; then @@ -2774,10 +2813,58 @@ function setup_ruby() { # Ensure APT is working before installing dependencies ensure_apt_working || return 1 - # Install all required build dependencies - ensure_dependencies jq autoconf patch build-essential rustc libssl-dev libyaml-dev \ - libreadline6-dev zlib1g-dev libgmp-dev libncurses5-dev libffi-dev libgdbm6 \ - libgdbm-dev libdb-dev uuid-dev + # Install build dependencies - with fallback for different Debian versions + # In Trixie: libreadline6-dev -> libreadline-dev, libncurses5-dev -> libncurses-dev + msg_info "Installing Ruby build dependencies" + + local ruby_deps=() + local dep_variations=( + "jq" + "autoconf" + "patch" + "build-essential" + "libssl-dev" + "libyaml-dev" + "libreadline-dev|libreadline6-dev" + "zlib1g-dev" + "libgmp-dev" + "libncurses-dev|libncurses5-dev" + "libffi-dev" + "libgdbm-dev" + "libdb-dev" + "uuid-dev" + ) + + for dep_pattern in "${dep_variations[@]}"; do + if [[ "$dep_pattern" == *"|"* ]]; then + # Try multiple variations + IFS='|' read -ra variations <<<"$dep_pattern" + local found=false + for var in "${variations[@]}"; do + if apt-cache search "^${var}$" 2>/dev/null | grep -q .; then + ruby_deps+=("$var") + found=true + break + fi + done + [[ "$found" == false ]] && msg_warn "No version of $dep_pattern available" + else + if apt-cache search "^${dep_pattern}$" 2>/dev/null | grep -q .; then + ruby_deps+=("$dep_pattern") + else + msg_warn "Package $dep_pattern not available" + fi + fi + done + + if [[ ${#ruby_deps[@]} -gt 0 ]]; then + if ! $STD apt install -y "${ruby_deps[@]}" 2>/dev/null; then + msg_warn "Some Ruby dependencies failed to install - Ruby may not compile fully\nContinuing anyway..." + fi + else + msg_error "No Ruby build dependencies available" + return 1 + fi local RBENV_RELEASE RBENV_RELEASE=$(curl -fsSL https://api.github.com/repos/rbenv/rbenv/releases/latest | jq -r '.tag_name' | sed 's/^v//') From ffcebea870f2898d08ee0a948188c8986d00541a Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Mon, 20 Oct 2025 10:17:16 +0200 Subject: [PATCH 1459/1733] Update Intel graphics installation process Removed installation of 'intel-opencl-icd' and related repository setup. Added fetching of Intel graphics compiler and runtime packages. --- install/jellyfin-install.sh | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/install/jellyfin-install.sh b/install/jellyfin-install.sh index b230c7cd3..c6f1a76ab 100644 --- a/install/jellyfin-install.sh +++ b/install/jellyfin-install.sh @@ -17,17 +17,12 @@ msg_info "Setting Up Hardware Acceleration" if [[ ! -d /etc/apt/keyrings ]]; then mkdir -p /etc/apt/keyrings fi -curl -fsSL https://repositories.intel.com/graphics/intel-graphics.key | gpg --dearmor --yes -o /etc/apt/keyrings/intel-graphics.gpg -cat <<'EOF' | sudo tee /etc/apt/sources.list.d/intel-gpu.sources > /dev/null -Types: deb -URIs: https://repositories.intel.com/graphics/ubuntu -Suites: stable -Components: main -Architectures: amd64 -Signed-By: /etc/apt/keyrings/intel-graphics.gpg -EOF +fetch_and_deploy_gh_release "intel-igc-core-2" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-core-2_*_amd64.deb" +fetch_and_deploy_gh_release "intel-igc-opencl-2" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-opencl-2_*_amd64.deb" +fetch_and_deploy_gh_release "intel-libgdgmm12" "intel/compute-runtime" "binary" "latest" "" "libigdgmm12_*_amd64.deb" +fetch_and_deploy_gh_release "intel-opencl-icd" "intel/compute-runtime" "binary" "latest" "" "intel-opencl-icd_*_amd64.deb" -$STD apt-get -y install {va-driver-all,ocl-icd-libopencl1,intel-opencl-icd,vainfo,intel-gpu-tools} +$STD apt-get -y install {va-driver-all,ocl-icd-libopencl1,vainfo,intel-gpu-tools} if [[ "$CTTYPE" == "0" ]]; then chgrp video /dev/dri chmod 755 /dev/dri From 4a771d0446cea353804759955fbfd6c2985fc4fa Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Mon, 20 Oct 2025 10:18:47 +0200 Subject: [PATCH 1460/1733] Refactor Jellyfin installation script for keyring setup --- install/jellyfin-install.sh | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/install/jellyfin-install.sh b/install/jellyfin-install.sh index c6f1a76ab..24daabdff 100644 --- a/install/jellyfin-install.sh +++ b/install/jellyfin-install.sh @@ -14,9 +14,6 @@ network_check update_os msg_info "Setting Up Hardware Acceleration" -if [[ ! -d /etc/apt/keyrings ]]; then - mkdir -p /etc/apt/keyrings -fi fetch_and_deploy_gh_release "intel-igc-core-2" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-core-2_*_amd64.deb" fetch_and_deploy_gh_release "intel-igc-opencl-2" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-opencl-2_*_amd64.deb" fetch_and_deploy_gh_release "intel-libgdgmm12" "intel/compute-runtime" "binary" "latest" "" "libigdgmm12_*_amd64.deb" @@ -33,10 +30,11 @@ fi msg_ok "Set Up Hardware Acceleration" msg_info "Installing Jellyfin" +if [[ ! -d /etc/apt/keyrings ]]; then + mkdir -p /etc/apt/keyrings +fi VERSION="$(awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release)" -# Download the repository signing key and install it to the keyring directory curl -fsSL https://repo.jellyfin.org/jellyfin_team.gpg.key | gpg --dearmor --yes --output /etc/apt/keyrings/jellyfin.gpg -# Install the Deb822 format jellyfin.sources entry cat </etc/apt/sources.list.d/jellyfin.sources Types: deb URIs: https://repo.jellyfin.org/${PCT_OSTYPE} @@ -45,9 +43,8 @@ Components: main Architectures: amd64 Signed-By: /etc/apt/keyrings/jellyfin.gpg EOF -# Install Jellyfin using the metapackage (which will fetch jellyfin-server, jellyfin-web, and jellyfin-ffmpeg5) -$STD apt-get update -$STD apt-get install -y jellyfin +$STD apt update +$STD apt install -y jellyfin sed -i 's/"MinimumLevel": "Information"/"MinimumLevel": "Error"/g' /etc/jellyfin/logging.json chown -R jellyfin:adm /etc/jellyfin sleep 10 From b508ab26b9fd09b606d7b26cd310c8d33875f8be Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 20 Oct 2025 10:21:56 +0200 Subject: [PATCH 1461/1733] Update dispatcharr-install.sh --- install/dispatcharr-install.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/install/dispatcharr-install.sh b/install/dispatcharr-install.sh index 1079dbfb8..3d53e9164 100644 --- a/install/dispatcharr-install.sh +++ b/install/dispatcharr-install.sh @@ -64,9 +64,10 @@ for u in "${EXTRA_INDEX_URLS[@]}"; do [[ -n "$u" && "$u" != "$PYPI_URL" ]] && UV_INDEX_ARGS+=(--extra-index-url "$u") done if [[ -f requirements.txt ]]; then - $STD uv pip install "${UV_INDEX_ARGS[@]}" -r requirements.txt + $STD /opt/dispatcharr/env/bin/python -m pip install --upgrade pip setuptools wheel + $STD /opt/dispatcharr/env/bin/pip install "${UV_INDEX_ARGS[@]}" -r requirements.txt fi -$STD uv pip install "${UV_INDEX_ARGS[@]}" gunicorn gevent celery daphne +$STD /opt/dispatcharr/env/bin/pip install "${UV_INDEX_ARGS[@]}" gunicorn gevent celery daphne ln -sf /usr/bin/ffmpeg /opt/dispatcharr/env/bin/ffmpeg msg_ok "Python Requirements Installed" From 071f2aa83a31248811720ec809c8d1b1d052fba3 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 20 Oct 2025 10:40:23 +0200 Subject: [PATCH 1462/1733] Update Dispatcharr defaults and improve install script Increased default RAM to 2048MB and updated default OS version to 13 in ct/dispatcharr.sh. Refactored update_script for improved readability. Modified install/dispatcharr-install.sh to use uv for Python package installation and clarified virtual environment creation comments. --- ct/dispatcharr.sh | 170 ++++++++++++++++----------------- install/dispatcharr-install.sh | 9 +- 2 files changed, 89 insertions(+), 90 deletions(-) diff --git a/ct/dispatcharr.sh b/ct/dispatcharr.sh index 11b0f4b6d..9e0324289 100644 --- a/ct/dispatcharr.sh +++ b/ct/dispatcharr.sh @@ -9,10 +9,10 @@ APP="Dispatcharr" APP_NAME=${APP,,} var_tags="${var_tags:-media;arr}" var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-1024}" +var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-debian}" -var_version="${var_version:-12}" +var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" @@ -21,91 +21,91 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources + header_info + check_container_storage + check_container_resources - if [[ ! -d "/opt/dispatcharr" ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - RELEASE=$(curl -fsSL https://api.github.com/repos/Dispatcharr/Dispatcharr/releases/latest | jq -r '.tag_name' | sed 's/^v//') - if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then - msg_ok "Starting update" - APP_DIR="/opt/dispatcharr" - APP_USER="dispatcharr" - APP_GROUP="dispatcharr" - - msg_info "Stopping $APP" - systemctl stop dispatcharr-celery - systemctl stop dispatcharr-celerybeat - systemctl stop dispatcharr-daphne - systemctl stop dispatcharr - msg_ok "Stopped $APP" - - msg_info "Creating Backup" - BACKUP_FILE="/opt/dispatcharr_$(date +%F).tar.gz" - msg_info "Source and Database backup" - set -o allexport - source /etc/$APP_NAME/$APP_NAME.env - set +o allexport - PGPASSWORD=$POSTGRES_PASSWORD pg_dump -U $POSTGRES_USER -h $POSTGRES_HOST $POSTGRES_DB >/opt/$POSTGRES_DB-$(date +%F).sql - $STD tar -czf "$BACKUP_FILE" /opt/dispatcharr /opt/Dispatcharr_version.txt /opt/$POSTGRES_DB-$(date +%F).sql &>/dev/null - msg_ok "Backup Created" - - msg_info "Updating $APP to v${RELEASE}" - rm -rf /opt/dispatcharr - fetch_and_deploy_gh_release "dispatcharr" "Dispatcharr/Dispatcharr" - chown -R "$APP_USER:$APP_GROUP" "$APP_DIR" - sed -i 's/program\[\x27channel_id\x27\]/program["channel_id"]/g' "${APP_DIR}/apps/output/views.py" - - msg_ok "Dispatcharr Updated to $RELEASE" - - msg_info "Creating Python Virtual Environment" - cd $APP_DIR - python3 -m venv env - source env/bin/activate - $STD pip install --upgrade pip - $STD pip install -r requirements.txt - $STD pip install gunicorn - ln -sf /usr/bin/ffmpeg $APP_DIR/env/bin/ffmpeg - msg_ok "Python Environment Setup" - - msg_info "Building Frontend" - cd $APP_DIR/frontend - $STD npm install --legacy-peer-deps - $STD npm run build - msg_ok "Built Frontend" - - msg_info "Running Django Migrations" - cd $APP_DIR - source env/bin/activate - set -o allexport - source /etc/$APP_NAME/$APP_NAME.env - set +o allexport - $STD python manage.py migrate --noinput - $STD python manage.py collectstatic --noinput - msg_ok "Migrations Complete" - - msg_info "Starting $APP" - systemctl start dispatcharr-celery - systemctl start dispatcharr-celerybeat - systemctl start dispatcharr-daphne - systemctl start dispatcharr - msg_ok "Started $APP" - echo "${RELEASE}" >"/opt/${APP}_version.txt" - - msg_info "Cleaning Up" - rm -rf /opt/$POSTGRES_DB-$(date +%F).sql - msg_ok "Cleanup Completed" - - msg_ok "Update Successful, Backup saved to $BACKUP_FILE" - - else - msg_ok "No update required. ${APP} is already at v${RELEASE}" - fi + if [[ ! -d "/opt/dispatcharr" ]]; then + msg_error "No ${APP} Installation Found!" exit + fi + + RELEASE=$(curl -fsSL https://api.github.com/repos/Dispatcharr/Dispatcharr/releases/latest | jq -r '.tag_name' | sed 's/^v//') + if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then + msg_ok "Starting update" + APP_DIR="/opt/dispatcharr" + APP_USER="dispatcharr" + APP_GROUP="dispatcharr" + + msg_info "Stopping $APP" + systemctl stop dispatcharr-celery + systemctl stop dispatcharr-celerybeat + systemctl stop dispatcharr-daphne + systemctl stop dispatcharr + msg_ok "Stopped $APP" + + msg_info "Creating Backup" + BACKUP_FILE="/opt/dispatcharr_$(date +%F).tar.gz" + msg_info "Source and Database backup" + set -o allexport + source /etc/$APP_NAME/$APP_NAME.env + set +o allexport + PGPASSWORD=$POSTGRES_PASSWORD pg_dump -U $POSTGRES_USER -h $POSTGRES_HOST $POSTGRES_DB >/opt/$POSTGRES_DB-$(date +%F).sql + $STD tar -czf "$BACKUP_FILE" /opt/dispatcharr /opt/Dispatcharr_version.txt /opt/$POSTGRES_DB-$(date +%F).sql &>/dev/null + msg_ok "Backup Created" + + msg_info "Updating $APP to v${RELEASE}" + rm -rf /opt/dispatcharr + fetch_and_deploy_gh_release "dispatcharr" "Dispatcharr/Dispatcharr" + chown -R "$APP_USER:$APP_GROUP" "$APP_DIR" + sed -i 's/program\[\x27channel_id\x27\]/program["channel_id"]/g' "${APP_DIR}/apps/output/views.py" + + msg_ok "Dispatcharr Updated to $RELEASE" + + msg_info "Creating Python Virtual Environment" + cd $APP_DIR + python3 -m venv env + source env/bin/activate + $STD pip install --upgrade pip + $STD pip install -r requirements.txt + $STD pip install gunicorn + ln -sf /usr/bin/ffmpeg $APP_DIR/env/bin/ffmpeg + msg_ok "Python Environment Setup" + + msg_info "Building Frontend" + cd $APP_DIR/frontend + $STD npm install --legacy-peer-deps + $STD npm run build + msg_ok "Built Frontend" + + msg_info "Running Django Migrations" + cd $APP_DIR + source env/bin/activate + set -o allexport + source /etc/$APP_NAME/$APP_NAME.env + set +o allexport + $STD python manage.py migrate --noinput + $STD python manage.py collectstatic --noinput + msg_ok "Migrations Complete" + + msg_info "Starting $APP" + systemctl start dispatcharr-celery + systemctl start dispatcharr-celerybeat + systemctl start dispatcharr-daphne + systemctl start dispatcharr + msg_ok "Started $APP" + echo "${RELEASE}" >"/opt/${APP}_version.txt" + + msg_info "Cleaning Up" + rm -rf /opt/$POSTGRES_DB-$(date +%F).sql + msg_ok "Cleanup Completed" + + msg_ok "Update Successful, Backup saved to $BACKUP_FILE" + + else + msg_ok "No update required. ${APP} is already at v${RELEASE}" + fi + exit } start diff --git a/install/dispatcharr-install.sh b/install/dispatcharr-install.sh index 3d53e9164..e4ac7bacf 100644 --- a/install/dispatcharr-install.sh +++ b/install/dispatcharr-install.sh @@ -51,7 +51,7 @@ fetch_and_deploy_gh_release "dispatcharr" "Dispatcharr/Dispatcharr" msg_info "Setup Python virtual environment" cd /opt/dispatcharr -# WICHTIG: Erstelle ein echtes venv statt --system +# uv venv erstellt ein minimales venv - wir nutzen uv zum Installieren $STD uv venv env msg_ok "Virtual environment created" @@ -63,11 +63,11 @@ UV_INDEX_ARGS=(--index-url "$PYPI_URL" --index-strategy unsafe-best-match) for u in "${EXTRA_INDEX_URLS[@]}"; do [[ -n "$u" && "$u" != "$PYPI_URL" ]] && UV_INDEX_ARGS+=(--extra-index-url "$u") done + if [[ -f requirements.txt ]]; then - $STD /opt/dispatcharr/env/bin/python -m pip install --upgrade pip setuptools wheel - $STD /opt/dispatcharr/env/bin/pip install "${UV_INDEX_ARGS[@]}" -r requirements.txt + $STD uv pip install --python /opt/dispatcharr/env/bin/python "${UV_INDEX_ARGS[@]}" -r requirements.txt fi -$STD /opt/dispatcharr/env/bin/pip install "${UV_INDEX_ARGS[@]}" gunicorn gevent celery daphne +$STD uv pip install --python /opt/dispatcharr/env/bin/python "${UV_INDEX_ARGS[@]}" gunicorn gevent celery daphne ln -sf /usr/bin/ffmpeg /opt/dispatcharr/env/bin/ffmpeg msg_ok "Python Requirements Installed" @@ -79,7 +79,6 @@ msg_ok "Built Frontend" msg_info "Running Django Migrations" cd /opt/dispatcharr -# WICHTIG: Setze Umgebungsvariablen für Django/PostgreSQL export POSTGRES_DB="$DB_NAME" export POSTGRES_USER="$DB_USER" export POSTGRES_PASSWORD="$DB_PASS" From 5ad3e3043b9df546cff05f00c3fac05af3f54c0b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 20 Oct 2025 11:04:20 +0200 Subject: [PATCH 1463/1733] Refactor Dispatcharr install script for uv and systemd Switches Python environment setup and package installation to use uv, updates PostgreSQL version to 17, and streamlines database credential creation. Replaces direct systemd ExecStart commands with wrapper scripts for Gunicorn, Celery, Celery Beat, and Daphne, and updates systemd service files for improved reliability. Adds extra proxy headers to Nginx config and improves overall script structure and messaging. --- install/dispatcharr-install.sh | 196 +++++++++++++++++---------------- 1 file changed, 101 insertions(+), 95 deletions(-) diff --git a/install/dispatcharr-install.sh b/install/dispatcharr-install.sh index e4ac7bacf..7b2b84f8d 100644 --- a/install/dispatcharr-install.sh +++ b/install/dispatcharr-install.sh @@ -17,7 +17,6 @@ msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ gcc \ - libpcre3-dev \ libpq-dev \ nginx \ redis-server \ @@ -26,11 +25,11 @@ $STD apt install -y \ streamlink msg_ok "Installed Dependencies" -PYTHON_VERSION="3.13" setup_uv +setup_uv NODE_VERSION="22" setup_nodejs -PG_VERSION="16" setup_postgresql +PG_VERSION="17" setup_postgresql -msg_info "Set up PostgreSQL Database" +msg_info "Creating PostgreSQL Database" DB_NAME=dispatcharr_db DB_USER=dispatcharr_usr DB_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" @@ -39,59 +38,34 @@ $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCO $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" -{ - echo "Dispatcharr-Credentials" - echo "Dispatcharr Database Name: $DB_NAME" - echo "Dispatcharr Database User: $DB_USER" - echo "Dispatcharr Database Password: $DB_PASS" -} >>~/dispatcharr.creds -msg_ok "Set up PostgreSQL Database" + +cat <~/dispatcharr.creds +Dispatcharr-Credentials +Dispatcharr Database Name: $DB_NAME +Dispatcharr Database User: $DB_USER +Dispatcharr Database Password: $DB_PASS +EOF +msg_ok "Created PostgreSQL Database" fetch_and_deploy_gh_release "dispatcharr" "Dispatcharr/Dispatcharr" -msg_info "Setup Python virtual environment" -cd /opt/dispatcharr -# uv venv erstellt ein minimales venv - wir nutzen uv zum Installieren -$STD uv venv env -msg_ok "Virtual environment created" +msg_info "Configuring Dispatcharr" +cd /opt/dispatcharr || exit -msg_info "Installing Python requirements" -PYPI_URL="https://pypi.org/simple" -mapfile -t EXTRA_INDEX_URLS < <(grep -E '^(--(extra-)?index-url|-i)\s' requirements.txt 2>/dev/null | awk '{print $2}' | sed 's#/*$##') +$STD uv sync --frozen +$STD uv run --frozen python manage.py migrate --noinput +$STD uv run --frozen python manage.py collectstatic --noinput -UV_INDEX_ARGS=(--index-url "$PYPI_URL" --index-strategy unsafe-best-match) -for u in "${EXTRA_INDEX_URLS[@]}"; do - [[ -n "$u" && "$u" != "$PYPI_URL" ]] && UV_INDEX_ARGS+=(--extra-index-url "$u") -done - -if [[ -f requirements.txt ]]; then - $STD uv pip install --python /opt/dispatcharr/env/bin/python "${UV_INDEX_ARGS[@]}" -r requirements.txt -fi -$STD uv pip install --python /opt/dispatcharr/env/bin/python "${UV_INDEX_ARGS[@]}" gunicorn gevent celery daphne -ln -sf /usr/bin/ffmpeg /opt/dispatcharr/env/bin/ffmpeg -msg_ok "Python Requirements Installed" - -msg_info "Building Frontend" -cd /opt/dispatcharr/frontend +cd /opt/dispatcharr/frontend || exit $STD npm install --legacy-peer-deps $STD npm run build -msg_ok "Built Frontend" - -msg_info "Running Django Migrations" -cd /opt/dispatcharr -export POSTGRES_DB="$DB_NAME" -export POSTGRES_USER="$DB_USER" -export POSTGRES_PASSWORD="$DB_PASS" -export POSTGRES_HOST="localhost" - -$STD ./env/bin/python manage.py migrate --noinput -$STD ./env/bin/python manage.py collectstatic --noinput -msg_ok "Migrations Complete" +msg_ok "Configured Dispatcharr" msg_info "Configuring Nginx" cat </etc/nginx/sites-available/dispatcharr.conf server { listen 9191; + server_name _; location / { include proxy_params; @@ -116,6 +90,9 @@ server { proxy_set_header Upgrade \$http_upgrade; proxy_set_header Connection "Upgrade"; proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto \$scheme; } } EOF @@ -123,34 +100,74 @@ EOF ln -sf /etc/nginx/sites-available/dispatcharr.conf /etc/nginx/sites-enabled/dispatcharr.conf rm -f /etc/nginx/sites-enabled/default $STD nginx -t -systemctl restart nginx -systemctl enable nginx +systemctl enable -q --now nginx msg_ok "Configured Nginx" -msg_info "Creating systemd services" +msg_info "Creating Services" + +cat </opt/dispatcharr/start-gunicorn.sh +#!/usr/bin/env bash +cd /opt/dispatcharr +export POSTGRES_DB=$DB_NAME +export POSTGRES_USER=$DB_USER +export POSTGRES_PASSWORD=$DB_PASS +export POSTGRES_HOST=localhost +uv run --frozen gunicorn \ + --workers=4 \ + --worker-class=gevent \ + --timeout=300 \ + --bind 0.0.0.0:5656 \ + dispatcharr.wsgi:application +EOF +chmod +x /opt/dispatcharr/start-gunicorn.sh + +cat </opt/dispatcharr/start-celery.sh +#!/usr/bin/env bash +cd /opt/dispatcharr +export POSTGRES_DB=$DB_NAME +export POSTGRES_USER=$DB_USER +export POSTGRES_PASSWORD=$DB_PASS +export POSTGRES_HOST=localhost +export CELERY_BROKER_URL=redis://localhost:6379/0 +uv run --frozen celery -A dispatcharr worker -l info -c 4 +EOF +chmod +x /opt/dispatcharr/start-celery.sh + +cat </opt/dispatcharr/start-celerybeat.sh +#!/usr/bin/env bash +cd /opt/dispatcharr +export POSTGRES_DB=$DB_NAME +export POSTGRES_USER=$DB_USER +export POSTGRES_PASSWORD=$DB_PASS +export POSTGRES_HOST=localhost +export CELERY_BROKER_URL=redis://localhost:6379/0 +uv run --frozen celery -A dispatcharr beat -l info +EOF +chmod +x /opt/dispatcharr/start-celerybeat.sh + +cat </opt/dispatcharr/start-daphne.sh +#!/usr/bin/env bash +cd /opt/dispatcharr +export POSTGRES_DB=$DB_NAME +export POSTGRES_USER=$DB_USER +export POSTGRES_PASSWORD=$DB_PASS +export POSTGRES_HOST=localhost +uv run --frozen daphne -b 0.0.0.0 -p 8001 dispatcharr.asgi:application +EOF +chmod +x /opt/dispatcharr/start-daphne.sh cat </etc/systemd/system/dispatcharr.service [Unit] -Description=Gunicorn for Dispatcharr +Description=Dispatcharr Web Server After=network.target postgresql.service redis-server.service [Service] +Type=simple WorkingDirectory=/opt/dispatcharr -RuntimeDirectory=dispatcharr -RuntimeDirectoryMode=0775 -Environment="PATH=/opt/dispatcharr/env/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin" -Environment="POSTGRES_DB=$DB_NAME" -Environment="POSTGRES_USER=$DB_USER" -Environment="POSTGRES_PASSWORD=$DB_PASS" -Environment="POSTGRES_HOST=localhost" -ExecStart=/opt/dispatcharr/env/bin/gunicorn \\ - --workers=4 \\ - --worker-class=gevent \\ - --timeout=300 \\ - --bind 0.0.0.0:5656 \\ - dispatcharr.wsgi:application -Restart=always -KillMode=mixed +ExecStart=/opt/dispatcharr/start-gunicorn.sh +Restart=on-failure +RestartSec=10 +User=root [Install] WantedBy=multi-user.target @@ -158,21 +175,17 @@ EOF cat </etc/systemd/system/dispatcharr-celery.service [Unit] -Description=Celery Worker for Dispatcharr +Description=Dispatcharr Celery Worker After=network.target redis-server.service Requires=dispatcharr.service [Service] +Type=simple WorkingDirectory=/opt/dispatcharr -Environment="PATH=/opt/dispatcharr/env/bin" -Environment="POSTGRES_DB=$DB_NAME" -Environment="POSTGRES_USER=$DB_USER" -Environment="POSTGRES_PASSWORD=$DB_PASS" -Environment="POSTGRES_HOST=localhost" -Environment="CELERY_BROKER_URL=redis://localhost:6379/0" -ExecStart=/opt/dispatcharr/env/bin/celery -A dispatcharr worker -l info -c 4 -Restart=always -KillMode=mixed +ExecStart=/opt/dispatcharr/start-celery.sh +Restart=on-failure +RestartSec=10 +User=root [Install] WantedBy=multi-user.target @@ -180,21 +193,17 @@ EOF cat </etc/systemd/system/dispatcharr-celerybeat.service [Unit] -Description=Celery Beat Scheduler for Dispatcharr +Description=Dispatcharr Celery Beat Scheduler After=network.target redis-server.service Requires=dispatcharr.service [Service] +Type=simple WorkingDirectory=/opt/dispatcharr -Environment="PATH=/opt/dispatcharr/env/bin" -Environment="POSTGRES_DB=$DB_NAME" -Environment="POSTGRES_USER=$DB_USER" -Environment="POSTGRES_PASSWORD=$DB_PASS" -Environment="POSTGRES_HOST=localhost" -Environment="CELERY_BROKER_URL=redis://localhost:6379/0" -ExecStart=/opt/dispatcharr/env/bin/celery -A dispatcharr beat -l info -Restart=always -KillMode=mixed +ExecStart=/opt/dispatcharr/start-celerybeat.sh +Restart=on-failure +RestartSec=10 +User=root [Install] WantedBy=multi-user.target @@ -202,28 +211,25 @@ EOF cat </etc/systemd/system/dispatcharr-daphne.service [Unit] -Description=Daphne for Dispatcharr (ASGI) +Description=Dispatcharr WebSocket Server (Daphne) After=network.target Requires=dispatcharr.service [Service] +Type=simple WorkingDirectory=/opt/dispatcharr -Environment="PATH=/opt/dispatcharr/env/bin" -Environment="POSTGRES_DB=$DB_NAME" -Environment="POSTGRES_USER=$DB_USER" -Environment="POSTGRES_PASSWORD=$DB_PASS" -Environment="POSTGRES_HOST=localhost" -ExecStart=/opt/dispatcharr/env/bin/daphne -b 0.0.0.0 -p 8001 dispatcharr.asgi:application -Restart=always -KillMode=mixed +ExecStart=/opt/dispatcharr/start-daphne.sh +Restart=on-failure +RestartSec=10 +User=root [Install] WantedBy=multi-user.target EOF systemctl daemon-reload -systemctl enable --now dispatcharr dispatcharr-celery dispatcharr-celerybeat dispatcharr-daphne -msg_ok "Started Dispatcharr Services" +systemctl enable -q --now dispatcharr dispatcharr-celery dispatcharr-celerybeat dispatcharr-daphne +msg_ok "Created Services" motd_ssh customize From aea08ee1ce0734d33b626c30a3d9dd28de7db1da Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 20 Oct 2025 11:16:14 +0200 Subject: [PATCH 1464/1733] fix ssl issue psql 17 --- install/dispatcharr-install.sh | 2 +- misc/tools.func | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/install/dispatcharr-install.sh b/install/dispatcharr-install.sh index 7b2b84f8d..507300fb0 100644 --- a/install/dispatcharr-install.sh +++ b/install/dispatcharr-install.sh @@ -27,7 +27,7 @@ msg_ok "Installed Dependencies" setup_uv NODE_VERSION="22" setup_nodejs -PG_VERSION="17" setup_postgresql +PG_VERSION="16" setup_postgresql msg_info "Creating PostgreSQL Database" DB_NAME=dispatcharr_db diff --git a/misc/tools.func b/misc/tools.func index 70d5a79c4..13860ec3a 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -2727,6 +2727,11 @@ function setup_postgresql() { # Try multiple PostgreSQL package patterns local pg_install_success=false + # First, ensure ssl-cert is available (PostgreSQL dependency) + if apt-cache search "^ssl-cert$" 2>/dev/null | grep -q .; then + $STD apt install -y ssl-cert 2>/dev/null || true + fi + # First try: postgresql-X (common in most repos) if apt-cache search "^postgresql-${PG_VERSION}$" 2>/dev/null | grep -q . && $STD apt install -y "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}" 2>/dev/null; then pg_install_success=true From 606eefd71debfca121c7445f5ba9cd39ca81879b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 20 Oct 2025 11:29:14 +0200 Subject: [PATCH 1465/1733] Update tools.func --- misc/tools.func | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/misc/tools.func b/misc/tools.func index 13860ec3a..acc99ef88 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -554,13 +554,21 @@ cleanup_orphaned_sources() { [[ ! -d "$sources_dir" ]] && return 0 while IFS= read -r -d '' sources_file; do + local basename_file + basename_file=$(basename "$sources_file") + + # NEVER remove debian.sources - this is the standard Debian repository + if [[ "$basename_file" == "debian.sources" ]]; then + continue + fi + # Extract Signed-By path from .sources file local keyring_path keyring_path=$(grep -E '^Signed-By:' "$sources_file" 2>/dev/null | awk '{print $2}') # If keyring doesn't exist, remove the .sources file if [[ -n "$keyring_path" ]] && [[ ! -f "$keyring_path" ]]; then - msg_warn "Removing orphaned sources file: $(basename "$sources_file") (missing keyring: $(basename "$keyring_path"))" + msg_warn "Removing orphaned sources file: $basename_file (missing keyring: $(basename "$keyring_path"))" rm -f "$sources_file" fi done < <(find "$sources_dir" -name "*.sources" -print0 2>/dev/null) From 3077f8787aac688f34125f28a8336613bff2c7a8 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 20 Oct 2025 11:55:15 +0200 Subject: [PATCH 1466/1733] Refactor install script for improved Python env setup Updates Dispatcharr install script to use uv venv and pip for Python dependency management, adds explicit installation of key packages, and centralizes environment variables in a .env file for service scripts. Also updates Node and Python versions, improves service startup scripts to source environment variables, and enhances overall reliability and maintainability of the deployment process. --- install/dispatcharr-install.sh | 79 +++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 31 deletions(-) diff --git a/install/dispatcharr-install.sh b/install/dispatcharr-install.sh index 507300fb0..f66122800 100644 --- a/install/dispatcharr-install.sh +++ b/install/dispatcharr-install.sh @@ -16,7 +16,9 @@ update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ + git \ gcc \ + python3-dev \ libpq-dev \ nginx \ redis-server \ @@ -26,7 +28,7 @@ $STD apt install -y \ msg_ok "Installed Dependencies" setup_uv -NODE_VERSION="22" setup_nodejs +NODE_VERSION="24" setup_nodejs PG_VERSION="16" setup_postgresql msg_info "Creating PostgreSQL Database" @@ -49,12 +51,23 @@ msg_ok "Created PostgreSQL Database" fetch_and_deploy_gh_release "dispatcharr" "Dispatcharr/Dispatcharr" -msg_info "Configuring Dispatcharr" +msg_info "Installing Python Dependencies with uv" cd /opt/dispatcharr || exit -$STD uv sync --frozen -$STD uv run --frozen python manage.py migrate --noinput -$STD uv run --frozen python manage.py collectstatic --noinput +$STD uv venv +$STD uv pip install -r requirements.txt --index-strategy unsafe-best-match +$STD uv pip install gunicorn gevent celery redis daphne +msg_ok "Installed Python Dependencies" + +msg_info "Configuring Dispatcharr" +export DATABASE_URL="postgresql://${DB_USER}:${DB_PASS}@localhost:5432/${DB_NAME}" +export POSTGRES_DB=$DB_NAME +export POSTGRES_USER=$DB_USER +export POSTGRES_PASSWORD=$DB_PASS +export POSTGRES_HOST=localhost + +$STD uv run python manage.py migrate --noinput +$STD uv run python manage.py collectstatic --noinput cd /opt/dispatcharr/frontend || exit $STD npm install --legacy-peer-deps @@ -105,18 +118,27 @@ msg_ok "Configured Nginx" msg_info "Creating Services" +# Create environment file for services +cat </opt/dispatcharr/.env +DATABASE_URL=postgresql://${DB_USER}:${DB_PASS}@localhost:5432/${DB_NAME} +POSTGRES_DB=$DB_NAME +POSTGRES_USER=$DB_USER +POSTGRES_PASSWORD=$DB_PASS +POSTGRES_HOST=localhost +CELERY_BROKER_URL=redis://localhost:6379/0 +EOF + cat </opt/dispatcharr/start-gunicorn.sh #!/usr/bin/env bash cd /opt/dispatcharr -export POSTGRES_DB=$DB_NAME -export POSTGRES_USER=$DB_USER -export POSTGRES_PASSWORD=$DB_PASS -export POSTGRES_HOST=localhost -uv run --frozen gunicorn \ - --workers=4 \ - --worker-class=gevent \ - --timeout=300 \ - --bind 0.0.0.0:5656 \ +set -a +source .env +set +a +exec uv run gunicorn \\ + --workers=4 \\ + --worker-class=gevent \\ + --timeout=300 \\ + --bind 0.0.0.0:5656 \\ dispatcharr.wsgi:application EOF chmod +x /opt/dispatcharr/start-gunicorn.sh @@ -124,35 +146,30 @@ chmod +x /opt/dispatcharr/start-gunicorn.sh cat </opt/dispatcharr/start-celery.sh #!/usr/bin/env bash cd /opt/dispatcharr -export POSTGRES_DB=$DB_NAME -export POSTGRES_USER=$DB_USER -export POSTGRES_PASSWORD=$DB_PASS -export POSTGRES_HOST=localhost -export CELERY_BROKER_URL=redis://localhost:6379/0 -uv run --frozen celery -A dispatcharr worker -l info -c 4 +set -a +source .env +set +a +exec uv run celery -A dispatcharr worker -l info -c 4 EOF chmod +x /opt/dispatcharr/start-celery.sh cat </opt/dispatcharr/start-celerybeat.sh #!/usr/bin/env bash cd /opt/dispatcharr -export POSTGRES_DB=$DB_NAME -export POSTGRES_USER=$DB_USER -export POSTGRES_PASSWORD=$DB_PASS -export POSTGRES_HOST=localhost -export CELERY_BROKER_URL=redis://localhost:6379/0 -uv run --frozen celery -A dispatcharr beat -l info +set -a +source .env +set +a +exec uv run celery -A dispatcharr beat -l info EOF chmod +x /opt/dispatcharr/start-celerybeat.sh cat </opt/dispatcharr/start-daphne.sh #!/usr/bin/env bash cd /opt/dispatcharr -export POSTGRES_DB=$DB_NAME -export POSTGRES_USER=$DB_USER -export POSTGRES_PASSWORD=$DB_PASS -export POSTGRES_HOST=localhost -uv run --frozen daphne -b 0.0.0.0 -p 8001 dispatcharr.asgi:application +set -a +source .env +set +a +exec uv run daphne -b 0.0.0.0 -p 8001 dispatcharr.asgi:application EOF chmod +x /opt/dispatcharr/start-daphne.sh From 80fae87b51c5599fa74606dac49d5cd62621ae03 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 20 Oct 2025 12:09:48 +0200 Subject: [PATCH 1467/1733] Update dispatcharr-install.sh --- install/dispatcharr-install.sh | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/install/dispatcharr-install.sh b/install/dispatcharr-install.sh index f66122800..86b0c89b8 100644 --- a/install/dispatcharr-install.sh +++ b/install/dispatcharr-install.sh @@ -80,17 +80,28 @@ server { listen 9191; server_name _; - location / { - include proxy_params; - proxy_pass http://127.0.0.1:5656; + # Serve static assets with correct MIME types + location /assets/ { + alias /opt/dispatcharr/frontend/dist/assets/; + expires 30d; + add_header Cache-Control "public, immutable"; + + # Explicitly set MIME types for webpack-built assets + types { + text/javascript js; + text/css css; + image/png png; + image/svg+xml svg svgz; + font/woff2 woff2; + font/woff woff; + font/ttf ttf; + } } location /static/ { alias /opt/dispatcharr/static/; - } - - location /assets/ { - alias /opt/dispatcharr/frontend/dist/assets/; + expires 30d; + add_header Cache-Control "public, immutable"; } location /media/ { @@ -107,6 +118,12 @@ server { proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto \$scheme; } + + # All other requests proxy to Gunicorn + location / { + include proxy_params; + proxy_pass http://127.0.0.1:5656; + } } EOF @@ -118,7 +135,6 @@ msg_ok "Configured Nginx" msg_info "Creating Services" -# Create environment file for services cat </opt/dispatcharr/.env DATABASE_URL=postgresql://${DB_USER}:${DB_PASS}@localhost:5432/${DB_NAME} POSTGRES_DB=$DB_NAME From 5c8d892965e82045e78e9262853c20dd679f8b9c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 20 Oct 2025 12:11:47 +0200 Subject: [PATCH 1468/1733] fix port --- ct/dispatcharr.sh | 2 +- install/dispatcharr-install.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/dispatcharr.sh b/ct/dispatcharr.sh index 9e0324289..de33d7928 100644 --- a/ct/dispatcharr.sh +++ b/ct/dispatcharr.sh @@ -115,4 +115,4 @@ description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9191${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" diff --git a/install/dispatcharr-install.sh b/install/dispatcharr-install.sh index 86b0c89b8..dc1867966 100644 --- a/install/dispatcharr-install.sh +++ b/install/dispatcharr-install.sh @@ -77,7 +77,7 @@ msg_ok "Configured Dispatcharr" msg_info "Configuring Nginx" cat </etc/nginx/sites-available/dispatcharr.conf server { - listen 9191; + listen 80; server_name _; # Serve static assets with correct MIME types From b3f5c30232f11baab1ea988431aa376abee90c65 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 20 Oct 2025 12:14:13 +0200 Subject: [PATCH 1469/1733] cleanup --- install/dispatcharr-install.sh | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/install/dispatcharr-install.sh b/install/dispatcharr-install.sh index dc1867966..5fda99373 100644 --- a/install/dispatcharr-install.sh +++ b/install/dispatcharr-install.sh @@ -16,7 +16,6 @@ update_os msg_info "Installing Dependencies" $STD apt install -y \ build-essential \ - git \ gcc \ python3-dev \ libpq-dev \ @@ -65,10 +64,16 @@ export POSTGRES_DB=$DB_NAME export POSTGRES_USER=$DB_USER export POSTGRES_PASSWORD=$DB_PASS export POSTGRES_HOST=localhost - $STD uv run python manage.py migrate --noinput $STD uv run python manage.py collectstatic --noinput - +cat </opt/dispatcharr/.env +DATABASE_URL=postgresql://${DB_USER}:${DB_PASS}@localhost:5432/${DB_NAME} +POSTGRES_DB=$DB_NAME +POSTGRES_USER=$DB_USER +POSTGRES_PASSWORD=$DB_PASS +POSTGRES_HOST=localhost +CELERY_BROKER_URL=redis://localhost:6379/0 +EOF cd /opt/dispatcharr/frontend || exit $STD npm install --legacy-peer-deps $STD npm run build @@ -129,21 +134,10 @@ EOF ln -sf /etc/nginx/sites-available/dispatcharr.conf /etc/nginx/sites-enabled/dispatcharr.conf rm -f /etc/nginx/sites-enabled/default -$STD nginx -t systemctl enable -q --now nginx msg_ok "Configured Nginx" msg_info "Creating Services" - -cat </opt/dispatcharr/.env -DATABASE_URL=postgresql://${DB_USER}:${DB_PASS}@localhost:5432/${DB_NAME} -POSTGRES_DB=$DB_NAME -POSTGRES_USER=$DB_USER -POSTGRES_PASSWORD=$DB_PASS -POSTGRES_HOST=localhost -CELERY_BROKER_URL=redis://localhost:6379/0 -EOF - cat </opt/dispatcharr/start-gunicorn.sh #!/usr/bin/env bash cd /opt/dispatcharr @@ -259,8 +253,6 @@ User=root [Install] WantedBy=multi-user.target EOF - -systemctl daemon-reload systemctl enable -q --now dispatcharr dispatcharr-celery dispatcharr-celerybeat dispatcharr-daphne msg_ok "Created Services" From e1c3eb373faf0bace0bdac9b4d9481a3ec897b06 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Mon, 20 Oct 2025 14:28:25 +0200 Subject: [PATCH 1470/1733] add: intel deps --- ct/jellyfin.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ct/jellyfin.sh b/ct/jellyfin.sh index 54fb1af56..6b94ad6a2 100644 --- a/ct/jellyfin.sh +++ b/ct/jellyfin.sh @@ -27,6 +27,14 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi + + msg_info "Updating Intel Dependencies" + fetch_and_deploy_gh_release "intel-igc-core-2" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-core-2_*_amd64.deb" + fetch_and_deploy_gh_release "intel-igc-opencl-2" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-opencl-2_*_amd64.deb" + fetch_and_deploy_gh_release "intel-libgdgmm12" "intel/compute-runtime" "binary" "latest" "" "libigdgmm12_*_amd64.deb" + fetch_and_deploy_gh_release "intel-opencl-icd" "intel/compute-runtime" "binary" "latest" "" "intel-opencl-icd_*_amd64.deb" + msg_ok "Updated Intel Dependencies" + msg_info "Updating ${APP} LXC" $STD apt-get update $STD apt-get -y upgrade From 1961175ba20838f1216c320854d843d954f927b4 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Mon, 20 Oct 2025 23:36:17 +0200 Subject: [PATCH 1471/1733] Dispatcharr: fix nginx --- install/dispatcharr-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/dispatcharr-install.sh b/install/dispatcharr-install.sh index 5fda99373..09529d68d 100644 --- a/install/dispatcharr-install.sh +++ b/install/dispatcharr-install.sh @@ -134,7 +134,7 @@ EOF ln -sf /etc/nginx/sites-available/dispatcharr.conf /etc/nginx/sites-enabled/dispatcharr.conf rm -f /etc/nginx/sites-enabled/default -systemctl enable -q --now nginx +systemctl restart nginx msg_ok "Configured Nginx" msg_info "Creating Services" From 186490e0c9de70591fef01c5f8d0bebb05239327 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 21 Oct 2025 05:10:14 +0000 Subject: [PATCH 1472/1733] Bump vite in /frontend in the npm_and_yarn group across 1 directory Bumps the npm_and_yarn group with 1 update in the /frontend directory: [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite). Updates `vite` from 6.3.6 to 6.4.1 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/create-vite@6.4.1/packages/vite) --- updated-dependencies: - dependency-name: vite dependency-version: 6.4.1 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] --- frontend/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 04b1ca67d..132da5f05 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -10202,9 +10202,9 @@ "license": "MIT" }, "node_modules/vite": { - "version": "6.3.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.6.tgz", - "integrity": "sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", + "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "dev": true, "license": "MIT", "dependencies": { From 4ac0db3dc2994e6c198aea913e956530d38699d6 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 21 Oct 2025 09:46:00 +0200 Subject: [PATCH 1473/1733] Update Node.js version and improve MySQL setup for Debian Bumps Node.js version from 22 to 24 in ente-install.sh. Enhances MySQL setup in tools.func to explicitly block installation on Debian Trixie/forky/sid due to incompatibility, providing alternative solutions and clearer messaging. --- install/ente-install.sh | 2 +- misc/tools.func | 26 +++++++++++++++----------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/install/ente-install.sh b/install/ente-install.sh index b08386d42..8a4b06ab4 100644 --- a/install/ente-install.sh +++ b/install/ente-install.sh @@ -24,7 +24,7 @@ msg_ok "Installed Dependencies" PG_VERSION="17" setup_postgresql setup_go -NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs +NODE_VERSION="24" NODE_MODULE="yarn" setup_nodejs fetch_and_deploy_gh_release "ente" "ente-io/ente" "tarball" "latest" "/opt/ente" msg_info "Setting up PostgreSQL" diff --git a/misc/tools.func b/misc/tools.func index acc99ef88..59983a905 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -2311,22 +2311,26 @@ function setup_mysql() { # Cleanup old repository files cleanup_old_repo_files "mysql" + # MySQL compatibility check for Debian Trixie + if [[ "$DISTRO_ID" == "debian" && "$DISTRO_CODENAME" =~ ^(trixie|forky|sid)$ ]]; then + msg_error "MySQL is not compatible with Debian testing/unstable (${DISTRO_CODENAME})" + msg_info "Reason: MySQL packages depend on libaio1, but Debian Trixie uses libaio1t64" + msg_info "" + msg_info "Alternative solutions:" + msg_info " 1. Use MariaDB instead: MARIADB_VERSION=\"11.4\" setup_mariadb" + msg_info " 2. Wait for Oracle to update MySQL packages for Debian 13" + msg_info " 3. Use Debian 12 (Bookworm) which is officially supported" + return 1 + fi + # MySQL doesn't support Debian testing/unstable - try bookworm as fallback local SUITE case "$DISTRO_CODENAME" in - trixie | forky | sid) - # MySQL doesn't publish packages for Debian testing/unstable - # Try bookworm as fallback - if verify_repo_available "https://repo.mysql.com/apt/${DISTRO_ID}" "bookworm"; then - SUITE="bookworm" - msg_warn "MySQL ${MYSQL_VERSION} not available for ${DISTRO_CODENAME}, using bookworm packages" - else - msg_error "MySQL ${MYSQL_VERSION} package not available for ${DISTRO_ID}-${DISTRO_CODENAME}" - msg_info "MySQL typically doesn't support Debian testing/unstable. Consider using MariaDB instead." - return 1 - fi + bookworm | bullseye) + SUITE="$DISTRO_CODENAME" ;; *) + # For Ubuntu SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://repo.mysql.com/apt/${DISTRO_ID}") ;; esac From a0212eb276016247f7471139f0b242071628c30c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 21 Oct 2025 09:50:34 +0200 Subject: [PATCH 1474/1733] Replace su with runuser for PostgreSQL operations Updated the setup_postgresql function to use runuser instead of su for executing pg_dumpall and psql as the postgres user. This improves compatibility and security when running these commands. --- misc/tools.func | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 59983a905..39061a726 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -2694,7 +2694,7 @@ function setup_postgresql() { msg_info "Installing PostgreSQL $PG_VERSION" if [[ -n "$CURRENT_PG_VERSION" ]]; then - $STD su - postgres -c "pg_dumpall > /var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql" + $STD runuser -u postgres -- pg_dumpall > /var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql $STD systemctl stop postgresql fi @@ -2775,7 +2775,7 @@ function setup_postgresql() { if [[ -n "$CURRENT_PG_VERSION" ]]; then $STD apt purge -y "postgresql-${CURRENT_PG_VERSION}" "postgresql-client-${CURRENT_PG_VERSION}" 2>/dev/null || true - $STD su - postgres -c "psql < /var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql" 2>/dev/null || true + $STD runuser -u postgres -- psql < /var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql 2>/dev/null || true fi $STD systemctl enable --now postgresql 2>/dev/null || true From 915e2d4f585eddfe679ab37dd475209daa7bf96f Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 21 Oct 2025 09:57:33 +0200 Subject: [PATCH 1475/1733] Update tools.func --- misc/tools.func | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 39061a726..11f8076ea 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -2694,7 +2694,7 @@ function setup_postgresql() { msg_info "Installing PostgreSQL $PG_VERSION" if [[ -n "$CURRENT_PG_VERSION" ]]; then - $STD runuser -u postgres -- pg_dumpall > /var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql + $STD runuser -u postgres -- pg_dumpall >/var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql $STD systemctl stop postgresql fi @@ -2775,7 +2775,7 @@ function setup_postgresql() { if [[ -n "$CURRENT_PG_VERSION" ]]; then $STD apt purge -y "postgresql-${CURRENT_PG_VERSION}" "postgresql-client-${CURRENT_PG_VERSION}" 2>/dev/null || true - $STD runuser -u postgres -- psql < /var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql 2>/dev/null || true + $STD runuser -u postgres -- psql /dev/null || true fi $STD systemctl enable --now postgresql 2>/dev/null || true From dbbf14f22a125530c831c8ae97b78c24c5478c76 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 21 Oct 2025 10:00:06 +0200 Subject: [PATCH 1476/1733] Improve MySQL setup for Debian Trixie compatibility Refactored MySQL installation logic to use Bookworm packages for Debian testing/unstable (Trixie, Forky, Sid) instead of blocking installation. Added conditional purge of MySQL packages only if installed and improved messaging for unsupported distributions. --- misc/tools.func | 44 +++++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 11f8076ea..3b874acc0 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -2305,35 +2305,33 @@ function setup_mysql() { fi if [[ "$NEED_INSTALL" == true ]]; then - $STD systemctl stop mysql || true - $STD apt purge -y "^mysql-server.*" "^mysql-client.*" "^mysql-common.*" || true - # Cleanup old repository files cleanup_old_repo_files "mysql" - # MySQL compatibility check for Debian Trixie + # MySQL compatibility check for Debian Trixie - use Bookworm packages + local SUITE if [[ "$DISTRO_ID" == "debian" && "$DISTRO_CODENAME" =~ ^(trixie|forky|sid)$ ]]; then - msg_error "MySQL is not compatible with Debian testing/unstable (${DISTRO_CODENAME})" - msg_info "Reason: MySQL packages depend on libaio1, but Debian Trixie uses libaio1t64" - msg_info "" - msg_info "Alternative solutions:" - msg_info " 1. Use MariaDB instead: MARIADB_VERSION=\"11.4\" setup_mariadb" - msg_info " 2. Wait for Oracle to update MySQL packages for Debian 13" - msg_info " 3. Use Debian 12 (Bookworm) which is officially supported" - return 1 + msg_warn "MySQL not officially supported on Debian testing/unstable - using Bookworm packages" + SUITE="bookworm" + else + case "$DISTRO_CODENAME" in + bookworm | bullseye) + SUITE="$DISTRO_CODENAME" + ;; + *) + # For Ubuntu + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://repo.mysql.com/apt/${DISTRO_ID}") + ;; + esac fi - # MySQL doesn't support Debian testing/unstable - try bookworm as fallback - local SUITE - case "$DISTRO_CODENAME" in - bookworm | bullseye) - SUITE="$DISTRO_CODENAME" - ;; - *) - # For Ubuntu - SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://repo.mysql.com/apt/${DISTRO_ID}") - ;; - esac + # Stop existing MySQL if running + $STD systemctl stop mysql 2>/dev/null || true + + # Only purge if MySQL is actually installed + if dpkg -l | grep -q "^ii.*mysql-server"; then + $STD apt purge -y mysql-server* mysql-client* mysql-common 2>/dev/null || true + fi # Use standardized repo setup setup_deb822_repo \ From 098afdb9dc845e39ec6978f3278d8f7474bf3279 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 21 Oct 2025 10:05:29 +0200 Subject: [PATCH 1477/1733] Update tools.func --- misc/tools.func | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 3b874acc0..dd8a28b7f 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -2308,23 +2308,25 @@ function setup_mysql() { # Cleanup old repository files cleanup_old_repo_files "mysql" - # MySQL compatibility check for Debian Trixie - use Bookworm packages - local SUITE + # MySQL compatibility check for Debian Trixie if [[ "$DISTRO_ID" == "debian" && "$DISTRO_CODENAME" =~ ^(trixie|forky|sid)$ ]]; then - msg_warn "MySQL not officially supported on Debian testing/unstable - using Bookworm packages" - SUITE="bookworm" - else - case "$DISTRO_CODENAME" in - bookworm | bullseye) - SUITE="$DISTRO_CODENAME" - ;; - *) - # For Ubuntu - SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://repo.mysql.com/apt/${DISTRO_ID}") - ;; - esac + msg_error "MySQL is not compatible with Debian ${DISTRO_CODENAME}" + msg_info "Use MariaDB instead: MARIADB_VERSION=\"11.4\" setup_mariadb" + return 1 fi + # Determine suite + local SUITE + case "$DISTRO_CODENAME" in + bookworm | bullseye) + SUITE="$DISTRO_CODENAME" + ;; + *) + # For Ubuntu + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://repo.mysql.com/apt/${DISTRO_ID}") + ;; + esac + # Stop existing MySQL if running $STD systemctl stop mysql 2>/dev/null || true From 300db370f030f8fec4a4f704b011b29d5824233d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 21 Oct 2025 10:38:14 +0200 Subject: [PATCH 1478/1733] Update tools.func --- misc/tools.func | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index dd8a28b7f..2ca6d09eb 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -2308,30 +2308,31 @@ function setup_mysql() { # Cleanup old repository files cleanup_old_repo_files "mysql" - # MySQL compatibility check for Debian Trixie - if [[ "$DISTRO_ID" == "debian" && "$DISTRO_CODENAME" =~ ^(trixie|forky|sid)$ ]]; then - msg_error "MySQL is not compatible with Debian ${DISTRO_CODENAME}" - msg_info "Use MariaDB instead: MARIADB_VERSION=\"11.4\" setup_mariadb" - return 1 - fi - - # Determine suite + # Determine suite - use bookworm for Debian testing/unstable local SUITE - case "$DISTRO_CODENAME" in - bookworm | bullseye) - SUITE="$DISTRO_CODENAME" - ;; - *) - # For Ubuntu + if [[ "$DISTRO_ID" == "debian" ]]; then + case "$DISTRO_CODENAME" in + bookworm | bullseye) + SUITE="$DISTRO_CODENAME" + ;; + trixie | forky | sid) + msg_warn "Using MySQL Bookworm packages on Debian ${DISTRO_CODENAME}" + SUITE="bookworm" + ;; + *) + SUITE="bookworm" # Fallback to bookworm for unknown Debian versions + ;; + esac + else + # For Ubuntu - use fallback detection SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://repo.mysql.com/apt/${DISTRO_ID}") - ;; - esac + fi # Stop existing MySQL if running $STD systemctl stop mysql 2>/dev/null || true # Only purge if MySQL is actually installed - if dpkg -l | grep -q "^ii.*mysql-server"; then + if dpkg -l 2>/dev/null | grep -q "^ii.*mysql-server"; then $STD apt purge -y mysql-server* mysql-client* mysql-common 2>/dev/null || true fi From 5441f790c44365249f7098c57b585920ad5be569 Mon Sep 17 00:00:00 2001 From: 1DaCode <49216638+jdacode@users.noreply.github.com> Date: Tue, 21 Oct 2025 19:34:18 +1030 Subject: [PATCH 1479/1733] Comfyui (#814) * Init ComfyUI dev branch Testing * Fixed url build.func testing * Revert urls * Removed subfunctions * Updated .sh correct url for testing * Fixed install.func for testing * Fixed urls for testing * Added manager installation * Revert urls * Updated script * Removed comments from comfyui.sh * Updated script * Updated script * Updated script * Updated Debian 13 * Updated Debian 13 * Changed apt-get --- ct/comfyui.sh | 42 +++++++++++++++ frontend/public/json/comfyui.json | 43 +++++++++++++++ install/comfyui-install.sh | 87 +++++++++++++++++++++++++++++++ 3 files changed, 172 insertions(+) create mode 100644 ct/comfyui.sh create mode 100644 frontend/public/json/comfyui.json create mode 100644 install/comfyui-install.sh diff --git a/ct/comfyui.sh b/ct/comfyui.sh new file mode 100644 index 000000000..52354e45a --- /dev/null +++ b/ct/comfyui.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: jdacode +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/comfyanonymous/ComfyUI + +APP="ComfyUI" +var_tags="${var_tags:-ai}" +var_cpu="${var_cpu:-4}" +var_ram="${var_ram:-8192}" +var_disk="${var_disk:-25}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -f /opt/${APP} ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_error "To update use the ${APP} Manager." + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8188${CL}" diff --git a/frontend/public/json/comfyui.json b/frontend/public/json/comfyui.json new file mode 100644 index 000000000..6c9687f94 --- /dev/null +++ b/frontend/public/json/comfyui.json @@ -0,0 +1,43 @@ +{ + "name": "ComfyUI", + "slug": "comfyui", + "categories": [ + 20 + ], + "date_created": "2025-08-01", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 8188, + "documentation": "https://github.com/comfyanonymous/ComfyUI", + "website": "https://www.comfy.org/", + "logo": "https://framerusercontent.com/images/3cNQMWKzIhIrQ5KErBm7dSmbd2w.png", + "description": "ComfyUI is a node-based interface and inference engine for generative AI. Users can combine various AI models and operations through nodes to achieve highly customizable and controllable content generation.", + "install_methods": [ + { + "type": "default", + "script": "ct/comfyui.sh", + "resources": { + "cpu": 4, + "ram": 8192, + "hdd": 25, + "os": "debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Application takes long time to install. Please be patient!", + "type": "warning" + }, + { + "text": "Please check that you have installed the drivers for your GPU.", + "type": "info" + } + ] +} diff --git a/install/comfyui-install.sh b/install/comfyui-install.sh new file mode 100644 index 000000000..4f5b7de41 --- /dev/null +++ b/install/comfyui-install.sh @@ -0,0 +1,87 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: jdacode +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/comfyanonymous/ComfyUI + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +echo +echo "${TAB3}Choose the GPU type for ComfyUI:" +echo "${TAB3}[1]-None [2]-NVIDIA [3]-AMD [4]-Intel" +read -rp "${TAB3}Enter your choice [1-4] (default: 1): " gpu_choice +gpu_choice=${gpu_choice:-1} +case "$gpu_choice" in +1) comfyui_gpu_type="none";; +2) comfyui_gpu_type="nvidia";; +3) comfyui_gpu_type="amd";; +4) comfyui_gpu_type="intel";; +*) comfyui_gpu_type="none"; echo "${TAB3}Invalid choice. Defaulting to ${comfyui_gpu_type}." ;; +esac +echo + +PYTHON_VERSION="3.12" setup_uv + +fetch_and_deploy_gh_release "ComfyUI" "comfyanonymous/ComfyUI" "tarball" "latest" "/opt/ComfyUI" + +msg_info "Python dependencies" +$STD uv venv "/opt/ComfyUI/venv" +if [[ "${comfyui_gpu_type,,}" == "nvidia" ]]; then + $STD uv pip install \ + torch \ + torchvision \ + torchaudio \ + --extra-index-url "https://download.pytorch.org/whl/cu128" \ + --python="/opt/ComfyUI/venv/bin/python" +elif [[ "${comfyui_gpu_type,,}" == "amd" ]]; then + $STD uv pip install \ + torch \ + torchvision \ + torchaudio \ + --index-url "https://download.pytorch.org/whl/rocm6.3" \ + --python="/opt/ComfyUI/venv/bin/python" +elif [[ "${comfyui_gpu_type,,}" == "intel" ]]; then + $STD uv pip install \ + torch \ + torchvision \ + torchaudio \ + --index-url "https://download.pytorch.org/whl/xpu" \ + --python="/opt/ComfyUI/venv/bin/python" +fi +$STD uv pip install -r "/opt/ComfyUI/requirements.txt" --python="/opt/ComfyUI/venv/bin/python" +msg_ok "Python dependencies" + +msg_info "Creating Service" +cat </etc/systemd/system/comfyui.service +[Unit] +Description=ComfyUI Service +After=network.target + +[Service] +Type=simple +User=root +WorkingDirectory=/opt/ComfyUI +ExecStart=/opt/ComfyUI/venv/bin/python /opt/ComfyUI/main.py --listen --port 8188 --cpu +Restart=on-failure + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now comfyui +msg_ok "Created Service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt -y autoremove +$STD apt -y autoclean +$STD apt -y clean +msg_ok "Cleaned" From d099b83462a1613051763e734b79dc8949549cc7 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Tue, 21 Oct 2025 11:19:24 +0200 Subject: [PATCH 1480/1733] hwacel, by @micklesk --- misc/tools.func | 89 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/misc/tools.func b/misc/tools.func index 2ca6d09eb..4cd9b483c 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -1803,6 +1803,95 @@ function setup_gs() { msg_ok "Installed Ghostscript $LATEST_VERSION_DOTTED" } + +# ------------------------------------------------------------------------------ +# Sets up Hardware Acceleration on debian or ubuntu. +# +# Description: +# - Determites CPU/GPU/APU Vendor +# - Installs the correct libraries and packages +# - Sets up Hardware Acceleration +# +# Notes: +# - Requires: build-essential, libtool, libjpeg-dev, libpng-dev, etc. +# ------------------------------------------------------------------------------ +function setup_hwaccel () { + msg_info "Setting Up Hardware Acceleration" + + # Detect GPU vendor (Intel, AMD, NVIDIA) + local gpu_vendor + gpu_vendor=$(lspci | grep -Ei 'vga|3d|display' | grep -Eo 'Intel|AMD|NVIDIA' | head -n1) + + # Detect CPU vendor (relevant for AMD APUs) + local cpu_vendor + cpu_vendor=$(lscpu | grep -i 'Vendor ID' | awk '{print $3}') + + if [[ -z "$gpu_vendor" && -z "$cpu_vendor" ]]; then + msg_error "No GPU or CPU vendor detected (missing lspci/lscpu output)" + return 1 + fi + + # Detect OS + local os_id os_codename + os_id=$(grep -oP '(?<=^ID=).+' /etc/os-release | tr -d '"') + os_codename=$(grep -oP '(?<=^VERSION_CODENAME=).+' /etc/os-release | tr -d '"') + + # Determine if we are on a VM or LXC + local in_ct="${CTTYPE:-0}" + + case "$gpu_vendor" in + Intel) + msg_info "Detected Intel GPU — configuring Intel hardware acceleration" + + if [[ "$os_id" == "ubuntu" ]]; then + $STD apt -y install intel-opencl-icd || msg_error "Failed to install intel-opencl-icd" + else + fetch_and_deploy_gh_release "intel-igc-core-2" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-core-2_*_amd64.deb" + fetch_and_deploy_gh_release "intel-igc-opencl-2" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-opencl-2_*_amd64.deb" + fetch_and_deploy_gh_release "intel-libgdgmm12" "intel/compute-runtime" "binary" "latest" "" "libigdgmm12_*_amd64.deb" + fetch_and_deploy_gh_release "intel-opencl-icd" "intel/compute-runtime" "binary" "latest" "" "intel-opencl-icd_*_amd64.deb" + fi + + $STD apt -y install va-driver-all ocl-icd-libopencl1 vainfo intel-gpu-tools || msg_error "Failed to install GPU dependencies" + ;; + AMD) + msg_info "Detected AMD GPU — configuring Mesa-based hardware acceleration" + + $STD apt -y install mesa-va-drivers mesa-vdpau-drivers mesa-opencl-icd vainfo clinfo || msg_error "Failed to install AMD GPU dependencies" + + # For AMD CPUs without discrete GPU (APUs) + if [[ "$cpu_vendor" == "AuthenticAMD" && "$gpu_vendor" != "AMD" ]]; then + msg_info "Detected AMD CPU (APU) — enabling VAAPI via Mesa" + $STD apt -y install libdrm-amdgpu1 firmware-amd-graphics || true + fi + ;; + NVIDIA) + msg_info "Detected NVIDIA GPU — skipping automatic configuration (manual driver setup required)" + msg_info "→ Please install proprietary drivers manually via: apt install nvidia-driver" + ;; + *) + # If no discrete GPU, but AMD CPU (e.g., Ryzen APU) + if [[ "$cpu_vendor" == "AuthenticAMD" ]]; then + msg_info "Detected AMD CPU without discrete GPU — installing Mesa OpenCL stack" + $STD apt -y install mesa-opencl-icd ocl-icd-libopencl1 clinfo || msg_error "Failed to install Mesa OpenCL stack" + else + msg_error "No supported GPU vendor detected" + return 1 + fi + ;; + esac + + if [[ "$in_ct" == "0" ]]; then + chgrp video /dev/dri 2>/dev/null || true + chmod 755 /dev/dri 2>/dev/null || true + chmod 660 /dev/dri/* 2>/dev/null || true + $STD adduser "$(id -u -n)" video + $STD adduser "$(id -u -n)" render + fi + + msg_ok "Hardware Acceleration Setup Complete" +} + # ------------------------------------------------------------------------------ # Installs ImageMagick 7 from source (Debian/Ubuntu only). # From b0fe58c6237209f25fd1a8ffb0aeaaa94268a1b4 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Tue, 21 Oct 2025 11:20:12 +0200 Subject: [PATCH 1481/1733] Update setup_hwaccel function notes Clarified notes regarding library requirements and repository sources. --- misc/tools.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/tools.func b/misc/tools.func index 4cd9b483c..84b238556 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -1813,7 +1813,7 @@ function setup_gs() { # - Sets up Hardware Acceleration # # Notes: -# - Requires: build-essential, libtool, libjpeg-dev, libpng-dev, etc. +# - Some things are fetched from intel repositories due to not being in debian repositories. # ------------------------------------------------------------------------------ function setup_hwaccel () { msg_info "Setting Up Hardware Acceleration" From 17fc99b0385ee1c6603dab21a38ddd87e05db5e4 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Tue, 21 Oct 2025 11:39:15 +0200 Subject: [PATCH 1482/1733] Remove hardware acceleration setup from install script Removed hardware acceleration setup steps from the installation script. --- install/jellyfin-install.sh | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/install/jellyfin-install.sh b/install/jellyfin-install.sh index 24daabdff..05b6572db 100644 --- a/install/jellyfin-install.sh +++ b/install/jellyfin-install.sh @@ -12,22 +12,7 @@ catch_errors setting_up_container network_check update_os - -msg_info "Setting Up Hardware Acceleration" -fetch_and_deploy_gh_release "intel-igc-core-2" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-core-2_*_amd64.deb" -fetch_and_deploy_gh_release "intel-igc-opencl-2" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-opencl-2_*_amd64.deb" -fetch_and_deploy_gh_release "intel-libgdgmm12" "intel/compute-runtime" "binary" "latest" "" "libigdgmm12_*_amd64.deb" -fetch_and_deploy_gh_release "intel-opencl-icd" "intel/compute-runtime" "binary" "latest" "" "intel-opencl-icd_*_amd64.deb" - -$STD apt-get -y install {va-driver-all,ocl-icd-libopencl1,vainfo,intel-gpu-tools} -if [[ "$CTTYPE" == "0" ]]; then - chgrp video /dev/dri - chmod 755 /dev/dri - chmod 660 /dev/dri/* - $STD adduser $(id -u -n) video - $STD adduser $(id -u -n) render -fi -msg_ok "Set Up Hardware Acceleration" +setup_hwaccel msg_info "Installing Jellyfin" if [[ ! -d /etc/apt/keyrings ]]; then From dce2be757198f0cf6be9abdbe0b80e0b8e1a8167 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Tue, 21 Oct 2025 11:45:46 +0200 Subject: [PATCH 1483/1733] fix: lspci --- misc/tools.func | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/misc/tools.func b/misc/tools.func index 84b238556..b5c172378 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -1818,6 +1818,14 @@ function setup_gs() { function setup_hwaccel () { msg_info "Setting Up Hardware Acceleration" + if ! command -v lspci &>/dev/null; then + msg_info "Installing pciutils (provides lspci)" + $STD apt -y update && $STD apt -y install pciutils || { + msg_error "Failed to install pciutils — cannot detect GPU" + return 1 + } + fi + # Detect GPU vendor (Intel, AMD, NVIDIA) local gpu_vendor gpu_vendor=$(lspci | grep -Ei 'vga|3d|display' | grep -Eo 'Intel|AMD|NVIDIA' | head -n1) From 74616c7b73b571c24ba5c6710da1132e607dddfd Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Tue, 21 Oct 2025 11:49:11 +0200 Subject: [PATCH 1484/1733] chore --- misc/tools.func | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index b5c172378..c7699dd44 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -1819,11 +1819,8 @@ function setup_hwaccel () { msg_info "Setting Up Hardware Acceleration" if ! command -v lspci &>/dev/null; then - msg_info "Installing pciutils (provides lspci)" - $STD apt -y update && $STD apt -y install pciutils || { - msg_error "Failed to install pciutils — cannot detect GPU" - return 1 - } + $STD apt -y update + $STD apt -y install pciutils fi # Detect GPU vendor (Intel, AMD, NVIDIA) From e103c822d41da606d68ef619e3dccf4bba818737 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Tue, 21 Oct 2025 13:31:41 +0200 Subject: [PATCH 1485/1733] ubuntu --- ct/jellyfin.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/jellyfin.sh b/ct/jellyfin.sh index 6b94ad6a2..a14fd8c2a 100644 --- a/ct/jellyfin.sh +++ b/ct/jellyfin.sh @@ -10,8 +10,8 @@ var_tags="${var_tags:-media}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" +var_os="${var_os:-ubuntu}" +var_version="${var_version:-24.04}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" From 84cd527ce829dac670e0013fa9d16725b8cb1c2a Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Tue, 21 Oct 2025 13:56:24 +0200 Subject: [PATCH 1486/1733] Update Jellyfin version to 24.10 --- ct/jellyfin.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/jellyfin.sh b/ct/jellyfin.sh index a14fd8c2a..69ab3b873 100644 --- a/ct/jellyfin.sh +++ b/ct/jellyfin.sh @@ -11,7 +11,7 @@ var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-ubuntu}" -var_version="${var_version:-24.04}" +var_version="${var_version:-24.10}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" From a4b22af8433cdde735e3a9642f1cef3f653b4a99 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Tue, 21 Oct 2025 14:01:58 +0200 Subject: [PATCH 1487/1733] Update Jellyfin version from 24.10 to 24.04 --- ct/jellyfin.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/jellyfin.sh b/ct/jellyfin.sh index 69ab3b873..a14fd8c2a 100644 --- a/ct/jellyfin.sh +++ b/ct/jellyfin.sh @@ -11,7 +11,7 @@ var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-ubuntu}" -var_version="${var_version:-24.10}" +var_version="${var_version:-24.04}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" From 6b58ff689edc76623e5c8c57f9bb3c7f0d775e0c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 21 Oct 2025 14:03:53 +0200 Subject: [PATCH 1488/1733] fix pct options for var_version --- misc/build.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/build.func b/misc/build.func index 24bb790f2..040de56ca 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2119,7 +2119,7 @@ build_container() { export ENABLE_FUSE="$ENABLE_FUSE" export ENABLE_TUN="$ENABLE_TUN" export PCT_OSTYPE="$var_os" - export PCT_OSVERSION="${var_version%%.*}" + export PCT_OSVERSION="$var_version" export PCT_DISK_SIZE="$DISK_SIZE" export PCT_OPTIONS=" -features $FEATURES From da4118d4520b36ee925d9b79511463df8927d9ad Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 21 Oct 2025 14:04:46 +0200 Subject: [PATCH 1489/1733] 24.10 --- ct/jellyfin.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/jellyfin.sh b/ct/jellyfin.sh index a14fd8c2a..4593ab21c 100644 --- a/ct/jellyfin.sh +++ b/ct/jellyfin.sh @@ -11,7 +11,7 @@ var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-8}" var_os="${var_os:-ubuntu}" -var_version="${var_version:-24.04}" +var_version="${var_version:-24.10}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" @@ -34,7 +34,7 @@ function update_script() { fetch_and_deploy_gh_release "intel-libgdgmm12" "intel/compute-runtime" "binary" "latest" "" "libigdgmm12_*_amd64.deb" fetch_and_deploy_gh_release "intel-opencl-icd" "intel/compute-runtime" "binary" "latest" "" "intel-opencl-icd_*_amd64.deb" msg_ok "Updated Intel Dependencies" - + msg_info "Updating ${APP} LXC" $STD apt-get update $STD apt-get -y upgrade From 9b624944c7638fd7c8693c5af61c18baefe86e41 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 21 Oct 2025 14:12:26 +0200 Subject: [PATCH 1490/1733] Improve LXC template selection and cleanup hwaccel code Enhanced the LXC container creation process to prompt for available template versions if the requested template is missing, allowing users to select from available options interactively. Also refactored the hardware acceleration setup function for better readability and consistency, and made minor whitespace and formatting adjustments. --- misc/build.func | 73 ++++++++++++++++++++++++++++++++++++++++++++-- misc/tools.func | 77 ++++++++++++++++++++++++------------------------- 2 files changed, 109 insertions(+), 41 deletions(-) diff --git a/misc/build.func b/misc/build.func index 040de56ca..286351854 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3052,9 +3052,78 @@ create_lxc_container() { TEMPLATE_BASE=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) [[ -n "$TEMPLATE_BASE" ]] && TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE" fi + [[ -n "$TEMPLATE_PATH" ]] || { - msg_error "Unable to resolve template path for $TEMPLATE_STORAGE. Check storage type and permissions." - exit 220 + if [[ -z "$TEMPLATE" ]]; then + msg_error "Template ${PCT_OSTYPE} ${PCT_OSVERSION} not available" + + # Get available versions + mapfile -t AVAILABLE_VERSIONS < <( + pveam available -section system 2>/dev/null | + grep "^${PCT_OSTYPE}-" | + sed -E 's/.*'"${PCT_OSTYPE}"'-([0-9]+\.[0-9]+).*/\1/' | + sort -V -u + ) + + if [[ ${#AVAILABLE_VERSIONS[@]} -gt 0 ]]; then + msg_info "Available versions:" + for i in "${!AVAILABLE_VERSIONS[@]}"; do + echo " [$((i + 1))] ${AVAILABLE_VERSIONS[$i]}" + done + + echo "" + read -p "Select version [1-${#AVAILABLE_VERSIONS[@]}] or Enter to exit: " choice + + if [[ "$choice" =~ ^[0-9]+$ ]] && [[ "$choice" -ge 1 ]] && [[ "$choice" -le ${#AVAILABLE_VERSIONS[@]} ]]; then + export var_version="${AVAILABLE_VERSIONS[$((choice - 1))]}" + export PCT_OSVERSION="$var_version" + msg_ok "Switched to ${PCT_OSTYPE} ${var_version}" + + # Retry template search with new version + TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" + mapfile -t LOCAL_TEMPLATES < <( + pveam list "$TEMPLATE_STORAGE" 2>/dev/null | + awk -v s="$TEMPLATE_SEARCH" -v p="$TEMPLATE_PATTERN" '$1 ~ s && $1 ~ p {print $1}' | + sed 's|.*/||' | sort -t - -k 2 -V + ) + mapfile -t ONLINE_TEMPLATES < <( + pveam available -section system 2>/dev/null | + sed -n "s/.*\($TEMPLATE_SEARCH.*$TEMPLATE_PATTERN.*\)/\1/p" | + sort -t - -k 2 -V + ) + ONLINE_TEMPLATE="" + [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" + + if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then + TEMPLATE="${LOCAL_TEMPLATES[-1]}" + TEMPLATE_SOURCE="local" + else + TEMPLATE="$ONLINE_TEMPLATE" + TEMPLATE_SOURCE="online" + fi + + TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" + if [[ -z "$TEMPLATE_PATH" ]]; then + TEMPLATE_BASE=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) + [[ -n "$TEMPLATE_BASE" ]] && TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE" + fi + + [[ -n "$TEMPLATE_PATH" ]] || { + msg_error "Template still not found after version change" + exit 220 + } + else + msg_info "Installation cancelled" + exit 0 + fi + else + msg_error "No ${PCT_OSTYPE} templates available" + exit 220 + fi + else + msg_error "Unable to resolve template path for $TEMPLATE_STORAGE" + exit 220 + fi } msg_ok "Template ${BL}$TEMPLATE${CL} [$TEMPLATE_SOURCE]" diff --git a/misc/tools.func b/misc/tools.func index c7699dd44..9cd272a6d 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -1803,7 +1803,6 @@ function setup_gs() { msg_ok "Installed Ghostscript $LATEST_VERSION_DOTTED" } - # ------------------------------------------------------------------------------ # Sets up Hardware Acceleration on debian or ubuntu. # @@ -1815,7 +1814,7 @@ function setup_gs() { # Notes: # - Some things are fetched from intel repositories due to not being in debian repositories. # ------------------------------------------------------------------------------ -function setup_hwaccel () { +function setup_hwaccel() { msg_info "Setting Up Hardware Acceleration" if ! command -v lspci &>/dev/null; then @@ -1845,45 +1844,45 @@ function setup_hwaccel () { local in_ct="${CTTYPE:-0}" case "$gpu_vendor" in - Intel) - msg_info "Detected Intel GPU — configuring Intel hardware acceleration" + Intel) + msg_info "Detected Intel GPU — configuring Intel hardware acceleration" - if [[ "$os_id" == "ubuntu" ]]; then - $STD apt -y install intel-opencl-icd || msg_error "Failed to install intel-opencl-icd" - else - fetch_and_deploy_gh_release "intel-igc-core-2" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-core-2_*_amd64.deb" - fetch_and_deploy_gh_release "intel-igc-opencl-2" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-opencl-2_*_amd64.deb" - fetch_and_deploy_gh_release "intel-libgdgmm12" "intel/compute-runtime" "binary" "latest" "" "libigdgmm12_*_amd64.deb" - fetch_and_deploy_gh_release "intel-opencl-icd" "intel/compute-runtime" "binary" "latest" "" "intel-opencl-icd_*_amd64.deb" - fi + if [[ "$os_id" == "ubuntu" ]]; then + $STD apt -y install intel-opencl-icd || msg_error "Failed to install intel-opencl-icd" + else + fetch_and_deploy_gh_release "intel-igc-core-2" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-core-2_*_amd64.deb" + fetch_and_deploy_gh_release "intel-igc-opencl-2" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-opencl-2_*_amd64.deb" + fetch_and_deploy_gh_release "intel-libgdgmm12" "intel/compute-runtime" "binary" "latest" "" "libigdgmm12_*_amd64.deb" + fetch_and_deploy_gh_release "intel-opencl-icd" "intel/compute-runtime" "binary" "latest" "" "intel-opencl-icd_*_amd64.deb" + fi - $STD apt -y install va-driver-all ocl-icd-libopencl1 vainfo intel-gpu-tools || msg_error "Failed to install GPU dependencies" - ;; - AMD) - msg_info "Detected AMD GPU — configuring Mesa-based hardware acceleration" + $STD apt -y install va-driver-all ocl-icd-libopencl1 vainfo intel-gpu-tools || msg_error "Failed to install GPU dependencies" + ;; + AMD) + msg_info "Detected AMD GPU — configuring Mesa-based hardware acceleration" - $STD apt -y install mesa-va-drivers mesa-vdpau-drivers mesa-opencl-icd vainfo clinfo || msg_error "Failed to install AMD GPU dependencies" + $STD apt -y install mesa-va-drivers mesa-vdpau-drivers mesa-opencl-icd vainfo clinfo || msg_error "Failed to install AMD GPU dependencies" - # For AMD CPUs without discrete GPU (APUs) - if [[ "$cpu_vendor" == "AuthenticAMD" && "$gpu_vendor" != "AMD" ]]; then - msg_info "Detected AMD CPU (APU) — enabling VAAPI via Mesa" - $STD apt -y install libdrm-amdgpu1 firmware-amd-graphics || true - fi - ;; - NVIDIA) - msg_info "Detected NVIDIA GPU — skipping automatic configuration (manual driver setup required)" - msg_info "→ Please install proprietary drivers manually via: apt install nvidia-driver" - ;; - *) - # If no discrete GPU, but AMD CPU (e.g., Ryzen APU) - if [[ "$cpu_vendor" == "AuthenticAMD" ]]; then - msg_info "Detected AMD CPU without discrete GPU — installing Mesa OpenCL stack" - $STD apt -y install mesa-opencl-icd ocl-icd-libopencl1 clinfo || msg_error "Failed to install Mesa OpenCL stack" - else - msg_error "No supported GPU vendor detected" - return 1 - fi - ;; + # For AMD CPUs without discrete GPU (APUs) + if [[ "$cpu_vendor" == "AuthenticAMD" && "$gpu_vendor" != "AMD" ]]; then + msg_info "Detected AMD CPU (APU) — enabling VAAPI via Mesa" + $STD apt -y install libdrm-amdgpu1 firmware-amd-graphics || true + fi + ;; + NVIDIA) + msg_info "Detected NVIDIA GPU — skipping automatic configuration (manual driver setup required)" + msg_info "→ Please install proprietary drivers manually via: apt install nvidia-driver" + ;; + *) + # If no discrete GPU, but AMD CPU (e.g., Ryzen APU) + if [[ "$cpu_vendor" == "AuthenticAMD" ]]; then + msg_info "Detected AMD CPU without discrete GPU — installing Mesa OpenCL stack" + $STD apt -y install mesa-opencl-icd ocl-icd-libopencl1 clinfo || msg_error "Failed to install Mesa OpenCL stack" + else + msg_error "No supported GPU vendor detected" + return 1 + fi + ;; esac if [[ "$in_ct" == "0" ]]; then @@ -2414,7 +2413,7 @@ function setup_mysql() { SUITE="bookworm" ;; *) - SUITE="bookworm" # Fallback to bookworm for unknown Debian versions + SUITE="bookworm" # Fallback to bookworm for unknown Debian versions ;; esac else @@ -2424,7 +2423,7 @@ function setup_mysql() { # Stop existing MySQL if running $STD systemctl stop mysql 2>/dev/null || true - + # Only purge if MySQL is actually installed if dpkg -l 2>/dev/null | grep -q "^ii.*mysql-server"; then $STD apt purge -y mysql-server* mysql-client* mysql-common 2>/dev/null || true From 7d8a42fe97bbad3f02c135f025640d461b1c433b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 21 Oct 2025 14:30:06 +0200 Subject: [PATCH 1491/1733] Filter and display available LXC container versions Added a grep to ensure only valid version numbers are listed and improved the display of available versions with formatting. This enhances clarity and prevents non-version strings from appearing in the version selection. --- misc/build.func | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/misc/build.func b/misc/build.func index 286351854..3c2b991ff 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3062,11 +3062,12 @@ create_lxc_container() { pveam available -section system 2>/dev/null | grep "^${PCT_OSTYPE}-" | sed -E 's/.*'"${PCT_OSTYPE}"'-([0-9]+\.[0-9]+).*/\1/' | - sort -V -u + grep -E '^[0-9]+\.[0-9]+$' | + sort -u -V 2>/dev/null || sort -u ) if [[ ${#AVAILABLE_VERSIONS[@]} -gt 0 ]]; then - msg_info "Available versions:" + echo -e "\n${BL}Available versions:${CL}" for i in "${!AVAILABLE_VERSIONS[@]}"; do echo " [$((i + 1))] ${AVAILABLE_VERSIONS[$i]}" done From 814711b2a82613b057d50d5f39dc6c36d6e1238f Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 21 Oct 2025 14:39:30 +0200 Subject: [PATCH 1492/1733] Refactor LXC container creation and template selection Removes redundant success messages for storage selection, improves template filtering with stricter matching, and refactors LXC stack upgrade retry logic for better error handling and code clarity during container creation. --- misc/build.func | 50 ++++++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/misc/build.func b/misc/build.func index 3c2b991ff..b79154100 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1747,7 +1747,6 @@ echo_storage_summary_from_file() { if [[ -n "$tpl_store" ]] && resolve_storage_preselect template "$tpl_store"; then TEMPLATE_STORAGE="$STORAGE_RESULT" TEMPLATE_STORAGE_INFO="$STORAGE_INFO" - msg_ok "Using Template-Storage → $TEMPLATE_STORAGE${TEMPLATE_STORAGE_INFO:+ ($TEMPLATE_STORAGE_INFO)}" else choose_and_set_storage_for_file "$vars_file" template fi @@ -1755,7 +1754,6 @@ echo_storage_summary_from_file() { if [[ -n "$ct_store" ]] && resolve_storage_preselect container "$ct_store"; then CONTAINER_STORAGE="$STORAGE_RESULT" CONTAINER_STORAGE_INFO="$STORAGE_INFO" - msg_ok "Using Container-Storage → $CONTAINER_STORAGE${CONTAINER_STORAGE_INFO:+ ($CONTAINER_STORAGE_INFO)}" else choose_and_set_storage_for_file "$vars_file" container fi @@ -2888,7 +2886,7 @@ create_lxc_container() { msg_info "Retrying container creation after upgrade" if pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then msg_ok "Container created successfully after upgrade." - return 1 + return 0 else msg_error "pct create still failed after upgrade. See $LOGFILE" return 3 @@ -3026,14 +3024,14 @@ create_lxc_container() { mapfile -t LOCAL_TEMPLATES < <( pveam list "$TEMPLATE_STORAGE" 2>/dev/null | - awk -v s="$TEMPLATE_SEARCH" -v p="$TEMPLATE_PATTERN" '$1 ~ s && $1 ~ p {print $1}' | + awk -v s="$TEMPLATE_SEARCH" -v p="$TEMPLATE_PATTERN" '$1 ~ "^"s"[-_]" && $1 ~ p {print $1}' | sed 's|.*/||' | sort -t - -k 2 -V ) pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)." mapfile -t ONLINE_TEMPLATES < <( pveam available -section system 2>/dev/null | - sed -n "s/.*\($TEMPLATE_SEARCH.*$TEMPLATE_PATTERN.*\)/\1/p" | + grep -E "^${TEMPLATE_SEARCH}[-_].*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V ) ONLINE_TEMPLATE="" @@ -3262,17 +3260,16 @@ create_lxc_container() { echo echo "pct reported 'unsupported ... version' – your LXC stack might be too old for this template." echo "We can try to upgrade 'pve-container' and 'lxc-pve' now and retry automatically." - if offer_lxc_stack_upgrade_and_maybe_retry "yes"; then - : # success after retry - else - rc=$? - case $rc in - 2) echo "Upgrade was declined. Please update and re-run: - apt update && apt install --only-upgrade pve-container lxc-pve" ;; - 3) echo "Upgrade and/or retry failed. Please inspect: $LOGFILE" ;; - esac - exit 231 - fi + offer_lxc_stack_upgrade_and_maybe_retry "yes" + rc=$? + case $rc in + 0) : ;; # success - container created, continue + 2) echo "Upgrade was declined. Please update and re-run: + apt update && apt install --only-upgrade pve-container lxc-pve" + exit 231 ;; + 3) echo "Upgrade and/or retry failed. Please inspect: $LOGFILE" + exit 231 ;; + esac else msg_error "Container creation failed even with local fallback. See $LOGFILE" if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then @@ -3290,16 +3287,17 @@ create_lxc_container() { echo echo "pct reported 'unsupported ... version' – your LXC stack might be too old for this template." echo "We can try to upgrade 'pve-container' and 'lxc-pve' now and retry automatically." - if offer_lxc_stack_upgrade_and_maybe_retry "yes"; then - : # success after retry - else - rc=$? - case $rc in - 2) echo "Upgrade was declined. Please update and re-run: - apt update && apt install --only-upgrade pve-container lxc-pve" ;; - 3) echo "Upgrade and/or retry failed. Please inspect: $LOGFILE" ;; - esac - exit 231 + offer_lxc_stack_upgrade_and_maybe_retry "yes" + rc=$? + case $rc in + 0) : ;; # success - container created, continue + 2) echo "Upgrade was declined. Please update and re-run: + apt update && apt install --only-upgrade pve-container lxc-pve" + exit 231 ;; + 3) echo "Upgrade and/or retry failed. Please inspect: $LOGFILE" + exit 231 ;; + esac + else fi else if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then From 7e4a0018f83befc3c80bdfb4ec75386cf93ff954 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 21 Oct 2025 14:41:31 +0200 Subject: [PATCH 1493/1733] Update build.func --- misc/build.func | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/misc/build.func b/misc/build.func index b79154100..f62205a29 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1460,15 +1460,12 @@ ensure_storage_selection_for_vars_file() { tpl=$(grep -E '^var_template_storage=' "$vf" | cut -d= -f2-) ct=$(grep -E '^var_container_storage=' "$vf" | cut -d= -f2-) - # Wenn beide Werte schon existieren → übernehmen if [[ -n "$tpl" && -n "$ct" ]]; then TEMPLATE_STORAGE="$tpl" CONTAINER_STORAGE="$ct" - echo_storage_summary_from_file "$vf" return 0 fi - # --- Erstmalige Auswahl: beide Abfragen (nutze existierende Helper) --- choose_and_set_storage_for_file "$vf" template choose_and_set_storage_for_file "$vf" container @@ -1738,26 +1735,6 @@ choose_and_set_storage_for_file() { msg_ok "Updated ${key} → ${STORAGE_RESULT}" } -echo_storage_summary_from_file() { - local vars_file="$1" - local tpl_store ct_store - tpl_store=$(awk -F= '/^var_template_storage=/ {print $2; exit}' "$vars_file") - ct_store=$(awk -F= '/^var_container_storage=/ {print $2; exit}' "$vars_file") - - if [[ -n "$tpl_store" ]] && resolve_storage_preselect template "$tpl_store"; then - TEMPLATE_STORAGE="$STORAGE_RESULT" - TEMPLATE_STORAGE_INFO="$STORAGE_INFO" - else - choose_and_set_storage_for_file "$vars_file" template - fi - - if [[ -n "$ct_store" ]] && resolve_storage_preselect container "$ct_store"; then - CONTAINER_STORAGE="$STORAGE_RESULT" - CONTAINER_STORAGE_INFO="$STORAGE_INFO" - else - choose_and_set_storage_for_file "$vars_file" container - fi -} # ------------------------------------------------------------------------------ # check_container_resources() From d6fee2e0eaa3b42f55c52c0850bcc0b0bd248b35 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 21 Oct 2025 14:42:47 +0200 Subject: [PATCH 1494/1733] remove fi --- misc/build.func | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/misc/build.func b/misc/build.func index f62205a29..3e177e7b6 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1735,7 +1735,6 @@ choose_and_set_storage_for_file() { msg_ok "Updated ${key} → ${STORAGE_RESULT}" } - # ------------------------------------------------------------------------------ # check_container_resources() # @@ -3240,12 +3239,16 @@ create_lxc_container() { offer_lxc_stack_upgrade_and_maybe_retry "yes" rc=$? case $rc in - 0) : ;; # success - container created, continue - 2) echo "Upgrade was declined. Please update and re-run: + 0) : ;; # success - container created, continue + 2) + echo "Upgrade was declined. Please update and re-run: apt update && apt install --only-upgrade pve-container lxc-pve" - exit 231 ;; - 3) echo "Upgrade and/or retry failed. Please inspect: $LOGFILE" - exit 231 ;; + exit 231 + ;; + 3) + echo "Upgrade and/or retry failed. Please inspect: $LOGFILE" + exit 231 + ;; esac else msg_error "Container creation failed even with local fallback. See $LOGFILE" @@ -3267,16 +3270,19 @@ create_lxc_container() { offer_lxc_stack_upgrade_and_maybe_retry "yes" rc=$? case $rc in - 0) : ;; # success - container created, continue - 2) echo "Upgrade was declined. Please update and re-run: + 0) : ;; # success - container created, continue + 2) + echo "Upgrade was declined. Please update and re-run: apt update && apt install --only-upgrade pve-container lxc-pve" - exit 231 ;; - 3) echo "Upgrade and/or retry failed. Please inspect: $LOGFILE" - exit 231 ;; + exit 231 + ;; + 3) + echo "Upgrade and/or retry failed. Please inspect: $LOGFILE" + exit 231 + ;; esac else - fi - else + msg_error "Container creation failed. See $LOGFILE" if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then set -x bash -x -c "pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" 2>&1 | tee -a "$LOGFILE" From 934e9d31cc7eabcc02a77b9ce649e3eb2b401da6 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 21 Oct 2025 14:50:24 +0200 Subject: [PATCH 1495/1733] Improve LXC template path resolution logic Adds a fallback to construct the template path if it is still unset but a valid template name exists. Refines template search patterns for both local and online templates, and removes redundant error handling for unresolved template paths. --- misc/build.func | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/misc/build.func b/misc/build.func index 3e177e7b6..6e2ae2015 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3027,6 +3027,11 @@ create_lxc_container() { [[ -n "$TEMPLATE_BASE" ]] && TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE" fi + # If we still don't have a path but have a valid template name, construct it + if [[ -z "$TEMPLATE_PATH" && -n "$TEMPLATE" ]]; then + TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" + fi + [[ -n "$TEMPLATE_PATH" ]] || { if [[ -z "$TEMPLATE" ]]; then msg_error "Template ${PCT_OSTYPE} ${PCT_OSVERSION} not available" @@ -3058,12 +3063,12 @@ create_lxc_container() { TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" mapfile -t LOCAL_TEMPLATES < <( pveam list "$TEMPLATE_STORAGE" 2>/dev/null | - awk -v s="$TEMPLATE_SEARCH" -v p="$TEMPLATE_PATTERN" '$1 ~ s && $1 ~ p {print $1}' | + awk -v s="$TEMPLATE_SEARCH" -v p="$TEMPLATE_PATTERN" '$1 ~ "^"s"[-_]" && $1 ~ p {print $1}' | sed 's|.*/||' | sort -t - -k 2 -V ) mapfile -t ONLINE_TEMPLATES < <( pveam available -section system 2>/dev/null | - sed -n "s/.*\($TEMPLATE_SEARCH.*$TEMPLATE_PATTERN.*\)/\1/p" | + grep -E "^${TEMPLATE_SEARCH}[-_].*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V ) ONLINE_TEMPLATE="" @@ -3083,6 +3088,11 @@ create_lxc_container() { [[ -n "$TEMPLATE_BASE" ]] && TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE" fi + # If we still don't have a path but have a valid template name, construct it + if [[ -z "$TEMPLATE_PATH" && -n "$TEMPLATE" ]]; then + TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" + fi + [[ -n "$TEMPLATE_PATH" ]] || { msg_error "Template still not found after version change" exit 220 @@ -3095,9 +3105,6 @@ create_lxc_container() { msg_error "No ${PCT_OSTYPE} templates available" exit 220 fi - else - msg_error "Unable to resolve template path for $TEMPLATE_STORAGE" - exit 220 fi } From de7b985b8805b88c4fa4f8c1cf71710b6d22d9a9 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 21 Oct 2025 14:55:37 +0200 Subject: [PATCH 1496/1733] Refine LXC template matching to exclude digits Updated awk and grep patterns in create_lxc_container to match non-digit characters after TEMPLATE_SEARCH, instead of only dashes or underscores. This improves accuracy when filtering available and local LXC templates. --- misc/build.func | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/misc/build.func b/misc/build.func index 6e2ae2015..ea92c0eee 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3000,14 +3000,14 @@ create_lxc_container() { mapfile -t LOCAL_TEMPLATES < <( pveam list "$TEMPLATE_STORAGE" 2>/dev/null | - awk -v s="$TEMPLATE_SEARCH" -v p="$TEMPLATE_PATTERN" '$1 ~ "^"s"[-_]" && $1 ~ p {print $1}' | + awk -v s="$TEMPLATE_SEARCH" -v p="$TEMPLATE_PATTERN" '$1 ~ "^"s"[^0-9]" && $1 ~ p {print $1}' | sed 's|.*/||' | sort -t - -k 2 -V ) pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)." mapfile -t ONLINE_TEMPLATES < <( pveam available -section system 2>/dev/null | - grep -E "^${TEMPLATE_SEARCH}[-_].*${TEMPLATE_PATTERN}" | + grep -E "^${TEMPLATE_SEARCH}[^0-9].*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V ) ONLINE_TEMPLATE="" @@ -3063,12 +3063,12 @@ create_lxc_container() { TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" mapfile -t LOCAL_TEMPLATES < <( pveam list "$TEMPLATE_STORAGE" 2>/dev/null | - awk -v s="$TEMPLATE_SEARCH" -v p="$TEMPLATE_PATTERN" '$1 ~ "^"s"[-_]" && $1 ~ p {print $1}' | + awk -v s="$TEMPLATE_SEARCH" -v p="$TEMPLATE_PATTERN" '$1 ~ "^"s"[^0-9]" && $1 ~ p {print $1}' | sed 's|.*/||' | sort -t - -k 2 -V ) mapfile -t ONLINE_TEMPLATES < <( pveam available -section system 2>/dev/null | - grep -E "^${TEMPLATE_SEARCH}[-_].*${TEMPLATE_PATTERN}" | + grep -E "^${TEMPLATE_SEARCH}[^0-9].*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V ) ONLINE_TEMPLATE="" From 6866830633d6ac7feaa724b2cdd2c9bd9ed78440 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 21 Oct 2025 15:03:07 +0200 Subject: [PATCH 1497/1733] fixes --- misc/build.func | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/misc/build.func b/misc/build.func index ea92c0eee..977bc2a08 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3000,14 +3000,14 @@ create_lxc_container() { mapfile -t LOCAL_TEMPLATES < <( pveam list "$TEMPLATE_STORAGE" 2>/dev/null | - awk -v s="$TEMPLATE_SEARCH" -v p="$TEMPLATE_PATTERN" '$1 ~ "^"s"[^0-9]" && $1 ~ p {print $1}' | + awk -v s="$TEMPLATE_SEARCH" -v p="$TEMPLATE_PATTERN" '$1 ~ "^"s"-" && $1 ~ p {print $1}' | sed 's|.*/||' | sort -t - -k 2 -V ) pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)." mapfile -t ONLINE_TEMPLATES < <( pveam available -section system 2>/dev/null | - grep -E "^${TEMPLATE_SEARCH}[^0-9].*${TEMPLATE_PATTERN}" | + grep -E "^${TEMPLATE_SEARCH}-.*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V ) ONLINE_TEMPLATE="" @@ -3063,12 +3063,12 @@ create_lxc_container() { TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" mapfile -t LOCAL_TEMPLATES < <( pveam list "$TEMPLATE_STORAGE" 2>/dev/null | - awk -v s="$TEMPLATE_SEARCH" -v p="$TEMPLATE_PATTERN" '$1 ~ "^"s"[^0-9]" && $1 ~ p {print $1}' | + awk -v s="$TEMPLATE_SEARCH" -v p="$TEMPLATE_PATTERN" '$1 ~ "^"s"-" && $1 ~ p {print $1}' | sed 's|.*/||' | sort -t - -k 2 -V ) mapfile -t ONLINE_TEMPLATES < <( pveam available -section system 2>/dev/null | - grep -E "^${TEMPLATE_SEARCH}[^0-9].*${TEMPLATE_PATTERN}" | + grep -E "^${TEMPLATE_SEARCH}-.*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V ) ONLINE_TEMPLATE="" From 78835a3903c134232a70c82777c9f6fc6c2c3d4c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 21 Oct 2025 15:06:39 +0200 Subject: [PATCH 1498/1733] Update build.func --- misc/build.func | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/misc/build.func b/misc/build.func index 977bc2a08..40e1d9291 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3007,12 +3007,15 @@ create_lxc_container() { pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)." mapfile -t ONLINE_TEMPLATES < <( pveam available -section system 2>/dev/null | - grep -E "^${TEMPLATE_SEARCH}-.*${TEMPLATE_PATTERN}" | + grep -E "^${TEMPLATE_SEARCH}.*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V ) ONLINE_TEMPLATE="" [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" + msg_debug "Found ${#LOCAL_TEMPLATES[@]} local templates, ${#ONLINE_TEMPLATES[@]} online templates" + msg_debug "ONLINE_TEMPLATE='$ONLINE_TEMPLATE'" + if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then TEMPLATE="${LOCAL_TEMPLATES[-1]}" TEMPLATE_SOURCE="local" @@ -3021,6 +3024,8 @@ create_lxc_container() { TEMPLATE_SOURCE="online" fi + echo -e "Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'" + TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" if [[ -z "$TEMPLATE_PATH" ]]; then TEMPLATE_BASE=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) @@ -3068,7 +3073,7 @@ create_lxc_container() { ) mapfile -t ONLINE_TEMPLATES < <( pveam available -section system 2>/dev/null | - grep -E "^${TEMPLATE_SEARCH}-.*${TEMPLATE_PATTERN}" | + grep -E "^${TEMPLATE_SEARCH}.*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V ) ONLINE_TEMPLATE="" From e02643b6f8d40ede9cd8da6770be06da1da08d23 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 21 Oct 2025 15:12:11 +0200 Subject: [PATCH 1499/1733] fixes --- misc/build.func | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/misc/build.func b/misc/build.func index 40e1d9291..330dd3aee 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3000,7 +3000,7 @@ create_lxc_container() { mapfile -t LOCAL_TEMPLATES < <( pveam list "$TEMPLATE_STORAGE" 2>/dev/null | - awk -v s="$TEMPLATE_SEARCH" -v p="$TEMPLATE_PATTERN" '$1 ~ "^"s"-" && $1 ~ p {print $1}' | + awk -v search="^${TEMPLATE_SEARCH}-" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' | sed 's|.*/||' | sort -t - -k 2 -V ) @@ -3068,7 +3068,7 @@ create_lxc_container() { TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" mapfile -t LOCAL_TEMPLATES < <( pveam list "$TEMPLATE_STORAGE" 2>/dev/null | - awk -v s="$TEMPLATE_SEARCH" -v p="$TEMPLATE_PATTERN" '$1 ~ "^"s"-" && $1 ~ p {print $1}' | + awk -v search="^${TEMPLATE_SEARCH}-" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' | sed 's|.*/||' | sort -t - -k 2 -V ) mapfile -t ONLINE_TEMPLATES < <( From 93f343fec5759b3c3e0f469bf64264fc717b0b94 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 21 Oct 2025 15:17:30 +0200 Subject: [PATCH 1500/1733] fixes --- misc/build.func | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/misc/build.func b/misc/build.func index 330dd3aee..7ee23b88f 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2998,21 +2998,26 @@ create_lxc_container() { msg_info "Searching for template '$TEMPLATE_SEARCH'" + # Build regex patterns outside awk/grep for clarity + SEARCH_PATTERN="^${TEMPLATE_SEARCH}-" + mapfile -t LOCAL_TEMPLATES < <( pveam list "$TEMPLATE_STORAGE" 2>/dev/null | - awk -v search="^${TEMPLATE_SEARCH}-" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' | + awk -v search="${SEARCH_PATTERN}" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' | sed 's|.*/||' | sort -t - -k 2 -V ) pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)." mapfile -t ONLINE_TEMPLATES < <( pveam available -section system 2>/dev/null | - grep -E "^${TEMPLATE_SEARCH}.*${TEMPLATE_PATTERN}" | + grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | + awk '{print $1}' | sort -t - -k 2 -V ) ONLINE_TEMPLATE="" [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" + msg_debug "SEARCH_PATTERN='${SEARCH_PATTERN}' TEMPLATE_PATTERN='${TEMPLATE_PATTERN}'" msg_debug "Found ${#LOCAL_TEMPLATES[@]} local templates, ${#ONLINE_TEMPLATES[@]} online templates" msg_debug "ONLINE_TEMPLATE='$ONLINE_TEMPLATE'" @@ -3024,7 +3029,7 @@ create_lxc_container() { TEMPLATE_SOURCE="online" fi - echo -e "Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'" + msg_debug "Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'" TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" if [[ -z "$TEMPLATE_PATH" ]]; then @@ -3066,14 +3071,17 @@ create_lxc_container() { # Retry template search with new version TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" + SEARCH_PATTERN="^${TEMPLATE_SEARCH}-" + mapfile -t LOCAL_TEMPLATES < <( pveam list "$TEMPLATE_STORAGE" 2>/dev/null | - awk -v search="^${TEMPLATE_SEARCH}-" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' | + awk -v search="${SEARCH_PATTERN}" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' | sed 's|.*/||' | sort -t - -k 2 -V ) mapfile -t ONLINE_TEMPLATES < <( pveam available -section system 2>/dev/null | - grep -E "^${TEMPLATE_SEARCH}.*${TEMPLATE_PATTERN}" | + grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | + awk '{print $1}' | sort -t - -k 2 -V ) ONLINE_TEMPLATE="" From ff1db9c90e273712975364540777d4864359a610 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 21 Oct 2025 15:21:06 +0200 Subject: [PATCH 1501/1733] Update build.func --- misc/build.func | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/misc/build.func b/misc/build.func index 7ee23b88f..613adeaf0 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3010,8 +3010,8 @@ create_lxc_container() { pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)." mapfile -t ONLINE_TEMPLATES < <( pveam available -section system 2>/dev/null | + awk '/\.tar\.(zst|xz|gz)/ {print $1}' | grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | - awk '{print $1}' | sort -t - -k 2 -V ) ONLINE_TEMPLATE="" @@ -3080,8 +3080,8 @@ create_lxc_container() { ) mapfile -t ONLINE_TEMPLATES < <( pveam available -section system 2>/dev/null | + awk '/\.tar\.(zst|xz|gz)/ {print $1}' | grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | - awk '{print $1}' | sort -t - -k 2 -V ) ONLINE_TEMPLATE="" From 4e856c86a29482ee136c4b0c3de706a229fcdda8 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 21 Oct 2025 15:24:33 +0200 Subject: [PATCH 1502/1733] Update build.func --- misc/build.func | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/misc/build.func b/misc/build.func index 613adeaf0..b2e4ce411 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3010,7 +3010,8 @@ create_lxc_container() { pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)." mapfile -t ONLINE_TEMPLATES < <( pveam available -section system 2>/dev/null | - awk '/\.tar\.(zst|xz|gz)/ {print $1}' | + grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | + awk '{print $1}' | grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V ) @@ -3019,6 +3020,12 @@ create_lxc_container() { msg_debug "SEARCH_PATTERN='${SEARCH_PATTERN}' TEMPLATE_PATTERN='${TEMPLATE_PATTERN}'" msg_debug "Found ${#LOCAL_TEMPLATES[@]} local templates, ${#ONLINE_TEMPLATES[@]} online templates" + if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then + msg_debug "First 3 online templates:" + for i in {0..2}; do + [[ -n "${ONLINE_TEMPLATES[$i]}" ]] && msg_debug " [$i]: ${ONLINE_TEMPLATES[$i]}" + done + fi msg_debug "ONLINE_TEMPLATE='$ONLINE_TEMPLATE'" if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then @@ -3080,7 +3087,8 @@ create_lxc_container() { ) mapfile -t ONLINE_TEMPLATES < <( pveam available -section system 2>/dev/null | - awk '/\.tar\.(zst|xz|gz)/ {print $1}' | + grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | + awk '{print $1}' | grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V ) @@ -3121,6 +3129,15 @@ create_lxc_container() { fi } + # Validate that we found a template + if [[ -z "$TEMPLATE" ]]; then + msg_error "No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}" + msg_info "Please check:" + msg_info " - Is pveam catalog available? (run: pveam available -section system)" + msg_info " - Does the template exist for your OS version?" + exit 225 + fi + msg_ok "Template ${BL}$TEMPLATE${CL} [$TEMPLATE_SOURCE]" msg_debug "Resolved TEMPLATE_PATH=$TEMPLATE_PATH" From 928c5b465d553089b08ed8178661d05f452a21b2 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 21 Oct 2025 15:28:06 +0200 Subject: [PATCH 1503/1733] debug output --- misc/build.func | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/misc/build.func b/misc/build.func index b2e4ce411..a09f3bab5 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3001,6 +3001,10 @@ create_lxc_container() { # Build regex patterns outside awk/grep for clarity SEARCH_PATTERN="^${TEMPLATE_SEARCH}-" + echo "[DEBUG] TEMPLATE_SEARCH='$TEMPLATE_SEARCH'" + echo "[DEBUG] SEARCH_PATTERN='$SEARCH_PATTERN'" + echo "[DEBUG] TEMPLATE_PATTERN='$TEMPLATE_PATTERN'" + mapfile -t LOCAL_TEMPLATES < <( pveam list "$TEMPLATE_STORAGE" 2>/dev/null | awk -v search="${SEARCH_PATTERN}" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' | @@ -3008,6 +3012,10 @@ create_lxc_container() { ) pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)." + + echo "[DEBUG] pveam available output (first 5 lines with .tar files):" + pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | head -5 | sed 's/^/ /' + mapfile -t ONLINE_TEMPLATES < <( pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | @@ -3015,6 +3023,15 @@ create_lxc_container() { grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V ) + + echo "[DEBUG] After filtering: ${#ONLINE_TEMPLATES[@]} online templates found" + if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then + echo "[DEBUG] Online templates:" + for tmpl in "${ONLINE_TEMPLATES[@]}"; do + echo " - $tmpl" + done + fi + ONLINE_TEMPLATE="" [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" @@ -3036,9 +3053,8 @@ create_lxc_container() { TEMPLATE_SOURCE="online" fi - msg_debug "Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'" - - TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" + echo "[DEBUG] Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'" + msg_debug "Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'" TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" if [[ -z "$TEMPLATE_PATH" ]]; then TEMPLATE_BASE=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) [[ -n "$TEMPLATE_BASE" ]] && TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE" From 5e6fa752c71bbb78650858d23f264a308afdb5ed Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 21 Oct 2025 15:30:24 +0200 Subject: [PATCH 1504/1733] Update build.func --- misc/build.func | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/misc/build.func b/misc/build.func index a09f3bab5..edbca67e1 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3015,16 +3015,14 @@ create_lxc_container() { echo "[DEBUG] pveam available output (first 5 lines with .tar files):" pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | head -5 | sed 's/^/ /' - + mapfile -t ONLINE_TEMPLATES < <( pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | - awk '{print $1}' | + awk -F'\t' '{print $1}' | grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | - sort -t - -k 2 -V - ) - - echo "[DEBUG] After filtering: ${#ONLINE_TEMPLATES[@]} online templates found" + sort -t - -k 2 -V 2>/dev/null || true + ) echo "[DEBUG] After filtering: ${#ONLINE_TEMPLATES[@]} online templates found" if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then echo "[DEBUG] Online templates:" for tmpl in "${ONLINE_TEMPLATES[@]}"; do @@ -3104,9 +3102,9 @@ create_lxc_container() { mapfile -t ONLINE_TEMPLATES < <( pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | - awk '{print $1}' | + awk -F'\t' '{print $1}' | grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | - sort -t - -k 2 -V + sort -t - -k 2 -V 2>/dev/null || true ) ONLINE_TEMPLATE="" [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" From bd58b2cbfdc0d007b2c17fde895538552a13e642 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 21 Oct 2025 15:35:19 +0200 Subject: [PATCH 1505/1733] Improve LXC template selection and add alternative search Refactors the logic for fetching available LXC templates and adds a fallback mechanism to prompt the user to select an alternative OS version if the desired template is not found. Enhances debugging output and improves robustness in template selection. --- misc/build.func | 73 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 64 insertions(+), 9 deletions(-) diff --git a/misc/build.func b/misc/build.func index edbca67e1..3817861eb 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3015,14 +3015,10 @@ create_lxc_container() { echo "[DEBUG] pveam available output (first 5 lines with .tar files):" pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | head -5 | sed 's/^/ /' - - mapfile -t ONLINE_TEMPLATES < <( - pveam available -section system 2>/dev/null | - grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | - awk -F'\t' '{print $1}' | - grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | - sort -t - -k 2 -V 2>/dev/null || true - ) echo "[DEBUG] After filtering: ${#ONLINE_TEMPLATES[@]} online templates found" + + mapfile -t ONLINE_TEMPLATES \ + \ + echo "[DEBUG] After filtering: ${#ONLINE_TEMPLATES[@]} online templates found" < <(pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | awk -F'\t' '{print $1}' | grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V 2>/dev/null || true) if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then echo "[DEBUG] Online templates:" for tmpl in "${ONLINE_TEMPLATES[@]}"; do @@ -3051,8 +3047,67 @@ create_lxc_container() { TEMPLATE_SOURCE="online" fi + # If still no template, try to find alternatives + if [[ -z "$TEMPLATE" ]]; then + echo "" + echo "[DEBUG] No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}, searching for alternatives..." + + # Get all available versions for this OS type + mapfile -t AVAILABLE_VERSIONS < <( + pveam available -section system 2>/dev/null | + grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | + awk -F'\t' '{print $1}' | + grep "^${PCT_OSTYPE}-" | + sed -E "s/.*${PCT_OSTYPE}-([0-9]+(\.[0-9]+)?).*/\1/" | + sort -u -V 2>/dev/null + ) + + if [[ ${#AVAILABLE_VERSIONS[@]} -gt 0 ]]; then + echo "" + echo "${BL}Available ${PCT_OSTYPE} versions:${CL}" + for i in "${!AVAILABLE_VERSIONS[@]}"; do + echo " [$((i + 1))] ${AVAILABLE_VERSIONS[$i]}" + done + echo "" + read -p "Select version [1-${#AVAILABLE_VERSIONS[@]}] or press Enter to cancel: " choice + + if [[ "$choice" =~ ^[0-9]+$ ]] && [[ "$choice" -ge 1 ]] && [[ "$choice" -le ${#AVAILABLE_VERSIONS[@]} ]]; then + PCT_OSVERSION="${AVAILABLE_VERSIONS[$((choice - 1))]}" + TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION}" + SEARCH_PATTERN="^${TEMPLATE_SEARCH}-" + + echo "[DEBUG] Retrying with version: $PCT_OSVERSION" + + mapfile -t ONLINE_TEMPLATES < <( + pveam available -section system 2>/dev/null | + grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | + awk -F'\t' '{print $1}' | + grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | + sort -t - -k 2 -V 2>/dev/null || true + ) + + if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then + TEMPLATE="${ONLINE_TEMPLATES[-1]}" + TEMPLATE_SOURCE="online" + echo "[DEBUG] Found alternative: $TEMPLATE" + else + msg_error "No templates available for ${PCT_OSTYPE} ${PCT_OSVERSION}" + exit 225 + fi + else + msg_info "Installation cancelled" + exit 0 + fi + else + msg_error "No ${PCT_OSTYPE} templates available at all" + exit 225 + fi + fi + echo "[DEBUG] Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'" - msg_debug "Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'" TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" + msg_debug "Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'" + + TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" if [[ -z "$TEMPLATE_PATH" ]]; then TEMPLATE_BASE=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) [[ -n "$TEMPLATE_BASE" ]] && TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE" From ff3bd05452ef8bd9c5c00b64df72ade6aacf28b7 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 21 Oct 2025 15:40:09 +0200 Subject: [PATCH 1506/1733] Update build.func --- misc/build.func | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/misc/build.func b/misc/build.func index 3817861eb..410599758 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3016,9 +3016,15 @@ create_lxc_container() { echo "[DEBUG] pveam available output (first 5 lines with .tar files):" pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | head -5 | sed 's/^/ /' - mapfile -t ONLINE_TEMPLATES \ - \ - echo "[DEBUG] After filtering: ${#ONLINE_TEMPLATES[@]} online templates found" < <(pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | awk -F'\t' '{print $1}' | grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V 2>/dev/null || true) + mapfile -t ONLINE_TEMPLATES < <( + pveam available -section system 2>/dev/null | + grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | + awk '{print $NF}' | + grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | + sort -V 2>/dev/null + ) + + echo "[DEBUG] After filtering: ${#ONLINE_TEMPLATES[@]} online templates found" if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then echo "[DEBUG] Online templates:" for tmpl in "${ONLINE_TEMPLATES[@]}"; do @@ -3081,9 +3087,9 @@ create_lxc_container() { mapfile -t ONLINE_TEMPLATES < <( pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | - awk -F'\t' '{print $1}' | + awk '{print $NF}' | grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | - sort -t - -k 2 -V 2>/dev/null || true + sort -V 2>/dev/null ) if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then @@ -3157,9 +3163,9 @@ create_lxc_container() { mapfile -t ONLINE_TEMPLATES < <( pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | - awk -F'\t' '{print $1}' | + awk '{print $NF}' | grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | - sort -t - -k 2 -V 2>/dev/null || true + sort -V 2>/dev/null ) ONLINE_TEMPLATE="" [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" From 0c2382c2d7add2516bb488ece0789847f5e45af7 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 21 Oct 2025 15:48:44 +0200 Subject: [PATCH 1507/1733] Update build.func --- misc/build.func | 145 ++++++++++++++++++++---------------------------- 1 file changed, 60 insertions(+), 85 deletions(-) diff --git a/misc/build.func b/misc/build.func index 410599758..5c1402891 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2988,86 +2988,74 @@ create_lxc_container() { # ------------------------------------------------------------------------------ # Template discovery & validation + # Simple approach: var_os (alpine/ubuntu/debian) + var_version (24.04/11/12/13/22.04) + # 1. Check local storage + # 2. Check for newer version in pveam + # 3. If not found at all, offer alternatives # ------------------------------------------------------------------------------ - TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" + msg_info "Finding template for ${PCT_OSTYPE} ${PCT_OSVERSION}" + + # Determine template pattern based on OS case "$PCT_OSTYPE" in debian | ubuntu) TEMPLATE_PATTERN="-standard_" ;; alpine | fedora | rocky | centos) TEMPLATE_PATTERN="-default_" ;; - *) TEMPLATE_PATTERN="" ;; + *) TEMPLATE_PATTERN="-" ;; esac - msg_info "Searching for template '$TEMPLATE_SEARCH'" - - # Build regex patterns outside awk/grep for clarity - SEARCH_PATTERN="^${TEMPLATE_SEARCH}-" - - echo "[DEBUG] TEMPLATE_SEARCH='$TEMPLATE_SEARCH'" - echo "[DEBUG] SEARCH_PATTERN='$SEARCH_PATTERN'" - echo "[DEBUG] TEMPLATE_PATTERN='$TEMPLATE_PATTERN'" - - mapfile -t LOCAL_TEMPLATES < <( - pveam list "$TEMPLATE_STORAGE" 2>/dev/null | - awk -v search="${SEARCH_PATTERN}" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' | - sed 's|.*/||' | sort -t - -k 2 -V - ) - - pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)." - - echo "[DEBUG] pveam available output (first 5 lines with .tar files):" - pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | head -5 | sed 's/^/ /' - - mapfile -t ONLINE_TEMPLATES < <( - pveam available -section system 2>/dev/null | - grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | - awk '{print $NF}' | - grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | - sort -V 2>/dev/null - ) + # Step 1: Search in local storage + LOCAL_TEMPLATE=$(pveam list "$TEMPLATE_STORAGE" 2>/dev/null | awk '{print $NF}' | grep "^${PCT_OSTYPE}-${PCT_OSVERSION}.*${TEMPLATE_PATTERN}" | sort -V | tail -1) - echo "[DEBUG] After filtering: ${#ONLINE_TEMPLATES[@]} online templates found" - if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then - echo "[DEBUG] Online templates:" - for tmpl in "${ONLINE_TEMPLATES[@]}"; do - echo " - $tmpl" - done - fi - - ONLINE_TEMPLATE="" - [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" - - msg_debug "SEARCH_PATTERN='${SEARCH_PATTERN}' TEMPLATE_PATTERN='${TEMPLATE_PATTERN}'" - msg_debug "Found ${#LOCAL_TEMPLATES[@]} local templates, ${#ONLINE_TEMPLATES[@]} online templates" - if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then - msg_debug "First 3 online templates:" - for i in {0..2}; do - [[ -n "${ONLINE_TEMPLATES[$i]}" ]] && msg_debug " [$i]: ${ONLINE_TEMPLATES[$i]}" - done - fi - msg_debug "ONLINE_TEMPLATE='$ONLINE_TEMPLATE'" - - if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then - TEMPLATE="${LOCAL_TEMPLATES[-1]}" + # Step 2: Check pveam for available version (could be newer) + ONLINE_TEMPLATE=$(pveam available -section system 2>/dev/null | awk '{print $NF}' | grep "^${PCT_OSTYPE}-${PCT_OSVERSION}.*${TEMPLATE_PATTERN}" | sort -V | tail -1) + + # Decide what to use + if [[ -n "$LOCAL_TEMPLATE" && -n "$ONLINE_TEMPLATE" ]]; then + # Both exist - check if online is newer + if [[ "$LOCAL_TEMPLATE" != "$ONLINE_TEMPLATE" ]]; then + msg_info "Local template: $LOCAL_TEMPLATE" + msg_info "Newer version available: $ONLINE_TEMPLATE" + echo "" + read -p "Download newer version? [y/N]: " answer + case "${answer,,}" in + y|yes) + TEMPLATE="$ONLINE_TEMPLATE" + TEMPLATE_SOURCE="online" + msg_ok "Using newer version: $TEMPLATE" + ;; + *) + TEMPLATE="$LOCAL_TEMPLATE" + TEMPLATE_SOURCE="local" + msg_ok "Using local version: $TEMPLATE" + ;; + esac + else + # Same version + TEMPLATE="$LOCAL_TEMPLATE" + TEMPLATE_SOURCE="local" + msg_ok "Using local template: $TEMPLATE" + fi + elif [[ -n "$LOCAL_TEMPLATE" ]]; then + # Only local exists + TEMPLATE="$LOCAL_TEMPLATE" TEMPLATE_SOURCE="local" - else + msg_ok "Using local template: $TEMPLATE" + elif [[ -n "$ONLINE_TEMPLATE" ]]; then + # Only online exists TEMPLATE="$ONLINE_TEMPLATE" TEMPLATE_SOURCE="online" - fi - - # If still no template, try to find alternatives - if [[ -z "$TEMPLATE" ]]; then - echo "" - echo "[DEBUG] No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}, searching for alternatives..." - - # Get all available versions for this OS type + msg_ok "Template found online: $TEMPLATE" + else + # Nothing found - offer alternatives + msg_error "No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}" + mapfile -t AVAILABLE_VERSIONS < <( pveam available -section system 2>/dev/null | - grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | - awk -F'\t' '{print $1}' | + awk '{print $NF}' | grep "^${PCT_OSTYPE}-" | sed -E "s/.*${PCT_OSTYPE}-([0-9]+(\.[0-9]+)?).*/\1/" | - sort -u -V 2>/dev/null + sort -u -V ) - + if [[ ${#AVAILABLE_VERSIONS[@]} -gt 0 ]]; then echo "" echo "${BL}Available ${PCT_OSTYPE} versions:${CL}" @@ -3076,36 +3064,23 @@ create_lxc_container() { done echo "" read -p "Select version [1-${#AVAILABLE_VERSIONS[@]}] or press Enter to cancel: " choice - + if [[ "$choice" =~ ^[0-9]+$ ]] && [[ "$choice" -ge 1 ]] && [[ "$choice" -le ${#AVAILABLE_VERSIONS[@]} ]]; then PCT_OSVERSION="${AVAILABLE_VERSIONS[$((choice - 1))]}" - TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION}" - SEARCH_PATTERN="^${TEMPLATE_SEARCH}-" - - echo "[DEBUG] Retrying with version: $PCT_OSVERSION" - - mapfile -t ONLINE_TEMPLATES < <( - pveam available -section system 2>/dev/null | - grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | - awk '{print $NF}' | - grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | - sort -V 2>/dev/null - ) - - if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then - TEMPLATE="${ONLINE_TEMPLATES[-1]}" + TEMPLATE=$(pveam available -section system 2>/dev/null | awk '{print $NF}' | grep "^${PCT_OSTYPE}-${PCT_OSVERSION}.*${TEMPLATE_PATTERN}" | sort -V | tail -1) + if [[ -n "$TEMPLATE" ]]; then + msg_ok "Selected: $TEMPLATE" TEMPLATE_SOURCE="online" - echo "[DEBUG] Found alternative: $TEMPLATE" else - msg_error "No templates available for ${PCT_OSTYPE} ${PCT_OSVERSION}" + msg_error "No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}" exit 225 fi else - msg_info "Installation cancelled" + msg_info "Cancelled" exit 0 fi else - msg_error "No ${PCT_OSTYPE} templates available at all" + msg_error "No templates available for ${PCT_OSTYPE}" exit 225 fi fi From 9c9dc863a2b2e6cb505a7281d5477eb11e08ed6e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 21 Oct 2025 15:52:06 +0200 Subject: [PATCH 1508/1733] Update build.func --- misc/build.func | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/misc/build.func b/misc/build.func index 5c1402891..55fc96a83 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3046,7 +3046,12 @@ create_lxc_container() { msg_ok "Template found online: $TEMPLATE" else # Nothing found - offer alternatives - msg_error "No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}" + msg_error "Template not found for ${PCT_OSTYPE} ${PCT_OSVERSION}" + echo "" + echo "This could mean:" + echo " • The version is not yet available in the Proxmox template repository" + echo " • Your Proxmox VE might need an update to access newer templates" + echo " • The version number might be incorrect" mapfile -t AVAILABLE_VERSIONS < <( pveam available -section system 2>/dev/null | @@ -3058,7 +3063,7 @@ create_lxc_container() { if [[ ${#AVAILABLE_VERSIONS[@]} -gt 0 ]]; then echo "" - echo "${BL}Available ${PCT_OSTYPE} versions:${CL}" + msg_info "Available ${PCT_OSTYPE} versions:" for i in "${!AVAILABLE_VERSIONS[@]}"; do echo " [$((i + 1))] ${AVAILABLE_VERSIONS[$i]}" done @@ -3076,11 +3081,16 @@ create_lxc_container() { exit 225 fi else - msg_info "Cancelled" + msg_info "Installation cancelled" exit 0 fi else msg_error "No templates available for ${PCT_OSTYPE}" + echo "" + echo "Please check:" + echo " • Run: pveam update" + echo " • Check network connectivity" + echo " • Verify Proxmox VE version: pveversion" exit 225 fi fi From 3e31d59d82006a89bf172e2d47ec7f332c60769b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 21 Oct 2025 15:54:36 +0200 Subject: [PATCH 1509/1733] Update build.func --- misc/build.func | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/misc/build.func b/misc/build.func index 55fc96a83..330c0b90a 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3004,10 +3004,10 @@ create_lxc_container() { # Step 1: Search in local storage LOCAL_TEMPLATE=$(pveam list "$TEMPLATE_STORAGE" 2>/dev/null | awk '{print $NF}' | grep "^${PCT_OSTYPE}-${PCT_OSVERSION}.*${TEMPLATE_PATTERN}" | sort -V | tail -1) - + # Step 2: Check pveam for available version (could be newer) ONLINE_TEMPLATE=$(pveam available -section system 2>/dev/null | awk '{print $NF}' | grep "^${PCT_OSTYPE}-${PCT_OSVERSION}.*${TEMPLATE_PATTERN}" | sort -V | tail -1) - + # Decide what to use if [[ -n "$LOCAL_TEMPLATE" && -n "$ONLINE_TEMPLATE" ]]; then # Both exist - check if online is newer @@ -3017,16 +3017,16 @@ create_lxc_container() { echo "" read -p "Download newer version? [y/N]: " answer case "${answer,,}" in - y|yes) - TEMPLATE="$ONLINE_TEMPLATE" - TEMPLATE_SOURCE="online" - msg_ok "Using newer version: $TEMPLATE" - ;; - *) - TEMPLATE="$LOCAL_TEMPLATE" - TEMPLATE_SOURCE="local" - msg_ok "Using local version: $TEMPLATE" - ;; + y | yes) + TEMPLATE="$ONLINE_TEMPLATE" + TEMPLATE_SOURCE="online" + msg_ok "Using newer version: $TEMPLATE" + ;; + *) + TEMPLATE="$LOCAL_TEMPLATE" + TEMPLATE_SOURCE="local" + msg_ok "Using local version: $TEMPLATE" + ;; esac else # Same version @@ -3052,7 +3052,7 @@ create_lxc_container() { echo " • The version is not yet available in the Proxmox template repository" echo " • Your Proxmox VE might need an update to access newer templates" echo " • The version number might be incorrect" - + mapfile -t AVAILABLE_VERSIONS < <( pveam available -section system 2>/dev/null | awk '{print $NF}' | @@ -3060,7 +3060,7 @@ create_lxc_container() { sed -E "s/.*${PCT_OSTYPE}-([0-9]+(\.[0-9]+)?).*/\1/" | sort -u -V ) - + if [[ ${#AVAILABLE_VERSIONS[@]} -gt 0 ]]; then echo "" msg_info "Available ${PCT_OSTYPE} versions:" @@ -3069,7 +3069,7 @@ create_lxc_container() { done echo "" read -p "Select version [1-${#AVAILABLE_VERSIONS[@]}] or press Enter to cancel: " choice - + if [[ "$choice" =~ ^[0-9]+$ ]] && [[ "$choice" -ge 1 ]] && [[ "$choice" -le ${#AVAILABLE_VERSIONS[@]} ]]; then PCT_OSVERSION="${AVAILABLE_VERSIONS[$((choice - 1))]}" TEMPLATE=$(pveam available -section system 2>/dev/null | awk '{print $NF}' | grep "^${PCT_OSTYPE}-${PCT_OSVERSION}.*${TEMPLATE_PATTERN}" | sort -V | tail -1) @@ -3180,7 +3180,7 @@ create_lxc_container() { } else msg_info "Installation cancelled" - exit 0 + exit 1 fi else msg_error "No ${PCT_OSTYPE} templates available" From a0dc98c454f4666e217b60ac60e575527a9c4ccf Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 21 Oct 2025 16:58:01 +0200 Subject: [PATCH 1510/1733] Update tools.func --- misc/tools.func | 43 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 9cd272a6d..3d5ce870c 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -2365,6 +2365,7 @@ EOF # - Detects existing MySQL installation # - Purges conflicting packages before installation # - Supports clean upgrade +# - Handles Debian Trixie libaio1t64 transition # # Variables: # MYSQL_VERSION - MySQL version to install (e.g. 5.7, 8.0) (default: 8.0) @@ -2398,20 +2399,52 @@ function setup_mysql() { fi if [[ "$NEED_INSTALL" == true ]]; then + # For Debian Trixie+, use native packages due to libaio1t64 transition + if [[ "$DISTRO_ID" == "debian" ]] && [[ "$DISTRO_CODENAME" =~ ^(trixie|forky|sid)$ ]]; then + msg_info "Using Debian native MySQL packages for ${DISTRO_CODENAME} (libaio1t64 compatibility)" + + # Stop existing MySQL if running + $STD systemctl stop mysql 2>/dev/null || true + + # Only purge if MySQL is actually installed + if dpkg -l 2>/dev/null | grep -q "^ii.*mysql-server"; then + $STD apt purge -y mysql-server* mysql-client* mysql-common 2>/dev/null || true + fi + + # Update and install from Debian repos + $STD apt update + + if ! $STD apt install -y mysql-server mysql-client; then + msg_error "Failed to install MySQL from Debian repository" + return 1 + fi + + # Verify installation + if ! command -v mysql >/dev/null 2>&1; then + msg_warn "MySQL installed but mysql command not immediately available - retrying after shell refresh" + hash -r + if ! command -v mysql >/dev/null 2>&1; then + msg_error "MySQL installed but mysql command still not found" + return 1 + fi + fi + + cache_installed_version "mysql" "$MYSQL_VERSION" + msg_ok "Installed MySQL $MYSQL_VERSION from Debian repository" + return 0 + fi + + # For stable Debian and Ubuntu - use upstream MySQL repo # Cleanup old repository files cleanup_old_repo_files "mysql" - # Determine suite - use bookworm for Debian testing/unstable + # Determine suite local SUITE if [[ "$DISTRO_ID" == "debian" ]]; then case "$DISTRO_CODENAME" in bookworm | bullseye) SUITE="$DISTRO_CODENAME" ;; - trixie | forky | sid) - msg_warn "Using MySQL Bookworm packages on Debian ${DISTRO_CODENAME}" - SUITE="bookworm" - ;; *) SUITE="bookworm" # Fallback to bookworm for unknown Debian versions ;; From 128db5320acd383853f2be03dde654a68359b320 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 21 Oct 2025 17:09:20 +0200 Subject: [PATCH 1511/1733] Update tools.func --- misc/tools.func | 84 +++++++++++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 37 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 3d5ce870c..1fbcc6986 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -2399,52 +2399,20 @@ function setup_mysql() { fi if [[ "$NEED_INSTALL" == true ]]; then - # For Debian Trixie+, use native packages due to libaio1t64 transition - if [[ "$DISTRO_ID" == "debian" ]] && [[ "$DISTRO_CODENAME" =~ ^(trixie|forky|sid)$ ]]; then - msg_info "Using Debian native MySQL packages for ${DISTRO_CODENAME} (libaio1t64 compatibility)" - - # Stop existing MySQL if running - $STD systemctl stop mysql 2>/dev/null || true - - # Only purge if MySQL is actually installed - if dpkg -l 2>/dev/null | grep -q "^ii.*mysql-server"; then - $STD apt purge -y mysql-server* mysql-client* mysql-common 2>/dev/null || true - fi - - # Update and install from Debian repos - $STD apt update - - if ! $STD apt install -y mysql-server mysql-client; then - msg_error "Failed to install MySQL from Debian repository" - return 1 - fi - - # Verify installation - if ! command -v mysql >/dev/null 2>&1; then - msg_warn "MySQL installed but mysql command not immediately available - retrying after shell refresh" - hash -r - if ! command -v mysql >/dev/null 2>&1; then - msg_error "MySQL installed but mysql command still not found" - return 1 - fi - fi - - cache_installed_version "mysql" "$MYSQL_VERSION" - msg_ok "Installed MySQL $MYSQL_VERSION from Debian repository" - return 0 - fi - - # For stable Debian and Ubuntu - use upstream MySQL repo # Cleanup old repository files cleanup_old_repo_files "mysql" - # Determine suite + # Determine suite - use bookworm for Debian testing/unstable local SUITE if [[ "$DISTRO_ID" == "debian" ]]; then case "$DISTRO_CODENAME" in bookworm | bullseye) SUITE="$DISTRO_CODENAME" ;; + trixie | forky | sid) + msg_warn "Using MySQL Bookworm packages on Debian ${DISTRO_CODENAME}" + SUITE="bookworm" + ;; *) SUITE="bookworm" # Fallback to bookworm for unknown Debian versions ;; @@ -2462,6 +2430,48 @@ function setup_mysql() { $STD apt purge -y mysql-server* mysql-client* mysql-common 2>/dev/null || true fi + # Handle libaio dependency for Debian Trixie+ (time64 transition) + if [[ "$DISTRO_ID" == "debian" ]] && [[ "$DISTRO_CODENAME" =~ ^(trixie|forky|sid)$ ]]; then + msg_info "Installing libaio compatibility for Debian ${DISTRO_CODENAME}" + + # Install libaio1t64 if not present + if ! dpkg -l libaio1t64 2>/dev/null | grep -q "^ii"; then + $STD apt update + $STD apt install -y libaio1t64 + fi + + # Create dummy libaio1 package for dependency satisfaction + local TEMP_DIR="/tmp/libaio1-compat-$$" + mkdir -p "$TEMP_DIR" + cd "$TEMP_DIR" + + # Create control file + mkdir -p DEBIAN + cat > DEBIAN/control < libaio1t64 transition + This is a transitional dummy package to satisfy dependencies on libaio1 + while actually using libaio1t64 (time64 transition). +EOF + + # Build the dummy package + cd /tmp + dpkg-deb -b "$TEMP_DIR" libaio1-compat.deb >/dev/null 2>&1 + + # Install it + $STD dpkg -i libaio1-compat.deb + + # Cleanup + rm -rf "$TEMP_DIR" libaio1-compat.deb + + msg_ok "libaio1 compatibility package installed" + fi + # Use standardized repo setup setup_deb822_repo \ "mysql" \ From 6c2ea268de0c408ffd477927e581f41ff743b0eb Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 22 Oct 2025 08:38:01 +0200 Subject: [PATCH 1512/1733] test: dynamic update --- ct/gitea-mirror.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ct/gitea-mirror.sh b/ct/gitea-mirror.sh index bfe93ff89..4c5fca198 100644 --- a/ct/gitea-mirror.sh +++ b/ct/gitea-mirror.sh @@ -13,6 +13,7 @@ var_disk="${var_disk:-6}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" +var_app_version="${var_app_version:-latest}" header_info "$APP" @@ -97,7 +98,7 @@ fi msg_ok "Installed Bun" rm -rf /opt/gitea-mirror - fetch_and_deploy_gh_release "gitea-mirror" "RayLabsHQ/gitea-mirror" "tarball" "v3.8.1" + fetch_and_deploy_gh_release "gitea-mirror" "RayLabsHQ/gitea-mirror" "tarball" $var_app_version msg_info "Updating and rebuilding ${APP}" cd /opt/gitea-mirror From 3a363e5d2739a94682491c1acc02e1d089e2867d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 22 Oct 2025 08:38:41 +0200 Subject: [PATCH 1513/1733] cleanup --- ct/gitea-mirror.sh | 130 ------------ ct/guardian.sh | 79 -------- ct/limesurvey.sh | 42 ---- ct/nginxproxymanager.sh | 171 ---------------- ct/open-archiver.sh | 61 ------ ct/palmr.sh | 75 ------- ct/plex.sh | 60 ------ ct/prometheus-blackbox-exporter.sh | 62 ------ ct/proxmox-datacenter-manager.sh | 54 ----- ct/pve-scripts-local.sh | 41 ---- ct/sonarqube.sh | 71 ------- frontend/public/json/freepbx.json | 40 ---- frontend/public/json/guardian.json | 35 ---- frontend/public/json/limesurvey.json | 40 ---- frontend/public/json/open-archiver.json | 40 ---- .../json/prometheus-blackbox-exporter.json | 41 ---- frontend/public/json/pve-scripts-local.json | 35 ---- frontend/public/json/sonarqube.json | 36 ---- install/gitea-mirror-install.sh | 77 ------- install/guardian-install.sh | 74 ------- install/jellyfin-install.sh | 51 ----- install/limesurvey-install.sh | 74 ------- install/nginxproxymanager-install.sh | 189 ------------------ install/open-archiver-install.sh | 124 ------------ install/palmr-install.sh | 95 --------- install/plex-install.sh | 55 ----- .../prometheus-blackbox-exporter-install.sh | 42 ---- install/pve-scripts-local-install.sh | 64 ------ install/sonarqube-install.sh | 81 -------- 29 files changed, 2039 deletions(-) delete mode 100644 ct/gitea-mirror.sh delete mode 100755 ct/guardian.sh delete mode 100644 ct/limesurvey.sh delete mode 100644 ct/nginxproxymanager.sh delete mode 100644 ct/open-archiver.sh delete mode 100644 ct/palmr.sh delete mode 100644 ct/plex.sh delete mode 100644 ct/prometheus-blackbox-exporter.sh delete mode 100644 ct/proxmox-datacenter-manager.sh delete mode 100644 ct/pve-scripts-local.sh delete mode 100644 ct/sonarqube.sh delete mode 100644 frontend/public/json/freepbx.json delete mode 100644 frontend/public/json/guardian.json delete mode 100644 frontend/public/json/limesurvey.json delete mode 100644 frontend/public/json/open-archiver.json delete mode 100644 frontend/public/json/prometheus-blackbox-exporter.json delete mode 100644 frontend/public/json/pve-scripts-local.json delete mode 100644 frontend/public/json/sonarqube.json delete mode 100644 install/gitea-mirror-install.sh delete mode 100644 install/guardian-install.sh delete mode 100644 install/jellyfin-install.sh delete mode 100644 install/limesurvey-install.sh delete mode 100644 install/nginxproxymanager-install.sh delete mode 100644 install/open-archiver-install.sh delete mode 100644 install/palmr-install.sh delete mode 100644 install/plex-install.sh delete mode 100644 install/prometheus-blackbox-exporter-install.sh delete mode 100644 install/pve-scripts-local-install.sh delete mode 100644 install/sonarqube-install.sh diff --git a/ct/gitea-mirror.sh b/ct/gitea-mirror.sh deleted file mode 100644 index bfe93ff89..000000000 --- a/ct/gitea-mirror.sh +++ /dev/null @@ -1,130 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: CrazyWolf13 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/RayLabsHQ/gitea-mirror - -APP="gitea-mirror" -var_tags="${var_tags:-mirror;gitea}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-6}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" - -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/gitea-mirror ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - APP_VERSION=$(grep -o '"version": *"[^"]*"' /opt/gitea-mirror/package.json | cut -d'"' -f4) - if [[ $APP_VERSION =~ ^2\. ]]; then - if ! whiptail --backtitle "Gitea Mirror Update" --title "⚠️ VERSION 2.x DETECTED" --yesno \ - "WARNING: Version $APP_VERSION detected!\n\nUpdating from version 2.x will CLEAR ALL CONFIGURATION.\n\nThis includes:\n• API tokens\n• User settings\n• Repository configurations\n• All custom settings\n\nDo you want to continue with the update process?" 15 70 --defaultno; then - exit 0 - fi - - if ! whiptail --backtitle "Gitea Mirror Update" --title "⚠️ FINAL CONFIRMATION" --yesno \ - "FINAL WARNING: This update WILL clear all configuration!\n\nBEFORE PROCEEDING, please:\n\n• Copy API tokens to a safe location\n• Backup any custom configurations\n• Note down repository settings\n\nThis action CANNOT be undone!" 18 70 --defaultno; then - whiptail --backtitle "Gitea Mirror Update" --title "Update Cancelled" --msgbox "Update process cancelled. Please backup your configuration before proceeding." 8 60 - exit 0 - fi - whiptail --backtitle "Gitea Mirror Update" --title "Proceeding with Update" --msgbox \ - "Proceeding with version $APP_VERSION update.\n\nAll configuration will be cleared as warned." 8 50 - rm -rf /opt/gitea-mirror - fi - - if [[ ! -f /opt/gitea-mirror.env ]]; then - msg_info "Detected old Enviroment, updating files" - APP_SECRET=$(openssl rand -base64 32) - HOST_IP=$(hostname -I | awk '{print $1}') - cat </opt/gitea-mirror.env -# See here for config options: https://github.com/RayLabsHQ/gitea-mirror/blob/main/docs/ENVIRONMENT_VARIABLES.md -NODE_ENV=production -HOST=0.0.0.0 -PORT=4321 -DATABASE_URL=sqlite://data/gitea-mirror.db -BETTER_AUTH_URL=http://${HOST_IP}:4321 -BETTER_AUTH_SECRET=${APP_SECRET} -npm_package_version=${APP_VERSION} -EOF - rm /etc/systemd/system/gitea-mirror.service - cat </etc/systemd/system/gitea-mirror.service -[Unit] -Description=Gitea Mirror -After=network.target -[Service] -Type=simple -WorkingDirectory=/opt/gitea-mirror -ExecStart=/usr/local/bin/bun dist/server/entry.mjs -Restart=on-failure -RestartSec=10 -EnvironmentFile=/opt/gitea-mirror.env -[Install] -WantedBy=multi-user.target -EOF - systemctl daemon-reload - msg_ok "Old Enviroment fixed" -fi - - if check_for_gh_release "gitea-mirror" "RayLabsHQ/gitea-mirror"; then - msg_info "Stopping Services" - systemctl stop gitea-mirror - msg_ok "Services Stopped" - - msg_info "Backup Data" - mkdir -p /opt/gitea-mirror-backup/data - cp /opt/gitea-mirror/data/* /opt/gitea-mirror-backup/data/ - msg_ok "Backup Data" - - msg_info "Installing Bun" - export BUN_INSTALL=/opt/bun - curl -fsSL https://bun.sh/install | $STD bash - ln -sf /opt/bun/bin/bun /usr/local/bin/bun - ln -sf /opt/bun/bin/bun /usr/local/bin/bunx - msg_ok "Installed Bun" - - rm -rf /opt/gitea-mirror - fetch_and_deploy_gh_release "gitea-mirror" "RayLabsHQ/gitea-mirror" "tarball" "v3.8.1" - - msg_info "Updating and rebuilding ${APP}" - cd /opt/gitea-mirror - $STD bun run setup - $STD bun run build - APP_VERSION=$(grep -o '"version": *"[^"]*"' package.json | cut -d'"' -f4) - - sudo sed -i.bak "s|^npm_package_version=.*|npm_package_version=${APP_VERSION}|" /opt/gitea-mirror.env - msg_ok "Updated and rebuilt ${APP}" - - msg_info "Restoring Data" - cp /opt/gitea-mirror-backup/data/* /opt/gitea-mirror/data - msg_ok "Restored Data" - - msg_info "Starting Service" - systemctl start gitea-mirror - msg_ok "Service Started" - msg_ok "Update Successfully" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:4321${CL}" diff --git a/ct/guardian.sh b/ct/guardian.sh deleted file mode 100755 index 41fbaf30a..000000000 --- a/ct/guardian.sh +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: HydroshieldMKII -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/HydroshieldMKII/Guardian - -APP="Guardian" -var_tags="${var_tags:-media;monitoring}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-6}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { -header_info -check_container_storage -check_container_resources - -if [[ ! -d "/opt/guardian" ]] ; then - msg_error "No ${APP} Installation Found!" - exit -fi - -if check_for_gh_release "guardian" "HydroshieldMKII/Guardian" ; then - msg_info "Stopping Services" - systemctl stop guardian-backend guardian-frontend - msg_ok "Stopped Services" - - if [[ -f "/opt/guardian/backend/plex-guard.db" ]] ; then - msg_info "Saving Database" - cp "/opt/guardian/backend/plex-guard.db" "/tmp/plex-guard.db.backup" - msg_ok "Database backed up" - fi - - cp /opt/guardian/.env /opt - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "guardian" "HydroshieldMKII/Guardian" "tarball" "latest" "/opt/guardian" - mv /opt/.env /opt/guardian - - if [[ -f "/tmp/plex-guard.db.backup" ]] ; then - msg_info "Restoring Database" - cp "/tmp/plex-guard.db.backup" "/opt/guardian/backend/plex-guard.db" - rm "/tmp/plex-guard.db.backup" - msg_ok "Database restored" - fi - - msg_info "Updating Guardian" - cd /opt/guardian/backend - $STD npm ci - $STD npm run build - - cd /opt/guardian/frontend - $STD npm ci - $STD DEPLOYMENT_MODE=standalone npm run build - msg_ok "Updated Guardian" - - msg_info "Starting Services" - systemctl start guardian-backend guardian-frontend - msg_ok "Started Services" - msg_ok "Updated Successfully" -fi -exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" diff --git a/ct/limesurvey.sh b/ct/limesurvey.sh deleted file mode 100644 index c0ee8221d..000000000 --- a/ct/limesurvey.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Slaviša Arežina (tremor021) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://community.limesurvey.org/ - -APP="LimeSurvey" -var_tags="${var_tags:-os}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-512}" -var_disk="${var_disk:-2}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/limesurvey ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - msg_warn "Application is updated via Web Interface" - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" diff --git a/ct/nginxproxymanager.sh b/ct/nginxproxymanager.sh deleted file mode 100644 index 82eef7146..000000000 --- a/ct/nginxproxymanager.sh +++ /dev/null @@ -1,171 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://nginxproxymanager.com/ - -APP="Nginx Proxy Manager" -var_tags="${var_tags:-proxy}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-1024}" -var_disk="${var_disk:-4}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -f /lib/systemd/system/npm.service ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if ! command -v yarn &>/dev/null; then - msg_info "Installing Yarn" - $STD npm install -g yarn - msg_ok "Installed Yarn" - fi - - export NODE_OPTIONS="--openssl-legacy-provider" - - RELEASE=$(curl -fsSL https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/releases/latest | - grep "tag_name" | - awk '{print substr($2, 3, length($2)-4) }') - - msg_info "Downloading NPM v${RELEASE}" - curl -fsSL "https://codeload.github.com/NginxProxyManager/nginx-proxy-manager/tar.gz/v${RELEASE}" | tar -xz - cd nginx-proxy-manager-"${RELEASE}" || exit - msg_ok "Downloaded NPM v${RELEASE}" - - msg_info "Building Frontend" - ( - sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" backend/package.json - sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" frontend/package.json - cd ./frontend || exit - # Replace node-sass with sass in package.json before installation - sed -i 's/"node-sass".*$/"sass": "^1.92.1",/g' package.json - $STD yarn install --network-timeout 600000 - $STD yarn build - ) - msg_ok "Built Frontend" - - msg_info "Stopping Services" - systemctl stop openresty - systemctl stop npm - msg_ok "Stopped Services" - - msg_info "Cleaning Old Files" - rm -rf /app \ - /var/www/html \ - /etc/nginx \ - /var/log/nginx \ - /var/lib/nginx \ - "$STD" /var/cache/nginx - msg_ok "Cleaned Old Files" - - msg_info "Setting up Environment" - ln -sf /usr/bin/python3 /usr/bin/python - ln -sf /opt/certbot/bin/certbot /usr/local/bin/certbot - ln -sf /usr/local/openresty/nginx/sbin/nginx /usr/sbin/nginx - ln -sf /usr/local/openresty/nginx/ /etc/nginx - sed -i 's+^daemon+#daemon+g' docker/rootfs/etc/nginx/nginx.conf - NGINX_CONFS=$(find "$(pwd)" -type f -name "*.conf") - for NGINX_CONF in $NGINX_CONFS; do - sed -i 's+include conf.d+include /etc/nginx/conf.d+g' "$NGINX_CONF" - done - mkdir -p /var/www/html /etc/nginx/logs - cp -r docker/rootfs/var/www/html/* /var/www/html/ - cp -r docker/rootfs/etc/nginx/* /etc/nginx/ - cp docker/rootfs/etc/letsencrypt.ini /etc/letsencrypt.ini - cp docker/rootfs/etc/logrotate.d/nginx-proxy-manager /etc/logrotate.d/nginx-proxy-manager - ln -sf /etc/nginx/nginx.conf /etc/nginx/conf/nginx.conf - rm -f /etc/nginx/conf.d/dev.conf - mkdir -p /tmp/nginx/body \ - /run/nginx \ - /data/nginx \ - /data/custom_ssl \ - /data/logs \ - /data/access \ - /data/nginx/default_host \ - /data/nginx/default_www \ - /data/nginx/proxy_host \ - /data/nginx/redirection_host \ - /data/nginx/stream \ - /data/nginx/dead_host \ - /data/nginx/temp \ - /var/lib/nginx/cache/public \ - /var/lib/nginx/cache/private \ - /var/cache/nginx/proxy_temp - chmod -R 777 /var/cache/nginx - chown root /tmp/nginx - echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" {print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf);" >/etc/nginx/conf.d/include/resolvers.conf - if [ ! -f /data/nginx/dummycert.pem ] || [ ! -f /data/nginx/dummykey.pem ]; then - $STD openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 -subj "/O=Nginx Proxy Manager/OU=Dummy Certificate/CN=localhost" -keyout /data/nginx/dummykey.pem -out /data/nginx/dummycert.pem - fi - mkdir -p /app/global /app/frontend/images - cp -r frontend/dist/* /app/frontend - cp -r frontend/app-images/* /app/frontend/images - cp -r backend/* /app - cp -r global/* /app/global - - # Update Certbot and plugins in virtual environment - if [ -d /opt/certbot ]; then - $STD /opt/certbot/bin/pip install --upgrade pip setuptools wheel - $STD /opt/certbot/bin/pip install --upgrade certbot certbot-dns-cloudflare - fi - msg_ok "Setup Environment" - - msg_info "Initializing Backend" - $STD rm -rf /app/config/default.json - if [ ! -f /app/config/production.json ]; then - cat <<'EOF' >/app/config/production.json -{ - "database": { - "engine": "knex-native", - "knex": { - "client": "sqlite3", - "connection": { - "filename": "/data/database.sqlite" - } - } - } -} -EOF - fi - cd /app || exit - export NODE_OPTIONS="--openssl-legacy-provider" - $STD yarn install --network-timeout 600000 - msg_ok "Initialized Backend" - - msg_info "Starting Services" - sed -i 's/user npm/user root/g; s/^pid/#pid/g' /usr/local/openresty/nginx/conf/nginx.conf - sed -i 's/su npm npm/su root root/g' /etc/logrotate.d/nginx-proxy-manager - sed -i 's/include-system-site-packages = false/include-system-site-packages = true/g' /opt/certbot/pyvenv.cfg - systemctl enable -q --now openresty - systemctl enable -q --now npm - msg_ok "Started Services" - - msg_info "Cleaning up" - rm -rf ~/nginx-proxy-manager-* - msg_ok "Cleaned" - - msg_ok "Updated Successfully" - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:81${CL}" diff --git a/ct/open-archiver.sh b/ct/open-archiver.sh deleted file mode 100644 index 8bc105a23..000000000 --- a/ct/open-archiver.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Slaviša Arežina (tremor021) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://openarchiver.com/ - -APP="Open-Archiver" -var_tags="${var_tags:-os}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-3072}" -var_disk="${var_disk:-8}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/openarchiver ]]; then - msg_error "No Open Archiver Installation Found!" - exit - fi - - if check_for_gh_release "openarchiver" "LogicLabs-OU/OpenArchiver"; then - msg_info "Stopping Services" - systemctl stop openarchiver - msg_ok "Stopped Services" - - cp /opt/openarchiver/.env /opt/openarchiver.env - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "openarchiver" "LogicLabs-OU/OpenArchiver" "tarball" "latest" "/opt/openarchiver" - mv /opt/openarchiver.env /opt/openarchiver/.env - - msg_info "Updating Open Archiver" - $STD pnpm install --shamefully-hoist --frozen-lockfile --prod=false - $STD pnpm build - $STD pnpm db:migrate - msg_ok "Updated Open Archiver" - - msg_info "Starting Services" - systemctl start openarchiver - msg_ok "Started Services" - msg_ok "Updated Successfully" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" diff --git a/ct/palmr.sh b/ct/palmr.sh deleted file mode 100644 index eb7fec6fa..000000000 --- a/ct/palmr.sh +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: vhsdream -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/kyantech/Palmr - -APP="Palmr" -var_tags="${var_tags:-files}" -var_cpu="${var_cpu:-4}" -var_ram="${var_ram:-6144}" -var_disk="${var_disk:-6}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/palmr_data ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - if check_for_gh_release "palmr" "kyantech/Palmr"; then - msg_info "Stopping Services" - systemctl stop palmr-frontend palmr-backend - msg_ok "Stopped Services" - - cp /opt/palmr/apps/server/.env /opt/palmr.env - rm -rf /opt/palmr - fetch_and_deploy_gh_release "Palmr" "kyantech/Palmr" "tarball" "latest" "/opt/palmr" - - PNPM="$(jq -r '.packageManager' /opt/palmr/package.json)" - NODE_VERSION="20" NODE_MODULE="$PNPM" setup_nodejs - - msg_info "Updating ${APP}" - cd /opt/palmr/apps/server - mv /opt/palmr.env /opt/palmr/apps/server/.env - $STD pnpm install - $STD npx prisma generate - $STD npx prisma migrate deploy - $STD npx prisma db push - $STD pnpm build - - cd /opt/palmr/apps/web - export NODE_ENV=production - export NEXT_TELEMETRY_DISABLED=1 - mv ./.env.example ./.env - $STD pnpm install - $STD pnpm build - chown -R palmr:palmr /opt/palmr_data /opt/palmr - msg_ok "Updated ${APP}" - - msg_info "Starting Services" - systemctl start palmr-backend palmr-frontend - msg_ok "Started Services" - msg_ok "Updated Successfully" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" diff --git a/ct/plex.sh b/ct/plex.sh deleted file mode 100644 index ef3fd9d6a..000000000 --- a/ct/plex.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://www.plex.tv/ - -APP="Plex" -var_tags="${var_tags:-media}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-8}" -var_os="${var_os:-ubuntu}" -var_version="${var_version:-24.04}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - - if [[ -f /etc/apt/sources.list.d/plexmediaserver.list ]]; then - msg_info "Migrating Plex repository to Deb822 format" - rm -f /etc/apt/sources.list.d/plexmediaserver.list - curl -fsSL https://downloads.plex.tv/plex-keys/PlexSign.key | tee /usr/share/keyrings/PlexSign.asc >/dev/null - cat </etc/apt/sources.list.d/plexmediaserver.sources -Types: deb -URIs: https://downloads.plex.tv/repo/deb/ -Suites: public -Components: main -Signed-By: /usr/share/keyrings/PlexSign.asc -EOF - msg_ok "Migrated Plex repository to Deb822" - fi - - if [[ ! -f /etc/apt/sources.list.d/plexmediaserver.sources ]]; then - msg_error "No ${APP} repository found!" - exit 1 - fi - - msg_info "Updating ${APP}" - $STD apt update - $STD apt -y -o Dpkg::Options::="--force-confold" upgrade plexmediaserver - msg_ok "Updated ${APP}" - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:32400/web${CL}" diff --git a/ct/prometheus-blackbox-exporter.sh b/ct/prometheus-blackbox-exporter.sh deleted file mode 100644 index 44a62d494..000000000 --- a/ct/prometheus-blackbox-exporter.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Marfnl -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/prometheus/blackbox_exporter - -APP="Prometheus-Blackbox-Exporter" -var_tags="${var_tags:-monitoring;prometheus}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-512}" -var_disk="${var_disk:-4}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/blackbox-exporter ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if check_for_gh_release "blackbox-exporter" "prometheus/blackbox_exporter"; then - msg_info "Stopping $APP" - systemctl stop blackbox-exporter - msg_ok "Stopped $APP" - - msg_info "Creating backup" - mv /opt/blackbox-exporter/blackbox.yml /opt - msg_ok "Backup created" - - fetch_and_deploy_gh_release "blackbox-exporter" "prometheus/blackbox_exporter" "prebuild" "latest" "/opt/blackbox-exporter" "blackbox_exporter-*.linux-amd64.tar.gz" - - msg_info "Restoring backup" - cp -r /opt/blackbox.yml /opt/blackbox-exporter - rm -f /opt/blackbox.yml - msg_ok "Backup restored" - - msg_info "Starting $APP" - systemctl start blackbox-exporter - msg_ok "Started $APP" - msg_ok "Update Successful" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9115${CL}" diff --git a/ct/proxmox-datacenter-manager.sh b/ct/proxmox-datacenter-manager.sh deleted file mode 100644 index dfcb11208..000000000 --- a/ct/proxmox-datacenter-manager.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: CrazyWolf13 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: Proxmox Server Solution GmbH - -APP="Proxmox-Datacenter-Manager" -var_tags="${var_tags:-datacenter}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-10}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -e /usr/sbin/proxmox-datacenter-manager-admin ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if grep -q 'Debian GNU/Linux 12' /etc/os-release && [ -f /etc/apt/sources.list.d/proxmox-release-bookworm.list ] && [ -f /etc/apt/sources.list.d/pdm-test.list ]; then - msg_info "Updating outdated outdated source formats" - echo "deb [signed-by=/usr/share/keyrings/proxmox-archive-keyring.gpg] http://download.proxmox.com/debian/pdm bookworm pdm-test" >/etc/apt/sources.list.d/pdm-test.list - curl -fsSL https://enterprise.proxmox.com/debian/proxmox-archive-keyring-trixie.gpg -o /usr/share/keyrings/proxmox-archive-keyring.gpg - rm -f /etc/apt/keyrings/proxmox-release-bookworm.gpg /etc/apt/sources.list.d/proxmox-release-bookworm.list - $STD apt-get update - msg_ok "Updated old sources" - fi - - msg_info "Updating $APP LXC" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "Updated $APP LXC" - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:8443${CL}" diff --git a/ct/pve-scripts-local.sh b/ct/pve-scripts-local.sh deleted file mode 100644 index d3ee234a0..000000000 --- a/ct/pve-scripts-local.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: michelroegl-brunner -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://www.debian.org/ - -APP="PVE-Scripts-Local" -var_tags="${var_tags:-pve-scritps-local}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-4096}" -var_disk="${var_disk:-4}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /var ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - msg_info "No Update function implementd" - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" diff --git a/ct/sonarqube.sh b/ct/sonarqube.sh deleted file mode 100644 index c8338e4a5..000000000 --- a/ct/sonarqube.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: prop4n -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://docs.sonarsource.com/sonarqube-server - -APP="SonarQube" -var_tags="${var_tags:-automation}" -var_cpu="${var_cpu:-4}" -var_ram="${var_ram:-6144}" -var_disk="${var_disk:-25}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/sonarqube ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if check_for_gh_release "sonarqube" "SonarSource/sonarqube"; then - msg_info "Stopping service" - systemctl stop sonarqube - msg_ok "Service stopped" - - msg_info "Creating backup" - BACKUP_DIR="/opt/sonarqube-backup" - mv /opt/sonarqube ${BACKUP_DIR} - msg_ok "Backup created" - - msg_info "Installing sonarqube" - RELEASE=$(curl -fsSL https://api.github.com/repos/SonarSource/sonarqube/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - curl -fsSL "https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-${RELEASE}.zip" -o $temp_file - unzip -q "$temp_file" -d /opt - mv /opt/sonarqube-* /opt/sonarqube - msg_ok "Installed sonarqube" - - msg_info "Restoring backup" - cp -rp ${BACKUP_DIR}/data/ /opt/sonarqube/data/ - cp -rp ${BACKUP_DIR}/extensions/ /opt/sonarqube/extensions/ - cp -p ${BACKUP_DIR}/conf/sonar.properties /opt/sonarqube/conf/sonar.properties - rm -rf ${BACKUP_DIR} - chown -R sonarqube:sonarqube /opt/sonarqube - msg_ok "Backup restored" - - msg_info "Starting service" - systemctl start sonarqube - msg_ok "Service started" - msg_ok "Updated Successfully" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9000${CL}" diff --git a/frontend/public/json/freepbx.json b/frontend/public/json/freepbx.json deleted file mode 100644 index 11040c040..000000000 --- a/frontend/public/json/freepbx.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "FreePBX", - "slug": "freepbx", - "categories": [ - 0 - ], - "date_created": "2025-05-22", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 80, - "documentation": "https://sangomakb.atlassian.net/wiki/spaces/FP/overview?homepageId=8454359", - "website": "https://www.freepbx.org/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/freepbx.webp", - "config_path": "", - "description": "FreePBX is a web-based open-source graphical user interface that manages Asterisk, a voice over IP and telephony server.", - "install_methods": [ - { - "type": "default", - "script": "ct/freepbx.sh", - "resources": { - "cpu": 2, - "ram": 2048, - "hdd": 10, - "os": "debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "This script uses the official FreePBX install script. Check it here: https://github.com/FreePBX/sng_freepbx_debian_install", - "type": "info" - } - ] -} diff --git a/frontend/public/json/guardian.json b/frontend/public/json/guardian.json deleted file mode 100644 index b7c6761d5..000000000 --- a/frontend/public/json/guardian.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "Guardian", - "slug": "guardian", - "categories": [ - 13 - ], - "date_created": "2025-09-22", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 3000, - "documentation": "https://github.com/HydroshieldMKII/Guardian/blob/main/README.md", - "config_path": "/opt/guardian/.env", - "website": "https://github.com/HydroshieldMKII/Guardian", - "logo": null, - "description": "Guardian is a lightweight companion app for Plex that lets you monitor, approve or block devices in real time. It helps you enforce per-user or global policies, stop unwanted sessions automatically and grant temporary access - all through a simple web interface.", - "install_methods": [ - { - "type": "default", - "script": "ct/guardian.sh", - "resources": { - "cpu": 2, - "ram": 2048, - "hdd": 6, - "os": "Debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] -} diff --git a/frontend/public/json/limesurvey.json b/frontend/public/json/limesurvey.json deleted file mode 100644 index 0d67ef391..000000000 --- a/frontend/public/json/limesurvey.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "LimeSurvey", - "slug": "limesurvey", - "categories": [ - 25 - ], - "date_created": "2025-09-22", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 80, - "documentation": "https://www.limesurvey.org/manual/LimeSurvey_Manual", - "config_path": "", - "website": "https://community.limesurvey.org/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/limesurvey.webp", - "description": "LimeSurvey is the simple, quick and anonymous online survey tool that's bursting with juicy insights. Calling students, professionals and enterprises: design a survey and get the best insights, it’s free and as easy as squeezing a lime. Make a free online survey now!", - "install_methods": [ - { - "type": "default", - "script": "ct/limesurvey.sh", - "resources": { - "cpu": 1, - "ram": 512, - "hdd": 2, - "os": "Debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "You will need to input database credentials into LimeSurvey installer. Use `cat ~/limesurvey.creds` inside LXC.", - "type": "info" - } - ] -} diff --git a/frontend/public/json/open-archiver.json b/frontend/public/json/open-archiver.json deleted file mode 100644 index b203d38c1..000000000 --- a/frontend/public/json/open-archiver.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "Open-Archiver", - "slug": "open-archiver", - "categories": [ - 7 - ], - "date_created": "2025-09-30", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 3000, - "documentation": "https://docs.openarchiver.com/", - "config_path": "/opt/openarchiver/.env", - "website": "https://openarchiver.com/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/OpenArchiver.webp", - "description": "Open Archiver is a secure, self-hosted email archiving solution, and it's completely open source. Get an email archiver that enables full-text search across email and attachments. Create a permanent, searchable, and compliant mail archive from Google Workspace, Microsoft 35, and any IMAP server.", - "install_methods": [ - { - "type": "default", - "script": "ct/open-archiver.sh", - "resources": { - "cpu": 2, - "ram": 3072, - "hdd": 8, - "os": "debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "Data directory is: `/opt/openarchiver-data`. If you have a lot of email, you might consider mounting external storage to this directory.", - "type": "info" - } - ] -} diff --git a/frontend/public/json/prometheus-blackbox-exporter.json b/frontend/public/json/prometheus-blackbox-exporter.json deleted file mode 100644 index bc20843e5..000000000 --- a/frontend/public/json/prometheus-blackbox-exporter.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "Prometheus Blackbox Exporter", - "slug": "prometheus-blackbox-exporter", - "categories": [ - 1, - 9 - ], - "date_created": "2025-09-29", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 9115, - "documentation": "https://github.com/prometheus/blackbox_exporter", - "website": "https://github.com/prometheus/blackbox_exporter", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/prometheus.webp", - "config_path": "/opt/blackbox-exporter/blackbox.yml", - "description": "An exporter allows blackbox probing of endpoints over HTTP, HTTPS, DNS, TCP, ICMP and gRPC for use by the Prometheus monitoring system.", - "install_methods": [ - { - "type": "default", - "script": "ct/prometheus-blackbox-exporter.sh", - "resources": { - "cpu": 1, - "ram": 512, - "hdd": 4, - "os": "debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "Please adjust the Proxmox credentials in the configuration file!", - "type": "info" - } - ] -} diff --git a/frontend/public/json/pve-scripts-local.json b/frontend/public/json/pve-scripts-local.json deleted file mode 100644 index c8d5061fa..000000000 --- a/frontend/public/json/pve-scripts-local.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "PVEScriptsLocal", - "slug": "pve-scripts-local", - "categories": [ - 1 - ], - "date_created": "2025-10-03", - "type": "ct", - "updateable": false, - "privileged": false, - "interface_port": 3000, - "documentation": "https://github.com/community-scripts/ProxmoxVE-Local", - "config_path": "/opt/PVEScripts-Local/.env", - "website": "https://community-scripts.github.io/ProxmoxVE", - "logo": "https://community-scripts.github.io/ProxmoxVE/logo.png", - "description": "A modern web-based management interface for Proxmox VE (PVE) helper scripts. This tool provides a user-friendly way to discover, download, and execute community-sourced Proxmox scripts locally with real-time terminal output streaming.", - "install_methods": [ - { - "type": "default", - "script": "ct/pve-scripts-local.sh", - "resources": { - "cpu": 2, - "ram": 4096, - "hdd": 4, - "os": "Debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] -} diff --git a/frontend/public/json/sonarqube.json b/frontend/public/json/sonarqube.json deleted file mode 100644 index d3a822f36..000000000 --- a/frontend/public/json/sonarqube.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "sonarqube", - "slug": "sonarqube", - "categories": [ - 20, - 19 - ], - "date_created": "2025-09-30", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 9000, - "documentation": "https://docs.sonarsource.com/sonarqube-server", - "config_path": "/opt/sonarqube/conf/sonar.properties", - "website": "https://www.sonarsource.com/products/sonarqube/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/sonarqube.webp", - "description": "SonarQube Server automates code quality and security reviews and provides actionable code intelligence so developers can focus on building better, faster.", - "install_methods": [ - { - "type": "default", - "script": "ct/sonarqube.sh", - "resources": { - "cpu": 4, - "ram": 6144, - "hdd": 25, - "os": "Debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": "admin", - "password": "admin" - }, - "notes": [] -} diff --git a/install/gitea-mirror-install.sh b/install/gitea-mirror-install.sh deleted file mode 100644 index 7f15bd7af..000000000 --- a/install/gitea-mirror-install.sh +++ /dev/null @@ -1,77 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: CrazyWolf13 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/RayLabsHQ/gitea-mirror - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing dependencies" -$STD apt-get install -y \ - build-essential \ - openssl \ - sqlite3 \ - unzip -msg_ok "Installed Dependencies" - -msg_info "Installing Bun" -export BUN_INSTALL=/opt/bun -curl -fsSL https://bun.sh/install | $STD bash -ln -sf /opt/bun/bin/bun /usr/local/bin/bun -ln -sf /opt/bun/bin/bun /usr/local/bin/bunx -msg_ok "Installed Bun" - -fetch_and_deploy_gh_release "gitea-mirror" "RayLabsHQ/gitea-mirror" - -msg_info "Installing gitea-mirror" -cd /opt/gitea-mirror -$STD bun run setup -$STD bun run build -msg_ok "Installed gitea-mirror" - -msg_info "Creating Services" -APP_SECRET=$(openssl rand -base64 32) -APP_VERSION=$(grep -o '"version": *"[^"]*"' package.json | cut -d'"' -f4) -HOST_IP=$(hostname -I | awk '{print $1}') -cat </opt/gitea-mirror.env -# See here for config options: https://github.com/RayLabsHQ/gitea-mirror/blob/main/docs/ENVIRONMENT_VARIABLES.md -NODE_ENV=production -HOST=0.0.0.0 -PORT=4321 -DATABASE_URL=sqlite://data/gitea-mirror.db -BETTER_AUTH_URL=http://${HOST_IP}:4321 -BETTER_AUTH_SECRET=${APP_SECRET} -npm_package_version=${APP_VERSION} -EOF - -cat </etc/systemd/system/gitea-mirror.service -[Unit] -Description=Gitea Mirror -After=network.target -[Service] -Type=simple -WorkingDirectory=/opt/gitea-mirror -ExecStart=/usr/local/bin/bun dist/server/entry.mjs -Restart=on-failure -RestartSec=10 -EnvironmentFile=/opt/gitea-mirror.env -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now gitea-mirror -msg_ok "Created Service" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" diff --git a/install/guardian-install.sh b/install/guardian-install.sh deleted file mode 100644 index 8c917a7fb..000000000 --- a/install/guardian-install.sh +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: HydroshieldMKII -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/HydroshieldMKII/Guardian - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt install -y sqlite3 -msg_ok "Installed Dependencies" - -NODE_VERSION="24" setup_nodejs -fetch_and_deploy_gh_release "guardian" "HydroshieldMKII/Guardian" "tarball" "latest" "/opt/guardian" - -msg_info "Configuring ${APPLICATION}" -cd /opt/guardian/backend -$STD npm ci -$STD npm run build -cd /opt/guardian/frontend -$STD npm ci -export DEPLOYMENT_MODE=standalone -$STD npm run build -msg_ok "Configured ${APPLICATION}" - -msg_info "Creating Service" -cat </etc/systemd/system/guardian-backend.service -[Unit] -Description=Guardian Backend -After=network.target - -[Service] -WorkingDirectory=/opt/guardian/backend -ExecStart=/usr/bin/node dist/main.js -Restart=always -RestartSec=3 -[Install] -WantedBy=multi-user.target -EOF - -cat </etc/systemd/system/guardian-frontend.service -[Unit] -Description=Guardian Frontend -After=guardian-backend.service network.target -Wants=guardian-backend.service - -[Service] -WorkingDirectory=/opt/guardian/frontend -Environment=DEPLOYMENT_MODE=standalone -ExecStart=/usr/bin/npm run start -Restart=always -RestartSec=3 -[Install] -WantedBy=multi-user.target -EOF - -systemctl enable -q --now guardian-backend -systemctl enable -q --now guardian-frontend -msg_ok "Created Service" - -motd_ssh -customize - -apt -y autoremove -apt -y autoclean -apt -y clean -msg_ok "Cleaned" diff --git a/install/jellyfin-install.sh b/install/jellyfin-install.sh deleted file mode 100644 index 05b6572db..000000000 --- a/install/jellyfin-install.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 Community-Scripts ORG -# Author: tteck (tteckster) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://jellyfin.org/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os -setup_hwaccel - -msg_info "Installing Jellyfin" -if [[ ! -d /etc/apt/keyrings ]]; then - mkdir -p /etc/apt/keyrings -fi -VERSION="$(awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release)" -curl -fsSL https://repo.jellyfin.org/jellyfin_team.gpg.key | gpg --dearmor --yes --output /etc/apt/keyrings/jellyfin.gpg -cat </etc/apt/sources.list.d/jellyfin.sources -Types: deb -URIs: https://repo.jellyfin.org/${PCT_OSTYPE} -Suites: ${VERSION} -Components: main -Architectures: amd64 -Signed-By: /etc/apt/keyrings/jellyfin.gpg -EOF -$STD apt update -$STD apt install -y jellyfin -sed -i 's/"MinimumLevel": "Information"/"MinimumLevel": "Error"/g' /etc/jellyfin/logging.json -chown -R jellyfin:adm /etc/jellyfin -sleep 10 -systemctl restart jellyfin -if [[ "$CTTYPE" == "0" ]]; then - sed -i -e 's/^ssl-cert:x:104:$/render:x:104:root,jellyfin/' -e 's/^render:x:108:root,jellyfin$/ssl-cert:x:108:/' /etc/group -else - sed -i -e 's/^ssl-cert:x:104:$/render:x:104:jellyfin/' -e 's/^render:x:108:jellyfin$/ssl-cert:x:108:/' /etc/group -fi -msg_ok "Installed Jellyfin" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt -y autoremove -$STD apt -y autoclean -$STD apt -y clean -msg_ok "Cleaned" diff --git a/install/limesurvey-install.sh b/install/limesurvey-install.sh deleted file mode 100644 index 2f619776a..000000000 --- a/install/limesurvey-install.sh +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Slaviša Arežina (tremor021) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://community.limesurvey.org/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -PHP_VERSION="8.3" PHP_APACHE="YES" PHP_FPM="YES" PHP_MODULE="imap,ldap,mysql" setup_php -setup_mariadb - -msg_info "Configuring MariaDB Database" -DB_NAME=limesurvey_db -DB_USER=limesurvey -DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13) -$STD mariadb -u root -e "CREATE DATABASE $DB_NAME CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" -$STD mariadb -u root -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';" -$STD mariadb -u root -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" -{ - echo "LimeSurvey-Credentials" - echo "LimeSurvey Database User: $DB_USER" - echo "LimeSurvey Database Password: $DB_PASS" - echo "LimeSurvey Database Name: $DB_NAME" -} >>~/limesurvey.creds -msg_ok "Configured MariaDB Database" - -msg_info "Setting up LimeSurvey" -temp_file=$(mktemp) -RELEASE=$(curl -s https://community.limesurvey.org/downloads/ | grep -oE 'https://download\.limesurvey\.org/latest-master/limesurvey[0-9.+]+\.zip' | head -n1) -curl -fsSL "$RELEASE" -o "$temp_file" -unzip -q "$temp_file" -d /opt - -cat </etc/apache2/sites-enabled/000-default.conf - - ServerAdmin webmaster@localhost - DocumentRoot /opt/limesurvey - DirectoryIndex index.php index.html index.cgi index.pl index.xhtml - Options +ExecCGI - - - Options FollowSymLinks - Require all granted - AllowOverride All - - - - Require all granted - - - ErrorLog /var/log/apache2/error.log - CustomLog /var/log/apache2/access.log combined - -EOF -chown -R www-data:www-data "/opt/limesurvey" -chmod -R 750 "/opt/limesurvey" -systemctl reload apache2 -msg_ok "Set up LimeSurvey" - -motd_ssh -customize - -msg_info "Cleaning up" -rm -rf "$temp_file" -$STD apt -y autoremove -$STD apt -y autoclean -$STD apt -y clean -msg_ok "Cleaned" diff --git a/install/nginxproxymanager-install.sh b/install/nginxproxymanager-install.sh deleted file mode 100644 index 664878130..000000000 --- a/install/nginxproxymanager-install.sh +++ /dev/null @@ -1,189 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://nginxproxymanager.com/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt update -$STD apt -y install \ - ca-certificates \ - apache2-utils \ - logrotate \ - build-essential \ - git -msg_ok "Installed Dependencies" - -msg_info "Installing Python Dependencies" -$STD apt install -y \ - python3 \ - python3-dev \ - python3-pip \ - python3-venv \ - python3-cffi -msg_ok "Installed Python Dependencies" - -msg_info "Setting up Certbot" -$STD python3 -m venv /opt/certbot -$STD /opt/certbot/bin/pip install --upgrade pip setuptools wheel -$STD /opt/certbot/bin/pip install certbot certbot-dns-cloudflare -ln -sf /opt/certbot/bin/certbot /usr/local/bin/certbot -msg_ok "Set up Certbot" - -VERSION="$(awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release)" - -msg_info "Installing Openresty" -curl -fsSL "https://openresty.org/package/pubkey.gpg" | gpg --dearmor -o /etc/apt/trusted.gpg.d/openresty-archive-keyring.gpg -case "$VERSION" in -trixie) - echo -e "deb http://openresty.org/package/debian bookworm openresty" >/etc/apt/sources.list.d/openresty.list - ;; -*) - echo -e "deb http://openresty.org/package/debian $VERSION openresty" >/etc/apt/sources.list.d/openresty.list - ;; -esac -$STD apt update -$STD apt -y install openresty -msg_ok "Installed Openresty" - -NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs - -RELEASE=$(curl -fsSL https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/releases/latest | - grep "tag_name" | - awk '{print substr($2, 3, length($2)-4) }') - -msg_info "Downloading Nginx Proxy Manager v${RELEASE}" -curl -fsSL "https://codeload.github.com/NginxProxyManager/nginx-proxy-manager/tar.gz/v${RELEASE}" | tar -xz -cd ./nginx-proxy-manager-"${RELEASE}" -msg_ok "Downloaded Nginx Proxy Manager v${RELEASE}" - -msg_info "Setting up Environment" -ln -sf /usr/bin/python3 /usr/bin/python -ln -sf /usr/local/openresty/nginx/sbin/nginx /usr/sbin/nginx -ln -sf /usr/local/openresty/nginx/ /etc/nginx -sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" backend/package.json -sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" frontend/package.json -sed -i 's+^daemon+#daemon+g' docker/rootfs/etc/nginx/nginx.conf -NGINX_CONFS=$(find "$(pwd)" -type f -name "*.conf") -for NGINX_CONF in $NGINX_CONFS; do - sed -i 's+include conf.d+include /etc/nginx/conf.d+g' "$NGINX_CONF" -done - -mkdir -p /var/www/html /etc/nginx/logs -cp -r docker/rootfs/var/www/html/* /var/www/html/ -cp -r docker/rootfs/etc/nginx/* /etc/nginx/ -cp docker/rootfs/etc/letsencrypt.ini /etc/letsencrypt.ini -cp docker/rootfs/etc/logrotate.d/nginx-proxy-manager /etc/logrotate.d/nginx-proxy-manager -ln -sf /etc/nginx/nginx.conf /etc/nginx/conf/nginx.conf -rm -f /etc/nginx/conf.d/dev.conf - -mkdir -p /tmp/nginx/body \ - /run/nginx \ - /data/nginx \ - /data/custom_ssl \ - /data/logs \ - /data/access \ - /data/nginx/default_host \ - /data/nginx/default_www \ - /data/nginx/proxy_host \ - /data/nginx/redirection_host \ - /data/nginx/stream \ - /data/nginx/dead_host \ - /data/nginx/temp \ - /var/lib/nginx/cache/public \ - /var/lib/nginx/cache/private \ - /var/cache/nginx/proxy_temp - -chmod -R 777 /var/cache/nginx -chown root /tmp/nginx - -echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" {print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf);" >/etc/nginx/conf.d/include/resolvers.conf - -if [ ! -f /data/nginx/dummycert.pem ] || [ ! -f /data/nginx/dummykey.pem ]; then - openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 -subj "/O=Nginx Proxy Manager/OU=Dummy Certificate/CN=localhost" -keyout /data/nginx/dummykey.pem -out /data/nginx/dummycert.pem &>/dev/null -fi - -mkdir -p /app/global /app/frontend/images -cp -r backend/* /app -cp -r global/* /app/global -msg_ok "Set up Environment" - -msg_info "Building Frontend" -cd ./frontend -export NODE_OPTIONS="--openssl-legacy-provider" -# Replace node-sass with sass in package.json before installation -sed -i 's/"node-sass".*$/"sass": "^1.92.1",/g' package.json -$STD yarn install --network-timeout 600000 -$STD yarn build -cp -r dist/* /app/frontend -cp -r app-images/* /app/frontend/images -msg_ok "Built Frontend" - -msg_info "Initializing Backend" -rm -rf /app/config/default.json -if [ ! -f /app/config/production.json ]; then - cat <<'EOF' >/app/config/production.json -{ - "database": { - "engine": "knex-native", - "knex": { - "client": "sqlite3", - "connection": { - "filename": "/data/database.sqlite" - } - } - } -} -EOF -fi -cd /app -export NODE_OPTIONS="--openssl-legacy-provider" -$STD yarn install --network-timeout 600000 -msg_ok "Initialized Backend" - -msg_info "Creating Service" -cat <<'EOF' >/lib/systemd/system/npm.service -[Unit] -Description=Nginx Proxy Manager -After=network.target -Wants=openresty.service - -[Service] -Type=simple -Environment=NODE_ENV=production -ExecStartPre=-mkdir -p /tmp/nginx/body /data/letsencrypt-acme-challenge -ExecStart=/usr/bin/node index.js --abort_on_uncaught_exception --max_old_space_size=250 -WorkingDirectory=/app -Restart=on-failure - -[Install] -WantedBy=multi-user.target -EOF -msg_ok "Created Service" - -motd_ssh -customize - -msg_info "Starting Services" -sed -i 's/user npm/user root/g; s/^pid/#pid/g' /usr/local/openresty/nginx/conf/nginx.conf -sed -r -i 's/^([[:space:]]*)su npm npm/\1#su npm npm/g;' /etc/logrotate.d/nginx-proxy-manager -systemctl enable -q --now openresty -systemctl enable -q --now npm -msg_ok "Started Services" - -msg_info "Cleaning up" -rm -rf ../nginx-proxy-manager-* -systemctl restart openresty -$STD apt -y autoremove -$STD apt -y autoclean -$STD apt -y clean -msg_ok "Cleaned" diff --git a/install/open-archiver-install.sh b/install/open-archiver-install.sh deleted file mode 100644 index 628ede760..000000000 --- a/install/open-archiver-install.sh +++ /dev/null @@ -1,124 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Slaviša Arežina (tremor021) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://openarchiver.com/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing dependendencies" -$STD apt install -y valkey -msg_ok "Installed dependendencies" - -NODE_VERSION="22" NODE_MODULE="pnpm" setup_nodejs -PG_VERSION="17" setup_postgresql -fetch_and_deploy_gh_release "meilisearch" "meilisearch/meilisearch" "binary" -fetch_and_deploy_gh_release "openarchiver" "LogicLabs-OU/OpenArchiver" "tarball" -JWT_KEY="$(openssl rand -hex 32)" -SECRET_KEY="$(openssl rand -hex 32)" -IP_ADDR=$(hostname -I | awk '{print $1}') - -msg_info "Setting up PostgreSQL" -DB_NAME="openarchiver_db" -DB_USER="openarchiver" -DB_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-18)" -$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" -$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" -{ - echo "Open Archiver DB Credentials" - echo "Database Name: $DB_NAME" - echo "Database User: $DB_USER" - echo "Database Password: $DB_PASS" -} >>~/openarchiver.creds -msg_ok "Set up PostgreSQL" - -msg_info "Configuring MeiliSearch" -curl -fsSL https://raw.githubusercontent.com/meilisearch/meilisearch/latest/config.toml -o /etc/meilisearch.toml -MASTER_KEY=$(openssl rand -base64 12) -sed -i \ - -e 's|^env =.*|env = "production"|' \ - -e "s|^# master_key =.*|master_key = \"$MASTER_KEY\"|" \ - -e 's|^db_path =.*|db_path = "/var/lib/meilisearch/data"|' \ - -e 's|^dump_dir =.*|dump_dir = "/var/lib/meilisearch/dumps"|' \ - -e 's|^snapshot_dir =.*|snapshot_dir = "/var/lib/meilisearch/snapshots"|' \ - -e 's|^# no_analytics = true|no_analytics = true|' \ - -e 's|^http_addr =.*|http_addr = "127.0.0.1:7700"|' \ - /etc/meilisearch.toml - -cat </etc/systemd/system/meilisearch.service -[Unit] -Description=Meilisearch -After=network.target - -[Service] -ExecStart=/usr/bin/meilisearch --config-file-path /etc/meilisearch.toml -Restart=always - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now meilisearch -sleep 5 -msg_ok "Configured MeiliSearch" - -msg_info "Setting up Open Archiver" -mkdir -p /opt/openarchiver-data -cd /opt/openarchiver -cp .env.example .env -sed -i "s|^NODE_ENV=.*|NODE_ENV=production|g" /opt/openarchiver/.env -sed -i "s|^POSTGRES_DB=.*|POSTGRES_DB=openarchiver_db|g" /opt/openarchiver/.env -sed -i "s|^POSTGRES_USER=.*|POSTGRES_USER=openarchiver|g" /opt/openarchiver/.env -sed -i "s|^POSTGRES_PASSWORD=.*|POSTGRES_PASSWORD=$DB_PASS|g" /opt/openarchiver/.env -sed -i "s|^DATABASE_URL=.*|DATABASE_URL=\"postgresql://openarchiver:$DB_PASS@localhost:5432/openarchiver_db\"|g" /opt/openarchiver/.env -sed -i "s|^MEILI_HOST=.*|MEILI_HOST=http://localhost:7700|g" /opt/openarchiver/.env -sed -i "s|^MEILI_MASTER_KEY=.*|MEILI_MASTER_KEY=$MASTER_KEY|g" /opt/openarchiver/.env -sed -i "s|^REDIS_HOST=.*|REDIS_HOST=localhost|g" /opt/openarchiver/.env -sed -i "s|^REDIS_PASSWORD=.*|REDIS_PASSWORD=|g" /opt/openarchiver/.env -sed -i "s|^STORAGE_LOCAL_ROOT_PATH=.*|STORAGE_LOCAL_ROOT_PATH=/opt/openarchiver-data|g" /opt/openarchiver/.env -sed -i "s|^JWT_SECRET=.*|JWT_SECRET=$JWT_KEY|g" /opt/openarchiver/.env -sed -i "s|^ENCRYPTION_KEY=.*|ENCRYPTION_KEY=$SECRET_KEY|g" /opt/openarchiver/.env -sed -i "s|^TIKA_URL=.*|TIKA_URL=|g" /opt/openarchiver/.env -echo "ORIGIN=http://$IP_ADDR:3000" >> /opt/openarchiver/.env -$STD pnpm install --shamefully-hoist --frozen-lockfile --prod=false -$STD pnpm build -$STD pnpm db:migrate -msg_ok "Setup Open Archiver" - -msg_info "Creating Service" -cat </etc/systemd/system/openarchiver.service -[Unit] -Description=Open Archiver Service -After=network-online.target - -[Service] -Type=simple -User=root -EnvironmentFile=/opt/openarchiver/.env -WorkingDirectory=/opt/openarchiver -ExecStart=/usr/bin/pnpm docker-start -Restart=on-failure - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now openarchiver -msg_ok "Created Service" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt -y autoremove -$STD apt -y autoclean -$STD apt -y clean -msg_ok "Cleaned" diff --git a/install/palmr-install.sh b/install/palmr-install.sh deleted file mode 100644 index 9833ed5d8..000000000 --- a/install/palmr-install.sh +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2025 Community Scripts ORG -# Author: vhsdream -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/kyantech/Palmr - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -fetch_and_deploy_gh_release "Palmr" "kyantech/Palmr" "tarball" "latest" "/opt/palmr" -PNPM="$(jq -r '.packageManager' /opt/palmr/package.json)" -NODE_VERSION="20" NODE_MODULE="$PNPM" setup_nodejs - -msg_info "Configuring palmr backend" -PALMR_DIR="/opt/palmr_data" -mkdir -p "$PALMR_DIR" -PALMR_DB="${PALMR_DIR}/palmr.db" -PALMR_KEY="$(openssl rand -hex 32)" -cd /opt/palmr/apps/server -sed -e 's/_ENCRYPTION=true/_ENCRYPTION=false/' \ - -e '/^# ENC/s/# //' \ - -e "s/ENCRYPTION_KEY=.*$/ENCRYPTION_KEY=$PALMR_KEY/" \ - -e "s|file:.*$|file:$PALMR_DB\"|" \ - -e "\|db\"$|a\\# Uncomment below when using a reverse proxy\\ -# SECURE_SITE=true\\ -# Uncomment and add your path if using symlinks for data storage\\ -# CUSTOM_PATH=" \ - .env.example >./.env -$STD pnpm install -$STD npx prisma generate -$STD npx prisma migrate deploy -$STD npx prisma db push -$STD pnpm db:seed -$STD pnpm build -msg_ok "Configured palmr backend" - -msg_info "Configuring palmr frontend" -cd /opt/palmr/apps/web -mv ./.env.example ./.env -export NODE_ENV=production -export NEXT_TELEMETRY_DISABLED=1 -$STD pnpm install -$STD pnpm build -msg_ok "Configured palmr frontend" - -msg_info "Creating service" -useradd -d "$PALMR_DIR" -M -s /usr/sbin/nologin -U palmr -chown -R palmr:palmr "$PALMR_DIR" /opt/palmr -cat </etc/systemd/system/palmr-backend.service -[Unit] -Description=palmr Backend Service -After=network.target - -[Service] -Type=simple -User=palmr -Group=palmr -WorkingDirectory=/opt/palmr_data -ExecStart=/usr/bin/node /opt/palmr/apps/server/dist/server.js - -[Install] -WantedBy=multi-user.target -EOF - -cat </etc/systemd/system/palmr-frontend.service -[Unit] -Description=palmr Frontend Service -After=network.target palmr-backend.service - -[Service] -Type=simple -User=palmr -Group=palmr -WorkingDirectory=/opt/palmr/apps/web -ExecStart=/usr/bin/pnpm start - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now palmr-backend palmr-frontend -msg_ok "Created service" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" diff --git a/install/plex-install.sh b/install/plex-install.sh deleted file mode 100644 index d04d20c7a..000000000 --- a/install/plex-install.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://www.plex.tv/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Setting Up Hardware Acceleration" -$STD apt -y install va-driver-all ocl-icd-libopencl1 intel-opencl-icd vainfo intel-gpu-tools -if [[ "$CTTYPE" == "0" ]]; then - chgrp video /dev/dri - chmod 755 /dev/dri - chmod 660 /dev/dri/* - $STD adduser $(id -u -n) video - $STD adduser $(id -u -n) render -fi -msg_ok "Set Up Hardware Acceleration" - -msg_info "Setting Up Plex Media Server Repository" -curl -fsSL https://downloads.plex.tv/plex-keys/PlexSign.key | tee /usr/share/keyrings/PlexSign.asc >/dev/null -cat </etc/apt/sources.list.d/plexmediaserver.sources -Types: deb -URIs: https://downloads.plex.tv/repo/deb/ -Suites: public -Components: main -Signed-By: /usr/share/keyrings/PlexSign.asc -EOF -msg_ok "Set Up Plex Media Server Repository" - -msg_info "Installing Plex Media Server" -$STD apt update -$STD apt -y -o Dpkg::Options::="--force-confold" install plexmediaserver -if [[ "$CTTYPE" == "0" ]]; then - sed -i -e 's/^ssl-cert:x:104:plex$/render:x:104:root,plex/' -e 's/^render:x:108:root$/ssl-cert:x:108:plex/' /etc/group -else - sed -i -e 's/^ssl-cert:x:104:plex$/render:x:104:plex/' -e 's/^render:x:108:$/ssl-cert:x:108:/' /etc/group -fi -msg_ok "Installed Plex Media Server" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt -y autoremove -$STD apt -y autoclean -$STD apt -y clean -msg_ok "Cleaned" diff --git a/install/prometheus-blackbox-exporter-install.sh b/install/prometheus-blackbox-exporter-install.sh deleted file mode 100644 index be95d9645..000000000 --- a/install/prometheus-blackbox-exporter-install.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Marfnl -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/prometheus/blackbox_exporter - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -fetch_and_deploy_gh_release "blackbox-exporter" "prometheus/blackbox_exporter" "prebuild" "latest" "/opt/blackbox-exporter" "blackbox_exporter-*.linux-amd64.tar.gz" - -msg_info "Creating Service" -cat </etc/systemd/system/blackbox-exporter.service -[Unit] -Description=Blackbox Exporter Service -After=network.target - -[Service] -Type=simple -WorkingDirectory=/opt/blackbox-exporter -ExecStart=/opt/blackbox-exporter/blackbox_exporter -Restart=on-failure - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now blackbox-exporter -msg_ok "Service Created" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" diff --git a/install/pve-scripts-local-install.sh b/install/pve-scripts-local-install.sh deleted file mode 100644 index 368fb3087..000000000 --- a/install/pve-scripts-local-install.sh +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: michelroegl-brunner -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" - $STD apt-get update - $STD apt-get install -y \ - build-essential \ - sshpass \ - rsync \ - expect -msg_ok "Dependencies installed." - -NODE_VERSION=22 setup_nodejs -fetch_and_deploy_gh_release "ProxmoxVE-Local" "community-scripts/ProxmoxVE-Local" - -msg_info "Installing PVE Scripts local" -cd /opt/ProxmoxVE-Local -$STD npm install -cp .env.example .env -mkdir -p data -chmod 755 data -$STD npm run build -msg_ok "Installed PVE Scripts local" - -msg_info "Creating Service" -cat </etc/systemd/system/pvescriptslocal.service -[Unit] -Description=PVEScriptslocal Service -After=network.target - -[Service] -WorkingDirectory=/opt/ProxmoxVE-Local -ExecStart=/usr/bin/npm start -Restart=always -RestartSec=10 -Environment=NODE_ENV=production -User=root - -[Install] -WantedBy=multi-user.target -EOF - -systemctl enable -q --now pvescriptslocal -msg_ok "Created Service" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt -y autoremove -$STD apt -y autoclean -$STD apt -y clean -msg_ok "Cleaned" diff --git a/install/sonarqube-install.sh b/install/sonarqube-install.sh deleted file mode 100644 index 57211e967..000000000 --- a/install/sonarqube-install.sh +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/env bash -# Copyright (c) 2021-2025 community-scripts ORG -# Author: prop4n -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://docs.sonarsource.com/sonarqube-server - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -JAVA_VERSION="21" setup_java -PG_VERSION="17" setup_postgresql - -msg_info "Installing Postgresql" -DB_NAME="sonarqube" -DB_USER="sonarqube" -DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13) -$STD sudo -u postgres psql -c "CREATE USER $DB_USER WITH PASSWORD '$DB_PASS';" -$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME OWNER $DB_USER;" -{ - echo "Application Credentials" - echo "DB_NAME: $DB_NAME" - echo "DB_USER: $DB_USER" - echo "DB_PASS: $DB_PASS" -} >>~/sonarqube.creds -msg_ok "Installed PostgreSQL" - -msg_info "Configuring SonarQube" -temp_file=$(mktemp) -RELEASE=$(curl -fsSL https://api.github.com/repos/SonarSource/sonarqube/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') -curl -fsSL "https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-${RELEASE}.zip" -o $temp_file -unzip -q "$temp_file" -d /opt -mv /opt/sonarqube-* /opt/sonarqube -$STD useradd -r -m -U -d /opt/sonarqube -s /bin/bash sonarqube -chown -R sonarqube:sonarqube /opt/sonarqube -chmod -R 755 /opt/sonarqube -mkdir -p /opt/sonarqube/conf -cat </opt/sonarqube/conf/sonar.properties -sonar.jdbc.username=${DB_USER} -sonar.jdbc.password=${DB_PASS} -sonar.jdbc.url=jdbc:postgresql://localhost/${DB_NAME} -sonar.web.host=0.0.0.0 -sonar.web.port=9000 -EOF -chmod +x /opt/sonarqube/bin/linux-x86-64/sonar.sh -echo ${RELEASE} >>~/.sonarqube -msg_ok "Configured SonarQube" - -msg_info "Creating Service" -cat </etc/systemd/system/sonarqube.service -[Unit] -Description=SonarQube service -After=postgresql.service - -[Service] -Type=forking -ExecStart=/opt/sonarqube/bin/linux-x86-64/sonar.sh start -ExecStop=/opt/sonarqube/bin/linux-x86-64/sonar.sh stop -User=sonarqube -Group=sonarqube -Restart=on-failure -LimitNOFILE=131072 -LimitNPROC=8192 - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now sonarqube -msg_ok "Service Created" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" From e6e9e6dfbd6dd74487e2eb7a918b38613661d96e Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 22 Oct 2025 06:41:48 +0000 Subject: [PATCH 1514/1733] Update .app files --- ct/headers/comfyui | 6 ++++++ ct/headers/gitea-mirror | 6 ------ ct/headers/guardian | 6 ------ ct/headers/limesurvey | 6 ------ ct/headers/nginxproxymanager | 6 ------ ct/headers/open-archiver | 6 ------ ct/headers/palmr | 6 ------ ct/headers/plex | 6 ------ ct/headers/prometheus-blackbox-exporter | 6 ------ ct/headers/proxmox-datacenter-manager | 6 ------ ct/headers/pve-scripts-local | 6 ------ ct/headers/sonarqube | 6 ------ 12 files changed, 6 insertions(+), 66 deletions(-) create mode 100644 ct/headers/comfyui delete mode 100644 ct/headers/gitea-mirror delete mode 100644 ct/headers/guardian delete mode 100644 ct/headers/limesurvey delete mode 100644 ct/headers/nginxproxymanager delete mode 100644 ct/headers/open-archiver delete mode 100644 ct/headers/palmr delete mode 100644 ct/headers/plex delete mode 100644 ct/headers/prometheus-blackbox-exporter delete mode 100644 ct/headers/proxmox-datacenter-manager delete mode 100644 ct/headers/pve-scripts-local delete mode 100644 ct/headers/sonarqube diff --git a/ct/headers/comfyui b/ct/headers/comfyui new file mode 100644 index 000000000..5f3bfd60a --- /dev/null +++ b/ct/headers/comfyui @@ -0,0 +1,6 @@ + ______ ____ __ ______ + / ____/___ ____ ___ / __/_ __/ / / / _/ + / / / __ \/ __ `__ \/ /_/ / / / / / // / +/ /___/ /_/ / / / / / / __/ /_/ / /_/ // / +\____/\____/_/ /_/ /_/_/ \__, /\____/___/ + /____/ diff --git a/ct/headers/gitea-mirror b/ct/headers/gitea-mirror deleted file mode 100644 index 57003b058..000000000 --- a/ct/headers/gitea-mirror +++ /dev/null @@ -1,6 +0,0 @@ - _ __ _ - ____ _(_) /____ ____ _ ____ ___ (_)_____________ _____ - / __ `/ / __/ _ \/ __ `/_____/ __ `__ \/ / ___/ ___/ __ \/ ___/ - / /_/ / / /_/ __/ /_/ /_____/ / / / / / / / / / / /_/ / / - \__, /_/\__/\___/\__,_/ /_/ /_/ /_/_/_/ /_/ \____/_/ -/____/ diff --git a/ct/headers/guardian b/ct/headers/guardian deleted file mode 100644 index e19ee5708..000000000 --- a/ct/headers/guardian +++ /dev/null @@ -1,6 +0,0 @@ - ______ ___ - / ____/_ ______ __________/ (_)___ _____ - / / __/ / / / __ `/ ___/ __ / / __ `/ __ \ -/ /_/ / /_/ / /_/ / / / /_/ / / /_/ / / / / -\____/\__,_/\__,_/_/ \__,_/_/\__,_/_/ /_/ - diff --git a/ct/headers/limesurvey b/ct/headers/limesurvey deleted file mode 100644 index 9d071d0f1..000000000 --- a/ct/headers/limesurvey +++ /dev/null @@ -1,6 +0,0 @@ - __ _ _____ - / / (_)___ ___ ___ / ___/__ ________ _____ __ __ - / / / / __ `__ \/ _ \\__ \/ / / / ___/ | / / _ \/ / / / - / /___/ / / / / / / __/__/ / /_/ / / | |/ / __/ /_/ / -/_____/_/_/ /_/ /_/\___/____/\__,_/_/ |___/\___/\__, / - /____/ diff --git a/ct/headers/nginxproxymanager b/ct/headers/nginxproxymanager deleted file mode 100644 index d68d0c9d8..000000000 --- a/ct/headers/nginxproxymanager +++ /dev/null @@ -1,6 +0,0 @@ - _ __ _ ____ __ ___ - / | / /___ _(_)___ _ __ / __ \_________ _ ____ __ / |/ /___ _____ ____ _____ ____ _____ - / |/ / __ `/ / __ \| |/_/ / /_/ / ___/ __ \| |/_/ / / / / /|_/ / __ `/ __ \/ __ `/ __ `/ _ \/ ___/ - / /| / /_/ / / / / /> < / ____/ / / /_/ /> < -/_/ /_/\___/_/|_| - diff --git a/ct/headers/prometheus-blackbox-exporter b/ct/headers/prometheus-blackbox-exporter deleted file mode 100644 index 936f051d3..000000000 --- a/ct/headers/prometheus-blackbox-exporter +++ /dev/null @@ -1,6 +0,0 @@ - ____ __ __ ____ __ __ __ ______ __ - / __ \_________ ____ ___ ___ / /_/ /_ ___ __ _______ / __ )/ /___ ______/ /__/ /_ ____ _ __ / ____/ ______ ____ _____/ /____ _____ - / /_/ / ___/ __ \/ __ `__ \/ _ \/ __/ __ \/ _ \/ / / / ___/_____/ __ / / __ `/ ___/ //_/ __ \/ __ \| |/_/_____/ __/ | |/_/ __ \/ __ \/ ___/ __/ _ \/ ___/ - / ____/ / / /_/ / / / / / / __/ /_/ / / / __/ /_/ (__ )_____/ /_/ / / /_/ / /__/ ,< / /_/ / /_/ /> Date: Wed, 22 Oct 2025 08:42:57 +0200 Subject: [PATCH 1515/1733] Add config_path to comfyui.json --- frontend/public/json/comfyui.json | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/public/json/comfyui.json b/frontend/public/json/comfyui.json index 6c9687f94..95e3bd381 100644 --- a/frontend/public/json/comfyui.json +++ b/frontend/public/json/comfyui.json @@ -8,6 +8,7 @@ "type": "ct", "updateable": true, "privileged": false, + "config_path": "/opt", "interface_port": 8188, "documentation": "https://github.com/comfyanonymous/ComfyUI", "website": "https://www.comfy.org/", From 0cb853939c59dd61878a0848f7d502e9bdb2aaaa Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 22 Oct 2025 09:33:46 +0200 Subject: [PATCH 1516/1733] readd gitea-mirror This script manages the Gitea Mirror application, including installation, updates, and environment configuration. It handles version checks, service management, and data backup/restoration. --- ct/gitea-mirror.sh | 131 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 ct/gitea-mirror.sh diff --git a/ct/gitea-mirror.sh b/ct/gitea-mirror.sh new file mode 100644 index 000000000..4c5fca198 --- /dev/null +++ b/ct/gitea-mirror.sh @@ -0,0 +1,131 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: CrazyWolf13 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/RayLabsHQ/gitea-mirror + +APP="gitea-mirror" +var_tags="${var_tags:-mirror;gitea}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-6}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" +var_app_version="${var_app_version:-latest}" + +header_info "$APP" + +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/gitea-mirror ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + APP_VERSION=$(grep -o '"version": *"[^"]*"' /opt/gitea-mirror/package.json | cut -d'"' -f4) + if [[ $APP_VERSION =~ ^2\. ]]; then + if ! whiptail --backtitle "Gitea Mirror Update" --title "⚠️ VERSION 2.x DETECTED" --yesno \ + "WARNING: Version $APP_VERSION detected!\n\nUpdating from version 2.x will CLEAR ALL CONFIGURATION.\n\nThis includes:\n• API tokens\n• User settings\n• Repository configurations\n• All custom settings\n\nDo you want to continue with the update process?" 15 70 --defaultno; then + exit 0 + fi + + if ! whiptail --backtitle "Gitea Mirror Update" --title "⚠️ FINAL CONFIRMATION" --yesno \ + "FINAL WARNING: This update WILL clear all configuration!\n\nBEFORE PROCEEDING, please:\n\n• Copy API tokens to a safe location\n• Backup any custom configurations\n• Note down repository settings\n\nThis action CANNOT be undone!" 18 70 --defaultno; then + whiptail --backtitle "Gitea Mirror Update" --title "Update Cancelled" --msgbox "Update process cancelled. Please backup your configuration before proceeding." 8 60 + exit 0 + fi + whiptail --backtitle "Gitea Mirror Update" --title "Proceeding with Update" --msgbox \ + "Proceeding with version $APP_VERSION update.\n\nAll configuration will be cleared as warned." 8 50 + rm -rf /opt/gitea-mirror + fi + + if [[ ! -f /opt/gitea-mirror.env ]]; then + msg_info "Detected old Enviroment, updating files" + APP_SECRET=$(openssl rand -base64 32) + HOST_IP=$(hostname -I | awk '{print $1}') + cat </opt/gitea-mirror.env +# See here for config options: https://github.com/RayLabsHQ/gitea-mirror/blob/main/docs/ENVIRONMENT_VARIABLES.md +NODE_ENV=production +HOST=0.0.0.0 +PORT=4321 +DATABASE_URL=sqlite://data/gitea-mirror.db +BETTER_AUTH_URL=http://${HOST_IP}:4321 +BETTER_AUTH_SECRET=${APP_SECRET} +npm_package_version=${APP_VERSION} +EOF + rm /etc/systemd/system/gitea-mirror.service + cat </etc/systemd/system/gitea-mirror.service +[Unit] +Description=Gitea Mirror +After=network.target +[Service] +Type=simple +WorkingDirectory=/opt/gitea-mirror +ExecStart=/usr/local/bin/bun dist/server/entry.mjs +Restart=on-failure +RestartSec=10 +EnvironmentFile=/opt/gitea-mirror.env +[Install] +WantedBy=multi-user.target +EOF + systemctl daemon-reload + msg_ok "Old Enviroment fixed" +fi + + if check_for_gh_release "gitea-mirror" "RayLabsHQ/gitea-mirror"; then + msg_info "Stopping Services" + systemctl stop gitea-mirror + msg_ok "Services Stopped" + + msg_info "Backup Data" + mkdir -p /opt/gitea-mirror-backup/data + cp /opt/gitea-mirror/data/* /opt/gitea-mirror-backup/data/ + msg_ok "Backup Data" + + msg_info "Installing Bun" + export BUN_INSTALL=/opt/bun + curl -fsSL https://bun.sh/install | $STD bash + ln -sf /opt/bun/bin/bun /usr/local/bin/bun + ln -sf /opt/bun/bin/bun /usr/local/bin/bunx + msg_ok "Installed Bun" + + rm -rf /opt/gitea-mirror + fetch_and_deploy_gh_release "gitea-mirror" "RayLabsHQ/gitea-mirror" "tarball" $var_app_version + + msg_info "Updating and rebuilding ${APP}" + cd /opt/gitea-mirror + $STD bun run setup + $STD bun run build + APP_VERSION=$(grep -o '"version": *"[^"]*"' package.json | cut -d'"' -f4) + + sudo sed -i.bak "s|^npm_package_version=.*|npm_package_version=${APP_VERSION}|" /opt/gitea-mirror.env + msg_ok "Updated and rebuilt ${APP}" + + msg_info "Restoring Data" + cp /opt/gitea-mirror-backup/data/* /opt/gitea-mirror/data + msg_ok "Restored Data" + + msg_info "Starting Service" + systemctl start gitea-mirror + msg_ok "Service Started" + msg_ok "Update Successfully" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:4321${CL}" From d51e669a9d6f7044aa71d76c7a231f9076f67c6c Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 22 Oct 2025 09:34:19 +0200 Subject: [PATCH 1517/1733] readd: gitea-mirror --- install/gitea-mirror-install.sh | 77 +++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 install/gitea-mirror-install.sh diff --git a/install/gitea-mirror-install.sh b/install/gitea-mirror-install.sh new file mode 100644 index 000000000..7f15bd7af --- /dev/null +++ b/install/gitea-mirror-install.sh @@ -0,0 +1,77 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: CrazyWolf13 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/RayLabsHQ/gitea-mirror + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing dependencies" +$STD apt-get install -y \ + build-essential \ + openssl \ + sqlite3 \ + unzip +msg_ok "Installed Dependencies" + +msg_info "Installing Bun" +export BUN_INSTALL=/opt/bun +curl -fsSL https://bun.sh/install | $STD bash +ln -sf /opt/bun/bin/bun /usr/local/bin/bun +ln -sf /opt/bun/bin/bun /usr/local/bin/bunx +msg_ok "Installed Bun" + +fetch_and_deploy_gh_release "gitea-mirror" "RayLabsHQ/gitea-mirror" + +msg_info "Installing gitea-mirror" +cd /opt/gitea-mirror +$STD bun run setup +$STD bun run build +msg_ok "Installed gitea-mirror" + +msg_info "Creating Services" +APP_SECRET=$(openssl rand -base64 32) +APP_VERSION=$(grep -o '"version": *"[^"]*"' package.json | cut -d'"' -f4) +HOST_IP=$(hostname -I | awk '{print $1}') +cat </opt/gitea-mirror.env +# See here for config options: https://github.com/RayLabsHQ/gitea-mirror/blob/main/docs/ENVIRONMENT_VARIABLES.md +NODE_ENV=production +HOST=0.0.0.0 +PORT=4321 +DATABASE_URL=sqlite://data/gitea-mirror.db +BETTER_AUTH_URL=http://${HOST_IP}:4321 +BETTER_AUTH_SECRET=${APP_SECRET} +npm_package_version=${APP_VERSION} +EOF + +cat </etc/systemd/system/gitea-mirror.service +[Unit] +Description=Gitea Mirror +After=network.target +[Service] +Type=simple +WorkingDirectory=/opt/gitea-mirror +ExecStart=/usr/local/bin/bun dist/server/entry.mjs +Restart=on-failure +RestartSec=10 +EnvironmentFile=/opt/gitea-mirror.env +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now gitea-mirror +msg_ok "Created Service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From 61e3721c1d9afefee5a6a7c5be34f6553befda8b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 22 Oct 2025 13:52:17 +0200 Subject: [PATCH 1518/1733] Cleanup MSG's --- misc/build.func | 145 +++++++++++++++++++++++++----------------------- misc/tools.func | 94 ++++++++++--------------------- 2 files changed, 105 insertions(+), 134 deletions(-) diff --git a/misc/build.func b/misc/build.func index 330c0b90a..016279e06 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2988,82 +2988,83 @@ create_lxc_container() { # ------------------------------------------------------------------------------ # Template discovery & validation - # Simple approach: var_os (alpine/ubuntu/debian) + var_version (24.04/11/12/13/22.04) - # 1. Check local storage - # 2. Check for newer version in pveam - # 3. If not found at all, offer alternatives # ------------------------------------------------------------------------------ - msg_info "Finding template for ${PCT_OSTYPE} ${PCT_OSVERSION}" - - # Determine template pattern based on OS + TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" case "$PCT_OSTYPE" in debian | ubuntu) TEMPLATE_PATTERN="-standard_" ;; alpine | fedora | rocky | centos) TEMPLATE_PATTERN="-default_" ;; - *) TEMPLATE_PATTERN="-" ;; + *) TEMPLATE_PATTERN="" ;; esac - # Step 1: Search in local storage - LOCAL_TEMPLATE=$(pveam list "$TEMPLATE_STORAGE" 2>/dev/null | awk '{print $NF}' | grep "^${PCT_OSTYPE}-${PCT_OSVERSION}.*${TEMPLATE_PATTERN}" | sort -V | tail -1) + msg_info "Searching for template '$TEMPLATE_SEARCH'" - # Step 2: Check pveam for available version (could be newer) - ONLINE_TEMPLATE=$(pveam available -section system 2>/dev/null | awk '{print $NF}' | grep "^${PCT_OSTYPE}-${PCT_OSVERSION}.*${TEMPLATE_PATTERN}" | sort -V | tail -1) + # Build regex patterns outside awk/grep for clarity + SEARCH_PATTERN="^${TEMPLATE_SEARCH}-" - # Decide what to use - if [[ -n "$LOCAL_TEMPLATE" && -n "$ONLINE_TEMPLATE" ]]; then - # Both exist - check if online is newer - if [[ "$LOCAL_TEMPLATE" != "$ONLINE_TEMPLATE" ]]; then - msg_info "Local template: $LOCAL_TEMPLATE" - msg_info "Newer version available: $ONLINE_TEMPLATE" - echo "" - read -p "Download newer version? [y/N]: " answer - case "${answer,,}" in - y | yes) - TEMPLATE="$ONLINE_TEMPLATE" - TEMPLATE_SOURCE="online" - msg_ok "Using newer version: $TEMPLATE" - ;; - *) - TEMPLATE="$LOCAL_TEMPLATE" - TEMPLATE_SOURCE="local" - msg_ok "Using local version: $TEMPLATE" - ;; - esac - else - # Same version - TEMPLATE="$LOCAL_TEMPLATE" - TEMPLATE_SOURCE="local" - msg_ok "Using local template: $TEMPLATE" - fi - elif [[ -n "$LOCAL_TEMPLATE" ]]; then - # Only local exists - TEMPLATE="$LOCAL_TEMPLATE" + echo "[DEBUG] TEMPLATE_SEARCH='$TEMPLATE_SEARCH'" + echo "[DEBUG] SEARCH_PATTERN='$SEARCH_PATTERN'" + echo "[DEBUG] TEMPLATE_PATTERN='$TEMPLATE_PATTERN'" + + mapfile -t LOCAL_TEMPLATES < <( + pveam list "$TEMPLATE_STORAGE" 2>/dev/null | + awk -v search="${SEARCH_PATTERN}" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' | + sed 's|.*/||' | sort -t - -k 2 -V + ) + + pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)." + + echo "[DEBUG] pveam available output (first 5 lines with .tar files):" + pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | head -5 | sed 's/^/ /' + + mapfile -t ONLINE_TEMPLATES \ + \ + echo "[DEBUG] After filtering: ${#ONLINE_TEMPLATES[@]} online templates found" < <(pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | awk -F'\t' '{print $1}' | grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V 2>/dev/null || true) + if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then + echo "[DEBUG] Online templates:" + for tmpl in "${ONLINE_TEMPLATES[@]}"; do + echo " - $tmpl" + done + fi + + ONLINE_TEMPLATE="" + [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" + + msg_debug "SEARCH_PATTERN='${SEARCH_PATTERN}' TEMPLATE_PATTERN='${TEMPLATE_PATTERN}'" + msg_debug "Found ${#LOCAL_TEMPLATES[@]} local templates, ${#ONLINE_TEMPLATES[@]} online templates" + if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then + msg_debug "First 3 online templates:" + for i in {0..2}; do + [[ -n "${ONLINE_TEMPLATES[$i]}" ]] && msg_debug " [$i]: ${ONLINE_TEMPLATES[$i]}" + done + fi + msg_debug "ONLINE_TEMPLATE='$ONLINE_TEMPLATE'" + + if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then + TEMPLATE="${LOCAL_TEMPLATES[-1]}" TEMPLATE_SOURCE="local" - msg_ok "Using local template: $TEMPLATE" - elif [[ -n "$ONLINE_TEMPLATE" ]]; then - # Only online exists + else TEMPLATE="$ONLINE_TEMPLATE" TEMPLATE_SOURCE="online" - msg_ok "Template found online: $TEMPLATE" - else - # Nothing found - offer alternatives - msg_error "Template not found for ${PCT_OSTYPE} ${PCT_OSVERSION}" - echo "" - echo "This could mean:" - echo " • The version is not yet available in the Proxmox template repository" - echo " • Your Proxmox VE might need an update to access newer templates" - echo " • The version number might be incorrect" + fi + # If still no template, try to find alternatives + if [[ -z "$TEMPLATE" ]]; then + echo "" + echo "[DEBUG] No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}, searching for alternatives..." + + # Get all available versions for this OS type mapfile -t AVAILABLE_VERSIONS < <( pveam available -section system 2>/dev/null | - awk '{print $NF}' | + grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | + awk -F'\t' '{print $1}' | grep "^${PCT_OSTYPE}-" | sed -E "s/.*${PCT_OSTYPE}-([0-9]+(\.[0-9]+)?).*/\1/" | - sort -u -V + sort -u -V 2>/dev/null ) if [[ ${#AVAILABLE_VERSIONS[@]} -gt 0 ]]; then echo "" - msg_info "Available ${PCT_OSTYPE} versions:" + echo "${BL}Available ${PCT_OSTYPE} versions:${CL}" for i in "${!AVAILABLE_VERSIONS[@]}"; do echo " [$((i + 1))] ${AVAILABLE_VERSIONS[$i]}" done @@ -3072,12 +3073,25 @@ create_lxc_container() { if [[ "$choice" =~ ^[0-9]+$ ]] && [[ "$choice" -ge 1 ]] && [[ "$choice" -le ${#AVAILABLE_VERSIONS[@]} ]]; then PCT_OSVERSION="${AVAILABLE_VERSIONS[$((choice - 1))]}" - TEMPLATE=$(pveam available -section system 2>/dev/null | awk '{print $NF}' | grep "^${PCT_OSTYPE}-${PCT_OSVERSION}.*${TEMPLATE_PATTERN}" | sort -V | tail -1) - if [[ -n "$TEMPLATE" ]]; then - msg_ok "Selected: $TEMPLATE" + TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION}" + SEARCH_PATTERN="^${TEMPLATE_SEARCH}-" + + echo "[DEBUG] Retrying with version: $PCT_OSVERSION" + + mapfile -t ONLINE_TEMPLATES < <( + pveam available -section system 2>/dev/null | + grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | + awk -F'\t' '{print $1}' | + grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | + sort -t - -k 2 -V 2>/dev/null || true + ) + + if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then + TEMPLATE="${ONLINE_TEMPLATES[-1]}" TEMPLATE_SOURCE="online" + echo "[DEBUG] Found alternative: $TEMPLATE" else - msg_error "No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}" + msg_error "No templates available for ${PCT_OSTYPE} ${PCT_OSVERSION}" exit 225 fi else @@ -3085,12 +3099,7 @@ create_lxc_container() { exit 0 fi else - msg_error "No templates available for ${PCT_OSTYPE}" - echo "" - echo "Please check:" - echo " • Run: pveam update" - echo " • Check network connectivity" - echo " • Verify Proxmox VE version: pveversion" + msg_error "No ${PCT_OSTYPE} templates available at all" exit 225 fi fi @@ -3148,9 +3157,9 @@ create_lxc_container() { mapfile -t ONLINE_TEMPLATES < <( pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | - awk '{print $NF}' | + awk -F'\t' '{print $1}' | grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | - sort -V 2>/dev/null + sort -t - -k 2 -V 2>/dev/null || true ) ONLINE_TEMPLATE="" [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" diff --git a/misc/tools.func b/misc/tools.func index 1fbcc6986..ec13e1d87 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -29,7 +29,6 @@ get_cached_version() { # ------------------------------------------------------------------------------ upgrade_package() { local package="$1" - msg_info "Upgrading $package" # Use same caching logic as ensure_dependencies local apt_cache_file="/var/cache/apt-update-timestamp" @@ -46,7 +45,6 @@ upgrade_package() { fi $STD apt install --only-upgrade -y "$package" - msg_ok "Upgraded $package" } # ------------------------------------------------------------------------------ @@ -76,8 +74,6 @@ ensure_dependencies() { done if [[ ${#missing[@]} -gt 0 ]]; then - msg_info "Installing dependencies: ${missing[*]}" - # Only run apt update if not done recently (within last 5 minutes) local apt_cache_file="/var/cache/apt-update-timestamp" local current_time=$(date +%s) @@ -92,7 +88,6 @@ ensure_dependencies() { cleanup_orphaned_sources 2>/dev/null || true if ! $STD apt update; then - msg_warn "apt update failed, attempting to fix..." ensure_apt_working || return 1 fi echo "$current_time" >"$apt_cache_file" @@ -102,7 +97,6 @@ ensure_dependencies() { msg_error "Failed to install dependencies: ${missing[*]}" return 1 } - msg_ok "Installed dependencies" fi } @@ -511,7 +505,6 @@ wait_for_apt() { return 1 fi - debug_log "Waiting for apt to be available... (${waited}s)" sleep 5 waited=$((waited + 5)) done @@ -568,7 +561,6 @@ cleanup_orphaned_sources() { # If keyring doesn't exist, remove the .sources file if [[ -n "$keyring_path" ]] && [[ ! -f "$keyring_path" ]]; then - msg_warn "Removing orphaned sources file: $basename_file (missing keyring: $(basename "$keyring_path"))" rm -f "$sources_file" fi done < <(find "$sources_dir" -name "*.sources" -print0 2>/dev/null) @@ -589,8 +581,6 @@ ensure_apt_working() { # Try to update package lists if ! apt-get update -qq 2>/dev/null; then - msg_warn "APT update failed, attempting to fix..." - # More aggressive cleanup rm -f /etc/apt/sources.list.d/*.sources 2>/dev/null || true cleanup_orphaned_sources @@ -616,8 +606,6 @@ setup_deb822_repo() { local component="${5:-main}" local architectures="${6:-amd64 arm64}" - msg_info "Setting up $name repository" - # Cleanup old configs for this app cleanup_old_repo_files "$name" @@ -654,8 +642,6 @@ EOF $STD apt update echo "$current_time" >"$apt_cache_file" fi - - msg_ok "Set up $name repository" } # ------------------------------------------------------------------------------ @@ -698,7 +684,7 @@ enable_and_start_service() { local service="$1" if ! systemctl enable "$service" &>/dev/null; then - msg_warn "Could not enable $service (may not be installed yet)" + return 1 fi if ! systemctl start "$service" &>/dev/null; then @@ -793,7 +779,6 @@ end_timer() { local label="${2:-Operation}" local end_time=$(date +%s) local duration=$((end_time - start_time)) - debug_log "$label took ${duration}s" } # ------------------------------------------------------------------------------ @@ -912,27 +897,23 @@ check_for_gh_release() { fi if [[ "$current" != "$pin_clean" ]]; then - msg_info "${app} pinned to ${pinned_version_in} (installed ${current:-none}) → update required" CHECK_UPDATE_RELEASE="$match_raw" + msg_ok "Checking for update: ${app}" return 0 fi - if [[ "$pin_clean" == "$latest_clean" ]]; then - msg_ok "${app} pinned to ${pinned_version_in} (up to date)" - else - msg_ok "${app} pinned to ${pinned_version_in} (already installed, upstream ${latest_raw})" - fi + msg_ok "Checking for update: ${app}" return 1 fi # No pinning → use latest if [[ -z "$current" || "$current" != "$latest_clean" ]]; then CHECK_UPDATE_RELEASE="$latest_raw" - msg_info "New release available: ${latest_raw} (current: v${current:-none})" + msg_ok "Checking for update: ${app}" return 0 fi - msg_ok "${app} is up to date (${latest_raw})" + msg_ok "Checking for update: ${app}" return 1 } @@ -1418,7 +1399,7 @@ function setup_adminer() { CACHED_VERSION=$(get_cached_version "adminer") if grep -qi alpine /etc/os-release; then - msg_info "Installing Adminer (Alpine)" + msg_info "Setup Adminer (Alpine)" mkdir -p /var/www/localhost/htdocs/adminer curl -fsSL https://github.com/vrana/adminer/releases/latest/download/adminer.php \ -o /var/www/localhost/htdocs/adminer/index.php || { @@ -1426,16 +1407,16 @@ function setup_adminer() { return 1 } cache_installed_version "adminer" "latest-alpine" - msg_ok "Installed Adminer (Alpine)" + msg_ok "Setup Adminer (Alpine)" else - msg_info "Installing Adminer (Debian/Ubuntu)" + msg_info "Setup Adminer (Debian/Ubuntu)" ensure_dependencies adminer $STD a2enconf adminer $STD systemctl reload apache2 local VERSION VERSION=$(dpkg -s adminer 2>/dev/null | grep '^Version:' | awk '{print $2}') cache_installed_version "adminer" "${VERSION:-unknown}" - msg_ok "Installed Adminer (Debian/Ubuntu)" + msg_ok "Setup Adminer (Debian/Ubuntu)" fi } @@ -1453,6 +1434,8 @@ function setup_composer() { CACHED_VERSION=$(get_cached_version "composer") export COMPOSER_ALLOW_SUPERUSER=1 + msg_info "Setup Composer" + for old in /usr/bin/composer /bin/composer /root/.composer/vendor/bin/composer; do [[ -e "$old" && "$old" != "$COMPOSER_BIN" ]] && rm -f "$old" done @@ -1460,14 +1443,6 @@ function setup_composer() { ensure_usr_local_bin_persist export PATH="/usr/local/bin:$PATH" - if [[ -x "$COMPOSER_BIN" ]]; then - local CURRENT_VERSION - CURRENT_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}') - msg_info "Updating Composer from $CURRENT_VERSION to latest" - else - msg_info "Installing Composer" - fi - curl -fsSL https://getcomposer.org/installer -o /tmp/composer-setup.php || { msg_error "Failed to download Composer installer" return 1 @@ -1487,7 +1462,7 @@ function setup_composer() { local FINAL_VERSION FINAL_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}') cache_installed_version "composer" "$FINAL_VERSION" - msg_ok "Installed Composer $FINAL_VERSION" + msg_ok "Setup Composer" } # ------------------------------------------------------------------------------ @@ -1517,9 +1492,10 @@ function setup_ffmpeg() { local CACHED_VERSION CACHED_VERSION=$(get_cached_version "ffmpeg") + msg_info "Setup FFmpeg ${VERSION} ($TYPE)" + # Binary fallback mode if [[ "$TYPE" == "binary" ]]; then - msg_info "Installing FFmpeg (static binary)" curl -fsSL https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz -o "$TMP_DIR/ffmpeg.tar.xz" || { msg_error "Failed to download FFmpeg binary" rm -rf "$TMP_DIR" @@ -1535,7 +1511,7 @@ function setup_ffmpeg() { rm -rf "$TMP_DIR" cache_installed_version "ffmpeg" "$FINAL_VERSION" ensure_usr_local_bin_persist - msg_ok "Installed FFmpeg binary $FINAL_VERSION" + msg_ok "Setup FFmpeg" return 0 fi @@ -1555,8 +1531,6 @@ function setup_ffmpeg() { return 1 fi - msg_info "Installing FFmpeg ${VERSION} ($TYPE)" - # Dependency selection local DEPS=(build-essential yasm nasm pkg-config) case "$TYPE" in @@ -1649,7 +1623,7 @@ function setup_ffmpeg() { rm -rf "$TMP_DIR" cache_installed_version "ffmpeg" "$FINAL_VERSION" ensure_usr_local_bin_persist - msg_ok "Installed FFmpeg $FINAL_VERSION" + msg_ok "Setup FFmpeg" } # ------------------------------------------------------------------------------ @@ -1698,13 +1672,12 @@ function setup_go() { cache_installed_version "go" "$GO_VERSION" return 0 else - msg_info "Upgrading Go from $CURRENT_VERSION to $GO_VERSION" rm -rf "$GO_INSTALL_DIR" fi - else - msg_info "Installing Go $GO_VERSION" fi + msg_info "Setup Go $GO_VERSION" + local TARBALL="go${GO_VERSION}.linux-${ARCH}.tar.gz" local URL="https://go.dev/dl/${TARBALL}" local TMP_TAR=$(mktemp) @@ -1722,7 +1695,7 @@ function setup_go() { cache_installed_version "go" "$GO_VERSION" ensure_usr_local_bin_persist - msg_ok "Installed Go $GO_VERSION" + msg_ok "Setup Go $GO_VERSION" } # ------------------------------------------------------------------------------ @@ -1764,7 +1737,7 @@ function setup_gs() { return 0 fi - msg_info "Installing Ghostscript $LATEST_VERSION_DOTTED" + msg_info "Setup Ghostscript $LATEST_VERSION_DOTTED" curl -fsSL "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs${LATEST_VERSION}/ghostscript-${LATEST_VERSION_DOTTED}.tar.gz" -o "$TMP_DIR/ghostscript.tar.gz" || { msg_error "Failed to download Ghostscript" @@ -1800,7 +1773,7 @@ function setup_gs() { rm -rf "$TMP_DIR" cache_installed_version "ghostscript" "$LATEST_VERSION_DOTTED" ensure_usr_local_bin_persist - msg_ok "Installed Ghostscript $LATEST_VERSION_DOTTED" + msg_ok "Setup Ghostscript $LATEST_VERSION_DOTTED" } # ------------------------------------------------------------------------------ @@ -1815,7 +1788,7 @@ function setup_gs() { # - Some things are fetched from intel repositories due to not being in debian repositories. # ------------------------------------------------------------------------------ function setup_hwaccel() { - msg_info "Setting Up Hardware Acceleration" + msg_info "Setup Hardware Acceleration" if ! command -v lspci &>/dev/null; then $STD apt -y update @@ -1845,8 +1818,6 @@ function setup_hwaccel() { case "$gpu_vendor" in Intel) - msg_info "Detected Intel GPU — configuring Intel hardware acceleration" - if [[ "$os_id" == "ubuntu" ]]; then $STD apt -y install intel-opencl-icd || msg_error "Failed to install intel-opencl-icd" else @@ -1859,24 +1830,19 @@ function setup_hwaccel() { $STD apt -y install va-driver-all ocl-icd-libopencl1 vainfo intel-gpu-tools || msg_error "Failed to install GPU dependencies" ;; AMD) - msg_info "Detected AMD GPU — configuring Mesa-based hardware acceleration" - $STD apt -y install mesa-va-drivers mesa-vdpau-drivers mesa-opencl-icd vainfo clinfo || msg_error "Failed to install AMD GPU dependencies" # For AMD CPUs without discrete GPU (APUs) if [[ "$cpu_vendor" == "AuthenticAMD" && "$gpu_vendor" != "AMD" ]]; then - msg_info "Detected AMD CPU (APU) — enabling VAAPI via Mesa" $STD apt -y install libdrm-amdgpu1 firmware-amd-graphics || true fi ;; NVIDIA) - msg_info "Detected NVIDIA GPU — skipping automatic configuration (manual driver setup required)" - msg_info "→ Please install proprietary drivers manually via: apt install nvidia-driver" + # NVIDIA needs manual driver setup ;; *) # If no discrete GPU, but AMD CPU (e.g., Ryzen APU) if [[ "$cpu_vendor" == "AuthenticAMD" ]]; then - msg_info "Detected AMD CPU without discrete GPU — installing Mesa OpenCL stack" $STD apt -y install mesa-opencl-icd ocl-icd-libopencl1 clinfo || msg_error "Failed to install Mesa OpenCL stack" else msg_error "No supported GPU vendor detected" @@ -1893,7 +1859,7 @@ function setup_hwaccel() { $STD adduser "$(id -u -n)" render fi - msg_ok "Hardware Acceleration Setup Complete" + msg_ok "Setup Hardware Acceleration" } # ------------------------------------------------------------------------------ @@ -1925,7 +1891,7 @@ function setup_imagemagick() { return 0 fi - msg_info "Installing ImageMagick (Patience)" + msg_info "Setup ImageMagick" ensure_dependencies \ build-essential \ @@ -1980,7 +1946,7 @@ function setup_imagemagick() { rm -rf "$TMP_DIR" cache_installed_version "imagemagick" "$VERSION" ensure_usr_local_bin_persist - msg_ok "Installed ImageMagick $VERSION" + msg_ok "Setup ImageMagick" } # ------------------------------------------------------------------------------ @@ -2034,23 +2000,19 @@ function setup_java() { # Already at correct version, just upgrade if available upgrade_package "$DESIRED_PACKAGE" else - msg_info "Upgrading Temurin JDK $JAVA_VERSION" $STD apt update $STD apt install --only-upgrade -y "$DESIRED_PACKAGE" cache_installed_version "temurin-jdk" "$JAVA_VERSION" - msg_ok "Upgraded Temurin JDK $JAVA_VERSION" fi else + msg_info "Setup Temurin JDK $JAVA_VERSION" if [[ -n "$INSTALLED_VERSION" ]]; then - msg_info "Removing old Temurin JDK $INSTALLED_VERSION" $STD apt purge -y "temurin-${INSTALLED_VERSION}-jdk" - msg_ok "Removed old Temurin JDK" fi - msg_info "Installing Temurin JDK $JAVA_VERSION" $STD apt install -y "$DESIRED_PACKAGE" cache_installed_version "temurin-jdk" "$JAVA_VERSION" - msg_ok "Installed Temurin JDK $JAVA_VERSION" + msg_ok "Setup Temurin JDK $JAVA_VERSION" fi } From 03c9654140146ce32f7d65f19d6108016e22b2ca Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 22 Oct 2025 14:00:54 +0200 Subject: [PATCH 1519/1733] Standardize setup messages and simplify install logic Replaces various install/upgrade messages with standardized 'Setup ' and 'msg_ok' calls for consistency. Removes redundant or verbose info/warning messages, simplifies dependency installation logic, and ensures error handling uses '|| true' where appropriate. This improves readability and reduces unnecessary output during package setup. --- misc/tools.func | 146 +++++++++++++----------------------------------- 1 file changed, 40 insertions(+), 106 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index ec13e1d87..5fe101ce6 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -2143,16 +2143,14 @@ setup_mariadb() { upgrade_package mariadb-server upgrade_package mariadb-client else - msg_info "Upgrading MariaDB $MARIADB_VERSION" $STD apt update $STD apt install --only-upgrade -y mariadb-server mariadb-client cache_installed_version "mariadb" "$MARIADB_VERSION" - msg_ok "Upgraded MariaDB $MARIADB_VERSION" fi return 0 fi - msg_info "Installing MariaDB $MARIADB_VERSION" + msg_info "Setup MariaDB $MARIADB_VERSION" if [[ -n "$CURRENT_VERSION" ]]; then $STD systemctl stop mariadb >/dev/null 2>&1 || true @@ -2166,21 +2164,15 @@ setup_mariadb() { cleanup_old_repo_files "mariadb" # Install required dependencies first (MariaDB needs these from main repos) - msg_info "Installing MariaDB dependencies" - local mariadb_deps=() for dep in gawk rsync socat libdbi-perl pv; do if apt-cache search "^${dep}$" 2>/dev/null | grep -q .; then mariadb_deps+=("$dep") - else - msg_warn "Package $dep not available, skipping" fi done if [[ ${#mariadb_deps[@]} -gt 0 ]]; then - if ! $STD apt install -y "${mariadb_deps[@]}" 2>/dev/null; then - msg_warn "Some MariaDB dependencies failed to install - continuing anyway" - fi + $STD apt install -y "${mariadb_deps[@]}" 2>/dev/null || true fi # Use helper function to get fallback suite @@ -2209,7 +2201,7 @@ setup_mariadb() { } cache_installed_version "mariadb" "$MARIADB_VERSION" - msg_ok "Installed MariaDB $MARIADB_VERSION" + msg_ok "Setup MariaDB $MARIADB_VERSION" } # ------------------------------------------------------------------------------ @@ -2265,16 +2257,14 @@ function setup_mongodb() { if [[ "$CACHED_VERSION" == "$MONGO_VERSION" ]]; then upgrade_package mongodb-org else - msg_info "Upgrading MongoDB $MONGO_VERSION" $STD apt update $STD apt install --only-upgrade -y mongodb-org cache_installed_version "mongodb" "$MONGO_VERSION" - msg_ok "Upgraded MongoDB $MONGO_VERSION" fi return 0 fi - msg_info "Installing MongoDB $MONGO_VERSION" + msg_info "Setup MongoDB $MONGO_VERSION" if [[ -n "$INSTALLED_VERSION" ]]; then $STD systemctl stop mongod || true @@ -2317,7 +2307,7 @@ EOF $STD systemctl enable mongod safe_service_restart mongod cache_installed_version "mongodb" "$MONGO_VERSION" - msg_ok "Installed MongoDB $MONGO_VERSION" + msg_ok "Setup MongoDB $MONGO_VERSION" } # ------------------------------------------------------------------------------ @@ -2344,23 +2334,21 @@ function setup_mysql() { if command -v mysql >/dev/null; then CURRENT_VERSION="$(mysql --version | grep -oP '[0-9]+\.[0-9]+' | head -n1)" if [[ "$CURRENT_VERSION" != "$MYSQL_VERSION" ]]; then - msg_info "MySQL $CURRENT_VERSION will be upgraded to $MYSQL_VERSION" NEED_INSTALL=true else if apt list --upgradable 2>/dev/null | grep -q '^mysql-server/'; then - msg_info "MySQL $CURRENT_VERSION available for upgrade" $STD apt update $STD apt install --only-upgrade -y mysql-server - msg_ok "MySQL upgraded" fi return fi else - msg_info "Setup MySQL $MYSQL_VERSION" NEED_INSTALL=true fi if [[ "$NEED_INSTALL" == true ]]; then + msg_info "Setup MySQL $MYSQL_VERSION" + # Cleanup old repository files cleanup_old_repo_files "mysql" @@ -2372,7 +2360,6 @@ function setup_mysql() { SUITE="$DISTRO_CODENAME" ;; trixie | forky | sid) - msg_warn "Using MySQL Bookworm packages on Debian ${DISTRO_CODENAME}" SUITE="bookworm" ;; *) @@ -2394,8 +2381,6 @@ function setup_mysql() { # Handle libaio dependency for Debian Trixie+ (time64 transition) if [[ "$DISTRO_ID" == "debian" ]] && [[ "$DISTRO_CODENAME" =~ ^(trixie|forky|sid)$ ]]; then - msg_info "Installing libaio compatibility for Debian ${DISTRO_CODENAME}" - # Install libaio1t64 if not present if ! dpkg -l libaio1t64 2>/dev/null | grep -q "^ii"; then $STD apt update @@ -2430,8 +2415,6 @@ EOF # Cleanup rm -rf "$TEMP_DIR" libaio1-compat.deb - - msg_ok "libaio1 compatibility package installed" fi # Use standardized repo setup @@ -2471,14 +2454,11 @@ EOF if [[ "$mysql_install_success" == false ]]; then msg_error "MySQL ${MYSQL_VERSION} package not available for suite ${SUITE}" - msg_info "Available MySQL components:" - apt-cache search "mysql" | grep "^mysql" | head -20 | sed 's/^/ /' return 1 fi # Verify installation if ! command -v mysql >/dev/null 2>&1; then - msg_warn "MySQL installed but mysql command not immediately available - retrying after shell refresh" hash -r if ! command -v mysql >/dev/null 2>&1; then msg_error "MySQL installed but mysql command still not found" @@ -2487,7 +2467,7 @@ EOF fi cache_installed_version "mysql" "$MYSQL_VERSION" - msg_ok "Installed MySQL $MYSQL_VERSION" + msg_ok "Setup MySQL $MYSQL_VERSION" fi } @@ -2515,17 +2495,17 @@ function setup_nodejs() { if command -v node >/dev/null; then CURRENT_NODE_VERSION="$(node -v | grep -oP '^v\K[0-9]+')" if [[ "$CURRENT_NODE_VERSION" != "$NODE_VERSION" ]]; then - msg_info "Old Node.js $CURRENT_NODE_VERSION found, replacing with $NODE_VERSION" NEED_NODE_INSTALL=true fi else - msg_info "Setup Node.js $NODE_VERSION" NEED_NODE_INSTALL=true fi - ensure_dependencies jq - if [[ "$NEED_NODE_INSTALL" == true ]]; then + msg_info "Setup Node.js $NODE_VERSION" + + ensure_dependencies jq + $STD apt purge -y nodejs # Cleanup old repository files @@ -2546,12 +2526,10 @@ function setup_nodejs() { return 1 fi - $STD npm install -g npm@latest || { - msg_warn "Failed to update npm to latest version" - } + $STD npm install -g npm@latest 2>/dev/null || true cache_installed_version "nodejs" "$NODE_VERSION" - msg_ok "Installed Node.js ${NODE_VERSION}" + msg_ok "Setup Node.js $NODE_VERSION" fi export NODE_OPTIONS="--max-old-space-size=4096" @@ -2582,27 +2560,23 @@ function setup_nodejs() { if npm list -g --depth=0 "$MODULE_NAME" >/dev/null 2>&1; then MODULE_INSTALLED_VERSION="$(npm list -g --depth=0 "$MODULE_NAME" | grep "$MODULE_NAME@" | awk -F@ '{print $2}' | tr -d '[:space:]')" if [[ "$MODULE_REQ_VERSION" != "latest" && "$MODULE_REQ_VERSION" != "$MODULE_INSTALLED_VERSION" ]]; then - msg_info "Updating $MODULE_NAME from v$MODULE_INSTALLED_VERSION to v$MODULE_REQ_VERSION" if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}"; then msg_error "Failed to update $MODULE_NAME to version $MODULE_REQ_VERSION" return 1 fi elif [[ "$MODULE_REQ_VERSION" == "latest" ]]; then - msg_info "Updating $MODULE_NAME to latest version" if ! $STD npm install -g "${MODULE_NAME}@latest"; then msg_error "Failed to update $MODULE_NAME to latest version" return 1 fi fi else - msg_info "Installing $MODULE_NAME@$MODULE_REQ_VERSION" if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}"; then msg_error "Failed to install $MODULE_NAME@$MODULE_REQ_VERSION" return 1 fi fi done - msg_ok "Installed Node.js modules: $NODE_MODULE" fi } @@ -2658,10 +2632,9 @@ function setup_php() { CURRENT_PHP=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2) fi - if [[ -z "$CURRENT_PHP" ]]; then - msg_info "Setup PHP $PHP_VERSION" - elif [[ "$CURRENT_PHP" != "$PHP_VERSION" ]]; then - msg_info "Old PHP $CURRENT_PHP detected, Setup new PHP $PHP_VERSION" + msg_info "Setup PHP $PHP_VERSION" + + if [[ -n "$CURRENT_PHP" && "$CURRENT_PHP" != "$PHP_VERSION" ]]; then $STD apt purge -y "php${CURRENT_PHP//./}"* || true fi @@ -2694,8 +2667,6 @@ EOF for mod in "${MODULES[@]}"; do if apt-cache show "php${PHP_VERSION}-${mod}" >/dev/null 2>&1; then MODULE_LIST+=" php${PHP_VERSION}-${mod}" - else - msg_warn "PHP-Module ${mod} for PHP ${PHP_VERSION} not found – skipping" fi done if [[ "$PHP_FPM" == "YES" ]]; then @@ -2705,17 +2676,13 @@ EOF # install apache2 with PHP support if requested if [[ "$PHP_APACHE" == "YES" ]]; then if ! dpkg -l | grep -q "libapache2-mod-php${PHP_VERSION}"; then - msg_info "Installing Apache with PHP${PHP_VERSION} support" $STD apt install -y apache2 libapache2-mod-php${PHP_VERSION} - else - msg_info "Apache with PHP${PHP_VERSION} already installed – skipping install" fi fi # setup / update PHP modules $STD apt install -y $MODULE_LIST cache_installed_version "php" "$PHP_VERSION" - msg_ok "Installed PHP $PHP_VERSION" # optional stop old PHP-FPM service if [[ "$PHP_FPM" == "YES" && -n "$CURRENT_PHP" && "$CURRENT_PHP" != "$PHP_VERSION" ]]; then @@ -2753,10 +2720,10 @@ EOF if systemctl list-unit-files | grep -q "php${PHP_VERSION}-fpm.service"; then $STD systemctl enable php${PHP_VERSION}-fpm safe_service_restart php${PHP_VERSION}-fpm - else - msg_warn "FPM requested but service php${PHP_VERSION}-fpm not found" fi fi + + msg_ok "Setup PHP $PHP_VERSION" } # ------------------------------------------------------------------------------ @@ -2790,7 +2757,7 @@ function setup_postgresql() { fi if [[ "$NEED_PG_INSTALL" == true ]]; then - msg_info "Installing PostgreSQL $PG_VERSION" + msg_info "Setup PostgreSQL $PG_VERSION" if [[ -n "$CURRENT_PG_VERSION" ]]; then $STD runuser -u postgres -- pg_dumpall >/var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql @@ -2856,13 +2823,10 @@ function setup_postgresql() { # Third try: just postgresql (any version) if [[ "$pg_install_success" == false ]] && apt-cache search "^postgresql$" 2>/dev/null | grep -q . && $STD apt install -y postgresql postgresql-client 2>/dev/null; then pg_install_success=true - msg_warn "PostgreSQL ${PG_VERSION} not available, installed latest available version" fi if [[ "$pg_install_success" == false ]]; then msg_error "PostgreSQL package not available for suite ${SUITE}" - msg_info "Available PostgreSQL packages:" - apt-cache search "postgresql" | grep "^postgresql" | head -20 | sed 's/^/ /' return 1 fi @@ -2885,19 +2849,14 @@ function setup_postgresql() { fi cache_installed_version "postgresql" "$PG_VERSION" - msg_ok "Installed PostgreSQL $PG_VERSION" + msg_ok "Setup PostgreSQL $PG_VERSION" fi if [[ -n "$PG_MODULES" ]]; then - msg_info "Installing PostgreSQL modules" IFS=',' read -ra MODULES <<<"$PG_MODULES" for module in "${MODULES[@]}"; do - $STD apt install -y "postgresql-${PG_VERSION}-${module}" || { - msg_warn "Failed to install postgresql-${PG_VERSION}-${module}" - continue - } + $STD apt install -y "postgresql-${PG_VERSION}-${module}" 2>/dev/null || true done - msg_ok "Installed PostgreSQL modules" fi } @@ -2924,15 +2883,13 @@ function setup_ruby() { local CACHED_VERSION CACHED_VERSION=$(get_cached_version "ruby") - msg_info "Installing Ruby $RUBY_VERSION" + msg_info "Setup Ruby $RUBY_VERSION" # Ensure APT is working before installing dependencies ensure_apt_working || return 1 # Install build dependencies - with fallback for different Debian versions # In Trixie: libreadline6-dev -> libreadline-dev, libncurses5-dev -> libncurses-dev - msg_info "Installing Ruby build dependencies" - local ruby_deps=() local dep_variations=( "jq" @@ -2963,20 +2920,15 @@ function setup_ruby() { break fi done - [[ "$found" == false ]] && msg_warn "No version of $dep_pattern available" else if apt-cache search "^${dep_pattern}$" 2>/dev/null | grep -q .; then ruby_deps+=("$dep_pattern") - else - msg_warn "Package $dep_pattern not available" fi fi done if [[ ${#ruby_deps[@]} -gt 0 ]]; then - if ! $STD apt install -y "${ruby_deps[@]}" 2>/dev/null; then - msg_warn "Some Ruby dependencies failed to install - Ruby may not compile fully\nContinuing anyway..." - fi + $STD apt install -y "${ruby_deps[@]}" 2>/dev/null || true else msg_error "No Ruby build dependencies available" return 1 @@ -3047,13 +2999,11 @@ function setup_ruby() { if [[ "$RUBY_INSTALL_RAILS" == "true" ]]; then $STD gem install rails - local RAILS_VERSION=$(rails -v 2>/dev/null | awk '{print $2}') - msg_ok "Installed Rails $RAILS_VERSION" fi rm -rf "$TMP_DIR" cache_installed_version "ruby" "$RUBY_VERSION" - msg_ok "Installed Ruby $RUBY_VERSION" + msg_ok "Setup Ruby $RUBY_VERSION" } # ------------------------------------------------------------------------------ @@ -3118,16 +3068,16 @@ function setup_clickhouse() { upgrade_package clickhouse-server upgrade_package clickhouse-client else - msg_info "Upgrading ClickHouse $CLICKHOUSE_VERSION" + msg_info "Setup ClickHouse $CLICKHOUSE_VERSION" $STD apt update $STD apt install --only-upgrade -y clickhouse-server clickhouse-client cache_installed_version "clickhouse" "$CLICKHOUSE_VERSION" - msg_ok "Upgraded ClickHouse $CLICKHOUSE_VERSION" + msg_ok "Setup ClickHouse $CLICKHOUSE_VERSION" fi return 0 fi - msg_info "Installing ClickHouse $CLICKHOUSE_VERSION" + msg_info "Setup ClickHouse $CLICKHOUSE_VERSION" # Stop existing service if upgrading if [[ -n "$CURRENT_VERSION" ]]; then @@ -3176,21 +3126,14 @@ function setup_clickhouse() { # Check if clickhouse user exists before chown if id clickhouse >/dev/null 2>&1; then chown -R clickhouse:clickhouse /var/lib/clickhouse - else - msg_warn "ClickHouse user not found, skipping chown" fi # Enable and start service - if ! $STD systemctl enable clickhouse-server; then - msg_warn "Failed to enable clickhouse-server service" - fi - - if ! safe_service_restart clickhouse-server; then - msg_warn "Failed to start clickhouse-server service (this may be normal on first install)" - fi + $STD systemctl enable clickhouse-server + safe_service_restart clickhouse-server || true cache_installed_version "clickhouse" "$CLICKHOUSE_VERSION" - msg_ok "Installed ClickHouse $CLICKHOUSE_VERSION" + msg_ok "Setup ClickHouse $CLICKHOUSE_VERSION" } # ------------------------------------------------------------------------------ @@ -3218,7 +3161,7 @@ function setup_rust() { CACHED_VERSION=$(get_cached_version "rust") if ! command -v rustup &>/dev/null; then - msg_info "Installing Rust" + msg_info "Setup Rust" curl -fsSL https://sh.rustup.rs | $STD sh -s -- -y --default-toolchain "$RUST_TOOLCHAIN" || { msg_error "Failed to install Rust" return 1 @@ -3227,14 +3170,15 @@ function setup_rust() { echo 'export PATH="$HOME/.cargo/bin:$PATH"' >>"$HOME/.profile" local RUST_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}') cache_installed_version "rust" "$RUST_VERSION" - msg_ok "Installed Rust $RUST_VERSION" + msg_ok "Setup Rust $RUST_VERSION" else + msg_info "Setup Rust" $STD rustup install "$RUST_TOOLCHAIN" $STD rustup default "$RUST_TOOLCHAIN" $STD rustup update "$RUST_TOOLCHAIN" local RUST_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}') cache_installed_version "rust" "$RUST_VERSION" - msg_ok "Updated Rust toolchain to $RUST_TOOLCHAIN ($RUST_VERSION)" + msg_ok "Setup Rust $RUST_VERSION" fi if [[ -n "$RUST_CRATES" ]]; then @@ -3253,19 +3197,12 @@ function setup_rust() { if [[ -n "$INSTALLED_VER" ]]; then if [[ -n "$VER" && "$VER" != "$INSTALLED_VER" ]]; then - msg_info "Updating $NAME: $INSTALLED_VER → $VER" $STD cargo install "$NAME" --version "$VER" --force - msg_ok "Updated $NAME to $VER" elif [[ -z "$VER" ]]; then - msg_info "Updating $NAME: $INSTALLED_VER → latest" $STD cargo install "$NAME" --force - local NEW_VER=$(cargo install --list 2>/dev/null | awk "/^$NAME v[0-9]/ {print \$2}" | tr -d 'v') - msg_ok "Updated $NAME to $NEW_VER" fi else - msg_info "Installing $NAME ${VER:+$VER}" $STD cargo install "$NAME" ${VER:+--version "$VER"} - msg_ok "Installed $NAME ${VER:-latest}" fi done fi @@ -3333,13 +3270,11 @@ function setup_uv() { cache_installed_version "uv" "$LATEST_VERSION" rm -rf "$TMP_DIR" return 0 - else - msg_info "Updating uv from $INSTALLED_VERSION to $LATEST_VERSION" fi - else - msg_info "Installing uv $LATEST_VERSION" fi + msg_info "Setup uv $LATEST_VERSION" + local UV_URL="https://github.com/astral-sh/uv/releases/latest/download/${UV_TAR}" curl -fsSL "$UV_URL" -o "$TMP_DIR/uv.tar.gz" || { msg_error "Failed to download uv" @@ -3365,7 +3300,7 @@ function setup_uv() { $STD uv python update-shell || true cache_installed_version "uv" "$LATEST_VERSION" - msg_ok "Installed uv $LATEST_VERSION" + msg_ok "Setup uv $LATEST_VERSION" if [[ -n "${PYTHON_VERSION:-}" ]]; then local VERSION_MATCH @@ -3383,7 +3318,6 @@ function setup_uv() { msg_error "Failed to install Python $VERSION_MATCH" return 1 } - msg_ok "Installed Python $VERSION_MATCH" fi fi } @@ -3436,7 +3370,7 @@ function setup_yq() { return 0 fi - msg_info "Installing yq $LATEST_VERSION" + msg_info "Setup yq $LATEST_VERSION" curl -fsSL "https://github.com/${GITHUB_REPO}/releases/download/v${LATEST_VERSION}/yq_linux_amd64" -o "$TMP_DIR/yq" || { msg_error "Failed to download yq" rm -rf "$TMP_DIR" @@ -3458,5 +3392,5 @@ function setup_yq() { local FINAL_VERSION FINAL_VERSION=$("$BINARY_PATH" --version 2>/dev/null | awk '{print $NF}' | sed 's/^v//') cache_installed_version "yq" "$FINAL_VERSION" - msg_ok "Installed yq $FINAL_VERSION" + msg_ok "Setup yq $FINAL_VERSION" } From 6a74c6d51ce29cf1e989db718eabbafa4395b587 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 22 Oct 2025 14:13:37 +0200 Subject: [PATCH 1520/1733] Add error handling to package and archive operations This commit adds error checking and user-friendly error messages to various package installation, archive extraction, and build steps throughout the script. It ensures that failures in commands such as apt, curl, tar, unzip, make, and others are detected, appropriate cleanup is performed, and informative error messages are displayed, improving robustness and debuggability. --- misc/tools.func | 284 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 225 insertions(+), 59 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 5fe101ce6..ee35767cf 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -616,7 +616,10 @@ setup_deb822_repo() { mkdir -p /etc/apt/keyrings # Download GPG key (with --yes to avoid interactive prompts) - curl -fsSL "$gpg_url" | gpg --dearmor --yes -o "/etc/apt/keyrings/${name}.gpg" + curl -fsSL "$gpg_url" | gpg --dearmor --yes -o "/etc/apt/keyrings/${name}.gpg" || { + msg_error "Failed to download or import GPG key for ${name}" + return 1 + } # Create deb822 sources file cat </etc/apt/sources.list.d/${name}.sources @@ -936,14 +939,23 @@ create_self_signed_cert() { return 0 fi - $STD apt update - $STD apt install -y openssl + $STD apt update || { + msg_error "Failed to update package list" + return 1 + } + $STD apt install -y openssl || { + msg_error "Failed to install OpenSSL" + return 1 + } mkdir -p "$CERT_DIR" $STD openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 \ -subj "/C=US/ST=State/L=City/O=Organization/CN=${APP_NAME}" \ -keyout "$CERT_KEY" \ - -out "$CERT_CRT" + -out "$CERT_CRT" || { + msg_error "Failed to create self-signed certificate" + return 1 + } chmod 600 "$CERT_KEY" chmod 644 "$CERT_CRT" @@ -1129,7 +1141,11 @@ function fetch_and_deploy_gh_release() { rm -rf "${target:?}/"* fi - tar -xzf "$tmpdir/$filename" -C "$tmpdir" + tar -xzf "$tmpdir/$filename" -C "$tmpdir" || { + msg_error "Failed to extract tarball" + rm -rf "$tmpdir" + return 1 + } local unpack_dir unpack_dir=$(find "$tmpdir" -mindepth 1 -maxdepth 1 -type d | head -n1) @@ -1241,9 +1257,17 @@ function fetch_and_deploy_gh_release() { if [[ "$filename" == *.zip ]]; then ensure_dependencies unzip - unzip -q "$tmpdir/$filename" -d "$unpack_tmp" + unzip -q "$tmpdir/$filename" -d "$unpack_tmp" || { + msg_error "Failed to extract ZIP archive" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + } elif [[ "$filename" == *.tar.* || "$filename" == *.tgz ]]; then - tar -xf "$tmpdir/$filename" -C "$unpack_tmp" + tar -xf "$tmpdir/$filename" -C "$unpack_tmp" || { + msg_error "Failed to extract TAR archive" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + } else msg_error "Unsupported archive format: $filename" rm -rf "$tmpdir" "$unpack_tmp" @@ -1411,8 +1435,14 @@ function setup_adminer() { else msg_info "Setup Adminer (Debian/Ubuntu)" ensure_dependencies adminer - $STD a2enconf adminer - $STD systemctl reload apache2 + $STD a2enconf adminer || { + msg_error "Failed to enable Adminer Apache config" + return 1 + } + $STD systemctl reload apache2 || { + msg_error "Failed to reload Apache" + return 1 + } local VERSION VERSION=$(dpkg -s adminer 2>/dev/null | grep '^Version:' | awk '{print $2}') cache_installed_version "adminer" "${VERSION:-unknown}" @@ -1448,7 +1478,11 @@ function setup_composer() { return 1 } - $STD php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer + $STD php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer || { + msg_error "Failed to install Composer" + rm -f /tmp/composer-setup.php + return 1 + } rm -f /tmp/composer-setup.php if [[ ! -x "$COMPOSER_BIN" ]]; then @@ -1501,7 +1535,11 @@ function setup_ffmpeg() { rm -rf "$TMP_DIR" return 1 } - tar -xf "$TMP_DIR/ffmpeg.tar.xz" -C "$TMP_DIR" + tar -xf "$TMP_DIR/ffmpeg.tar.xz" -C "$TMP_DIR" || { + msg_error "Failed to extract FFmpeg binary" + rm -rf "$TMP_DIR" + return 1 + } local EXTRACTED_DIR EXTRACTED_DIR=$(find "$TMP_DIR" -maxdepth 1 -type d -name "ffmpeg-*") cp "$EXTRACTED_DIR/ffmpeg" "$BIN_PATH" @@ -1600,9 +1638,21 @@ function setup_ffmpeg() { return 1 fi - $STD ./configure "${args[@]}" - $STD make -j"$(nproc)" - $STD make install + $STD ./configure "${args[@]}" || { + msg_error "FFmpeg configure failed" + rm -rf "$TMP_DIR" + return 1 + } + $STD make -j"$(nproc)" || { + msg_error "FFmpeg compilation failed" + rm -rf "$TMP_DIR" + return 1 + } + $STD make install || { + msg_error "FFmpeg installation failed" + rm -rf "$TMP_DIR" + return 1 + } echo "/usr/local/lib" >/etc/ld.so.conf.d/ffmpeg.conf $STD ldconfig @@ -1688,7 +1738,11 @@ function setup_go() { return 1 } - $STD tar -C /usr/local -xzf "$TMP_TAR" + $STD tar -C /usr/local -xzf "$TMP_TAR" || { + msg_error "Failed to extract Go tarball" + rm -f "$TMP_TAR" + return 1 + } ln -sf /usr/local/go/bin/go /usr/local/bin/go ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt rm -f "$TMP_TAR" @@ -1759,9 +1813,21 @@ function setup_gs() { ensure_dependencies build-essential libpng-dev zlib1g-dev - $STD ./configure - $STD make -j"$(nproc)" - $STD make install + $STD ./configure || { + msg_error "Ghostscript configure failed" + rm -rf "$TMP_DIR" + return 1 + } + $STD make -j"$(nproc)" || { + msg_error "Ghostscript compilation failed" + rm -rf "$TMP_DIR" + return 1 + } + $STD make install || { + msg_error "Ghostscript installation failed" + rm -rf "$TMP_DIR" + return 1 + } hash -r if [[ ! -x "$(command -v gs)" ]]; then @@ -1791,8 +1857,14 @@ function setup_hwaccel() { msg_info "Setup Hardware Acceleration" if ! command -v lspci &>/dev/null; then - $STD apt -y update - $STD apt -y install pciutils + $STD apt -y update || { + msg_error "Failed to update package list" + return 1 + } + $STD apt -y install pciutils || { + msg_error "Failed to install pciutils" + return 1 + } fi # Detect GPU vendor (Intel, AMD, NVIDIA) @@ -1931,9 +2003,21 @@ function setup_imagemagick() { return 1 } - $STD ./configure --disable-static - $STD make -j"$(nproc)" - $STD make install + $STD ./configure --disable-static || { + msg_error "ImageMagick configure failed" + rm -rf "$TMP_DIR" + return 1 + } + $STD make -j"$(nproc)" || { + msg_error "ImageMagick compilation failed" + rm -rf "$TMP_DIR" + return 1 + } + $STD make install || { + msg_error "ImageMagick installation failed" + rm -rf "$TMP_DIR" + return 1 + } $STD ldconfig /usr/local/lib if [[ ! -x "$BINARY_PATH" ]]; then @@ -2010,7 +2094,10 @@ function setup_java() { $STD apt purge -y "temurin-${INSTALLED_VERSION}-jdk" fi - $STD apt install -y "$DESIRED_PACKAGE" + $STD apt install -y "$DESIRED_PACKAGE" || { + msg_error "Failed to install Temurin JDK $JAVA_VERSION" + return 1 + } cache_installed_version "temurin-jdk" "$JAVA_VERSION" msg_ok "Setup Temurin JDK $JAVA_VERSION" fi @@ -2034,8 +2121,14 @@ function setup_local_ip_helper() { # Install networkd-dispatcher if not present if ! dpkg -s networkd-dispatcher >/dev/null 2>&1; then - $STD apt update - $STD apt install -y networkd-dispatcher + $STD apt update || { + msg_error "Failed to update package list" + return 1 + } + $STD apt install -y networkd-dispatcher || { + msg_error "Failed to install networkd-dispatcher" + return 1 + } fi # Write update_local_ip.sh @@ -2143,8 +2236,14 @@ setup_mariadb() { upgrade_package mariadb-server upgrade_package mariadb-client else - $STD apt update - $STD apt install --only-upgrade -y mariadb-server mariadb-client + $STD apt update || { + msg_error "Failed to update package list" + return 1 + } + $STD apt install --only-upgrade -y mariadb-server mariadb-client || { + msg_error "Failed to upgrade MariaDB" + return 1 + } cache_installed_version "mariadb" "$MARIADB_VERSION" fi return 0 @@ -2197,7 +2296,10 @@ setup_mariadb() { DEBIAN_FRONTEND=noninteractive $STD apt install -y mariadb-server mariadb-client || { cleanup_old_repo_files "mariadb" $STD apt update - DEBIAN_FRONTEND=noninteractive $STD apt install -y mariadb-server mariadb-client + DEBIAN_FRONTEND=noninteractive $STD apt install -y mariadb-server mariadb-client || { + msg_error "Failed to install MariaDB packages" + return 1 + } } cache_installed_version "mariadb" "$MARIADB_VERSION" @@ -2257,8 +2359,14 @@ function setup_mongodb() { if [[ "$CACHED_VERSION" == "$MONGO_VERSION" ]]; then upgrade_package mongodb-org else - $STD apt update - $STD apt install --only-upgrade -y mongodb-org + $STD apt update || { + msg_error "Failed to update package list" + return 1 + } + $STD apt install --only-upgrade -y mongodb-org || { + msg_error "Failed to upgrade MongoDB" + return 1 + } cache_installed_version "mongodb" "$MONGO_VERSION" fi return 0 @@ -2283,7 +2391,10 @@ function setup_mongodb() { # Use standardized repo setup mkdir -p /etc/apt/keyrings - curl -fsSL "https://pgp.mongodb.com/server-${MONGO_VERSION}.asc" | gpg --dearmor --yes -o "/etc/apt/keyrings/mongodb-${MONGO_VERSION}.gpg" + curl -fsSL "https://pgp.mongodb.com/server-${MONGO_VERSION}.asc" | gpg --dearmor --yes -o "/etc/apt/keyrings/mongodb-${MONGO_VERSION}.gpg" || { + msg_error "Failed to download or import MongoDB GPG key" + return 1 + } cat </etc/apt/sources.list.d/mongodb-org-${MONGO_VERSION}.sources Types: deb @@ -2299,7 +2410,10 @@ EOF return 1 } - $STD apt install -y mongodb-org + $STD apt install -y mongodb-org || { + msg_error "Failed to install MongoDB packages" + return 1 + } mkdir -p /var/lib/mongodb chown -R mongodb:mongodb /var/lib/mongodb @@ -2337,8 +2451,14 @@ function setup_mysql() { NEED_INSTALL=true else if apt list --upgradable 2>/dev/null | grep -q '^mysql-server/'; then - $STD apt update - $STD apt install --only-upgrade -y mysql-server + $STD apt update || { + msg_error "Failed to update package list" + return 1 + } + $STD apt install --only-upgrade -y mysql-server || { + msg_error "Failed to upgrade MySQL" + return 1 + } fi return fi @@ -2348,7 +2468,7 @@ function setup_mysql() { if [[ "$NEED_INSTALL" == true ]]; then msg_info "Setup MySQL $MYSQL_VERSION" - + # Cleanup old repository files cleanup_old_repo_files "mysql" @@ -2383,18 +2503,24 @@ function setup_mysql() { if [[ "$DISTRO_ID" == "debian" ]] && [[ "$DISTRO_CODENAME" =~ ^(trixie|forky|sid)$ ]]; then # Install libaio1t64 if not present if ! dpkg -l libaio1t64 2>/dev/null | grep -q "^ii"; then - $STD apt update - $STD apt install -y libaio1t64 + $STD apt update || { + msg_error "Failed to update package list" + return 1 + } + $STD apt install -y libaio1t64 || { + msg_error "Failed to install libaio1t64" + return 1 + } fi - + # Create dummy libaio1 package for dependency satisfaction local TEMP_DIR="/tmp/libaio1-compat-$$" mkdir -p "$TEMP_DIR" cd "$TEMP_DIR" - + # Create control file mkdir -p DEBIAN - cat > DEBIAN/control <DEBIAN/control < libaio1t64 transition This is a transitional dummy package to satisfy dependencies on libaio1 while actually using libaio1t64 (time64 transition). EOF - + # Build the dummy package cd /tmp dpkg-deb -b "$TEMP_DIR" libaio1-compat.deb >/dev/null 2>&1 - + # Install it $STD dpkg -i libaio1-compat.deb - + # Cleanup rm -rf "$TEMP_DIR" libaio1-compat.deb fi @@ -2503,9 +2629,9 @@ function setup_nodejs() { if [[ "$NEED_NODE_INSTALL" == true ]]; then msg_info "Setup Node.js $NODE_VERSION" - + ensure_dependencies jq - + $STD apt purge -y nodejs # Cleanup old repository files @@ -2643,8 +2769,15 @@ function setup_php() { # Cleanup old repository files cleanup_old_repo_files "php" - $STD curl -fsSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb - $STD dpkg -i /tmp/debsuryorg-archive-keyring.deb + $STD curl -fsSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb || { + msg_error "Failed to download PHP repository keyring" + return 1 + } + $STD dpkg -i /tmp/debsuryorg-archive-keyring.deb || { + msg_error "Failed to install PHP repository keyring" + rm -f /tmp/debsuryorg-archive-keyring.deb + return 1 + } # Use helper function to get fallback suite local SUITE @@ -2658,7 +2791,10 @@ Components: main Architectures: amd64 arm64 Signed-By: /usr/share/keyrings/deb.sury.org-php.gpg EOF - $STD apt update + $STD apt update || { + msg_error "APT update failed for PHP repository" + return 1 + } fi # Build module list @@ -2676,12 +2812,18 @@ EOF # install apache2 with PHP support if requested if [[ "$PHP_APACHE" == "YES" ]]; then if ! dpkg -l | grep -q "libapache2-mod-php${PHP_VERSION}"; then - $STD apt install -y apache2 libapache2-mod-php${PHP_VERSION} + $STD apt install -y apache2 libapache2-mod-php${PHP_VERSION} || { + msg_error "Failed to install Apache with PHP module" + return 1 + } fi fi # setup / update PHP modules - $STD apt install -y $MODULE_LIST + $STD apt install -y $MODULE_LIST || { + msg_error "Failed to install PHP packages" + return 1 + } cache_installed_version "php" "$PHP_VERSION" # optional stop old PHP-FPM service @@ -2722,7 +2864,7 @@ EOF safe_service_restart php${PHP_VERSION}-fpm fi fi - + msg_ok "Setup PHP $PHP_VERSION" } @@ -2956,7 +3098,11 @@ function setup_ruby() { mkdir -p "$RBENV_DIR" cp -r "$TMP_DIR/rbenv-${RBENV_RELEASE}/." "$RBENV_DIR/" - cd "$RBENV_DIR" && src/configure && $STD make -C src + cd "$RBENV_DIR" && src/configure && $STD make -C src || { + msg_error "Failed to build rbenv" + rm -rf "$TMP_DIR" + return 1 + } local RUBY_BUILD_RELEASE RUBY_BUILD_RELEASE=$(curl -fsSL https://api.github.com/repos/rbenv/ruby-build/releases/latest | jq -r '.tag_name' | sed 's/^v//') @@ -2991,14 +3137,22 @@ function setup_ruby() { eval "$("$RBENV_BIN" init - bash)" if ! "$RBENV_BIN" versions --bare | grep -qx "$RUBY_VERSION"; then - $STD "$RBENV_BIN" install "$RUBY_VERSION" + $STD "$RBENV_BIN" install "$RUBY_VERSION" || { + msg_error "Failed to install Ruby $RUBY_VERSION" + rm -rf "$TMP_DIR" + return 1 + } fi "$RBENV_BIN" global "$RUBY_VERSION" hash -r if [[ "$RUBY_INSTALL_RAILS" == "true" ]]; then - $STD gem install rails + $STD gem install rails || { + msg_error "Failed to install Rails" + rm -rf "$TMP_DIR" + return 1 + } fi rm -rf "$TMP_DIR" @@ -3069,8 +3223,14 @@ function setup_clickhouse() { upgrade_package clickhouse-client else msg_info "Setup ClickHouse $CLICKHOUSE_VERSION" - $STD apt update - $STD apt install --only-upgrade -y clickhouse-server clickhouse-client + $STD apt update || { + msg_error "Failed to update package list" + return 1 + } + $STD apt install --only-upgrade -y clickhouse-server clickhouse-client || { + msg_error "Failed to upgrade ClickHouse" + return 1 + } cache_installed_version "clickhouse" "$CLICKHOUSE_VERSION" msg_ok "Setup ClickHouse $CLICKHOUSE_VERSION" fi @@ -3173,8 +3333,14 @@ function setup_rust() { msg_ok "Setup Rust $RUST_VERSION" else msg_info "Setup Rust" - $STD rustup install "$RUST_TOOLCHAIN" - $STD rustup default "$RUST_TOOLCHAIN" + $STD rustup install "$RUST_TOOLCHAIN" || { + msg_error "Failed to install Rust toolchain" + return 1 + } + $STD rustup default "$RUST_TOOLCHAIN" || { + msg_error "Failed to set default Rust toolchain" + return 1 + } $STD rustup update "$RUST_TOOLCHAIN" local RUST_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}') cache_installed_version "rust" "$RUST_VERSION" From 863e8d22ba45d1ae2f0a58ce5c8e0a0a3fd29866 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Wed, 22 Oct 2025 12:21:16 -0400 Subject: [PATCH 1521/1733] Move OpenCloud to deferred for now --- {frontend/public/json => ct/deferred}/opencloud.json | 0 ct/{ => deferred}/opencloud.sh | 0 install/{ => deferred}/opencloud-install.sh | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename {frontend/public/json => ct/deferred}/opencloud.json (100%) rename ct/{ => deferred}/opencloud.sh (100%) rename install/{ => deferred}/opencloud-install.sh (100%) diff --git a/frontend/public/json/opencloud.json b/ct/deferred/opencloud.json similarity index 100% rename from frontend/public/json/opencloud.json rename to ct/deferred/opencloud.json diff --git a/ct/opencloud.sh b/ct/deferred/opencloud.sh similarity index 100% rename from ct/opencloud.sh rename to ct/deferred/opencloud.sh diff --git a/install/opencloud-install.sh b/install/deferred/opencloud-install.sh similarity index 100% rename from install/opencloud-install.sh rename to install/deferred/opencloud-install.sh From 4a221ea9e3918425ec843ff1b70500a14f9421e5 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 22 Oct 2025 16:21:38 +0000 Subject: [PATCH 1522/1733] Update .app files --- ct/headers/gitea-mirror | 6 ++++++ ct/headers/opencloud | 6 ------ 2 files changed, 6 insertions(+), 6 deletions(-) create mode 100644 ct/headers/gitea-mirror delete mode 100644 ct/headers/opencloud diff --git a/ct/headers/gitea-mirror b/ct/headers/gitea-mirror new file mode 100644 index 000000000..57003b058 --- /dev/null +++ b/ct/headers/gitea-mirror @@ -0,0 +1,6 @@ + _ __ _ + ____ _(_) /____ ____ _ ____ ___ (_)_____________ _____ + / __ `/ / __/ _ \/ __ `/_____/ __ `__ \/ / ___/ ___/ __ \/ ___/ + / /_/ / / /_/ __/ /_/ /_____/ / / / / / / / / / / /_/ / / + \__, /_/\__/\___/\__,_/ /_/ /_/ /_/_/_/ /_/ \____/_/ +/____/ diff --git a/ct/headers/opencloud b/ct/headers/opencloud deleted file mode 100644 index 6a32b1fae..000000000 --- a/ct/headers/opencloud +++ /dev/null @@ -1,6 +0,0 @@ - ____ ________ __ - / __ \____ ___ ____ / ____/ /___ __ ______/ / - / / / / __ \/ _ \/ __ \/ / / / __ \/ / / / __ / -/ /_/ / /_/ / __/ / / / /___/ / /_/ / /_/ / /_/ / -\____/ .___/\___/_/ /_/\____/_/\____/\__,_/\__,_/ - /_/ From dad22d88fdd69c0fae14d07f102bfa3077bad16e Mon Sep 17 00:00:00 2001 From: vhsdream Date: Wed, 22 Oct 2025 16:13:35 -0400 Subject: [PATCH 1523/1733] Add files for PatchMon --- ct/patchmon.sh | 79 ++++++++ frontend/public/json/patchmon.json | 35 ++++ install/patchmon-install.sh | 289 +++++++++++++++++++++++++++++ 3 files changed, 403 insertions(+) create mode 100644 ct/patchmon.sh create mode 100644 frontend/public/json/patchmon.json create mode 100644 install/patchmon-install.sh diff --git a/ct/patchmon.sh b/ct/patchmon.sh new file mode 100644 index 000000000..9ea0f5a5a --- /dev/null +++ b/ct/patchmon.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: vhsdream +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/PatchMon/PatchMon + +APP="PatchMon" +APP_NAME=${APP,,} +var_tags="${var_tags:-monitoring}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -d "/opt/patchmon" ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "PatchMon" "PatchMon/PatchMon"; then + + msg_info "Stopping $APP" + systemctl stop patchmon-server + msg_ok "Stopped $APP" + + msg_info "Creating Backup" + cp /opt/patchmon/backend/.env /opt/backend.env + cp /opt/patchmon/frontend/.env /opt/frontend.env + msg_ok "Backup Created" + + rm -rf /opt/patchmon + fetch_and_deploy_gh_release "PatchMon" "PatchMon/PatchMon" "tarball" "latest" "/opt/patchmon" + + msg_info "Updating ${APP}" + cd /opt/patchmon + export NODE_ENV=production + export NPM_CONFIG_CACHE=/opt/patchmon/.npm + export NPM_CONFIG_PREFIX=/opt/patchmon/.npm-global + export NPM_CONFIG_TMP=/opt/patchmon/.npm/tmp + $STD npm install --omit=dev --no-audit --no-fund --no-save --ignore-scripts + cd /opt/patchmon/backend + $STD npm install --omit=dev --no-audit --no-fund --no-save --ignore-scripts + cd /opt/patchmon/frontend + $STD npm install --no-audit --no-fund --no-save --ignore-scripts + $STD npm run build + cd /opt/patchmon/backend + $STD npx prisma migrate deploy + mv /opt/backend.env /opt/patchmon/backend/.env + mv /opt/frontend.env /opt/patchmon/frontend/.env + msg_ok "Updated ${APP}" + + msg_info "Starting $APP" + systemctl start patchmon-server + msg_ok "Started $APP" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" diff --git a/frontend/public/json/patchmon.json b/frontend/public/json/patchmon.json new file mode 100644 index 000000000..039a359a6 --- /dev/null +++ b/frontend/public/json/patchmon.json @@ -0,0 +1,35 @@ +{ + "name": "PatchMon", + "slug": "patchmon", + "categories": [ + 9 + ], + "date_created": "2025-10-23", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 3399, + "documentation": "https://docs.patchmon.net", + "website": "https://patchmon.net", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/patchmon.webp", + "config_path": "", + "description": "Monitor Linux patches across all your hosts with real-time visibility, security update tracking, and comprehensive package management.", + "install_methods": [ + { + "type": "default", + "script": "ct/patchmon.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 4, + "os": "debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} diff --git a/install/patchmon-install.sh b/install/patchmon-install.sh new file mode 100644 index 000000000..d6f69b6b1 --- /dev/null +++ b/install/patchmon-install.sh @@ -0,0 +1,289 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: vhsdream +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/PatcMmon/PatchMon + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt install -y \ + build-essential \ + gcc \ + nginx \ + redis-server +msg_ok "Installed Dependencies" + +NODE_VERSION="22" setup_nodejs +PG_VERSION="17" setup_postgresql + +msg_info "Creating PostgreSQL Database" +DB_NAME=patchmon_db +DB_USER=patchmon_usr +DB_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" +$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" +$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" +$STD sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER;" + +cat <~/patchmon.creds +PatchMon Credentials +PatchMon Database Name: $DB_NAME +PatchMon Database User: $DB_USER +PatchMon Database Password: $DB_PASS +EOF +msg_ok "Created PostgreSQL Database" + +fetch_and_deploy_gh_release "PatchMon" "PatchMon/PatchMon" "tarball" "latest" "/opt/patchmon" + +msg_info "Configuring PatchMon" +cd /opt/patchmon +export NODE_ENV=production +export NPM_CONFIG_CACHE=/opt/patchmon/.npm +export NPM_CONFIG_PREFIX=/opt/patchmon/.npm-global +export NPM_CONFIG_TMP=/opt/patchmon/.npm/tmp +$STD npm install --omit=dev --no-audit --no-fund --no-save --ignore-scripts +cd /opt/patchmon/backend +$STD npm install --omit=dev --no-audit --no-fund --no-save --ignore-scripts +cd /opt/patchmon/frontend +$STD npm install --no-audit --no-fund --no-save --ignore-scripts +$STD npm run build +msg_ok "Configured PatchMon" + +msg_info "Creating env files" +JWT_SECRET="$(openssl rand -base64 64 | tr -d "=+/" | cut -c1-50)" +LOCAL_IP="$(hostname -I | awk '{print $1}')" +cat </opt/patchmon/backend/.env +# Database Configuration +DATABASE_URL="postgresql://$DB_USER:$DB_PASS@localhost:5432/$DB_NAME" +PY_THRESHOLD=3M_DB_CONN_MAX_ATTEMPTS=30 +PM_DB_CONN_WAIT_INTERVAL=2 + +# JWT Configuration +JWT_SECRET="$JWT_SECRET" +JWT_EXPIRES_IN=1h +JWT_REFRESH_EXPIRES_IN=7d + +# Server Configuration +PORT=3399 +NODE_ENV=production + +# API Configuration +API_VERSION=v1 + +# CORS Configuration +CORS_ORIGIN="http://$LOCAL_IP" + +# Session Configuration +SESSION_INACTIVITY_TIMEOUT_MINUTES=30 + +# User Configuration +DEFAULT_USER_ROLE=user + +# Rate Limiting (times in milliseconds) +RATE_LIMIT_WINDOW_MS=900000 +RATE_LIMIT_MAX=5000 +AUTH_RATE_LIMIT_WINDOW_MS=600000 +AUTH_RATE_LIMIT_MAX=500 +AGENT_RATE_LIMIT_WINDOW_MS=60000 +AGENT_RATE_LIMIT_MAX=1000 + +# Redis Configuration +REDIS_HOST=localhost +REDIS_PORT=6379 + +# Logging +LOG_LEVEL=info +ENABLE_LOGGING=true + +# TFA Configuration +TFA_REMEMBER_ME_EXPIRES_IN=30d +TFA_MAX_REMEMBER_SESSIONS=5 +TFA_SUSPICIOUS_ACTIVITY_THRESHOLD=3 +EOF + +cat </opt/patchmon/frontend/.env +VITE_API_URL=http://$LOCAL_IP/api/v1 +VITE_APP_NAME=PatchMon +VITE_APP_VERSION=1.3.0 +EOF +msg_ok "Created env files" + +msg_info "Running database migrations" +cd /opt/patchmon/backend +$STD npx prisma migrate deploy +$STD npx prisma generate +msg_ok "Database migrations complete" + +msg_info "Configuring Nginx" +cat </etc/nginx/sites-available/patchmon.conf +server { + listen 80; + server_name $LOCAL_IP; + + # Security headers + add_header X-Frame-Options DENY always; + add_header X-Content-Type-Options nosniff always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + + # Frontend + location / { + root /opt/patchmon/frontend/dist; + try_files \$uri \$uri/ /index.html; + } + + # Bull Board proxy + location /bullboard { + proxy_pass http://127.0.0.1:3399; + proxy_http_version 1.1; + proxy_set_header Upgrade \$http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto \$scheme; + proxy_set_header X-Forwarded-Host \$host; + proxy_set_header Cookie \$http_cookie; + proxy_cache_bypass \$http_upgrade; + proxy_read_timeout 300s; + proxy_connect_timeout 75s; + + # Enable cookie passthrough + proxy_pass_header Set-Cookie; + proxy_cookie_path / /; + + # Preserve original client IP + proxy_set_header X-Original-Forwarded-For \$http_x_forwarded_for; + if (\$request_method = 'OPTIONS') { + return 204; + } + } + + # API proxy + location /api/ { + proxy_pass http://127.0.0.1:3399; + proxy_http_version 1.1; + proxy_set_header Upgrade \$http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto \$scheme; + proxy_cache_bypass \$http_upgrade; + proxy_read_timeout 300s; + proxy_connect_timeout 75s; + + # Preserve original client IP + proxy_set_header X-Original-Forwarded-For \$http_x_forwarded_for; + if (\$request_method = 'OPTIONS') { + return 204; + } + } + + # Static assets caching (exclude Bull Board assets) + location ~* ^/(?!bullboard).*\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + root /opt/patchmon/frontend/dist; + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # Health check endpoint + location /health { + proxy_pass http://127.0.0.1:3399/health; + access_log off; + } +} +EOF +ln -sf /etc/nginx/sites-available/patchmon.conf /etc/nginx/sites-enabled/ +rm -f /etc/nginx/sites-enabled/default +$STD nginx -t +systemctl restart nginx +msg_ok "Configured Nginx" + +msg_info "Creating service" +cat </etc/systemd/system/patchmon-server.service +[Unit] +Description=PatchMon Service +After=network.target postgresql.service + +[Service] +Type=simple +WorkingDirectory=/opt/patchmon/backend +ExecStart=/usr/bin/node src/server.js +Restart=always +RestartSec=10 +Environment=NODE_ENV=production +Environment=PATH=/usr/bin:/usr/local/bin +NoNewPrivileges=true +PrivateTmp=true +ProtectSystem=strict +ProtectHome=true +ReadWritePaths=/opt/patchmon + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now patchmon-server +msg_ok "Created and started service" + +msg_info "Populating server settings in DB" +cat </opt/patchmon/backend/update-settings.js +const { PrismaClient } = require('@prisma/client'); +const prisma = new PrismaClient(); + +async function updateSettings() { + try { + const settingsData = { + server_url: 'http://$LOCAL_IP', + server_protocol: 'http', + server_host: '$LOCAL_IP', + server_port: 3399, + update_interval: 60, + auto_update: true + }; + + if (existingSettings) { + // Update existing settings + await prisma.settings.update({ + where: { id: existingSettings.id }, + data: settingsData + }); + } else { + // Create new settings record + await prisma.settings.create({ + data: settingsData + }); + } + + console.log('✅ Database settings updated successfully'); + } catch (error) { + console.error('❌ Error updating settings:', error.message); + process.exit(1); + } finally { + await prisma.\$disconnect(); + } +} + +updateSettings(); +EOF + +cd /opt/patchmon/backend +$STD node update-settings.js +msg_ok "Server settings populated successfully" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt -y autoremove +$STD apt -y autoclean +$STD apt -y clean +msg_ok "Cleaned" From e0f5480f23f4cc55cbebf00dfaddb928ab01bdfe Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 22 Oct 2025 20:14:05 +0000 Subject: [PATCH 1524/1733] Update .app files --- ct/headers/patchmon | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/patchmon diff --git a/ct/headers/patchmon b/ct/headers/patchmon new file mode 100644 index 000000000..87d928deb --- /dev/null +++ b/ct/headers/patchmon @@ -0,0 +1,6 @@ + ____ __ __ __ ___ + / __ \____ _/ /______/ /_ / |/ /___ ____ + / /_/ / __ `/ __/ ___/ __ \/ /|_/ / __ \/ __ \ + / ____/ /_/ / /_/ /__/ / / / / / / /_/ / / / / +/_/ \__,_/\__/\___/_/ /_/_/ /_/\____/_/ /_/ + From b3fcb5addffccb6b32573363f4a7270cef272ea3 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Wed, 22 Oct 2025 17:07:58 -0400 Subject: [PATCH 1525/1733] Tweak template search debug test to avoid unbound var --- misc/build.func | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/misc/build.func b/misc/build.func index 016279e06..a7336f821 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2999,7 +2999,7 @@ create_lxc_container() { msg_info "Searching for template '$TEMPLATE_SEARCH'" # Build regex patterns outside awk/grep for clarity - SEARCH_PATTERN="^${TEMPLATE_SEARCH}-" + SEARCH_PATTERN="^${TEMPLATE_SEARCH}" echo "[DEBUG] TEMPLATE_SEARCH='$TEMPLATE_SEARCH'" echo "[DEBUG] SEARCH_PATTERN='$SEARCH_PATTERN'" @@ -3016,9 +3016,11 @@ create_lxc_container() { echo "[DEBUG] pveam available output (first 5 lines with .tar files):" pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | head -5 | sed 's/^/ /' + set +u mapfile -t ONLINE_TEMPLATES \ \ - echo "[DEBUG] After filtering: ${#ONLINE_TEMPLATES[@]} online templates found" < <(pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | awk -F'\t' '{print $1}' | grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V 2>/dev/null || true) + echo "[DEBUG] After filtering: ${#ONLINE_TEMPLATES[@]} online templates found" < <(pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | awk '{print $2}' | grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V 2>/dev/null || true) + set -u if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then echo "[DEBUG] Online templates:" for tmpl in "${ONLINE_TEMPLATES[@]}"; do From 55903a8d5e7f7051eae5fa2d224304f93ac03d55 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 24 Oct 2025 09:06:02 +0200 Subject: [PATCH 1526/1733] Fix Array out of bounds bug in build.func --- misc/build.func | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/misc/build.func b/misc/build.func index a7336f821..94a411158 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3035,8 +3035,11 @@ create_lxc_container() { msg_debug "Found ${#LOCAL_TEMPLATES[@]} local templates, ${#ONLINE_TEMPLATES[@]} online templates" if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then msg_debug "First 3 online templates:" - for i in {0..2}; do - [[ -n "${ONLINE_TEMPLATES[$i]}" ]] && msg_debug " [$i]: ${ONLINE_TEMPLATES[$i]}" + count=0 + for idx in "${!ONLINE_TEMPLATES[@]}"; do + msg_debug " [$idx]: ${ONLINE_TEMPLATES[$idx]}" + ((count++)) + [[ $count -ge 3 ]] && break done fi msg_debug "ONLINE_TEMPLATE='$ONLINE_TEMPLATE'" From dae2223a38443ca7128ee62f35ad1c4b455450df Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 24 Oct 2025 09:10:27 +0200 Subject: [PATCH 1527/1733] Comment out debug output in create_lxc_container Disabled various debug echo and msg_debug statements in the create_lxc_container function to reduce console output during normal operation. This helps keep logs cleaner while retaining the code for future debugging if needed. --- misc/build.func | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/misc/build.func b/misc/build.func index a7336f821..a97aea05f 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3001,9 +3001,9 @@ create_lxc_container() { # Build regex patterns outside awk/grep for clarity SEARCH_PATTERN="^${TEMPLATE_SEARCH}" - echo "[DEBUG] TEMPLATE_SEARCH='$TEMPLATE_SEARCH'" - echo "[DEBUG] SEARCH_PATTERN='$SEARCH_PATTERN'" - echo "[DEBUG] TEMPLATE_PATTERN='$TEMPLATE_PATTERN'" + #echo "[DEBUG] TEMPLATE_SEARCH='$TEMPLATE_SEARCH'" + #echo "[DEBUG] SEARCH_PATTERN='$SEARCH_PATTERN'" + #echo "[DEBUG] TEMPLATE_PATTERN='$TEMPLATE_PATTERN'" mapfile -t LOCAL_TEMPLATES < <( pveam list "$TEMPLATE_STORAGE" 2>/dev/null | @@ -3013,16 +3013,15 @@ create_lxc_container() { pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)." - echo "[DEBUG] pveam available output (first 5 lines with .tar files):" + #echo "[DEBUG] pveam available output (first 5 lines with .tar files):" pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | head -5 | sed 's/^/ /' set +u - mapfile -t ONLINE_TEMPLATES \ - \ - echo "[DEBUG] After filtering: ${#ONLINE_TEMPLATES[@]} online templates found" < <(pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | awk '{print $2}' | grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V 2>/dev/null || true) + mapfile -t ONLINE_TEMPLATES < <(pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | awk '{print $2}' | grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V 2>/dev/null || true) + #echo "[DEBUG] After filtering: ${#ONLINE_TEMPLATES[@]} online templates found" set -u if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then - echo "[DEBUG] Online templates:" + #echo "[DEBUG] Online templates:" for tmpl in "${ONLINE_TEMPLATES[@]}"; do echo " - $tmpl" done @@ -3031,15 +3030,15 @@ create_lxc_container() { ONLINE_TEMPLATE="" [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" - msg_debug "SEARCH_PATTERN='${SEARCH_PATTERN}' TEMPLATE_PATTERN='${TEMPLATE_PATTERN}'" - msg_debug "Found ${#LOCAL_TEMPLATES[@]} local templates, ${#ONLINE_TEMPLATES[@]} online templates" + #msg_debug "SEARCH_PATTERN='${SEARCH_PATTERN}' TEMPLATE_PATTERN='${TEMPLATE_PATTERN}'" + #msg_debug "Found ${#LOCAL_TEMPLATES[@]} local templates, ${#ONLINE_TEMPLATES[@]} online templates" if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then - msg_debug "First 3 online templates:" + #msg_debug "First 3 online templates:" for i in {0..2}; do [[ -n "${ONLINE_TEMPLATES[$i]}" ]] && msg_debug " [$i]: ${ONLINE_TEMPLATES[$i]}" done fi - msg_debug "ONLINE_TEMPLATE='$ONLINE_TEMPLATE'" + #msg_debug "ONLINE_TEMPLATE='$ONLINE_TEMPLATE'" if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then TEMPLATE="${LOCAL_TEMPLATES[-1]}" From a4e8e1e44ff371522c1f67cb6d72ce73c2009a22 Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 24 Oct 2025 09:17:26 +0200 Subject: [PATCH 1528/1733] Fix a bug while prining the LXC-ID during advanced install. --- misc/build.func | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/misc/build.func b/misc/build.func index b76973fd5..f8fcaa5fb 100644 --- a/misc/build.func +++ b/misc/build.func @@ -504,16 +504,14 @@ advanced_settings() { fi done - if CT_ID=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Container ID" 8 58 "$NEXTID" --title "CONTAINER ID" 3>&1 1>&2 2>&3); then + if CT_ID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Container ID" 8 58 "$NEXTID" --title "CONTAINER ID" 3>&1 1>&2 2>&3); then if [ -z "$CT_ID" ]; then CT_ID="$NEXTID" - echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" - else - echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" fi else exit_script fi + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" while true; do if CT_NAME=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 "$NSAPP" --title "HOSTNAME" 3>&1 1>&2 2>&3); then From dc804416b0e9825ac5d5b26665310922ec58de9f Mon Sep 17 00:00:00 2001 From: Michel Roegl-Brunner Date: Fri, 24 Oct 2025 09:28:37 +0200 Subject: [PATCH 1529/1733] Test Patchmon --- install/patchmon-install.sh | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/install/patchmon-install.sh b/install/patchmon-install.sh index d6f69b6b1..0643922f6 100644 --- a/install/patchmon-install.sh +++ b/install/patchmon-install.sh @@ -53,7 +53,8 @@ $STD npm install --omit=dev --no-audit --no-fund --no-save --ignore-scripts cd /opt/patchmon/backend $STD npm install --omit=dev --no-audit --no-fund --no-save --ignore-scripts cd /opt/patchmon/frontend -$STD npm install --no-audit --no-fund --no-save --ignore-scripts +export npm_config_production=false +$STD npm install --no-audit --no-fund --no-save $STD npm run build msg_ok "Configured PatchMon" @@ -127,13 +128,13 @@ cat </etc/nginx/sites-available/patchmon.conf server { listen 80; server_name $LOCAL_IP; - + # Security headers add_header X-Frame-Options DENY always; add_header X-Content-Type-Options nosniff always; add_header X-XSS-Protection "1; mode=block" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; - + # Frontend location / { root /opt/patchmon/frontend/dist; @@ -155,11 +156,11 @@ server { proxy_cache_bypass \$http_upgrade; proxy_read_timeout 300s; proxy_connect_timeout 75s; - + # Enable cookie passthrough proxy_pass_header Set-Cookie; proxy_cookie_path / /; - + # Preserve original client IP proxy_set_header X-Original-Forwarded-For \$http_x_forwarded_for; if (\$request_method = 'OPTIONS') { @@ -180,7 +181,7 @@ server { proxy_cache_bypass \$http_upgrade; proxy_read_timeout 300s; proxy_connect_timeout 75s; - + # Preserve original client IP proxy_set_header X-Original-Forwarded-For \$http_x_forwarded_for; if (\$request_method = 'OPTIONS') { @@ -194,7 +195,7 @@ server { expires 1y; add_header Cache-Control "public, immutable"; } - + # Health check endpoint location /health { proxy_pass http://127.0.0.1:3399/health; From 05f56e9ef1fedf465187afa6be74b0347142bc64 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Fri, 24 Oct 2025 09:11:57 -0400 Subject: [PATCH 1530/1733] Update PatchMon --- ct/patchmon.sh | 11 ++++------- install/patchmon-install.sh | 27 +++++++++++++++------------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/ct/patchmon.sh b/ct/patchmon.sh index 9ea0f5a5a..284400bc6 100644 --- a/ct/patchmon.sh +++ b/ct/patchmon.sh @@ -47,14 +47,11 @@ function update_script() { msg_info "Updating ${APP}" cd /opt/patchmon export NODE_ENV=production - export NPM_CONFIG_CACHE=/opt/patchmon/.npm - export NPM_CONFIG_PREFIX=/opt/patchmon/.npm-global - export NPM_CONFIG_TMP=/opt/patchmon/.npm/tmp - $STD npm install --omit=dev --no-audit --no-fund --no-save --ignore-scripts - cd /opt/patchmon/backend - $STD npm install --omit=dev --no-audit --no-fund --no-save --ignore-scripts - cd /opt/patchmon/frontend $STD npm install --no-audit --no-fund --no-save --ignore-scripts + cd /opt/patchmon/backend + $STD npm install --no-audit --no-fund --no-save --ignore-scripts + cd /opt/patchmon/frontend + $STD npm install --include=dev --no-audit --no-fund --no-save --ignore-scripts $STD npm run build cd /opt/patchmon/backend $STD npx prisma migrate deploy diff --git a/install/patchmon-install.sh b/install/patchmon-install.sh index 0643922f6..250297759 100644 --- a/install/patchmon-install.sh +++ b/install/patchmon-install.sh @@ -46,15 +46,11 @@ fetch_and_deploy_gh_release "PatchMon" "PatchMon/PatchMon" "tarball" "latest" "/ msg_info "Configuring PatchMon" cd /opt/patchmon export NODE_ENV=production -export NPM_CONFIG_CACHE=/opt/patchmon/.npm -export NPM_CONFIG_PREFIX=/opt/patchmon/.npm-global -export NPM_CONFIG_TMP=/opt/patchmon/.npm/tmp -$STD npm install --omit=dev --no-audit --no-fund --no-save --ignore-scripts +$STD npm install --no-audit --no-fund --no-save --ignore-scripts cd /opt/patchmon/backend -$STD npm install --omit=dev --no-audit --no-fund --no-save --ignore-scripts +$STD npm install --no-audit --no-fund --no-save --ignore-scripts cd /opt/patchmon/frontend -export npm_config_production=false -$STD npm install --no-audit --no-fund --no-save +$STD npm install --include=dev --no-audit --no-fund --no-save --ignore-scripts $STD npm run build msg_ok "Configured PatchMon" @@ -156,11 +152,11 @@ server { proxy_cache_bypass \$http_upgrade; proxy_read_timeout 300s; proxy_connect_timeout 75s; - + # Enable cookie passthrough proxy_pass_header Set-Cookie; proxy_cookie_path / /; - + # Preserve original client IP proxy_set_header X-Original-Forwarded-For \$http_x_forwarded_for; if (\$request_method = 'OPTIONS') { @@ -181,7 +177,7 @@ server { proxy_cache_bypass \$http_upgrade; proxy_read_timeout 300s; proxy_connect_timeout 75s; - + # Preserve original client IP proxy_set_header X-Original-Forwarded-For \$http_x_forwarded_for; if (\$request_method = 'OPTIONS') { @@ -195,7 +191,7 @@ server { expires 1y; add_header Cache-Control "public, immutable"; } - + # Health check endpoint location /health { proxy_pass http://127.0.0.1:3399/health; @@ -238,17 +234,24 @@ msg_ok "Created and started service" msg_info "Populating server settings in DB" cat </opt/patchmon/backend/update-settings.js const { PrismaClient } = require('@prisma/client'); +const { v4: uuidv4 } = require('uuid'); const prisma = new PrismaClient(); async function updateSettings() { try { + const existingSettings = await prisma.settings.findFirst(); + const settingsData = { + id: uuidv4(), server_url: 'http://$LOCAL_IP', server_protocol: 'http', server_host: '$LOCAL_IP', server_port: 3399, update_interval: 60, - auto_update: true + auto_update: true, + signup_enabled: false, + ignore_ssl_self_signed: false, + updated_at: new Date() }; if (existingSettings) { From 8a7ddb05dc9a3225abe4897fd49fe616c5f47100 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 24 Oct 2025 15:16:37 +0200 Subject: [PATCH 1531/1733] Improve MariaDB setup and repository update logic Enhanced the setup_mariadb function to better handle MariaDB repository updates, version checks, and upgrade flows. The script now updates the repository if the version differs, provides clearer messaging, and ensures proper upgrade steps for both repository and package updates. --- misc/tools.func | 80 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 60 insertions(+), 20 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index ee35767cf..5be58fa57 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -2231,31 +2231,71 @@ setup_mariadb() { local CACHED_VERSION CACHED_VERSION=$(get_cached_version "mariadb") - if [[ "$CURRENT_VERSION" == "$MARIADB_VERSION" ]]; then - if [[ "$CACHED_VERSION" == "$MARIADB_VERSION" ]]; then - upgrade_package mariadb-server - upgrade_package mariadb-client - else - $STD apt update || { - msg_error "Failed to update package list" - return 1 - } - $STD apt install --only-upgrade -y mariadb-server mariadb-client || { - msg_error "Failed to upgrade MariaDB" - return 1 - } - cache_installed_version "mariadb" "$MARIADB_VERSION" + # Check if MariaDB is already installed and needs update + if [[ -n "$CURRENT_VERSION" ]]; then + # Check if repository exists and needs update + local REPO_VERSION="" + if [[ -f /etc/apt/sources.list.d/mariadb.sources ]]; then + REPO_VERSION=$(grep -oP 'repo/\K[0-9]+\.[0-9]+\.[0-9]+' /etc/apt/sources.list.d/mariadb.sources 2>/dev/null || echo "") + fi + + if [[ -n "$REPO_VERSION" && "$REPO_VERSION" != "$MARIADB_VERSION" ]]; then + msg_info "Update MariaDB repository from $REPO_VERSION to $MARIADB_VERSION" + + # Remove old repository + cleanup_old_repo_files "mariadb" + + # Use helper function to get fallback suite + local SUITE + DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "http://mirror.mariadb.org/repo/${MARIADB_VERSION}/${DISTRO_ID}") + + # Setup new repository + setup_deb822_repo \ + "mariadb" \ + "https://mariadb.org/mariadb_release_signing_key.asc" \ + "http://mirror.mariadb.org/repo/${MARIADB_VERSION}/${DISTRO_ID}" \ + "$SUITE" \ + "main" \ + "amd64 arm64" || { + msg_error "Failed to update MariaDB repository" + return 1 + } + + msg_ok "Update MariaDB repository from $REPO_VERSION to $MARIADB_VERSION" + fi + + # Now perform the update + if [[ "$CURRENT_VERSION" == "$MARIADB_VERSION" ]]; then + if [[ "$CACHED_VERSION" == "$MARIADB_VERSION" ]]; then + msg_info "Update MariaDB $MARIADB_VERSION" + upgrade_package mariadb-server + upgrade_package mariadb-client + msg_ok "Update MariaDB $MARIADB_VERSION" + else + msg_info "Update MariaDB $MARIADB_VERSION" + $STD apt update || { + msg_error "Failed to update package list" + return 1 + } + $STD apt install --only-upgrade -y mariadb-server mariadb-client || { + msg_error "Failed to upgrade MariaDB" + return 1 + } + cache_installed_version "mariadb" "$MARIADB_VERSION" + msg_ok "Update MariaDB $MARIADB_VERSION" + fi + return 0 + else + # Version mismatch - need to upgrade to new version + msg_info "Upgrade MariaDB from $CURRENT_VERSION to $MARIADB_VERSION" + $STD systemctl stop mariadb >/dev/null 2>&1 || true fi - return 0 fi msg_info "Setup MariaDB $MARIADB_VERSION" - if [[ -n "$CURRENT_VERSION" ]]; then - $STD systemctl stop mariadb >/dev/null 2>&1 || true - $STD apt purge -y 'mariadb*' || true - fi - # Ensure APT is working before proceeding ensure_apt_working || return 1 From 1442ebea88be4f45a3ff6f6d92dafd3c0fb6f4ac Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 24 Oct 2025 15:20:25 +0200 Subject: [PATCH 1532/1733] migrate from VE to VED --- misc/tools.func | 239 +++++++++++++++++++++++++++--------------------- 1 file changed, 135 insertions(+), 104 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 5be58fa57..cc27410b3 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -477,7 +477,7 @@ get_default_python_version() { # ------------------------------------------------------------------------------ get_default_nodejs_version() { # Always return current LTS (as of 2025) - echo "20" + echo "22" } # ------------------------------------------------------------------------------ @@ -901,22 +901,22 @@ check_for_gh_release() { if [[ "$current" != "$pin_clean" ]]; then CHECK_UPDATE_RELEASE="$match_raw" - msg_ok "Checking for update: ${app}" + msg_ok "Update available: ${app} ${current:-not installed} → ${pin_clean}" return 0 fi - msg_ok "Checking for update: ${app}" + msg_error "No update available: ${app} is not installed!" return 1 fi # No pinning → use latest if [[ -z "$current" || "$current" != "$latest_clean" ]]; then CHECK_UPDATE_RELEASE="$latest_raw" - msg_ok "Checking for update: ${app}" + msg_ok "Update available: ${app} ${current:-not installed} → ${latest_clean}" return 0 fi - msg_ok "Checking for update: ${app}" + msg_ok "No update available: ${app} (${latest_clean})" return 1 } @@ -1141,7 +1141,7 @@ function fetch_and_deploy_gh_release() { rm -rf "${target:?}/"* fi - tar -xzf "$tmpdir/$filename" -C "$tmpdir" || { + tar --no-same-owner -xzf "$tmpdir/$filename" -C "$tmpdir" || { msg_error "Failed to extract tarball" rm -rf "$tmpdir" return 1 @@ -1263,7 +1263,7 @@ function fetch_and_deploy_gh_release() { return 1 } elif [[ "$filename" == *.tar.* || "$filename" == *.tgz ]]; then - tar -xf "$tmpdir/$filename" -C "$unpack_tmp" || { + tar --no-same-owner -xf "$tmpdir/$filename" -C "$unpack_tmp" || { msg_error "Failed to extract TAR archive" rm -rf "$tmpdir" "$unpack_tmp" return 1 @@ -2360,8 +2360,8 @@ setup_mariadb() { function setup_mongodb() { local MONGO_VERSION="${MONGO_VERSION:-8.0}" local DISTRO_ID DISTRO_CODENAME - DISTRO_ID=$(awk -F= '/^ID=/{ gsub(/"/,"",$2); print $2 }' /etc/os-release) - DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{ print $2 }' /etc/os-release) + DISTRO_ID=$(get_os_info id) + DISTRO_CODENAME=$(get_os_info codename) # Check AVX support if ! grep -qm1 'avx[^ ]*' /proc/cpuinfo; then @@ -2388,7 +2388,7 @@ function setup_mongodb() { esac local INSTALLED_VERSION="" - if command -v mongod >/dev/null; then + if command -v mongod >/dev/null 2>&1; then INSTALLED_VERSION=$(mongod --version | awk '/db version/{print $3}' | cut -d. -f1,2) fi @@ -2415,26 +2415,72 @@ function setup_mongodb() { msg_info "Setup MongoDB $MONGO_VERSION" if [[ -n "$INSTALLED_VERSION" ]]; then - $STD systemctl stop mongod || true - $STD apt purge -y mongodb-org || true + $STD systemctl stop mongod 2>/dev/null || true + $STD apt purge -y mongodb-org 2>/dev/null || true fi - # Cleanup old repository files cleanup_old_repo_files "mongodb-org-${MONGO_VERSION}" - - # Cleanup any orphaned .sources files from other apps cleanup_orphaned_sources - # Use helper function to get fallback suite + # Map distro codenames to MongoDB-supported suites local SUITE - SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "$MONGO_BASE_URL") + if [[ "$DISTRO_ID" == "debian" ]]; then + case "$DISTRO_CODENAME" in + trixie | forky | sid) + # Debian 13+ and testing/unstable use Bookworm (MongoDB doesn't support newer) + SUITE="bookworm" + ;; + bookworm) + SUITE="bookworm" + ;; + bullseye) + SUITE="bullseye" + ;; + buster) + SUITE="buster" + ;; + *) + # Fallback: try bookworm for unknown Debian releases + msg_warn "Unknown Debian release '${DISTRO_CODENAME}', using bookworm" + SUITE="bookworm" + ;; + esac + elif [[ "$DISTRO_ID" == "ubuntu" ]]; then + case "$DISTRO_CODENAME" in + oracular | plucky) + # Ubuntu 24.10+ uses noble + SUITE="noble" + ;; + noble) + SUITE="noble" + ;; + jammy) + SUITE="jammy" + ;; + focal) + SUITE="focal" + ;; + *) + # Fallback: try noble for unknown Ubuntu releases + msg_warn "Unknown Ubuntu release '${DISTRO_CODENAME}', using noble" + SUITE="noble" + ;; + esac + fi + + # Verify MongoDB repository is available (MongoDB has nested structure) + if ! curl -fsSL --max-time 10 "${MONGO_BASE_URL}/dists/${SUITE}/mongodb-org/${MONGO_VERSION}/Release" &>/dev/null; then + msg_error "MongoDB ${MONGO_VERSION} repository not available for ${DISTRO_ID}-${SUITE}" + msg_error "Please check: ${MONGO_BASE_URL}/dists/${SUITE}/mongodb-org/${MONGO_VERSION}/" + return 1 + fi - # Use standardized repo setup mkdir -p /etc/apt/keyrings - curl -fsSL "https://pgp.mongodb.com/server-${MONGO_VERSION}.asc" | gpg --dearmor --yes -o "/etc/apt/keyrings/mongodb-${MONGO_VERSION}.gpg" || { + if ! curl -fsSL "https://www.mongodb.org/static/pgp/server-${MONGO_VERSION}.asc" | + gpg --dearmor --yes -o "/etc/apt/keyrings/mongodb-server-${MONGO_VERSION}.gpg"; then msg_error "Failed to download or import MongoDB GPG key" return 1 - } + fi cat </etc/apt/sources.list.d/mongodb-org-${MONGO_VERSION}.sources Types: deb @@ -2442,11 +2488,11 @@ URIs: ${MONGO_BASE_URL} Suites: ${SUITE}/mongodb-org/${MONGO_VERSION} Components: ${REPO_COMPONENT} Architectures: amd64 arm64 -Signed-By: /etc/apt/keyrings/mongodb-${MONGO_VERSION}.gpg +Signed-By: /etc/apt/keyrings/mongodb-server-${MONGO_VERSION}.gpg EOF $STD apt update || { - msg_error "APT update failed — invalid MongoDB repo for ${DISTRO_ID}-${DISTRO_CODENAME}?" + msg_error "APT update failed — invalid MongoDB repo for ${DISTRO_ID}-${DISTRO_CODENAME}? (Suite: ${SUITE})" return 1 } @@ -2461,6 +2507,7 @@ EOF $STD systemctl enable mongod safe_service_restart mongod cache_installed_version "mongodb" "$MONGO_VERSION" + msg_ok "Setup MongoDB $MONGO_VERSION" } @@ -2509,81 +2556,46 @@ function setup_mysql() { if [[ "$NEED_INSTALL" == true ]]; then msg_info "Setup MySQL $MYSQL_VERSION" - # Cleanup old repository files cleanup_old_repo_files "mysql" - # Determine suite - use bookworm for Debian testing/unstable local SUITE if [[ "$DISTRO_ID" == "debian" ]]; then case "$DISTRO_CODENAME" in - bookworm | bullseye) - SUITE="$DISTRO_CODENAME" - ;; - trixie | forky | sid) - SUITE="bookworm" - ;; - *) - SUITE="bookworm" # Fallback to bookworm for unknown Debian versions - ;; + bookworm | bullseye) SUITE="$DISTRO_CODENAME" ;; + trixie | forky | sid) SUITE="bookworm" ;; + *) SUITE="bookworm" ;; esac else - # For Ubuntu - use fallback detection SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://repo.mysql.com/apt/${DISTRO_ID}") fi - # Stop existing MySQL if running $STD systemctl stop mysql 2>/dev/null || true - - # Only purge if MySQL is actually installed if dpkg -l 2>/dev/null | grep -q "^ii.*mysql-server"; then - $STD apt purge -y mysql-server* mysql-client* mysql-common 2>/dev/null || true + $STD apt purge -y mysql-server* mysql-client* mysql-common mysql-community-* 2>/dev/null || true fi - # Handle libaio dependency for Debian Trixie+ (time64 transition) - if [[ "$DISTRO_ID" == "debian" ]] && [[ "$DISTRO_CODENAME" =~ ^(trixie|forky|sid)$ ]]; then - # Install libaio1t64 if not present - if ! dpkg -l libaio1t64 2>/dev/null | grep -q "^ii"; then - $STD apt update || { - msg_error "Failed to update package list" - return 1 - } - $STD apt install -y libaio1t64 || { - msg_error "Failed to install libaio1t64" - return 1 - } - fi - - # Create dummy libaio1 package for dependency satisfaction - local TEMP_DIR="/tmp/libaio1-compat-$$" - mkdir -p "$TEMP_DIR" - cd "$TEMP_DIR" - - # Create control file - mkdir -p DEBIAN - cat >DEBIAN/control < libaio1t64 transition - This is a transitional dummy package to satisfy dependencies on libaio1 - while actually using libaio1t64 (time64 transition). + # --- Debian 13+ Fix: MySQL 8.0 incompatible with libaio1t64 --- + if [[ "$DISTRO_ID" == "debian" && "$DISTRO_CODENAME" =~ ^(trixie|forky|sid)$ ]]; then + msg_info "Debian ${DISTRO_CODENAME} detected → using MySQL 8.4 LTS (libaio1t64)" + curl -fsSL https://repo.mysql.com/RPM-GPG-KEY-mysql-2023 | gpg --dearmor -o /etc/apt/keyrings/mysql.gpg + cat >/etc/apt/sources.list.d/mysql.sources <<'EOF' +Types: deb +URIs: https://repo.mysql.com/apt/debian/ +Suites: bookworm +Components: mysql-8.4-lts +Architectures: amd64 arm64 +Signed-By: /etc/apt/keyrings/mysql.gpg EOF - - # Build the dummy package - cd /tmp - dpkg-deb -b "$TEMP_DIR" libaio1-compat.deb >/dev/null 2>&1 - - # Install it - $STD dpkg -i libaio1-compat.deb - - # Cleanup - rm -rf "$TEMP_DIR" libaio1-compat.deb + $STD apt update + if ! $STD apt install -y mysql-community-server mysql-community-client; then + msg_error "MySQL 8.4 LTS installation failed – falling back to MariaDB" + $STD apt install -y mariadb-server mariadb-client + fi + msg_ok "Setup Database Engine (MySQL 8.4 LTS / MariaDB)" + return fi + # ------------------------------------------------------------- - # Use standardized repo setup setup_deb822_repo \ "mysql" \ "https://repo.mysql.com/RPM-GPG-KEY-mysql-2023" \ @@ -2593,37 +2605,26 @@ EOF "amd64 arm64" export DEBIAN_FRONTEND=noninteractive - - # Update apt if ! $STD apt update; then msg_error "APT update failed for MySQL repository" return 1 fi - # Try multiple MySQL package patterns local mysql_install_success=false - - # First try: mysql-server (most common) if apt-cache search "^mysql-server$" 2>/dev/null | grep -q . && $STD apt install -y mysql-server mysql-client 2>/dev/null; then mysql_install_success=true fi - - # Second try: mysql-community-server (when official repo) if [[ "$mysql_install_success" == false ]] && apt-cache search "^mysql-community-server$" 2>/dev/null | grep -q . && $STD apt install -y mysql-community-server mysql-community-client 2>/dev/null; then mysql_install_success=true fi - - # Third try: just mysql meta package if [[ "$mysql_install_success" == false ]] && apt-cache search "^mysql$" 2>/dev/null | grep -q . && $STD apt install -y mysql 2>/dev/null; then mysql_install_success=true fi - if [[ "$mysql_install_success" == false ]]; then msg_error "MySQL ${MYSQL_VERSION} package not available for suite ${SUITE}" return 1 fi - # Verify installation if ! command -v mysql >/dev/null 2>&1; then hash -r if ! command -v mysql >/dev/null 2>&1; then @@ -2654,31 +2655,38 @@ function setup_nodejs() { local NODE_MODULE="${NODE_MODULE:-}" local CURRENT_NODE_VERSION="" local NEED_NODE_INSTALL=false - local DISTRO_ID DISTRO_CODENAME - DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') - DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + # Check if Node.js is already installed if command -v node >/dev/null; then CURRENT_NODE_VERSION="$(node -v | grep -oP '^v\K[0-9]+')" if [[ "$CURRENT_NODE_VERSION" != "$NODE_VERSION" ]]; then + msg_info "Old Node.js $CURRENT_NODE_VERSION found, replacing with $NODE_VERSION" NEED_NODE_INSTALL=true fi else + msg_info "Setup Node.js $NODE_VERSION" NEED_NODE_INSTALL=true fi + if ! command -v jq &>/dev/null; then + $STD apt-get update + $STD apt-get install -y jq || { + msg_error "Failed to install jq" + return 1 + } + fi + + # Install Node.js if required if [[ "$NEED_NODE_INSTALL" == true ]]; then - msg_info "Setup Node.js $NODE_VERSION" + ensure_dependencies curl ca-certificates gnupg - ensure_dependencies jq + if [[ -n "$CURRENT_NODE_VERSION" ]]; then + $STD apt-get purge -y nodejs npm || true + fi - $STD apt purge -y nodejs - - # Cleanup old repository files cleanup_old_repo_files "nodesource" - # NodeSource uses 'nodistro' for all distributions - no fallback needed - # Use standardized repo setup + # NodeSource uses deb822 format with "nodistro" setup_deb822_repo \ "nodesource" \ "https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key" \ @@ -2687,19 +2695,33 @@ function setup_nodejs() { "main" \ "amd64 arm64" - if ! $STD apt install -y nodejs; then + sleep 2 + if ! apt-get update >/dev/null 2>&1; then + msg_warn "APT update failed – retrying in 5s" + sleep 5 + if ! apt-get update >/dev/null 2>&1; then + msg_error "Failed to update APT repositories after adding NodeSource" + return 1 + fi + fi + + if ! apt-get install -y nodejs >/dev/null 2>&1; then msg_error "Failed to install Node.js ${NODE_VERSION} from NodeSource" return 1 fi - $STD npm install -g npm@latest 2>/dev/null || true + # Update to latest npm + $STD npm install -g npm@latest || { + msg_error "Failed to update npm to latest version" + } cache_installed_version "nodejs" "$NODE_VERSION" - msg_ok "Setup Node.js $NODE_VERSION" + msg_ok "Setup Node.js ${NODE_VERSION}" fi export NODE_OPTIONS="--max-old-space-size=4096" + # Ensure valid working directory for npm (avoids uv_cwd error) if [[ ! -d /opt ]]; then mkdir -p /opt fi @@ -2708,41 +2730,50 @@ function setup_nodejs() { return 1 } + # Install global Node modules if [[ -n "$NODE_MODULE" ]]; then IFS=',' read -ra MODULES <<<"$NODE_MODULE" for mod in "${MODULES[@]}"; do local MODULE_NAME MODULE_REQ_VERSION MODULE_INSTALLED_VERSION if [[ "$mod" == @*/*@* ]]; then + # Scoped package with version, e.g. @vue/cli-service@latest MODULE_NAME="${mod%@*}" MODULE_REQ_VERSION="${mod##*@}" elif [[ "$mod" == *"@"* ]]; then + # Unscoped package with version, e.g. yarn@latest MODULE_NAME="${mod%@*}" MODULE_REQ_VERSION="${mod##*@}" else + # No version specified MODULE_NAME="$mod" MODULE_REQ_VERSION="latest" fi + # Check if the module is already installed if npm list -g --depth=0 "$MODULE_NAME" >/dev/null 2>&1; then MODULE_INSTALLED_VERSION="$(npm list -g --depth=0 "$MODULE_NAME" | grep "$MODULE_NAME@" | awk -F@ '{print $2}' | tr -d '[:space:]')" if [[ "$MODULE_REQ_VERSION" != "latest" && "$MODULE_REQ_VERSION" != "$MODULE_INSTALLED_VERSION" ]]; then + msg_info "Updating $MODULE_NAME from v$MODULE_INSTALLED_VERSION to v$MODULE_REQ_VERSION" if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}"; then msg_error "Failed to update $MODULE_NAME to version $MODULE_REQ_VERSION" return 1 fi elif [[ "$MODULE_REQ_VERSION" == "latest" ]]; then + msg_info "Updating $MODULE_NAME to latest version" if ! $STD npm install -g "${MODULE_NAME}@latest"; then msg_error "Failed to update $MODULE_NAME to latest version" return 1 fi fi else + msg_info "Installing $MODULE_NAME@$MODULE_REQ_VERSION" if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}"; then msg_error "Failed to install $MODULE_NAME@$MODULE_REQ_VERSION" return 1 fi fi done + msg_ok "Installed Node.js modules: $NODE_MODULE" fi } From 0f624dfba8574637c286992e94867da47859b83e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 24 Oct 2025 16:03:59 +0200 Subject: [PATCH 1533/1733] Add unified tool install, upgrade, and repo management Introduces functions for checking tool installation and version, removing old versions, determining if upgrades are needed, and managing repositories for major tools (MariaDB, MongoDB, Node.js, PHP, PostgreSQL, MySQL). Refactors setup functions for Composer, FFmpeg, Go, Ghostscript, ImageMagick, Java, local IP helper, MariaDB, MongoDB, MySQL, and Node.js to use these new utilities, improving idempotency, upgrade handling, and error reporting. Enhances repository setup with parameter validation and error handling. --- misc/tools.func | 1750 ++++++++++++++++++++++++++++------------------- 1 file changed, 1033 insertions(+), 717 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index cc27410b3..ab3e11308 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -25,6 +25,345 @@ get_cached_version() { } # ------------------------------------------------------------------------------ +# Check if tool is already installed and optionally verify exact version +# Returns: 0 if installed (with optional version match), 1 if not installed +# Usage: is_tool_installed "mariadb" "11.4" || echo "Not installed" +# ------------------------------------------------------------------------------ +is_tool_installed() { + local tool_name="$1" + local required_version="${2:-}" + local installed_version="" + + case "$tool_name" in + mariadb) + if command -v mariadb >/dev/null 2>&1; then + installed_version=$(mariadb --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1) + fi + ;; + mysql) + if command -v mysql >/dev/null 2>&1; then + installed_version=$(mysql --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1) + fi + ;; + mongodb | mongod) + if command -v mongod >/dev/null 2>&1; then + installed_version=$(mongod --version 2>/dev/null | awk '/db version/{print $3}' | cut -d. -f1,2) + fi + ;; + node | nodejs) + if command -v node >/dev/null 2>&1; then + installed_version=$(node -v 2>/dev/null | grep -oP '^v\K[0-9]+') + fi + ;; + php) + if command -v php >/dev/null 2>&1; then + installed_version=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2) + fi + ;; + postgres | postgresql) + if command -v psql >/dev/null 2>&1; then + installed_version=$(psql --version 2>/dev/null | awk '{print $3}' | cut -d. -f1) + fi + ;; + ruby) + if command -v ruby >/dev/null 2>&1; then + installed_version=$(ruby --version 2>/dev/null | awk '{print $2}' | cut -d. -f1,2) + fi + ;; + rust | rustc) + if command -v rustc >/dev/null 2>&1; then + installed_version=$(rustc --version 2>/dev/null | awk '{print $2}') + fi + ;; + go | golang) + if command -v go >/dev/null 2>&1; then + installed_version=$(go version 2>/dev/null | awk '{print $3}' | sed 's/go//') + fi + ;; + clickhouse) + if command -v clickhouse >/dev/null 2>&1; then + installed_version=$(clickhouse --version 2>/dev/null | awk '{print $2}') + fi + ;; + esac + + if [[ -z "$installed_version" ]]; then + return 1 # Not installed + fi + + if [[ -n "$required_version" && "$installed_version" != "$required_version" ]]; then + echo "$installed_version" + return 1 # Version mismatch + fi + + echo "$installed_version" + return 0 # Installed and version matches (if specified) +} + +# ------------------------------------------------------------------------------ +# Remove old tool version completely (purge + cleanup repos) +# Usage: remove_old_tool_version "mariadb" "repository-name" +# ------------------------------------------------------------------------------ +remove_old_tool_version() { + local tool_name="$1" + local repo_name="${2:-$tool_name}" + + case "$tool_name" in + mariadb) + $STD systemctl stop mariadb >/dev/null 2>&1 || true + $STD apt purge -y 'mariadb*' >/dev/null 2>&1 || true + ;; + mysql) + $STD systemctl stop mysql >/dev/null 2>&1 || true + $STD apt purge -y 'mysql*' >/dev/null 2>&1 || true + rm -rf /var/lib/mysql >/dev/null 2>&1 || true + ;; + mongodb) + $STD systemctl stop mongod >/dev/null 2>&1 || true + $STD apt purge -y 'mongodb*' >/dev/null 2>&1 || true + rm -rf /var/lib/mongodb >/dev/null 2>&1 || true + ;; + node | nodejs) + $STD apt purge -y nodejs npm >/dev/null 2>&1 || true + npm list -g 2>/dev/null | grep -oE '^ \S+' | awk '{print $1}' | while read -r module; do + npm uninstall -g "$module" >/dev/null 2>&1 || true + done + ;; + php) + # Disable PHP-FPM if running + $STD systemctl disable php*-fpm >/dev/null 2>&1 || true + $STD systemctl stop php*-fpm >/dev/null 2>&1 || true + $STD apt purge -y 'php*' >/dev/null 2>&1 || true + rm -rf /etc/php >/dev/null 2>&1 || true + ;; + postgresql) + $STD systemctl stop postgresql >/dev/null 2>&1 || true + $STD apt purge -y 'postgresql*' >/dev/null 2>&1 || true + rm -rf /var/lib/postgresql >/dev/null 2>&1 || true + ;; + ruby) + if [[ -d "$HOME/.rbenv" ]]; then + rm -rf "$HOME/.rbenv" + fi + $STD apt purge -y 'ruby*' >/dev/null 2>&1 || true + ;; + rust) + rm -rf "$HOME/.cargo" "$HOME/.rustup" >/dev/null 2>&1 || true + ;; + go | golang) + rm -rf /usr/local/go >/dev/null 2>&1 || true + ;; + clickhouse) + $STD systemctl stop clickhouse-server >/dev/null 2>&1 || true + $STD apt purge -y 'clickhouse*' >/dev/null 2>&1 || true + rm -rf /var/lib/clickhouse >/dev/null 2>&1 || true + ;; + esac + + # Clean up old repositories + cleanup_old_repo_files "$repo_name" + + return 0 +} + +# ------------------------------------------------------------------------------ +# Determine if tool update/upgrade is needed +# Returns: 0 (update needed), 1 (already up-to-date) +# Usage: if should_update_tool "mariadb" "11.4"; then ... fi +# ------------------------------------------------------------------------------ +should_update_tool() { + local tool_name="$1" + local target_version="$2" + local current_version="" + + # Get currently installed version + current_version=$(is_tool_installed "$tool_name" 2>/dev/null) || return 0 # Not installed = needs install + + # If versions are identical, no update needed + if [[ "$current_version" == "$target_version" ]]; then + return 1 # No update needed + fi + + return 0 # Update needed +} + +# ---------------------–---------------------------------------------------------- +# Unified repository management for tools +# Handles adding, updating, and verifying tool repositories +# Usage: manage_tool_repository "mariadb" "11.4" "https://repo..." "GPG_key_url" +# Supports: mariadb, mongodb, nodejs, postgresql, php, mysql +# ------------------------------------------------------------------------------ +manage_tool_repository() { + local tool_name="$1" + local version="$2" + local repo_url="$3" + local gpg_key_url="${4:-}" + local distro_id repo_component suite + + distro_id=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + + case "$tool_name" in + mariadb) + if [[ -z "$repo_url" || -z "$gpg_key_url" ]]; then + msg_error "MariaDB repository requires repo_url and gpg_key_url" + return 1 + fi + + # Clean old repos first + cleanup_old_repo_files "mariadb" + + # Get suite for fallback handling + local distro_codename + distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + suite=$(get_fallback_suite "$distro_id" "$distro_codename" "$repo_url/$distro_id") + + # Setup new repository using deb822 format + setup_deb822_repo "mariadb" "$gpg_key_url" "$repo_url/$distro_id" "$suite" "main" "amd64 arm64" || return 1 + return 0 + ;; + + mongodb) + if [[ -z "$repo_url" || -z "$gpg_key_url" ]]; then + msg_error "MongoDB repository requires repo_url and gpg_key_url" + return 1 + fi + + # Clean old repos first + cleanup_old_repo_files "mongodb" + + # Import GPG key + mkdir -p /etc/apt/keyrings + if ! curl -fsSL "$gpg_key_url" | gpg --dearmor --yes -o "/etc/apt/keyrings/mongodb-server-${version}.gpg" 2>/dev/null; then + msg_error "Failed to download MongoDB GPG key" + return 1 + fi + + # Setup repository + local distro_codename + distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + suite=$(get_fallback_suite "$distro_id" "$distro_codename" "$repo_url") + + repo_component="main" + [[ "$distro_id" == "ubuntu" ]] && repo_component="multiverse" + + cat </etc/apt/sources.list.d/mongodb-org-${version}.sources +Types: deb +URIs: ${repo_url} +Suites: ${suite}/mongodb-org/${version} +Components: ${repo_component} +Architectures: amd64 arm64 +Signed-By: /etc/apt/keyrings/mongodb-server-${version}.gpg +EOF + return 0 + ;; + + nodejs) + if [[ -z "$repo_url" ]]; then + msg_error "Node.js repository requires repo_url" + return 1 + fi + + cleanup_old_repo_files "nodesource" + + # NodeSource uses deb822 format with GPG from repo + local distro_codename + distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + + # Create keyring directory first + mkdir -p /etc/apt/keyrings + + # Download GPG key from NodeSource + curl -fsSL "$repo_url/gpg.key" | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg || { + msg_error "Failed to import NodeSource GPG key" + return 1 + } + + cat </etc/apt/sources.list.d/nodesource.sources +Types: deb +URIs: $repo_url +Suites: $distro_codename +Components: main +Architectures: amd64 arm64 +Signed-By: /etc/apt/keyrings/nodesource.gpg +EOF + return 0 + ;; + + php) + if [[ -z "$gpg_key_url" ]]; then + msg_error "PHP repository requires gpg_key_url" + return 1 + fi + + cleanup_old_repo_files "php" + + # Download and install keyring + curl -fsSLo /tmp/debsuryorg-archive-keyring.deb "$gpg_key_url" || { + msg_error "Failed to download PHP keyring" + return 1 + } + dpkg -i /tmp/debsuryorg-archive-keyring.deb >/dev/null 2>&1 || { + msg_error "Failed to install PHP keyring" + rm -f /tmp/debsuryorg-archive-keyring.deb + return 1 + } + rm -f /tmp/debsuryorg-archive-keyring.deb + + # Setup repository + local distro_codename + distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + cat </etc/apt/sources.list.d/php.sources +Types: deb +URIs: https://packages.sury.org/php +Suites: $distro_codename +Components: main +Architectures: amd64 arm64 +Signed-By: /usr/share/keyrings/deb.sury.org-php.gpg +EOF + return 0 + ;; + + postgresql) + if [[ -z "$gpg_key_url" ]]; then + msg_error "PostgreSQL repository requires gpg_key_url" + return 1 + fi + + cleanup_old_repo_files "postgresql" + + # Create keyring directory first + mkdir -p /etc/apt/keyrings + + # Import PostgreSQL key + curl -fsSL "$gpg_key_url" | gpg --dearmor -o /etc/apt/keyrings/postgresql.gpg || { + msg_error "Failed to import PostgreSQL GPG key" + return 1 + } + + # Setup repository + local distro_codename + distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + cat </etc/apt/sources.list.d/postgresql.sources +Types: deb +URIs: http://apt.postgresql.org/pub/repos/apt +Suites: $distro_codename-pgdg +Components: main +Architectures: amd64 arm64 +Signed-By: /etc/apt/keyrings/postgresql.gpg +EOF + return 0 + ;; + + *) + msg_error "Unknown tool repository: $tool_name" + return 1 + ;; + esac + + return 0 +} + +# ------–---------------------------------------------------------------------- # Unified package upgrade function (with apt update caching) # ------------------------------------------------------------------------------ upgrade_package() { @@ -597,6 +936,7 @@ ensure_apt_working() { # ------------------------------------------------------------------------------ # Standardized deb822 repository setup +# Validates all parameters and fails safely if any are empty # ------------------------------------------------------------------------------ setup_deb822_repo() { local name="$1" @@ -606,6 +946,12 @@ setup_deb822_repo() { local component="${5:-main}" local architectures="${6:-amd64 arm64}" + # Validate required parameters + if [[ -z "$name" || -z "$gpg_url" || -z "$repo_url" || -z "$suite" ]]; then + msg_error "setup_deb822_repo: missing required parameters (name=$name, gpg=$gpg_url, repo=$repo_url, suite=$suite)" + return 1 + fi + # Cleanup old configs for this app cleanup_old_repo_files "$name" @@ -613,11 +959,14 @@ setup_deb822_repo() { cleanup_orphaned_sources # Ensure keyring directory exists - mkdir -p /etc/apt/keyrings + mkdir -p /etc/apt/keyrings || { + msg_error "Failed to create /etc/apt/keyrings directory" + return 1 + } # Download GPG key (with --yes to avoid interactive prompts) - curl -fsSL "$gpg_url" | gpg --dearmor --yes -o "/etc/apt/keyrings/${name}.gpg" || { - msg_error "Failed to download or import GPG key for ${name}" + curl -fsSL "$gpg_url" | gpg --dearmor --yes -o "/etc/apt/keyrings/${name}.gpg" 2>/dev/null || { + msg_error "Failed to download or import GPG key for ${name} from $gpg_url" return 1 } @@ -1419,9 +1768,6 @@ function import_local_ip() { # ------------------------------------------------------------------------------ function setup_adminer() { - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "adminer") - if grep -qi alpine /etc/os-release; then msg_info "Setup Adminer (Alpine)" mkdir -p /var/www/localhost/htdocs/adminer @@ -1456,14 +1802,31 @@ function setup_adminer() { # - Installs to /usr/local/bin/composer # - Removes old binaries/symlinks in /usr/bin, /bin, /root/.composer, etc. # - Ensures /usr/local/bin is in PATH (permanent) +# - Auto-updates to latest version # ------------------------------------------------------------------------------ function setup_composer() { local COMPOSER_BIN="/usr/local/bin/composer" - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "composer") export COMPOSER_ALLOW_SUPERUSER=1 + # Get currently installed version + local INSTALLED_VERSION="" + if [[ -x "$COMPOSER_BIN" ]]; then + INSTALLED_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}') + fi + + # Scenario 1: Already installed - just self-update + if [[ -n "$INSTALLED_VERSION" ]]; then + msg_info "Update Composer $INSTALLED_VERSION" + $STD "$COMPOSER_BIN" self-update --no-interaction || true + local UPDATED_VERSION + UPDATED_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}') + cache_installed_version "composer" "$UPDATED_VERSION" + msg_ok "Update Composer $UPDATED_VERSION" + return 0 + fi + + # Scenario 2: Fresh install msg_info "Setup Composer" for old in /usr/bin/composer /bin/composer /root/.composer/vendor/bin/composer; do @@ -1488,7 +1851,7 @@ function setup_composer() { if [[ ! -x "$COMPOSER_BIN" ]]; then msg_error "Composer installation failed" return 1 - fi + } chmod +x "$COMPOSER_BIN" $STD "$COMPOSER_BIN" self-update --no-interaction || true @@ -1523,8 +1886,12 @@ function setup_ffmpeg() { local VERSION="${FFMPEG_VERSION:-latest}" local TYPE="${FFMPEG_TYPE:-full}" local BIN_PATH="/usr/local/bin/ffmpeg" - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "ffmpeg") + + # Get currently installed version + local INSTALLED_VERSION="" + if command -v ffmpeg &>/dev/null; then + INSTALLED_VERSION=$(ffmpeg -version 2>/dev/null | head -n1 | awk '{print $3}') + fi msg_info "Setup FFmpeg ${VERSION} ($TYPE)" @@ -1545,11 +1912,11 @@ function setup_ffmpeg() { cp "$EXTRACTED_DIR/ffmpeg" "$BIN_PATH" cp "$EXTRACTED_DIR/ffprobe" /usr/local/bin/ffprobe chmod +x "$BIN_PATH" /usr/local/bin/ffprobe - local FINAL_VERSION=$($BIN_PATH -version | head -n1 | awk '{print $3}') + local FINAL_VERSION=$($BIN_PATH -version 2>/dev/null | head -n1 | awk '{print $3}') rm -rf "$TMP_DIR" cache_installed_version "ffmpeg" "$FINAL_VERSION" ensure_usr_local_bin_persist - msg_ok "Setup FFmpeg" + [[ -n "$INSTALLED_VERSION" ]] && msg_ok "Upgrade FFmpeg $INSTALLED_VERSION → $FINAL_VERSION" || msg_ok "Setup FFmpeg $FINAL_VERSION" return 0 fi @@ -1557,7 +1924,7 @@ function setup_ffmpeg() { # Auto-detect latest stable version if none specified if [[ "$VERSION" == "latest" || -z "$VERSION" ]]; then - VERSION=$(curl -fsSL "https://api.github.com/repos/${GITHUB_REPO}/tags" | + VERSION=$(curl -fsSL "https://api.github.com/repos/${GITHUB_REPO}/tags" 2>/dev/null | jq -r '.[].name' | grep -E '^n[0-9]+\.[0-9]+\.[0-9]+$' | sort -V | tail -n1) @@ -1656,7 +2023,7 @@ function setup_ffmpeg() { echo "/usr/local/lib" >/etc/ld.so.conf.d/ffmpeg.conf $STD ldconfig - ldconfig -p | grep libavdevice >/dev/null || { + ldconfig -p 2>/dev/null | grep libavdevice >/dev/null || { msg_error "libavdevice not registered with dynamic linker" rm -rf "$TMP_DIR" return 1 @@ -1669,11 +2036,11 @@ function setup_ffmpeg() { fi local FINAL_VERSION - FINAL_VERSION=$(ffmpeg -version | head -n1 | awk '{print $3}') + FINAL_VERSION=$(ffmpeg -version 2>/dev/null | head -n1 | awk '{print $3}') rm -rf "$TMP_DIR" cache_installed_version "ffmpeg" "$FINAL_VERSION" ensure_usr_local_bin_persist - msg_ok "Setup FFmpeg" + [[ -n "$INSTALLED_VERSION" ]] && msg_ok "Upgrade FFmpeg $INSTALLED_VERSION → $FINAL_VERSION" || msg_ok "Setup FFmpeg $FINAL_VERSION" } # ------------------------------------------------------------------------------ @@ -1698,42 +2065,48 @@ function setup_go() { ;; esac - # Determine version - if [[ -z "${GO_VERSION:-}" || "${GO_VERSION}" == "latest" ]]; then - GO_VERSION=$(curl -fsSL https://go.dev/VERSION?m=text | head -n1 | sed 's/^go//') - if [[ -z "$GO_VERSION" ]]; then + # Resolve "latest" version + local GO_VERSION="${GO_VERSION:-latest}" + if [[ "$GO_VERSION" == "latest" ]]; then + GO_VERSION=$(curl -fsSL https://go.dev/VERSION?m=text 2>/dev/null | head -n1 | sed 's/^go//') || { msg_error "Could not determine latest Go version" return 1 - fi + } + [[ -z "$GO_VERSION" ]] && { + msg_error "Latest Go version is empty" + return 1 + } fi local GO_BIN="/usr/local/bin/go" local GO_INSTALL_DIR="/usr/local/go" - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "go") + # Get currently installed version + local CURRENT_VERSION="" if [[ -x "$GO_BIN" ]]; then - local CURRENT_VERSION - CURRENT_VERSION=$("$GO_BIN" version | awk '{print $3}' | sed 's/go//') - if [[ "$CURRENT_VERSION" == "$GO_VERSION" ]]; then - if [[ "$CACHED_VERSION" == "$GO_VERSION" ]]; then - return 0 - fi - cache_installed_version "go" "$GO_VERSION" - return 0 - else - rm -rf "$GO_INSTALL_DIR" - fi + CURRENT_VERSION=$("$GO_BIN" version 2>/dev/null | awk '{print $3}' | sed 's/go//') fi - msg_info "Setup Go $GO_VERSION" + # Scenario 1: Already at target version + if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$GO_VERSION" ]]; then + cache_installed_version "go" "$GO_VERSION" + return 0 + fi + + # Scenario 2: Different version or not installed + if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" != "$GO_VERSION" ]]; then + msg_info "Upgrade Go from $CURRENT_VERSION to $GO_VERSION" + remove_old_tool_version "go" + else + msg_info "Setup Go $GO_VERSION" + fi local TARBALL="go${GO_VERSION}.linux-${ARCH}.tar.gz" local URL="https://go.dev/dl/${TARBALL}" local TMP_TAR=$(mktemp) curl -fsSL "$URL" -o "$TMP_TAR" || { - msg_error "Failed to download $TARBALL" + msg_error "Failed to download Go $GO_VERSION" rm -f "$TMP_TAR" return 1 } @@ -1743,6 +2116,7 @@ function setup_go() { rm -f "$TMP_TAR" return 1 } + ln -sf /usr/local/go/bin/go /usr/local/bin/go ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt rm -f "$TMP_TAR" @@ -1763,13 +2137,11 @@ function setup_go() { function setup_gs() { local TMP_DIR=$(mktemp -d) local CURRENT_VERSION=$(gs --version 2>/dev/null || echo "0") - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "ghostscript") ensure_dependencies jq local RELEASE_JSON - RELEASE_JSON=$(curl -fsSL https://api.github.com/repos/ArtifexSoftware/ghostpdl-downloads/releases/latest) + RELEASE_JSON=$(curl -fsSL https://api.github.com/repos/ArtifexSoftware/ghostpdl-downloads/releases/latest 2>/dev/null) local LATEST_VERSION LATEST_VERSION=$(echo "$RELEASE_JSON" | jq -r '.tag_name' | sed 's/^gs//') local LATEST_VERSION_DOTTED @@ -1781,17 +2153,19 @@ function setup_gs() { return 1 fi + # Scenario 1: Already at latest version if dpkg --compare-versions "$CURRENT_VERSION" ge "$LATEST_VERSION_DOTTED" 2>/dev/null; then - if [[ "$CACHED_VERSION" == "$LATEST_VERSION_DOTTED" ]]; then - rm -rf "$TMP_DIR" - return 0 - fi cache_installed_version "ghostscript" "$LATEST_VERSION_DOTTED" rm -rf "$TMP_DIR" return 0 fi - msg_info "Setup Ghostscript $LATEST_VERSION_DOTTED" + # Scenario 2: New install or upgrade + if [[ "$CURRENT_VERSION" != "0" && "$CURRENT_VERSION" != "$LATEST_VERSION_DOTTED" ]]; then + msg_info "Upgrade Ghostscript from $CURRENT_VERSION to $LATEST_VERSION_DOTTED" + else + msg_info "Setup Ghostscript $LATEST_VERSION_DOTTED" + fi curl -fsSL "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs${LATEST_VERSION}/ghostscript-${LATEST_VERSION_DOTTED}.tar.gz" -o "$TMP_DIR/ghostscript.tar.gz" || { msg_error "Failed to download Ghostscript" @@ -1931,6 +2305,7 @@ function setup_hwaccel() { $STD adduser "$(id -u -n)" render fi + cache_installed_version "hwaccel" "1.0" msg_ok "Setup Hardware Acceleration" } @@ -1947,20 +2322,12 @@ function setup_hwaccel() { # ------------------------------------------------------------------------------ function setup_imagemagick() { local TMP_DIR=$(mktemp -d) - local VERSION="" local BINARY_PATH="/usr/local/bin/magick" - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "imagemagick") + # Get currently installed version + local INSTALLED_VERSION="" if command -v magick &>/dev/null; then - VERSION=$(magick -version | awk '/^Version/ {print $3}') - if [[ "$CACHED_VERSION" == "$VERSION" ]]; then - rm -rf "$TMP_DIR" - return 0 - fi - cache_installed_version "imagemagick" "$VERSION" - rm -rf "$TMP_DIR" - return 0 + INSTALLED_VERSION=$(magick -version | awk '/^Version/ {print $3}') fi msg_info "Setup ImageMagick" @@ -2026,11 +2393,17 @@ function setup_imagemagick() { return 1 fi - VERSION=$("$BINARY_PATH" -version | awk '/^Version/ {print $3}') + local FINAL_VERSION + FINAL_VERSION=$("$BINARY_PATH" -version | awk '/^Version/ {print $3}') rm -rf "$TMP_DIR" - cache_installed_version "imagemagick" "$VERSION" + cache_installed_version "imagemagick" "$FINAL_VERSION" ensure_usr_local_bin_persist - msg_ok "Setup ImageMagick" + + if [[ -n "$INSTALLED_VERSION" ]]; then + msg_ok "Upgrade ImageMagick $INSTALLED_VERSION → $FINAL_VERSION" + else + msg_ok "Setup ImageMagick $FINAL_VERSION" + fi } # ------------------------------------------------------------------------------ @@ -2051,20 +2424,11 @@ function setup_java() { DISTRO_CODENAME=$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release) local DESIRED_PACKAGE="temurin-${JAVA_VERSION}-jdk" - # Check cached version - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "temurin-jdk") - - # Add repo nur wenn nötig + # Add repo if needed if [[ ! -f /etc/apt/sources.list.d/adoptium.sources ]]; then - # Cleanup old repository files cleanup_old_repo_files "adoptium" - - # Use helper function to get fallback suite local SUITE SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://packages.adoptium.net/artifactory/deb") - - # Use standardized repo setup setup_deb822_repo \ "adoptium" \ "https://packages.adoptium.net/artifactory/api/gpg/key/public" \ @@ -2074,33 +2438,38 @@ function setup_java() { "amd64 arm64" fi + # Get currently installed version local INSTALLED_VERSION="" if dpkg -l | grep -q "temurin-.*-jdk"; then INSTALLED_VERSION=$(dpkg -l | awk '/temurin-.*-jdk/{print $2}' | grep -oP 'temurin-\K[0-9]+') fi + # Scenario 1: Already at correct version if [[ "$INSTALLED_VERSION" == "$JAVA_VERSION" ]]; then - if [[ "$CACHED_VERSION" == "$JAVA_VERSION" ]]; then - # Already at correct version, just upgrade if available - upgrade_package "$DESIRED_PACKAGE" - else - $STD apt update - $STD apt install --only-upgrade -y "$DESIRED_PACKAGE" - cache_installed_version "temurin-jdk" "$JAVA_VERSION" - fi + msg_info "Update Temurin JDK $JAVA_VERSION" + $STD apt update + $STD apt install --only-upgrade -y "$DESIRED_PACKAGE" + cache_installed_version "temurin-jdk" "$JAVA_VERSION" + msg_ok "Update Temurin JDK $JAVA_VERSION" + return 0 + fi + + # Scenario 2: Different version - remove old and install new + if [[ -n "$INSTALLED_VERSION" ]]; then + msg_info "Upgrade Temurin JDK from $INSTALLED_VERSION to $JAVA_VERSION" + $STD apt purge -y "temurin-${INSTALLED_VERSION}-jdk" || true else msg_info "Setup Temurin JDK $JAVA_VERSION" - if [[ -n "$INSTALLED_VERSION" ]]; then - $STD apt purge -y "temurin-${INSTALLED_VERSION}-jdk" - fi - - $STD apt install -y "$DESIRED_PACKAGE" || { - msg_error "Failed to install Temurin JDK $JAVA_VERSION" - return 1 - } - cache_installed_version "temurin-jdk" "$JAVA_VERSION" - msg_ok "Setup Temurin JDK $JAVA_VERSION" fi + + $STD apt update + $STD apt install -y "$DESIRED_PACKAGE" || { + msg_error "Failed to install Temurin JDK $JAVA_VERSION" + return 1 + } + + cache_installed_version "temurin-jdk" "$JAVA_VERSION" + msg_ok "Setup Temurin JDK $JAVA_VERSION" } # ------------------------------------------------------------------------------ @@ -2117,6 +2486,15 @@ function setup_local_ip_helper() { local IP_FILE="/run/local-ip.env" local DISPATCHER_SCRIPT="/etc/networkd-dispatcher/routable.d/10-update-local-ip.sh" + # Check if already set up + if [[ -f "$SCRIPT_PATH" && -f "$DISPATCHER_SCRIPT" ]]; then + msg_info "Update Local IP Helper" + cache_installed_version "local-ip-helper" "1.0" + msg_ok "Update Local IP Helper" + else + msg_info "Setup Local IP Helper" + fi + mkdir -p "$BASE_DIR" # Install networkd-dispatcher if not present @@ -2185,6 +2563,9 @@ EOF chmod +x "$DISPATCHER_SCRIPT" systemctl enable -q --now networkd-dispatcher.service + + cache_installed_version "local-ip-helper" "1.0" + msg_ok "Setup Local IP Helper" } # ------------------------------------------------------------------------------ @@ -2201,108 +2582,80 @@ EOF setup_mariadb() { local MARIADB_VERSION="${MARIADB_VERSION:-latest}" - local DISTRO_ID DISTRO_CODENAME - DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') - DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - - if ! curl -fsI http://mirror.mariadb.org/repo/ >/dev/null; then - msg_error "MariaDB mirror not reachable" - return 1 - fi + # Resolve "latest" to actual version if [[ "$MARIADB_VERSION" == "latest" ]]; then - MARIADB_VERSION=$(curl -fsSL http://mirror.mariadb.org/repo/ | + if ! curl -fsI http://mirror.mariadb.org/repo/ >/dev/null 2>&1; then + msg_error "MariaDB mirror not reachable" + return 1 + fi + MARIADB_VERSION=$(curl -fsSL http://mirror.mariadb.org/repo/ 2>/dev/null | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+/' | grep -vE 'rc/|rolling/' | sed 's|/||' | sort -Vr | - head -n1) - if [[ -z "$MARIADB_VERSION" ]]; then + head -n1) || { msg_error "Could not determine latest GA MariaDB version" return 1 - fi + } + [[ -z "$MARIADB_VERSION" ]] && { + msg_error "Latest MariaDB version is empty" + return 1 + } fi + # Get currently installed version local CURRENT_VERSION="" - if command -v mariadb >/dev/null; then - CURRENT_VERSION=$(mariadb --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+') - fi + CURRENT_VERSION=$(is_tool_installed "mariadb" 2>/dev/null) || true - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "mariadb") + # Scenario 1: Already installed at target version - just update packages + if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$MARIADB_VERSION" ]]; then + msg_info "Update MariaDB $MARIADB_VERSION" - # Check if MariaDB is already installed and needs update - if [[ -n "$CURRENT_VERSION" ]]; then - # Check if repository exists and needs update - local REPO_VERSION="" + # Ensure APT is working + ensure_apt_working || return 1 + + # Check if repository needs to be refreshed if [[ -f /etc/apt/sources.list.d/mariadb.sources ]]; then - REPO_VERSION=$(grep -oP 'repo/\K[0-9]+\.[0-9]+\.[0-9]+' /etc/apt/sources.list.d/mariadb.sources 2>/dev/null || echo "") - fi - - if [[ -n "$REPO_VERSION" && "$REPO_VERSION" != "$MARIADB_VERSION" ]]; then - msg_info "Update MariaDB repository from $REPO_VERSION to $MARIADB_VERSION" - - # Remove old repository - cleanup_old_repo_files "mariadb" - - # Use helper function to get fallback suite - local SUITE - DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') - DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "http://mirror.mariadb.org/repo/${MARIADB_VERSION}/${DISTRO_ID}") - - # Setup new repository - setup_deb822_repo \ - "mariadb" \ - "https://mariadb.org/mariadb_release_signing_key.asc" \ - "http://mirror.mariadb.org/repo/${MARIADB_VERSION}/${DISTRO_ID}" \ - "$SUITE" \ - "main" \ - "amd64 arm64" || { - msg_error "Failed to update MariaDB repository" - return 1 - } - - msg_ok "Update MariaDB repository from $REPO_VERSION to $MARIADB_VERSION" - fi - - # Now perform the update - if [[ "$CURRENT_VERSION" == "$MARIADB_VERSION" ]]; then - if [[ "$CACHED_VERSION" == "$MARIADB_VERSION" ]]; then - msg_info "Update MariaDB $MARIADB_VERSION" - upgrade_package mariadb-server - upgrade_package mariadb-client - msg_ok "Update MariaDB $MARIADB_VERSION" - else - msg_info "Update MariaDB $MARIADB_VERSION" - $STD apt update || { - msg_error "Failed to update package list" + local REPO_VERSION="" + REPO_VERSION=$(grep -oP 'repo/\K[0-9]+\.[0-9]+' /etc/apt/sources.list.d/mariadb.sources 2>/dev/null || echo "") + if [[ -n "$REPO_VERSION" && "$REPO_VERSION" != "${MARIADB_VERSION%.*}" ]]; then + msg_warn "Repository version mismatch, updating..." + manage_tool_repository "mariadb" "$MARIADB_VERSION" "http://mirror.mariadb.org/repo/$MARIADB_VERSION" \ + "https://mariadb.org/mariadb_release_signing_key.asc" || { + msg_error "Failed to update MariaDB repository" return 1 } - $STD apt install --only-upgrade -y mariadb-server mariadb-client || { - msg_error "Failed to upgrade MariaDB" - return 1 - } - cache_installed_version "mariadb" "$MARIADB_VERSION" - msg_ok "Update MariaDB $MARIADB_VERSION" fi - return 0 - else - # Version mismatch - need to upgrade to new version - msg_info "Upgrade MariaDB from $CURRENT_VERSION to $MARIADB_VERSION" - $STD systemctl stop mariadb >/dev/null 2>&1 || true fi + + # Perform upgrade + $STD apt update || { + msg_error "Failed to update package list" + return 1 + } + $STD apt install --only-upgrade -y mariadb-server mariadb-client || { + msg_error "Failed to upgrade MariaDB packages" + return 1 + } + cache_installed_version "mariadb" "$MARIADB_VERSION" + msg_ok "Update MariaDB $MARIADB_VERSION" + return 0 fi + # Scenario 2: Different version installed - clean upgrade + if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" != "$MARIADB_VERSION" ]]; then + msg_info "Upgrade MariaDB from $CURRENT_VERSION to $MARIADB_VERSION" + remove_old_tool_version "mariadb" + fi + + # Scenario 3: Fresh install or version change msg_info "Setup MariaDB $MARIADB_VERSION" # Ensure APT is working before proceeding ensure_apt_working || return 1 - # Cleanup old repository files - cleanup_old_repo_files "mariadb" - - # Install required dependencies first (MariaDB needs these from main repos) + # Install required dependencies first local mariadb_deps=() for dep in gawk rsync socat libdbi-perl pv; do if apt-cache search "^${dep}$" 2>/dev/null | grep -q .; then @@ -2314,30 +2667,28 @@ setup_mariadb() { $STD apt install -y "${mariadb_deps[@]}" 2>/dev/null || true fi - # Use helper function to get fallback suite - local SUITE - SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "http://mirror.mariadb.org/repo/${MARIADB_VERSION}/${DISTRO_ID}") - - # Use standardized repo setup - setup_deb822_repo \ - "mariadb" \ - "https://mariadb.org/mariadb_release_signing_key.asc" \ - "http://mirror.mariadb.org/repo/${MARIADB_VERSION}/${DISTRO_ID}" \ - "$SUITE" \ - "main" \ - "amd64 arm64" + # Setup repository + manage_tool_repository "mariadb" "$MARIADB_VERSION" "http://mirror.mariadb.org/repo/$MARIADB_VERSION" \ + "https://mariadb.org/mariadb_release_signing_key.asc" || { + msg_error "Failed to setup MariaDB repository" + return 1 + } + # Set debconf selections for all potential versions local MARIADB_MAJOR_MINOR MARIADB_MAJOR_MINOR=$(echo "$MARIADB_VERSION" | awk -F. '{print $1"."$2}') if [[ -n "$MARIADB_MAJOR_MINOR" ]]; then echo "mariadb-server-$MARIADB_MAJOR_MINOR mariadb-server/feedback boolean false" | debconf-set-selections fi + # Install packages DEBIAN_FRONTEND=noninteractive $STD apt install -y mariadb-server mariadb-client || { + # Fallback: try without specific version + msg_warn "Failed to install MariaDB packages from upstream repo, trying distro fallback..." cleanup_old_repo_files "mariadb" $STD apt update DEBIAN_FRONTEND=noninteractive $STD apt install -y mariadb-server mariadb-client || { - msg_error "Failed to install MariaDB packages" + msg_error "Failed to install MariaDB packages (both upstream and distro)" return 1 } } @@ -2375,11 +2726,9 @@ function setup_mongodb() { case "$DISTRO_ID" in ubuntu) MONGO_BASE_URL="https://repo.mongodb.org/apt/ubuntu" - REPO_COMPONENT="multiverse" ;; debian) MONGO_BASE_URL="https://repo.mongodb.org/apt/debian" - REPO_COMPONENT="main" ;; *) msg_error "Unsupported distribution: $DISTRO_ID" @@ -2387,115 +2736,50 @@ function setup_mongodb() { ;; esac + # Get currently installed version local INSTALLED_VERSION="" - if command -v mongod >/dev/null 2>&1; then - INSTALLED_VERSION=$(mongod --version | awk '/db version/{print $3}' | cut -d. -f1,2) - fi + INSTALLED_VERSION=$(is_tool_installed "mongodb" 2>/dev/null) || true - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "mongodb") + # Scenario 1: Already at target version - just update packages + if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" == "$MONGO_VERSION" ]]; then + msg_info "Update MongoDB $MONGO_VERSION" - if [[ "$INSTALLED_VERSION" == "$MONGO_VERSION" ]]; then - if [[ "$CACHED_VERSION" == "$MONGO_VERSION" ]]; then - upgrade_package mongodb-org - else - $STD apt update || { - msg_error "Failed to update package list" - return 1 - } - $STD apt install --only-upgrade -y mongodb-org || { - msg_error "Failed to upgrade MongoDB" - return 1 - } - cache_installed_version "mongodb" "$MONGO_VERSION" - fi + ensure_apt_working || return 1 + + # Perform upgrade + $STD apt install --only-upgrade -y mongodb-org || { + msg_error "Failed to upgrade MongoDB" + return 1 + } + cache_installed_version "mongodb" "$MONGO_VERSION" + msg_ok "Update MongoDB $MONGO_VERSION" return 0 fi - msg_info "Setup MongoDB $MONGO_VERSION" - - if [[ -n "$INSTALLED_VERSION" ]]; then - $STD systemctl stop mongod 2>/dev/null || true - $STD apt purge -y mongodb-org 2>/dev/null || true + # Scenario 2: Different version installed - clean upgrade + if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" != "$MONGO_VERSION" ]]; then + msg_info "Upgrade MongoDB from $INSTALLED_VERSION to $MONGO_VERSION" + remove_old_tool_version "mongodb" + else + msg_info "Setup MongoDB $MONGO_VERSION" fi - cleanup_old_repo_files "mongodb-org-${MONGO_VERSION}" cleanup_orphaned_sources - # Map distro codenames to MongoDB-supported suites - local SUITE - if [[ "$DISTRO_ID" == "debian" ]]; then - case "$DISTRO_CODENAME" in - trixie | forky | sid) - # Debian 13+ and testing/unstable use Bookworm (MongoDB doesn't support newer) - SUITE="bookworm" - ;; - bookworm) - SUITE="bookworm" - ;; - bullseye) - SUITE="bullseye" - ;; - buster) - SUITE="buster" - ;; - *) - # Fallback: try bookworm for unknown Debian releases - msg_warn "Unknown Debian release '${DISTRO_CODENAME}', using bookworm" - SUITE="bookworm" - ;; - esac - elif [[ "$DISTRO_ID" == "ubuntu" ]]; then - case "$DISTRO_CODENAME" in - oracular | plucky) - # Ubuntu 24.10+ uses noble - SUITE="noble" - ;; - noble) - SUITE="noble" - ;; - jammy) - SUITE="jammy" - ;; - focal) - SUITE="focal" - ;; - *) - # Fallback: try noble for unknown Ubuntu releases - msg_warn "Unknown Ubuntu release '${DISTRO_CODENAME}', using noble" - SUITE="noble" - ;; - esac - fi - - # Verify MongoDB repository is available (MongoDB has nested structure) - if ! curl -fsSL --max-time 10 "${MONGO_BASE_URL}/dists/${SUITE}/mongodb-org/${MONGO_VERSION}/Release" &>/dev/null; then - msg_error "MongoDB ${MONGO_VERSION} repository not available for ${DISTRO_ID}-${SUITE}" - msg_error "Please check: ${MONGO_BASE_URL}/dists/${SUITE}/mongodb-org/${MONGO_VERSION}/" - return 1 - fi - - mkdir -p /etc/apt/keyrings - if ! curl -fsSL "https://www.mongodb.org/static/pgp/server-${MONGO_VERSION}.asc" | - gpg --dearmor --yes -o "/etc/apt/keyrings/mongodb-server-${MONGO_VERSION}.gpg"; then - msg_error "Failed to download or import MongoDB GPG key" - return 1 - fi - - cat </etc/apt/sources.list.d/mongodb-org-${MONGO_VERSION}.sources -Types: deb -URIs: ${MONGO_BASE_URL} -Suites: ${SUITE}/mongodb-org/${MONGO_VERSION} -Components: ${REPO_COMPONENT} -Architectures: amd64 arm64 -Signed-By: /etc/apt/keyrings/mongodb-server-${MONGO_VERSION}.gpg -EOF - - $STD apt update || { - msg_error "APT update failed — invalid MongoDB repo for ${DISTRO_ID}-${DISTRO_CODENAME}? (Suite: ${SUITE})" + # Setup repository + manage_tool_repository "mongodb" "$MONGO_VERSION" "$MONGO_BASE_URL" \ + "https://www.mongodb.org/static/pgp/server-${MONGO_VERSION}.asc" || { + msg_error "Failed to setup MongoDB repository" return 1 } + # Wait for repo to settle + $STD apt update || { + msg_error "APT update failed — invalid MongoDB repo for ${DISTRO_ID}-${DISTRO_CODENAME}?" + return 1 + } + + # Install MongoDB $STD apt install -y mongodb-org || { msg_error "Failed to install MongoDB packages" return 1 @@ -2526,59 +2810,47 @@ EOF function setup_mysql() { local MYSQL_VERSION="${MYSQL_VERSION:-8.0}" - local CURRENT_VERSION="" - local NEED_INSTALL=false local DISTRO_ID DISTRO_CODENAME DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - if command -v mysql >/dev/null; then - CURRENT_VERSION="$(mysql --version | grep -oP '[0-9]+\.[0-9]+' | head -n1)" - if [[ "$CURRENT_VERSION" != "$MYSQL_VERSION" ]]; then - NEED_INSTALL=true - else - if apt list --upgradable 2>/dev/null | grep -q '^mysql-server/'; then - $STD apt update || { - msg_error "Failed to update package list" - return 1 - } - $STD apt install --only-upgrade -y mysql-server || { - msg_error "Failed to upgrade MySQL" - return 1 - } - fi - return - fi - else - NEED_INSTALL=true + # Get currently installed version + local CURRENT_VERSION="" + CURRENT_VERSION=$(is_tool_installed "mysql" 2>/dev/null) || true + + # Scenario 1: Already at target version - just update packages + if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$MYSQL_VERSION" ]]; then + msg_info "Update MySQL $MYSQL_VERSION" + + ensure_apt_working || return 1 + + $STD apt install --only-upgrade -y mysql-server mysql-client || true + + cache_installed_version "mysql" "$MYSQL_VERSION" + msg_ok "Update MySQL $MYSQL_VERSION" + return 0 fi - if [[ "$NEED_INSTALL" == true ]]; then + # Scenario 2: Different version installed - clean upgrade + if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" != "$MYSQL_VERSION" ]]; then + msg_info "Upgrade MySQL from $CURRENT_VERSION to $MYSQL_VERSION" + remove_old_tool_version "mysql" + else msg_info "Setup MySQL $MYSQL_VERSION" + fi + + # Debian 13+ Fix: MySQL 8.0 incompatible with libaio1t64, use 8.4 LTS + if [[ "$DISTRO_ID" == "debian" && "$DISTRO_CODENAME" =~ ^(trixie|forky|sid)$ ]]; then + msg_info "Debian ${DISTRO_CODENAME} detected → using MySQL 8.4 LTS (libaio1t64 compatible)" cleanup_old_repo_files "mysql" - local SUITE - if [[ "$DISTRO_ID" == "debian" ]]; then - case "$DISTRO_CODENAME" in - bookworm | bullseye) SUITE="$DISTRO_CODENAME" ;; - trixie | forky | sid) SUITE="bookworm" ;; - *) SUITE="bookworm" ;; - esac - else - SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://repo.mysql.com/apt/${DISTRO_ID}") + if ! curl -fsSL https://repo.mysql.com/RPM-GPG-KEY-mysql-2023 | gpg --dearmor -o /etc/apt/keyrings/mysql.gpg 2>/dev/null; then + msg_error "Failed to import MySQL GPG key" + return 1 fi - $STD systemctl stop mysql 2>/dev/null || true - if dpkg -l 2>/dev/null | grep -q "^ii.*mysql-server"; then - $STD apt purge -y mysql-server* mysql-client* mysql-common mysql-community-* 2>/dev/null || true - fi - - # --- Debian 13+ Fix: MySQL 8.0 incompatible with libaio1t64 --- - if [[ "$DISTRO_ID" == "debian" && "$DISTRO_CODENAME" =~ ^(trixie|forky|sid)$ ]]; then - msg_info "Debian ${DISTRO_CODENAME} detected → using MySQL 8.4 LTS (libaio1t64)" - curl -fsSL https://repo.mysql.com/RPM-GPG-KEY-mysql-2023 | gpg --dearmor -o /etc/apt/keyrings/mysql.gpg - cat >/etc/apt/sources.list.d/mysql.sources <<'EOF' + cat >/etc/apt/sources.list.d/mysql.sources <<'EOF' Types: deb URIs: https://repo.mysql.com/apt/debian/ Suites: bookworm @@ -2586,56 +2858,80 @@ Components: mysql-8.4-lts Architectures: amd64 arm64 Signed-By: /etc/apt/keyrings/mysql.gpg EOF + + $STD apt update || { + msg_error "Failed to update APT for MySQL 8.4 LTS" + return 1 + } + + if ! $STD apt install -y mysql-community-server mysql-community-client; then + msg_warn "MySQL 8.4 LTS installation failed – falling back to MariaDB" + cleanup_old_repo_files "mysql" $STD apt update - if ! $STD apt install -y mysql-community-server mysql-community-client; then - msg_error "MySQL 8.4 LTS installation failed – falling back to MariaDB" - $STD apt install -y mariadb-server mariadb-client - fi - msg_ok "Setup Database Engine (MySQL 8.4 LTS / MariaDB)" - return - fi - # ------------------------------------------------------------- - - setup_deb822_repo \ - "mysql" \ - "https://repo.mysql.com/RPM-GPG-KEY-mysql-2023" \ - "https://repo.mysql.com/apt/${DISTRO_ID}" \ - "$SUITE" \ - "mysql-${MYSQL_VERSION}" \ - "amd64 arm64" - - export DEBIAN_FRONTEND=noninteractive - if ! $STD apt update; then - msg_error "APT update failed for MySQL repository" - return 1 - fi - - local mysql_install_success=false - if apt-cache search "^mysql-server$" 2>/dev/null | grep -q . && $STD apt install -y mysql-server mysql-client 2>/dev/null; then - mysql_install_success=true - fi - if [[ "$mysql_install_success" == false ]] && apt-cache search "^mysql-community-server$" 2>/dev/null | grep -q . && $STD apt install -y mysql-community-server mysql-community-client 2>/dev/null; then - mysql_install_success=true - fi - if [[ "$mysql_install_success" == false ]] && apt-cache search "^mysql$" 2>/dev/null | grep -q . && $STD apt install -y mysql 2>/dev/null; then - mysql_install_success=true - fi - if [[ "$mysql_install_success" == false ]]; then - msg_error "MySQL ${MYSQL_VERSION} package not available for suite ${SUITE}" - return 1 - fi - - if ! command -v mysql >/dev/null 2>&1; then - hash -r - if ! command -v mysql >/dev/null 2>&1; then - msg_error "MySQL installed but mysql command still not found" + $STD apt install -y mariadb-server mariadb-client || { + msg_error "Failed to install database engine (MySQL/MariaDB fallback)" return 1 - fi + } + msg_ok "Setup Database Engine (MariaDB fallback on Debian ${DISTRO_CODENAME})" + return 0 fi - cache_installed_version "mysql" "$MYSQL_VERSION" - msg_ok "Setup MySQL $MYSQL_VERSION" + cache_installed_version "mysql" "8.4" + msg_ok "Setup MySQL 8.4 LTS (Debian ${DISTRO_CODENAME})" + return 0 fi + + # Standard setup for other distributions + local SUITE + if [[ "$DISTRO_ID" == "debian" ]]; then + case "$DISTRO_CODENAME" in + bookworm | bullseye) SUITE="$DISTRO_CODENAME" ;; + *) SUITE="bookworm" ;; + esac + else + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://repo.mysql.com/apt/${DISTRO_ID}") + fi + + # Setup repository + manage_tool_repository "mysql" "$MYSQL_VERSION" "https://repo.mysql.com/apt/${DISTRO_ID}" \ + "https://repo.mysql.com/RPM-GPG-KEY-mysql-2023" || { + msg_error "Failed to setup MySQL repository" + return 1 + } + + ensure_apt_working || return 1 + + # Try multiple package names (mysql-server, mysql-community-server, mysql) + export DEBIAN_FRONTEND=noninteractive + local mysql_install_success=false + + if apt-cache search "^mysql-server$" 2>/dev/null | grep -q . && \ + $STD apt install -y mysql-server mysql-client 2>/dev/null; then + mysql_install_success=true + elif apt-cache search "^mysql-community-server$" 2>/dev/null | grep -q . && \ + $STD apt install -y mysql-community-server mysql-community-client 2>/dev/null; then + mysql_install_success=true + elif apt-cache search "^mysql$" 2>/dev/null | grep -q . && \ + $STD apt install -y mysql 2>/dev/null; then + mysql_install_success=true + fi + + if [[ "$mysql_install_success" == false ]]; then + msg_error "MySQL ${MYSQL_VERSION} package not available for suite ${SUITE}" + return 1 + fi + + # Verify mysql command is accessible + if ! command -v mysql >/dev/null 2>&1; then + hash -r + if ! command -v mysql >/dev/null 2>&1; then + msg_error "MySQL installed but mysql command still not found" + return 1 + fi + fi + + cache_installed_version "mysql" "$MYSQL_VERSION" + msg_ok "Setup MySQL $MYSQL_VERSION" } # ------------------------------------------------------------------------------ @@ -2653,59 +2949,62 @@ EOF function setup_nodejs() { local NODE_VERSION="${NODE_VERSION:-22}" local NODE_MODULE="${NODE_MODULE:-}" + + # Get currently installed version local CURRENT_NODE_VERSION="" - local NEED_NODE_INSTALL=false - - # Check if Node.js is already installed - if command -v node >/dev/null; then - CURRENT_NODE_VERSION="$(node -v | grep -oP '^v\K[0-9]+')" - if [[ "$CURRENT_NODE_VERSION" != "$NODE_VERSION" ]]; then - msg_info "Old Node.js $CURRENT_NODE_VERSION found, replacing with $NODE_VERSION" - NEED_NODE_INSTALL=true - fi - else - msg_info "Setup Node.js $NODE_VERSION" - NEED_NODE_INSTALL=true - fi + CURRENT_NODE_VERSION=$(is_tool_installed "nodejs" 2>/dev/null) || true + # Ensure jq is available for JSON parsing if ! command -v jq &>/dev/null; then - $STD apt-get update - $STD apt-get install -y jq || { + $STD apt update + $STD apt install -y jq || { msg_error "Failed to install jq" return 1 } fi - # Install Node.js if required - if [[ "$NEED_NODE_INSTALL" == true ]]; then - ensure_dependencies curl ca-certificates gnupg + # Scenario 1: Already installed at target version - just update packages/modules + if [[ -n "$CURRENT_NODE_VERSION" && "$CURRENT_NODE_VERSION" == "$NODE_VERSION" ]]; then + msg_info "Update Node.js $NODE_VERSION" - if [[ -n "$CURRENT_NODE_VERSION" ]]; then - $STD apt-get purge -y nodejs npm || true + ensure_apt_working || return 1 + + # Just update npm to latest + $STD npm install -g npm@latest 2>/dev/null || true + + cache_installed_version "nodejs" "$NODE_VERSION" + msg_ok "Update Node.js $NODE_VERSION" + else + # Scenario 2: Different version installed - clean upgrade + if [[ -n "$CURRENT_NODE_VERSION" && "$CURRENT_NODE_VERSION" != "$NODE_VERSION" ]]; then + msg_info "Upgrade Node.js from $CURRENT_NODE_VERSION to $NODE_VERSION" + remove_old_tool_version "nodejs" + else + msg_info "Setup Node.js $NODE_VERSION" fi - cleanup_old_repo_files "nodesource" + ensure_dependencies curl ca-certificates gnupg - # NodeSource uses deb822 format with "nodistro" - setup_deb822_repo \ - "nodesource" \ - "https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key" \ - "https://deb.nodesource.com/node_${NODE_VERSION}.x" \ - "nodistro" \ - "main" \ - "amd64 arm64" + # Setup repository + manage_tool_repository "nodejs" "$NODE_VERSION" "https://deb.nodesource.com/node_${NODE_VERSION}.x" "" || { + msg_error "Failed to setup Node.js repository" + return 1 + } + # Wait for repo to settle sleep 2 - if ! apt-get update >/dev/null 2>&1; then + + # Install Node.js + if ! apt update >/dev/null 2>&1; then msg_warn "APT update failed – retrying in 5s" sleep 5 - if ! apt-get update >/dev/null 2>&1; then + if ! apt update >/dev/null 2>&1; then msg_error "Failed to update APT repositories after adding NodeSource" return 1 fi fi - if ! apt-get install -y nodejs >/dev/null 2>&1; then + if ! apt install -y nodejs >/dev/null 2>&1; then msg_error "Failed to install Node.js ${NODE_VERSION} from NodeSource" return 1 fi @@ -2713,10 +3012,11 @@ function setup_nodejs() { # Update to latest npm $STD npm install -g npm@latest || { msg_error "Failed to update npm to latest version" + return 1 } cache_installed_version "nodejs" "$NODE_VERSION" - msg_ok "Setup Node.js ${NODE_VERSION}" + msg_ok "Setup Node.js $NODE_VERSION" fi export NODE_OPTIONS="--max-old-space-size=4096" @@ -2733,6 +3033,7 @@ function setup_nodejs() { # Install global Node modules if [[ -n "$NODE_MODULE" ]]; then IFS=',' read -ra MODULES <<<"$NODE_MODULE" + local failed_modules=0 for mod in "${MODULES[@]}"; do local MODULE_NAME MODULE_REQ_VERSION MODULE_INSTALLED_VERSION if [[ "$mod" == @*/*@* ]]; then @@ -2754,26 +3055,33 @@ function setup_nodejs() { MODULE_INSTALLED_VERSION="$(npm list -g --depth=0 "$MODULE_NAME" | grep "$MODULE_NAME@" | awk -F@ '{print $2}' | tr -d '[:space:]')" if [[ "$MODULE_REQ_VERSION" != "latest" && "$MODULE_REQ_VERSION" != "$MODULE_INSTALLED_VERSION" ]]; then msg_info "Updating $MODULE_NAME from v$MODULE_INSTALLED_VERSION to v$MODULE_REQ_VERSION" - if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}"; then - msg_error "Failed to update $MODULE_NAME to version $MODULE_REQ_VERSION" - return 1 + if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}" 2>/dev/null; then + msg_warn "Failed to update $MODULE_NAME to version $MODULE_REQ_VERSION" + ((failed_modules++)) + continue fi elif [[ "$MODULE_REQ_VERSION" == "latest" ]]; then msg_info "Updating $MODULE_NAME to latest version" - if ! $STD npm install -g "${MODULE_NAME}@latest"; then - msg_error "Failed to update $MODULE_NAME to latest version" - return 1 + if ! $STD npm install -g "${MODULE_NAME}@latest" 2>/dev/null; then + msg_warn "Failed to update $MODULE_NAME to latest version" + ((failed_modules++)) + continue fi fi else msg_info "Installing $MODULE_NAME@$MODULE_REQ_VERSION" - if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}"; then - msg_error "Failed to install $MODULE_NAME@$MODULE_REQ_VERSION" - return 1 + if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}" 2>/dev/null; then + msg_warn "Failed to install $MODULE_NAME@$MODULE_REQ_VERSION" + ((failed_modules++)) + continue fi fi done - msg_ok "Installed Node.js modules: $NODE_MODULE" + if [[ $failed_modules -eq 0 ]]; then + msg_ok "Installed Node.js modules: $NODE_MODULE" + else + msg_warn "Installed Node.js modules with $failed_modules failure(s): $NODE_MODULE" + fi fi } @@ -2825,47 +3133,46 @@ function setup_php() { # Get current PHP-CLI version local CURRENT_PHP="" - if command -v php >/dev/null 2>&1; then - CURRENT_PHP=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2) - fi + CURRENT_PHP=$(is_tool_installed "php" 2>/dev/null) || true - msg_info "Setup PHP $PHP_VERSION" + # Scenario 1: Already at target version - just update packages + if [[ -n "$CURRENT_PHP" && "$CURRENT_PHP" == "$PHP_VERSION" ]]; then + msg_info "Update PHP $PHP_VERSION" - if [[ -n "$CURRENT_PHP" && "$CURRENT_PHP" != "$PHP_VERSION" ]]; then - $STD apt purge -y "php${CURRENT_PHP//./}"* || true - fi + # Ensure Sury repo is available + if [[ ! -f /etc/apt/sources.list.d/php.sources ]]; then + manage_tool_repository "php" "$PHP_VERSION" "" "https://packages.sury.org/debsuryorg-archive-keyring.deb" || { + msg_error "Failed to setup PHP repository" + return 1 + } + fi - # Ensure Sury repo is available - if [[ ! -f /etc/apt/sources.list.d/php.sources ]]; then - # Cleanup old repository files - cleanup_old_repo_files "php" + ensure_apt_working || return 1 - $STD curl -fsSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb || { - msg_error "Failed to download PHP repository keyring" - return 1 - } - $STD dpkg -i /tmp/debsuryorg-archive-keyring.deb || { - msg_error "Failed to install PHP repository keyring" - rm -f /tmp/debsuryorg-archive-keyring.deb + # Just update PHP packages + $STD apt install --only-upgrade -y "php${PHP_VERSION}" || true + + cache_installed_version "php" "$PHP_VERSION" + msg_ok "Update PHP $PHP_VERSION" + else + # Scenario 2: Different version installed - clean upgrade + if [[ -n "$CURRENT_PHP" && "$CURRENT_PHP" != "$PHP_VERSION" ]]; then + msg_info "Upgrade PHP from $CURRENT_PHP to $PHP_VERSION" + # Stop old PHP-FPM if running + $STD systemctl stop "php${CURRENT_PHP}-fpm" >/dev/null 2>&1 || true + $STD systemctl disable "php${CURRENT_PHP}-fpm" >/dev/null 2>&1 || true + remove_old_tool_version "php" + else + msg_info "Setup PHP $PHP_VERSION" + fi + + # Setup Sury repository + manage_tool_repository "php" "$PHP_VERSION" "" "https://packages.sury.org/debsuryorg-archive-keyring.deb" || { + msg_error "Failed to setup PHP repository" return 1 } - # Use helper function to get fallback suite - local SUITE - SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://packages.sury.org/php") - - cat </etc/apt/sources.list.d/php.sources -Types: deb -URIs: https://packages.sury.org/php/ -Suites: $SUITE -Components: main -Architectures: amd64 arm64 -Signed-By: /usr/share/keyrings/deb.sury.org-php.gpg -EOF - $STD apt update || { - msg_error "APT update failed for PHP repository" - return 1 - } + ensure_apt_working || return 1 fi # Build module list @@ -2890,20 +3197,14 @@ EOF fi fi - # setup / update PHP modules + # Install PHP packages $STD apt install -y $MODULE_LIST || { msg_error "Failed to install PHP packages" return 1 } cache_installed_version "php" "$PHP_VERSION" - # optional stop old PHP-FPM service - if [[ "$PHP_FPM" == "YES" && -n "$CURRENT_PHP" && "$CURRENT_PHP" != "$PHP_VERSION" ]]; then - $STD systemctl stop php"${CURRENT_PHP}"-fpm || true - $STD systemctl disable php"${CURRENT_PHP}"-fpm || true - fi - - # Patch all relevant php.ini files (silent) + # Patch all relevant php.ini files local PHP_INI_PATHS=("/etc/php/${PHP_VERSION}/cli/php.ini") [[ "$PHP_FPM" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/fpm/php.ini") [[ "$PHP_APACHE" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/apache2/php.ini") @@ -2916,7 +3217,7 @@ EOF fi done - # patch Apache configuration if needed + # Patch Apache configuration if needed if [[ "$PHP_APACHE" == "YES" ]]; then for mod in $(ls /etc/apache2/mods-enabled/ 2>/dev/null | grep -E '^php[0-9]\.[0-9]\.conf$' | sed 's/\.conf//'); do if [[ "$mod" != "php${PHP_VERSION}" ]]; then @@ -2928,7 +3229,7 @@ EOF safe_service_restart apache2 || true fi - # enable and restart PHP-FPM if requested + # Enable and restart PHP-FPM if requested if [[ "$PHP_FPM" == "YES" ]]; then if systemctl list-unit-files | grep -q "php${PHP_VERSION}-fpm.service"; then $STD systemctl enable php${PHP_VERSION}-fpm @@ -2951,120 +3252,136 @@ EOF # # Variables: # PG_VERSION - Major PostgreSQL version (e.g. 15, 16) (default: 16) -# PG_MODULES - Comma-separated list of extensions (e.g. "postgis,contrib") -# ------------------------------------------------------------------------------ function setup_postgresql() { local PG_VERSION="${PG_VERSION:-16}" local PG_MODULES="${PG_MODULES:-}" - local CURRENT_PG_VERSION="" local DISTRO_ID DISTRO_CODENAME - local NEED_PG_INSTALL=false DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + # Get currently installed version + local CURRENT_PG_VERSION="" if command -v psql >/dev/null; then - CURRENT_PG_VERSION="$(psql -V | awk '{print $3}' | cut -d. -f1)" - [[ "$CURRENT_PG_VERSION" != "$PG_VERSION" ]] && NEED_PG_INSTALL=true - else - NEED_PG_INSTALL=true + CURRENT_PG_VERSION="$(psql -V 2>/dev/null | awk '{print $3}' | cut -d. -f1)" fi - if [[ "$NEED_PG_INSTALL" == true ]]; then - msg_info "Setup PostgreSQL $PG_VERSION" - - if [[ -n "$CURRENT_PG_VERSION" ]]; then - $STD runuser -u postgres -- pg_dumpall >/var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql - $STD systemctl stop postgresql - fi - - # Cleanup old repository files - cleanup_old_repo_files "pgdg" - - # PostgreSQL PGDG repository uses special suite naming - # For unstable/testing Debian, we need to check what's actually available - local SUITE - case "$DISTRO_CODENAME" in - trixie | forky | sid) - # Try trixie-pgdg first, fallback to bookworm-pgdg if not available - if verify_repo_available "https://apt.postgresql.org/pub/repos/apt" "trixie-pgdg"; then - SUITE="trixie-pgdg" - else - SUITE="bookworm-pgdg" - fi - ;; - *) - # Use helper function for stable releases - SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://apt.postgresql.org/pub/repos/apt") - # PGDG uses special suite naming: ${SUITE}-pgdg - SUITE="${SUITE}-pgdg" - ;; - esac - - # Use standardized repo setup - setup_deb822_repo \ - "pgdg" \ - "https://www.postgresql.org/media/keys/ACCC4CF8.asc" \ - "https://apt.postgresql.org/pub/repos/apt" \ - "$SUITE" \ - "main" \ - "amd64 arm64" - - # Update apt - if ! $STD apt update; then - msg_error "APT update failed for PostgreSQL repository" - return 1 - fi - - # Try multiple PostgreSQL package patterns - local pg_install_success=false - - # First, ensure ssl-cert is available (PostgreSQL dependency) - if apt-cache search "^ssl-cert$" 2>/dev/null | grep -q .; then - $STD apt install -y ssl-cert 2>/dev/null || true - fi - - # First try: postgresql-X (common in most repos) - if apt-cache search "^postgresql-${PG_VERSION}$" 2>/dev/null | grep -q . && $STD apt install -y "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}" 2>/dev/null; then - pg_install_success=true - fi - - # Second try: postgresql-server-X (some repos use this) - if [[ "$pg_install_success" == false ]] && apt-cache search "^postgresql-server-${PG_VERSION}$" 2>/dev/null | grep -q . && $STD apt install -y "postgresql-server-${PG_VERSION}" "postgresql-client-${PG_VERSION}" 2>/dev/null; then - pg_install_success=true - fi - - # Third try: just postgresql (any version) - if [[ "$pg_install_success" == false ]] && apt-cache search "^postgresql$" 2>/dev/null | grep -q . && $STD apt install -y postgresql postgresql-client 2>/dev/null; then - pg_install_success=true - fi - - if [[ "$pg_install_success" == false ]]; then - msg_error "PostgreSQL package not available for suite ${SUITE}" - return 1 - fi - - # Verify installation - if ! command -v psql >/dev/null 2>&1; then - msg_error "PostgreSQL installed but psql command not found" - return 1 - fi - - if [[ -n "$CURRENT_PG_VERSION" ]]; then - $STD apt purge -y "postgresql-${CURRENT_PG_VERSION}" "postgresql-client-${CURRENT_PG_VERSION}" 2>/dev/null || true - $STD runuser -u postgres -- psql /dev/null || true - fi - - $STD systemctl enable --now postgresql 2>/dev/null || true - - # Add PostgreSQL binaries to PATH for the install script - if ! grep -q '/usr/lib/postgresql' /etc/environment 2>/dev/null; then - echo 'PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/postgresql/'"${PG_VERSION}"'/bin"' >/etc/environment - fi - + # Scenario 1: Already at correct version + if [[ "$CURRENT_PG_VERSION" == "$PG_VERSION" ]]; then + msg_info "Update PostgreSQL $PG_VERSION" + $STD apt update + $STD apt install --only-upgrade -y "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}" 2>/dev/null || true cache_installed_version "postgresql" "$PG_VERSION" - msg_ok "Setup PostgreSQL $PG_VERSION" + msg_ok "Update PostgreSQL $PG_VERSION" + + # Still install modules if specified + if [[ -n "$PG_MODULES" ]]; then + IFS=',' read -ra MODULES <<<"$PG_MODULES" + for module in "${MODULES[@]}"; do + $STD apt install -y "postgresql-${PG_VERSION}-${module}" 2>/dev/null || true + done + fi + return 0 fi + # Scenario 2: Different version - backup, remove old, install new + if [[ -n "$CURRENT_PG_VERSION" ]]; then + msg_info "Upgrade PostgreSQL from $CURRENT_PG_VERSION to $PG_VERSION" + msg_info "Creating backup of PostgreSQL $CURRENT_PG_VERSION databases..." + $STD runuser -u postgres -- pg_dumpall >/var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql || { + msg_error "Failed to backup PostgreSQL databases" + return 1 + } + $STD systemctl stop postgresql || true + $STD apt purge -y "postgresql-${CURRENT_PG_VERSION}" "postgresql-client-${CURRENT_PG_VERSION}" 2>/dev/null || true + else + msg_info "Setup PostgreSQL $PG_VERSION" + fi + + # Scenario 3: Fresh install or after removal - setup repo and install + cleanup_old_repo_files "pgdg" + + local SUITE + case "$DISTRO_CODENAME" in + trixie | forky | sid) + if verify_repo_available "https://apt.postgresql.org/pub/repos/apt" "trixie-pgdg"; then + SUITE="trixie-pgdg" + else + SUITE="bookworm-pgdg" + fi + ;; + *) + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://apt.postgresql.org/pub/repos/apt") + SUITE="${SUITE}-pgdg" + ;; + esac + + setup_deb822_repo \ + "pgdg" \ + "https://www.postgresql.org/media/keys/ACCC4CF8.asc" \ + "https://apt.postgresql.org/pub/repos/apt" \ + "$SUITE" \ + "main" \ + "amd64 arm64" + + if ! $STD apt update; then + msg_error "APT update failed for PostgreSQL repository" + return 1 + fi + + # Install ssl-cert dependency if available + if apt-cache search "^ssl-cert$" 2>/dev/null | grep -q .; then + $STD apt install -y ssl-cert 2>/dev/null || true + fi + + # Try multiple PostgreSQL package patterns + local pg_install_success=false + + if apt-cache search "^postgresql-${PG_VERSION}$" 2>/dev/null | grep -q . && \ + $STD apt install -y "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}" 2>/dev/null; then + pg_install_success=true + fi + + if [[ "$pg_install_success" == false ]] && \ + apt-cache search "^postgresql-server-${PG_VERSION}$" 2>/dev/null | grep -q . && \ + $STD apt install -y "postgresql-server-${PG_VERSION}" "postgresql-client-${PG_VERSION}" 2>/dev/null; then + pg_install_success=true + fi + + if [[ "$pg_install_success" == false ]] && \ + apt-cache search "^postgresql$" 2>/dev/null | grep -q . && \ + $STD apt install -y postgresql postgresql-client 2>/dev/null; then + pg_install_success=true + fi + + if [[ "$pg_install_success" == false ]]; then + msg_error "PostgreSQL package not available for suite ${SUITE}" + return 1 + fi + + if ! command -v psql >/dev/null 2>&1; then + msg_error "PostgreSQL installed but psql command not found" + return 1 + fi + + # Restore database backup if we upgraded from previous version + if [[ -n "$CURRENT_PG_VERSION" ]]; then + msg_info "Restoring PostgreSQL databases from backup..." + $STD runuser -u postgres -- psql /dev/null || { + msg_warn "Failed to restore database backup - this may be expected for major version upgrades" + } + fi + + $STD systemctl enable --now postgresql 2>/dev/null || true + + # Add PostgreSQL binaries to PATH + if ! grep -q '/usr/lib/postgresql' /etc/environment 2>/dev/null; then + echo 'PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/postgresql/'"${PG_VERSION}"'/bin"' >/etc/environment + fi + + cache_installed_version "postgresql" "$PG_VERSION" + msg_ok "Setup PostgreSQL $PG_VERSION" + + # Install optional modules if [[ -n "$PG_MODULES" ]]; then IFS=',' read -ra MODULES <<<"$PG_MODULES" for module in "${MODULES[@]}"; do @@ -3093,16 +3410,31 @@ function setup_ruby() { local RBENV_BIN="$RBENV_DIR/bin/rbenv" local PROFILE_FILE="$HOME/.profile" local TMP_DIR=$(mktemp -d) - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "ruby") - msg_info "Setup Ruby $RUBY_VERSION" + # Get currently installed Ruby version + local CURRENT_RUBY_VERSION="" + if [[ -x "$RBENV_BIN" ]]; then + CURRENT_RUBY_VERSION=$("$RBENV_BIN" global 2>/dev/null || echo "") + fi + + # Scenario 1: Already at correct Ruby version + if [[ "$CURRENT_RUBY_VERSION" == "$RUBY_VERSION" ]]; then + msg_info "Update Ruby $RUBY_VERSION" + cache_installed_version "ruby" "$RUBY_VERSION" + msg_ok "Update Ruby $RUBY_VERSION" + return 0 + fi + + # Scenario 2: Different version - reinstall + if [[ -n "$CURRENT_RUBY_VERSION" ]]; then + msg_info "Upgrade Ruby from $CURRENT_RUBY_VERSION to $RUBY_VERSION" + else + msg_info "Setup Ruby $RUBY_VERSION" + fi - # Ensure APT is working before installing dependencies ensure_apt_working || return 1 - # Install build dependencies - with fallback for different Debian versions - # In Trixie: libreadline6-dev -> libreadline-dev, libncurses5-dev -> libncurses-dev + # Install build dependencies with fallbacks local ruby_deps=() local dep_variations=( "jq" @@ -3123,13 +3455,10 @@ function setup_ruby() { for dep_pattern in "${dep_variations[@]}"; do if [[ "$dep_pattern" == *"|"* ]]; then - # Try multiple variations IFS='|' read -ra variations <<<"$dep_pattern" - local found=false for var in "${variations[@]}"; do if apt-cache search "^${var}$" 2>/dev/null | grep -q .; then ruby_deps+=("$var") - found=true break fi done @@ -3144,70 +3473,76 @@ function setup_ruby() { $STD apt install -y "${ruby_deps[@]}" 2>/dev/null || true else msg_error "No Ruby build dependencies available" - return 1 - fi - - local RBENV_RELEASE - RBENV_RELEASE=$(curl -fsSL https://api.github.com/repos/rbenv/rbenv/releases/latest | jq -r '.tag_name' | sed 's/^v//') - if [[ -z "$RBENV_RELEASE" ]]; then - msg_error "Failed to fetch latest rbenv version" rm -rf "$TMP_DIR" return 1 fi - curl -fsSL "https://github.com/rbenv/rbenv/archive/refs/tags/v${RBENV_RELEASE}.tar.gz" -o "$TMP_DIR/rbenv.tar.gz" || { - msg_error "Failed to download rbenv" - rm -rf "$TMP_DIR" - return 1 - } + # Download and build rbenv if needed + if [[ ! -x "$RBENV_BIN" ]]; then + local RBENV_RELEASE + RBENV_RELEASE=$(curl -fsSL https://api.github.com/repos/rbenv/rbenv/releases/latest 2>/dev/null | jq -r '.tag_name' | sed 's/^v//') || { + msg_error "Failed to fetch latest rbenv version" + rm -rf "$TMP_DIR" + return 1 + } - tar -xzf "$TMP_DIR/rbenv.tar.gz" -C "$TMP_DIR" || { - msg_error "Failed to extract rbenv" - rm -rf "$TMP_DIR" - return 1 - } + curl -fsSL "https://github.com/rbenv/rbenv/archive/refs/tags/v${RBENV_RELEASE}.tar.gz" -o "$TMP_DIR/rbenv.tar.gz" || { + msg_error "Failed to download rbenv" + rm -rf "$TMP_DIR" + return 1 + } - mkdir -p "$RBENV_DIR" - cp -r "$TMP_DIR/rbenv-${RBENV_RELEASE}/." "$RBENV_DIR/" - cd "$RBENV_DIR" && src/configure && $STD make -C src || { - msg_error "Failed to build rbenv" - rm -rf "$TMP_DIR" - return 1 - } + tar -xzf "$TMP_DIR/rbenv.tar.gz" -C "$TMP_DIR" || { + msg_error "Failed to extract rbenv" + rm -rf "$TMP_DIR" + return 1 + } - local RUBY_BUILD_RELEASE - RUBY_BUILD_RELEASE=$(curl -fsSL https://api.github.com/repos/rbenv/ruby-build/releases/latest | jq -r '.tag_name' | sed 's/^v//') - if [[ -z "$RUBY_BUILD_RELEASE" ]]; then - msg_error "Failed to fetch latest ruby-build version" - rm -rf "$TMP_DIR" - return 1 + mkdir -p "$RBENV_DIR" + cp -r "$TMP_DIR/rbenv-${RBENV_RELEASE}/." "$RBENV_DIR/" + (cd "$RBENV_DIR" && src/configure && $STD make -C src) || { + msg_error "Failed to build rbenv" + rm -rf "$TMP_DIR" + return 1 + } + + # Setup profile + if ! grep -q 'rbenv init' "$PROFILE_FILE" 2>/dev/null; then + echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >>"$PROFILE_FILE" + echo 'eval "$(rbenv init -)"' >>"$PROFILE_FILE" + fi fi - curl -fsSL "https://github.com/rbenv/ruby-build/archive/refs/tags/v${RUBY_BUILD_RELEASE}.tar.gz" -o "$TMP_DIR/ruby-build.tar.gz" || { - msg_error "Failed to download ruby-build" - rm -rf "$TMP_DIR" - return 1 - } + # Install ruby-build plugin + if [[ ! -d "$RBENV_DIR/plugins/ruby-build" ]]; then + local RUBY_BUILD_RELEASE + RUBY_BUILD_RELEASE=$(curl -fsSL https://api.github.com/repos/rbenv/ruby-build/releases/latest 2>/dev/null | jq -r '.tag_name' | sed 's/^v//') || { + msg_error "Failed to fetch latest ruby-build version" + rm -rf "$TMP_DIR" + return 1 + } - tar -xzf "$TMP_DIR/ruby-build.tar.gz" -C "$TMP_DIR" || { - msg_error "Failed to extract ruby-build" - rm -rf "$TMP_DIR" - return 1 - } + curl -fsSL "https://github.com/rbenv/ruby-build/archive/refs/tags/v${RUBY_BUILD_RELEASE}.tar.gz" -o "$TMP_DIR/ruby-build.tar.gz" || { + msg_error "Failed to download ruby-build" + rm -rf "$TMP_DIR" + return 1 + } - mkdir -p "$RBENV_DIR/plugins/ruby-build" - cp -r "$TMP_DIR/ruby-build-${RUBY_BUILD_RELEASE}/." "$RBENV_DIR/plugins/ruby-build/" - echo "$RUBY_BUILD_RELEASE" >"$RBENV_DIR/plugins/ruby-build/RUBY_BUILD_version.txt" + tar -xzf "$TMP_DIR/ruby-build.tar.gz" -C "$TMP_DIR" || { + msg_error "Failed to extract ruby-build" + rm -rf "$TMP_DIR" + return 1 + } - if ! grep -q 'rbenv init' "$PROFILE_FILE"; then - echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >>"$PROFILE_FILE" - echo 'eval "$(rbenv init -)"' >>"$PROFILE_FILE" + mkdir -p "$RBENV_DIR/plugins/ruby-build" + cp -r "$TMP_DIR/ruby-build-${RUBY_BUILD_RELEASE}/." "$RBENV_DIR/plugins/ruby-build/" fi + # Setup PATH and install Ruby version export PATH="$RBENV_DIR/bin:$PATH" - eval "$("$RBENV_BIN" init - bash)" + eval "$("$RBENV_BIN" init - bash)" 2>/dev/null || true - if ! "$RBENV_BIN" versions --bare | grep -qx "$RUBY_VERSION"; then + if ! "$RBENV_BIN" versions --bare 2>/dev/null | grep -qx "$RUBY_VERSION"; then $STD "$RBENV_BIN" install "$RUBY_VERSION" || { msg_error "Failed to install Ruby $RUBY_VERSION" rm -rf "$TMP_DIR" @@ -3215,14 +3550,18 @@ function setup_ruby() { } fi - "$RBENV_BIN" global "$RUBY_VERSION" + "$RBENV_BIN" global "$RUBY_VERSION" || { + msg_error "Failed to set Ruby $RUBY_VERSION as global version" + rm -rf "$TMP_DIR" + return 1 + } + hash -r + # Install Rails if requested if [[ "$RUBY_INSTALL_RAILS" == "true" ]]; then $STD gem install rails || { - msg_error "Failed to install Rails" - rm -rf "$TMP_DIR" - return 1 + msg_warn "Failed to install Rails - Ruby installation successful" } fi @@ -3250,100 +3589,68 @@ function setup_clickhouse() { DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - # Determine latest version if needed + # Resolve "latest" version if [[ "$CLICKHOUSE_VERSION" == "latest" ]]; then - # Try multiple methods to get the latest version CLICKHOUSE_VERSION=$(curl -fsSL https://packages.clickhouse.com/tgz/stable/ 2>/dev/null | grep -oP 'clickhouse-common-static-\K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | - sort -V | - tail -n1) - - # Fallback: Try GitHub releases API - if [[ -z "$CLICKHOUSE_VERSION" ]]; then + sort -V | tail -n1) || { CLICKHOUSE_VERSION=$(curl -fsSL https://api.github.com/repos/ClickHouse/ClickHouse/releases/latest 2>/dev/null | - grep -oP '"tag_name":\s*"v\K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | - head -n1) - fi + grep -oP '"tag_name":\s*"v\K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -n1) + } - # Final fallback: Parse HTML more liberally - if [[ -z "$CLICKHOUSE_VERSION" ]]; then - CLICKHOUSE_VERSION=$(curl -fsSL https://packages.clickhouse.com/tgz/stable/ 2>/dev/null | - grep -oP '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+(?=/clickhouse)' | - sort -V | - tail -n1) - fi - - if [[ -z "$CLICKHOUSE_VERSION" ]]; then + [[ -z "$CLICKHOUSE_VERSION" ]] && { msg_error "Could not determine latest ClickHouse version" return 1 - fi + } fi + # Get currently installed version local CURRENT_VERSION="" if command -v clickhouse-server >/dev/null 2>&1; then CURRENT_VERSION=$(clickhouse-server --version 2>/dev/null | grep -oP 'version \K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -n1) fi - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "clickhouse") - - # Check if already at target version - if [[ "$CURRENT_VERSION" == "$CLICKHOUSE_VERSION" ]]; then - if [[ "$CACHED_VERSION" == "$CLICKHOUSE_VERSION" ]]; then - upgrade_package clickhouse-server - upgrade_package clickhouse-client - else - msg_info "Setup ClickHouse $CLICKHOUSE_VERSION" - $STD apt update || { - msg_error "Failed to update package list" - return 1 - } - $STD apt install --only-upgrade -y clickhouse-server clickhouse-client || { - msg_error "Failed to upgrade ClickHouse" - return 1 - } - cache_installed_version "clickhouse" "$CLICKHOUSE_VERSION" - msg_ok "Setup ClickHouse $CLICKHOUSE_VERSION" - fi + # Scenario 1: Already at target version - just update packages + if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$CLICKHOUSE_VERSION" ]]; then + msg_info "Update ClickHouse $CLICKHOUSE_VERSION" + ensure_apt_working || return 1 + $STD apt install --only-upgrade -y clickhouse-server clickhouse-client || true + cache_installed_version "clickhouse" "$CLICKHOUSE_VERSION" + msg_ok "Update ClickHouse $CLICKHOUSE_VERSION" return 0 fi - msg_info "Setup ClickHouse $CLICKHOUSE_VERSION" - - # Stop existing service if upgrading - if [[ -n "$CURRENT_VERSION" ]]; then - $STD systemctl stop clickhouse-server || true + # Scenario 2: Different version - clean upgrade + if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" != "$CLICKHOUSE_VERSION" ]]; then + msg_info "Upgrade ClickHouse from $CURRENT_VERSION to $CLICKHOUSE_VERSION" + $STD systemctl stop clickhouse-server >/dev/null 2>&1 || true + remove_old_tool_version "clickhouse" + else + msg_info "Setup ClickHouse $CLICKHOUSE_VERSION" fi - # Cleanup old repository files - cleanup_old_repo_files "clickhouse" - - # Ensure dependencies ensure_dependencies apt-transport-https ca-certificates dirmngr gnupg - # ClickHouse uses 'stable' instead of distro codenames - local SUITE="stable" - - # Use standardized repo setup + # Setup repository (ClickHouse uses 'stable' suite) setup_deb822_repo \ "clickhouse" \ "https://packages.clickhouse.com/rpm/lts/repodata/repomd.xml.key" \ "https://packages.clickhouse.com/deb" \ - "$SUITE" \ + "stable" \ "main" \ "amd64 arm64" - # Update and install ClickHouse packages + # Install packages export DEBIAN_FRONTEND=noninteractive - if ! $STD apt update; then + $STD apt update || { msg_error "APT update failed for ClickHouse repository" return 1 - fi + } - if ! $STD apt install -y clickhouse-server clickhouse-client; then + $STD apt install -y clickhouse-server clickhouse-client || { msg_error "Failed to install ClickHouse packages" return 1 - fi + } # Verify installation if ! command -v clickhouse-server >/dev/null 2>&1; then @@ -3351,10 +3658,8 @@ function setup_clickhouse() { return 1 fi - # Create data directory if it doesn't exist + # Setup data directory mkdir -p /var/lib/clickhouse - - # Check if clickhouse user exists before chown if id clickhouse >/dev/null 2>&1; then chown -R clickhouse:clickhouse /var/lib/clickhouse fi @@ -3388,11 +3693,16 @@ function setup_rust() { local RUST_TOOLCHAIN="${RUST_TOOLCHAIN:-stable}" local RUST_CRATES="${RUST_CRATES:-}" local CARGO_BIN="${HOME}/.cargo/bin" - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "rust") + # Get currently installed version + local CURRENT_VERSION="" + if command -v rustc &>/dev/null; then + CURRENT_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}') + fi + + # Scenario 1: Rustup not installed - fresh install if ! command -v rustup &>/dev/null; then - msg_info "Setup Rust" + msg_info "Setup Rust ($RUST_TOOLCHAIN)" curl -fsSL https://sh.rustup.rs | $STD sh -s -- -y --default-toolchain "$RUST_TOOLCHAIN" || { msg_error "Failed to install Rust" return 1 @@ -3403,21 +3713,23 @@ function setup_rust() { cache_installed_version "rust" "$RUST_VERSION" msg_ok "Setup Rust $RUST_VERSION" else - msg_info "Setup Rust" + # Scenario 2: Rustup already installed - update/maintain + msg_info "Update Rust ($RUST_TOOLCHAIN)" $STD rustup install "$RUST_TOOLCHAIN" || { - msg_error "Failed to install Rust toolchain" + msg_error "Failed to install Rust toolchain $RUST_TOOLCHAIN" return 1 } $STD rustup default "$RUST_TOOLCHAIN" || { msg_error "Failed to set default Rust toolchain" return 1 } - $STD rustup update "$RUST_TOOLCHAIN" + $STD rustup update "$RUST_TOOLCHAIN" || true local RUST_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}') cache_installed_version "rust" "$RUST_VERSION" - msg_ok "Setup Rust $RUST_VERSION" + msg_ok "Update Rust $RUST_VERSION" fi + # Install global crates if [[ -n "$RUST_CRATES" ]]; then IFS=',' read -ra CRATES <<<"$RUST_CRATES" for crate in "${CRATES[@]}"; do @@ -3487,30 +3799,32 @@ function setup_uv() { ensure_dependencies jq local LATEST_VERSION - LATEST_VERSION=$(curl -fsSL https://api.github.com/repos/astral-sh/uv/releases/latest | - jq -r '.tag_name' | sed 's/^v//') - - if [[ -z "$LATEST_VERSION" ]]; then + LATEST_VERSION=$(curl -fsSL https://api.github.com/repos/astral-sh/uv/releases/latest 2>/dev/null | + jq -r '.tag_name' | sed 's/^v//') || { msg_error "Could not fetch latest uv version" rm -rf "$TMP_DIR" return 1 - fi + } + # Get currently installed version + local INSTALLED_VERSION="" if [[ -x "$UV_BIN" ]]; then - local INSTALLED_VERSION INSTALLED_VERSION=$($UV_BIN -V 2>/dev/null | awk '{print $2}') - if [[ "$INSTALLED_VERSION" == "$LATEST_VERSION" ]]; then - if [[ "$CACHED_VERSION" == "$LATEST_VERSION" ]]; then - rm -rf "$TMP_DIR" - return 0 - fi - cache_installed_version "uv" "$LATEST_VERSION" - rm -rf "$TMP_DIR" - return 0 - fi fi - msg_info "Setup uv $LATEST_VERSION" + # Scenario 1: Already at latest version + if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" == "$LATEST_VERSION" ]]; then + cache_installed_version "uv" "$LATEST_VERSION" + rm -rf "$TMP_DIR" + return 0 + fi + + # Scenario 2: New install or upgrade + if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" != "$LATEST_VERSION" ]]; then + msg_info "Upgrade uv from $INSTALLED_VERSION to $LATEST_VERSION" + else + msg_info "Setup uv $LATEST_VERSION" + fi local UV_URL="https://github.com/astral-sh/uv/releases/latest/download/${UV_TAR}" curl -fsSL "$UV_URL" -o "$TMP_DIR/uv.tar.gz" || { @@ -3539,9 +3853,10 @@ function setup_uv() { cache_installed_version "uv" "$LATEST_VERSION" msg_ok "Setup uv $LATEST_VERSION" + # Optional: Install specific Python version if [[ -n "${PYTHON_VERSION:-}" ]]; then local VERSION_MATCH - VERSION_MATCH=$(uv python list --only-downloads | + VERSION_MATCH=$(uv python list --only-downloads 2>/dev/null | grep -E "^cpython-${PYTHON_VERSION//./\\.}\.[0-9]+-linux" | cut -d'-' -f2 | sort -V | tail -n1) @@ -3550,7 +3865,7 @@ function setup_uv() { return 1 fi - if ! uv python list | grep -q "cpython-${VERSION_MATCH}-linux.*uv/python"; then + if ! uv python list 2>/dev/null | grep -q "cpython-${VERSION_MATCH}-linux.*uv/python"; then $STD uv python install "$VERSION_MATCH" || { msg_error "Failed to install Python $VERSION_MATCH" return 1 @@ -3572,42 +3887,45 @@ function setup_yq() { local TMP_DIR=$(mktemp -d) local BINARY_PATH="/usr/local/bin/yq" local GITHUB_REPO="mikefarah/yq" - local CURRENT_VERSION="" - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "yq") ensure_dependencies jq ensure_usr_local_bin_persist + # Remove non-mikefarah implementations if command -v yq &>/dev/null; then if ! yq --version 2>&1 | grep -q 'mikefarah'; then rm -f "$(command -v yq)" - else - CURRENT_VERSION=$(yq --version 2>/dev/null | awk '{print $NF}' | sed 's/^v//') fi fi local LATEST_VERSION - LATEST_VERSION=$(curl -fsSL "https://api.github.com/repos/${GITHUB_REPO}/releases/latest" | - jq -r '.tag_name' | sed 's/^v//') - - if [[ -z "$LATEST_VERSION" ]]; then + LATEST_VERSION=$(curl -fsSL "https://api.github.com/repos/${GITHUB_REPO}/releases/latest" 2>/dev/null | + jq -r '.tag_name' | sed 's/^v//') || { msg_error "Could not determine latest yq version" rm -rf "$TMP_DIR" return 1 + } + + # Get currently installed version + local INSTALLED_VERSION="" + if command -v yq &>/dev/null && yq --version 2>&1 | grep -q 'mikefarah'; then + INSTALLED_VERSION=$(yq --version 2>/dev/null | awk '{print $NF}' | sed 's/^v//') fi - if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$LATEST_VERSION" ]]; then - if [[ "$CACHED_VERSION" == "$LATEST_VERSION" ]]; then - rm -rf "$TMP_DIR" - return 0 - fi + # Scenario 1: Already at latest version + if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" == "$LATEST_VERSION" ]]; then cache_installed_version "yq" "$LATEST_VERSION" rm -rf "$TMP_DIR" return 0 fi - msg_info "Setup yq $LATEST_VERSION" + # Scenario 2: New install or upgrade + if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" != "$LATEST_VERSION" ]]; then + msg_info "Upgrade yq from $INSTALLED_VERSION to $LATEST_VERSION" + else + msg_info "Setup yq $LATEST_VERSION" + fi + curl -fsSL "https://github.com/${GITHUB_REPO}/releases/download/v${LATEST_VERSION}/yq_linux_amd64" -o "$TMP_DIR/yq" || { msg_error "Failed to download yq" rm -rf "$TMP_DIR" @@ -3615,13 +3933,11 @@ function setup_yq() { } chmod +x "$TMP_DIR/yq" - mv "$TMP_DIR/yq" "$BINARY_PATH" - - if [[ ! -x "$BINARY_PATH" ]]; then + mv "$TMP_DIR/yq" "$BINARY_PATH" || { msg_error "Failed to install yq" rm -rf "$TMP_DIR" return 1 - fi + } rm -rf "$TMP_DIR" hash -r From 19dca627b983623a6d34e8814e107ec521fbc8d0 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 24 Oct 2025 16:09:56 +0200 Subject: [PATCH 1534/1733] Improve network error handling and fallbacks in setup scripts Adds robust error handling and fallback logic for network operations in setup_ffmpeg, setup_gs, setup_hwaccel, setup_java, setup_mariadb, setup_ruby, setup_clickhouse, setup_uv, and setup_yq functions. Now uses timeouts, checks for empty responses, and provides alternative installation paths or informative warnings when API or mirror requests fail. This improves reliability in environments with intermittent or restricted network access. --- misc/tools.func | 294 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 220 insertions(+), 74 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index ab3e11308..23431f207 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -1924,16 +1924,22 @@ function setup_ffmpeg() { # Auto-detect latest stable version if none specified if [[ "$VERSION" == "latest" || -z "$VERSION" ]]; then - VERSION=$(curl -fsSL "https://api.github.com/repos/${GITHUB_REPO}/tags" 2>/dev/null | - jq -r '.[].name' | - grep -E '^n[0-9]+\.[0-9]+\.[0-9]+$' | - sort -V | tail -n1) + local ffmpeg_tags + ffmpeg_tags=$(curl -fsSL --max-time 15 "https://api.github.com/repos/${GITHUB_REPO}/tags" 2>/dev/null || echo "") + + if [[ -z "$ffmpeg_tags" ]]; then + msg_warn "Could not fetch FFmpeg versions from GitHub, trying binary fallback" + VERSION="" # Will trigger binary fallback below + else + VERSION=$(echo "$ffmpeg_tags" | jq -r '.[].name' 2>/dev/null | + grep -E '^n[0-9]+\.[0-9]+\.[0-9]+$' | + sort -V | tail -n1 || echo "") + fi fi if [[ -z "$VERSION" ]]; then - msg_error "Could not determine FFmpeg version" - rm -rf "$TMP_DIR" - return 1 + msg_info "Could not determine FFmpeg source version, using pre-built binary" + VERSION="" # Will use binary fallback fi # Dependency selection @@ -1962,14 +1968,43 @@ function setup_ffmpeg() { ensure_dependencies "${DEPS[@]}" - curl -fsSL "https://github.com/${GITHUB_REPO}/archive/refs/tags/${VERSION}.tar.gz" -o "$TMP_DIR/ffmpeg.tar.gz" || { - msg_error "Failed to download FFmpeg source" + # Try to download source if VERSION is set + if [[ -n "$VERSION" ]]; then + curl -fsSL "https://github.com/${GITHUB_REPO}/archive/refs/tags/${VERSION}.tar.gz" -o "$TMP_DIR/ffmpeg.tar.gz" || { + msg_warn "Failed to download FFmpeg source ${VERSION}, falling back to pre-built binary" + VERSION="" + } + fi + + # If no source download (either VERSION empty or download failed), use binary + if [[ -z "$VERSION" ]]; then + msg_info "Setup FFmpeg from pre-built binary" + curl -fsSL https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz -o "$TMP_DIR/ffmpeg.tar.xz" || { + msg_error "Failed to download FFmpeg pre-built binary" + rm -rf "$TMP_DIR" + return 1 + } + + tar -xJf "$TMP_DIR/ffmpeg.tar.xz" -C "$TMP_DIR" || { + msg_error "Failed to extract FFmpeg binary archive" + rm -rf "$TMP_DIR" + return 1 + } + + if ! cp "$TMP_DIR/ffmpeg-"*/ffmpeg /usr/local/bin/ffmpeg 2>/dev/null; then + msg_error "Failed to install FFmpeg binary" + rm -rf "$TMP_DIR" + return 1 + fi + + cache_installed_version "ffmpeg" "static" rm -rf "$TMP_DIR" - return 1 - } + msg_ok "Setup FFmpeg from pre-built binary" + return 0 + fi tar -xzf "$TMP_DIR/ffmpeg.tar.gz" -C "$TMP_DIR" || { - msg_error "Failed to extract FFmpeg" + msg_error "Failed to extract FFmpeg source" rm -rf "$TMP_DIR" return 1 } @@ -2141,20 +2176,38 @@ function setup_gs() { ensure_dependencies jq local RELEASE_JSON - RELEASE_JSON=$(curl -fsSL https://api.github.com/repos/ArtifexSoftware/ghostpdl-downloads/releases/latest 2>/dev/null) + RELEASE_JSON=$(curl -fsSL --max-time 15 https://api.github.com/repos/ArtifexSoftware/ghostpdl-downloads/releases/latest 2>/dev/null || echo "") + + if [[ -z "$RELEASE_JSON" ]]; then + msg_warn "Cannot fetch latest Ghostscript version from GitHub API" + # Try to get from current version + if command -v gs &>/dev/null; then + gs --version | head -n1 + cache_installed_version "ghostscript" "$CURRENT_VERSION" + return 0 + fi + msg_error "Cannot determine Ghostscript version and no existing installation found" + return 1 + fi local LATEST_VERSION LATEST_VERSION=$(echo "$RELEASE_JSON" | jq -r '.tag_name' | sed 's/^gs//') local LATEST_VERSION_DOTTED LATEST_VERSION_DOTTED=$(echo "$RELEASE_JSON" | jq -r '.name' | grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+') if [[ -z "$LATEST_VERSION" || -z "$LATEST_VERSION_DOTTED" ]]; then - msg_error "Could not determine latest Ghostscript version" + msg_warn "Could not determine latest Ghostscript version from GitHub - checking system" + # Fallback: try to use system version or return error + if [[ "$CURRENT_VERSION" == "0" ]]; then + msg_error "Ghostscript not installed and cannot determine latest version" + rm -rf "$TMP_DIR" + return 1 + fi rm -rf "$TMP_DIR" - return 1 + return 0 fi # Scenario 1: Already at latest version - if dpkg --compare-versions "$CURRENT_VERSION" ge "$LATEST_VERSION_DOTTED" 2>/dev/null; then + if [[ -n "$LATEST_VERSION_DOTTED" ]] && dpkg --compare-versions "$CURRENT_VERSION" ge "$LATEST_VERSION_DOTTED" 2>/dev/null; then cache_installed_version "ghostscript" "$LATEST_VERSION_DOTTED" rm -rf "$TMP_DIR" return 0 @@ -2179,6 +2232,13 @@ function setup_gs() { return 1 fi + # Verify directory exists before cd + if [[ ! -d "$TMP_DIR/ghostscript-${LATEST_VERSION_DOTTED}" ]]; then + msg_error "Ghostscript source directory not found: $TMP_DIR/ghostscript-${LATEST_VERSION_DOTTED}" + rm -rf "$TMP_DIR" + return 1 + fi + cd "$TMP_DIR/ghostscript-${LATEST_VERSION_DOTTED}" || { msg_error "Failed to enter Ghostscript source directory" rm -rf "$TMP_DIR" @@ -2243,21 +2303,26 @@ function setup_hwaccel() { # Detect GPU vendor (Intel, AMD, NVIDIA) local gpu_vendor - gpu_vendor=$(lspci | grep -Ei 'vga|3d|display' | grep -Eo 'Intel|AMD|NVIDIA' | head -n1) + gpu_vendor=$(lspci 2>/dev/null | grep -Ei 'vga|3d|display' | grep -Eo 'Intel|AMD|NVIDIA' | head -n1 || echo "") # Detect CPU vendor (relevant for AMD APUs) local cpu_vendor - cpu_vendor=$(lscpu | grep -i 'Vendor ID' | awk '{print $3}') + cpu_vendor=$(lscpu 2>/dev/null | grep -i 'Vendor ID' | awk '{print $3}' || echo "") if [[ -z "$gpu_vendor" && -z "$cpu_vendor" ]]; then msg_error "No GPU or CPU vendor detected (missing lspci/lscpu output)" return 1 fi - # Detect OS + # Detect OS with fallbacks local os_id os_codename - os_id=$(grep -oP '(?<=^ID=).+' /etc/os-release | tr -d '"') - os_codename=$(grep -oP '(?<=^VERSION_CODENAME=).+' /etc/os-release | tr -d '"') + os_id=$(grep -oP '(?<=^ID=).+' /etc/os-release 2>/dev/null | tr -d '"' || grep '^ID=' /etc/os-release 2>/dev/null | cut -d'=' -f2 | tr -d '"' || echo "debian") + os_codename=$(grep -oP '(?<=^VERSION_CODENAME=).+' /etc/os-release 2>/dev/null | tr -d '"' || grep '^VERSION_CODENAME=' /etc/os-release 2>/dev/null | cut -d'=' -f2 | tr -d '"' || echo "unknown") + + # Validate os_id + if [[ -z "$os_id" ]]; then + os_id="debian" + fi # Determine if we are on a VM or LXC local in_ct="${CTTYPE:-0}" @@ -2265,34 +2330,55 @@ function setup_hwaccel() { case "$gpu_vendor" in Intel) if [[ "$os_id" == "ubuntu" ]]; then - $STD apt -y install intel-opencl-icd || msg_error "Failed to install intel-opencl-icd" + $STD apt -y install intel-opencl-icd || { + msg_error "Failed to install intel-opencl-icd" + return 1 + } else - fetch_and_deploy_gh_release "intel-igc-core-2" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-core-2_*_amd64.deb" - fetch_and_deploy_gh_release "intel-igc-opencl-2" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-opencl-2_*_amd64.deb" - fetch_and_deploy_gh_release "intel-libgdgmm12" "intel/compute-runtime" "binary" "latest" "" "libigdgmm12_*_amd64.deb" - fetch_and_deploy_gh_release "intel-opencl-icd" "intel/compute-runtime" "binary" "latest" "" "intel-opencl-icd_*_amd64.deb" + # For Debian: fetch Intel GPU drivers from GitHub + fetch_and_deploy_gh_release "" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-core-2_*_amd64.deb" || { + msg_warn "Failed to deploy Intel IGC core 2" + } + fetch_and_deploy_gh_release "" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-opencl-2_*_amd64.deb" || { + msg_warn "Failed to deploy Intel IGC OpenCL 2" + } + fetch_and_deploy_gh_release "" "intel/compute-runtime" "binary" "latest" "" "libigdgmm12_*_amd64.deb" || { + msg_warn "Failed to deploy Intel GDGMM12" + } + fetch_and_deploy_gh_release "" "intel/compute-runtime" "binary" "latest" "" "intel-opencl-icd_*_amd64.deb" || { + msg_warn "Failed to deploy Intel OpenCL ICD" + } fi - $STD apt -y install va-driver-all ocl-icd-libopencl1 vainfo intel-gpu-tools || msg_error "Failed to install GPU dependencies" + $STD apt -y install va-driver-all ocl-icd-libopencl1 vainfo intel-gpu-tools || { + msg_error "Failed to install Intel GPU dependencies" + return 1 + } ;; AMD) - $STD apt -y install mesa-va-drivers mesa-vdpau-drivers mesa-opencl-icd vainfo clinfo || msg_error "Failed to install AMD GPU dependencies" + $STD apt -y install mesa-va-drivers mesa-vdpau-drivers mesa-opencl-icd vainfo clinfo || { + msg_error "Failed to install AMD GPU dependencies" + return 1 + } # For AMD CPUs without discrete GPU (APUs) - if [[ "$cpu_vendor" == "AuthenticAMD" && "$gpu_vendor" != "AMD" ]]; then + if [[ "$cpu_vendor" == "AuthenticAMD" && -n "$gpu_vendor" ]]; then $STD apt -y install libdrm-amdgpu1 firmware-amd-graphics || true fi ;; NVIDIA) - # NVIDIA needs manual driver setup + # NVIDIA needs manual driver setup - skip for now + msg_info "NVIDIA GPU detected - manual driver setup required" ;; *) # If no discrete GPU, but AMD CPU (e.g., Ryzen APU) if [[ "$cpu_vendor" == "AuthenticAMD" ]]; then - $STD apt -y install mesa-opencl-icd ocl-icd-libopencl1 clinfo || msg_error "Failed to install Mesa OpenCL stack" + $STD apt -y install mesa-opencl-icd ocl-icd-libopencl1 clinfo || { + msg_error "Failed to install Mesa OpenCL stack" + return 1 + } else - msg_error "No supported GPU vendor detected" - return 1 + msg_warn "No supported GPU vendor detected - skipping GPU acceleration" fi ;; esac @@ -2440,15 +2526,27 @@ function setup_java() { # Get currently installed version local INSTALLED_VERSION="" - if dpkg -l | grep -q "temurin-.*-jdk"; then - INSTALLED_VERSION=$(dpkg -l | awk '/temurin-.*-jdk/{print $2}' | grep -oP 'temurin-\K[0-9]+') + if dpkg -l | grep -q "temurin-.*-jdk" 2>/dev/null; then + INSTALLED_VERSION=$(dpkg -l 2>/dev/null | awk '/temurin-.*-jdk/{print $2}' | grep -oP 'temurin-\K[0-9]+' | head -n1 || echo "") + fi + + # Validate INSTALLED_VERSION is not empty if matched + if [[ -z "$INSTALLED_VERSION" && $(dpkg -l 2>/dev/null | grep -c "temurin-.*-jdk" || echo "0") -gt 0 ]]; then + msg_warn "Found Temurin JDK but cannot determine version" + INSTALLED_VERSION="0" fi # Scenario 1: Already at correct version if [[ "$INSTALLED_VERSION" == "$JAVA_VERSION" ]]; then msg_info "Update Temurin JDK $JAVA_VERSION" - $STD apt update - $STD apt install --only-upgrade -y "$DESIRED_PACKAGE" + $STD apt update || { + msg_error "APT update failed" + return 1 + } + $STD apt install --only-upgrade -y "$DESIRED_PACKAGE" || { + msg_error "Failed to update Temurin JDK" + return 1 + } cache_installed_version "temurin-jdk" "$JAVA_VERSION" msg_ok "Update Temurin JDK $JAVA_VERSION" return 0 @@ -2462,7 +2560,10 @@ function setup_java() { msg_info "Setup Temurin JDK $JAVA_VERSION" fi - $STD apt update + $STD apt update || { + msg_error "APT update failed" + return 1 + } $STD apt install -y "$DESIRED_PACKAGE" || { msg_error "Failed to install Temurin JDK $JAVA_VERSION" return 1 @@ -2585,23 +2686,23 @@ setup_mariadb() { # Resolve "latest" to actual version if [[ "$MARIADB_VERSION" == "latest" ]]; then - if ! curl -fsI http://mirror.mariadb.org/repo/ >/dev/null 2>&1; then - msg_error "MariaDB mirror not reachable" - return 1 + if ! curl -fsI --max-time 10 http://mirror.mariadb.org/repo/ >/dev/null 2>&1; then + msg_warn "MariaDB mirror not reachable - trying cached package list fallback" + # Fallback: try to use a known stable version + MARIADB_VERSION="12.0" + else + MARIADB_VERSION=$(curl -fsSL --max-time 15 http://mirror.mariadb.org/repo/ 2>/dev/null | + grep -Eo '[0-9]+\.[0-9]+\.[0-9]+/' | + grep -vE 'rc/|rolling/' | + sed 's|/||' | + sort -Vr | + head -n1 || echo "") + + if [[ -z "$MARIADB_VERSION" ]]; then + msg_warn "Could not parse latest GA MariaDB version from mirror - using fallback" + MARIADB_VERSION="12.0" + fi fi - MARIADB_VERSION=$(curl -fsSL http://mirror.mariadb.org/repo/ 2>/dev/null | - grep -Eo '[0-9]+\.[0-9]+\.[0-9]+/' | - grep -vE 'rc/|rolling/' | - sed 's|/||' | - sort -Vr | - head -n1) || { - msg_error "Could not determine latest GA MariaDB version" - return 1 - } - [[ -z "$MARIADB_VERSION" ]] && { - msg_error "Latest MariaDB version is empty" - return 1 - } fi # Get currently installed version @@ -3480,11 +3581,22 @@ function setup_ruby() { # Download and build rbenv if needed if [[ ! -x "$RBENV_BIN" ]]; then local RBENV_RELEASE - RBENV_RELEASE=$(curl -fsSL https://api.github.com/repos/rbenv/rbenv/releases/latest 2>/dev/null | jq -r '.tag_name' | sed 's/^v//') || { - msg_error "Failed to fetch latest rbenv version" + local rbenv_json + rbenv_json=$(curl -fsSL --max-time 15 https://api.github.com/repos/rbenv/rbenv/releases/latest 2>/dev/null || echo "") + + if [[ -z "$rbenv_json" ]]; then + msg_error "Failed to fetch latest rbenv version from GitHub" rm -rf "$TMP_DIR" return 1 - } + fi + + RBENV_RELEASE=$(echo "$rbenv_json" | jq -r '.tag_name' 2>/dev/null | sed 's/^v//' || echo "") + + if [[ -z "$RBENV_RELEASE" ]]; then + msg_error "Could not parse rbenv version from GitHub response" + rm -rf "$TMP_DIR" + return 1 + fi curl -fsSL "https://github.com/rbenv/rbenv/archive/refs/tags/v${RBENV_RELEASE}.tar.gz" -o "$TMP_DIR/rbenv.tar.gz" || { msg_error "Failed to download rbenv" @@ -3516,11 +3628,22 @@ function setup_ruby() { # Install ruby-build plugin if [[ ! -d "$RBENV_DIR/plugins/ruby-build" ]]; then local RUBY_BUILD_RELEASE - RUBY_BUILD_RELEASE=$(curl -fsSL https://api.github.com/repos/rbenv/ruby-build/releases/latest 2>/dev/null | jq -r '.tag_name' | sed 's/^v//') || { - msg_error "Failed to fetch latest ruby-build version" + local ruby_build_json + ruby_build_json=$(curl -fsSL --max-time 15 https://api.github.com/repos/rbenv/ruby-build/releases/latest 2>/dev/null || echo "") + + if [[ -z "$ruby_build_json" ]]; then + msg_error "Failed to fetch latest ruby-build version from GitHub" rm -rf "$TMP_DIR" return 1 - } + fi + + RUBY_BUILD_RELEASE=$(echo "$ruby_build_json" | jq -r '.tag_name' 2>/dev/null | sed 's/^v//' || echo "") + + if [[ -z "$RUBY_BUILD_RELEASE" ]]; then + msg_error "Could not parse ruby-build version from GitHub response" + rm -rf "$TMP_DIR" + return 1 + fi curl -fsSL "https://github.com/rbenv/ruby-build/archive/refs/tags/v${RUBY_BUILD_RELEASE}.tar.gz" -o "$TMP_DIR/ruby-build.tar.gz" || { msg_error "Failed to download ruby-build" @@ -3591,15 +3714,18 @@ function setup_clickhouse() { # Resolve "latest" version if [[ "$CLICKHOUSE_VERSION" == "latest" ]]; then - CLICKHOUSE_VERSION=$(curl -fsSL https://packages.clickhouse.com/tgz/stable/ 2>/dev/null | + CLICKHOUSE_VERSION=$(curl -fsSL --max-time 15 https://packages.clickhouse.com/tgz/stable/ 2>/dev/null | grep -oP 'clickhouse-common-static-\K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | - sort -V | tail -n1) || { - CLICKHOUSE_VERSION=$(curl -fsSL https://api.github.com/repos/ClickHouse/ClickHouse/releases/latest 2>/dev/null | - grep -oP '"tag_name":\s*"v\K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -n1) - } + sort -V | tail -n1 || echo "") + + # Fallback to GitHub API if package server failed + if [[ -z "$CLICKHOUSE_VERSION" ]]; then + CLICKHOUSE_VERSION=$(curl -fsSL --max-time 15 https://api.github.com/repos/ClickHouse/ClickHouse/releases/latest 2>/dev/null | + grep -oP '"tag_name":\s*"v\K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -n1 || echo "") + fi [[ -z "$CLICKHOUSE_VERSION" ]] && { - msg_error "Could not determine latest ClickHouse version" + msg_error "Could not determine latest ClickHouse version from any source" return 1 } fi @@ -3799,12 +3925,22 @@ function setup_uv() { ensure_dependencies jq local LATEST_VERSION - LATEST_VERSION=$(curl -fsSL https://api.github.com/repos/astral-sh/uv/releases/latest 2>/dev/null | - jq -r '.tag_name' | sed 's/^v//') || { - msg_error "Could not fetch latest uv version" + local releases_json + releases_json=$(curl -fsSL --max-time 15 https://api.github.com/repos/astral-sh/uv/releases/latest 2>/dev/null || echo "") + + if [[ -z "$releases_json" ]]; then + msg_error "Could not fetch latest uv version from GitHub API" rm -rf "$TMP_DIR" return 1 - } + fi + + LATEST_VERSION=$(echo "$releases_json" | jq -r '.tag_name' 2>/dev/null | sed 's/^v//' || echo "") + + if [[ -z "$LATEST_VERSION" ]]; then + msg_error "Could not parse uv version from GitHub API response" + rm -rf "$TMP_DIR" + return 1 + fi # Get currently installed version local INSTALLED_VERSION="" @@ -3899,12 +4035,22 @@ function setup_yq() { fi local LATEST_VERSION - LATEST_VERSION=$(curl -fsSL "https://api.github.com/repos/${GITHUB_REPO}/releases/latest" 2>/dev/null | - jq -r '.tag_name' | sed 's/^v//') || { - msg_error "Could not determine latest yq version" + local releases_json + releases_json=$(curl -fsSL --max-time 15 "https://api.github.com/repos/${GITHUB_REPO}/releases/latest" 2>/dev/null || echo "") + + if [[ -z "$releases_json" ]]; then + msg_error "Could not fetch latest yq version from GitHub API" rm -rf "$TMP_DIR" return 1 - } + fi + + LATEST_VERSION=$(echo "$releases_json" | jq -r '.tag_name' 2>/dev/null | sed 's/^v//' || echo "") + + if [[ -z "$LATEST_VERSION" ]]; then + msg_error "Could not parse yq version from GitHub API response" + rm -rf "$TMP_DIR" + return 1 + fi # Get currently installed version local INSTALLED_VERSION="" From ed9efffcb5ecc915a346557aac339729d740bedd Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 24 Oct 2025 16:12:23 +0200 Subject: [PATCH 1535/1733] Update tools.func --- misc/tools.func | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 23431f207..2c8be18b8 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -379,11 +379,16 @@ upgrade_package() { fi if ((current_time - last_update > 300)); then - $STD apt update + $STD apt update || { + msg_warn "APT update failed in upgrade_package - continuing with cached packages" + } echo "$current_time" >"$apt_cache_file" fi - $STD apt install --only-upgrade -y "$package" + $STD apt install --only-upgrade -y "$package" || { + msg_warn "Failed to upgrade $package" + return 1 + } } # ------------------------------------------------------------------------------ @@ -919,13 +924,13 @@ ensure_apt_working() { cleanup_orphaned_sources # Try to update package lists - if ! apt-get update -qq 2>/dev/null; then + if ! apt update -qq 2>/dev/null; then # More aggressive cleanup rm -f /etc/apt/sources.list.d/*.sources 2>/dev/null || true cleanup_orphaned_sources # Try again - if ! apt-get update -qq 2>/dev/null; then + if ! apt update -qq 2>/dev/null; then msg_error "Cannot update package lists - APT is critically broken" return 1 fi @@ -2663,7 +2668,9 @@ $SCRIPT_PATH EOF chmod +x "$DISPATCHER_SCRIPT" - systemctl enable -q --now networkd-dispatcher.service + systemctl enable -q --now networkd-dispatcher.service || { + msg_warn "Failed to enable networkd-dispatcher service" + } cache_installed_version "local-ip-helper" "1.0" msg_ok "Setup Local IP Helper" @@ -2787,7 +2794,9 @@ setup_mariadb() { # Fallback: try without specific version msg_warn "Failed to install MariaDB packages from upstream repo, trying distro fallback..." cleanup_old_repo_files "mariadb" - $STD apt update + $STD apt update || { + msg_warn "APT update also failed, continuing with cache" + } DEBIAN_FRONTEND=noninteractive $STD apt install -y mariadb-server mariadb-client || { msg_error "Failed to install MariaDB packages (both upstream and distro)" return 1 @@ -2889,7 +2898,9 @@ function setup_mongodb() { mkdir -p /var/lib/mongodb chown -R mongodb:mongodb /var/lib/mongodb - $STD systemctl enable mongod + $STD systemctl enable mongod || { + msg_warn "Failed to enable mongod service" + } safe_service_restart mongod cache_installed_version "mongodb" "$MONGO_VERSION" @@ -3290,7 +3301,7 @@ function setup_php() { # install apache2 with PHP support if requested if [[ "$PHP_APACHE" == "YES" ]]; then - if ! dpkg -l | grep -q "libapache2-mod-php${PHP_VERSION}"; then + if ! dpkg -l 2>/dev/null | grep -q "libapache2-mod-php${PHP_VERSION}"; then $STD apt install -y apache2 libapache2-mod-php${PHP_VERSION} || { msg_error "Failed to install Apache with PHP module" return 1 @@ -3791,7 +3802,9 @@ function setup_clickhouse() { fi # Enable and start service - $STD systemctl enable clickhouse-server + $STD systemctl enable clickhouse-server || { + msg_warn "Failed to enable clickhouse-server service" + } safe_service_restart clickhouse-server || true cache_installed_version "clickhouse" "$CLICKHOUSE_VERSION" From 7b8d6e72226aff2c6d43416d039e63ef2512ca65 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Fri, 24 Oct 2025 16:19:40 +0200 Subject: [PATCH 1536/1733] Create bookstack.sh --- ct/bookstack.sh | 81 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 ct/bookstack.sh diff --git a/ct/bookstack.sh b/ct/bookstack.sh new file mode 100644 index 000000000..2034adbbc --- /dev/null +++ b/ct/bookstack.sh @@ -0,0 +1,81 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: MickLesk (Canbiz) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/BookStackApp/BookStack + +APP="Bookstack" +var_tags="${var_tags:-organizer}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-1024}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + if [[ ! -d /opt/bookstack ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + if check_for_gh_release "bookstack" "BookStackApp/BookStack"; then + msg_info "Stopping Apache2" + systemctl stop apache2 + msg_ok "Services Stopped" + + msg_info "Backing up data" + mv /opt/bookstack /opt/bookstack-backup + msg_ok "Backup finished" + + setup_mariadb + fetch_and_deploy_gh_release "bookstack" "BookStackApp/BookStack" + PHP_MODULE="ldap,tidy,bz2,mysqli" PHP_FPM="YES" PHP_APACHE="YES" PHP_VERSION="8.3" setup_php + setup_composer + + msg_info "Restoring backup" + cp /opt/bookstack-backup/.env /opt/bookstack/.env + [[ -d /opt/bookstack-backup/public/uploads ]] && cp -a /opt/bookstack-backup/public/uploads/. /opt/bookstack/public/uploads/ + [[ -d /opt/bookstack-backup/storage/uploads ]] && cp -a /opt/bookstack-backup/storage/uploads/. /opt/bookstack/storage/uploads/ + [[ -d /opt/bookstack-backup/themes ]] && cp -a /opt/bookstack-backup/themes/. /opt/bookstack/themes/ + msg_ok "Backup restored" + + msg_info "Configuring BookStack" + cd /opt/bookstack + export COMPOSER_ALLOW_SUPERUSER=1 + $STD composer install --no-dev + $STD php artisan migrate --force + chown www-data:www-data -R /opt/bookstack /opt/bookstack/bootstrap/cache /opt/bookstack/public/uploads /opt/bookstack/storage + chmod -R 755 /opt/bookstack /opt/bookstack/bootstrap/cache /opt/bookstack/public/uploads /opt/bookstack/storage + chmod -R 775 /opt/bookstack/storage /opt/bookstack/bootstrap/cache /opt/bookstack/public/uploads + chmod -R 640 /opt/bookstack/.env + msg_ok "Configured BookStack" + + msg_info "Starting Apache2" + systemctl start apache2 + msg_ok "Started Apache2" + + msg_info "Cleaning Up" + rm -rf /opt/bookstack-backup + msg_ok "Cleaned" + msg_ok "Updated Successfully" + fi + exit +} +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" From a0e76feae227a6e3237b992d900d19cc8cb653e1 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Fri, 24 Oct 2025 10:25:35 -0400 Subject: [PATCH 1537/1733] fix error in tools.func --- misc/tools.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/tools.func b/misc/tools.func index 2c8be18b8..51e00b39e 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -1856,7 +1856,7 @@ function setup_composer() { if [[ ! -x "$COMPOSER_BIN" ]]; then msg_error "Composer installation failed" return 1 - } + fi chmod +x "$COMPOSER_BIN" $STD "$COMPOSER_BIN" self-update --no-interaction || true From e11254b185f506aca40cb05360e113cca6620064 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sat, 25 Oct 2025 12:53:46 -0400 Subject: [PATCH 1538/1733] PatchMon: use NodeJS 24; remove some msgs --- ct/patchmon.sh | 2 ++ install/patchmon-install.sh | 12 ++++-------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/ct/patchmon.sh b/ct/patchmon.sh index 284400bc6..8671ddcf5 100644 --- a/ct/patchmon.sh +++ b/ct/patchmon.sh @@ -30,6 +30,8 @@ function update_script() { exit fi + NODE_VERSION="24" setup_nodejs + if check_for_gh_release "PatchMon" "PatchMon/PatchMon"; then msg_info "Stopping $APP" diff --git a/install/patchmon-install.sh b/install/patchmon-install.sh index 250297759..209b03e1c 100644 --- a/install/patchmon-install.sh +++ b/install/patchmon-install.sh @@ -21,7 +21,7 @@ $STD apt install -y \ redis-server msg_ok "Installed Dependencies" -NODE_VERSION="22" setup_nodejs +NODE_VERSION="24" setup_nodejs PG_VERSION="17" setup_postgresql msg_info "Creating PostgreSQL Database" @@ -52,9 +52,7 @@ $STD npm install --no-audit --no-fund --no-save --ignore-scripts cd /opt/patchmon/frontend $STD npm install --include=dev --no-audit --no-fund --no-save --ignore-scripts $STD npm run build -msg_ok "Configured PatchMon" -msg_info "Creating env files" JWT_SECRET="$(openssl rand -base64 64 | tr -d "=+/" | cut -c1-50)" LOCAL_IP="$(hostname -I | awk '{print $1}')" cat </opt/patchmon/backend/.env @@ -111,13 +109,11 @@ VITE_API_URL=http://$LOCAL_IP/api/v1 VITE_APP_NAME=PatchMon VITE_APP_VERSION=1.3.0 EOF -msg_ok "Created env files" -msg_info "Running database migrations" cd /opt/patchmon/backend $STD npx prisma migrate deploy $STD npx prisma generate -msg_ok "Database migrations complete" +msg_ok "Configured PatchMon" msg_info "Configuring Nginx" cat </etc/nginx/sites-available/patchmon.conf @@ -231,7 +227,7 @@ EOF systemctl enable -q --now patchmon-server msg_ok "Created and started service" -msg_info "Populating server settings in DB" +msg_info "Updating settings" cat </opt/patchmon/backend/update-settings.js const { PrismaClient } = require('@prisma/client'); const { v4: uuidv4 } = require('uuid'); @@ -281,7 +277,7 @@ EOF cd /opt/patchmon/backend $STD node update-settings.js -msg_ok "Server settings populated successfully" +msg_ok "Settings updated successfully" motd_ssh customize From c96f4e9696ac7fbe3ce21b70d6a8fa78f7f3854b Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sat, 25 Oct 2025 13:28:53 -0400 Subject: [PATCH 1539/1733] tools.func: NodeJS install also requires gpg_key_url --- misc/tools.func | 54 ++++++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 51e00b39e..b0810e8d0 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -88,16 +88,16 @@ is_tool_installed() { esac if [[ -z "$installed_version" ]]; then - return 1 # Not installed + return 1 # Not installed fi if [[ -n "$required_version" && "$installed_version" != "$required_version" ]]; then echo "$installed_version" - return 1 # Version mismatch + return 1 # Version mismatch fi echo "$installed_version" - return 0 # Installed and version matches (if specified) + return 0 # Installed and version matches (if specified) } # ------------------------------------------------------------------------------ @@ -177,14 +177,14 @@ should_update_tool() { local current_version="" # Get currently installed version - current_version=$(is_tool_installed "$tool_name" 2>/dev/null) || return 0 # Not installed = needs install + current_version=$(is_tool_installed "$tool_name" 2>/dev/null) || return 0 # Not installed = needs install # If versions are identical, no update needed if [[ "$current_version" == "$target_version" ]]; then - return 1 # No update needed + return 1 # No update needed fi - return 0 # Update needed + return 0 # Update needed } # ---------------------–---------------------------------------------------------- @@ -258,8 +258,8 @@ EOF ;; nodejs) - if [[ -z "$repo_url" ]]; then - msg_error "Node.js repository requires repo_url" + if [[ -z "$repo_url" || -z "$gpg_key_url" ]]; then + msg_error "Node.js repository requires repo_url and gpg_key_url" return 1 fi @@ -273,7 +273,7 @@ EOF mkdir -p /etc/apt/keyrings # Download GPG key from NodeSource - curl -fsSL "$repo_url/gpg.key" | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg || { + curl -fsSL "$gpg_key_url" | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg || { msg_error "Failed to import NodeSource GPG key" return 1 } @@ -1495,7 +1495,7 @@ function fetch_and_deploy_gh_release() { rm -rf "${target:?}/"* fi - tar --no-same-owner -xzf "$tmpdir/$filename" -C "$tmpdir" || { + tar --no-same-owner -xzf "$tmpdir/$filename" -C "$tmpdir" || { msg_error "Failed to extract tarball" rm -rf "$tmpdir" return 1 @@ -1934,7 +1934,7 @@ function setup_ffmpeg() { if [[ -z "$ffmpeg_tags" ]]; then msg_warn "Could not fetch FFmpeg versions from GitHub, trying binary fallback" - VERSION="" # Will trigger binary fallback below + VERSION="" # Will trigger binary fallback below else VERSION=$(echo "$ffmpeg_tags" | jq -r '.[].name' 2>/dev/null | grep -E '^n[0-9]+\.[0-9]+\.[0-9]+$' | @@ -1944,7 +1944,7 @@ function setup_ffmpeg() { if [[ -z "$VERSION" ]]; then msg_info "Could not determine FFmpeg source version, using pre-built binary" - VERSION="" # Will use binary fallback + VERSION="" # Will use binary fallback fi # Dependency selection @@ -3017,14 +3017,14 @@ EOF export DEBIAN_FRONTEND=noninteractive local mysql_install_success=false - if apt-cache search "^mysql-server$" 2>/dev/null | grep -q . && \ - $STD apt install -y mysql-server mysql-client 2>/dev/null; then + if apt-cache search "^mysql-server$" 2>/dev/null | grep -q . && + $STD apt install -y mysql-server mysql-client 2>/dev/null; then mysql_install_success=true - elif apt-cache search "^mysql-community-server$" 2>/dev/null | grep -q . && \ - $STD apt install -y mysql-community-server mysql-community-client 2>/dev/null; then + elif apt-cache search "^mysql-community-server$" 2>/dev/null | grep -q . && + $STD apt install -y mysql-community-server mysql-community-client 2>/dev/null; then mysql_install_success=true - elif apt-cache search "^mysql$" 2>/dev/null | grep -q . && \ - $STD apt install -y mysql 2>/dev/null; then + elif apt-cache search "^mysql$" 2>/dev/null | grep -q . && + $STD apt install -y mysql 2>/dev/null; then mysql_install_success=true fi @@ -3098,7 +3098,7 @@ function setup_nodejs() { ensure_dependencies curl ca-certificates gnupg # Setup repository - manage_tool_repository "nodejs" "$NODE_VERSION" "https://deb.nodesource.com/node_${NODE_VERSION}.x" "" || { + manage_tool_repository "nodejs" "$NODE_VERSION" "https://deb.nodesource.com/node_${NODE_VERSION}.x" "https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key" || { msg_error "Failed to setup Node.js repository" return 1 } @@ -3448,20 +3448,20 @@ function setup_postgresql() { # Try multiple PostgreSQL package patterns local pg_install_success=false - if apt-cache search "^postgresql-${PG_VERSION}$" 2>/dev/null | grep -q . && \ - $STD apt install -y "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}" 2>/dev/null; then + if apt-cache search "^postgresql-${PG_VERSION}$" 2>/dev/null | grep -q . && + $STD apt install -y "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}" 2>/dev/null; then pg_install_success=true fi - if [[ "$pg_install_success" == false ]] && \ - apt-cache search "^postgresql-server-${PG_VERSION}$" 2>/dev/null | grep -q . && \ - $STD apt install -y "postgresql-server-${PG_VERSION}" "postgresql-client-${PG_VERSION}" 2>/dev/null; then + if [[ "$pg_install_success" == false ]] && + apt-cache search "^postgresql-server-${PG_VERSION}$" 2>/dev/null | grep -q . && + $STD apt install -y "postgresql-server-${PG_VERSION}" "postgresql-client-${PG_VERSION}" 2>/dev/null; then pg_install_success=true fi - if [[ "$pg_install_success" == false ]] && \ - apt-cache search "^postgresql$" 2>/dev/null | grep -q . && \ - $STD apt install -y postgresql postgresql-client 2>/dev/null; then + if [[ "$pg_install_success" == false ]] && + apt-cache search "^postgresql$" 2>/dev/null | grep -q . && + $STD apt install -y postgresql postgresql-client 2>/dev/null; then pg_install_success=true fi From 63a76ca4bbdbab084e52b52d300720c2a3242410 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sat, 25 Oct 2025 13:38:17 -0400 Subject: [PATCH 1540/1733] tools.func: NodeJS 'Suite' should be 'nodistro' --- misc/tools.func | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/tools.func b/misc/tools.func index b0810e8d0..1914bc894 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -281,7 +281,7 @@ EOF cat </etc/apt/sources.list.d/nodesource.sources Types: deb URIs: $repo_url -Suites: $distro_codename +Suites: nodistro Components: main Architectures: amd64 arm64 Signed-By: /etc/apt/keyrings/nodesource.gpg From 6896fcb664f3db6cddbf3bd691c5ae2c89809d02 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sat, 25 Oct 2025 14:11:08 -0400 Subject: [PATCH 1541/1733] PatchMon: restore env files before DB operations --- ct/patchmon.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/patchmon.sh b/ct/patchmon.sh index 8671ddcf5..97677309c 100644 --- a/ct/patchmon.sh +++ b/ct/patchmon.sh @@ -56,9 +56,9 @@ function update_script() { $STD npm install --include=dev --no-audit --no-fund --no-save --ignore-scripts $STD npm run build cd /opt/patchmon/backend - $STD npx prisma migrate deploy mv /opt/backend.env /opt/patchmon/backend/.env mv /opt/frontend.env /opt/patchmon/frontend/.env + $STD npx prisma migrate deploy msg_ok "Updated ${APP}" msg_info "Starting $APP" From c0f5fa10253d5779aa40285301ece07ad87b2669 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sat, 25 Oct 2025 14:15:16 -0400 Subject: [PATCH 1542/1733] PatchMon: generate prisma client post-update --- ct/patchmon.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ct/patchmon.sh b/ct/patchmon.sh index 97677309c..0080df89c 100644 --- a/ct/patchmon.sh +++ b/ct/patchmon.sh @@ -59,6 +59,7 @@ function update_script() { mv /opt/backend.env /opt/patchmon/backend/.env mv /opt/frontend.env /opt/patchmon/frontend/.env $STD npx prisma migrate deploy + $STD npx prisma generate msg_ok "Updated ${APP}" msg_info "Starting $APP" From 161f77ddf1806bee5b05bcb9ebe90946f86afc9e Mon Sep 17 00:00:00 2001 From: vhsdream Date: Sat, 25 Oct 2025 14:20:01 -0400 Subject: [PATCH 1543/1733] PatchMon: update json --- frontend/public/json/patchmon.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/patchmon.json b/frontend/public/json/patchmon.json index 039a359a6..9b78f66f6 100644 --- a/frontend/public/json/patchmon.json +++ b/frontend/public/json/patchmon.json @@ -12,7 +12,7 @@ "documentation": "https://docs.patchmon.net", "website": "https://patchmon.net", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/patchmon.webp", - "config_path": "", + "config_path": "/opt/patchmon/backend/.env, /opt/patchmon/frontend/.env", "description": "Monitor Linux patches across all your hosts with real-time visibility, security update tracking, and comprehensive package management.", "install_methods": [ { From a0b4e0b5b86c9e02ade552c9347e87e29e7ffc30 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sun, 26 Oct 2025 09:05:03 +0100 Subject: [PATCH 1544/1733] Mylar3 testing --- ct/mylar3.sh | 43 ++++++++++++++++++++++++++++ install/mylar3-install.sh | 60 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 ct/mylar3.sh create mode 100644 install/mylar3-install.sh diff --git a/ct/mylar3.sh b/ct/mylar3.sh new file mode 100644 index 000000000..2bb791386 --- /dev/null +++ b/ct/mylar3.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: davalanche +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/mylar3/mylar3 + +APP="Mylar3" +var_tags="${var_tags:-torrent;downloader;comic}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-512}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + if [[ ! -d /opt/mylar3 ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "mylar3" "mylar3/mylar3"; then + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "mylar3" "mylar3/mylar3" "tarball" + systemctl restart mylar3 + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8090${CL}" diff --git a/install/mylar3-install.sh b/install/mylar3-install.sh new file mode 100644 index 000000000..bdce72d09 --- /dev/null +++ b/install/mylar3-install.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: davalanche +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/mylar3/mylar3 + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt install -y unrar +msg_ok "Installed Dependencies" + +msg_info "Setup Python3" +$STD apt install -y python3-pip +msg_ok "Setup Python3" + +setup_uv +fetch_and_deploy_gh_release "mylar3" "mylar3/mylar3" "tarball" + +msg_info "Installing ${APPLICATION}" +mkdir -p /opt/mylar3-data +$STD uv venv /opt/mylar3/.venv +$STD /opt/mylar3/.venv/bin/python -m ensurepip --upgrade +$STD /opt/mylar3/.venv/bin/python -m pip install --upgrade pip +$STD /opt/mylar3/.venv/bin/python -m pip install --no-cache-dir -r /opt/mylar3/requirements.txt +msg_ok "Installed ${APPLICATION}" + +msg_info "Creating Service" +cat </etc/systemd/system/mylar3.service +[Unit] +Description=Mylar3 Service +After=network-online.target + +[Service] +ExecStart=/opt/mylar3/.venv/bin/python /opt/mylar3/Mylar.py --daemon --nolaunch --datadir=/opt/mylar3-data +GuessMainPID=no +Type=forking +Restart=on-failure + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now mylar3 +msg_ok "Created Service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt -y autoremove +$STD apt -y autoclean +$STD apt -y clean +msg_ok "Cleaned" From 3098a1ac11a44994987f61e7a912bab05574f4b0 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sun, 26 Oct 2025 08:05:39 +0000 Subject: [PATCH 1545/1733] Update .app files --- ct/headers/bookstack | 6 ++++++ ct/headers/mylar3 | 6 ++++++ 2 files changed, 12 insertions(+) create mode 100644 ct/headers/bookstack create mode 100644 ct/headers/mylar3 diff --git a/ct/headers/bookstack b/ct/headers/bookstack new file mode 100644 index 000000000..f68646662 --- /dev/null +++ b/ct/headers/bookstack @@ -0,0 +1,6 @@ + ____ __ __ __ + / __ )____ ____ / /_______/ /_____ ______/ /__ + / __ / __ \/ __ \/ //_/ ___/ __/ __ `/ ___/ //_/ + / /_/ / /_/ / /_/ / ,< (__ ) /_/ /_/ / /__/ ,< +/_____/\____/\____/_/|_/____/\__/\__,_/\___/_/|_| + diff --git a/ct/headers/mylar3 b/ct/headers/mylar3 new file mode 100644 index 000000000..cd3f10dcc --- /dev/null +++ b/ct/headers/mylar3 @@ -0,0 +1,6 @@ + __ ___ __ _____ + / |/ /_ __/ /___ _____|__ / + / /|_/ / / / / / __ `/ ___//_ < + / / / / /_/ / / /_/ / / ___/ / +/_/ /_/\__, /_/\__,_/_/ /____/ + /____/ From e6a13d86d4cdb1446f81049f1c1f9cb53b7721da Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sun, 26 Oct 2025 09:18:23 +0100 Subject: [PATCH 1546/1733] VED --- ct/mylar3.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/mylar3.sh b/ct/mylar3.sh index 2bb791386..53441e8c2 100644 --- a/ct/mylar3.sh +++ b/ct/mylar3.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: davalanche # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE From 0e9e071bd6d9969d05307da15c970c2bd9bf38ad Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sun, 26 Oct 2025 09:24:05 +0100 Subject: [PATCH 1547/1733] Add non-free --- install/mylar3-install.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/install/mylar3-install.sh b/install/mylar3-install.sh index bdce72d09..8423e9337 100644 --- a/install/mylar3-install.sh +++ b/install/mylar3-install.sh @@ -14,6 +14,13 @@ network_check update_os msg_info "Installing Dependencies" +cat </etc/apt/sources.list.d/non-free.sources +Types: deb +URIs: http://deb.debian.org/debian +Suites: bookworm +Components: non-free non-free-firmware +EOF +$STD apt update $STD apt install -y unrar msg_ok "Installed Dependencies" From b58e2668f7bfe4817aede5bd44a83222d870e418 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sun, 26 Oct 2025 09:34:56 +0100 Subject: [PATCH 1548/1733] Update python to 3.12 --- install/mylar3-install.sh | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/install/mylar3-install.sh b/install/mylar3-install.sh index 8423e9337..750a709e7 100644 --- a/install/mylar3-install.sh +++ b/install/mylar3-install.sh @@ -24,11 +24,7 @@ $STD apt update $STD apt install -y unrar msg_ok "Installed Dependencies" -msg_info "Setup Python3" -$STD apt install -y python3-pip -msg_ok "Setup Python3" - -setup_uv +PYTHON_VERSION="3.12" setup_uv fetch_and_deploy_gh_release "mylar3" "mylar3/mylar3" "tarball" msg_info "Installing ${APPLICATION}" From cd8324c07cc381f6a264e59d0256c2f31e6d2ee7 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sun, 26 Oct 2025 09:47:14 +0100 Subject: [PATCH 1549/1733] Delete mylar3 --- ct/headers/mylar3 | 6 ------ ct/mylar3.sh | 43 ------------------------------------------- 2 files changed, 49 deletions(-) delete mode 100644 ct/headers/mylar3 delete mode 100644 ct/mylar3.sh diff --git a/ct/headers/mylar3 b/ct/headers/mylar3 deleted file mode 100644 index cd3f10dcc..000000000 --- a/ct/headers/mylar3 +++ /dev/null @@ -1,6 +0,0 @@ - __ ___ __ _____ - / |/ /_ __/ /___ _____|__ / - / /|_/ / / / / / __ `/ ___//_ < - / / / / /_/ / / /_/ / / ___/ / -/_/ /_/\__, /_/\__,_/_/ /____/ - /____/ diff --git a/ct/mylar3.sh b/ct/mylar3.sh deleted file mode 100644 index 53441e8c2..000000000 --- a/ct/mylar3.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: davalanche -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/mylar3/mylar3 - -APP="Mylar3" -var_tags="${var_tags:-torrent;downloader;comic}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-512}" -var_disk="${var_disk:-4}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - if [[ ! -d /opt/mylar3 ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if check_for_gh_release "mylar3" "mylar3/mylar3"; then - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "mylar3" "mylar3/mylar3" "tarball" - systemctl restart mylar3 - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8090${CL}" From 135f78725f7dfd78169a77e60d98c44bf89bdbdc Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sun, 26 Oct 2025 09:47:41 +0100 Subject: [PATCH 1550/1733] Delete mylar3 --- install/mylar3-install.sh | 63 --------------------------------------- 1 file changed, 63 deletions(-) delete mode 100644 install/mylar3-install.sh diff --git a/install/mylar3-install.sh b/install/mylar3-install.sh deleted file mode 100644 index 750a709e7..000000000 --- a/install/mylar3-install.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: davalanche -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/mylar3/mylar3 - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -cat </etc/apt/sources.list.d/non-free.sources -Types: deb -URIs: http://deb.debian.org/debian -Suites: bookworm -Components: non-free non-free-firmware -EOF -$STD apt update -$STD apt install -y unrar -msg_ok "Installed Dependencies" - -PYTHON_VERSION="3.12" setup_uv -fetch_and_deploy_gh_release "mylar3" "mylar3/mylar3" "tarball" - -msg_info "Installing ${APPLICATION}" -mkdir -p /opt/mylar3-data -$STD uv venv /opt/mylar3/.venv -$STD /opt/mylar3/.venv/bin/python -m ensurepip --upgrade -$STD /opt/mylar3/.venv/bin/python -m pip install --upgrade pip -$STD /opt/mylar3/.venv/bin/python -m pip install --no-cache-dir -r /opt/mylar3/requirements.txt -msg_ok "Installed ${APPLICATION}" - -msg_info "Creating Service" -cat </etc/systemd/system/mylar3.service -[Unit] -Description=Mylar3 Service -After=network-online.target - -[Service] -ExecStart=/opt/mylar3/.venv/bin/python /opt/mylar3/Mylar.py --daemon --nolaunch --datadir=/opt/mylar3-data -GuessMainPID=no -Type=forking -Restart=on-failure - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now mylar3 -msg_ok "Created Service" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt -y autoremove -$STD apt -y autoclean -$STD apt -y clean -msg_ok "Cleaned" From 6c6af834c08a9322d09c0a39e2b57cba4a46bdab Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sun, 26 Oct 2025 11:01:22 +0100 Subject: [PATCH 1551/1733] Delete notesnook --- ct/headers/notesnook | 6 --- ct/notesnook.sh | 59 ----------------------- frontend/public/json/notesnook.json | 40 ---------------- install/notesnook-install.sh | 72 ----------------------------- 4 files changed, 177 deletions(-) delete mode 100644 ct/headers/notesnook delete mode 100644 ct/notesnook.sh delete mode 100644 frontend/public/json/notesnook.json delete mode 100644 install/notesnook-install.sh diff --git a/ct/headers/notesnook b/ct/headers/notesnook deleted file mode 100644 index c5dc22073..000000000 --- a/ct/headers/notesnook +++ /dev/null @@ -1,6 +0,0 @@ - __ __ - ____ ____ / /____ _________ ____ ____ / /__ - / __ \/ __ \/ __/ _ \/ ___/ __ \/ __ \/ __ \/ //_/ - / / / / /_/ / /_/ __(__ ) / / / /_/ / /_/ / ,< -/_/ /_/\____/\__/\___/____/_/ /_/\____/\____/_/|_| - diff --git a/ct/notesnook.sh b/ct/notesnook.sh deleted file mode 100644 index 3d1fbb3cf..000000000 --- a/ct/notesnook.sh +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Slaviša Arežina (tremor021) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/streetwriters/notesnook - -APP="notesnook" -var_tags="${var_tags:-os}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-3072}" -var_disk="${var_disk:-10}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/notesnook ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - msg_info "Stopping Service" - systemctl stop notesnook - msg_ok "Stopped Service" - - msg_info "Updating ${APP} (Patience)" - rm -rf /opt/notesnook - fetch_and_deploy_gh_release "notesnook" "streetwriters/notesnook" "tarball" - cd /opt/notesnook - export NODE_OPTIONS="--max-old-space-size=2560" - $STD npm install - $STD npm run build:web - msg_ok "Updated $APP" - - msg_info "Starting Service" - systemctl start notesnook - msg_ok "Started Service" - - msg_ok "Updated Successfully" - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}https://${IP}${CL}" diff --git a/frontend/public/json/notesnook.json b/frontend/public/json/notesnook.json deleted file mode 100644 index 335520b05..000000000 --- a/frontend/public/json/notesnook.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "Notesnook", - "slug": "notesnook", - "categories": [ - 12 - ], - "date_created": "2025-05-27", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 443, - "documentation": null, - "config_path": "/", - "website": "https://notesnook.com/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/notesnook.webp", - "description": "Notesnook is a free (as in speech) & open-source note-taking app focused on user privacy & ease of use. To ensure zero knowledge principles, Notesnook encrypts everything on your device using XChaCha20-Poly1305 & Argon2.", - "install_methods": [ - { - "type": "default", - "script": "ct/notesnook.sh", - "resources": { - "cpu": 2, - "ram": 3072, - "hdd": 10, - "os": "Debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "Before doing update of the app, please make a backup in the application Web UI. You will need to restore this backup after update finishes!", - "type": "warning" - } - ] -} diff --git a/install/notesnook-install.sh b/install/notesnook-install.sh deleted file mode 100644 index dbc93cbf8..000000000 --- a/install/notesnook-install.sh +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Slaviša Arežina (tremor021) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/streetwriters/notesnook - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt-get install -y \ - make \ - git \ - caddy -msg_ok "Installed Dependencies" - -NODE_VERSION="22" setup_nodejs -fetch_and_deploy_gh_release "notesnook" "streetwriters/notesnook" "tarball" - -msg_info "Configuring Notesnook (Patience)" -cd /opt/notesnook -export NODE_OPTIONS="--max-old-space-size=2560" -$STD npm install -$STD npm run build:web -msg_ok "Configured Notesnook" - -msg_info "Configuring Caddy" -LOCAL_IP=$(hostname -I | awk '{print $1}') -cat </etc/caddy/Caddyfile -{ - email admin@example.com -} - -${LOCAL_IP} { - reverse_proxy 127.0.0.1:3000 -} -EOF -msg_ok "Configured Caddy" - -msg_info "Creating Service" -cat </etc/systemd/system/notesnook.service -[Unit] -Description=Notesnook Service -After=network-online.target - -[Service] -Type=simple -User=root -WorkingDirectory=/opt/notesnook -ExecStart=/usr/bin/npx serve apps/web/build -Restart=on-failure - -[Install] -WantedBy=multi-user.target -EOF -systemctl reload caddy -systemctl enable -q --now notesnook -msg_ok "Created Service" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" From 9609615e61bb0d4eb7ba3fcdf54b97d2a54baf97 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sun, 26 Oct 2025 11:03:09 +0100 Subject: [PATCH 1552/1733] Delete Postiz --- ct/postiz.sh | 64 ------------------ frontend/public/json/postiz.json | 35 ---------- install/postiz-install.sh | 112 ------------------------------- 3 files changed, 211 deletions(-) delete mode 100644 ct/postiz.sh delete mode 100644 frontend/public/json/postiz.json delete mode 100644 install/postiz-install.sh diff --git a/ct/postiz.sh b/ct/postiz.sh deleted file mode 100644 index e53553677..000000000 --- a/ct/postiz.sh +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Slaviša Arežina (tremor021) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/Casvt/Kapowarr - -APP="Postiz" -var_tags="${var_tags:-Arr}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-3072}" -var_disk="${var_disk:-8}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - - if [[ ! -f /etc/systemd/system/postiz.service ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - RELEASE=$(curl -fsSL https://api.github.com/repos/Casvt/Kapowarr/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - if [[ "${RELEASE}" != "$(cat $HOME/.kapowarr)" ]] || [[ ! -f $HOME/.kapowarr ]]; then - msg_info "Stopping $APP" - systemctl stop kapowarr - msg_ok "Stopped $APP" - - msg_info "Creating Backup" - mv /opt/kapowarr/db /opt/ - msg_ok "Backup Created" - - msg_info "Updating $APP to ${RELEASE}" - fetch_and_deploy_gh_release "kapowarr" "Casvt/Kapowarr" - mv /opt/db /opt/kapowarr - msg_ok "Updated $APP to ${RELEASE}" - - msg_info "Starting $APP" - systemctl start kapowarr - msg_ok "Started $APP" - - msg_ok "Update Successful" - else - msg_ok "No update required. ${APP} is already at ${RELEASE}" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:5656${CL}" diff --git a/frontend/public/json/postiz.json b/frontend/public/json/postiz.json deleted file mode 100644 index dfcb0f045..000000000 --- a/frontend/public/json/postiz.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "Postiz", - "slug": "postiz", - "categories": [ - 20 - ], - "date_created": "2025-07-02", - "type": "ct", - "updateable": true, - "privileged": false, - "config_path": "/opt/postiz/.env", - "interface_port": 3000, - "documentation": "https://postiz.io/", - "website": "https://postiz.io/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/postiz.svg", - "description": "Postiz is an open-source self-hosted application.", - "install_methods": [ - { - "type": "default", - "script": "ct/postiz.sh", - "resources": { - "cpu": 1, - "ram": 1024, - "hdd": 2, - "os": "Debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] -} \ No newline at end of file diff --git a/install/postiz-install.sh b/install/postiz-install.sh deleted file mode 100644 index 5b9e35923..000000000 --- a/install/postiz-install.sh +++ /dev/null @@ -1,112 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Slaviša Arežina (tremor021) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/gitroomhq/postiz-app - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing dependencies" -$STD apt-get install -y \ - build-essential \ - python3-pip \ - supervisor \ - debian-keyring \ - debian-archive-keyring \ - apt-transport-https \ - redis -msg_ok "Installed dependencies" - -NODE_VERSION="20" setup_nodejs -PG_VERSION="17" setup_postgresql - -msg_info "Setting up PostgreSQL Database" -DB_NAME=postiz -DB_USER=postiz -DB_PASS="$(openssl rand -base64 18 | cut -c1-13)" -$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" -$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC'" -{ - echo "Postiz DB Credentials" - echo "Postiz Database User: $DB_USER" - echo "Postiz Database Password: $DB_PASS" - echo "Postiz Database Name: $DB_NAME" -} >>~/postiz.creds -msg_ok "Set up PostgreSQL Database" - -msg_info "Setting up Caddy" -curl -1sLf "https://dl.cloudsmith.io/public/caddy/stable/gpg.key" | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg -curl -1sLf "https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt" >/etc/apt/sources.list.d/caddy-stable.list -$STD apt-get update -$STD apt-get install caddy -msg_ok "Set up Caddy" - -fetch_and_deploy_gh_release "postiz" "gitroomhq/postiz-app" - -msg_info "Configuring Postiz" -LOCAL_IP=$(hostname -I | awk '{print $1}') -JWT_SECRET=$(openssl rand -base64 64 | tr '+/' '-_' | tr -d '=') -cd /opt/postiz -mkdir -p /etc/supervisor.d -$STD npm --no-update-notifier --no-fund --global install pnpm@10.6.1 pm2 -cp var/docker/supervisord.conf /etc/supervisord.conf -cp var/docker/Caddyfile ./Caddyfile -cp var/docker/entrypoint.sh ./entrypoint.sh -cp var/docker/supervisord/caddy.conf /etc/supervisor.d/caddy.conf -sed -i "s#/app/Caddyfile#/opt/postiz/Caddyfile#g" /etc/supervisor.d/caddy.conf -sed -i "s#/app/Caddyfile#/opt/postiz/Caddyfile#g" /opt/postiz/entrypoint.sh -sed -i "s#directory=/app#directory=/opt/postiz#g" /etc/supervisor.d/caddy.conf -export NODE_OPTIONS="--max-old-space-size=2560" -$STD pnpm install -$STD pnpm run build -chmod +x entrypoint.sh - -cat <.env -NOT_SECURED="true" -IS_GENERAL="true" -DATABASE_URL="postgresql://$DB_USER:$DB_PASS@localhost:5432/$DB_NAME" -REDIS_URL="redis://localhost:6379" -JWT_SECRET="$JWT_SECRET" -FRONTEND_URL="http://$LOCAL_IP:4200" -NEXT_PUBLIC_BACKEND_URL="http://$LOCAL_IP:3000" -BACKEND_INTERNAL_URL="http://$LOCAL_IP:3000" -EOF -msg_ok "Configured Postiz" - -msg_info "Creating Service" -cat </etc/systemd/system/postiz.service -[Unit] -Description=Postiz Service -After=network.target - -[Service] -Type=simple -User=root -WorkingDirectory=/opt/postiz -EnvironmentFile=/opt/postiz/.env -ExecStart=/usr/bin/pnpm run pm2-run -Restart=always - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now postiz -msg_ok "Created Service" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" From 4d307bfb12042eb1317a959f38ce2459753717e2 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sun, 26 Oct 2025 10:03:41 +0000 Subject: [PATCH 1553/1733] Update .app files --- ct/headers/postiz | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 ct/headers/postiz diff --git a/ct/headers/postiz b/ct/headers/postiz deleted file mode 100644 index 2c5c0ba81..000000000 --- a/ct/headers/postiz +++ /dev/null @@ -1,6 +0,0 @@ - ____ __ _ - / __ \____ _____/ /_(_)___ - / /_/ / __ \/ ___/ __/ /_ / - / ____/ /_/ (__ ) /_/ / / /_ -/_/ \____/____/\__/_/ /___/ - From b09c0bebac0109074d2803f52ad0f0ca12e72a96 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sun, 26 Oct 2025 20:35:58 +0100 Subject: [PATCH 1554/1733] Pangolin test --- ct/pangolin.sh | 44 +++++++++++++++++ install/pangolin-install.sh | 97 +++++++++++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 ct/pangolin.sh create mode 100644 install/pangolin-install.sh diff --git a/ct/pangolin.sh b/ct/pangolin.sh new file mode 100644 index 000000000..b1f5b7193 --- /dev/null +++ b/ct/pangolin.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://pangolin.net/ + +APP="Pangolin" +var_tags="${var_tags:-proxy}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-4096}" +var_disk="${var_disk:-5}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/pangolin ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating $APP LXC" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Updated $APP LXC" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" diff --git a/install/pangolin-install.sh b/install/pangolin-install.sh new file mode 100644 index 000000000..850440e78 --- /dev/null +++ b/install/pangolin-install.sh @@ -0,0 +1,97 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://pangolin.net/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt install -y \ + sqlite3 \ + iptables +msg_ok "Installed Dependencies" + +NODE_VERSION="22" setup_nodejs +fetch_and_deploy_gh_release "pangolin" "fosrl/pangolin" "tarball" +fetch_and_deploy_gh_release "gerbil" "fosrl/gerbil" "singlefile" "latest" "/usr/bin" "gerbil_linux_amd64" +IP_ADDR=$(hostname -I | awk '{print $1}') + +msg_info "Setup Pangolin (Patience)" +export BUILD=oss +export DATABASE=sqlite +cd /opt/pangolin +$STD npm ci +echo "export * from \"./$DATABASE\";" > server/db/index.ts +echo "export const build = \"$BUILD\" as any;" > server/build.ts +cp tsconfig.oss.json tsconfig.json +mkdir -p dist +$STD npm run next:build +$STD node esbuild.mjs -e server/index.ts -o dist/server.mjs -b $BUILD +$STD node esbuild.mjs -e server/setup/migrationsSqlite.ts -o dist/migrations.mjs +$STD npm run build:cli +cp -R .next/standalone ./ +cp ./cli/wrapper.sh /usr/local/bin/pangctl +chmod +x /usr/local/bin/pangctl ./dist/cli.mjs +cp server/db/names.json ./dist/names.json +$STD npm run db:sqlite:generate +$STD npm run db:sqlite:push +msg_ok "Setup Pangolin" + +msg_info "Creating Pangolin Service" +cat </etc/systemd/system/pangolin.service +[Unit] +Description=Pangolin Service +After=network.target + +[Service] +Type=simple +User=root +WorkingDirectory=/opt/pangolin +ExecStart=/usr/bin/npm start +Restart=always +RestartSec=10 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now pangolin +msg_ok "Created pangolin Service" + +msg_info "Setting up gerbil" +mkdir -p /var/config +cat </etc/systemd/system/gerbil.service +[Unit] +Description=Gerbil Service +After=network.target +Requires=pangolin.service + +[Service] +Type=simple +User=root +ExecStart=/usr/bin/gerbil --reachableAt=http://$IP_ADDR:3004 --generateAndSaveKeyTo=/var/config/key --remoteConfig=http://$IP_ADDR:3001/api/v1/ +Restart=always +RestartSec=10 + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now gerbil +msg_ok "Set up gerbil" + + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt -y autoremove +$STD apt -y autoclean +$STD apt -y clean +msg_ok "Cleaned" From 904078e6a1022de6cbf929899c380dd47da1f659 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sun, 26 Oct 2025 21:50:23 +0100 Subject: [PATCH 1555/1733] Update pangolin --- install/pangolin-install.sh | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/install/pangolin-install.sh b/install/pangolin-install.sh index 850440e78..26f3901ca 100644 --- a/install/pangolin-install.sh +++ b/install/pangolin-install.sh @@ -23,6 +23,7 @@ NODE_VERSION="22" setup_nodejs fetch_and_deploy_gh_release "pangolin" "fosrl/pangolin" "tarball" fetch_and_deploy_gh_release "gerbil" "fosrl/gerbil" "singlefile" "latest" "/usr/bin" "gerbil_linux_amd64" IP_ADDR=$(hostname -I | awk '{print $1}') +SECRET_KEY=$(openssl rand -base64 48 | tr -dc 'A-Za-z0-9' | head -c 32) msg_info "Setup Pangolin (Patience)" export BUILD=oss @@ -41,6 +42,34 @@ cp -R .next/standalone ./ cp ./cli/wrapper.sh /usr/local/bin/pangctl chmod +x /usr/local/bin/pangctl ./dist/cli.mjs cp server/db/names.json ./dist/names.json + +cat </opt/pangolin/config/config.yml +app: + dashboard_url: http://$IP_ADDR:3002 + log_level: debug + +domains: + domain1: + base_domain: example.com + +server: + secret: $SECRET_KEY + +gerbil: + base_endpoint: example.com + +orgs: + block_size: 24 + subnet_group: 100.90.137.0/20 + +flags: + require_email_verification: false + disable_signup_without_invite: true + disable_user_create_org: true + allow_raw_resources: true + enable_integration_api: true + enable_clients: true +EOF $STD npm run db:sqlite:generate $STD npm run db:sqlite:push msg_ok "Setup Pangolin" @@ -63,6 +92,7 @@ RestartSec=10 WantedBy=multi-user.target EOF systemctl enable -q --now pangolin +journalctl -u pangolin -f | grep -m1 -oP 'Token:\s*\K\w+' > ~/pangolin.creds msg_ok "Created pangolin Service" msg_info "Setting up gerbil" @@ -86,7 +116,6 @@ EOF systemctl enable -q --now gerbil msg_ok "Set up gerbil" - motd_ssh customize From b1859248c494ac3b1ee869d2412de570259fb1fb Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sun, 26 Oct 2025 21:54:44 +0100 Subject: [PATCH 1556/1733] Pangolin: update port --- ct/pangolin.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/pangolin.sh b/ct/pangolin.sh index b1f5b7193..227df06d0 100644 --- a/ct/pangolin.sh +++ b/ct/pangolin.sh @@ -41,4 +41,4 @@ description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3002${CL}" From 9360e502c9e98b139faabe50fbb53579aefa3a74 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sun, 26 Oct 2025 22:00:32 +0100 Subject: [PATCH 1557/1733] Pangolin: update --- install/pangolin-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/pangolin-install.sh b/install/pangolin-install.sh index 26f3901ca..6da8fed65 100644 --- a/install/pangolin-install.sh +++ b/install/pangolin-install.sh @@ -92,7 +92,7 @@ RestartSec=10 WantedBy=multi-user.target EOF systemctl enable -q --now pangolin -journalctl -u pangolin -f | grep -m1 -oP 'Token:\s*\K\w+' > ~/pangolin.creds +journalctl -u pangolin -f | grep -m1 'Token:' | awk '{print $NF}' > ~/pangolin.creds msg_ok "Created pangolin Service" msg_info "Setting up gerbil" From a411da3f30e485fcf22792a7f47c20b42b4dcc39 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sun, 26 Oct 2025 22:10:12 +0100 Subject: [PATCH 1558/1733] Pangolin: update fix --- install/pangolin-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/pangolin-install.sh b/install/pangolin-install.sh index 6da8fed65..860d26786 100644 --- a/install/pangolin-install.sh +++ b/install/pangolin-install.sh @@ -92,7 +92,7 @@ RestartSec=10 WantedBy=multi-user.target EOF systemctl enable -q --now pangolin -journalctl -u pangolin -f | grep -m1 'Token:' | awk '{print $NF}' > ~/pangolin.creds +journalctl -u pangolin -f | grep -m1 'Token:' | awk '{print $NF}' | tee ~/pangolin.creds > /dev/null msg_ok "Created pangolin Service" msg_info "Setting up gerbil" From 0a4bb023b034aede0a8422985dc351a14203d147 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sun, 26 Oct 2025 22:18:30 +0100 Subject: [PATCH 1559/1733] Pangolin: update fix --- install/pangolin-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/install/pangolin-install.sh b/install/pangolin-install.sh index 860d26786..37ad2e3f6 100644 --- a/install/pangolin-install.sh +++ b/install/pangolin-install.sh @@ -92,7 +92,6 @@ RestartSec=10 WantedBy=multi-user.target EOF systemctl enable -q --now pangolin -journalctl -u pangolin -f | grep -m1 'Token:' | awk '{print $NF}' | tee ~/pangolin.creds > /dev/null msg_ok "Created pangolin Service" msg_info "Setting up gerbil" From a4a39c66c30fbd3cfdd99877267a29b6fa011399 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sun, 26 Oct 2025 22:37:39 +0100 Subject: [PATCH 1560/1733] Pangolin: add json --- frontend/public/json/pangolin.json | 44 ++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 frontend/public/json/pangolin.json diff --git a/frontend/public/json/pangolin.json b/frontend/public/json/pangolin.json new file mode 100644 index 000000000..109bb77f9 --- /dev/null +++ b/frontend/public/json/pangolin.json @@ -0,0 +1,44 @@ +{ + "name": "Pangolin", + "slug": "pangolin", + "categories": [ + 21 + ], + "date_created": "2025-09-04", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 3002, + "documentation": "https://docs.pangolin.net/", + "config_path": "/opt/pangolin/config/config.yml", + "website": "https://pangolin.net/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/pangolin.webp", + "description": "Pangolin securely routes traffic over WireGuard tunnels to any private network. It works like a reverse proxy that spans multiple networks — no public IPs, DNS setup, or certificates required.", + "install_methods": [ + { + "type": "default", + "script": "ct/pangolin.sh", + "resources": { + "cpu": 2, + "ram": 4096, + "hdd": 5, + "os": "Debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Type `journalctl -u pangolin | grep -oP 'Token:\\s*\\K\\w+'` into LXC console to get admin token which you will use to create admin account.", + "type": "info" + }, + { + "text": "LXC has 4GB of RAM set initially for the build stage. After installation finishes, you can decrease the RAM allocated to 1024MB or 512MB even.", + "type": "info" + } + ] +} From d2bbc3472d68ca6c51bfc28c030fc3db1bd33371 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sun, 26 Oct 2025 23:08:29 +0100 Subject: [PATCH 1561/1733] Pangolin: add update procedure --- ct/pangolin.sh | 46 +++++++++++++++++++++++++++++++++---- install/pangolin-install.sh | 7 +++++- 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/ct/pangolin.sh b/ct/pangolin.sh index 227df06d0..87b3e4856 100644 --- a/ct/pangolin.sh +++ b/ct/pangolin.sh @@ -27,10 +27,48 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - msg_info "Updating $APP LXC" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "Updated $APP LXC" + + if check_for_gh_release "pangolin" "fosrl/pangolin"; then + msg_info "Stopping ${APP}" + systemctl stop pangolin + msg_info "Service stopped" + + msg_info "Creating backup" + tar -czf /opt/pangolin_config_backup.tar.gz -C /opt/pangolin config + msg_ok "Created backup" + + fetch_and_deploy_gh_release "pangolin" "fosrl/pangolin" "tarball" + fetch_and_deploy_gh_release "gerbil" "fosrl/gerbil" "singlefile" "latest" "/usr/bin" "gerbil_linux_amd64" + + msg_info "Updating ${APP}" + export BUILD=oss + export DATABASE=sqlite + cd /opt/pangolin + $STD npm ci + echo "export * from \"./$DATABASE\";" > server/db/index.ts + echo "export const build = \"$BUILD\" as any;" > server/build.ts + cp tsconfig.oss.json tsconfig.json + $STD npm run next:build + $STD node esbuild.mjs -e server/index.ts -o dist/server.mjs -b $BUILD + $STD node esbuild.mjs -e server/setup/migrationsSqlite.ts -o dist/migrations.mjs + $STD npm run build:cli + cp -R .next/standalone ./ + + cat </usr/local/bin/pangctl +#!/bin/sh +cd /opt/pangolin +./dist/cli.mjs "$@" +EOF + chmod +x /usr/local/bin/pangctl ./dist/cli.mjs + cp server/db/names.json ./dist/names.json + msg_ok "Updated ${APP}" + + msg_info "Restoring config" + tar -xzf /opt/pangolin_config_backup.tar.gz -C /opt/pangolin --overwrite + rm -f /opt/pangolin_config_backup.tar.gz + msg_ok "Restored config" + msg_ok "Updated successfully!" + fi exit } diff --git a/install/pangolin-install.sh b/install/pangolin-install.sh index 37ad2e3f6..2936394b7 100644 --- a/install/pangolin-install.sh +++ b/install/pangolin-install.sh @@ -39,7 +39,12 @@ $STD node esbuild.mjs -e server/index.ts -o dist/server.mjs -b $BUILD $STD node esbuild.mjs -e server/setup/migrationsSqlite.ts -o dist/migrations.mjs $STD npm run build:cli cp -R .next/standalone ./ -cp ./cli/wrapper.sh /usr/local/bin/pangctl + +cat </usr/local/bin/pangctl +#!/bin/sh +cd /opt/pangolin +./dist/cli.mjs "$@" +EOF chmod +x /usr/local/bin/pangctl ./dist/cli.mjs cp server/db/names.json ./dist/names.json From 6ce2a44ea17468b3422d1690fbdc306328be03e6 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sun, 26 Oct 2025 22:10:08 +0000 Subject: [PATCH 1562/1733] Update .app files --- ct/headers/pangolin | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/pangolin diff --git a/ct/headers/pangolin b/ct/headers/pangolin new file mode 100644 index 000000000..0a2d42304 --- /dev/null +++ b/ct/headers/pangolin @@ -0,0 +1,6 @@ + ____ ___ + / __ \____ _____ ____ _____ / (_)___ + / /_/ / __ `/ __ \/ __ `/ __ \/ / / __ \ + / ____/ /_/ / / / / /_/ / /_/ / / / / / / +/_/ \__,_/_/ /_/\__, /\____/_/_/_/ /_/ + /____/ From d204dcd68d826bf5524b31bab473905f3e0ff9b7 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sun, 26 Oct 2025 23:41:12 +0100 Subject: [PATCH 1563/1733] Pangolin: update json --- frontend/public/json/pangolin.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frontend/public/json/pangolin.json b/frontend/public/json/pangolin.json index 109bb77f9..e197b36b5 100644 --- a/frontend/public/json/pangolin.json +++ b/frontend/public/json/pangolin.json @@ -39,6 +39,10 @@ { "text": "LXC has 4GB of RAM set initially for the build stage. After installation finishes, you can decrease the RAM allocated to 1024MB or 512MB even.", "type": "info" + }, + { + "text": "Make sure you edit `/opt/pangolin/config/config.yml` and change it to match your needs", + "type": "warning" } ] } From 729a48b3631717ad5ba8e114db6642e2b450c1ca Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 27 Oct 2025 08:02:57 +0100 Subject: [PATCH 1564/1733] cleanup --- ct/comfyui.sh | 42 --------------- frontend/public/json/comfyui.json | 44 ---------------- install/comfyui-install.sh | 87 ------------------------------- 3 files changed, 173 deletions(-) delete mode 100644 ct/comfyui.sh delete mode 100644 frontend/public/json/comfyui.json delete mode 100644 install/comfyui-install.sh diff --git a/ct/comfyui.sh b/ct/comfyui.sh deleted file mode 100644 index 52354e45a..000000000 --- a/ct/comfyui.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: jdacode -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/comfyanonymous/ComfyUI - -APP="ComfyUI" -var_tags="${var_tags:-ai}" -var_cpu="${var_cpu:-4}" -var_ram="${var_ram:-8192}" -var_disk="${var_disk:-25}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - - if [[ ! -f /opt/${APP} ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - msg_error "To update use the ${APP} Manager." - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8188${CL}" diff --git a/frontend/public/json/comfyui.json b/frontend/public/json/comfyui.json deleted file mode 100644 index 95e3bd381..000000000 --- a/frontend/public/json/comfyui.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "name": "ComfyUI", - "slug": "comfyui", - "categories": [ - 20 - ], - "date_created": "2025-08-01", - "type": "ct", - "updateable": true, - "privileged": false, - "config_path": "/opt", - "interface_port": 8188, - "documentation": "https://github.com/comfyanonymous/ComfyUI", - "website": "https://www.comfy.org/", - "logo": "https://framerusercontent.com/images/3cNQMWKzIhIrQ5KErBm7dSmbd2w.png", - "description": "ComfyUI is a node-based interface and inference engine for generative AI. Users can combine various AI models and operations through nodes to achieve highly customizable and controllable content generation.", - "install_methods": [ - { - "type": "default", - "script": "ct/comfyui.sh", - "resources": { - "cpu": 4, - "ram": 8192, - "hdd": 25, - "os": "debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "Application takes long time to install. Please be patient!", - "type": "warning" - }, - { - "text": "Please check that you have installed the drivers for your GPU.", - "type": "info" - } - ] -} diff --git a/install/comfyui-install.sh b/install/comfyui-install.sh deleted file mode 100644 index 4f5b7de41..000000000 --- a/install/comfyui-install.sh +++ /dev/null @@ -1,87 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: jdacode -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/comfyanonymous/ComfyUI - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -echo -echo "${TAB3}Choose the GPU type for ComfyUI:" -echo "${TAB3}[1]-None [2]-NVIDIA [3]-AMD [4]-Intel" -read -rp "${TAB3}Enter your choice [1-4] (default: 1): " gpu_choice -gpu_choice=${gpu_choice:-1} -case "$gpu_choice" in -1) comfyui_gpu_type="none";; -2) comfyui_gpu_type="nvidia";; -3) comfyui_gpu_type="amd";; -4) comfyui_gpu_type="intel";; -*) comfyui_gpu_type="none"; echo "${TAB3}Invalid choice. Defaulting to ${comfyui_gpu_type}." ;; -esac -echo - -PYTHON_VERSION="3.12" setup_uv - -fetch_and_deploy_gh_release "ComfyUI" "comfyanonymous/ComfyUI" "tarball" "latest" "/opt/ComfyUI" - -msg_info "Python dependencies" -$STD uv venv "/opt/ComfyUI/venv" -if [[ "${comfyui_gpu_type,,}" == "nvidia" ]]; then - $STD uv pip install \ - torch \ - torchvision \ - torchaudio \ - --extra-index-url "https://download.pytorch.org/whl/cu128" \ - --python="/opt/ComfyUI/venv/bin/python" -elif [[ "${comfyui_gpu_type,,}" == "amd" ]]; then - $STD uv pip install \ - torch \ - torchvision \ - torchaudio \ - --index-url "https://download.pytorch.org/whl/rocm6.3" \ - --python="/opt/ComfyUI/venv/bin/python" -elif [[ "${comfyui_gpu_type,,}" == "intel" ]]; then - $STD uv pip install \ - torch \ - torchvision \ - torchaudio \ - --index-url "https://download.pytorch.org/whl/xpu" \ - --python="/opt/ComfyUI/venv/bin/python" -fi -$STD uv pip install -r "/opt/ComfyUI/requirements.txt" --python="/opt/ComfyUI/venv/bin/python" -msg_ok "Python dependencies" - -msg_info "Creating Service" -cat </etc/systemd/system/comfyui.service -[Unit] -Description=ComfyUI Service -After=network.target - -[Service] -Type=simple -User=root -WorkingDirectory=/opt/ComfyUI -ExecStart=/opt/ComfyUI/venv/bin/python /opt/ComfyUI/main.py --listen --port 8188 --cpu -Restart=on-failure - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now comfyui -msg_ok "Created Service" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt -y autoremove -$STD apt -y autoclean -$STD apt -y clean -msg_ok "Cleaned" From a8fa0cdb8b5c9698d9bac6b18da47ab71ba3a264 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 27 Oct 2025 07:03:34 +0000 Subject: [PATCH 1565/1733] Update .app files --- ct/headers/comfyui | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 ct/headers/comfyui diff --git a/ct/headers/comfyui b/ct/headers/comfyui deleted file mode 100644 index 5f3bfd60a..000000000 --- a/ct/headers/comfyui +++ /dev/null @@ -1,6 +0,0 @@ - ______ ____ __ ______ - / ____/___ ____ ___ / __/_ __/ / / / _/ - / / / __ \/ __ `__ \/ /_/ / / / / / // / -/ /___/ /_/ / / / / / / __/ /_/ / /_/ // / -\____/\____/_/ /_/ /_/_/ \__, /\____/___/ - /____/ From 5333e2c6e3218de047276d26333232787d23a3c4 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 27 Oct 2025 08:46:40 +0100 Subject: [PATCH 1566/1733] garage --- ct/alpine.sh | 6 ++-- ct/garage.sh | 42 +++++++++++++++++++++++ install/alpine-install.sh | 1 - install/garage-install.sh | 70 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 ct/garage.sh create mode 100644 install/garage-install.sh diff --git a/ct/alpine.sh b/ct/alpine.sh index ea946a60e..51767cb01 100644 --- a/ct/alpine.sh +++ b/ct/alpine.sh @@ -7,9 +7,9 @@ source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxV APP="Alpine" var_tags="${var_tags:-os;alpine}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-512}" -var_disk="${var_disk:-1}" +var_cpu="${var_cpu:-4}" +var_ram="${var_ram:-4096}" +var_disk="${var_disk:-5}" var_os="${var_os:-alpine}" var_version="${var_version:-3.22}" var_unprivileged="${var_unprivileged:-1}" diff --git a/ct/garage.sh b/ct/garage.sh new file mode 100644 index 000000000..f07efc473 --- /dev/null +++ b/ct/garage.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://garagehq.deuxfleurs.fr/ + +APP="Garage" +var_tags="${var_tags:-}" +var_cpu="${var_cpu:-4}" +var_ram="${var_ram:-8192}" +var_disk="${var_disk:-20}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /var ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating $APP LXC" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Updated $APP LXC" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!" +msg_custom "🚀" "${GN}" "${APP} setup has been successfully initialized!" diff --git a/install/alpine-install.sh b/install/alpine-install.sh index 4922f1641..c8c95c5e0 100644 --- a/install/alpine-install.sh +++ b/install/alpine-install.sh @@ -21,7 +21,6 @@ $STD apk add nano $STD apk add mc msg_ok "Installed Dependencies" -fetch_and_deploy_gh_release "redlib" "redlib-org/redlib" "prebuild" "latest" "/opt/redlib" "redlib-x86_64-unknown-linux-musl.tar.gz" motd_ssh customize diff --git a/install/garage-install.sh b/install/garage-install.sh new file mode 100644 index 000000000..06dc470e9 --- /dev/null +++ b/install/garage-install.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Test Suite for tools.func +# License: MIT +# https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Purpose: Run comprehensive test suite for all setup_* functions from tools.func + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Setup Garage" +GITEA_RELEASE=$(curl -s https://api.github.com/repos/deuxfleurs-org/garage/tags | jq -r '.[0].name') +curl -fsSL "https://garagehq.deuxfleurs.fr/_releases/${GITEA_RELEASE}/x86_64-unknown-linux-musl/garage" -o /usr/local/bin/garage +chmod +x /usr/local/bin/garage +mkdir -p /var/lib/garage/{data,meta,snapshots} +mkdir -p /etc/garage +RPC_SECRET=$(openssl rand -hex 32) +ADMIN_TOKEN=$(openssl rand -base64 32) +METRICS_TOKEN=$(openssl rand -base64 32) +{ + echo "Garage Tokens and Secrets" + echo "RPC Secret: $RPC_SECRET" + echo "Admin Token: $ADMIN_TOKEN" + echo "Metrics Token: $METRICS_TOKEN" +} >>~/garage.creds +cat </etc/garage.toml +metadata_dir = "/var/lib/garage/meta" +data_dir = "/var/lib/garage/data" +db_engine = "sqlite" +replication_factor = 1 + +rpc_bind_addr = "[::]:3901" +rpc_public_addr = "127.0.0.1:3901" +rpc_secret = "${RPC_SECRET}" + +[s3_api] +s3_region = "garage" +api_bind_addr = "[::]:3900" +root_domain = ".s3.garage.localhost" + +[s3_web] +bind_addr = "[::]:3902" +root_domain = ".web.garage.localhost" +index = "index.html" + +[k2v_api] +api_bind_addr = "[::]:3904" + +[admin] +api_bind_addr = "[::]:3903" +admin_token = "${ADMIN_TOKEN}" +metrics_token = "${METRICS_TOKEN}" +EOF +msg_ok "Set up Garage" + + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt -y autoremove +$STD apt -y autoclean +$STD apt -y clean +msg_ok "Cleaned" From bcaaf666abfd8370425ec230543c85666c034221 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 27 Oct 2025 09:10:55 +0100 Subject: [PATCH 1567/1733] Add Alpine support and update Garage install scripts Introduces Alpine-based installation and management scripts for Garage, including ct/alpine-garage.sh and install/alpine-garage-install.sh. Updates ct/garage.sh and install/garage-install.sh to unify update logic, resource defaults, and credential handling. Adds frontend/public/json/garage.json with metadata and install options for both Debian and Alpine. Improves service management and configuration generation for both environments. --- ct/alpine-garage.sh | 65 +++++++++++++++++++ ct/garage.sh | 45 +++++++++---- frontend/public/json/garage.json | 59 ++++++++++++++++++ install/alpine-garage-install.sh | 104 +++++++++++++------------------ install/garage-install.sh | 1 + 5 files changed, 204 insertions(+), 70 deletions(-) create mode 100644 ct/alpine-garage.sh create mode 100644 frontend/public/json/garage.json diff --git a/ct/alpine-garage.sh b/ct/alpine-garage.sh new file mode 100644 index 000000000..ecf981770 --- /dev/null +++ b/ct/alpine-garage.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://alpinelinux.org/ + +APP="Alpine-Garage" +var_tags="${var_tags:-alpine;object-storage}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-512}" +var_disk="${var_disk:-3}" +var_os="${var_os:-alpine}" +var_version="${var_version:-3.22}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + if [[ ! -f /usr/local/bin/garage ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + GITEA_RELEASE=$(curl -fsSL https://api.github.com/repos/deuxfleurs-org/garage/tags | jq -r '.[0].name') + if [[ "${GITEA_RELEASE}" != "$(cat ~/.garage 2>/dev/null)" ]] || [[ ! -f ~/.garage ]]; then + msg_info "Stopping Service" + rc-service garage stop || true + msg_ok "Stopped Service" + + msg_info "Backing Up Data" + cp /usr/local/bin/garage /usr/local/bin/garage.old 2>/dev/null || true + cp /etc/garage.toml /etc/garage.toml.bak 2>/dev/null || true + msg_ok "Backed Up Data" + + msg_info "Updating Garage" + curl -fsSL "https://garagehq.deuxfleurs.fr/_releases/${GITEA_RELEASE}/x86_64-unknown-linux-musl/garage" -o /usr/local/bin/garage + chmod +x /usr/local/bin/garage + echo "${GITEA_RELEASE}" > ~/.garage + msg_ok "Updated Garage" + + msg_info "Starting Service" + rc-service garage start || rc-service garage restart + msg_ok "Started Service" + msg_ok "Update Successfully!" + else + msg_ok "No update required. Garage is already at ${GITEA_RELEASE}" + fi + exit +} + + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" + diff --git a/ct/garage.sh b/ct/garage.sh index f07efc473..a43859bb7 100644 --- a/ct/garage.sh +++ b/ct/garage.sh @@ -6,10 +6,10 @@ source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxV # Source: https://garagehq.deuxfleurs.fr/ APP="Garage" -var_tags="${var_tags:-}" -var_cpu="${var_cpu:-4}" -var_ram="${var_ram:-8192}" -var_disk="${var_disk:-20}" +var_tags="${var_tags:-object-storage}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-512}" +var_disk="${var_disk:-3}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" @@ -23,14 +23,34 @@ function update_script() { header_info check_container_storage check_container_resources - if [[ ! -d /var ]]; then + if [[ ! -f /usr/local/bin/garage ]]; then msg_error "No ${APP} Installation Found!" exit fi - msg_info "Updating $APP LXC" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "Updated $APP LXC" + GITEA_RELEASE=$(curl -fsSL https://api.github.com/repos/deuxfleurs-org/garage/tags | jq -r '.[0].name') + if [[ "${GITEA_RELEASE}" != "$(cat ~/.garage 2>/dev/null)" ]] || [[ ! -f ~/.garage ]]; then + msg_info "Stopping Service" + systemctl stop garage + msg_ok "Stopped Service" + + msg_info "Backing Up Data" + cp /usr/local/bin/garage /usr/local/bin/garage.old 2>/dev/null || true + cp /etc/garage.toml /etc/garage.toml.bak 2>/dev/null || true + msg_ok "Backed Up Data" + + msg_info "Updating Garage" + curl -fsSL "https://garagehq.deuxfleurs.fr/_releases/${GITEA_RELEASE}/x86_64-unknown-linux-musl/garage" -o /usr/local/bin/garage + chmod +x /usr/local/bin/garage + echo "${GITEA_RELEASE}" > ~/.garage + msg_ok "Updated Garage" + + msg_info "Starting Service" + systemctl start garage + msg_ok "Started Service" + msg_ok "Update Successfully!" + else + msg_ok "No update required. Garage is already at ${GITEA_RELEASE}" + fi exit } @@ -38,5 +58,8 @@ start build_container description -msg_ok "Completed Successfully!" -msg_custom "🚀" "${GN}" "${APP} setup has been successfully initialized!" +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" + diff --git a/frontend/public/json/garage.json b/frontend/public/json/garage.json new file mode 100644 index 000000000..d7a407392 --- /dev/null +++ b/frontend/public/json/garage.json @@ -0,0 +1,59 @@ +{ + "name": "Garage", + "slug": "garage", + "categories": [ + 8 + ], + "date_created": "2025-10-27", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 3900, + "documentation": "https://garagehq.deuxfleurs.fr/documentation/quick-start/", + "website": "https://garagehq.deuxfleurs.fr/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/garage.webp", + "config_path": "/etc/garage.toml", + "description": "Garage is a lightweight, self-hosted, S3-compatible object storage service built for distributed environments. It is designed to be simple, efficient, and easy to deploy across multiple nodes.", + "install_methods": [ + { + "type": "default", + "script": "ct/garage.sh", + "resources": { + "cpu": 1, + "ram": 512, + "hdd": 3, + "os": "debian", + "version": "13" + } + }, + { + "type": "alpine", + "script": "ct/alpine-garage.sh", + "resources": { + "cpu": 1, + "ram": 512, + "hdd": 3, + "os": "alpine", + "version": "3.22" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "The Garage configuration file is located at `/etc/garage.toml`. You can edit RPC and API bindings, tokens, and data directories there.", + "type": "info" + }, + { + "text": "Admin API runs by default on port `3903`, S3 API on port `3900`, Web UI on `3902`. Adjust firewall rules accordingly.", + "type": "warning" + }, + { + "text": "To view your generated tokens and RPC secret, check `~/garage.creds` after installation.", + "type": "info" + } + ] +} diff --git a/install/alpine-garage-install.sh b/install/alpine-garage-install.sh index 5138656a8..c82f3e03c 100644 --- a/install/alpine-garage-install.sh +++ b/install/alpine-garage-install.sh @@ -13,86 +13,72 @@ setting_up_container network_check update_os -msg_info "Preparing directories" -mkdir -p /var/lib/garage/meta /var/lib/garage/data /var/lib/garage/snapshots -msg_ok "Prepared directories" - -msg_info "Setup Garage packages" -$STD apk add --no-cache garage garage-openrc openssl -msg_ok "Setup Garage packages" - -# msg_info "Generating RPC secret" -# if [[ ! -s /etc/garage.rpc_secret ]]; then -# openssl rand -hex 32 | tr -d '\n' >/etc/garage.rpc_secret -# chmod 600 /etc/garage.rpc_secret -# fi -# msg_ok "Generated RPC secret" - -# msg_info "Generating tokens" -# if [[ ! -s /etc/garage.tokens.env ]]; then -# ADMIN_TOKEN="$(openssl rand -base64 32)" -# METRICS_TOKEN="$(openssl rand -base64 32)" -# cat >/etc/garage.tokens.env </etc/garage.toml <~/garage.creds +echo $GITEA_RELEASE >>~/.garage +cat </etc/garage.toml metadata_dir = "/var/lib/garage/meta" data_dir = "/var/lib/garage/data" -metadata_snapshots_dir = "/var/lib/garage/snapshots" - -db_engine = "lmdb" -metadata_fsync = true -data_fsync = false -metadata_auto_snapshot_interval = "6h" +db_engine = "sqlite" +replication_factor = 1 rpc_bind_addr = "0.0.0.0:3901" rpc_public_addr = "127.0.0.1:3901" -allow_world_readable_secrets = false +rpc_secret = "${RPC_SECRET}" [s3_api] -api_bind_addr = "0.0.0.0:3900" s3_region = "garage" +api_bind_addr = "0.0.0.0:3900" root_domain = ".s3.garage" [s3_web] bind_addr = "0.0.0.0:3902" root_domain = ".web.garage" -add_host_to_metrics = true +index = "index.html" + +[k2v_api] +api_bind_addr = "0.0.0.0:3904" [admin] api_bind_addr = "0.0.0.0:3903" -metrics_require_token = false +admin_token = "${ADMIN_TOKEN}" +metrics_token = "${METRICS_TOKEN}" EOF -fi -msg_ok "Wrote config" +msg_ok "Configured Garage" -msg_info "Enable + start service" +msg_info "Creating Service" +cat <<'EOF' >/etc/init.d/garage +#!/sbin/openrc-run +name="Garage Object Storage" +command="/usr/local/bin/garage" +command_args="server" +command_background="yes" +pidfile="/run/garage.pid" +depend() { + need net +} +EOF + +chmod +x /etc/init.d/garage $STD rc-update add garage default -$STD rc-service garage restart || $STD rc-service garage start -$STD rc-service garage status || true +$STD rc-service garage restart || rc-service garage start msg_ok "Service active" -msg_info "Setup Node" -garage node id -NODE_ID=$(garage node id | cut -d@ -f1) -garage layout assign $NODE_ID --capacity 1T -garage layout apply -garage status -msg_ok "Node setup" - motd_ssh customize diff --git a/install/garage-install.sh b/install/garage-install.sh index 06dc470e9..b8f1a39ec 100644 --- a/install/garage-install.sh +++ b/install/garage-install.sh @@ -29,6 +29,7 @@ METRICS_TOKEN=$(openssl rand -base64 32) echo "Admin Token: $ADMIN_TOKEN" echo "Metrics Token: $METRICS_TOKEN" } >>~/garage.creds +echo $GITEA_RELEASE >>~/.garage cat </etc/garage.toml metadata_dir = "/var/lib/garage/meta" data_dir = "/var/lib/garage/data" From 1070d0cdad91343d22cc42c82a4c87a1fd0ed300 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 27 Oct 2025 10:29:12 +0100 Subject: [PATCH 1568/1733] remove debug output --- misc/build.func | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/misc/build.func b/misc/build.func index f8fcaa5fb..b776fdb4b 100644 --- a/misc/build.func +++ b/misc/build.func @@ -504,7 +504,7 @@ advanced_settings() { fi done - if CT_ID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Container ID" 8 58 "$NEXTID" --title "CONTAINER ID" 3>&1 1>&2 2>&3); then + if CT_ID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Container ID" 8 58 "$NEXTID" --title "CONTAINER ID" 3>&1 1>&2 2>&3); then if [ -z "$CT_ID" ]; then CT_ID="$NEXTID" fi @@ -3078,7 +3078,7 @@ create_lxc_container() { TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION}" SEARCH_PATTERN="^${TEMPLATE_SEARCH}-" - echo "[DEBUG] Retrying with version: $PCT_OSVERSION" + #echo "[DEBUG] Retrying with version: $PCT_OSVERSION" mapfile -t ONLINE_TEMPLATES < <( pveam available -section system 2>/dev/null | @@ -3091,7 +3091,7 @@ create_lxc_container() { if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then TEMPLATE="${ONLINE_TEMPLATES[-1]}" TEMPLATE_SOURCE="online" - echo "[DEBUG] Found alternative: $TEMPLATE" + #echo "[DEBUG] Found alternative: $TEMPLATE" else msg_error "No templates available for ${PCT_OSTYPE} ${PCT_OSVERSION}" exit 225 @@ -3106,8 +3106,8 @@ create_lxc_container() { fi fi - echo "[DEBUG] Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'" - msg_debug "Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'" + #echo "[DEBUG] Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'" + #msg_debug "Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'" TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" if [[ -z "$TEMPLATE_PATH" ]]; then From 11f786366d5170be544c4e11253910604826401f Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 27 Oct 2025 10:35:29 +0100 Subject: [PATCH 1569/1733] Enhance Ente install script with automation and helpers Adds installation of curl, jq, and the Ente CLI, automates frontend builds with dynamic IP detection, and generates a rebuild script for frontend updates if the IP changes. Updates configuration files to use the container IP, improves post-installation instructions, and creates helper scripts for email verification and subscription upgrades. Also enhances Caddy configuration with CORS headers and provides a detailed final setup summary for easier onboarding. --- install/ente-install.sh | 209 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 193 insertions(+), 16 deletions(-) diff --git a/install/ente-install.sh b/install/ente-install.sh index 8a4b06ab4..9d8d1fcdb 100644 --- a/install/ente-install.sh +++ b/install/ente-install.sh @@ -15,11 +15,13 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - libsodium23 \ - libsodium-dev \ - pkg-config \ - caddy \ - gcc + libsodium23 \ + libsodium-dev \ + pkg-config \ + caddy \ + gcc \ + curl \ + jq msg_ok "Installed Dependencies" PG_VERSION="17" setup_postgresql @@ -37,10 +39,28 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8' $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" { - echo "Ente Credentials" - echo "Database Name: $DB_NAME" - echo "Database User: $DB_USER" - echo "Database Password: $DB_PASS" + echo "Ente Credentials" + echo "Database Name: $DB_NAME" + echo "Database User: $DB_USER" + echo "Database Password: $DB_PASS" + echo "" + echo "Important Configuration Notes:" + echo "- Frontend is built with IP: $(hostname -I | awk '{print $1}')" + echo "- If IP changes, run: /opt/ente/rebuild-frontend.sh" + echo "- Museum API: http://$(hostname -I | awk '{print $1}'):8080" + echo "- Photos UI: http://$(hostname -I | awk '{print $1}'):3000" + echo "- Accounts UI: http://$(hostname -I | awk '{print $1}'):3001" + echo "- Auth UI: http://$(hostname -I | awk '{print $1}'):3003" + echo "" + echo "Post-Installation Steps Required:" + echo "1. Create your first user account via the web UI" + echo "2. Check museum logs for email verification code:" + echo " journalctl -u ente-museum -n 100 | grep -i 'verification'" + echo "3. Use verification code to complete account setup" + echo "4. Remove subscription limit (replace with your account):" + echo " ente admin update-subscription -a -u --no-limit" + echo "" + echo "Note: Email verification requires manual intervention since SMTP is not configured" } >>~/ente.creds msg_ok "Set up PostgreSQL" @@ -52,10 +72,10 @@ export CGO_ENABLED=1 CGO_CFLAGS="$(pkg-config --cflags libsodium || true)" CGO_LDFLAGS="$(pkg-config --libs libsodium || true)" if [ -z "$CGO_CFLAGS" ]; then - CGO_CFLAGS="-I/usr/include" + CGO_CFLAGS="-I/usr/include" fi if [ -z "$CGO_LDFLAGS" ]; then - CGO_LDFLAGS="-lsodium" + CGO_LDFLAGS="-lsodium" fi export CGO_CFLAGS export CGO_LDFLAGS @@ -69,6 +89,7 @@ SECRET_JWT=$($STD go run tools/gen-random-keys/main.go | grep "jwt" | awk '{prin msg_ok "Generated Secrets" msg_info "Creating museum.yaml" +CONTAINER_IP=$(hostname -I | awk '{print $1}') cat </opt/ente/server/museum.yaml db: host: 127.0.0.1 @@ -88,9 +109,9 @@ s3: bucket: ente-dev apps: - public-albums: http://localhost:3002 - cast: http://localhost:3004 - accounts: http://localhost:3001 + public-albums: http://${CONTAINER_IP}:3002 + cast: http://${CONTAINER_IP}:3004 + accounts: http://${CONTAINER_IP}:3001 key: encryption: $SECRET_ENC @@ -98,14 +119,25 @@ key: jwt: secret: $SECRET_JWT + +# SMTP not configured - verification codes will appear in logs +# To configure SMTP, add: +# smtp: +# host: your-smtp-server +# port: 587 +# username: your-username +# password: your-password +# email: noreply@yourdomain.com EOF msg_ok "Created museum.yaml" msg_info "Building Web Applications" +# Get container IP address +CONTAINER_IP=$(hostname -I | awk '{print $1}') cd /opt/ente/web $STD yarn install -export NEXT_PUBLIC_ENTE_ENDPOINT=http://localhost:8080 -export NEXT_PUBLIC_ENTE_ALBUMS_ENDPOINT=http://localhost:3002 +export NEXT_PUBLIC_ENTE_ENDPOINT=http://${CONTAINER_IP}:8080 +export NEXT_PUBLIC_ENTE_ALBUMS_ENDPOINT=http://${CONTAINER_IP}:3002 $STD yarn build $STD yarn build:accounts $STD yarn build:auth @@ -115,6 +147,29 @@ cp -r apps/photos/out /var/www/ente/apps/photos cp -r apps/accounts/out /var/www/ente/apps/accounts cp -r apps/auth/out /var/www/ente/apps/auth cp -r apps/cast/out /var/www/ente/apps/cast + +# Save build configuration for future rebuilds +cat </opt/ente/rebuild-frontend.sh +#!/usr/bin/env bash +# Rebuild Ente frontend with current IP +CONTAINER_IP=\$(hostname -I | awk '{print \$1}') +echo "Building frontend with IP: \$CONTAINER_IP" +cd /opt/ente/web +export NEXT_PUBLIC_ENTE_ENDPOINT=http://\${CONTAINER_IP}:8080 +export NEXT_PUBLIC_ENTE_ALBUMS_ENDPOINT=http://\${CONTAINER_IP}:3002 +yarn build +yarn build:accounts +yarn build:auth +yarn build:cast +rm -rf /var/www/ente/apps/* +cp -r apps/photos/out /var/www/ente/apps/photos +cp -r apps/accounts/out /var/www/ente/apps/accounts +cp -r apps/auth/out /var/www/ente/apps/auth +cp -r apps/cast/out /var/www/ente/apps/cast +systemctl reload caddy +echo "Frontend rebuilt successfully!" +REBUILD_EOF +chmod +x /opt/ente/rebuild-frontend.sh msg_ok "Built Web Applications" msg_info "Creating Museum Service" @@ -134,32 +189,96 @@ EOF systemctl enable -q --now ente-museum msg_ok "Created Museum Service" +msg_info "Installing Ente CLI" +ENTE_CLI_VERSION=$(curl -s https://api.github.com/repos/ente-io/ente/releases | jq -r '[.[] | select(.tag_name | startswith("cli-v"))][0].tag_name') +if [ -n "$ENTE_CLI_VERSION" ]; then + ENTE_CLI_URL="https://github.com/ente-io/ente/releases/download/${ENTE_CLI_VERSION}/ente-${ENTE_CLI_VERSION#cli-}-linux-amd64.tar.gz" + $STD curl -fsSL "$ENTE_CLI_URL" -o /tmp/ente-cli.tar.gz + $STD tar -xzf /tmp/ente-cli.tar.gz -C /usr/local/bin + chmod +x /usr/local/bin/ente + rm /tmp/ente-cli.tar.gz + msg_ok "Installed Ente CLI ($ENTE_CLI_VERSION)" +else + msg_warn "Could not determine latest Ente CLI version, skipping CLI installation" +fi + msg_info "Configuring Caddy" +CONTAINER_IP=$(hostname -I | awk '{print $1}') cat </etc/caddy/Caddyfile +# Ente Photos - Main Application :3000 { root * /var/www/ente/apps/photos file_server try_files {path} {path}.html /index.html + + header { + Access-Control-Allow-Origin * + Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" + Access-Control-Allow-Headers * + } } + +# Ente Accounts :3001 { root * /var/www/ente/apps/accounts file_server try_files {path} {path}.html /index.html + + header { + Access-Control-Allow-Origin * + Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" + Access-Control-Allow-Headers * + } } + +# Public Albums :3002 { root * /var/www/ente/apps/photos file_server try_files {path} {path}.html /index.html + + header { + Access-Control-Allow-Origin * + Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" + Access-Control-Allow-Headers * + } } + +# Auth :3003 { root * /var/www/ente/apps/auth file_server try_files {path} {path}.html /index.html + + header { + Access-Control-Allow-Origin * + Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" + Access-Control-Allow-Headers * + } } + +# Cast :3004 { root * /var/www/ente/apps/cast file_server try_files {path} {path}.html /index.html + + header { + Access-Control-Allow-Origin * + Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" + Access-Control-Allow-Headers * + } +} + +# Museum API Proxy +:8080 { + reverse_proxy localhost:8080 + + header { + Access-Control-Allow-Origin * + Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" + Access-Control-Allow-Headers * + } } EOF systemctl reload caddy @@ -168,7 +287,65 @@ msg_ok "Configured Caddy" motd_ssh customize +msg_info "Creating helper scripts" +# Create verification code finder script +cat <<'HELPER_EOF' >/usr/local/bin/ente-get-verification +#!/usr/bin/env bash +echo "Searching for verification codes in museum logs..." +journalctl -u ente-museum --no-pager | grep -i "verification\|verify\|code" | tail -20 +HELPER_EOF +chmod +x /usr/local/bin/ente-get-verification + +# Create subscription upgrade helper +cat <<'HELPER_EOF' >/usr/local/bin/ente-upgrade-subscription +#!/usr/bin/env bash +if [ -z "$1" ]; then + echo "Usage: ente-upgrade-subscription " + echo "Example: ente-upgrade-subscription user@example.com" + exit 1 +fi +EMAIL="$1" +echo "Upgrading subscription for: $EMAIL" +ente admin update-subscription -a "$EMAIL" -u "$EMAIL" --no-limit +HELPER_EOF +chmod +x /usr/local/bin/ente-upgrade-subscription + +msg_ok "Created helper scripts" + msg_info "Cleaning up" $STD apt-get -y autoremove $STD apt-get -y autoclean msg_ok "Cleaned" + +# Final setup summary +CONTAINER_IP=$(hostname -I | awk '{print $1}') +echo -e "\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo -e " ${GN}Ente Installation Complete!${CL}" +echo -e "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo -e "\n${BL}Access URLs:${CL}" +echo -e " Photos: http://${CONTAINER_IP}:3000" +echo -e " Accounts: http://${CONTAINER_IP}:3001" +echo -e " Auth: http://${CONTAINER_IP}:3003" +echo -e " API: http://${CONTAINER_IP}:8080" +echo -e "\n${YW}⚠️ Important Post-Installation Steps:${CL}" +echo -e "\n${BL}1. Create your first account:${CL}" +echo -e " • Open http://${CONTAINER_IP}:3000 in your browser" +echo -e " • Click 'Sign Up' and create an account" +echo -e "\n${BL}2. Verify your email (required):${CL}" +echo -e " • Run: ${GN}ente-get-verification${CL}" +echo -e " • Look for the verification code in the output" +echo -e " • Enter the code in the web UI to complete registration" +echo -e "\n${BL}3. Remove storage limit:${CL}" +echo -e " • After email verification is complete" +echo -e " • Run: ${GN}ente-upgrade-subscription your@email.com${CL}" +echo -e " • This removes the 10GB limit" +echo -e "\n${BL}4. If IP changes:${CL}" +echo -e " • Run: ${GN}/opt/ente/rebuild-frontend.sh${CL}" +echo -e " • This rebuilds the frontend with the new IP" +echo -e "\n${YW}Known Limitations:${CL}" +echo -e " • Email verification requires checking logs (no SMTP configured)" +echo -e " • Account creation must be done manually via web UI" +echo -e " • Subscription upgrade requires CLI after account creation" +echo -e " • Frontend must be rebuilt if container IP changes" +echo -e "\n${BL}Credentials saved to:${CL} ~/ente.creds" +echo -e "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n" From b9bfbba7ae53c58f46b30ec3a7d26c67a2264059 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 27 Oct 2025 10:39:42 +0100 Subject: [PATCH 1570/1733] Update dispatcharr.sh --- ct/dispatcharr.sh | 99 ++++++++++++++++++++++------------------------- 1 file changed, 47 insertions(+), 52 deletions(-) diff --git a/ct/dispatcharr.sh b/ct/dispatcharr.sh index de33d7928..57bfcbd07 100644 --- a/ct/dispatcharr.sh +++ b/ct/dispatcharr.sh @@ -30,80 +30,75 @@ function update_script() { exit fi - RELEASE=$(curl -fsSL https://api.github.com/repos/Dispatcharr/Dispatcharr/releases/latest | jq -r '.tag_name' | sed 's/^v//') - if [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]] || [[ ! -f /opt/${APP}_version.txt ]]; then - msg_ok "Starting update" - APP_DIR="/opt/dispatcharr" - APP_USER="dispatcharr" - APP_GROUP="dispatcharr" + setup_uv + NODE_VERSION="24" setup_nodejs - msg_info "Stopping $APP" + if check_for_gh_release "Dispatcharr" "Dispatcharr/Dispatcharr"; then + msg_info "Stopping Services" systemctl stop dispatcharr-celery systemctl stop dispatcharr-celerybeat systemctl stop dispatcharr-daphne systemctl stop dispatcharr - msg_ok "Stopped $APP" + msg_ok "Stopped Services" msg_info "Creating Backup" - BACKUP_FILE="/opt/dispatcharr_$(date +%F).tar.gz" - msg_info "Source and Database backup" - set -o allexport - source /etc/$APP_NAME/$APP_NAME.env - set +o allexport - PGPASSWORD=$POSTGRES_PASSWORD pg_dump -U $POSTGRES_USER -h $POSTGRES_HOST $POSTGRES_DB >/opt/$POSTGRES_DB-$(date +%F).sql - $STD tar -czf "$BACKUP_FILE" /opt/dispatcharr /opt/Dispatcharr_version.txt /opt/$POSTGRES_DB-$(date +%F).sql &>/dev/null - msg_ok "Backup Created" + BACKUP_FILE="/opt/dispatcharr_backup_$(date +%F_%H-%M-%S).tar.gz" + if [[ -f /opt/dispatcharr/.env ]]; then + cp /opt/dispatcharr/.env /tmp/dispatcharr.env.backup + fi + if [[ -f /opt/dispatcharr/.env ]]; then + set -o allexport + source /opt/dispatcharr/.env + set +o allexport + if [[ -n "$POSTGRES_DB" ]] && [[ -n "$POSTGRES_USER" ]] && [[ -n "$POSTGRES_PASSWORD" ]]; then + PGPASSWORD=$POSTGRES_PASSWORD pg_dump -U $POSTGRES_USER -h ${POSTGRES_HOST:-localhost} $POSTGRES_DB >/tmp/dispatcharr_db_$(date +%F).sql + msg_info "Database backup created" + fi + fi + $STD tar -czf "$BACKUP_FILE" -C /opt dispatcharr /tmp/dispatcharr_db_*.sql 2>/dev/null || true + msg_ok "Backup created: $BACKUP_FILE" - msg_info "Updating $APP to v${RELEASE}" - rm -rf /opt/dispatcharr - fetch_and_deploy_gh_release "dispatcharr" "Dispatcharr/Dispatcharr" - chown -R "$APP_USER:$APP_GROUP" "$APP_DIR" - sed -i 's/program\[\x27channel_id\x27\]/program["channel_id"]/g' "${APP_DIR}/apps/output/views.py" + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "dispatcharr" "Dispatcharr/Dispatcharr" - msg_ok "Dispatcharr Updated to $RELEASE" - - msg_info "Creating Python Virtual Environment" - cd $APP_DIR - python3 -m venv env - source env/bin/activate - $STD pip install --upgrade pip - $STD pip install -r requirements.txt - $STD pip install gunicorn - ln -sf /usr/bin/ffmpeg $APP_DIR/env/bin/ffmpeg - msg_ok "Python Environment Setup" + msg_info "Updating Dispatcharr Backend" + if [[ -f /tmp/dispatcharr.env.backup ]]; then + mv /tmp/dispatcharr.env.backup /opt/dispatcharr/.env + msg_info "Restored environment configuration" + fi + cd /opt/dispatcharr || exit + $STD uv venv + $STD uv pip install -r requirements.txt --index-strategy unsafe-best-match + $STD uv pip install gunicorn gevent celery redis daphne + msg_ok "Updated Dispatcharr Backend" msg_info "Building Frontend" - cd $APP_DIR/frontend + cd /opt/dispatcharr/frontend || exit $STD npm install --legacy-peer-deps $STD npm run build msg_ok "Built Frontend" msg_info "Running Django Migrations" - cd $APP_DIR - source env/bin/activate - set -o allexport - source /etc/$APP_NAME/$APP_NAME.env - set +o allexport - $STD python manage.py migrate --noinput - $STD python manage.py collectstatic --noinput + cd /opt/dispatcharr || exit + if [[ -f .env ]]; then + set -o allexport + source .env + set +o allexport + fi + $STD uv run python manage.py migrate --noinput + $STD uv run python manage.py collectstatic --noinput msg_ok "Migrations Complete" - msg_info "Starting $APP" + msg_info "Starting Services" + systemctl start dispatcharr systemctl start dispatcharr-celery systemctl start dispatcharr-celerybeat systemctl start dispatcharr-daphne - systemctl start dispatcharr - msg_ok "Started $APP" - echo "${RELEASE}" >"/opt/${APP}_version.txt" + msg_ok "Started Services" - msg_info "Cleaning Up" - rm -rf /opt/$POSTGRES_DB-$(date +%F).sql - msg_ok "Cleanup Completed" - - msg_ok "Update Successful, Backup saved to $BACKUP_FILE" - - else - msg_ok "No update required. ${APP} is already at v${RELEASE}" + msg_info "Cleaning up" + rm -f /tmp/dispatcharr_db_*.sql + msg_ok "Cleanup completed" + msg_ok "Update Successfully!" fi exit } From f849b4996a0aebdfb4ade24749471b18302e791a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 27 Oct 2025 10:47:01 +0100 Subject: [PATCH 1571/1733] Update ente-install.sh --- install/ente-install.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/install/ente-install.sh b/install/ente-install.sh index 9d8d1fcdb..52cd3300c 100644 --- a/install/ente-install.sh +++ b/install/ente-install.sh @@ -83,9 +83,9 @@ $STD go build cmd/museum/main.go msg_ok "Built Museum" msg_info "Generating Secrets" -SECRET_ENC=$($STD go run tools/gen-random-keys/main.go | grep "encryption" | awk '{print $2}') -SECRET_HASH=$($STD go run tools/gen-random-keys/main.go | grep "hash" | awk '{print $2}') -SECRET_JWT=$($STD go run tools/gen-random-keys/main.go | grep "jwt" | awk '{print $2}') +SECRET_ENC=$(go run tools/gen-random-keys/main.go 2>/dev/null | grep "encryption" | awk '{print $2}') +SECRET_HASH=$(go run tools/gen-random-keys/main.go 2>/dev/null | grep "hash" | awk '{print $2}') +SECRET_JWT=$(go run tools/gen-random-keys/main.go 2>/dev/null | grep "jwt" | awk '{print $2}') msg_ok "Generated Secrets" msg_info "Creating museum.yaml" From 2a4cb846f8c525be43975daa28ecc4ed32c12ae7 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 27 Oct 2025 10:48:39 +0100 Subject: [PATCH 1572/1733] Update dispatcharr.sh --- ct/dispatcharr.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ct/dispatcharr.sh b/ct/dispatcharr.sh index 57bfcbd07..6b76fec57 100644 --- a/ct/dispatcharr.sh +++ b/ct/dispatcharr.sh @@ -66,6 +66,7 @@ function update_script() { msg_info "Restored environment configuration" fi cd /opt/dispatcharr || exit + rm -rf .venv $STD uv venv $STD uv pip install -r requirements.txt --index-strategy unsafe-best-match $STD uv pip install gunicorn gevent celery redis daphne From 500df0f2f34f797e891ca4257870423ecac2ab19 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 27 Oct 2025 10:51:32 +0100 Subject: [PATCH 1573/1733] Update dispatcharr.sh --- ct/dispatcharr.sh | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/ct/dispatcharr.sh b/ct/dispatcharr.sh index 6b76fec57..91113904b 100644 --- a/ct/dispatcharr.sh +++ b/ct/dispatcharr.sh @@ -46,6 +46,20 @@ function update_script() { if [[ -f /opt/dispatcharr/.env ]]; then cp /opt/dispatcharr/.env /tmp/dispatcharr.env.backup fi + + if [[ -f /opt/dispatcharr/start-gunicorn.sh ]]; then + cp /opt/dispatcharr/start-gunicorn.sh /tmp/start-gunicorn.sh.backup + fi + if [[ -f /opt/dispatcharr/start-celery.sh ]]; then + cp /opt/dispatcharr/start-celery.sh /tmp/start-celery.sh.backup + fi + if [[ -f /opt/dispatcharr/start-celerybeat.sh ]]; then + cp /opt/dispatcharr/start-celerybeat.sh /tmp/start-celerybeat.sh.backup + fi + if [[ -f /opt/dispatcharr/start-daphne.sh ]]; then + cp /opt/dispatcharr/start-daphne.sh /tmp/start-daphne.sh.backup + fi + if [[ -f /opt/dispatcharr/.env ]]; then set -o allexport source /opt/dispatcharr/.env @@ -65,6 +79,21 @@ function update_script() { mv /tmp/dispatcharr.env.backup /opt/dispatcharr/.env msg_info "Restored environment configuration" fi + + # Restore service scripts + if [[ -f /tmp/start-gunicorn.sh.backup ]]; then + mv /tmp/start-gunicorn.sh.backup /opt/dispatcharr/start-gunicorn.sh + fi + if [[ -f /tmp/start-celery.sh.backup ]]; then + mv /tmp/start-celery.sh.backup /opt/dispatcharr/start-celery.sh + fi + if [[ -f /tmp/start-celerybeat.sh.backup ]]; then + mv /tmp/start-celerybeat.sh.backup /opt/dispatcharr/start-celerybeat.sh + fi + if [[ -f /tmp/start-daphne.sh.backup ]]; then + mv /tmp/start-daphne.sh.backup /opt/dispatcharr/start-daphne.sh + fi + cd /opt/dispatcharr || exit rm -rf .venv $STD uv venv From 1e40a810183c5b10412db2885d5837fde9df7bbe Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 27 Oct 2025 09:52:04 +0000 Subject: [PATCH 1574/1733] Update .app files --- ct/headers/alpine-garage | 6 ++++++ ct/headers/garage | 6 ++++++ 2 files changed, 12 insertions(+) create mode 100644 ct/headers/alpine-garage create mode 100644 ct/headers/garage diff --git a/ct/headers/alpine-garage b/ct/headers/alpine-garage new file mode 100644 index 000000000..c14c5aaa0 --- /dev/null +++ b/ct/headers/alpine-garage @@ -0,0 +1,6 @@ + ___ __ _ ______ + / | / /___ (_)___ ___ / ____/___ __________ _____ ____ + / /| | / / __ \/ / __ \/ _ \______/ / __/ __ `/ ___/ __ `/ __ `/ _ \ + / ___ |/ / /_/ / / / / / __/_____/ /_/ / /_/ / / / /_/ / /_/ / __/ +/_/ |_/_/ .___/_/_/ /_/\___/ \____/\__,_/_/ \__,_/\__, /\___/ + /_/ /____/ diff --git a/ct/headers/garage b/ct/headers/garage new file mode 100644 index 000000000..fb0adb2cd --- /dev/null +++ b/ct/headers/garage @@ -0,0 +1,6 @@ + ______ + / ____/___ __________ _____ ____ + / / __/ __ `/ ___/ __ `/ __ `/ _ \ +/ /_/ / /_/ / / / /_/ / /_/ / __/ +\____/\__,_/_/ \__,_/\__, /\___/ + /____/ From 00dd7cb5746057fff330716730d4e146ca6f91d6 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 27 Oct 2025 11:03:10 +0100 Subject: [PATCH 1575/1733] Update dispatcharr-install.sh --- install/dispatcharr-install.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/install/dispatcharr-install.sh b/install/dispatcharr-install.sh index 09529d68d..9e35bfe5c 100644 --- a/install/dispatcharr-install.sh +++ b/install/dispatcharr-install.sh @@ -39,13 +39,13 @@ $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCO $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" - -cat <~/dispatcharr.creds -Dispatcharr-Credentials -Dispatcharr Database Name: $DB_NAME -Dispatcharr Database User: $DB_USER -Dispatcharr Database Password: $DB_PASS -EOF +{ + echo "Dispatcharr Credentials" + echo "Database Name: $DB_NAME" + echo "Database User: $DB_USER" + echo "Database Password: $DB_PASS" + echo "" +} >>~/dispatcharr.creds msg_ok "Created PostgreSQL Database" fetch_and_deploy_gh_release "dispatcharr" "Dispatcharr/Dispatcharr" From 1c14bbe7c79b92e7115f71d37c7735d88e3670e4 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 27 Oct 2025 12:04:44 +0100 Subject: [PATCH 1576/1733] Refactor Ente CLI installation and cleanup steps Moved Ente CLI installation to use fetch_and_deploy_gh_release for consistency and removed the previous manual installation block. Also updated cleanup commands to use 'apt' instead of 'apt-get'. --- install/ente-install.sh | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/install/ente-install.sh b/install/ente-install.sh index 52cd3300c..c0fe3697f 100644 --- a/install/ente-install.sh +++ b/install/ente-install.sh @@ -27,7 +27,9 @@ msg_ok "Installed Dependencies" PG_VERSION="17" setup_postgresql setup_go NODE_VERSION="24" NODE_MODULE="yarn" setup_nodejs +ENTE_CLI_VERSION=$(curl -s https://api.github.com/repos/ente-io/ente/releases | jq -r '[.[] | select(.tag_name | startswith("cli-v"))][0].tag_name') fetch_and_deploy_gh_release "ente" "ente-io/ente" "tarball" "latest" "/opt/ente" +fetch_and_deploy_gh_release "ente" "ente-io/ente" "tarball" "$ENTE_CLI_VERSION" "/usr/local/bin/ente" "ente-cli-$ENTE_CLI_VERSION-linux-amd64.tar.gz" msg_info "Setting up PostgreSQL" DB_NAME="ente_db" @@ -189,19 +191,6 @@ EOF systemctl enable -q --now ente-museum msg_ok "Created Museum Service" -msg_info "Installing Ente CLI" -ENTE_CLI_VERSION=$(curl -s https://api.github.com/repos/ente-io/ente/releases | jq -r '[.[] | select(.tag_name | startswith("cli-v"))][0].tag_name') -if [ -n "$ENTE_CLI_VERSION" ]; then - ENTE_CLI_URL="https://github.com/ente-io/ente/releases/download/${ENTE_CLI_VERSION}/ente-${ENTE_CLI_VERSION#cli-}-linux-amd64.tar.gz" - $STD curl -fsSL "$ENTE_CLI_URL" -o /tmp/ente-cli.tar.gz - $STD tar -xzf /tmp/ente-cli.tar.gz -C /usr/local/bin - chmod +x /usr/local/bin/ente - rm /tmp/ente-cli.tar.gz - msg_ok "Installed Ente CLI ($ENTE_CLI_VERSION)" -else - msg_warn "Could not determine latest Ente CLI version, skipping CLI installation" -fi - msg_info "Configuring Caddy" CONTAINER_IP=$(hostname -I | awk '{print $1}') cat </etc/caddy/Caddyfile @@ -288,7 +277,6 @@ motd_ssh customize msg_info "Creating helper scripts" -# Create verification code finder script cat <<'HELPER_EOF' >/usr/local/bin/ente-get-verification #!/usr/bin/env bash echo "Searching for verification codes in museum logs..." @@ -296,7 +284,6 @@ journalctl -u ente-museum --no-pager | grep -i "verification\|verify\|code" | ta HELPER_EOF chmod +x /usr/local/bin/ente-get-verification -# Create subscription upgrade helper cat <<'HELPER_EOF' >/usr/local/bin/ente-upgrade-subscription #!/usr/bin/env bash if [ -z "$1" ]; then @@ -313,8 +300,8 @@ chmod +x /usr/local/bin/ente-upgrade-subscription msg_ok "Created helper scripts" msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean +$STD apt -y autoremove +$STD apt -y autoclean msg_ok "Cleaned" # Final setup summary From 3467f91992998c3a25af855fcecde1a2f65493d0 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 28 Oct 2025 13:18:37 +0100 Subject: [PATCH 1577/1733] cleanup and new script --- ct/alpine-garage.sh | 65 ------- ct/dispatcharr.sh | 143 -------------- ct/garage.sh | 65 ------- ct/hanko.sh | 44 ----- ct/patchmon.sh | 79 -------- ct/reitti.sh | 54 ++++++ frontend/public/json/garage.json | 59 ------ frontend/public/json/hanko.json | 35 ---- frontend/public/json/patchmon.json | 35 ---- install/alpine-garage-install.sh | 84 --------- install/deferred/hanko-install.sh | 79 -------- install/dispatcharr-install.sh | 266 -------------------------- install/garage-install.sh | 71 ------- install/patchmon-install.sh | 289 ----------------------------- install/reitti-install.sh | 152 +++++++++++++++ 15 files changed, 206 insertions(+), 1314 deletions(-) delete mode 100644 ct/alpine-garage.sh delete mode 100644 ct/dispatcharr.sh delete mode 100644 ct/garage.sh delete mode 100644 ct/hanko.sh delete mode 100644 ct/patchmon.sh create mode 100644 ct/reitti.sh delete mode 100644 frontend/public/json/garage.json delete mode 100644 frontend/public/json/hanko.json delete mode 100644 frontend/public/json/patchmon.json delete mode 100644 install/alpine-garage-install.sh delete mode 100644 install/deferred/hanko-install.sh delete mode 100644 install/dispatcharr-install.sh delete mode 100644 install/garage-install.sh delete mode 100644 install/patchmon-install.sh create mode 100644 install/reitti-install.sh diff --git a/ct/alpine-garage.sh b/ct/alpine-garage.sh deleted file mode 100644 index ecf981770..000000000 --- a/ct/alpine-garage.sh +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://alpinelinux.org/ - -APP="Alpine-Garage" -var_tags="${var_tags:-alpine;object-storage}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-512}" -var_disk="${var_disk:-3}" -var_os="${var_os:-alpine}" -var_version="${var_version:-3.22}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - if [[ ! -f /usr/local/bin/garage ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - GITEA_RELEASE=$(curl -fsSL https://api.github.com/repos/deuxfleurs-org/garage/tags | jq -r '.[0].name') - if [[ "${GITEA_RELEASE}" != "$(cat ~/.garage 2>/dev/null)" ]] || [[ ! -f ~/.garage ]]; then - msg_info "Stopping Service" - rc-service garage stop || true - msg_ok "Stopped Service" - - msg_info "Backing Up Data" - cp /usr/local/bin/garage /usr/local/bin/garage.old 2>/dev/null || true - cp /etc/garage.toml /etc/garage.toml.bak 2>/dev/null || true - msg_ok "Backed Up Data" - - msg_info "Updating Garage" - curl -fsSL "https://garagehq.deuxfleurs.fr/_releases/${GITEA_RELEASE}/x86_64-unknown-linux-musl/garage" -o /usr/local/bin/garage - chmod +x /usr/local/bin/garage - echo "${GITEA_RELEASE}" > ~/.garage - msg_ok "Updated Garage" - - msg_info "Starting Service" - rc-service garage start || rc-service garage restart - msg_ok "Started Service" - msg_ok "Update Successfully!" - else - msg_ok "No update required. Garage is already at ${GITEA_RELEASE}" - fi - exit -} - - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" - diff --git a/ct/dispatcharr.sh b/ct/dispatcharr.sh deleted file mode 100644 index 91113904b..000000000 --- a/ct/dispatcharr.sh +++ /dev/null @@ -1,143 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: ekke85 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/Dispatcharr/Dispatcharr - -APP="Dispatcharr" -APP_NAME=${APP,,} -var_tags="${var_tags:-media;arr}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-8}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - - if [[ ! -d "/opt/dispatcharr" ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - setup_uv - NODE_VERSION="24" setup_nodejs - - if check_for_gh_release "Dispatcharr" "Dispatcharr/Dispatcharr"; then - msg_info "Stopping Services" - systemctl stop dispatcharr-celery - systemctl stop dispatcharr-celerybeat - systemctl stop dispatcharr-daphne - systemctl stop dispatcharr - msg_ok "Stopped Services" - - msg_info "Creating Backup" - BACKUP_FILE="/opt/dispatcharr_backup_$(date +%F_%H-%M-%S).tar.gz" - if [[ -f /opt/dispatcharr/.env ]]; then - cp /opt/dispatcharr/.env /tmp/dispatcharr.env.backup - fi - - if [[ -f /opt/dispatcharr/start-gunicorn.sh ]]; then - cp /opt/dispatcharr/start-gunicorn.sh /tmp/start-gunicorn.sh.backup - fi - if [[ -f /opt/dispatcharr/start-celery.sh ]]; then - cp /opt/dispatcharr/start-celery.sh /tmp/start-celery.sh.backup - fi - if [[ -f /opt/dispatcharr/start-celerybeat.sh ]]; then - cp /opt/dispatcharr/start-celerybeat.sh /tmp/start-celerybeat.sh.backup - fi - if [[ -f /opt/dispatcharr/start-daphne.sh ]]; then - cp /opt/dispatcharr/start-daphne.sh /tmp/start-daphne.sh.backup - fi - - if [[ -f /opt/dispatcharr/.env ]]; then - set -o allexport - source /opt/dispatcharr/.env - set +o allexport - if [[ -n "$POSTGRES_DB" ]] && [[ -n "$POSTGRES_USER" ]] && [[ -n "$POSTGRES_PASSWORD" ]]; then - PGPASSWORD=$POSTGRES_PASSWORD pg_dump -U $POSTGRES_USER -h ${POSTGRES_HOST:-localhost} $POSTGRES_DB >/tmp/dispatcharr_db_$(date +%F).sql - msg_info "Database backup created" - fi - fi - $STD tar -czf "$BACKUP_FILE" -C /opt dispatcharr /tmp/dispatcharr_db_*.sql 2>/dev/null || true - msg_ok "Backup created: $BACKUP_FILE" - - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "dispatcharr" "Dispatcharr/Dispatcharr" - - msg_info "Updating Dispatcharr Backend" - if [[ -f /tmp/dispatcharr.env.backup ]]; then - mv /tmp/dispatcharr.env.backup /opt/dispatcharr/.env - msg_info "Restored environment configuration" - fi - - # Restore service scripts - if [[ -f /tmp/start-gunicorn.sh.backup ]]; then - mv /tmp/start-gunicorn.sh.backup /opt/dispatcharr/start-gunicorn.sh - fi - if [[ -f /tmp/start-celery.sh.backup ]]; then - mv /tmp/start-celery.sh.backup /opt/dispatcharr/start-celery.sh - fi - if [[ -f /tmp/start-celerybeat.sh.backup ]]; then - mv /tmp/start-celerybeat.sh.backup /opt/dispatcharr/start-celerybeat.sh - fi - if [[ -f /tmp/start-daphne.sh.backup ]]; then - mv /tmp/start-daphne.sh.backup /opt/dispatcharr/start-daphne.sh - fi - - cd /opt/dispatcharr || exit - rm -rf .venv - $STD uv venv - $STD uv pip install -r requirements.txt --index-strategy unsafe-best-match - $STD uv pip install gunicorn gevent celery redis daphne - msg_ok "Updated Dispatcharr Backend" - - msg_info "Building Frontend" - cd /opt/dispatcharr/frontend || exit - $STD npm install --legacy-peer-deps - $STD npm run build - msg_ok "Built Frontend" - - msg_info "Running Django Migrations" - cd /opt/dispatcharr || exit - if [[ -f .env ]]; then - set -o allexport - source .env - set +o allexport - fi - $STD uv run python manage.py migrate --noinput - $STD uv run python manage.py collectstatic --noinput - msg_ok "Migrations Complete" - - msg_info "Starting Services" - systemctl start dispatcharr - systemctl start dispatcharr-celery - systemctl start dispatcharr-celerybeat - systemctl start dispatcharr-daphne - msg_ok "Started Services" - - msg_info "Cleaning up" - rm -f /tmp/dispatcharr_db_*.sql - msg_ok "Cleanup completed" - msg_ok "Update Successfully!" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" diff --git a/ct/garage.sh b/ct/garage.sh deleted file mode 100644 index a43859bb7..000000000 --- a/ct/garage.sh +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: MickLesk (CanbiZ) -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://garagehq.deuxfleurs.fr/ - -APP="Garage" -var_tags="${var_tags:-object-storage}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-512}" -var_disk="${var_disk:-3}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -f /usr/local/bin/garage ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - GITEA_RELEASE=$(curl -fsSL https://api.github.com/repos/deuxfleurs-org/garage/tags | jq -r '.[0].name') - if [[ "${GITEA_RELEASE}" != "$(cat ~/.garage 2>/dev/null)" ]] || [[ ! -f ~/.garage ]]; then - msg_info "Stopping Service" - systemctl stop garage - msg_ok "Stopped Service" - - msg_info "Backing Up Data" - cp /usr/local/bin/garage /usr/local/bin/garage.old 2>/dev/null || true - cp /etc/garage.toml /etc/garage.toml.bak 2>/dev/null || true - msg_ok "Backed Up Data" - - msg_info "Updating Garage" - curl -fsSL "https://garagehq.deuxfleurs.fr/_releases/${GITEA_RELEASE}/x86_64-unknown-linux-musl/garage" -o /usr/local/bin/garage - chmod +x /usr/local/bin/garage - echo "${GITEA_RELEASE}" > ~/.garage - msg_ok "Updated Garage" - - msg_info "Starting Service" - systemctl start garage - msg_ok "Started Service" - msg_ok "Update Successfully!" - else - msg_ok "No update required. Garage is already at ${GITEA_RELEASE}" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" - diff --git a/ct/hanko.sh b/ct/hanko.sh deleted file mode 100644 index de3079c26..000000000 --- a/ct/hanko.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://www.debian.org/ - -APP="Hanko" -var_tags="${var_tags:-os}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-1024}" -var_disk="${var_disk:-4}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /var ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - msg_info "Updating $APP LXC" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "Updated $APP LXC" - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8000${CL}" diff --git a/ct/patchmon.sh b/ct/patchmon.sh deleted file mode 100644 index 0080df89c..000000000 --- a/ct/patchmon.sh +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: vhsdream -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/PatchMon/PatchMon - -APP="PatchMon" -APP_NAME=${APP,,} -var_tags="${var_tags:-monitoring}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-4}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - - if [[ ! -d "/opt/patchmon" ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - NODE_VERSION="24" setup_nodejs - - if check_for_gh_release "PatchMon" "PatchMon/PatchMon"; then - - msg_info "Stopping $APP" - systemctl stop patchmon-server - msg_ok "Stopped $APP" - - msg_info "Creating Backup" - cp /opt/patchmon/backend/.env /opt/backend.env - cp /opt/patchmon/frontend/.env /opt/frontend.env - msg_ok "Backup Created" - - rm -rf /opt/patchmon - fetch_and_deploy_gh_release "PatchMon" "PatchMon/PatchMon" "tarball" "latest" "/opt/patchmon" - - msg_info "Updating ${APP}" - cd /opt/patchmon - export NODE_ENV=production - $STD npm install --no-audit --no-fund --no-save --ignore-scripts - cd /opt/patchmon/backend - $STD npm install --no-audit --no-fund --no-save --ignore-scripts - cd /opt/patchmon/frontend - $STD npm install --include=dev --no-audit --no-fund --no-save --ignore-scripts - $STD npm run build - cd /opt/patchmon/backend - mv /opt/backend.env /opt/patchmon/backend/.env - mv /opt/frontend.env /opt/patchmon/frontend/.env - $STD npx prisma migrate deploy - $STD npx prisma generate - msg_ok "Updated ${APP}" - - msg_info "Starting $APP" - systemctl start patchmon-server - msg_ok "Started $APP" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" diff --git a/ct/reitti.sh b/ct/reitti.sh new file mode 100644 index 000000000..11bbdd8fd --- /dev/null +++ b/ct/reitti.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: madelyn (DysfunctionalProgramming) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/dedicatedcode/reitti + +APP="Reitti" +var_tags="${var_tags:-location-tracker}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -f /opt/reitti/reitti.jar ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + if check_for_gh_release "reitti" "dedicatedcode/reitti"; then + msg_info "Stopping Service" + systemctl stop reitti + msg_ok "Stopped Service" + + rm -f /opt/reitti/reitti.jar + USE_ORIGINAL_FILENAME="true" fetch_and_deploy_gh_release "reitti" "dedicatedcode/reitti" "singlefile" "latest" "/opt/reitti" "reitti-app.jar" + mv /opt/reitti/reitti-*.jar /opt/reitti/reitti.jar + + msg_info "Starting Service" + systemctl start reitti + msg_ok "Started Service" + msg_ok "Updated Successfully" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:25600${CL}" diff --git a/frontend/public/json/garage.json b/frontend/public/json/garage.json deleted file mode 100644 index d7a407392..000000000 --- a/frontend/public/json/garage.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "name": "Garage", - "slug": "garage", - "categories": [ - 8 - ], - "date_created": "2025-10-27", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 3900, - "documentation": "https://garagehq.deuxfleurs.fr/documentation/quick-start/", - "website": "https://garagehq.deuxfleurs.fr/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/garage.webp", - "config_path": "/etc/garage.toml", - "description": "Garage is a lightweight, self-hosted, S3-compatible object storage service built for distributed environments. It is designed to be simple, efficient, and easy to deploy across multiple nodes.", - "install_methods": [ - { - "type": "default", - "script": "ct/garage.sh", - "resources": { - "cpu": 1, - "ram": 512, - "hdd": 3, - "os": "debian", - "version": "13" - } - }, - { - "type": "alpine", - "script": "ct/alpine-garage.sh", - "resources": { - "cpu": 1, - "ram": 512, - "hdd": 3, - "os": "alpine", - "version": "3.22" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "The Garage configuration file is located at `/etc/garage.toml`. You can edit RPC and API bindings, tokens, and data directories there.", - "type": "info" - }, - { - "text": "Admin API runs by default on port `3903`, S3 API on port `3900`, Web UI on `3902`. Adjust firewall rules accordingly.", - "type": "warning" - }, - { - "text": "To view your generated tokens and RPC secret, check `~/garage.creds` after installation.", - "type": "info" - } - ] -} diff --git a/frontend/public/json/hanko.json b/frontend/public/json/hanko.json deleted file mode 100644 index d8628ad87..000000000 --- a/frontend/public/json/hanko.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "Hanko", - "slug": "hanko", - "categories": [ - 21 - ], - "date_created": "2025-07-02", - "type": "ct", - "updateable": true, - "privileged": false, - "config_path": "/opt/hanko/.env", - "interface_port": 3000, - "documentation": "https://docs.hanko.io/", - "website": "https://hanko.io/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/hanko.svg", - "description": "Hanko is an open-source authentication solution providing passkey-first login with support for WebAuthn/FIDO2, biometrics and modern identity flows. Easy to self-host and integrate via API or widget.", - "install_methods": [ - { - "type": "default", - "script": "ct/hanko.sh", - "resources": { - "cpu": 1, - "ram": 1024, - "hdd": 2, - "os": "Debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] -} diff --git a/frontend/public/json/patchmon.json b/frontend/public/json/patchmon.json deleted file mode 100644 index 9b78f66f6..000000000 --- a/frontend/public/json/patchmon.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "PatchMon", - "slug": "patchmon", - "categories": [ - 9 - ], - "date_created": "2025-10-23", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 3399, - "documentation": "https://docs.patchmon.net", - "website": "https://patchmon.net", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/patchmon.webp", - "config_path": "/opt/patchmon/backend/.env, /opt/patchmon/frontend/.env", - "description": "Monitor Linux patches across all your hosts with real-time visibility, security update tracking, and comprehensive package management.", - "install_methods": [ - { - "type": "default", - "script": "ct/patchmon.sh", - "resources": { - "cpu": 2, - "ram": 2048, - "hdd": 4, - "os": "debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] -} diff --git a/install/alpine-garage-install.sh b/install/alpine-garage-install.sh deleted file mode 100644 index c82f3e03c..000000000 --- a/install/alpine-garage-install.sh +++ /dev/null @@ -1,84 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: MickLesk (CanbiZ) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://garagehq.deuxfleurs.fr/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apk add -y openssl -msg_ok "Installed Dependencies" - -GITEA_RELEASE=$(curl -s https://api.github.com/repos/deuxfleurs-org/garage/tags | jq -r '.[0].name') -curl -fsSL "https://garagehq.deuxfleurs.fr/_releases/${GITEA_RELEASE}/x86_64-unknown-linux-musl/garage" -o /usr/local/bin/garage -chmod +x /usr/local/bin/garage -mkdir -p /var/lib/garage/{data,meta,snapshots} -mkdir -p /etc/garage -RPC_SECRET=$(openssl rand -hex 64 | cut -c1-64) -ADMIN_TOKEN=$(openssl rand -base64 32) -METRICS_TOKEN=$(openssl rand -base64 32) -{ - echo "Garage Tokens and Secrets" - echo "RPC Secret: $RPC_SECRET" - echo "Admin Token: $ADMIN_TOKEN" - echo "Metrics Token: $METRICS_TOKEN" -} >~/garage.creds -echo $GITEA_RELEASE >>~/.garage -cat </etc/garage.toml -metadata_dir = "/var/lib/garage/meta" -data_dir = "/var/lib/garage/data" -db_engine = "sqlite" -replication_factor = 1 - -rpc_bind_addr = "0.0.0.0:3901" -rpc_public_addr = "127.0.0.1:3901" -rpc_secret = "${RPC_SECRET}" - -[s3_api] -s3_region = "garage" -api_bind_addr = "0.0.0.0:3900" -root_domain = ".s3.garage" - -[s3_web] -bind_addr = "0.0.0.0:3902" -root_domain = ".web.garage" -index = "index.html" - -[k2v_api] -api_bind_addr = "0.0.0.0:3904" - -[admin] -api_bind_addr = "0.0.0.0:3903" -admin_token = "${ADMIN_TOKEN}" -metrics_token = "${METRICS_TOKEN}" -EOF -msg_ok "Configured Garage" - -msg_info "Creating Service" -cat <<'EOF' >/etc/init.d/garage -#!/sbin/openrc-run -name="Garage Object Storage" -command="/usr/local/bin/garage" -command_args="server" -command_background="yes" -pidfile="/run/garage.pid" -depend() { - need net -} -EOF - -chmod +x /etc/init.d/garage -$STD rc-update add garage default -$STD rc-service garage restart || rc-service garage start -msg_ok "Service active" - -motd_ssh -customize diff --git a/install/deferred/hanko-install.sh b/install/deferred/hanko-install.sh deleted file mode 100644 index e1ec43c9d..000000000 --- a/install/deferred/hanko-install.sh +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: MickLesk (CanbiZ) -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://hanko.io/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -setup_yq -PG_VERSION="16" setup_postgresql -NODE_VERSION=22 NODE_MODULE="yarn@latest,npm@latest" setup_nodejs - -msg_info "Setting up PostgreSQL Database" -DB_NAME=hanko -DB_USER=hanko -DB_PASS="$(openssl rand -base64 18 | cut -c1-13)" -APP_SECRET=$(openssl rand -base64 32) -$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" -$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC'" -{ - echo "Hanko-Credentials" - echo "Hanko Database User: $DB_USER" - echo "Hanko Database Password: $DB_PASS" - echo "Hanko Database Name: $DB_NAME" -} >>~/hanko.creds -msg_ok "Set up PostgreSQL Database" - -msg_info "Setup Hanko" -fetch_and_deploy_gh_release "hanko" "teamhanko/hanko" "prebuild" "latest" "/opt/hanko" "hanko_Linux_x86_64.tar.gz" -curl -fsSL https://raw.githubusercontent.com/teamhanko/hanko/refs/heads/main/backend/config/config.yaml -o /opt/hanko/config.yaml -env DB_USER="$DB_USER" DB_PASS="$DB_PASS" APP_SECRET="$APP_SECRET" \ - yq eval ' - .database.user = strenv(DB_USER) | - .database.password = strenv(DB_PASS) | - .database.host = "localhost" | - .database.port = "5432" | - .database.dialect = "postgres" | - .app.secret = strenv(APP_SECRET) -' -i /opt/hanko/config.yaml -$STD /opt/hanko/hanko --config /opt/hanko/config.yaml migrate up -yarn add @teamhanko/hanko-elements -msg_ok "Setup Hanko" - -msg_info "Setup Service" -cat </etc/systemd/system/hanko.service -[Unit] -Description=Hanko Service -After=network.target - -[Service] -Type=simple -ExecStart=/opt/hanko/hanko serve all --config /opt/hanko/config.yaml -Restart=on-failure -RestartSec=5 - -[Install] -WantedBy=multi-user.target -EOF - -systemctl enable -q --now hanko -msg_ok "Service Setup" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" diff --git a/install/dispatcharr-install.sh b/install/dispatcharr-install.sh deleted file mode 100644 index 9e35bfe5c..000000000 --- a/install/dispatcharr-install.sh +++ /dev/null @@ -1,266 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: ekke85 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/Dispatcharr/Dispatcharr - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt install -y \ - build-essential \ - gcc \ - python3-dev \ - libpq-dev \ - nginx \ - redis-server \ - ffmpeg \ - procps \ - streamlink -msg_ok "Installed Dependencies" - -setup_uv -NODE_VERSION="24" setup_nodejs -PG_VERSION="16" setup_postgresql - -msg_info "Creating PostgreSQL Database" -DB_NAME=dispatcharr_db -DB_USER=dispatcharr_usr -DB_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" -$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" -$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" -{ - echo "Dispatcharr Credentials" - echo "Database Name: $DB_NAME" - echo "Database User: $DB_USER" - echo "Database Password: $DB_PASS" - echo "" -} >>~/dispatcharr.creds -msg_ok "Created PostgreSQL Database" - -fetch_and_deploy_gh_release "dispatcharr" "Dispatcharr/Dispatcharr" - -msg_info "Installing Python Dependencies with uv" -cd /opt/dispatcharr || exit - -$STD uv venv -$STD uv pip install -r requirements.txt --index-strategy unsafe-best-match -$STD uv pip install gunicorn gevent celery redis daphne -msg_ok "Installed Python Dependencies" - -msg_info "Configuring Dispatcharr" -export DATABASE_URL="postgresql://${DB_USER}:${DB_PASS}@localhost:5432/${DB_NAME}" -export POSTGRES_DB=$DB_NAME -export POSTGRES_USER=$DB_USER -export POSTGRES_PASSWORD=$DB_PASS -export POSTGRES_HOST=localhost -$STD uv run python manage.py migrate --noinput -$STD uv run python manage.py collectstatic --noinput -cat </opt/dispatcharr/.env -DATABASE_URL=postgresql://${DB_USER}:${DB_PASS}@localhost:5432/${DB_NAME} -POSTGRES_DB=$DB_NAME -POSTGRES_USER=$DB_USER -POSTGRES_PASSWORD=$DB_PASS -POSTGRES_HOST=localhost -CELERY_BROKER_URL=redis://localhost:6379/0 -EOF -cd /opt/dispatcharr/frontend || exit -$STD npm install --legacy-peer-deps -$STD npm run build -msg_ok "Configured Dispatcharr" - -msg_info "Configuring Nginx" -cat </etc/nginx/sites-available/dispatcharr.conf -server { - listen 80; - server_name _; - - # Serve static assets with correct MIME types - location /assets/ { - alias /opt/dispatcharr/frontend/dist/assets/; - expires 30d; - add_header Cache-Control "public, immutable"; - - # Explicitly set MIME types for webpack-built assets - types { - text/javascript js; - text/css css; - image/png png; - image/svg+xml svg svgz; - font/woff2 woff2; - font/woff woff; - font/ttf ttf; - } - } - - location /static/ { - alias /opt/dispatcharr/static/; - expires 30d; - add_header Cache-Control "public, immutable"; - } - - location /media/ { - alias /opt/dispatcharr/media/; - } - - location /ws/ { - proxy_pass http://127.0.0.1:8001; - proxy_http_version 1.1; - proxy_set_header Upgrade \$http_upgrade; - proxy_set_header Connection "Upgrade"; - proxy_set_header Host \$host; - proxy_set_header X-Real-IP \$remote_addr; - proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto \$scheme; - } - - # All other requests proxy to Gunicorn - location / { - include proxy_params; - proxy_pass http://127.0.0.1:5656; - } -} -EOF - -ln -sf /etc/nginx/sites-available/dispatcharr.conf /etc/nginx/sites-enabled/dispatcharr.conf -rm -f /etc/nginx/sites-enabled/default -systemctl restart nginx -msg_ok "Configured Nginx" - -msg_info "Creating Services" -cat </opt/dispatcharr/start-gunicorn.sh -#!/usr/bin/env bash -cd /opt/dispatcharr -set -a -source .env -set +a -exec uv run gunicorn \\ - --workers=4 \\ - --worker-class=gevent \\ - --timeout=300 \\ - --bind 0.0.0.0:5656 \\ - dispatcharr.wsgi:application -EOF -chmod +x /opt/dispatcharr/start-gunicorn.sh - -cat </opt/dispatcharr/start-celery.sh -#!/usr/bin/env bash -cd /opt/dispatcharr -set -a -source .env -set +a -exec uv run celery -A dispatcharr worker -l info -c 4 -EOF -chmod +x /opt/dispatcharr/start-celery.sh - -cat </opt/dispatcharr/start-celerybeat.sh -#!/usr/bin/env bash -cd /opt/dispatcharr -set -a -source .env -set +a -exec uv run celery -A dispatcharr beat -l info -EOF -chmod +x /opt/dispatcharr/start-celerybeat.sh - -cat </opt/dispatcharr/start-daphne.sh -#!/usr/bin/env bash -cd /opt/dispatcharr -set -a -source .env -set +a -exec uv run daphne -b 0.0.0.0 -p 8001 dispatcharr.asgi:application -EOF -chmod +x /opt/dispatcharr/start-daphne.sh - -cat </etc/systemd/system/dispatcharr.service -[Unit] -Description=Dispatcharr Web Server -After=network.target postgresql.service redis-server.service - -[Service] -Type=simple -WorkingDirectory=/opt/dispatcharr -ExecStart=/opt/dispatcharr/start-gunicorn.sh -Restart=on-failure -RestartSec=10 -User=root - -[Install] -WantedBy=multi-user.target -EOF - -cat </etc/systemd/system/dispatcharr-celery.service -[Unit] -Description=Dispatcharr Celery Worker -After=network.target redis-server.service -Requires=dispatcharr.service - -[Service] -Type=simple -WorkingDirectory=/opt/dispatcharr -ExecStart=/opt/dispatcharr/start-celery.sh -Restart=on-failure -RestartSec=10 -User=root - -[Install] -WantedBy=multi-user.target -EOF - -cat </etc/systemd/system/dispatcharr-celerybeat.service -[Unit] -Description=Dispatcharr Celery Beat Scheduler -After=network.target redis-server.service -Requires=dispatcharr.service - -[Service] -Type=simple -WorkingDirectory=/opt/dispatcharr -ExecStart=/opt/dispatcharr/start-celerybeat.sh -Restart=on-failure -RestartSec=10 -User=root - -[Install] -WantedBy=multi-user.target -EOF - -cat </etc/systemd/system/dispatcharr-daphne.service -[Unit] -Description=Dispatcharr WebSocket Server (Daphne) -After=network.target -Requires=dispatcharr.service - -[Service] -Type=simple -WorkingDirectory=/opt/dispatcharr -ExecStart=/opt/dispatcharr/start-daphne.sh -Restart=on-failure -RestartSec=10 -User=root - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now dispatcharr dispatcharr-celery dispatcharr-celerybeat dispatcharr-daphne -msg_ok "Created Services" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt -y autoremove -$STD apt -y autoclean -$STD apt -y clean -msg_ok "Cleaned" diff --git a/install/garage-install.sh b/install/garage-install.sh deleted file mode 100644 index b8f1a39ec..000000000 --- a/install/garage-install.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: Test Suite for tools.func -# License: MIT -# https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Purpose: Run comprehensive test suite for all setup_* functions from tools.func - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Setup Garage" -GITEA_RELEASE=$(curl -s https://api.github.com/repos/deuxfleurs-org/garage/tags | jq -r '.[0].name') -curl -fsSL "https://garagehq.deuxfleurs.fr/_releases/${GITEA_RELEASE}/x86_64-unknown-linux-musl/garage" -o /usr/local/bin/garage -chmod +x /usr/local/bin/garage -mkdir -p /var/lib/garage/{data,meta,snapshots} -mkdir -p /etc/garage -RPC_SECRET=$(openssl rand -hex 32) -ADMIN_TOKEN=$(openssl rand -base64 32) -METRICS_TOKEN=$(openssl rand -base64 32) -{ - echo "Garage Tokens and Secrets" - echo "RPC Secret: $RPC_SECRET" - echo "Admin Token: $ADMIN_TOKEN" - echo "Metrics Token: $METRICS_TOKEN" -} >>~/garage.creds -echo $GITEA_RELEASE >>~/.garage -cat </etc/garage.toml -metadata_dir = "/var/lib/garage/meta" -data_dir = "/var/lib/garage/data" -db_engine = "sqlite" -replication_factor = 1 - -rpc_bind_addr = "[::]:3901" -rpc_public_addr = "127.0.0.1:3901" -rpc_secret = "${RPC_SECRET}" - -[s3_api] -s3_region = "garage" -api_bind_addr = "[::]:3900" -root_domain = ".s3.garage.localhost" - -[s3_web] -bind_addr = "[::]:3902" -root_domain = ".web.garage.localhost" -index = "index.html" - -[k2v_api] -api_bind_addr = "[::]:3904" - -[admin] -api_bind_addr = "[::]:3903" -admin_token = "${ADMIN_TOKEN}" -metrics_token = "${METRICS_TOKEN}" -EOF -msg_ok "Set up Garage" - - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt -y autoremove -$STD apt -y autoclean -$STD apt -y clean -msg_ok "Cleaned" diff --git a/install/patchmon-install.sh b/install/patchmon-install.sh deleted file mode 100644 index 209b03e1c..000000000 --- a/install/patchmon-install.sh +++ /dev/null @@ -1,289 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: vhsdream -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/PatcMmon/PatchMon - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt install -y \ - build-essential \ - gcc \ - nginx \ - redis-server -msg_ok "Installed Dependencies" - -NODE_VERSION="24" setup_nodejs -PG_VERSION="17" setup_postgresql - -msg_info "Creating PostgreSQL Database" -DB_NAME=patchmon_db -DB_USER=patchmon_usr -DB_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" -$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" -$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" -$STD sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER;" - -cat <~/patchmon.creds -PatchMon Credentials -PatchMon Database Name: $DB_NAME -PatchMon Database User: $DB_USER -PatchMon Database Password: $DB_PASS -EOF -msg_ok "Created PostgreSQL Database" - -fetch_and_deploy_gh_release "PatchMon" "PatchMon/PatchMon" "tarball" "latest" "/opt/patchmon" - -msg_info "Configuring PatchMon" -cd /opt/patchmon -export NODE_ENV=production -$STD npm install --no-audit --no-fund --no-save --ignore-scripts -cd /opt/patchmon/backend -$STD npm install --no-audit --no-fund --no-save --ignore-scripts -cd /opt/patchmon/frontend -$STD npm install --include=dev --no-audit --no-fund --no-save --ignore-scripts -$STD npm run build - -JWT_SECRET="$(openssl rand -base64 64 | tr -d "=+/" | cut -c1-50)" -LOCAL_IP="$(hostname -I | awk '{print $1}')" -cat </opt/patchmon/backend/.env -# Database Configuration -DATABASE_URL="postgresql://$DB_USER:$DB_PASS@localhost:5432/$DB_NAME" -PY_THRESHOLD=3M_DB_CONN_MAX_ATTEMPTS=30 -PM_DB_CONN_WAIT_INTERVAL=2 - -# JWT Configuration -JWT_SECRET="$JWT_SECRET" -JWT_EXPIRES_IN=1h -JWT_REFRESH_EXPIRES_IN=7d - -# Server Configuration -PORT=3399 -NODE_ENV=production - -# API Configuration -API_VERSION=v1 - -# CORS Configuration -CORS_ORIGIN="http://$LOCAL_IP" - -# Session Configuration -SESSION_INACTIVITY_TIMEOUT_MINUTES=30 - -# User Configuration -DEFAULT_USER_ROLE=user - -# Rate Limiting (times in milliseconds) -RATE_LIMIT_WINDOW_MS=900000 -RATE_LIMIT_MAX=5000 -AUTH_RATE_LIMIT_WINDOW_MS=600000 -AUTH_RATE_LIMIT_MAX=500 -AGENT_RATE_LIMIT_WINDOW_MS=60000 -AGENT_RATE_LIMIT_MAX=1000 - -# Redis Configuration -REDIS_HOST=localhost -REDIS_PORT=6379 - -# Logging -LOG_LEVEL=info -ENABLE_LOGGING=true - -# TFA Configuration -TFA_REMEMBER_ME_EXPIRES_IN=30d -TFA_MAX_REMEMBER_SESSIONS=5 -TFA_SUSPICIOUS_ACTIVITY_THRESHOLD=3 -EOF - -cat </opt/patchmon/frontend/.env -VITE_API_URL=http://$LOCAL_IP/api/v1 -VITE_APP_NAME=PatchMon -VITE_APP_VERSION=1.3.0 -EOF - -cd /opt/patchmon/backend -$STD npx prisma migrate deploy -$STD npx prisma generate -msg_ok "Configured PatchMon" - -msg_info "Configuring Nginx" -cat </etc/nginx/sites-available/patchmon.conf -server { - listen 80; - server_name $LOCAL_IP; - - # Security headers - add_header X-Frame-Options DENY always; - add_header X-Content-Type-Options nosniff always; - add_header X-XSS-Protection "1; mode=block" always; - add_header Referrer-Policy "strict-origin-when-cross-origin" always; - - # Frontend - location / { - root /opt/patchmon/frontend/dist; - try_files \$uri \$uri/ /index.html; - } - - # Bull Board proxy - location /bullboard { - proxy_pass http://127.0.0.1:3399; - proxy_http_version 1.1; - proxy_set_header Upgrade \$http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_set_header Host \$host; - proxy_set_header X-Real-IP \$remote_addr; - proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto \$scheme; - proxy_set_header X-Forwarded-Host \$host; - proxy_set_header Cookie \$http_cookie; - proxy_cache_bypass \$http_upgrade; - proxy_read_timeout 300s; - proxy_connect_timeout 75s; - - # Enable cookie passthrough - proxy_pass_header Set-Cookie; - proxy_cookie_path / /; - - # Preserve original client IP - proxy_set_header X-Original-Forwarded-For \$http_x_forwarded_for; - if (\$request_method = 'OPTIONS') { - return 204; - } - } - - # API proxy - location /api/ { - proxy_pass http://127.0.0.1:3399; - proxy_http_version 1.1; - proxy_set_header Upgrade \$http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_set_header Host \$host; - proxy_set_header X-Real-IP \$remote_addr; - proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto \$scheme; - proxy_cache_bypass \$http_upgrade; - proxy_read_timeout 300s; - proxy_connect_timeout 75s; - - # Preserve original client IP - proxy_set_header X-Original-Forwarded-For \$http_x_forwarded_for; - if (\$request_method = 'OPTIONS') { - return 204; - } - } - - # Static assets caching (exclude Bull Board assets) - location ~* ^/(?!bullboard).*\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { - root /opt/patchmon/frontend/dist; - expires 1y; - add_header Cache-Control "public, immutable"; - } - - # Health check endpoint - location /health { - proxy_pass http://127.0.0.1:3399/health; - access_log off; - } -} -EOF -ln -sf /etc/nginx/sites-available/patchmon.conf /etc/nginx/sites-enabled/ -rm -f /etc/nginx/sites-enabled/default -$STD nginx -t -systemctl restart nginx -msg_ok "Configured Nginx" - -msg_info "Creating service" -cat </etc/systemd/system/patchmon-server.service -[Unit] -Description=PatchMon Service -After=network.target postgresql.service - -[Service] -Type=simple -WorkingDirectory=/opt/patchmon/backend -ExecStart=/usr/bin/node src/server.js -Restart=always -RestartSec=10 -Environment=NODE_ENV=production -Environment=PATH=/usr/bin:/usr/local/bin -NoNewPrivileges=true -PrivateTmp=true -ProtectSystem=strict -ProtectHome=true -ReadWritePaths=/opt/patchmon - -[Install] -WantedBy=multi-user.target -EOF -systemctl enable -q --now patchmon-server -msg_ok "Created and started service" - -msg_info "Updating settings" -cat </opt/patchmon/backend/update-settings.js -const { PrismaClient } = require('@prisma/client'); -const { v4: uuidv4 } = require('uuid'); -const prisma = new PrismaClient(); - -async function updateSettings() { - try { - const existingSettings = await prisma.settings.findFirst(); - - const settingsData = { - id: uuidv4(), - server_url: 'http://$LOCAL_IP', - server_protocol: 'http', - server_host: '$LOCAL_IP', - server_port: 3399, - update_interval: 60, - auto_update: true, - signup_enabled: false, - ignore_ssl_self_signed: false, - updated_at: new Date() - }; - - if (existingSettings) { - // Update existing settings - await prisma.settings.update({ - where: { id: existingSettings.id }, - data: settingsData - }); - } else { - // Create new settings record - await prisma.settings.create({ - data: settingsData - }); - } - - console.log('✅ Database settings updated successfully'); - } catch (error) { - console.error('❌ Error updating settings:', error.message); - process.exit(1); - } finally { - await prisma.\$disconnect(); - } -} - -updateSettings(); -EOF - -cd /opt/patchmon/backend -$STD node update-settings.js -msg_ok "Settings updated successfully" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt -y autoremove -$STD apt -y autoclean -$STD apt -y clean -msg_ok "Cleaned" diff --git a/install/reitti-install.sh b/install/reitti-install.sh new file mode 100644 index 000000000..f0762a0d2 --- /dev/null +++ b/install/reitti-install.sh @@ -0,0 +1,152 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Test Suite for tools.func +# License: MIT +# https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Purpose: Run comprehensive test suite for all setup_* functions from tools.func + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +apt install -y \ + redis-server \ + rabbitmq-server \ + libpq-dev +msg_ok "Installed Dependencies" + +JAVA_VERSION="24" setup_java +PG_VERSION="17" PG_MODULES="postgis" setup_postgresql + +msg_info "Setting up PostgreSQL" +DB_NAME="reitti_db" +DB_USER="reitti" +DB_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" +$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" +$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" +$STD sudo -u postgres psql -d "$DB_NAME" -c "CREATE EXTENSION IF NOT EXISTS postgis;" +$STD sudo -u postgres psql -d "$DB_NAME" -c "CREATE EXTENSION IF NOT EXISTS postgis_topology;" +{ + echo "Reitti Credentials" + echo "Database Name: $DB_NAME" + echo "Database User: $DB_USER" + echo "Database Password: $DB_PASS" +} >>~/reitti.creds +msg_ok "PostgreSQL Setup Completed" + +msg_info "Configuring RabbitMQ" +RABBIT_USER="reitti" +RABBIT_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" +RABBIT_VHOST="/" +$STD rabbitmqctl add_user "$RABBIT_USER" "$RABBIT_PASS" +$STD rabbitmqctl add_vhost "$RABBIT_VHOST" +$STD rabbitmqctl set_permissions -p "$RABBIT_VHOST" "$RABBIT_USER" ".*" ".*" ".*" +$STD rabbitmqctl set_user_tags "$RABBIT_USER" administrator +{ + echo "" + echo "Reitti Credentials" + echo "RabbitMQ User: $RABBIT_USER" + echo "RabbitMQ Password: $RABBIT_PASS" +} >>~/reitti.creds +msg_ok "Configured RabbitMQ" + +USE_ORIGINAL_FILENAME="true" fetch_and_deploy_gh_release "reitti" "dedicatedcode/reitti" "singlefile" "latest" "/opt/reitti" "reitti-app.jar" +mv /opt/reitti/reitti-*.jar /opt/reitti/reitti.jar +USE_ORIGINAL_FILENAME="true" fetch_and_deploy_gh_release "photon" "komoot/photon" "singlefile" "latest" "/opt/photon" "photon*.jar" +mv /opt/photon/photon-*.jar /opt/photon/photon.jar + +msg_info "Create Configuration" +cat </opt/reitti/application.properties +# PostgreSQL Database Connection +spring.datasource.url=jdbc:postgresql://127.0.0.1:5432/$DB_NAME +spring.datasource.username=$DB_USER +spring.datasource.password=$DB_PASS +spring.datasource.driver-class-name=org.postgresql.Driver + +# Flyway Database Migrations +spring.flyway.enabled=true +spring.flyway.locations=classpath:db/migration +spring.flyway.baseline-on-migrate=true + +# RabbitMQ (Message Queue) +spring.rabbitmq.host=127.0.0.1 +spring.rabbitmq.port=5672 +spring.rabbitmq.username=$RABBIT_USER +spring.rabbitmq.password=$RABBIT_PASS + +# Redis (Cache) +spring.data.redis.host=127.0.0.1 +spring.data.redis.port=6379 + +# Server Port +server.port=8080 + +# Optional: Logging & Performance +logging.level.root=INFO +spring.jpa.hibernate.ddl-auto=none +spring.datasource.hikari.maximum-pool-size=10 + +# Photon (Geocoding) +PHOTON_BASE_URL=http://127.0.0.1:2322 +PROCESSING_WAIT_TIME=15 +PROCESSING_BATCH_SIZE=1000 +PROCESSING_WORKERS_PER_QUEUE=4-16 + +# Disable potentially dangerous features unless needed +DANGEROUS_LIFE=false +EOF + +msg_info "Creating Services" +cat </etc/systemd/system/reitti.service +[Unit] +Description=Reitti +After=syslog.target network.target + +[Service] +Type=simple +WorkingDirectory=/opt/reitti/ +Environment=LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu +ExecStart=/usr/bin/java --enable-native-access=ALL-UNNAMED -jar -Xmx2g reitti.jar +TimeoutStopSec=20 +KillMode=process +Restart=on-failure + +[Install] +WantedBy=multi-user.target +EOF + +cat </etc/systemd/system/photon.service +[Unit] +Description=Photon Geocoding Service +After=network.target + +[Service] +Type=simple +WorkingDirectory=/opt/photon +ExecStart=/usr/bin/java -Xmx2g -jar photon.jar +Restart=on-failure +TimeoutStopSec=20 + +[Install] +WantedBy=multi-user.target +EOF + +systemctl enable -q --now photon +msg_ok "Created Service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" From feeef5f376b3391ec45ee5d1d793e0ae85a485c3 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 28 Oct 2025 12:18:56 +0000 Subject: [PATCH 1578/1733] Update .app files --- ct/headers/alpine-garage | 6 ------ ct/headers/dispatcharr | 6 ------ ct/headers/garage | 6 ------ ct/headers/hanko | 6 ------ ct/headers/patchmon | 6 ------ ct/headers/reitti | 6 ++++++ 6 files changed, 6 insertions(+), 30 deletions(-) delete mode 100644 ct/headers/alpine-garage delete mode 100644 ct/headers/dispatcharr delete mode 100644 ct/headers/garage delete mode 100644 ct/headers/hanko delete mode 100644 ct/headers/patchmon create mode 100644 ct/headers/reitti diff --git a/ct/headers/alpine-garage b/ct/headers/alpine-garage deleted file mode 100644 index c14c5aaa0..000000000 --- a/ct/headers/alpine-garage +++ /dev/null @@ -1,6 +0,0 @@ - ___ __ _ ______ - / | / /___ (_)___ ___ / ____/___ __________ _____ ____ - / /| | / / __ \/ / __ \/ _ \______/ / __/ __ `/ ___/ __ `/ __ `/ _ \ - / ___ |/ / /_/ / / / / / __/_____/ /_/ / /_/ / / / /_/ / /_/ / __/ -/_/ |_/_/ .___/_/_/ /_/\___/ \____/\__,_/_/ \__,_/\__, /\___/ - /_/ /____/ diff --git a/ct/headers/dispatcharr b/ct/headers/dispatcharr deleted file mode 100644 index a8ad53965..000000000 --- a/ct/headers/dispatcharr +++ /dev/null @@ -1,6 +0,0 @@ - ____ _ __ __ - / __ \(_)________ ____ _/ /______/ /_ ____ ___________ - / / / / / ___/ __ \/ __ `/ __/ ___/ __ \/ __ `/ ___/ ___/ - / /_/ / (__ ) /_/ / /_/ / /_/ /__/ / / / /_/ / / / / -/_____/_/____/ .___/\__,_/\__/\___/_/ /_/\__,_/_/ /_/ - /_/ diff --git a/ct/headers/garage b/ct/headers/garage deleted file mode 100644 index fb0adb2cd..000000000 --- a/ct/headers/garage +++ /dev/null @@ -1,6 +0,0 @@ - ______ - / ____/___ __________ _____ ____ - / / __/ __ `/ ___/ __ `/ __ `/ _ \ -/ /_/ / /_/ / / / /_/ / /_/ / __/ -\____/\__,_/_/ \__,_/\__, /\___/ - /____/ diff --git a/ct/headers/hanko b/ct/headers/hanko deleted file mode 100644 index e823d45dc..000000000 --- a/ct/headers/hanko +++ /dev/null @@ -1,6 +0,0 @@ - __ __ __ - / / / /___ _____ / /______ - / /_/ / __ `/ __ \/ //_/ __ \ - / __ / /_/ / / / / ,< / /_/ / -/_/ /_/\__,_/_/ /_/_/|_|\____/ - diff --git a/ct/headers/patchmon b/ct/headers/patchmon deleted file mode 100644 index 87d928deb..000000000 --- a/ct/headers/patchmon +++ /dev/null @@ -1,6 +0,0 @@ - ____ __ __ __ ___ - / __ \____ _/ /______/ /_ / |/ /___ ____ - / /_/ / __ `/ __/ ___/ __ \/ /|_/ / __ \/ __ \ - / ____/ /_/ / /_/ /__/ / / / / / / /_/ / / / / -/_/ \__,_/\__/\___/_/ /_/_/ /_/\____/_/ /_/ - diff --git a/ct/headers/reitti b/ct/headers/reitti new file mode 100644 index 000000000..8e7627609 --- /dev/null +++ b/ct/headers/reitti @@ -0,0 +1,6 @@ + ____ _ __ __ _ + / __ \___ (_) /_/ /_(_) + / /_/ / _ \/ / __/ __/ / + / _, _/ __/ / /_/ /_/ / +/_/ |_|\___/_/\__/\__/_/ + From a4ba95e9cdb10c67f9f9c8973a8e2da2f02328cc Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 28 Oct 2025 13:28:31 +0100 Subject: [PATCH 1579/1733] reitti json --- frontend/public/json/reitti.json | 40 ++++++++++++++++++++++++++++++++ install/reitti-install.sh | 12 ++++++---- 2 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 frontend/public/json/reitti.json diff --git a/frontend/public/json/reitti.json b/frontend/public/json/reitti.json new file mode 100644 index 000000000..2921a5548 --- /dev/null +++ b/frontend/public/json/reitti.json @@ -0,0 +1,40 @@ +{ + "name": "Reitti", + "slug": "reitti", + "categories": [ + 21 + ], + "date_created": "2025-10-28", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 8080, + "documentation": "https://github.com/dedicatedcode/reitti", + "config_path": "/opt/reitti/application.properties", + "website": "https://www.dedicatedcode.com/projects/reitti/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/reitti.webp", + "description": "Reitti is a self-hosted location tracking and analysis platform that detects significant places, trip patterns, and integrates with OwnTracks, GPSLogger, and Immich. It uses PostgreSQL + PostGIS, RabbitMQ, Redis, and an optional Photon geocoder.", + "install_methods": [ + { + "type": "default", + "script": "ct/reitti.sh", + "resources": { + "cpu": 4, + "ram": 6144, + "hdd": 20, + "os": "Debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": "admin", + "password": "admin" + }, + "notes": [ + { + "text": "Photon Geocoder must be running at http://127.0.0.1:2322. The installer sets this up automatically using the Germany OpenSearch dataset.", + "type": "info" + } + ] +} diff --git a/install/reitti-install.sh b/install/reitti-install.sh index f0762a0d2..cabcac1f6 100644 --- a/install/reitti-install.sh +++ b/install/reitti-install.sh @@ -61,7 +61,7 @@ msg_ok "Configured RabbitMQ" USE_ORIGINAL_FILENAME="true" fetch_and_deploy_gh_release "reitti" "dedicatedcode/reitti" "singlefile" "latest" "/opt/reitti" "reitti-app.jar" mv /opt/reitti/reitti-*.jar /opt/reitti/reitti.jar -USE_ORIGINAL_FILENAME="true" fetch_and_deploy_gh_release "photon" "komoot/photon" "singlefile" "latest" "/opt/photon" "photon*.jar" +USE_ORIGINAL_FILENAME="true" fetch_and_deploy_gh_release "photon" "komoot/photon" "singlefile" "latest" "/opt/photon" "photon-0*.jar" mv /opt/photon/photon-*.jar /opt/photon/photon.jar msg_info "Create Configuration" @@ -124,15 +124,19 @@ Restart=on-failure WantedBy=multi-user.target EOF -cat </etc/systemd/system/photon.service +cat <<'EOF' >/etc/systemd/system/photon.service [Unit] -Description=Photon Geocoding Service +Description=Photon Geocoding Service (Germany, OpenSearch) After=network.target [Service] Type=simple WorkingDirectory=/opt/photon -ExecStart=/usr/bin/java -Xmx2g -jar photon.jar +ExecStart=/usr/bin/java -Xmx4g -jar photon.jar \ + -data-dir /opt/photon \ + -listen-port 2322 \ + -listen-ip 0.0.0.0 \ + -cors-any Restart=on-failure TimeoutStopSec=20 From bdf260be0760654b567e5cd97c30989225d972bf Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 28 Oct 2025 14:40:32 +0100 Subject: [PATCH 1580/1733] Update reitti-install.sh --- install/reitti-install.sh | 43 +++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/install/reitti-install.sh b/install/reitti-install.sh index cabcac1f6..9f001ad74 100644 --- a/install/reitti-install.sh +++ b/install/reitti-install.sh @@ -2,9 +2,7 @@ # Copyright (c) 2021-2025 community-scripts ORG # Author: Test Suite for tools.func -# License: MIT -# https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Purpose: Run comprehensive test suite for all setup_* functions from tools.func +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color @@ -15,10 +13,11 @@ network_check update_os msg_info "Installing Dependencies" -apt install -y \ +$STD apt install -y \ redis-server \ rabbitmq-server \ - libpq-dev + libpq-dev \ + zstd msg_ok "Installed Dependencies" JAVA_VERSION="24" setup_java @@ -109,7 +108,8 @@ msg_info "Creating Services" cat </etc/systemd/system/reitti.service [Unit] Description=Reitti -After=syslog.target network.target +After=network.target postgresql.service redis-server.service rabbitmq-server.service photon.service +Wants=postgresql.service redis-server.service rabbitmq-server.service photon.service [Service] Type=simple @@ -145,12 +145,39 @@ WantedBy=multi-user.target EOF systemctl enable -q --now photon +systemctl enable -q --now reitti msg_ok "Created Service" +read -rp "Would you like to setup sample data for Photon (Switzerland)? (y/n): " setup_sample +if [[ "$setup_sample" =~ ^[Yy]$ ]]; then + msg_info "Setup Sample Data for Photon (Switzerland) - Patience" + systemctl stop photon + PHOTON_DUMP_URL="$( + curl -fsSL https://download1.graphhopper.com/public/europe/switzerland-liechtenstein/ | + grep -A1 '>0\.7' | + grep -o 'https[^"]*photon-dump-switzerland-liechtenstein-[^"]*\.jsonl\.zst' | + head -n1 + )" + if [[ -z "$PHOTON_DUMP_URL" ]]; then + PHOTON_DUMP_URL="https://download1.graphhopper.com/public/europe/switzerland-liechtenstein/photon-dump-switzerland-liechtenstein-0.7-latest.jsonl.zst" + fi + mkdir -p /opt/photon + zstd --stdout -d /opt/photon/photon-dump-switzerland-liechtenstein.jsonl.zst | + java --enable-native-access=ALL-UNNAMED -Xmx4g -jar /opt/photon/photon.jar \ + -nominatim-import \ + -import-file - \ + -data-dir /opt/photon \ + -languages de,en,fr,it + curl -fsSL -o /opt/photon/photon-dump-switzerland-liechtenstein.jsonl.zst "$PHOTON_DUMP_URL" + msg_ok "Sample Data Setup Completed" +fi + motd_ssh customize msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean +rm -rf /opt/photon/photon-dump-*.jsonl.zst +$STD apt -y autoremove +$STD apt -y autoclean +$STD apt -y clean msg_ok "Cleaned" From cf814177a0451b40df34ff85b12b6b94cbc51ab8 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 28 Oct 2025 14:41:02 +0100 Subject: [PATCH 1581/1733] Update reitti-install.sh --- install/reitti-install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install/reitti-install.sh b/install/reitti-install.sh index 9f001ad74..a76b6cb4f 100644 --- a/install/reitti-install.sh +++ b/install/reitti-install.sh @@ -169,6 +169,7 @@ if [[ "$setup_sample" =~ ^[Yy]$ ]]; then -data-dir /opt/photon \ -languages de,en,fr,it curl -fsSL -o /opt/photon/photon-dump-switzerland-liechtenstein.jsonl.zst "$PHOTON_DUMP_URL" + systemctl start photon msg_ok "Sample Data Setup Completed" fi From 9f7a54dfb6ea7ac604b0af8b00c1dae90893db38 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 28 Oct 2025 14:41:32 +0100 Subject: [PATCH 1582/1733] Update reitti.sh --- ct/reitti.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ct/reitti.sh b/ct/reitti.sh index 11bbdd8fd..1460c7503 100644 --- a/ct/reitti.sh +++ b/ct/reitti.sh @@ -7,9 +7,9 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV APP="Reitti" var_tags="${var_tags:-location-tracker}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-4}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-4096}" +var_disk="${var_disk:-15}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" From ddcd37a41936b104119c82c73800987a8eaea874 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 28 Oct 2025 14:48:23 +0100 Subject: [PATCH 1583/1733] fixes --- ct/reitti.sh | 16 +++++++++++++++- misc/build.func | 2 +- misc/tools.func | 3 ++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/ct/reitti.sh b/ct/reitti.sh index 1460c7503..e0f9b7647 100644 --- a/ct/reitti.sh +++ b/ct/reitti.sh @@ -39,7 +39,21 @@ function update_script() { msg_info "Starting Service" systemctl start reitti msg_ok "Started Service" - msg_ok "Updated Successfully" + msg_ok "Updated Successfully!" + fi + if check_for_gh_release "photon" "dedicatedcode/reitti"; then + msg_info "Stopping Service" + systemctl stop photon + msg_ok "Stopped Service" + + rm -f /opt/photon/photon.jar + USE_ORIGINAL_FILENAME="true" fetch_and_deploy_gh_release "photon" "komoot/photon" "singlefile" "latest" "/opt/photon" "photon-0*.jar" + mv /opt/photon/photon-*.jar /opt/photon/photon.jar + + msg_info "Starting Service" + systemctl start photon + msg_ok "Started Service" + msg_ok "Updated Successfully!" fi exit } diff --git a/misc/build.func b/misc/build.func index b776fdb4b..01050c57c 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3012,7 +3012,7 @@ create_lxc_container() { pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)." #echo "[DEBUG] pveam available output (first 5 lines with .tar files):" - pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | head -5 | sed 's/^/ /' + #pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | head -5 | sed 's/^/ /' set +u mapfile -t ONLINE_TEMPLATES < <(pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | awk '{print $2}' | grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V 2>/dev/null || true) diff --git a/misc/tools.func b/misc/tools.func index 1914bc894..fb96ed0ee 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -2536,7 +2536,8 @@ function setup_java() { fi # Validate INSTALLED_VERSION is not empty if matched - if [[ -z "$INSTALLED_VERSION" && $(dpkg -l 2>/dev/null | grep -c "temurin-.*-jdk" || echo "0") -gt 0 ]]; then + local JDK_COUNT=$(dpkg -l 2>/dev/null | grep -c "temurin-.*-jdk" || echo "0") + if [[ -z "$INSTALLED_VERSION" && "$JDK_COUNT" -gt 0 ]]; then msg_warn "Found Temurin JDK but cannot determine version" INSTALLED_VERSION="0" fi From a5b4fdc3d7d7b33513f237e59eee65e1dafd96f2 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 28 Oct 2025 15:36:26 +0100 Subject: [PATCH 1584/1733] finalize --- ct/reitti.sh | 2 +- install/reitti-install.sh | 83 ++++++++++++--------------------------- 2 files changed, 26 insertions(+), 59 deletions(-) diff --git a/ct/reitti.sh b/ct/reitti.sh index e0f9b7647..b4d891984 100644 --- a/ct/reitti.sh +++ b/ct/reitti.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG -# Author: madelyn (DysfunctionalProgramming) +# Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/dedicatedcode/reitti diff --git a/install/reitti-install.sh b/install/reitti-install.sh index a76b6cb4f..0a7a1a121 100644 --- a/install/reitti-install.sh +++ b/install/reitti-install.sh @@ -1,8 +1,9 @@ #!/usr/bin/env bash # Copyright (c) 2021-2025 community-scripts ORG -# Author: Test Suite for tools.func +# Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://github.com/dedicatedcode/reitti source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color @@ -63,36 +64,25 @@ mv /opt/reitti/reitti-*.jar /opt/reitti/reitti.jar USE_ORIGINAL_FILENAME="true" fetch_and_deploy_gh_release "photon" "komoot/photon" "singlefile" "latest" "/opt/photon" "photon-0*.jar" mv /opt/photon/photon-*.jar /opt/photon/photon.jar -msg_info "Create Configuration" -cat </opt/reitti/application.properties -# PostgreSQL Database Connection -spring.datasource.url=jdbc:postgresql://127.0.0.1:5432/$DB_NAME -spring.datasource.username=$DB_USER -spring.datasource.password=$DB_PASS -spring.datasource.driver-class-name=org.postgresql.Driver +msg_info "Creating Reitti Environment (.env)" +cat </opt/reitti/.env +# PostgreSQL (PostGIS) +POSTGIS_HOST=127.0.0.1 +POSTGIS_PORT=5432 +POSTGIS_DB=$DB_NAME +POSTGIS_USER=$DB_USER +POSTGIS_PASSWORD=$DB_PASS -# Flyway Database Migrations -spring.flyway.enabled=true -spring.flyway.locations=classpath:db/migration -spring.flyway.baseline-on-migrate=true +# RabbitMQ +RABBITMQ_HOST=127.0.0.1 +RABBITMQ_PORT=5672 +RABBITMQ_USER=$RABBIT_USER +RABBITMQ_PASSWORD=$RABBIT_PASS +RABBITMQ_VHOST=/ -# RabbitMQ (Message Queue) -spring.rabbitmq.host=127.0.0.1 -spring.rabbitmq.port=5672 -spring.rabbitmq.username=$RABBIT_USER -spring.rabbitmq.password=$RABBIT_PASS - -# Redis (Cache) -spring.data.redis.host=127.0.0.1 -spring.data.redis.port=6379 - -# Server Port -server.port=8080 - -# Optional: Logging & Performance -logging.level.root=INFO -spring.jpa.hibernate.ddl-auto=none -spring.datasource.hikari.maximum-pool-size=10 +# Redis +REDIS_HOST=127.0.0.1 +REDIS_PORT=6379 # Photon (Geocoding) PHOTON_BASE_URL=http://127.0.0.1:2322 @@ -100,9 +90,12 @@ PROCESSING_WAIT_TIME=15 PROCESSING_BATCH_SIZE=1000 PROCESSING_WORKERS_PER_QUEUE=4-16 -# Disable potentially dangerous features unless needed +# General +SERVER_PORT=8080 +LOGGING_LEVEL=INFO DANGEROUS_LIFE=false EOF +msg_ok "Created .env for Reitti" msg_info "Creating Services" cat </etc/systemd/system/reitti.service @@ -114,7 +107,7 @@ Wants=postgresql.service redis-server.service rabbitmq-server.service photon.ser [Service] Type=simple WorkingDirectory=/opt/reitti/ -Environment=LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu +EnvironmentFile=/opt/reitti/.env ExecStart=/usr/bin/java --enable-native-access=ALL-UNNAMED -jar -Xmx2g reitti.jar TimeoutStopSec=20 KillMode=process @@ -146,38 +139,12 @@ EOF systemctl enable -q --now photon systemctl enable -q --now reitti -msg_ok "Created Service" - -read -rp "Would you like to setup sample data for Photon (Switzerland)? (y/n): " setup_sample -if [[ "$setup_sample" =~ ^[Yy]$ ]]; then - msg_info "Setup Sample Data for Photon (Switzerland) - Patience" - systemctl stop photon - PHOTON_DUMP_URL="$( - curl -fsSL https://download1.graphhopper.com/public/europe/switzerland-liechtenstein/ | - grep -A1 '>0\.7' | - grep -o 'https[^"]*photon-dump-switzerland-liechtenstein-[^"]*\.jsonl\.zst' | - head -n1 - )" - if [[ -z "$PHOTON_DUMP_URL" ]]; then - PHOTON_DUMP_URL="https://download1.graphhopper.com/public/europe/switzerland-liechtenstein/photon-dump-switzerland-liechtenstein-0.7-latest.jsonl.zst" - fi - mkdir -p /opt/photon - zstd --stdout -d /opt/photon/photon-dump-switzerland-liechtenstein.jsonl.zst | - java --enable-native-access=ALL-UNNAMED -Xmx4g -jar /opt/photon/photon.jar \ - -nominatim-import \ - -import-file - \ - -data-dir /opt/photon \ - -languages de,en,fr,it - curl -fsSL -o /opt/photon/photon-dump-switzerland-liechtenstein.jsonl.zst "$PHOTON_DUMP_URL" - systemctl start photon - msg_ok "Sample Data Setup Completed" -fi +msg_ok "Created Services" motd_ssh customize msg_info "Cleaning up" -rm -rf /opt/photon/photon-dump-*.jsonl.zst $STD apt -y autoremove $STD apt -y autoclean $STD apt -y clean From 4ebeb5fd357e9d72a81bd27ceff6079b5c9f03ec Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 28 Oct 2025 15:38:28 +0100 Subject: [PATCH 1585/1733] remove dispatcharr --- frontend/public/json/dispatcharr.json | 35 --------------------------- 1 file changed, 35 deletions(-) delete mode 100644 frontend/public/json/dispatcharr.json diff --git a/frontend/public/json/dispatcharr.json b/frontend/public/json/dispatcharr.json deleted file mode 100644 index 46abd5550..000000000 --- a/frontend/public/json/dispatcharr.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "Dispatcharr", - "slug": "dispatcharr", - "categories": [ - 14 - ], - "date_created": "2025-07-01", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 9191, - "documentation": "https://dispatcharr.github.io/Dispatcharr-Docs/", - "website": "https://dispatcharr.github.io/Dispatcharr-Docs/", - "logo": "https://raw.githubusercontent.com/Dispatcharr/Dispatcharr/refs/heads/main/frontend/src/images/logo.png", - "config_path": "", - "description": "Dispatcharr is an open-source powerhouse for managing IPTV streams and EPG data with elegance and control. Born from necessity and built with passion, it started as a personal project by OkinawaBoss and evolved with contributions from legends like dekzter, SergeantPanda and Bucatini.", - "install_methods": [ - { - "type": "default", - "script": "ct/dispatcharr.sh", - "resources": { - "cpu": 1, - "ram": 1024, - "hdd": 8, - "os": "debian", - "version": "12" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] -} From 73099fd88b195ff9239aed4765eef8d277f1335f Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 28 Oct 2025 15:40:17 +0100 Subject: [PATCH 1586/1733] change port --- ct/reitti.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/reitti.sh b/ct/reitti.sh index b4d891984..1025051cb 100644 --- a/ct/reitti.sh +++ b/ct/reitti.sh @@ -65,4 +65,4 @@ description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:25600${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" From 6053bea5818f5755e74005b861d5a7a838ecb1cc Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 28 Oct 2025 15:44:55 +0100 Subject: [PATCH 1587/1733] Update reitti.json --- frontend/public/json/reitti.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/public/json/reitti.json b/frontend/public/json/reitti.json index 2921a5548..864821ed3 100644 --- a/frontend/public/json/reitti.json +++ b/frontend/public/json/reitti.json @@ -22,8 +22,8 @@ "cpu": 4, "ram": 6144, "hdd": 20, - "os": "Debian", - "version": "12" + "os": "debian", + "version": "13" } } ], @@ -33,7 +33,7 @@ }, "notes": [ { - "text": "Photon Geocoder must be running at http://127.0.0.1:2322. The installer sets this up automatically using the Germany OpenSearch dataset.", + "text": "Photon Geocoder must be running at http://127.0.0.1:2322. The installer sets this up Photon automatically, but without sample data. (filesize is big).", "type": "info" } ] From d6b15f607ac9ae18e957072ceeb739387c76b973 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 28 Oct 2025 15:45:13 +0100 Subject: [PATCH 1588/1733] Update reitti.json --- frontend/public/json/reitti.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/reitti.json b/frontend/public/json/reitti.json index 864821ed3..e97428ca3 100644 --- a/frontend/public/json/reitti.json +++ b/frontend/public/json/reitti.json @@ -10,7 +10,7 @@ "privileged": false, "interface_port": 8080, "documentation": "https://github.com/dedicatedcode/reitti", - "config_path": "/opt/reitti/application.properties", + "config_path": "/opt/reitti/.env", "website": "https://www.dedicatedcode.com/projects/reitti/", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/reitti.webp", "description": "Reitti is a self-hosted location tracking and analysis platform that detects significant places, trip patterns, and integrates with OwnTracks, GPSLogger, and Immich. It uses PostgreSQL + PostGIS, RabbitMQ, Redis, and an optional Photon geocoder.", From 54085936b01986e472772c7ba6e2e2cc401778bd Mon Sep 17 00:00:00 2001 From: vhsdream Date: Tue, 28 Oct 2025 16:10:56 -0400 Subject: [PATCH 1589/1733] BentoPDF --- ct/bentopdf.sh | 64 ++++++++++++++++++++++++++++++ frontend/public/json/bentopdf.json | 35 ++++++++++++++++ install/bentopdf-install.sh | 63 +++++++++++++++++++++++++++++ 3 files changed, 162 insertions(+) create mode 100644 ct/bentopdf.sh create mode 100644 frontend/public/json/bentopdf.json create mode 100644 install/bentopdf-install.sh diff --git a/ct/bentopdf.sh b/ct/bentopdf.sh new file mode 100644 index 000000000..f178ebf69 --- /dev/null +++ b/ct/bentopdf.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: vhsdream +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/alam00000/bentopdf + +APP="BentoPDF" +var_tags="${var_tags:-pdf-editor}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/bentopdf ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + NODE_VERSION="24" setup_nodejs + + if check_for_gh_release "bentopdf" "alam00000/bentopdf"; then + msg_info "Stopping Service" + systemctl stop bentopdf + msg_ok "Stopped Service" + + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "bentopdf" "alam00000/bentopdf" "tarball" "latest" "/opt/bentopdf" + + msg_info "Updating BentoPDF" + cd /opt/bentopdf + $STD npm ci --no-audit --no-fund + $STD npm run build -- --mode production + cp -r /opt/bentopdf/dist/* /usr/share/nginx/html/ + cp /opt/bentopdf/nginx.conf /etc/nginx/nginx.conf + chown -R nginx:nginx {/usr/share/nginx/html,/etc/nginx/tmp,/etc/nginx/nginx.conf,/var/log/nginx} + msg_ok "Updated BentoPDF" + + msg_info "Starting Service" + systemctl start bentopdf + msg_ok "Started Service" + msg_ok "Updated Successfully!" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" diff --git a/frontend/public/json/bentopdf.json b/frontend/public/json/bentopdf.json new file mode 100644 index 000000000..c6ac09c5e --- /dev/null +++ b/frontend/public/json/bentopdf.json @@ -0,0 +1,35 @@ +{ + "name": "BentoPDF", + "slug": "bentopdf", + "categories": [ + 12 + ], + "date_created": "2025-10-30", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 8080, + "documentation": "https://github.com/alam00000/bentopdf", + "website": "https://www.bentopdf.com", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/bentopdf.webp", + "config_path": "/opt/patchmon/backend/.env, /opt/patchmon/frontend/.env", + "description": "A privacy-first, 100% client-side PDF Toolkit. No signups/accounts, works in the browser, online or offline.", + "install_methods": [ + { + "type": "default", + "script": "ct/bentopdf.sh", + "resources": { + "cpu": 1, + "ram": 2048, + "hdd": 4, + "os": "debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} diff --git a/install/bentopdf-install.sh b/install/bentopdf-install.sh new file mode 100644 index 000000000..d1ad3c38f --- /dev/null +++ b/install/bentopdf-install.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: vhsdream +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/alam00000/bentopdf + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt-get install -y \ + nginx +msg_ok "Installed Dependencies" + +NODE_VERSION="24" setup_nodejs +fetch_and_deploy_gh_release "bentopdf" "alam00000/bentopdf" "tarball" "latest" "/opt/bentopdf" + +msg_info "Setup BentoPDF" +cd /opt/bentopdf +$STD npm ci --no-audit --no-fund +$STD npm run build -- --mode production +cp -r /opt/bentopdf/dist/* /usr/share/nginx/html/ +cp /opt/bentopdf/nginx.conf /etc/nginx/nginx.conf +mkdir -p /etc/nginx/tmp +useradd -M -s /usr/sbin/nologin -r -d /usr/share/nginx/html nginx +chown -R nginx:nginx {/usr/share/nginx/html,/etc/nginx/tmp,/etc/nginx/nginx.conf,/var/log/nginx} +msg_ok "Setup BentoPDF" + +msg_info "Creating Service" +cat </etc/systemd/system/bentopdf.service +[Unit] +Description=BentoPDF Service +After=network.target + +[Service] +Type=simple +User=nginx +Group=nginx +ExecStart=/sbin/nginx -g "daemon off;" +Restart=always +RestartSec=10 + +[Install] +WantedBy=multi-user.target +EOF + +systemctl -q enable --now bentopdf +msg_ok "Created & started service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt-get -y autoremove +$STD apt-get -y autoclean +$STD apt-get -y clean +msg_ok "Cleaned" From df2619c12ad42e54686b5123e654bf66bf71d4da Mon Sep 17 00:00:00 2001 From: vhsdream Date: Tue, 28 Oct 2025 16:13:17 -0400 Subject: [PATCH 1590/1733] Remove config path from BentoPDF JSON --- frontend/public/json/bentopdf.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/json/bentopdf.json b/frontend/public/json/bentopdf.json index c6ac09c5e..2a09b34cf 100644 --- a/frontend/public/json/bentopdf.json +++ b/frontend/public/json/bentopdf.json @@ -12,7 +12,7 @@ "documentation": "https://github.com/alam00000/bentopdf", "website": "https://www.bentopdf.com", "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/bentopdf.webp", - "config_path": "/opt/patchmon/backend/.env, /opt/patchmon/frontend/.env", + "config_path": "", "description": "A privacy-first, 100% client-side PDF Toolkit. No signups/accounts, works in the browser, online or offline.", "install_methods": [ { From b2cf9467217cefdf131988af738e7e2ccf189437 Mon Sep 17 00:00:00 2001 From: vhsdream Date: Tue, 28 Oct 2025 16:32:37 -0400 Subject: [PATCH 1591/1733] BentoPDF: Use simple mode (we don't need all that branding!) --- ct/bentopdf.sh | 4 +--- install/bentopdf-install.sh | 16 +++------------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/ct/bentopdf.sh b/ct/bentopdf.sh index f178ebf69..0a9650efb 100644 --- a/ct/bentopdf.sh +++ b/ct/bentopdf.sh @@ -40,10 +40,8 @@ function update_script() { msg_info "Updating BentoPDF" cd /opt/bentopdf $STD npm ci --no-audit --no-fund + export SIMPLE_MODE=true $STD npm run build -- --mode production - cp -r /opt/bentopdf/dist/* /usr/share/nginx/html/ - cp /opt/bentopdf/nginx.conf /etc/nginx/nginx.conf - chown -R nginx:nginx {/usr/share/nginx/html,/etc/nginx/tmp,/etc/nginx/nginx.conf,/var/log/nginx} msg_ok "Updated BentoPDF" msg_info "Starting Service" diff --git a/install/bentopdf-install.sh b/install/bentopdf-install.sh index d1ad3c38f..f426200b2 100644 --- a/install/bentopdf-install.sh +++ b/install/bentopdf-install.sh @@ -13,23 +13,14 @@ setting_up_container network_check update_os -msg_info "Installing Dependencies" -$STD apt-get install -y \ - nginx -msg_ok "Installed Dependencies" - NODE_VERSION="24" setup_nodejs fetch_and_deploy_gh_release "bentopdf" "alam00000/bentopdf" "tarball" "latest" "/opt/bentopdf" msg_info "Setup BentoPDF" cd /opt/bentopdf $STD npm ci --no-audit --no-fund +export SIMPLE_MODE=true $STD npm run build -- --mode production -cp -r /opt/bentopdf/dist/* /usr/share/nginx/html/ -cp /opt/bentopdf/nginx.conf /etc/nginx/nginx.conf -mkdir -p /etc/nginx/tmp -useradd -M -s /usr/sbin/nologin -r -d /usr/share/nginx/html nginx -chown -R nginx:nginx {/usr/share/nginx/html,/etc/nginx/tmp,/etc/nginx/nginx.conf,/var/log/nginx} msg_ok "Setup BentoPDF" msg_info "Creating Service" @@ -40,9 +31,8 @@ After=network.target [Service] Type=simple -User=nginx -Group=nginx -ExecStart=/sbin/nginx -g "daemon off;" +WorkingDirectory=/opt/bentopdf +ExecStart=/usr/bin/npx serve dist -p 8080 Restart=always RestartSec=10 From 8e9b7f4df3fed3b3127a8213b30835acc644aba8 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 28 Oct 2025 20:32:54 +0000 Subject: [PATCH 1592/1733] Update .app files --- ct/headers/bentopdf | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/bentopdf diff --git a/ct/headers/bentopdf b/ct/headers/bentopdf new file mode 100644 index 000000000..692eff64b --- /dev/null +++ b/ct/headers/bentopdf @@ -0,0 +1,6 @@ + ____ __ ____ ____ ______ + / __ )___ ____ / /_____ / __ \/ __ \/ ____/ + / __ / _ \/ __ \/ __/ __ \/ /_/ / / / / /_ + / /_/ / __/ / / / /_/ /_/ / ____/ /_/ / __/ +/_____/\___/_/ /_/\__/\____/_/ /_____/_/ + From b981a868cda791b86b73c86a29ac629480b569b5 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 29 Oct 2025 08:21:22 +0100 Subject: [PATCH 1593/1733] Add tracktor.sh script for container setup and updates --- ct/tracktor.sh | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 ct/tracktor.sh diff --git a/ct/tracktor.sh b/ct/tracktor.sh new file mode 100644 index 000000000..44fc386c4 --- /dev/null +++ b/ct/tracktor.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: CrazyWolf13 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://tracktor.bytedge.in/ + +APP="tracktor" +var_tags="${var_tags:-car;monitoring}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-4096}" +var_disk="${var_disk:-6}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/tracktor ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if check_for_gh_release "tracktor" "javedh-dev/tracktor"; then + msg_info "Stopping Service" + systemctl stop tracktor + msg_ok "Stopped Service" + + # msg_info "Correcting Services" + # if [ -f /opt/tracktor/app/backend/.env ]; then + #mv /opt/tracktor/app/backend/.env /opt/tracktor.env + # echo 'AUTH_PIN=123456' >> /opt/tracktor.env + # sed -i 's|^EnvironmentFile=.*|EnvironmentFile=/opt/tracktor.env|' /etc/systemd/system/tracktor.service + # systemctl daemon-reload + # fi + # msg_ok "Corrected Services" + + setup_nodejs + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "tracktor" "javedh-dev/tracktor" "tarball" "latest" "/opt/tracktor" + + msg_info "Updating tracktor" + cd /opt/tracktor + $STD npm install + $STD npm run build + msg_ok "Updated tracktor" + + msg_info "Starting Service" + systemctl start tracktor + msg_ok "Started Service" + msg_ok "Updated Successfully" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" From ba536ccc6a22d6b8e7f3307d7101973f9e5d0ecc Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 29 Oct 2025 08:30:15 +0100 Subject: [PATCH 1594/1733] Add installation script for Tracktor This script installs and configures Tracktor, setting up necessary directories, environment variables, and a systemd service. --- install/tracktor-install.sh | 70 +++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 install/tracktor-install.sh diff --git a/install/tracktor-install.sh b/install/tracktor-install.sh new file mode 100644 index 000000000..1111a698d --- /dev/null +++ b/install/tracktor-install.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash + +# Copyright (c) 2025 Community Scripts ORG +# Author: CrazyWolf13 +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://tracktor.bytedge.in + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +setup_nodejs +fetch_and_deploy_gh_release "tracktor" "javedh-dev/tracktor" "tarball" "latest" "/opt/tracktor" + +msg_info "Configuring Tracktor" +cd /opt/tracktor +$STD npm install +$STD npm run build +mkdir -p /opt/tracktor-data/uploads +mkdir -p /opt/tracktor-data/logs +HOST_IP=$(hostname -I | awk '{print $1}') +cat </opt/tracktor.env +NODE_ENV=production +DB_PATH=/opt/tracktor-data/tracktor.db +UPLOADS_DIR="/opt/tracktor-data/uploads" +LOG_DIR="/opt/tracktor-data/logs" +# If server host is not set by default it will run on all interfaces - `0.0.0.0` +SERVER_HOST="" +SERVER_PORT=3000 +PORT=3000 +# CORS_ORIGINS="*" // Set this if you want to secure your endpoints otherwise default will be "*" +# PUBLIC_API_BASE_URL="" // Set this if you are using backend and frontend separately. For lxc installation this is not needed +LOG_REQUESTS=true +LOG_LEVEL="info" +AUTH_PIN=123456 +# PUBLIC_DEMO_MODE=false +# FORCE_DATA_SEED=false +EOF +msg_ok "Configured Tracktor" + +msg_info "Creating service" +cat </etc/systemd/system/tracktor.service +[Unit] +Description=Tracktor Service +After=network.target + +[Service] +Type=simple +WorkingDirectory=/opt/tracktor +EnvironmentFile=/opt/tracktor.env +ExecStart=/usr/bin/npm start + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now tracktor +msg_ok "Created service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt -y autoremove +$STD apt -y autoclean +$STD apt -y clean +msg_ok "Cleaned" From 3b8373739275e91d1a4c5f106c8ecf6926531f1f Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 29 Oct 2025 08:46:09 +0100 Subject: [PATCH 1595/1733] Refactor comments in tracktor-install.sh Updated comments for clarity and removed default server host setting. --- install/tracktor-install.sh | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/install/tracktor-install.sh b/install/tracktor-install.sh index 1111a698d..a979dfdbc 100644 --- a/install/tracktor-install.sh +++ b/install/tracktor-install.sh @@ -28,12 +28,14 @@ NODE_ENV=production DB_PATH=/opt/tracktor-data/tracktor.db UPLOADS_DIR="/opt/tracktor-data/uploads" LOG_DIR="/opt/tracktor-data/logs" -# If server host is not set by default it will run on all interfaces - `0.0.0.0` -SERVER_HOST="" +# If server host is not set by default it will run on all interfaces - 0.0.0.0 +# SERVER_HOST="" SERVER_PORT=3000 PORT=3000 -# CORS_ORIGINS="*" // Set this if you want to secure your endpoints otherwise default will be "*" -# PUBLIC_API_BASE_URL="" // Set this if you are using backend and frontend separately. For lxc installation this is not needed +# Set this if you want to secure your endpoints otherwise default will be "*" +# CORS_ORIGINS="*" +# Set this if you are using backend and frontend separately. For lxc installation this is not needed +# PUBLIC_API_BASE_URL="" LOG_REQUESTS=true LOG_LEVEL="info" AUTH_PIN=123456 From 7daa03100111f8d95f63779e818abc14a1b1fab7 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 29 Oct 2025 09:28:11 +0100 Subject: [PATCH 1596/1733] Update reitti-install.sh --- install/reitti-install.sh | 67 ++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/install/reitti-install.sh b/install/reitti-install.sh index 0a7a1a121..b48e0b12a 100644 --- a/install/reitti-install.sh +++ b/install/reitti-install.sh @@ -64,38 +64,47 @@ mv /opt/reitti/reitti-*.jar /opt/reitti/reitti.jar USE_ORIGINAL_FILENAME="true" fetch_and_deploy_gh_release "photon" "komoot/photon" "singlefile" "latest" "/opt/photon" "photon-0*.jar" mv /opt/photon/photon-*.jar /opt/photon/photon.jar -msg_info "Creating Reitti Environment (.env)" -cat </opt/reitti/.env -# PostgreSQL (PostGIS) -POSTGIS_HOST=127.0.0.1 -POSTGIS_PORT=5432 -POSTGIS_DB=$DB_NAME -POSTGIS_USER=$DB_USER -POSTGIS_PASSWORD=$DB_PASS +msg_info "Creating Reitti Configuration-File" +cat <<'EOF' >/opt/reitti/application.properties +# ─── Database (PostgreSQL/PostGIS) ────────────────────────────────── +spring.datasource.url=jdbc:postgresql://${POSTGIS_HOST}:${POSTGIS_PORT}/${POSTGIS_DB} +spring.datasource.username=${POSTGIS_USER} +spring.datasource.password=${POSTGIS_PASSWORD} +spring.datasource.driver-class-name=org.postgresql.Driver -# RabbitMQ -RABBITMQ_HOST=127.0.0.1 -RABBITMQ_PORT=5672 -RABBITMQ_USER=$RABBIT_USER -RABBITMQ_PASSWORD=$RABBIT_PASS -RABBITMQ_VHOST=/ +# ─── Flyway Migration ─────────────────────────────────────────────── +spring.flyway.enabled=true +spring.flyway.locations=classpath:db/migration +spring.flyway.baseline-on-migrate=true -# Redis -REDIS_HOST=127.0.0.1 -REDIS_PORT=6379 +# ─── RabbitMQ ─────────────────────────────────────────────────────── +spring.rabbitmq.host=${RABBITMQ_HOST} +spring.rabbitmq.port=${RABBITMQ_PORT} +spring.rabbitmq.username=${RABBITMQ_USER} +spring.rabbitmq.password=${RABBITMQ_PASSWORD} +spring.rabbitmq.virtual-host=${RABBITMQ_VHOST} -# Photon (Geocoding) -PHOTON_BASE_URL=http://127.0.0.1:2322 -PROCESSING_WAIT_TIME=15 -PROCESSING_BATCH_SIZE=1000 -PROCESSING_WORKERS_PER_QUEUE=4-16 +# ─── Redis ───────────────────────────────────────────────────────── +spring.redis.host=${REDIS_HOST} +spring.redis.port=${REDIS_PORT} +# spring.redis.username=${REDIS_USERNAME} +# spring.redis.password=${REDIS_PASSWORD} -# General -SERVER_PORT=8080 -LOGGING_LEVEL=INFO -DANGEROUS_LIFE=false +# ─── Photon / Processing ──────────────────────────────────────────── +reitti.photon.base-url=${PHOTON_BASE_URL} +reitti.processing.wait-time=${PROCESSING_WAIT_TIME} +reitti.processing.batch-size=${PROCESSING_BATCH_SIZE} +reitti.processing.workers-per-queue=${PROCESSING_WORKERS_PER_QUEUE} + +# ─── Application Server / Logging ─────────────────────────────────── +server.port=${SERVER_PORT} +logging.level.root=${LOGGING_LEVEL} + +# ─── Misc / Safety ───────────────────────────────────────────────── +reitti.dangerous-life=${DANGEROUS_LIFE} +spring.jpa.hibernate.ddl-auto=none EOF -msg_ok "Created .env for Reitti" +msg_ok "Created Configuration-File for Reitti" msg_info "Creating Services" cat </etc/systemd/system/reitti.service @@ -107,8 +116,8 @@ Wants=postgresql.service redis-server.service rabbitmq-server.service photon.ser [Service] Type=simple WorkingDirectory=/opt/reitti/ -EnvironmentFile=/opt/reitti/.env -ExecStart=/usr/bin/java --enable-native-access=ALL-UNNAMED -jar -Xmx2g reitti.jar +ExecStart=/usr/bin/java -jar /opt/reitti/reitti.jar \ + --spring.config.location=file:/opt/reitti/application.properties TimeoutStopSec=20 KillMode=process Restart=on-failure From a8be512859ea2c53ab8c7ad5db31658d304085f1 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 29 Oct 2025 09:58:42 +0100 Subject: [PATCH 1597/1733] Refactor tracktor.sh to correct services setup Updated the script to correct services and set environment variables for the tracktor application. --- ct/tracktor.sh | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/ct/tracktor.sh b/ct/tracktor.sh index 44fc386c4..e39808c27 100644 --- a/ct/tracktor.sh +++ b/ct/tracktor.sh @@ -33,14 +33,34 @@ function update_script() { systemctl stop tracktor msg_ok "Stopped Service" - # msg_info "Correcting Services" - # if [ -f /opt/tracktor/app/backend/.env ]; then - #mv /opt/tracktor/app/backend/.env /opt/tracktor.env - # echo 'AUTH_PIN=123456' >> /opt/tracktor.env - # sed -i 's|^EnvironmentFile=.*|EnvironmentFile=/opt/tracktor.env|' /etc/systemd/system/tracktor.service - # systemctl daemon-reload - # fi - # msg_ok "Corrected Services" + msg_info "Correcting Services" + if [ -f /opt/tracktor/app/backend/.env ]; then + mv /opt/tracktor/app/backend/.env /opt/tracktor.env + echo 'AUTH_PIN=123456' >> /opt/tracktor.env + sed -i 's|^EnvironmentFile=.*|EnvironmentFile=/opt/tracktor.env|' /etc/systemd/system/tracktor.service + systemctl daemon-reload + fi + EXISTING_AUTH_PIN=$(grep '^AUTH_PIN=' /opt/tracktor.env 2>/dev/null | cut -d'=' -f2) + AUTH_PIN=${EXISTING_AUTH_PIN:-123456} + cat </opt/tracktor.env +NODE_ENV=production +DB_PATH=/opt/tracktor-data/tracktor.db +UPLOADS_DIR="/opt/tracktor-data/uploads" +LOG_DIR="/opt/tracktor-data/logs" +# If server host is not set by default it will run on all interfaces - 0.0.0.0 +# SERVER_HOST="" +SERVER_PORT=3000 +# Set this if you want to secure your endpoints otherwise default will be "*" +CORS_ORIGINS="*" +# Set this if you are using backend and frontend separately. +# PUBLIC_API_BASE_URL="" +LOG_REQUESTS=true +LOG_LEVEL="info" +AUTH_PIN=${AUTH_PIN} +# PUBLIC_DEMO_MODE=false +# FORCE_DATA_SEED=false +EOF + msg_ok "Corrected Services" setup_nodejs CLEAN_INSTALL=1 fetch_and_deploy_gh_release "tracktor" "javedh-dev/tracktor" "tarball" "latest" "/opt/tracktor" From ffce06ae2aca1a07d6c5fb5c88a5c0e2621ce996 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 29 Oct 2025 10:33:37 +0100 Subject: [PATCH 1598/1733] Set up environment variables for new directories Ensure environment variables are set when creating directories. --- ct/tracktor.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ct/tracktor.sh b/ct/tracktor.sh index e39808c27..53c37c1c1 100644 --- a/ct/tracktor.sh +++ b/ct/tracktor.sh @@ -40,9 +40,11 @@ function update_script() { sed -i 's|^EnvironmentFile=.*|EnvironmentFile=/opt/tracktor.env|' /etc/systemd/system/tracktor.service systemctl daemon-reload fi - EXISTING_AUTH_PIN=$(grep '^AUTH_PIN=' /opt/tracktor.env 2>/dev/null | cut -d'=' -f2) - AUTH_PIN=${EXISTING_AUTH_PIN:-123456} - cat </opt/tracktor.env + if [ ! -d "/opt/tracktor-data/uploads" ]; then + mkdir -p /opt/tracktor-data/{uploads,logs} + EXISTING_AUTH_PIN=$(grep '^AUTH_PIN=' /opt/tracktor.env 2>/dev/null | cut -d'=' -f2) + AUTH_PIN=${EXISTING_AUTH_PIN:-123456} + cat </opt/tracktor.env NODE_ENV=production DB_PATH=/opt/tracktor-data/tracktor.db UPLOADS_DIR="/opt/tracktor-data/uploads" @@ -60,6 +62,7 @@ AUTH_PIN=${AUTH_PIN} # PUBLIC_DEMO_MODE=false # FORCE_DATA_SEED=false EOF + fi msg_ok "Corrected Services" setup_nodejs From 25c1b348262ee3520ccaef379b158850b1ab8e94 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 29 Oct 2025 11:07:48 +0100 Subject: [PATCH 1599/1733] Update reitti-install.sh --- install/reitti-install.sh | 80 +++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/install/reitti-install.sh b/install/reitti-install.sh index b48e0b12a..1fb588fbe 100644 --- a/install/reitti-install.sh +++ b/install/reitti-install.sh @@ -15,10 +15,10 @@ update_os msg_info "Installing Dependencies" $STD apt install -y \ - redis-server \ - rabbitmq-server \ - libpq-dev \ - zstd + redis-server \ + rabbitmq-server \ + libpq-dev \ + zstd msg_ok "Installed Dependencies" JAVA_VERSION="24" setup_java @@ -36,10 +36,10 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" $STD sudo -u postgres psql -d "$DB_NAME" -c "CREATE EXTENSION IF NOT EXISTS postgis;" $STD sudo -u postgres psql -d "$DB_NAME" -c "CREATE EXTENSION IF NOT EXISTS postgis_topology;" { - echo "Reitti Credentials" - echo "Database Name: $DB_NAME" - echo "Database User: $DB_USER" - echo "Database Password: $DB_PASS" + echo "Reitti Credentials" + echo "Database Name: $DB_NAME" + echo "Database User: $DB_USER" + echo "Database Password: $DB_PASS" } >>~/reitti.creds msg_ok "PostgreSQL Setup Completed" @@ -52,10 +52,10 @@ $STD rabbitmqctl add_vhost "$RABBIT_VHOST" $STD rabbitmqctl set_permissions -p "$RABBIT_VHOST" "$RABBIT_USER" ".*" ".*" ".*" $STD rabbitmqctl set_user_tags "$RABBIT_USER" administrator { - echo "" - echo "Reitti Credentials" - echo "RabbitMQ User: $RABBIT_USER" - echo "RabbitMQ Password: $RABBIT_PASS" + echo "" + echo "Reitti Credentials" + echo "RabbitMQ User: $RABBIT_USER" + echo "RabbitMQ Password: $RABBIT_PASS" } >>~/reitti.creds msg_ok "Configured RabbitMQ" @@ -65,44 +65,44 @@ USE_ORIGINAL_FILENAME="true" fetch_and_deploy_gh_release "photon" "komoot/photon mv /opt/photon/photon-*.jar /opt/photon/photon.jar msg_info "Creating Reitti Configuration-File" -cat <<'EOF' >/opt/reitti/application.properties -# ─── Database (PostgreSQL/PostGIS) ────────────────────────────────── -spring.datasource.url=jdbc:postgresql://${POSTGIS_HOST}:${POSTGIS_PORT}/${POSTGIS_DB} -spring.datasource.username=${POSTGIS_USER} -spring.datasource.password=${POSTGIS_PASSWORD} +cat </opt/reitti/application.properties +# PostgreSQL Database Connection +spring.datasource.url=jdbc:postgresql://127.0.0.1:5432/$DB_NAME +spring.datasource.username=$DB_USER +spring.datasource.password=$DB_PASS spring.datasource.driver-class-name=org.postgresql.Driver -# ─── Flyway Migration ─────────────────────────────────────────────── +# Flyway Database Migrations spring.flyway.enabled=true spring.flyway.locations=classpath:db/migration spring.flyway.baseline-on-migrate=true -# ─── RabbitMQ ─────────────────────────────────────────────────────── -spring.rabbitmq.host=${RABBITMQ_HOST} -spring.rabbitmq.port=${RABBITMQ_PORT} -spring.rabbitmq.username=${RABBITMQ_USER} -spring.rabbitmq.password=${RABBITMQ_PASSWORD} -spring.rabbitmq.virtual-host=${RABBITMQ_VHOST} +# RabbitMQ (Message Queue) +spring.rabbitmq.host=127.0.0.1 +spring.rabbitmq.port=5672 +spring.rabbitmq.username=$RABBIT_USER +spring.rabbitmq.password=$RABBIT_PASS -# ─── Redis ───────────────────────────────────────────────────────── -spring.redis.host=${REDIS_HOST} -spring.redis.port=${REDIS_PORT} -# spring.redis.username=${REDIS_USERNAME} -# spring.redis.password=${REDIS_PASSWORD} +# Redis (Cache) +spring.data.redis.host=127.0.0.1 +spring.data.redis.port=6379 -# ─── Photon / Processing ──────────────────────────────────────────── -reitti.photon.base-url=${PHOTON_BASE_URL} -reitti.processing.wait-time=${PROCESSING_WAIT_TIME} -reitti.processing.batch-size=${PROCESSING_BATCH_SIZE} -reitti.processing.workers-per-queue=${PROCESSING_WORKERS_PER_QUEUE} +# Server Port +server.port=8080 -# ─── Application Server / Logging ─────────────────────────────────── -server.port=${SERVER_PORT} -logging.level.root=${LOGGING_LEVEL} - -# ─── Misc / Safety ───────────────────────────────────────────────── -reitti.dangerous-life=${DANGEROUS_LIFE} +# Optional: Logging & Performance +logging.level.root=INFO spring.jpa.hibernate.ddl-auto=none +spring.datasource.hikari.maximum-pool-size=10 + +# Photon (Geocoding) +PHOTON_BASE_URL=http://127.0.0.1:2322 +PROCESSING_WAIT_TIME=15 +PROCESSING_BATCH_SIZE=1000 +PROCESSING_WORKERS_PER_QUEUE=4-16 + +# Disable potentially dangerous features unless needed +DANGEROUS_LIFE=false EOF msg_ok "Created Configuration-File for Reitti" From 2a6569a6e3195b5beef89704af08324cf9976668 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 29 Oct 2025 12:36:50 +0100 Subject: [PATCH 1600/1733] add pve_version & kernel output --- misc/build.func | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/misc/build.func b/misc/build.func index 01050c57c..9c8a1fc84 100644 --- a/misc/build.func +++ b/misc/build.func @@ -13,6 +13,7 @@ # - Fetch hostname of Proxmox node # - Set default values for diagnostics/method # - Generate random UUID for tracking +# - Get Proxmox VE version and kernel version # ------------------------------------------------------------------------------ variables() { NSAPP=$(echo "${APP,,}" | tr -d ' ') # This function sets the NSAPP variable by converting the value of the APP variable to lowercase and removing any spaces. @@ -24,6 +25,14 @@ variables() { RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" # generates a random UUID and sets it to the RANDOM_UUID variable. CTTYPE="${CTTYPE:-${CT_TYPE:-1}}" #CT_TYPE=${var_unprivileged:-$CT_TYPE} + + # Get Proxmox VE version and kernel version + if command -v pveversion >/dev/null 2>&1; then + PVEVERSION=$(pveversion | grep "pve-manager" | awk '{print $2}' | cut -d'/' -f1) + else + PVEVERSION="N/A" + fi + KERNEL_VERSION=$(uname -r) } # ----------------------------------------------------------------------------- @@ -327,7 +336,7 @@ echo_default() { if [ "$CT_TYPE" -eq 0 ]; then CT_TYPE_DESC="Privileged" fi - + echo -e "${TAB}${INFO} ProxmoxVE Version ${PVEVERSION} | Kernel: ${KERNEL_VERSION}${CL}" echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}${CT_ID}${CL}" echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os ($var_version)${CL}" echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" @@ -438,7 +447,8 @@ advanced_settings() { if [ "$CT_TYPE" -eq 0 ]; then CT_TYPE_DESC="Privileged" fi - echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os | ${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" + echo -e "${TAB}${INFO} ProxmoxVE Version ${PVEVERSION} | Kernel: ${KERNEL_VERSION}${CL}" + echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os |${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" fi else @@ -455,6 +465,7 @@ advanced_settings() { if [ "$CT_TYPE" -eq 0 ]; then CT_TYPE_DESC="Privileged" fi + echo -e "${TAB}${INFO} ProxmoxVE Version ${PVEVERSION} | Kernel: ${KERNEL_VERSION}${CL}" echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os${CL}" echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" @@ -878,6 +889,7 @@ advanced_settings() { else clear header_info + echo -e "${TAB}${INFO} ProxmoxVE Version ${PVEVERSION} | Kernel: ${KERNEL_VERSION}${CL}" echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings on node $PVEHOST_NAME${CL}" advanced_settings fi @@ -1584,6 +1596,7 @@ install_script() { ;; 2 | advanced | ADVANCED) header_info + echo -e "${TAB}${INFO} ProxmoxVE Version ${PVEVERSION} | Kernel: ${KERNEL_VERSION}${CL}" echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Install on node $PVEHOST_NAME${CL}" METHOD="advanced" base_settings From cc40cd457c0ff9d4a113e039b884c28124cd1567 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 29 Oct 2025 12:47:45 +0100 Subject: [PATCH 1601/1733] Optimize build.func: Add PVE/kernel version display, reorganize functions, remove duplicates --- misc/build.func | 1811 ++++++++++++++++++++++------------------------- 1 file changed, 865 insertions(+), 946 deletions(-) diff --git a/misc/build.func b/misc/build.func index 9c8a1fc84..a7e377dcf 100644 --- a/misc/build.func +++ b/misc/build.func @@ -4,6 +4,10 @@ # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE # Revision: 1 +# ============================================================================== +# CORE INITIALIZATION & VARIABLES +# ============================================================================== + # ------------------------------------------------------------------------------ # variables() # @@ -35,124 +39,10 @@ variables() { KERNEL_VERSION=$(uname -r) } -# ----------------------------------------------------------------------------- -# Community-Scripts bootstrap loader -# - Always sources build.func from remote -# - Updates local core files only if build.func changed -# - Local cache: /usr/local/community-scripts/core -# ----------------------------------------------------------------------------- -# FUNC_DIR="/usr/local/community-scripts/core" -# mkdir -p "$FUNC_DIR" - -# BUILD_URL="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func" -# BUILD_REV="$FUNC_DIR/build.rev" -# DEVMODE="${DEVMODE:-no}" - -# # --- Step 1: fetch build.func content once, compute hash --- -# build_content="$(curl -fsSL "$BUILD_URL")" || { -# echo "❌ Failed to fetch build.func" -# exit 1 -# } - -# newhash=$(printf "%s" "$build_content" | sha256sum | awk '{print $1}') -# oldhash=$(cat "$BUILD_REV" 2>/dev/null || echo "") - -# # --- Step 2: if build.func changed, offer update for core files --- -# if [ "$newhash" != "$oldhash" ]; then -# echo "⚠️ build.func changed!" - -# while true; do -# read -rp "Refresh local core files? [y/N/diff]: " ans -# case "$ans" in -# [Yy]*) -# echo "$newhash" >"$BUILD_REV" - -# update_func_file() { -# local file="$1" -# local url="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/$file" -# local local_path="$FUNC_DIR/$file" - -# echo "⬇️ Downloading $file ..." -# curl -fsSL "$url" -o "$local_path" || { -# echo "❌ Failed to fetch $file" -# exit 1 -# } -# echo "✔️ Updated $file" -# } - -# update_func_file core.func -# update_func_file error_handler.func -# update_func_file tools.func -# break -# ;; -# [Dd]*) -# for file in core.func error_handler.func tools.func; do -# local_path="$FUNC_DIR/$file" -# url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/$file" -# remote_tmp="$(mktemp)" - -# curl -fsSL "$url" -o "$remote_tmp" || continue - -# if [ -f "$local_path" ]; then -# echo "🔍 Diff for $file:" -# diff -u "$local_path" "$remote_tmp" || echo "(no differences)" -# else -# echo "📦 New file $file will be installed" -# fi - -# rm -f "$remote_tmp" -# done -# ;; -# *) -# echo "❌ Skipped updating local core files" -# break -# ;; -# esac -# done -# else -# if [ "$DEVMODE" != "yes" ]; then -# echo "✔️ build.func unchanged → using existing local core files" -# fi -# fi - -# if [ -n "${_COMMUNITY_SCRIPTS_LOADER:-}" ]; then -# return 0 2>/dev/null || exit 0 -# fi -# _COMMUNITY_SCRIPTS_LOADER=1 - -# # --- Step 3: always source local versions of the core files --- -# source "$FUNC_DIR/core.func" -# source "$FUNC_DIR/error_handler.func" -# source "$FUNC_DIR/tools.func" - -# # --- Step 4: finally, source build.func directly from memory --- -# # (no tmp file needed) -# source <(printf "%s" "$build_content") - -# ------------------------------------------------------------------------------ -# Load core + error handler functions from community-scripts repo -# -# - Prefer curl if available, fallback to wget -# - Load: core.func, error_handler.func, api.func -# - Initialize error traps after loading -# ------------------------------------------------------------------------------ - -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) - -if command -v curl >/dev/null 2>&1; then - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) - load_functions - catch_errors - #echo "(build.func) Loaded core.func via curl" -elif command -v wget >/dev/null 2>&1; then - source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) - source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) - load_functions - catch_errors - #echo "(build.func) Loaded core.func via wget" -fi +# ============================================================================== +# SYSTEM VALIDATION & CHECKS +# ============================================================================== # ------------------------------------------------------------------------------ # maxkeys_check() @@ -163,7 +53,6 @@ fi # - Exits if thresholds are exceeded # - https://cleveruptime.com/docs/files/proc-key-users | https://docs.kernel.org/security/keys/core.html # ------------------------------------------------------------------------------ - maxkeys_check() { # Read kernel parameters per_user_maxkeys=$(cat /proc/sys/kernel/keys/maxkeys 2>/dev/null || echo 0) @@ -207,6 +96,86 @@ maxkeys_check() { echo -e "${CM}${GN} All kernel key limits are within safe thresholds.${CL}" } +# ------------------------------------------------------------------------------ +# check_container_resources() +# +# - Compares host RAM/CPU with required values +# - Warns if under-provisioned and asks user to continue or abort +# ------------------------------------------------------------------------------ +check_container_resources() { + current_ram=$(free -m | awk 'NR==2{print $2}') + current_cpu=$(nproc) + + if [[ "$current_ram" -lt "$var_ram" ]] || [[ "$current_cpu" -lt "$var_cpu" ]]; then + echo -e "\n${INFO}${HOLD} ${GN}Required: ${var_cpu} CPU, ${var_ram}MB RAM ${CL}| ${RD}Current: ${current_cpu} CPU, ${current_ram}MB RAM${CL}" + echo -e "${YWB}Please ensure that the ${APP} LXC is configured with at least ${var_cpu} vCPU and ${var_ram} MB RAM for the build process.${CL}\n" + echo -ne "${INFO}${HOLD} May cause data loss! ${INFO} Continue update with under-provisioned LXC? " + read -r prompt + if [[ ! ${prompt,,} =~ ^(yes)$ ]]; then + echo -e "${CROSS}${HOLD} ${YWB}Exiting based on user input.${CL}" + exit 1 + fi + else + echo -e "" + fi +} + +# ------------------------------------------------------------------------------ +# check_container_storage() +# +# - Checks /boot partition usage +# - Warns if usage >80% and asks user confirmation before proceeding +# ------------------------------------------------------------------------------ +check_container_storage() { + total_size=$(df /boot --output=size | tail -n 1) + local used_size=$(df /boot --output=used | tail -n 1) + usage=$((100 * used_size / total_size)) + if ((usage > 80)); then + echo -e "${INFO}${HOLD} ${YWB}Warning: Storage is dangerously low (${usage}%).${CL}" + echo -ne "Continue anyway? " + read -r prompt + if [[ ! ${prompt,,} =~ ^(y|yes)$ ]]; then + echo -e "${CROSS}${HOLD}${YWB}Exiting based on user input.${CL}" + exit 1 + fi + fi +} + +# NVIDIA-spezific check on host +check_nvidia_host_setup() { + if ! command -v nvidia-smi >/dev/null 2>&1; then + msg_warn "NVIDIA GPU detected but nvidia-smi not found on host" + msg_warn "Please install NVIDIA drivers on host first." + #echo " 1. Download driver: wget https://us.download.nvidia.com/XFree86/Linux-x86_64/550.127.05/NVIDIA-Linux-x86_64-550.127.05.run" + #echo " 2. Install: ./NVIDIA-Linux-x86_64-550.127.05.run --dkms" + #echo " 3. Verify: nvidia-smi" + return 1 + fi + + # check if nvidia-smi works + if ! nvidia-smi >/dev/null 2>&1; then + msg_warn "nvidia-smi installed but not working. Driver issue?" + return 1 + fi + + return 0 +} + +check_storage_support() { + local CONTENT="$1" VALID=0 + while IFS= read -r line; do + local STORAGE_NAME + STORAGE_NAME=$(awk '{print $1}' <<<"$line") + [[ -n "$STORAGE_NAME" ]] && VALID=1 + done < <(pvesm status -content "$CONTENT" 2>/dev/null | awk 'NR>1') + [[ $VALID -eq 1 ]] +} + + +# ============================================================================== +# NETWORK & IP MANAGEMENT +# ============================================================================== + # ------------------------------------------------------------------------------ # get_current_ip() # @@ -248,6 +217,11 @@ update_motd_ip() { fi } + +# ============================================================================== +# SSH KEY MANAGEMENT +# ============================================================================== + # ------------------------------------------------------------------------------ # install_ssh_keys_into_ct() # @@ -279,89 +253,6 @@ install_ssh_keys_into_ct() { return 0 } -# ------------------------------------------------------------------------------ -# base_settings() -# -# - Defines all base/default variables for container creation -# - Reads from environment variables (var_*) -# - Provides fallback defaults for OS type/version -# ------------------------------------------------------------------------------ -base_settings() { - # Default Settings - CT_TYPE=${var_unprivileged:-"1"} - DISK_SIZE=${var_disk:-"4"} - CORE_COUNT=${var_cpu:-"1"} - RAM_SIZE=${var_ram:-"1024"} - VERBOSE=${var_verbose:-"${1:-no}"} - PW=${var_pw:-""} - CT_ID=${var_ctid:-$NEXTID} - HN=${var_hostname:-$NSAPP} - BRG=${var_brg:-"vmbr0"} - NET=${var_net:-"dhcp"} - IPV6_METHOD=${var_ipv6_method:-"none"} - IPV6_STATIC=${var_ipv6_static:-""} - GATE=${var_gateway:-""} - APT_CACHER=${var_apt_cacher:-""} - APT_CACHER_IP=${var_apt_cacher_ip:-""} - MTU=${var_mtu:-""} - SD=${var_storage:-""} - NS=${var_ns:-""} - MAC=${var_mac:-""} - VLAN=${var_vlan:-""} - SSH=${var_ssh:-"no"} - SSH_AUTHORIZED_KEY=${var_ssh_authorized_key:-""} - UDHCPC_FIX=${var_udhcpc_fix:-""} - TAGS="community-script,${var_tags:-}" - ENABLE_FUSE=${var_fuse:-"${1:-no}"} - ENABLE_TUN=${var_tun:-"${1:-no}"} - - # Since these 2 are only defined outside of default_settings function, we add a temporary fallback. TODO: To align everything, we should add these as constant variables (e.g. OSTYPE and OSVERSION), but that would currently require updating the default_settings function for all existing scripts - if [ -z "$var_os" ]; then - var_os="debian" - fi - if [ -z "$var_version" ]; then - var_version="12" - fi -} - -# ------------------------------------------------------------------------------ -# echo_default() -# -# - Prints summary of default values (ID, OS, type, disk, RAM, CPU, etc.) -# - Uses icons and formatting for readability -# - Convert CT_TYPE to description -# ------------------------------------------------------------------------------ -echo_default() { - CT_TYPE_DESC="Unprivileged" - if [ "$CT_TYPE" -eq 0 ]; then - CT_TYPE_DESC="Privileged" - fi - echo -e "${TAB}${INFO} ProxmoxVE Version ${PVEVERSION} | Kernel: ${KERNEL_VERSION}${CL}" - echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}${CT_ID}${CL}" - echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os ($var_version)${CL}" - echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" - echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" - echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" - echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" - if [ "$VERBOSE" == "yes" ]; then - echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}Enabled${CL}" - fi - echo -e "${CREATING}${BOLD}${BL}Creating a ${APP} LXC using the above default settings${CL}" - echo -e " " -} - -# ------------------------------------------------------------------------------ -# exit_script() -# -# - Called when user cancels an action -# - Clears screen and exits gracefully -# ------------------------------------------------------------------------------ -exit_script() { - clear - echo -e "\n${CROSS}${RD}User exited script${CL}\n" - exit -} - # ------------------------------------------------------------------------------ # find_host_ssh_keys() # @@ -422,6 +313,271 @@ find_host_ssh_keys() { ) } +# ------------------------------------------------------------------------------ +# ssh_extract_keys_from_file() +# +# - Extracts valid SSH public keys from given file +# - Supports RSA, Ed25519, ECDSA and filters out comments/invalid lines +# ------------------------------------------------------------------------------ +ssh_extract_keys_from_file() { + local f="$1" + [[ -r "$f" ]] || return 0 + tr -d '\r' <"$f" | awk ' + /^[[:space:]]*#/ {next} + /^[[:space:]]*$/ {next} + # nackt: typ base64 [comment] + /^(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/ {print; next} + # mit Optionen: finde ab erstem Key-Typ + { + match($0, /(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/) + if (RSTART>0) { print substr($0, RSTART) } + } + ' +} + +# ------------------------------------------------------------------------------ +# ssh_build_choices_from_files() +# +# - Builds interactive whiptail checklist of available SSH keys +# - Generates fingerprint, type and comment for each key +# ------------------------------------------------------------------------------ +ssh_build_choices_from_files() { + local -a files=("$@") + CHOICES=() + COUNT=0 + MAPFILE="$(mktemp)" + local id key typ fp cmt base ln=0 + + for f in "${files[@]}"; do + [[ -f "$f" && -r "$f" ]] || continue + base="$(basename -- "$f")" + case "$base" in + known_hosts | known_hosts.* | config) continue ;; + id_*) [[ "$f" != *.pub ]] && continue ;; + esac + + # map every key in file + while IFS= read -r key; do + [[ -n "$key" ]] || continue + + typ="" + fp="" + cmt="" + # Only the pure key part (without options) is already included in ‘key’. + read -r _typ _b64 _cmt <<<"$key" + typ="${_typ:-key}" + cmt="${_cmt:-}" + # Fingerprint via ssh-keygen (if available) + if command -v ssh-keygen >/dev/null 2>&1; then + fp="$(printf '%s\n' "$key" | ssh-keygen -lf - 2>/dev/null | awk '{print $2}')" + fi + # Label shorten + [[ ${#cmt} -gt 40 ]] && cmt="${cmt:0:37}..." + + ln=$((ln + 1)) + COUNT=$((COUNT + 1)) + id="K${COUNT}" + echo "${id}|${key}" >>"$MAPFILE" + CHOICES+=("$id" "[$typ] ${fp:+$fp }${cmt:+$cmt }— ${base}" "OFF") + done < <(ssh_extract_keys_from_file "$f") + done +} + +# ------------------------------------------------------------------------------ +# ssh_discover_default_files() +# +# - Scans standard paths for SSH keys +# - Includes ~/.ssh/*.pub, /etc/ssh/authorized_keys, etc. +# ------------------------------------------------------------------------------ +ssh_discover_default_files() { + local -a cand=() + shopt -s nullglob + cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2) + cand+=(/root/.ssh/*.pub) + cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*) + shopt -u nullglob + printf '%s\0' "${cand[@]}" +} + +configure_ssh_settings() { + SSH_KEYS_FILE="$(mktemp)" + : >"$SSH_KEYS_FILE" + + IFS=$'\0' read -r -d '' -a _def_files < <(ssh_discover_default_files && printf '\0') + ssh_build_choices_from_files "${_def_files[@]}" + local default_key_count="$COUNT" + + local ssh_key_mode + if [[ "$default_key_count" -gt 0 ]]; then + ssh_key_mode=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ + "Provision SSH keys for root:" 14 72 4 \ + "found" "Select from detected keys (${default_key_count})" \ + "manual" "Paste a single public key" \ + "folder" "Scan another folder (path or glob)" \ + "none" "No keys" 3>&1 1>&2 2>&3) || exit_script + else + ssh_key_mode=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ + "No host keys detected; choose manual/none:" 12 72 2 \ + "manual" "Paste a single public key" \ + "none" "No keys" 3>&1 1>&2 2>&3) || exit_script + fi + + case "$ssh_key_mode" in + found) + local selection + selection=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT HOST KEYS" \ + --checklist "Select one or more keys to import:" 20 140 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script + for tag in $selection; do + tag="${tag%\"}" + tag="${tag#\"}" + local line + line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) + [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" + done + ;; + manual) + SSH_AUTHORIZED_KEY="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Paste one SSH public key line (ssh-ed25519/ssh-rsa/...)" 10 72 --title "SSH Public Key" 3>&1 1>&2 2>&3)" + [[ -n "$SSH_AUTHORIZED_KEY" ]] && printf '%s\n' "$SSH_AUTHORIZED_KEY" >>"$SSH_KEYS_FILE" + ;; + folder) + local glob_path + glob_path=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Enter a folder or glob to scan (e.g. /root/.ssh/*.pub)" 10 72 --title "Scan Folder/Glob" 3>&1 1>&2 2>&3) + if [[ -n "$glob_path" ]]; then + shopt -s nullglob + read -r -a _scan_files <<<"$glob_path" + shopt -u nullglob + if [[ "${#_scan_files[@]}" -gt 0 ]]; then + ssh_build_choices_from_files "${_scan_files[@]}" + if [[ "$COUNT" -gt 0 ]]; then + local folder_selection + folder_selection=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT FOLDER KEYS" \ + --checklist "Select key(s) to import:" 20 78 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script + for tag in $folder_selection; do + tag="${tag%\"}" + tag="${tag#\"}" + local line + line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) + [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" + done + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "No keys found in: $glob_path" 8 60 + fi + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "Path/glob returned no files." 8 60 + fi + fi + ;; + none) + : + ;; + esac + + if [[ -s "$SSH_KEYS_FILE" ]]; then + sort -u -o "$SSH_KEYS_FILE" "$SSH_KEYS_FILE" + printf '\n' >>"$SSH_KEYS_FILE" + fi + + if [[ -s "$SSH_KEYS_FILE" || "$PW" == -password* ]]; then + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable root SSH access?" 10 58); then + SSH="yes" + else + SSH="no" + fi + else + SSH="no" + fi +} + + +# ============================================================================== +# SETTINGS & CONFIGURATION +# ============================================================================== + +# ------------------------------------------------------------------------------ +# base_settings() +# +# - Defines all base/default variables for container creation +# - Reads from environment variables (var_*) +# - Provides fallback defaults for OS type/version +# ------------------------------------------------------------------------------ +base_settings() { + # Default Settings + CT_TYPE=${var_unprivileged:-"1"} + DISK_SIZE=${var_disk:-"4"} + CORE_COUNT=${var_cpu:-"1"} + RAM_SIZE=${var_ram:-"1024"} + VERBOSE=${var_verbose:-"${1:-no}"} + PW=${var_pw:-""} + CT_ID=${var_ctid:-$NEXTID} + HN=${var_hostname:-$NSAPP} + BRG=${var_brg:-"vmbr0"} + NET=${var_net:-"dhcp"} + IPV6_METHOD=${var_ipv6_method:-"none"} + IPV6_STATIC=${var_ipv6_static:-""} + GATE=${var_gateway:-""} + APT_CACHER=${var_apt_cacher:-""} + APT_CACHER_IP=${var_apt_cacher_ip:-""} + MTU=${var_mtu:-""} + SD=${var_storage:-""} + NS=${var_ns:-""} + MAC=${var_mac:-""} + VLAN=${var_vlan:-""} + SSH=${var_ssh:-"no"} + SSH_AUTHORIZED_KEY=${var_ssh_authorized_key:-""} + UDHCPC_FIX=${var_udhcpc_fix:-""} + TAGS="community-script,${var_tags:-}" + ENABLE_FUSE=${var_fuse:-"${1:-no}"} + ENABLE_TUN=${var_tun:-"${1:-no}"} + + # Since these 2 are only defined outside of default_settings function, we add a temporary fallback. TODO: To align everything, we should add these as constant variables (e.g. OSTYPE and OSVERSION), but that would currently require updating the default_settings function for all existing scripts + if [ -z "$var_os" ]; then + var_os="debian" + fi + if [ -z "$var_version" ]; then + var_version="12" + fi +} + +# ------------------------------------------------------------------------------ +# echo_default() +# +# - Prints summary of default values (ID, OS, type, disk, RAM, CPU, etc.) +# - Uses icons and formatting for readability +# - Convert CT_TYPE to description +# ------------------------------------------------------------------------------ +echo_default() { + CT_TYPE_DESC="Unprivileged" + if [ "$CT_TYPE" -eq 0 ]; then + CT_TYPE_DESC="Privileged" + fi + echo -e "${INFO}${BOLD}${DGN}PVE Version ${PVEVERSION} (Kernel: ${KERNEL_VERSION})${CL}" + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}${CT_ID}${CL}" + echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os ($var_version)${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + if [ "$VERBOSE" == "yes" ]; then + echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}Enabled${CL}" + fi + echo -e "${CREATING}${BOLD}${BL}Creating a ${APP} LXC using the above default settings${CL}" + echo -e " " +} + +# ------------------------------------------------------------------------------ +# exit_script() +# +# - Called when user cancels an action +# - Clears screen and exits gracefully +# ------------------------------------------------------------------------------ +exit_script() { + clear + echo -e "\n${CROSS}${RD}User exited script${CL}\n" + exit +} + # ------------------------------------------------------------------------------ # advanced_settings() # @@ -447,7 +603,7 @@ advanced_settings() { if [ "$CT_TYPE" -eq 0 ]; then CT_TYPE_DESC="Privileged" fi - echo -e "${TAB}${INFO} ProxmoxVE Version ${PVEVERSION} | Kernel: ${KERNEL_VERSION}${CL}" + echo -e "${INFO}${BOLD}${DGN}PVE Version ${PVEVERSION} (Kernel: ${KERNEL_VERSION})${CL}" echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os |${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" fi @@ -465,7 +621,7 @@ advanced_settings() { if [ "$CT_TYPE" -eq 0 ]; then CT_TYPE_DESC="Privileged" fi - echo -e "${TAB}${INFO} ProxmoxVE Version ${PVEVERSION} | Kernel: ${KERNEL_VERSION}${CL}" + echo -e "${INFO}${BOLD}${DGN}PVE Version ${PVEVERSION} (Kernel: ${KERNEL_VERSION})${CL}" echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os${CL}" echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" @@ -889,7 +1045,7 @@ advanced_settings() { else clear header_info - echo -e "${TAB}${INFO} ProxmoxVE Version ${PVEVERSION} | Kernel: ${KERNEL_VERSION}${CL}" + echo -e "${INFO}${BOLD}${DGN}PVE Version ${PVEVERSION} (Kernel: ${KERNEL_VERSION})${CL}" echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings on node $PVEHOST_NAME${CL}" advanced_settings fi @@ -1158,43 +1314,105 @@ EOF echo_default } +diagnostics_menu() { + if [ "${DIAGNOSTICS:-no}" = "yes" ]; then + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "DIAGNOSTIC SETTINGS" \ + --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ + --yes-button "No" --no-button "Back"; then + DIAGNOSTICS="no" + sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=no/' /usr/local/community-scripts/diagnostics + whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 + fi + else + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "DIAGNOSTIC SETTINGS" \ + --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ + --yes-button "Yes" --no-button "Back"; then + DIAGNOSTICS="yes" + sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=yes/' /usr/local/community-scripts/diagnostics + whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 + fi + fi +} + +ensure_global_default_vars_file() { + local vars_path="/usr/local/community-scripts/default.vars" + if [[ ! -f "$vars_path" ]]; then + mkdir -p "$(dirname "$vars_path")" + touch "$vars_path" + fi + echo "$vars_path" +} + +edit_default_storage() { + local vf="/usr/local/community-scripts/default.vars" + + # Ensure file exists + if [[ ! -f "$vf" ]]; then + mkdir -p "$(dirname "$vf")" + touch "$vf" + fi + + # Let ensure_storage_selection_for_vars_file handle everything + ensure_storage_selection_for_vars_file "$vf" +} + +settings_menu() { + while true; do + local settings_items=( + "1" "Manage API-Diagnostic Setting" + "2" "Edit Default.vars" + "3" "Edit Default Storage" + ) + if [ -f "$(get_app_defaults_path)" ]; then + settings_items+=("4" "Edit App.vars for ${APP}") + settings_items+=("5" "Exit") + else + settings_items+=("4" "Exit") + fi + + local choice + choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "Community-Scripts SETTINGS Menu" \ + --ok-button "OK" --cancel-button "Back" \ + --menu "\n\nChoose a settings option:\n\nUse TAB or Arrow keys to navigate, ENTER to select." 20 60 9 \ + "${settings_items[@]}" \ + 3>&1 1>&2 2>&3) || break + + case "$choice" in + 1) diagnostics_menu ;; + 2) ${EDITOR:-nano} /usr/local/community-scripts/default.vars ;; + 3) edit_default_storage ;; + 4) + if [ -f "$(get_app_defaults_path)" ]; then + ${EDITOR:-nano} "$(get_app_defaults_path)" + else + exit_script + fi + ;; + 5) exit_script ;; + esac + done +} + + +# ============================================================================== +# DEFAULTS MANAGEMENT (VAR_* FILES) +# ============================================================================== + # ------------------------------------------------------------------------------ # get_app_defaults_path() # # - Returns full path for app-specific defaults file # - Example: /usr/local/community-scripts/defaults/.vars # ------------------------------------------------------------------------------ - get_app_defaults_path() { local n="${NSAPP:-${APP,,}}" echo "/usr/local/community-scripts/defaults/${n}.vars" } -# ------------------------------------------------------------------------------ -# maybe_offer_save_app_defaults -# -# - Called after advanced_settings returned with fully chosen values. -# - If no .vars exists, offers to persist current advanced settings -# into /usr/local/community-scripts/defaults/.vars -# - Only writes whitelisted var_* keys. -# - Extracts raw values from flags like ",gw=..." ",mtu=..." etc. -# ------------------------------------------------------------------------------ -if ! declare -p VAR_WHITELIST >/dev/null 2>&1; then - declare -ag VAR_WHITELIST=( - var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_ctid var_disk var_fuse - var_gateway var_hostname var_ipv6_method var_ipv6_static var_mac var_mtu - var_net var_ns var_pw var_ram var_tags var_tun var_unprivileged - var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage - ) -fi - -_is_whitelisted_key() { - local k="$1" - local w - for w in "${VAR_WHITELIST[@]}"; do [[ "$k" == "$w" ]] && return 0; done - return 1 -} - +# Note: _is_whitelisted_key() is defined above in default_var_settings section _sanitize_value() { # Disallow Command-Substitution / Shell-Meta case "$1" in @@ -1206,12 +1424,10 @@ _sanitize_value() { echo "$1" } -# Map-Parser: read var_* from file into _VARS_IN associative array -declare -A _VARS_IN -_load_vars_file() { +_load_vars_file_to_map() { local file="$1" [ -f "$file" ] || return 0 - msg_info "Loading defaults from ${file}" + _VARS_IN=() # Clear array local line key val while IFS= read -r line || [ -n "$line" ]; do line="${line#"${line%%[![:space:]]*}"}" @@ -1225,12 +1441,11 @@ _load_vars_file() { case "$key" in var_*) if _is_whitelisted_key "$key"; then - [ -z "${!key+x}" ] && export "$key=$val" + _VARS_IN["$key"]="$val" fi ;; esac done <"$file" - msg_ok "Loaded ${file}" } # Diff function for two var_* files -> produces human-readable diff list for $1 (old) vs $2 (new) @@ -1482,220 +1697,11 @@ ensure_storage_selection_for_vars_file() { msg_ok "Storage configuration saved to $(basename "$vf")" } -diagnostics_menu() { - if [ "${DIAGNOSTICS:-no}" = "yes" ]; then - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "DIAGNOSTIC SETTINGS" \ - --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ - --yes-button "No" --no-button "Back"; then - DIAGNOSTICS="no" - sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=no/' /usr/local/community-scripts/diagnostics - whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 - fi - else - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "DIAGNOSTIC SETTINGS" \ - --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ - --yes-button "Yes" --no-button "Back"; then - DIAGNOSTICS="yes" - sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=yes/' /usr/local/community-scripts/diagnostics - whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 - fi - fi -} -ensure_global_default_vars_file() { - local vars_path="/usr/local/community-scripts/default.vars" - if [[ ! -f "$vars_path" ]]; then - mkdir -p "$(dirname "$vars_path")" - touch "$vars_path" - fi - echo "$vars_path" -} +# ============================================================================== +# STORAGE DISCOVERY & SELECTION +# ============================================================================== -# ------------------------------------------------------------------------------ -# install_script() -# -# - Main entrypoint for installation mode -# - Runs safety checks (pve_check, root_check, maxkeys_check, diagnostics_check) -# - Builds interactive menu (Default, Verbose, Advanced, My Defaults, App Defaults, Diagnostics, Storage, Exit) -# - Applies chosen settings and triggers container build -# ------------------------------------------------------------------------------ -install_script() { - pve_check - shell_check - root_check - arch_check - ssh_check - maxkeys_check - diagnostics_check - - if systemctl is-active -q ping-instances.service; then - systemctl -q stop ping-instances.service - fi - - NEXTID=$(pvesh get /cluster/nextid) - timezone=$(cat /etc/timezone) - - # Show APP Header - header_info - - # --- Support CLI argument as direct preset (default, advanced, …) --- - CHOICE="${mode:-${1:-}}" - - # If no CLI argument → show whiptail menu - # Build menu dynamically based on available options - local appdefaults_option="" - local settings_option="" - local menu_items=( - "1" "Default Install" - "2" "Advanced Install" - "3" "My Defaults" - ) - - if [ -f "$(get_app_defaults_path)" ]; then - appdefaults_option="4" - menu_items+=("4" "App Defaults for ${APP}") - settings_option="5" - menu_items+=("5" "Settings") - else - settings_option="4" - menu_items+=("4" "Settings") - fi - - if [ -z "$CHOICE" ]; then - - TMP_CHOICE=$(whiptail \ - --backtitle "Proxmox VE Helper Scripts" \ - --title "Community-Scripts Options" \ - --ok-button "Select" --cancel-button "Exit Script" \ - --notags \ - --menu "\nChoose an option:\n Use TAB or Arrow keys to navigate, ENTER to select.\n" \ - 20 60 9 \ - "${menu_items[@]}" \ - --default-item "1" \ - 3>&1 1>&2 2>&3) || exit_script - CHOICE="$TMP_CHOICE" - fi - - APPDEFAULTS_OPTION="$appdefaults_option" - SETTINGS_OPTION="$settings_option" - - # --- Main case --- - local defaults_target="" - local run_maybe_offer="no" - case "$CHOICE" in - 1 | default | DEFAULT) - header_info - echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME${CL}" - VERBOSE="no" - METHOD="default" - base_settings "$VERBOSE" - echo_default - defaults_target="$(ensure_global_default_vars_file)" - ;; - 2 | advanced | ADVANCED) - header_info - echo -e "${TAB}${INFO} ProxmoxVE Version ${PVEVERSION} | Kernel: ${KERNEL_VERSION}${CL}" - echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Install on node $PVEHOST_NAME${CL}" - METHOD="advanced" - base_settings - advanced_settings - defaults_target="$(ensure_global_default_vars_file)" - run_maybe_offer="yes" - ;; - 3 | mydefaults | MYDEFAULTS) - default_var_settings || { - msg_error "Failed to apply default.vars" - exit 1 - } - defaults_target="/usr/local/community-scripts/default.vars" - ;; - "$APPDEFAULTS_OPTION" | appdefaults | APPDEFAULTS) - if [ -f "$(get_app_defaults_path)" ]; then - header_info - echo -e "${DEFAULT}${BOLD}${BL}Using App Defaults for ${APP} on node $PVEHOST_NAME${CL}" - METHOD="appdefaults" - base_settings - _load_vars_file "$(get_app_defaults_path)" - echo_default - defaults_target="$(get_app_defaults_path)" - else - msg_error "No App Defaults available for ${APP}" - exit 1 - fi - ;; - "$SETTINGS_OPTION" | settings | SETTINGS) - settings_menu - defaults_target="" - ;; - *) - echo -e "${CROSS}${RD}Invalid option: $CHOICE${CL}" - exit 1 - ;; - esac - - if [[ -n "$defaults_target" ]]; then - ensure_storage_selection_for_vars_file "$defaults_target" - fi - - if [[ "$run_maybe_offer" == "yes" ]]; then - maybe_offer_save_app_defaults - fi -} - -edit_default_storage() { - local vf="/usr/local/community-scripts/default.vars" - - # Ensure file exists - if [[ ! -f "$vf" ]]; then - mkdir -p "$(dirname "$vf")" - touch "$vf" - fi - - # Let ensure_storage_selection_for_vars_file handle everything - ensure_storage_selection_for_vars_file "$vf" -} - -settings_menu() { - while true; do - local settings_items=( - "1" "Manage API-Diagnostic Setting" - "2" "Edit Default.vars" - "3" "Edit Default Storage" - ) - if [ -f "$(get_app_defaults_path)" ]; then - settings_items+=("4" "Edit App.vars for ${APP}") - settings_items+=("5" "Exit") - else - settings_items+=("4" "Exit") - fi - - local choice - choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ - --title "Community-Scripts SETTINGS Menu" \ - --ok-button "OK" --cancel-button "Back" \ - --menu "\n\nChoose a settings option:\n\nUse TAB or Arrow keys to navigate, ENTER to select." 20 60 9 \ - "${settings_items[@]}" \ - 3>&1 1>&2 2>&3) || break - - case "$choice" in - 1) diagnostics_menu ;; - 2) ${EDITOR:-nano} /usr/local/community-scripts/default.vars ;; - 3) edit_default_storage ;; - 4) - if [ -f "$(get_app_defaults_path)" ]; then - ${EDITOR:-nano} "$(get_app_defaults_path)" - else - exit_script - fi - ;; - 5) exit_script ;; - esac - done -} - -# ===== Unified storage selection & writing to vars files ===== _write_storage_to_vars() { # $1 = vars_file, $2 = key (var_container_storage / var_template_storage), $3 = value local vf="$1" key="$2" val="$3" @@ -1747,271 +1753,231 @@ choose_and_set_storage_for_file() { } # ------------------------------------------------------------------------------ -# check_container_resources() -# -# - Compares host RAM/CPU with required values -# - Warns if under-provisioned and asks user to continue or abort +# Storage discovery / selection helpers # ------------------------------------------------------------------------------ -check_container_resources() { - current_ram=$(free -m | awk 'NR==2{print $2}') - current_cpu=$(nproc) +resolve_storage_preselect() { + local class="$1" preselect="$2" required_content="" + case "$class" in + template) required_content="vztmpl" ;; + container) required_content="rootdir" ;; + *) return 1 ;; + esac + [[ -z "$preselect" ]] && return 1 + if ! pvesm status -content "$required_content" | awk 'NR>1{print $1}' | grep -qx -- "$preselect"; then + msg_warn "Preselected storage '${preselect}' does not support content '${required_content}' (or not found)" + return 1 + fi - if [[ "$current_ram" -lt "$var_ram" ]] || [[ "$current_cpu" -lt "$var_cpu" ]]; then - echo -e "\n${INFO}${HOLD} ${GN}Required: ${var_cpu} CPU, ${var_ram}MB RAM ${CL}| ${RD}Current: ${current_cpu} CPU, ${current_ram}MB RAM${CL}" - echo -e "${YWB}Please ensure that the ${APP} LXC is configured with at least ${var_cpu} vCPU and ${var_ram} MB RAM for the build process.${CL}\n" - echo -ne "${INFO}${HOLD} May cause data loss! ${INFO} Continue update with under-provisioned LXC? " - read -r prompt - if [[ ! ${prompt,,} =~ ^(yes)$ ]]; then - echo -e "${CROSS}${HOLD} ${YWB}Exiting based on user input.${CL}" - exit 1 - fi + local line total used free + line="$(pvesm status | awk -v s="$preselect" 'NR>1 && $1==s {print $0}')" + if [[ -z "$line" ]]; then + STORAGE_INFO="n/a" else - echo -e "" - fi -} - -# ------------------------------------------------------------------------------ -# check_container_storage() -# -# - Checks /boot partition usage -# - Warns if usage >80% and asks user confirmation before proceeding -# ------------------------------------------------------------------------------ -check_container_storage() { - total_size=$(df /boot --output=size | tail -n 1) - local used_size=$(df /boot --output=used | tail -n 1) - usage=$((100 * used_size / total_size)) - if ((usage > 80)); then - echo -e "${INFO}${HOLD} ${YWB}Warning: Storage is dangerously low (${usage}%).${CL}" - echo -ne "Continue anyway? " - read -r prompt - if [[ ! ${prompt,,} =~ ^(y|yes)$ ]]; then - echo -e "${CROSS}${HOLD}${YWB}Exiting based on user input.${CL}" - exit 1 + total="$(awk '{print $4}' <<<"$line")" + used="$(awk '{print $5}' <<<"$line")" + free="$(awk '{print $6}' <<<"$line")" + local total_h used_h free_h + if command -v numfmt >/dev/null 2>&1; then + total_h="$(numfmt --to=iec --suffix=B --format %.1f "$total" 2>/dev/null || echo "$total")" + used_h="$(numfmt --to=iec --suffix=B --format %.1f "$used" 2>/dev/null || echo "$used")" + free_h="$(numfmt --to=iec --suffix=B --format %.1f "$free" 2>/dev/null || echo "$free")" + STORAGE_INFO="Free: ${free_h} Used: ${used_h}" + else + STORAGE_INFO="Free: ${free} Used: ${used}" fi fi + STORAGE_RESULT="$preselect" + return 0 } -# ------------------------------------------------------------------------------ -# ssh_extract_keys_from_file() -# -# - Extracts valid SSH public keys from given file -# - Supports RSA, Ed25519, ECDSA and filters out comments/invalid lines -# ------------------------------------------------------------------------------ -ssh_extract_keys_from_file() { - local f="$1" - [[ -r "$f" ]] || return 0 - tr -d '\r' <"$f" | awk ' - /^[[:space:]]*#/ {next} - /^[[:space:]]*$/ {next} - # nackt: typ base64 [comment] - /^(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/ {print; next} - # mit Optionen: finde ab erstem Key-Typ - { - match($0, /(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/) - if (RSTART>0) { print substr($0, RSTART) } - } - ' -} - -# ------------------------------------------------------------------------------ -# ssh_build_choices_from_files() -# -# - Builds interactive whiptail checklist of available SSH keys -# - Generates fingerprint, type and comment for each key -# ------------------------------------------------------------------------------ -ssh_build_choices_from_files() { - local -a files=("$@") - CHOICES=() - COUNT=0 - MAPFILE="$(mktemp)" - local id key typ fp cmt base ln=0 - - for f in "${files[@]}"; do - [[ -f "$f" && -r "$f" ]] || continue - base="$(basename -- "$f")" - case "$base" in - known_hosts | known_hosts.* | config) continue ;; - id_*) [[ "$f" != *.pub ]] && continue ;; - esac - - # map every key in file - while IFS= read -r key; do - [[ -n "$key" ]] || continue - - typ="" - fp="" - cmt="" - # Only the pure key part (without options) is already included in ‘key’. - read -r _typ _b64 _cmt <<<"$key" - typ="${_typ:-key}" - cmt="${_cmt:-}" - # Fingerprint via ssh-keygen (if available) - if command -v ssh-keygen >/dev/null 2>&1; then - fp="$(printf '%s\n' "$key" | ssh-keygen -lf - 2>/dev/null | awk '{print $2}')" - fi - # Label shorten - [[ ${#cmt} -gt 40 ]] && cmt="${cmt:0:37}..." - - ln=$((ln + 1)) - COUNT=$((COUNT + 1)) - id="K${COUNT}" - echo "${id}|${key}" >>"$MAPFILE" - CHOICES+=("$id" "[$typ] ${fp:+$fp }${cmt:+$cmt }— ${base}" "OFF") - done < <(ssh_extract_keys_from_file "$f") - done -} - -# ------------------------------------------------------------------------------ -# ssh_discover_default_files() -# -# - Scans standard paths for SSH keys -# - Includes ~/.ssh/*.pub, /etc/ssh/authorized_keys, etc. -# ------------------------------------------------------------------------------ -ssh_discover_default_files() { - local -a cand=() - shopt -s nullglob - cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2) - cand+=(/root/.ssh/*.pub) - cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*) - shopt -u nullglob - printf '%s\0' "${cand[@]}" -} - -configure_ssh_settings() { - SSH_KEYS_FILE="$(mktemp)" - : >"$SSH_KEYS_FILE" - - IFS=$'\0' read -r -d '' -a _def_files < <(ssh_discover_default_files && printf '\0') - ssh_build_choices_from_files "${_def_files[@]}" - local default_key_count="$COUNT" - - local ssh_key_mode - if [[ "$default_key_count" -gt 0 ]]; then - ssh_key_mode=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ - "Provision SSH keys for root:" 14 72 4 \ - "found" "Select from detected keys (${default_key_count})" \ - "manual" "Paste a single public key" \ - "folder" "Scan another folder (path or glob)" \ - "none" "No keys" 3>&1 1>&2 2>&3) || exit_script - else - ssh_key_mode=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ - "No host keys detected; choose manual/none:" 12 72 2 \ - "manual" "Paste a single public key" \ - "none" "No keys" 3>&1 1>&2 2>&3) || exit_script - fi - - case "$ssh_key_mode" in - found) - local selection - selection=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT HOST KEYS" \ - --checklist "Select one or more keys to import:" 20 140 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script - for tag in $selection; do - tag="${tag%\"}" - tag="${tag#\"}" - local line - line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) - [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" - done +select_storage() { + local CLASS=$1 CONTENT CONTENT_LABEL + case $CLASS in + container) + CONTENT='rootdir' + CONTENT_LABEL='Container' ;; - manual) - SSH_AUTHORIZED_KEY="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --inputbox "Paste one SSH public key line (ssh-ed25519/ssh-rsa/...)" 10 72 --title "SSH Public Key" 3>&1 1>&2 2>&3)" - [[ -n "$SSH_AUTHORIZED_KEY" ]] && printf '%s\n' "$SSH_AUTHORIZED_KEY" >>"$SSH_KEYS_FILE" + template) + CONTENT='vztmpl' + CONTENT_LABEL='Container template' ;; - folder) - local glob_path - glob_path=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --inputbox "Enter a folder or glob to scan (e.g. /root/.ssh/*.pub)" 10 72 --title "Scan Folder/Glob" 3>&1 1>&2 2>&3) - if [[ -n "$glob_path" ]]; then - shopt -s nullglob - read -r -a _scan_files <<<"$glob_path" - shopt -u nullglob - if [[ "${#_scan_files[@]}" -gt 0 ]]; then - ssh_build_choices_from_files "${_scan_files[@]}" - if [[ "$COUNT" -gt 0 ]]; then - local folder_selection - folder_selection=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT FOLDER KEYS" \ - --checklist "Select key(s) to import:" 20 78 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script - for tag in $folder_selection; do - tag="${tag%\"}" - tag="${tag#\"}" - local line - line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) - [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" - done - else - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "No keys found in: $glob_path" 8 60 - fi - else - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "Path/glob returned no files." 8 60 - fi - fi + iso) + CONTENT='iso' + CONTENT_LABEL='ISO image' ;; - none) - : + images) + CONTENT='images' + CONTENT_LABEL='VM Disk image' + ;; + backup) + CONTENT='backup' + CONTENT_LABEL='Backup' + ;; + snippets) + CONTENT='snippets' + CONTENT_LABEL='Snippets' + ;; + *) + msg_error "Invalid storage class '$CLASS'" + return 1 ;; esac - if [[ -s "$SSH_KEYS_FILE" ]]; then - sort -u -o "$SSH_KEYS_FILE" "$SSH_KEYS_FILE" - printf '\n' >>"$SSH_KEYS_FILE" + declare -A STORAGE_MAP + local -a MENU=() + local COL_WIDTH=0 + + while read -r TAG TYPE _ TOTAL USED FREE _; do + [[ -n "$TAG" && -n "$TYPE" ]] || continue + local DISPLAY="${TAG} (${TYPE})" + local USED_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$USED") + local FREE_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$FREE") + local INFO="Free: ${FREE_FMT}B Used: ${USED_FMT}B" + STORAGE_MAP["$DISPLAY"]="$TAG" + MENU+=("$DISPLAY" "$INFO" "OFF") + ((${#DISPLAY} > COL_WIDTH)) && COL_WIDTH=${#DISPLAY} + done < <(pvesm status -content "$CONTENT" | awk 'NR>1') + + if [[ ${#MENU[@]} -eq 0 ]]; then + msg_error "No storage found for content type '$CONTENT'." + return 2 fi - if [[ -s "$SSH_KEYS_FILE" || "$PW" == -password* ]]; then - if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable root SSH access?" 10 58); then - SSH="yes" - else - SSH="no" - fi - else - SSH="no" - fi -} - -# ------------------------------------------------------------------------------ -# start() -# -# - Entry point of script -# - On Proxmox host: calls install_script -# - In silent mode: runs update_script -# - Otherwise: shows update/setting menu -# ------------------------------------------------------------------------------ -start() { - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) - if command -v pveversion >/dev/null 2>&1; then - install_script || return 0 + if [[ $((${#MENU[@]} / 3)) -eq 1 ]]; then + STORAGE_RESULT="${STORAGE_MAP[${MENU[0]}]}" + STORAGE_INFO="${MENU[1]}" return 0 - elif [ ! -z ${PHS_SILENT+x} ] && [[ "${PHS_SILENT}" == "1" ]]; then - VERBOSE="no" - set_std_mode - update_script - else - CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "${APP} LXC Update/Setting" --menu \ - "Support/Update functions for ${APP} LXC. Choose an option:" \ - 12 60 3 \ - "1" "YES (Silent Mode)" \ - "2" "YES (Verbose Mode)" \ - "3" "NO (Cancel Update)" --nocancel --default-item "1" 3>&1 1>&2 2>&3) + fi - case "$CHOICE" in - 1) - VERBOSE="no" - set_std_mode - ;; - 2) - VERBOSE="yes" - set_std_mode - ;; - 3) - clear - exit_script - exit - ;; - esac - update_script + local WIDTH=$((COL_WIDTH + 42)) + while true; do + local DISPLAY_SELECTED + DISPLAY_SELECTED=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "Storage Pools" \ + --radiolist "Which storage pool for ${CONTENT_LABEL,,}?\n(Spacebar to select)" \ + 16 "$WIDTH" 6 "${MENU[@]}" 3>&1 1>&2 2>&3) || { exit_script; } + + DISPLAY_SELECTED=$(sed 's/[[:space:]]*$//' <<<"$DISPLAY_SELECTED") + if [[ -z "$DISPLAY_SELECTED" || -z "${STORAGE_MAP[$DISPLAY_SELECTED]+_}" ]]; then + whiptail --msgbox "No valid storage selected. Please try again." 8 58 + continue + fi + STORAGE_RESULT="${STORAGE_MAP[$DISPLAY_SELECTED]}" + for ((i = 0; i < ${#MENU[@]}; i += 3)); do + if [[ "${MENU[$i]}" == "$DISPLAY_SELECTED" ]]; then + STORAGE_INFO="${MENU[$i + 1]}" + break + fi + done + return 0 + done +} + + +# ============================================================================== +# GPU & HARDWARE PASSTHROUGH +# ============================================================================== + +fix_gpu_gids() { + if [[ -z "${GPU_TYPE:-}" ]]; then + return 0 + fi + + msg_info "Detecting and setting correct GPU group IDs" + + # Ermittle die tatsächlichen GIDs aus dem Container + local video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") + local render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") + + # Fallbacks wenn Gruppen nicht existieren + if [[ -z "$video_gid" ]]; then + # Versuche die video Gruppe zu erstellen + pct exec "$CTID" -- sh -c "groupadd -r video 2>/dev/null || true" + video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") + [[ -z "$video_gid" ]] && video_gid="44" # Ultimate fallback + fi + + if [[ -z "$render_gid" ]]; then + # Versuche die render Gruppe zu erstellen + pct exec "$CTID" -- sh -c "groupadd -r render 2>/dev/null || true" + render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") + [[ -z "$render_gid" ]] && render_gid="104" # Ultimate fallback + fi + + msg_info "Container GIDs detected - video:${video_gid}, render:${render_gid}" + + # Prüfe ob die GIDs von den Defaults abweichen + local need_update=0 + if [[ "$video_gid" != "44" ]] || [[ "$render_gid" != "104" ]]; then + need_update=1 + fi + + if [[ $need_update -eq 1 ]]; then + msg_info "Updating device GIDs in container config" + + # Stoppe Container für Config-Update + pct stop "$CTID" >/dev/null 2>&1 + + # Update die dev Einträge mit korrekten GIDs + # Backup der Config + cp "$LXC_CONFIG" "${LXC_CONFIG}.bak" + + # Parse und update jeden dev Eintrag + while IFS= read -r line; do + if [[ "$line" =~ ^dev[0-9]+: ]]; then + # Extract device path + local device_path=$(echo "$line" | sed -E 's/^dev[0-9]+: ([^,]+).*/\1/') + local dev_num=$(echo "$line" | sed -E 's/^(dev[0-9]+):.*/\1/') + + if [[ "$device_path" =~ renderD ]]; then + # RenderD device - use render GID + echo "${dev_num}: ${device_path},gid=${render_gid}" + elif [[ "$device_path" =~ card ]]; then + # Card device - use video GID + echo "${dev_num}: ${device_path},gid=${video_gid}" + else + # Keep original line + echo "$line" + fi + else + # Keep non-dev lines + echo "$line" + fi + done <"$LXC_CONFIG" >"${LXC_CONFIG}.new" + + mv "${LXC_CONFIG}.new" "$LXC_CONFIG" + + # Starte Container wieder + pct start "$CTID" >/dev/null 2>&1 + sleep 3 + + msg_ok "Device GIDs updated successfully" + else + msg_ok "Device GIDs are already correct" + fi + if [[ "$CT_TYPE" == "0" ]]; then + pct exec "$CTID" -- bash -c " + if [ -d /dev/dri ]; then + for dev in /dev/dri/*; do + if [ -e \"\$dev\" ]; then + if [[ \"\$dev\" =~ renderD ]]; then + chgrp ${render_gid} \"\$dev\" 2>/dev/null || true + else + chgrp ${video_gid} \"\$dev\" 2>/dev/null || true + fi + chmod 660 \"\$dev\" 2>/dev/null || true + fi + done + fi + " >/dev/null 2>&1 fi } + +# ============================================================================== +# CONTAINER LIFECYCLE & CREATION +# ============================================================================== + # ------------------------------------------------------------------------------ # build_container() # @@ -2566,253 +2532,6 @@ destroy_lxc() { esac } -# ------------------------------------------------------------------------------ -# Storage discovery / selection helpers -# ------------------------------------------------------------------------------ -# ===== Storage discovery / selection helpers (ported from create_lxc.sh) ===== -resolve_storage_preselect() { - local class="$1" preselect="$2" required_content="" - case "$class" in - template) required_content="vztmpl" ;; - container) required_content="rootdir" ;; - *) return 1 ;; - esac - [[ -z "$preselect" ]] && return 1 - if ! pvesm status -content "$required_content" | awk 'NR>1{print $1}' | grep -qx -- "$preselect"; then - msg_warn "Preselected storage '${preselect}' does not support content '${required_content}' (or not found)" - return 1 - fi - - local line total used free - line="$(pvesm status | awk -v s="$preselect" 'NR>1 && $1==s {print $0}')" - if [[ -z "$line" ]]; then - STORAGE_INFO="n/a" - else - total="$(awk '{print $4}' <<<"$line")" - used="$(awk '{print $5}' <<<"$line")" - free="$(awk '{print $6}' <<<"$line")" - local total_h used_h free_h - if command -v numfmt >/dev/null 2>&1; then - total_h="$(numfmt --to=iec --suffix=B --format %.1f "$total" 2>/dev/null || echo "$total")" - used_h="$(numfmt --to=iec --suffix=B --format %.1f "$used" 2>/dev/null || echo "$used")" - free_h="$(numfmt --to=iec --suffix=B --format %.1f "$free" 2>/dev/null || echo "$free")" - STORAGE_INFO="Free: ${free_h} Used: ${used_h}" - else - STORAGE_INFO="Free: ${free} Used: ${used}" - fi - fi - STORAGE_RESULT="$preselect" - return 0 -} - -fix_gpu_gids() { - if [[ -z "${GPU_TYPE:-}" ]]; then - return 0 - fi - - msg_info "Detecting and setting correct GPU group IDs" - - # Ermittle die tatsächlichen GIDs aus dem Container - local video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") - local render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") - - # Fallbacks wenn Gruppen nicht existieren - if [[ -z "$video_gid" ]]; then - # Versuche die video Gruppe zu erstellen - pct exec "$CTID" -- sh -c "groupadd -r video 2>/dev/null || true" - video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") - [[ -z "$video_gid" ]] && video_gid="44" # Ultimate fallback - fi - - if [[ -z "$render_gid" ]]; then - # Versuche die render Gruppe zu erstellen - pct exec "$CTID" -- sh -c "groupadd -r render 2>/dev/null || true" - render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") - [[ -z "$render_gid" ]] && render_gid="104" # Ultimate fallback - fi - - msg_info "Container GIDs detected - video:${video_gid}, render:${render_gid}" - - # Prüfe ob die GIDs von den Defaults abweichen - local need_update=0 - if [[ "$video_gid" != "44" ]] || [[ "$render_gid" != "104" ]]; then - need_update=1 - fi - - if [[ $need_update -eq 1 ]]; then - msg_info "Updating device GIDs in container config" - - # Stoppe Container für Config-Update - pct stop "$CTID" >/dev/null 2>&1 - - # Update die dev Einträge mit korrekten GIDs - # Backup der Config - cp "$LXC_CONFIG" "${LXC_CONFIG}.bak" - - # Parse und update jeden dev Eintrag - while IFS= read -r line; do - if [[ "$line" =~ ^dev[0-9]+: ]]; then - # Extract device path - local device_path=$(echo "$line" | sed -E 's/^dev[0-9]+: ([^,]+).*/\1/') - local dev_num=$(echo "$line" | sed -E 's/^(dev[0-9]+):.*/\1/') - - if [[ "$device_path" =~ renderD ]]; then - # RenderD device - use render GID - echo "${dev_num}: ${device_path},gid=${render_gid}" - elif [[ "$device_path" =~ card ]]; then - # Card device - use video GID - echo "${dev_num}: ${device_path},gid=${video_gid}" - else - # Keep original line - echo "$line" - fi - else - # Keep non-dev lines - echo "$line" - fi - done <"$LXC_CONFIG" >"${LXC_CONFIG}.new" - - mv "${LXC_CONFIG}.new" "$LXC_CONFIG" - - # Starte Container wieder - pct start "$CTID" >/dev/null 2>&1 - sleep 3 - - msg_ok "Device GIDs updated successfully" - else - msg_ok "Device GIDs are already correct" - fi - if [[ "$CT_TYPE" == "0" ]]; then - pct exec "$CTID" -- bash -c " - if [ -d /dev/dri ]; then - for dev in /dev/dri/*; do - if [ -e \"\$dev\" ]; then - if [[ \"\$dev\" =~ renderD ]]; then - chgrp ${render_gid} \"\$dev\" 2>/dev/null || true - else - chgrp ${video_gid} \"\$dev\" 2>/dev/null || true - fi - chmod 660 \"\$dev\" 2>/dev/null || true - fi - done - fi - " >/dev/null 2>&1 - fi -} - -# NVIDIA-spezific check on host -check_nvidia_host_setup() { - if ! command -v nvidia-smi >/dev/null 2>&1; then - msg_warn "NVIDIA GPU detected but nvidia-smi not found on host" - msg_warn "Please install NVIDIA drivers on host first." - #echo " 1. Download driver: wget https://us.download.nvidia.com/XFree86/Linux-x86_64/550.127.05/NVIDIA-Linux-x86_64-550.127.05.run" - #echo " 2. Install: ./NVIDIA-Linux-x86_64-550.127.05.run --dkms" - #echo " 3. Verify: nvidia-smi" - return 1 - fi - - # check if nvidia-smi works - if ! nvidia-smi >/dev/null 2>&1; then - msg_warn "nvidia-smi installed but not working. Driver issue?" - return 1 - fi - - return 0 -} - -check_storage_support() { - local CONTENT="$1" VALID=0 - while IFS= read -r line; do - local STORAGE_NAME - STORAGE_NAME=$(awk '{print $1}' <<<"$line") - [[ -n "$STORAGE_NAME" ]] && VALID=1 - done < <(pvesm status -content "$CONTENT" 2>/dev/null | awk 'NR>1') - [[ $VALID -eq 1 ]] -} - -select_storage() { - local CLASS=$1 CONTENT CONTENT_LABEL - case $CLASS in - container) - CONTENT='rootdir' - CONTENT_LABEL='Container' - ;; - template) - CONTENT='vztmpl' - CONTENT_LABEL='Container template' - ;; - iso) - CONTENT='iso' - CONTENT_LABEL='ISO image' - ;; - images) - CONTENT='images' - CONTENT_LABEL='VM Disk image' - ;; - backup) - CONTENT='backup' - CONTENT_LABEL='Backup' - ;; - snippets) - CONTENT='snippets' - CONTENT_LABEL='Snippets' - ;; - *) - msg_error "Invalid storage class '$CLASS'" - return 1 - ;; - esac - - declare -A STORAGE_MAP - local -a MENU=() - local COL_WIDTH=0 - - while read -r TAG TYPE _ TOTAL USED FREE _; do - [[ -n "$TAG" && -n "$TYPE" ]] || continue - local DISPLAY="${TAG} (${TYPE})" - local USED_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$USED") - local FREE_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$FREE") - local INFO="Free: ${FREE_FMT}B Used: ${USED_FMT}B" - STORAGE_MAP["$DISPLAY"]="$TAG" - MENU+=("$DISPLAY" "$INFO" "OFF") - ((${#DISPLAY} > COL_WIDTH)) && COL_WIDTH=${#DISPLAY} - done < <(pvesm status -content "$CONTENT" | awk 'NR>1') - - if [[ ${#MENU[@]} -eq 0 ]]; then - msg_error "No storage found for content type '$CONTENT'." - return 2 - fi - - if [[ $((${#MENU[@]} / 3)) -eq 1 ]]; then - STORAGE_RESULT="${STORAGE_MAP[${MENU[0]}]}" - STORAGE_INFO="${MENU[1]}" - return 0 - fi - - local WIDTH=$((COL_WIDTH + 42)) - while true; do - local DISPLAY_SELECTED - DISPLAY_SELECTED=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "Storage Pools" \ - --radiolist "Which storage pool for ${CONTENT_LABEL,,}?\n(Spacebar to select)" \ - 16 "$WIDTH" 6 "${MENU[@]}" 3>&1 1>&2 2>&3) || { exit_script; } - - DISPLAY_SELECTED=$(sed 's/[[:space:]]*$//' <<<"$DISPLAY_SELECTED") - if [[ -z "$DISPLAY_SELECTED" || -z "${STORAGE_MAP[$DISPLAY_SELECTED]+_}" ]]; then - whiptail --msgbox "No valid storage selected. Please try again." 8 58 - continue - fi - STORAGE_RESULT="${STORAGE_MAP[$DISPLAY_SELECTED]}" - for ((i = 0; i < ${#MENU[@]}; i += 3)); do - if [[ "${MENU[$i]}" == "$DISPLAY_SELECTED" ]]; then - STORAGE_INFO="${MENU[$i + 1]}" - break - fi - done - return 0 - done -} - create_lxc_container() { # ------------------------------------------------------------------------------ # Optional verbose mode (debug tracing) @@ -3480,6 +3199,187 @@ EOF post_update_to_api "done" "none" } + +# ============================================================================== +# MAIN ENTRY POINTS & ERROR HANDLING +# ============================================================================== + +# ------------------------------------------------------------------------------ +# install_script() +# +# - Main entrypoint for installation mode +# - Runs safety checks (pve_check, root_check, maxkeys_check, diagnostics_check) +# - Builds interactive menu (Default, Verbose, Advanced, My Defaults, App Defaults, Diagnostics, Storage, Exit) +# - Applies chosen settings and triggers container build +# ------------------------------------------------------------------------------ +install_script() { + pve_check + shell_check + root_check + arch_check + ssh_check + maxkeys_check + diagnostics_check + + if systemctl is-active -q ping-instances.service; then + systemctl -q stop ping-instances.service + fi + + NEXTID=$(pvesh get /cluster/nextid) + timezone=$(cat /etc/timezone) + + # Show APP Header + header_info + + # --- Support CLI argument as direct preset (default, advanced, …) --- + CHOICE="${mode:-${1:-}}" + + # If no CLI argument → show whiptail menu + # Build menu dynamically based on available options + local appdefaults_option="" + local settings_option="" + local menu_items=( + "1" "Default Install" + "2" "Advanced Install" + "3" "My Defaults" + ) + + if [ -f "$(get_app_defaults_path)" ]; then + appdefaults_option="4" + menu_items+=("4" "App Defaults for ${APP}") + settings_option="5" + menu_items+=("5" "Settings") + else + settings_option="4" + menu_items+=("4" "Settings") + fi + + if [ -z "$CHOICE" ]; then + + TMP_CHOICE=$(whiptail \ + --backtitle "Proxmox VE Helper Scripts" \ + --title "Community-Scripts Options" \ + --ok-button "Select" --cancel-button "Exit Script" \ + --notags \ + --menu "\nChoose an option:\n Use TAB or Arrow keys to navigate, ENTER to select.\n" \ + 20 60 9 \ + "${menu_items[@]}" \ + --default-item "1" \ + 3>&1 1>&2 2>&3) || exit_script + CHOICE="$TMP_CHOICE" + fi + + APPDEFAULTS_OPTION="$appdefaults_option" + SETTINGS_OPTION="$settings_option" + + # --- Main case --- + local defaults_target="" + local run_maybe_offer="no" + case "$CHOICE" in + 1 | default | DEFAULT) + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME${CL}" + VERBOSE="no" + METHOD="default" + base_settings "$VERBOSE" + echo_default + defaults_target="$(ensure_global_default_vars_file)" + ;; + 2 | advanced | ADVANCED) + header_info + + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Install on node $PVEHOST_NAME${CL}" + echo -e "${INFO}${BOLD}${DGN}PVE Version ${PVEVERSION} (Kernel: ${KERNEL_VERSION})${CL}" + METHOD="advanced" + base_settings + advanced_settings + defaults_target="$(ensure_global_default_vars_file)" + run_maybe_offer="yes" + ;; + 3 | mydefaults | MYDEFAULTS) + default_var_settings || { + msg_error "Failed to apply default.vars" + exit 1 + } + defaults_target="/usr/local/community-scripts/default.vars" + ;; + "$APPDEFAULTS_OPTION" | appdefaults | APPDEFAULTS) + if [ -f "$(get_app_defaults_path)" ]; then + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using App Defaults for ${APP} on node $PVEHOST_NAME${CL}" + METHOD="appdefaults" + base_settings + _load_vars_file "$(get_app_defaults_path)" + echo_default + defaults_target="$(get_app_defaults_path)" + else + msg_error "No App Defaults available for ${APP}" + exit 1 + fi + ;; + "$SETTINGS_OPTION" | settings | SETTINGS) + settings_menu + defaults_target="" + ;; + *) + echo -e "${CROSS}${RD}Invalid option: $CHOICE${CL}" + exit 1 + ;; + esac + + if [[ -n "$defaults_target" ]]; then + ensure_storage_selection_for_vars_file "$defaults_target" + fi + + if [[ "$run_maybe_offer" == "yes" ]]; then + maybe_offer_save_app_defaults + fi +} + +# ------------------------------------------------------------------------------ +# start() +# +# - Entry point of script +# - On Proxmox host: calls install_script +# - In silent mode: runs update_script +# - Otherwise: shows update/setting menu +# ------------------------------------------------------------------------------ +start() { + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) + if command -v pveversion >/dev/null 2>&1; then + install_script || return 0 + return 0 + elif [ ! -z ${PHS_SILENT+x} ] && [[ "${PHS_SILENT}" == "1" ]]; then + VERBOSE="no" + set_std_mode + update_script + else + CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "${APP} LXC Update/Setting" --menu \ + "Support/Update functions for ${APP} LXC. Choose an option:" \ + 12 60 3 \ + "1" "YES (Silent Mode)" \ + "2" "YES (Verbose Mode)" \ + "3" "NO (Cancel Update)" --nocancel --default-item "1" 3>&1 1>&2 2>&3) + + case "$CHOICE" in + 1) + VERBOSE="no" + set_std_mode + ;; + 2) + VERBOSE="yes" + set_std_mode + ;; + 3) + clear + exit_script + exit + ;; + esac + update_script + fi +} + # ------------------------------------------------------------------------------ # api_exit_script() # @@ -3508,9 +3408,28 @@ api_exit_script() { fi } -if command -v pveversion >/dev/null 2>&1; then - trap 'api_exit_script' EXIT -fi -trap 'post_update_to_api "failed" "$BASH_COMMAND"' ERR -trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT -trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM + +# ============================================================================== +# DEPENDENCY LOADING +# ============================================================================== + +# Community-Scripts bootstrap loader + +# Load core + error handler functions from community-scripts repo + +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) + +if command -v curl >/dev/null 2>&1; then + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) + load_functions + catch_errors +elif command -v wget >/dev/null 2>&1; then + source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) + load_functions + catch_errors + + + declare -ag VAR_WHITELIST=( + var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_ctid var_disk var_fuse From b6812877e5b40c36e7149caca2cd1e129f690c99 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 29 Oct 2025 12:50:19 +0100 Subject: [PATCH 1602/1733] Revert build.func optimization - restore working version from backup --- misc/build.func | 1734 +++++++++++++++++++++++++---------------------- 1 file changed, 908 insertions(+), 826 deletions(-) diff --git a/misc/build.func b/misc/build.func index a7e377dcf..d452f4637 100644 --- a/misc/build.func +++ b/misc/build.func @@ -5,7 +5,7 @@ # Revision: 1 # ============================================================================== -# CORE INITIALIZATION & VARIABLES +# SECTION 1: CORE INITIALIZATION & VARIABLES # ============================================================================== # ------------------------------------------------------------------------------ @@ -39,10 +39,124 @@ variables() { KERNEL_VERSION=$(uname -r) } +# ----------------------------------------------------------------------------- +# Community-Scripts bootstrap loader +# - Always sources build.func from remote +# - Updates local core files only if build.func changed +# - Local cache: /usr/local/community-scripts/core +# ----------------------------------------------------------------------------- -# ============================================================================== -# SYSTEM VALIDATION & CHECKS -# ============================================================================== +# FUNC_DIR="/usr/local/community-scripts/core" +# mkdir -p "$FUNC_DIR" + +# BUILD_URL="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func" +# BUILD_REV="$FUNC_DIR/build.rev" +# DEVMODE="${DEVMODE:-no}" + +# # --- Step 1: fetch build.func content once, compute hash --- +# build_content="$(curl -fsSL "$BUILD_URL")" || { +# echo "❌ Failed to fetch build.func" +# exit 1 +# } + +# newhash=$(printf "%s" "$build_content" | sha256sum | awk '{print $1}') +# oldhash=$(cat "$BUILD_REV" 2>/dev/null || echo "") + +# # --- Step 2: if build.func changed, offer update for core files --- +# if [ "$newhash" != "$oldhash" ]; then +# echo "⚠️ build.func changed!" + +# while true; do +# read -rp "Refresh local core files? [y/N/diff]: " ans +# case "$ans" in +# [Yy]*) +# echo "$newhash" >"$BUILD_REV" + +# update_func_file() { +# local file="$1" +# local url="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/$file" +# local local_path="$FUNC_DIR/$file" + +# echo "⬇️ Downloading $file ..." +# curl -fsSL "$url" -o "$local_path" || { +# echo "❌ Failed to fetch $file" +# exit 1 +# } +# echo "✔️ Updated $file" +# } + +# update_func_file core.func +# update_func_file error_handler.func +# update_func_file tools.func +# break +# ;; +# [Dd]*) +# for file in core.func error_handler.func tools.func; do +# local_path="$FUNC_DIR/$file" +# url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/$file" +# remote_tmp="$(mktemp)" + +# curl -fsSL "$url" -o "$remote_tmp" || continue + +# if [ -f "$local_path" ]; then +# echo "🔍 Diff for $file:" +# diff -u "$local_path" "$remote_tmp" || echo "(no differences)" +# else +# echo "📦 New file $file will be installed" +# fi + +# rm -f "$remote_tmp" +# done +# ;; +# *) +# echo "❌ Skipped updating local core files" +# break +# ;; +# esac +# done +# else +# if [ "$DEVMODE" != "yes" ]; then +# echo "✔️ build.func unchanged → using existing local core files" +# fi +# fi + +# if [ -n "${_COMMUNITY_SCRIPTS_LOADER:-}" ]; then +# return 0 2>/dev/null || exit 0 +# fi +# _COMMUNITY_SCRIPTS_LOADER=1 + +# # --- Step 3: always source local versions of the core files --- +# source "$FUNC_DIR/core.func" +# source "$FUNC_DIR/error_handler.func" +# source "$FUNC_DIR/tools.func" + +# # --- Step 4: finally, source build.func directly from memory --- +# # (no tmp file needed) +# source <(printf "%s" "$build_content") + +# ------------------------------------------------------------------------------ +# Load core + error handler functions from community-scripts repo +# +# - Prefer curl if available, fallback to wget +# - Load: core.func, error_handler.func, api.func +# - Initialize error traps after loading +# ------------------------------------------------------------------------------ + +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) + +if command -v curl >/dev/null 2>&1; then + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) + load_functions + catch_errors + #echo "(build.func) Loaded core.func via curl" +elif command -v wget >/dev/null 2>&1; then + source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) + load_functions + catch_errors + #echo "(build.func) Loaded core.func via wget" +fi # ------------------------------------------------------------------------------ # maxkeys_check() @@ -53,6 +167,7 @@ variables() { # - Exits if thresholds are exceeded # - https://cleveruptime.com/docs/files/proc-key-users | https://docs.kernel.org/security/keys/core.html # ------------------------------------------------------------------------------ + maxkeys_check() { # Read kernel parameters per_user_maxkeys=$(cat /proc/sys/kernel/keys/maxkeys 2>/dev/null || echo 0) @@ -96,86 +211,6 @@ maxkeys_check() { echo -e "${CM}${GN} All kernel key limits are within safe thresholds.${CL}" } -# ------------------------------------------------------------------------------ -# check_container_resources() -# -# - Compares host RAM/CPU with required values -# - Warns if under-provisioned and asks user to continue or abort -# ------------------------------------------------------------------------------ -check_container_resources() { - current_ram=$(free -m | awk 'NR==2{print $2}') - current_cpu=$(nproc) - - if [[ "$current_ram" -lt "$var_ram" ]] || [[ "$current_cpu" -lt "$var_cpu" ]]; then - echo -e "\n${INFO}${HOLD} ${GN}Required: ${var_cpu} CPU, ${var_ram}MB RAM ${CL}| ${RD}Current: ${current_cpu} CPU, ${current_ram}MB RAM${CL}" - echo -e "${YWB}Please ensure that the ${APP} LXC is configured with at least ${var_cpu} vCPU and ${var_ram} MB RAM for the build process.${CL}\n" - echo -ne "${INFO}${HOLD} May cause data loss! ${INFO} Continue update with under-provisioned LXC? " - read -r prompt - if [[ ! ${prompt,,} =~ ^(yes)$ ]]; then - echo -e "${CROSS}${HOLD} ${YWB}Exiting based on user input.${CL}" - exit 1 - fi - else - echo -e "" - fi -} - -# ------------------------------------------------------------------------------ -# check_container_storage() -# -# - Checks /boot partition usage -# - Warns if usage >80% and asks user confirmation before proceeding -# ------------------------------------------------------------------------------ -check_container_storage() { - total_size=$(df /boot --output=size | tail -n 1) - local used_size=$(df /boot --output=used | tail -n 1) - usage=$((100 * used_size / total_size)) - if ((usage > 80)); then - echo -e "${INFO}${HOLD} ${YWB}Warning: Storage is dangerously low (${usage}%).${CL}" - echo -ne "Continue anyway? " - read -r prompt - if [[ ! ${prompt,,} =~ ^(y|yes)$ ]]; then - echo -e "${CROSS}${HOLD}${YWB}Exiting based on user input.${CL}" - exit 1 - fi - fi -} - -# NVIDIA-spezific check on host -check_nvidia_host_setup() { - if ! command -v nvidia-smi >/dev/null 2>&1; then - msg_warn "NVIDIA GPU detected but nvidia-smi not found on host" - msg_warn "Please install NVIDIA drivers on host first." - #echo " 1. Download driver: wget https://us.download.nvidia.com/XFree86/Linux-x86_64/550.127.05/NVIDIA-Linux-x86_64-550.127.05.run" - #echo " 2. Install: ./NVIDIA-Linux-x86_64-550.127.05.run --dkms" - #echo " 3. Verify: nvidia-smi" - return 1 - fi - - # check if nvidia-smi works - if ! nvidia-smi >/dev/null 2>&1; then - msg_warn "nvidia-smi installed but not working. Driver issue?" - return 1 - fi - - return 0 -} - -check_storage_support() { - local CONTENT="$1" VALID=0 - while IFS= read -r line; do - local STORAGE_NAME - STORAGE_NAME=$(awk '{print $1}' <<<"$line") - [[ -n "$STORAGE_NAME" ]] && VALID=1 - done < <(pvesm status -content "$CONTENT" 2>/dev/null | awk 'NR>1') - [[ $VALID -eq 1 ]] -} - - -# ============================================================================== -# NETWORK & IP MANAGEMENT -# ============================================================================== - # ------------------------------------------------------------------------------ # get_current_ip() # @@ -217,11 +252,6 @@ update_motd_ip() { fi } - -# ============================================================================== -# SSH KEY MANAGEMENT -# ============================================================================== - # ------------------------------------------------------------------------------ # install_ssh_keys_into_ct() # @@ -253,248 +283,6 @@ install_ssh_keys_into_ct() { return 0 } -# ------------------------------------------------------------------------------ -# find_host_ssh_keys() -# -# - Scans system for available SSH keys -# - Supports defaults (~/.ssh, /etc/ssh/authorized_keys) -# - Returns list of files containing valid SSH public keys -# - Sets FOUND_HOST_KEY_COUNT to number of keys found -# ------------------------------------------------------------------------------ -find_host_ssh_keys() { - local re='(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))' - local -a files=() cand=() - local g="${var_ssh_import_glob:-}" - local total=0 f base c - - shopt -s nullglob - if [[ -n "$g" ]]; then - for pat in $g; do cand+=($pat); done - else - cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2) - cand+=(/root/.ssh/*.pub) - cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*) - fi - shopt -u nullglob - - for f in "${cand[@]}"; do - [[ -f "$f" && -r "$f" ]] || continue - base="$(basename -- "$f")" - case "$base" in - known_hosts | known_hosts.* | config) continue ;; - id_*) [[ "$f" != *.pub ]] && continue ;; - esac - - # CRLF safe check for host keys - c=$(tr -d '\r' <"$f" | awk ' - /^[[:space:]]*#/ {next} - /^[[:space:]]*$/ {next} - {print} - ' | grep -E -c '"$re"' || true) - - if ((c > 0)); then - files+=("$f") - total=$((total + c)) - fi - done - - # Fallback to /root/.ssh/authorized_keys - if ((${#files[@]} == 0)) && [[ -r /root/.ssh/authorized_keys ]]; then - if grep -E -q "$re" /root/.ssh/authorized_keys; then - files+=(/root/.ssh/authorized_keys) - total=$((total + $(grep -E -c "$re" /root/.ssh/authorized_keys || echo 0))) - fi - fi - - FOUND_HOST_KEY_COUNT="$total" - ( - IFS=: - echo "${files[*]}" - ) -} - -# ------------------------------------------------------------------------------ -# ssh_extract_keys_from_file() -# -# - Extracts valid SSH public keys from given file -# - Supports RSA, Ed25519, ECDSA and filters out comments/invalid lines -# ------------------------------------------------------------------------------ -ssh_extract_keys_from_file() { - local f="$1" - [[ -r "$f" ]] || return 0 - tr -d '\r' <"$f" | awk ' - /^[[:space:]]*#/ {next} - /^[[:space:]]*$/ {next} - # nackt: typ base64 [comment] - /^(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/ {print; next} - # mit Optionen: finde ab erstem Key-Typ - { - match($0, /(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/) - if (RSTART>0) { print substr($0, RSTART) } - } - ' -} - -# ------------------------------------------------------------------------------ -# ssh_build_choices_from_files() -# -# - Builds interactive whiptail checklist of available SSH keys -# - Generates fingerprint, type and comment for each key -# ------------------------------------------------------------------------------ -ssh_build_choices_from_files() { - local -a files=("$@") - CHOICES=() - COUNT=0 - MAPFILE="$(mktemp)" - local id key typ fp cmt base ln=0 - - for f in "${files[@]}"; do - [[ -f "$f" && -r "$f" ]] || continue - base="$(basename -- "$f")" - case "$base" in - known_hosts | known_hosts.* | config) continue ;; - id_*) [[ "$f" != *.pub ]] && continue ;; - esac - - # map every key in file - while IFS= read -r key; do - [[ -n "$key" ]] || continue - - typ="" - fp="" - cmt="" - # Only the pure key part (without options) is already included in ‘key’. - read -r _typ _b64 _cmt <<<"$key" - typ="${_typ:-key}" - cmt="${_cmt:-}" - # Fingerprint via ssh-keygen (if available) - if command -v ssh-keygen >/dev/null 2>&1; then - fp="$(printf '%s\n' "$key" | ssh-keygen -lf - 2>/dev/null | awk '{print $2}')" - fi - # Label shorten - [[ ${#cmt} -gt 40 ]] && cmt="${cmt:0:37}..." - - ln=$((ln + 1)) - COUNT=$((COUNT + 1)) - id="K${COUNT}" - echo "${id}|${key}" >>"$MAPFILE" - CHOICES+=("$id" "[$typ] ${fp:+$fp }${cmt:+$cmt }— ${base}" "OFF") - done < <(ssh_extract_keys_from_file "$f") - done -} - -# ------------------------------------------------------------------------------ -# ssh_discover_default_files() -# -# - Scans standard paths for SSH keys -# - Includes ~/.ssh/*.pub, /etc/ssh/authorized_keys, etc. -# ------------------------------------------------------------------------------ -ssh_discover_default_files() { - local -a cand=() - shopt -s nullglob - cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2) - cand+=(/root/.ssh/*.pub) - cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*) - shopt -u nullglob - printf '%s\0' "${cand[@]}" -} - -configure_ssh_settings() { - SSH_KEYS_FILE="$(mktemp)" - : >"$SSH_KEYS_FILE" - - IFS=$'\0' read -r -d '' -a _def_files < <(ssh_discover_default_files && printf '\0') - ssh_build_choices_from_files "${_def_files[@]}" - local default_key_count="$COUNT" - - local ssh_key_mode - if [[ "$default_key_count" -gt 0 ]]; then - ssh_key_mode=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ - "Provision SSH keys for root:" 14 72 4 \ - "found" "Select from detected keys (${default_key_count})" \ - "manual" "Paste a single public key" \ - "folder" "Scan another folder (path or glob)" \ - "none" "No keys" 3>&1 1>&2 2>&3) || exit_script - else - ssh_key_mode=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ - "No host keys detected; choose manual/none:" 12 72 2 \ - "manual" "Paste a single public key" \ - "none" "No keys" 3>&1 1>&2 2>&3) || exit_script - fi - - case "$ssh_key_mode" in - found) - local selection - selection=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT HOST KEYS" \ - --checklist "Select one or more keys to import:" 20 140 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script - for tag in $selection; do - tag="${tag%\"}" - tag="${tag#\"}" - local line - line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) - [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" - done - ;; - manual) - SSH_AUTHORIZED_KEY="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --inputbox "Paste one SSH public key line (ssh-ed25519/ssh-rsa/...)" 10 72 --title "SSH Public Key" 3>&1 1>&2 2>&3)" - [[ -n "$SSH_AUTHORIZED_KEY" ]] && printf '%s\n' "$SSH_AUTHORIZED_KEY" >>"$SSH_KEYS_FILE" - ;; - folder) - local glob_path - glob_path=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --inputbox "Enter a folder or glob to scan (e.g. /root/.ssh/*.pub)" 10 72 --title "Scan Folder/Glob" 3>&1 1>&2 2>&3) - if [[ -n "$glob_path" ]]; then - shopt -s nullglob - read -r -a _scan_files <<<"$glob_path" - shopt -u nullglob - if [[ "${#_scan_files[@]}" -gt 0 ]]; then - ssh_build_choices_from_files "${_scan_files[@]}" - if [[ "$COUNT" -gt 0 ]]; then - local folder_selection - folder_selection=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT FOLDER KEYS" \ - --checklist "Select key(s) to import:" 20 78 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script - for tag in $folder_selection; do - tag="${tag%\"}" - tag="${tag#\"}" - local line - line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) - [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" - done - else - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "No keys found in: $glob_path" 8 60 - fi - else - whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "Path/glob returned no files." 8 60 - fi - fi - ;; - none) - : - ;; - esac - - if [[ -s "$SSH_KEYS_FILE" ]]; then - sort -u -o "$SSH_KEYS_FILE" "$SSH_KEYS_FILE" - printf '\n' >>"$SSH_KEYS_FILE" - fi - - if [[ -s "$SSH_KEYS_FILE" || "$PW" == -password* ]]; then - if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable root SSH access?" 10 58); then - SSH="yes" - else - SSH="no" - fi - else - SSH="no" - fi -} - - -# ============================================================================== -# SETTINGS & CONFIGURATION -# ============================================================================== - # ------------------------------------------------------------------------------ # base_settings() # @@ -578,6 +366,66 @@ exit_script() { exit } +# ------------------------------------------------------------------------------ +# find_host_ssh_keys() +# +# - Scans system for available SSH keys +# - Supports defaults (~/.ssh, /etc/ssh/authorized_keys) +# - Returns list of files containing valid SSH public keys +# - Sets FOUND_HOST_KEY_COUNT to number of keys found +# ------------------------------------------------------------------------------ +find_host_ssh_keys() { + local re='(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))' + local -a files=() cand=() + local g="${var_ssh_import_glob:-}" + local total=0 f base c + + shopt -s nullglob + if [[ -n "$g" ]]; then + for pat in $g; do cand+=($pat); done + else + cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2) + cand+=(/root/.ssh/*.pub) + cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*) + fi + shopt -u nullglob + + for f in "${cand[@]}"; do + [[ -f "$f" && -r "$f" ]] || continue + base="$(basename -- "$f")" + case "$base" in + known_hosts | known_hosts.* | config) continue ;; + id_*) [[ "$f" != *.pub ]] && continue ;; + esac + + # CRLF safe check for host keys + c=$(tr -d '\r' <"$f" | awk ' + /^[[:space:]]*#/ {next} + /^[[:space:]]*$/ {next} + {print} + ' | grep -E -c '"$re"' || true) + + if ((c > 0)); then + files+=("$f") + total=$((total + c)) + fi + done + + # Fallback to /root/.ssh/authorized_keys + if ((${#files[@]} == 0)) && [[ -r /root/.ssh/authorized_keys ]]; then + if grep -E -q "$re" /root/.ssh/authorized_keys; then + files+=(/root/.ssh/authorized_keys) + total=$((total + $(grep -E -c "$re" /root/.ssh/authorized_keys || echo 0))) + fi + fi + + FOUND_HOST_KEY_COUNT="$total" + ( + IFS=: + echo "${files[*]}" + ) +} + # ------------------------------------------------------------------------------ # advanced_settings() # @@ -1314,105 +1162,38 @@ EOF echo_default } -diagnostics_menu() { - if [ "${DIAGNOSTICS:-no}" = "yes" ]; then - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "DIAGNOSTIC SETTINGS" \ - --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ - --yes-button "No" --no-button "Back"; then - DIAGNOSTICS="no" - sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=no/' /usr/local/community-scripts/diagnostics - whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 - fi - else - if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "DIAGNOSTIC SETTINGS" \ - --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ - --yes-button "Yes" --no-button "Back"; then - DIAGNOSTICS="yes" - sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=yes/' /usr/local/community-scripts/diagnostics - whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 - fi - fi -} - -ensure_global_default_vars_file() { - local vars_path="/usr/local/community-scripts/default.vars" - if [[ ! -f "$vars_path" ]]; then - mkdir -p "$(dirname "$vars_path")" - touch "$vars_path" - fi - echo "$vars_path" -} - -edit_default_storage() { - local vf="/usr/local/community-scripts/default.vars" - - # Ensure file exists - if [[ ! -f "$vf" ]]; then - mkdir -p "$(dirname "$vf")" - touch "$vf" - fi - - # Let ensure_storage_selection_for_vars_file handle everything - ensure_storage_selection_for_vars_file "$vf" -} - -settings_menu() { - while true; do - local settings_items=( - "1" "Manage API-Diagnostic Setting" - "2" "Edit Default.vars" - "3" "Edit Default Storage" - ) - if [ -f "$(get_app_defaults_path)" ]; then - settings_items+=("4" "Edit App.vars for ${APP}") - settings_items+=("5" "Exit") - else - settings_items+=("4" "Exit") - fi - - local choice - choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ - --title "Community-Scripts SETTINGS Menu" \ - --ok-button "OK" --cancel-button "Back" \ - --menu "\n\nChoose a settings option:\n\nUse TAB or Arrow keys to navigate, ENTER to select." 20 60 9 \ - "${settings_items[@]}" \ - 3>&1 1>&2 2>&3) || break - - case "$choice" in - 1) diagnostics_menu ;; - 2) ${EDITOR:-nano} /usr/local/community-scripts/default.vars ;; - 3) edit_default_storage ;; - 4) - if [ -f "$(get_app_defaults_path)" ]; then - ${EDITOR:-nano} "$(get_app_defaults_path)" - else - exit_script - fi - ;; - 5) exit_script ;; - esac - done -} - - -# ============================================================================== -# DEFAULTS MANAGEMENT (VAR_* FILES) -# ============================================================================== - # ------------------------------------------------------------------------------ # get_app_defaults_path() # # - Returns full path for app-specific defaults file # - Example: /usr/local/community-scripts/defaults/.vars # ------------------------------------------------------------------------------ + get_app_defaults_path() { local n="${NSAPP:-${APP,,}}" echo "/usr/local/community-scripts/defaults/${n}.vars" } +# ------------------------------------------------------------------------------ +# maybe_offer_save_app_defaults +# +# - Called after advanced_settings returned with fully chosen values. +# - If no .vars exists, offers to persist current advanced settings +# into /usr/local/community-scripts/defaults/.vars +# - Only writes whitelisted var_* keys. +# - Extracts raw values from flags like ",gw=..." ",mtu=..." etc. +# ------------------------------------------------------------------------------ +if ! declare -p VAR_WHITELIST >/dev/null 2>&1; then + declare -ag VAR_WHITELIST=( + var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_ctid var_disk var_fuse + var_gateway var_hostname var_ipv6_method var_ipv6_static var_mac var_mtu + var_net var_ns var_pw var_ram var_tags var_tun var_unprivileged + var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage + ) +fi + # Note: _is_whitelisted_key() is defined above in default_var_settings section + _sanitize_value() { # Disallow Command-Substitution / Shell-Meta case "$1" in @@ -1424,6 +1205,10 @@ _sanitize_value() { echo "$1" } +# Map-Parser: read var_* from file into _VARS_IN associative array +# Note: Main _load_vars_file() with full validation is defined in default_var_settings section +# This simplified version is used specifically for diff operations via _VARS_IN array +declare -A _VARS_IN _load_vars_file_to_map() { local file="$1" [ -f "$file" ] || return 0 @@ -1697,11 +1482,221 @@ ensure_storage_selection_for_vars_file() { msg_ok "Storage configuration saved to $(basename "$vf")" } +diagnostics_menu() { + if [ "${DIAGNOSTICS:-no}" = "yes" ]; then + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "DIAGNOSTIC SETTINGS" \ + --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ + --yes-button "No" --no-button "Back"; then + DIAGNOSTICS="no" + sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=no/' /usr/local/community-scripts/diagnostics + whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 + fi + else + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "DIAGNOSTIC SETTINGS" \ + --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ + --yes-button "Yes" --no-button "Back"; then + DIAGNOSTICS="yes" + sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=yes/' /usr/local/community-scripts/diagnostics + whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 + fi + fi +} -# ============================================================================== -# STORAGE DISCOVERY & SELECTION -# ============================================================================== +ensure_global_default_vars_file() { + local vars_path="/usr/local/community-scripts/default.vars" + if [[ ! -f "$vars_path" ]]; then + mkdir -p "$(dirname "$vars_path")" + touch "$vars_path" + fi + echo "$vars_path" +} +# ------------------------------------------------------------------------------ +# install_script() +# +# - Main entrypoint for installation mode +# - Runs safety checks (pve_check, root_check, maxkeys_check, diagnostics_check) +# - Builds interactive menu (Default, Verbose, Advanced, My Defaults, App Defaults, Diagnostics, Storage, Exit) +# - Applies chosen settings and triggers container build +# ------------------------------------------------------------------------------ +install_script() { + pve_check + shell_check + root_check + arch_check + ssh_check + maxkeys_check + diagnostics_check + + if systemctl is-active -q ping-instances.service; then + systemctl -q stop ping-instances.service + fi + + NEXTID=$(pvesh get /cluster/nextid) + timezone=$(cat /etc/timezone) + + # Show APP Header + header_info + + # --- Support CLI argument as direct preset (default, advanced, …) --- + CHOICE="${mode:-${1:-}}" + + # If no CLI argument → show whiptail menu + # Build menu dynamically based on available options + local appdefaults_option="" + local settings_option="" + local menu_items=( + "1" "Default Install" + "2" "Advanced Install" + "3" "My Defaults" + ) + + if [ -f "$(get_app_defaults_path)" ]; then + appdefaults_option="4" + menu_items+=("4" "App Defaults for ${APP}") + settings_option="5" + menu_items+=("5" "Settings") + else + settings_option="4" + menu_items+=("4" "Settings") + fi + + if [ -z "$CHOICE" ]; then + + TMP_CHOICE=$(whiptail \ + --backtitle "Proxmox VE Helper Scripts" \ + --title "Community-Scripts Options" \ + --ok-button "Select" --cancel-button "Exit Script" \ + --notags \ + --menu "\nChoose an option:\n Use TAB or Arrow keys to navigate, ENTER to select.\n" \ + 20 60 9 \ + "${menu_items[@]}" \ + --default-item "1" \ + 3>&1 1>&2 2>&3) || exit_script + CHOICE="$TMP_CHOICE" + fi + + APPDEFAULTS_OPTION="$appdefaults_option" + SETTINGS_OPTION="$settings_option" + + # --- Main case --- + local defaults_target="" + local run_maybe_offer="no" + case "$CHOICE" in + 1 | default | DEFAULT) + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME${CL}" + VERBOSE="no" + METHOD="default" + base_settings "$VERBOSE" + echo_default + defaults_target="$(ensure_global_default_vars_file)" + ;; + 2 | advanced | ADVANCED) + header_info + + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Install on node $PVEHOST_NAME${CL}" + echo -e "${INFO}${BOLD}${DGN}PVE Version ${PVEVERSION} (Kernel: ${KERNEL_VERSION})${CL}" + METHOD="advanced" + base_settings + advanced_settings + defaults_target="$(ensure_global_default_vars_file)" + run_maybe_offer="yes" + ;; + 3 | mydefaults | MYDEFAULTS) + default_var_settings || { + msg_error "Failed to apply default.vars" + exit 1 + } + defaults_target="/usr/local/community-scripts/default.vars" + ;; + "$APPDEFAULTS_OPTION" | appdefaults | APPDEFAULTS) + if [ -f "$(get_app_defaults_path)" ]; then + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using App Defaults for ${APP} on node $PVEHOST_NAME${CL}" + METHOD="appdefaults" + base_settings + _load_vars_file "$(get_app_defaults_path)" + echo_default + defaults_target="$(get_app_defaults_path)" + else + msg_error "No App Defaults available for ${APP}" + exit 1 + fi + ;; + "$SETTINGS_OPTION" | settings | SETTINGS) + settings_menu + defaults_target="" + ;; + *) + echo -e "${CROSS}${RD}Invalid option: $CHOICE${CL}" + exit 1 + ;; + esac + + if [[ -n "$defaults_target" ]]; then + ensure_storage_selection_for_vars_file "$defaults_target" + fi + + if [[ "$run_maybe_offer" == "yes" ]]; then + maybe_offer_save_app_defaults + fi +} + +edit_default_storage() { + local vf="/usr/local/community-scripts/default.vars" + + # Ensure file exists + if [[ ! -f "$vf" ]]; then + mkdir -p "$(dirname "$vf")" + touch "$vf" + fi + + # Let ensure_storage_selection_for_vars_file handle everything + ensure_storage_selection_for_vars_file "$vf" +} + +settings_menu() { + while true; do + local settings_items=( + "1" "Manage API-Diagnostic Setting" + "2" "Edit Default.vars" + "3" "Edit Default Storage" + ) + if [ -f "$(get_app_defaults_path)" ]; then + settings_items+=("4" "Edit App.vars for ${APP}") + settings_items+=("5" "Exit") + else + settings_items+=("4" "Exit") + fi + + local choice + choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "Community-Scripts SETTINGS Menu" \ + --ok-button "OK" --cancel-button "Back" \ + --menu "\n\nChoose a settings option:\n\nUse TAB or Arrow keys to navigate, ENTER to select." 20 60 9 \ + "${settings_items[@]}" \ + 3>&1 1>&2 2>&3) || break + + case "$choice" in + 1) diagnostics_menu ;; + 2) ${EDITOR:-nano} /usr/local/community-scripts/default.vars ;; + 3) edit_default_storage ;; + 4) + if [ -f "$(get_app_defaults_path)" ]; then + ${EDITOR:-nano} "$(get_app_defaults_path)" + else + exit_script + fi + ;; + 5) exit_script ;; + esac + done +} + +# ===== Unified storage selection & writing to vars files ===== _write_storage_to_vars() { # $1 = vars_file, $2 = key (var_container_storage / var_template_storage), $3 = value local vf="$1" key="$2" val="$3" @@ -1753,230 +1748,270 @@ choose_and_set_storage_for_file() { } # ------------------------------------------------------------------------------ -# Storage discovery / selection helpers +# check_container_resources() +# +# - Compares host RAM/CPU with required values +# - Warns if under-provisioned and asks user to continue or abort # ------------------------------------------------------------------------------ -resolve_storage_preselect() { - local class="$1" preselect="$2" required_content="" - case "$class" in - template) required_content="vztmpl" ;; - container) required_content="rootdir" ;; - *) return 1 ;; - esac - [[ -z "$preselect" ]] && return 1 - if ! pvesm status -content "$required_content" | awk 'NR>1{print $1}' | grep -qx -- "$preselect"; then - msg_warn "Preselected storage '${preselect}' does not support content '${required_content}' (or not found)" - return 1 - fi +check_container_resources() { + current_ram=$(free -m | awk 'NR==2{print $2}') + current_cpu=$(nproc) - local line total used free - line="$(pvesm status | awk -v s="$preselect" 'NR>1 && $1==s {print $0}')" - if [[ -z "$line" ]]; then - STORAGE_INFO="n/a" - else - total="$(awk '{print $4}' <<<"$line")" - used="$(awk '{print $5}' <<<"$line")" - free="$(awk '{print $6}' <<<"$line")" - local total_h used_h free_h - if command -v numfmt >/dev/null 2>&1; then - total_h="$(numfmt --to=iec --suffix=B --format %.1f "$total" 2>/dev/null || echo "$total")" - used_h="$(numfmt --to=iec --suffix=B --format %.1f "$used" 2>/dev/null || echo "$used")" - free_h="$(numfmt --to=iec --suffix=B --format %.1f "$free" 2>/dev/null || echo "$free")" - STORAGE_INFO="Free: ${free_h} Used: ${used_h}" - else - STORAGE_INFO="Free: ${free} Used: ${used}" + if [[ "$current_ram" -lt "$var_ram" ]] || [[ "$current_cpu" -lt "$var_cpu" ]]; then + echo -e "\n${INFO}${HOLD} ${GN}Required: ${var_cpu} CPU, ${var_ram}MB RAM ${CL}| ${RD}Current: ${current_cpu} CPU, ${current_ram}MB RAM${CL}" + echo -e "${YWB}Please ensure that the ${APP} LXC is configured with at least ${var_cpu} vCPU and ${var_ram} MB RAM for the build process.${CL}\n" + echo -ne "${INFO}${HOLD} May cause data loss! ${INFO} Continue update with under-provisioned LXC? " + read -r prompt + if [[ ! ${prompt,,} =~ ^(yes)$ ]]; then + echo -e "${CROSS}${HOLD} ${YWB}Exiting based on user input.${CL}" + exit 1 fi + else + echo -e "" fi - STORAGE_RESULT="$preselect" - return 0 } -select_storage() { - local CLASS=$1 CONTENT CONTENT_LABEL - case $CLASS in - container) - CONTENT='rootdir' - CONTENT_LABEL='Container' - ;; - template) - CONTENT='vztmpl' - CONTENT_LABEL='Container template' - ;; - iso) - CONTENT='iso' - CONTENT_LABEL='ISO image' - ;; - images) - CONTENT='images' - CONTENT_LABEL='VM Disk image' - ;; - backup) - CONTENT='backup' - CONTENT_LABEL='Backup' - ;; - snippets) - CONTENT='snippets' - CONTENT_LABEL='Snippets' - ;; - *) - msg_error "Invalid storage class '$CLASS'" - return 1 - ;; - esac - - declare -A STORAGE_MAP - local -a MENU=() - local COL_WIDTH=0 - - while read -r TAG TYPE _ TOTAL USED FREE _; do - [[ -n "$TAG" && -n "$TYPE" ]] || continue - local DISPLAY="${TAG} (${TYPE})" - local USED_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$USED") - local FREE_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$FREE") - local INFO="Free: ${FREE_FMT}B Used: ${USED_FMT}B" - STORAGE_MAP["$DISPLAY"]="$TAG" - MENU+=("$DISPLAY" "$INFO" "OFF") - ((${#DISPLAY} > COL_WIDTH)) && COL_WIDTH=${#DISPLAY} - done < <(pvesm status -content "$CONTENT" | awk 'NR>1') - - if [[ ${#MENU[@]} -eq 0 ]]; then - msg_error "No storage found for content type '$CONTENT'." - return 2 - fi - - if [[ $((${#MENU[@]} / 3)) -eq 1 ]]; then - STORAGE_RESULT="${STORAGE_MAP[${MENU[0]}]}" - STORAGE_INFO="${MENU[1]}" - return 0 - fi - - local WIDTH=$((COL_WIDTH + 42)) - while true; do - local DISPLAY_SELECTED - DISPLAY_SELECTED=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ - --title "Storage Pools" \ - --radiolist "Which storage pool for ${CONTENT_LABEL,,}?\n(Spacebar to select)" \ - 16 "$WIDTH" 6 "${MENU[@]}" 3>&1 1>&2 2>&3) || { exit_script; } - - DISPLAY_SELECTED=$(sed 's/[[:space:]]*$//' <<<"$DISPLAY_SELECTED") - if [[ -z "$DISPLAY_SELECTED" || -z "${STORAGE_MAP[$DISPLAY_SELECTED]+_}" ]]; then - whiptail --msgbox "No valid storage selected. Please try again." 8 58 - continue +# ------------------------------------------------------------------------------ +# check_container_storage() +# +# - Checks /boot partition usage +# - Warns if usage >80% and asks user confirmation before proceeding +# ------------------------------------------------------------------------------ +check_container_storage() { + total_size=$(df /boot --output=size | tail -n 1) + local used_size=$(df /boot --output=used | tail -n 1) + usage=$((100 * used_size / total_size)) + if ((usage > 80)); then + echo -e "${INFO}${HOLD} ${YWB}Warning: Storage is dangerously low (${usage}%).${CL}" + echo -ne "Continue anyway? " + read -r prompt + if [[ ! ${prompt,,} =~ ^(y|yes)$ ]]; then + echo -e "${CROSS}${HOLD}${YWB}Exiting based on user input.${CL}" + exit 1 fi - STORAGE_RESULT="${STORAGE_MAP[$DISPLAY_SELECTED]}" - for ((i = 0; i < ${#MENU[@]}; i += 3)); do - if [[ "${MENU[$i]}" == "$DISPLAY_SELECTED" ]]; then - STORAGE_INFO="${MENU[$i + 1]}" - break + fi +} + +# ------------------------------------------------------------------------------ +# ssh_extract_keys_from_file() +# +# - Extracts valid SSH public keys from given file +# - Supports RSA, Ed25519, ECDSA and filters out comments/invalid lines +# ------------------------------------------------------------------------------ +ssh_extract_keys_from_file() { + local f="$1" + [[ -r "$f" ]] || return 0 + tr -d '\r' <"$f" | awk ' + /^[[:space:]]*#/ {next} + /^[[:space:]]*$/ {next} + # nackt: typ base64 [comment] + /^(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/ {print; next} + # mit Optionen: finde ab erstem Key-Typ + { + match($0, /(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/) + if (RSTART>0) { print substr($0, RSTART) } + } + ' +} + +# ------------------------------------------------------------------------------ +# ssh_build_choices_from_files() +# +# - Builds interactive whiptail checklist of available SSH keys +# - Generates fingerprint, type and comment for each key +# ------------------------------------------------------------------------------ +ssh_build_choices_from_files() { + local -a files=("$@") + CHOICES=() + COUNT=0 + MAPFILE="$(mktemp)" + local id key typ fp cmt base ln=0 + + for f in "${files[@]}"; do + [[ -f "$f" && -r "$f" ]] || continue + base="$(basename -- "$f")" + case "$base" in + known_hosts | known_hosts.* | config) continue ;; + id_*) [[ "$f" != *.pub ]] && continue ;; + esac + + # map every key in file + while IFS= read -r key; do + [[ -n "$key" ]] || continue + + typ="" + fp="" + cmt="" + # Only the pure key part (without options) is already included in ‘key’. + read -r _typ _b64 _cmt <<<"$key" + typ="${_typ:-key}" + cmt="${_cmt:-}" + # Fingerprint via ssh-keygen (if available) + if command -v ssh-keygen >/dev/null 2>&1; then + fp="$(printf '%s\n' "$key" | ssh-keygen -lf - 2>/dev/null | awk '{print $2}')" fi - done - return 0 + # Label shorten + [[ ${#cmt} -gt 40 ]] && cmt="${cmt:0:37}..." + + ln=$((ln + 1)) + COUNT=$((COUNT + 1)) + id="K${COUNT}" + echo "${id}|${key}" >>"$MAPFILE" + CHOICES+=("$id" "[$typ] ${fp:+$fp }${cmt:+$cmt }— ${base}" "OFF") + done < <(ssh_extract_keys_from_file "$f") done } +# ------------------------------------------------------------------------------ +# ssh_discover_default_files() +# +# - Scans standard paths for SSH keys +# - Includes ~/.ssh/*.pub, /etc/ssh/authorized_keys, etc. +# ------------------------------------------------------------------------------ +ssh_discover_default_files() { + local -a cand=() + shopt -s nullglob + cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2) + cand+=(/root/.ssh/*.pub) + cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*) + shopt -u nullglob + printf '%s\0' "${cand[@]}" +} -# ============================================================================== -# GPU & HARDWARE PASSTHROUGH -# ============================================================================== +configure_ssh_settings() { + SSH_KEYS_FILE="$(mktemp)" + : >"$SSH_KEYS_FILE" -fix_gpu_gids() { - if [[ -z "${GPU_TYPE:-}" ]]; then - return 0 + IFS=$'\0' read -r -d '' -a _def_files < <(ssh_discover_default_files && printf '\0') + ssh_build_choices_from_files "${_def_files[@]}" + local default_key_count="$COUNT" + + local ssh_key_mode + if [[ "$default_key_count" -gt 0 ]]; then + ssh_key_mode=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ + "Provision SSH keys for root:" 14 72 4 \ + "found" "Select from detected keys (${default_key_count})" \ + "manual" "Paste a single public key" \ + "folder" "Scan another folder (path or glob)" \ + "none" "No keys" 3>&1 1>&2 2>&3) || exit_script + else + ssh_key_mode=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ + "No host keys detected; choose manual/none:" 12 72 2 \ + "manual" "Paste a single public key" \ + "none" "No keys" 3>&1 1>&2 2>&3) || exit_script fi - msg_info "Detecting and setting correct GPU group IDs" - - # Ermittle die tatsächlichen GIDs aus dem Container - local video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") - local render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") - - # Fallbacks wenn Gruppen nicht existieren - if [[ -z "$video_gid" ]]; then - # Versuche die video Gruppe zu erstellen - pct exec "$CTID" -- sh -c "groupadd -r video 2>/dev/null || true" - video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") - [[ -z "$video_gid" ]] && video_gid="44" # Ultimate fallback - fi - - if [[ -z "$render_gid" ]]; then - # Versuche die render Gruppe zu erstellen - pct exec "$CTID" -- sh -c "groupadd -r render 2>/dev/null || true" - render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") - [[ -z "$render_gid" ]] && render_gid="104" # Ultimate fallback - fi - - msg_info "Container GIDs detected - video:${video_gid}, render:${render_gid}" - - # Prüfe ob die GIDs von den Defaults abweichen - local need_update=0 - if [[ "$video_gid" != "44" ]] || [[ "$render_gid" != "104" ]]; then - need_update=1 - fi - - if [[ $need_update -eq 1 ]]; then - msg_info "Updating device GIDs in container config" - - # Stoppe Container für Config-Update - pct stop "$CTID" >/dev/null 2>&1 - - # Update die dev Einträge mit korrekten GIDs - # Backup der Config - cp "$LXC_CONFIG" "${LXC_CONFIG}.bak" - - # Parse und update jeden dev Eintrag - while IFS= read -r line; do - if [[ "$line" =~ ^dev[0-9]+: ]]; then - # Extract device path - local device_path=$(echo "$line" | sed -E 's/^dev[0-9]+: ([^,]+).*/\1/') - local dev_num=$(echo "$line" | sed -E 's/^(dev[0-9]+):.*/\1/') - - if [[ "$device_path" =~ renderD ]]; then - # RenderD device - use render GID - echo "${dev_num}: ${device_path},gid=${render_gid}" - elif [[ "$device_path" =~ card ]]; then - # Card device - use video GID - echo "${dev_num}: ${device_path},gid=${video_gid}" + case "$ssh_key_mode" in + found) + local selection + selection=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT HOST KEYS" \ + --checklist "Select one or more keys to import:" 20 140 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script + for tag in $selection; do + tag="${tag%\"}" + tag="${tag#\"}" + local line + line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) + [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" + done + ;; + manual) + SSH_AUTHORIZED_KEY="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Paste one SSH public key line (ssh-ed25519/ssh-rsa/...)" 10 72 --title "SSH Public Key" 3>&1 1>&2 2>&3)" + [[ -n "$SSH_AUTHORIZED_KEY" ]] && printf '%s\n' "$SSH_AUTHORIZED_KEY" >>"$SSH_KEYS_FILE" + ;; + folder) + local glob_path + glob_path=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Enter a folder or glob to scan (e.g. /root/.ssh/*.pub)" 10 72 --title "Scan Folder/Glob" 3>&1 1>&2 2>&3) + if [[ -n "$glob_path" ]]; then + shopt -s nullglob + read -r -a _scan_files <<<"$glob_path" + shopt -u nullglob + if [[ "${#_scan_files[@]}" -gt 0 ]]; then + ssh_build_choices_from_files "${_scan_files[@]}" + if [[ "$COUNT" -gt 0 ]]; then + local folder_selection + folder_selection=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT FOLDER KEYS" \ + --checklist "Select key(s) to import:" 20 78 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script + for tag in $folder_selection; do + tag="${tag%\"}" + tag="${tag#\"}" + local line + line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) + [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" + done else - # Keep original line - echo "$line" + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "No keys found in: $glob_path" 8 60 fi else - # Keep non-dev lines - echo "$line" + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "Path/glob returned no files." 8 60 fi - done <"$LXC_CONFIG" >"${LXC_CONFIG}.new" + fi + ;; + none) + : + ;; + esac - mv "${LXC_CONFIG}.new" "$LXC_CONFIG" - - # Starte Container wieder - pct start "$CTID" >/dev/null 2>&1 - sleep 3 - - msg_ok "Device GIDs updated successfully" - else - msg_ok "Device GIDs are already correct" + if [[ -s "$SSH_KEYS_FILE" ]]; then + sort -u -o "$SSH_KEYS_FILE" "$SSH_KEYS_FILE" + printf '\n' >>"$SSH_KEYS_FILE" fi - if [[ "$CT_TYPE" == "0" ]]; then - pct exec "$CTID" -- bash -c " - if [ -d /dev/dri ]; then - for dev in /dev/dri/*; do - if [ -e \"\$dev\" ]; then - if [[ \"\$dev\" =~ renderD ]]; then - chgrp ${render_gid} \"\$dev\" 2>/dev/null || true - else - chgrp ${video_gid} \"\$dev\" 2>/dev/null || true - fi - chmod 660 \"\$dev\" 2>/dev/null || true - fi - done - fi - " >/dev/null 2>&1 + + if [[ -s "$SSH_KEYS_FILE" || "$PW" == -password* ]]; then + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable root SSH access?" 10 58); then + SSH="yes" + else + SSH="no" + fi + else + SSH="no" fi } +# ------------------------------------------------------------------------------ +# start() +# +# - Entry point of script +# - On Proxmox host: calls install_script +# - In silent mode: runs update_script +# - Otherwise: shows update/setting menu +# ------------------------------------------------------------------------------ +start() { + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) + if command -v pveversion >/dev/null 2>&1; then + install_script || return 0 + return 0 + elif [ ! -z ${PHS_SILENT+x} ] && [[ "${PHS_SILENT}" == "1" ]]; then + VERBOSE="no" + set_std_mode + update_script + else + CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "${APP} LXC Update/Setting" --menu \ + "Support/Update functions for ${APP} LXC. Choose an option:" \ + 12 60 3 \ + "1" "YES (Silent Mode)" \ + "2" "YES (Verbose Mode)" \ + "3" "NO (Cancel Update)" --nocancel --default-item "1" 3>&1 1>&2 2>&3) -# ============================================================================== -# CONTAINER LIFECYCLE & CREATION -# ============================================================================== + case "$CHOICE" in + 1) + VERBOSE="no" + set_std_mode + ;; + 2) + VERBOSE="yes" + set_std_mode + ;; + 3) + clear + exit_script + exit + ;; + esac + update_script + fi +} # ------------------------------------------------------------------------------ # build_container() @@ -2532,6 +2567,253 @@ destroy_lxc() { esac } +# ------------------------------------------------------------------------------ +# Storage discovery / selection helpers +# ------------------------------------------------------------------------------ +# ===== Storage discovery / selection helpers (ported from create_lxc.sh) ===== +resolve_storage_preselect() { + local class="$1" preselect="$2" required_content="" + case "$class" in + template) required_content="vztmpl" ;; + container) required_content="rootdir" ;; + *) return 1 ;; + esac + [[ -z "$preselect" ]] && return 1 + if ! pvesm status -content "$required_content" | awk 'NR>1{print $1}' | grep -qx -- "$preselect"; then + msg_warn "Preselected storage '${preselect}' does not support content '${required_content}' (or not found)" + return 1 + fi + + local line total used free + line="$(pvesm status | awk -v s="$preselect" 'NR>1 && $1==s {print $0}')" + if [[ -z "$line" ]]; then + STORAGE_INFO="n/a" + else + total="$(awk '{print $4}' <<<"$line")" + used="$(awk '{print $5}' <<<"$line")" + free="$(awk '{print $6}' <<<"$line")" + local total_h used_h free_h + if command -v numfmt >/dev/null 2>&1; then + total_h="$(numfmt --to=iec --suffix=B --format %.1f "$total" 2>/dev/null || echo "$total")" + used_h="$(numfmt --to=iec --suffix=B --format %.1f "$used" 2>/dev/null || echo "$used")" + free_h="$(numfmt --to=iec --suffix=B --format %.1f "$free" 2>/dev/null || echo "$free")" + STORAGE_INFO="Free: ${free_h} Used: ${used_h}" + else + STORAGE_INFO="Free: ${free} Used: ${used}" + fi + fi + STORAGE_RESULT="$preselect" + return 0 +} + +fix_gpu_gids() { + if [[ -z "${GPU_TYPE:-}" ]]; then + return 0 + fi + + msg_info "Detecting and setting correct GPU group IDs" + + # Ermittle die tatsächlichen GIDs aus dem Container + local video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") + local render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") + + # Fallbacks wenn Gruppen nicht existieren + if [[ -z "$video_gid" ]]; then + # Versuche die video Gruppe zu erstellen + pct exec "$CTID" -- sh -c "groupadd -r video 2>/dev/null || true" + video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") + [[ -z "$video_gid" ]] && video_gid="44" # Ultimate fallback + fi + + if [[ -z "$render_gid" ]]; then + # Versuche die render Gruppe zu erstellen + pct exec "$CTID" -- sh -c "groupadd -r render 2>/dev/null || true" + render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") + [[ -z "$render_gid" ]] && render_gid="104" # Ultimate fallback + fi + + msg_info "Container GIDs detected - video:${video_gid}, render:${render_gid}" + + # Prüfe ob die GIDs von den Defaults abweichen + local need_update=0 + if [[ "$video_gid" != "44" ]] || [[ "$render_gid" != "104" ]]; then + need_update=1 + fi + + if [[ $need_update -eq 1 ]]; then + msg_info "Updating device GIDs in container config" + + # Stoppe Container für Config-Update + pct stop "$CTID" >/dev/null 2>&1 + + # Update die dev Einträge mit korrekten GIDs + # Backup der Config + cp "$LXC_CONFIG" "${LXC_CONFIG}.bak" + + # Parse und update jeden dev Eintrag + while IFS= read -r line; do + if [[ "$line" =~ ^dev[0-9]+: ]]; then + # Extract device path + local device_path=$(echo "$line" | sed -E 's/^dev[0-9]+: ([^,]+).*/\1/') + local dev_num=$(echo "$line" | sed -E 's/^(dev[0-9]+):.*/\1/') + + if [[ "$device_path" =~ renderD ]]; then + # RenderD device - use render GID + echo "${dev_num}: ${device_path},gid=${render_gid}" + elif [[ "$device_path" =~ card ]]; then + # Card device - use video GID + echo "${dev_num}: ${device_path},gid=${video_gid}" + else + # Keep original line + echo "$line" + fi + else + # Keep non-dev lines + echo "$line" + fi + done <"$LXC_CONFIG" >"${LXC_CONFIG}.new" + + mv "${LXC_CONFIG}.new" "$LXC_CONFIG" + + # Starte Container wieder + pct start "$CTID" >/dev/null 2>&1 + sleep 3 + + msg_ok "Device GIDs updated successfully" + else + msg_ok "Device GIDs are already correct" + fi + if [[ "$CT_TYPE" == "0" ]]; then + pct exec "$CTID" -- bash -c " + if [ -d /dev/dri ]; then + for dev in /dev/dri/*; do + if [ -e \"\$dev\" ]; then + if [[ \"\$dev\" =~ renderD ]]; then + chgrp ${render_gid} \"\$dev\" 2>/dev/null || true + else + chgrp ${video_gid} \"\$dev\" 2>/dev/null || true + fi + chmod 660 \"\$dev\" 2>/dev/null || true + fi + done + fi + " >/dev/null 2>&1 + fi +} + +# NVIDIA-spezific check on host +check_nvidia_host_setup() { + if ! command -v nvidia-smi >/dev/null 2>&1; then + msg_warn "NVIDIA GPU detected but nvidia-smi not found on host" + msg_warn "Please install NVIDIA drivers on host first." + #echo " 1. Download driver: wget https://us.download.nvidia.com/XFree86/Linux-x86_64/550.127.05/NVIDIA-Linux-x86_64-550.127.05.run" + #echo " 2. Install: ./NVIDIA-Linux-x86_64-550.127.05.run --dkms" + #echo " 3. Verify: nvidia-smi" + return 1 + fi + + # check if nvidia-smi works + if ! nvidia-smi >/dev/null 2>&1; then + msg_warn "nvidia-smi installed but not working. Driver issue?" + return 1 + fi + + return 0 +} + +check_storage_support() { + local CONTENT="$1" VALID=0 + while IFS= read -r line; do + local STORAGE_NAME + STORAGE_NAME=$(awk '{print $1}' <<<"$line") + [[ -n "$STORAGE_NAME" ]] && VALID=1 + done < <(pvesm status -content "$CONTENT" 2>/dev/null | awk 'NR>1') + [[ $VALID -eq 1 ]] +} + +select_storage() { + local CLASS=$1 CONTENT CONTENT_LABEL + case $CLASS in + container) + CONTENT='rootdir' + CONTENT_LABEL='Container' + ;; + template) + CONTENT='vztmpl' + CONTENT_LABEL='Container template' + ;; + iso) + CONTENT='iso' + CONTENT_LABEL='ISO image' + ;; + images) + CONTENT='images' + CONTENT_LABEL='VM Disk image' + ;; + backup) + CONTENT='backup' + CONTENT_LABEL='Backup' + ;; + snippets) + CONTENT='snippets' + CONTENT_LABEL='Snippets' + ;; + *) + msg_error "Invalid storage class '$CLASS'" + return 1 + ;; + esac + + declare -A STORAGE_MAP + local -a MENU=() + local COL_WIDTH=0 + + while read -r TAG TYPE _ TOTAL USED FREE _; do + [[ -n "$TAG" && -n "$TYPE" ]] || continue + local DISPLAY="${TAG} (${TYPE})" + local USED_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$USED") + local FREE_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$FREE") + local INFO="Free: ${FREE_FMT}B Used: ${USED_FMT}B" + STORAGE_MAP["$DISPLAY"]="$TAG" + MENU+=("$DISPLAY" "$INFO" "OFF") + ((${#DISPLAY} > COL_WIDTH)) && COL_WIDTH=${#DISPLAY} + done < <(pvesm status -content "$CONTENT" | awk 'NR>1') + + if [[ ${#MENU[@]} -eq 0 ]]; then + msg_error "No storage found for content type '$CONTENT'." + return 2 + fi + + if [[ $((${#MENU[@]} / 3)) -eq 1 ]]; then + STORAGE_RESULT="${STORAGE_MAP[${MENU[0]}]}" + STORAGE_INFO="${MENU[1]}" + return 0 + fi + + local WIDTH=$((COL_WIDTH + 42)) + while true; do + local DISPLAY_SELECTED + DISPLAY_SELECTED=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "Storage Pools" \ + --radiolist "Which storage pool for ${CONTENT_LABEL,,}?\n(Spacebar to select)" \ + 16 "$WIDTH" 6 "${MENU[@]}" 3>&1 1>&2 2>&3) || { exit_script; } + + DISPLAY_SELECTED=$(sed 's/[[:space:]]*$//' <<<"$DISPLAY_SELECTED") + if [[ -z "$DISPLAY_SELECTED" || -z "${STORAGE_MAP[$DISPLAY_SELECTED]+_}" ]]; then + whiptail --msgbox "No valid storage selected. Please try again." 8 58 + continue + fi + STORAGE_RESULT="${STORAGE_MAP[$DISPLAY_SELECTED]}" + for ((i = 0; i < ${#MENU[@]}; i += 3)); do + if [[ "${MENU[$i]}" == "$DISPLAY_SELECTED" ]]; then + STORAGE_INFO="${MENU[$i + 1]}" + break + fi + done + return 0 + done +} + create_lxc_container() { # ------------------------------------------------------------------------------ # Optional verbose mode (debug tracing) @@ -3199,187 +3481,6 @@ EOF post_update_to_api "done" "none" } - -# ============================================================================== -# MAIN ENTRY POINTS & ERROR HANDLING -# ============================================================================== - -# ------------------------------------------------------------------------------ -# install_script() -# -# - Main entrypoint for installation mode -# - Runs safety checks (pve_check, root_check, maxkeys_check, diagnostics_check) -# - Builds interactive menu (Default, Verbose, Advanced, My Defaults, App Defaults, Diagnostics, Storage, Exit) -# - Applies chosen settings and triggers container build -# ------------------------------------------------------------------------------ -install_script() { - pve_check - shell_check - root_check - arch_check - ssh_check - maxkeys_check - diagnostics_check - - if systemctl is-active -q ping-instances.service; then - systemctl -q stop ping-instances.service - fi - - NEXTID=$(pvesh get /cluster/nextid) - timezone=$(cat /etc/timezone) - - # Show APP Header - header_info - - # --- Support CLI argument as direct preset (default, advanced, …) --- - CHOICE="${mode:-${1:-}}" - - # If no CLI argument → show whiptail menu - # Build menu dynamically based on available options - local appdefaults_option="" - local settings_option="" - local menu_items=( - "1" "Default Install" - "2" "Advanced Install" - "3" "My Defaults" - ) - - if [ -f "$(get_app_defaults_path)" ]; then - appdefaults_option="4" - menu_items+=("4" "App Defaults for ${APP}") - settings_option="5" - menu_items+=("5" "Settings") - else - settings_option="4" - menu_items+=("4" "Settings") - fi - - if [ -z "$CHOICE" ]; then - - TMP_CHOICE=$(whiptail \ - --backtitle "Proxmox VE Helper Scripts" \ - --title "Community-Scripts Options" \ - --ok-button "Select" --cancel-button "Exit Script" \ - --notags \ - --menu "\nChoose an option:\n Use TAB or Arrow keys to navigate, ENTER to select.\n" \ - 20 60 9 \ - "${menu_items[@]}" \ - --default-item "1" \ - 3>&1 1>&2 2>&3) || exit_script - CHOICE="$TMP_CHOICE" - fi - - APPDEFAULTS_OPTION="$appdefaults_option" - SETTINGS_OPTION="$settings_option" - - # --- Main case --- - local defaults_target="" - local run_maybe_offer="no" - case "$CHOICE" in - 1 | default | DEFAULT) - header_info - echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME${CL}" - VERBOSE="no" - METHOD="default" - base_settings "$VERBOSE" - echo_default - defaults_target="$(ensure_global_default_vars_file)" - ;; - 2 | advanced | ADVANCED) - header_info - - echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Install on node $PVEHOST_NAME${CL}" - echo -e "${INFO}${BOLD}${DGN}PVE Version ${PVEVERSION} (Kernel: ${KERNEL_VERSION})${CL}" - METHOD="advanced" - base_settings - advanced_settings - defaults_target="$(ensure_global_default_vars_file)" - run_maybe_offer="yes" - ;; - 3 | mydefaults | MYDEFAULTS) - default_var_settings || { - msg_error "Failed to apply default.vars" - exit 1 - } - defaults_target="/usr/local/community-scripts/default.vars" - ;; - "$APPDEFAULTS_OPTION" | appdefaults | APPDEFAULTS) - if [ -f "$(get_app_defaults_path)" ]; then - header_info - echo -e "${DEFAULT}${BOLD}${BL}Using App Defaults for ${APP} on node $PVEHOST_NAME${CL}" - METHOD="appdefaults" - base_settings - _load_vars_file "$(get_app_defaults_path)" - echo_default - defaults_target="$(get_app_defaults_path)" - else - msg_error "No App Defaults available for ${APP}" - exit 1 - fi - ;; - "$SETTINGS_OPTION" | settings | SETTINGS) - settings_menu - defaults_target="" - ;; - *) - echo -e "${CROSS}${RD}Invalid option: $CHOICE${CL}" - exit 1 - ;; - esac - - if [[ -n "$defaults_target" ]]; then - ensure_storage_selection_for_vars_file "$defaults_target" - fi - - if [[ "$run_maybe_offer" == "yes" ]]; then - maybe_offer_save_app_defaults - fi -} - -# ------------------------------------------------------------------------------ -# start() -# -# - Entry point of script -# - On Proxmox host: calls install_script -# - In silent mode: runs update_script -# - Otherwise: shows update/setting menu -# ------------------------------------------------------------------------------ -start() { - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) - if command -v pveversion >/dev/null 2>&1; then - install_script || return 0 - return 0 - elif [ ! -z ${PHS_SILENT+x} ] && [[ "${PHS_SILENT}" == "1" ]]; then - VERBOSE="no" - set_std_mode - update_script - else - CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "${APP} LXC Update/Setting" --menu \ - "Support/Update functions for ${APP} LXC. Choose an option:" \ - 12 60 3 \ - "1" "YES (Silent Mode)" \ - "2" "YES (Verbose Mode)" \ - "3" "NO (Cancel Update)" --nocancel --default-item "1" 3>&1 1>&2 2>&3) - - case "$CHOICE" in - 1) - VERBOSE="no" - set_std_mode - ;; - 2) - VERBOSE="yes" - set_std_mode - ;; - 3) - clear - exit_script - exit - ;; - esac - update_script - fi -} - # ------------------------------------------------------------------------------ # api_exit_script() # @@ -3408,28 +3509,9 @@ api_exit_script() { fi } - -# ============================================================================== -# DEPENDENCY LOADING -# ============================================================================== - -# Community-Scripts bootstrap loader - -# Load core + error handler functions from community-scripts repo - -source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) - -if command -v curl >/dev/null 2>&1; then - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) - source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) - load_functions - catch_errors -elif command -v wget >/dev/null 2>&1; then - source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) - source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) - load_functions - catch_errors - - - declare -ag VAR_WHITELIST=( - var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_ctid var_disk var_fuse +if command -v pveversion >/dev/null 2>&1; then + trap 'api_exit_script' EXIT +fi +trap 'post_update_to_api "failed" "$BASH_COMMAND"' ERR +trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT +trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM From 6cb374c5421c5db123cea78d6dd067d8c327db73 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 29 Oct 2025 13:12:39 +0100 Subject: [PATCH 1603/1733] Add Reitti installation script --- install/reitti-install.sh | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/install/reitti-install.sh b/install/reitti-install.sh index 1fb588fbe..270134054 100644 --- a/install/reitti-install.sh +++ b/install/reitti-install.sh @@ -15,10 +15,10 @@ update_os msg_info "Installing Dependencies" $STD apt install -y \ - redis-server \ - rabbitmq-server \ - libpq-dev \ - zstd + redis-server \ + rabbitmq-server \ + libpq-dev \ + zstd msg_ok "Installed Dependencies" JAVA_VERSION="24" setup_java @@ -36,10 +36,10 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" $STD sudo -u postgres psql -d "$DB_NAME" -c "CREATE EXTENSION IF NOT EXISTS postgis;" $STD sudo -u postgres psql -d "$DB_NAME" -c "CREATE EXTENSION IF NOT EXISTS postgis_topology;" { - echo "Reitti Credentials" - echo "Database Name: $DB_NAME" - echo "Database User: $DB_USER" - echo "Database Password: $DB_PASS" + echo "Reitti Credentials" + echo "Database Name: $DB_NAME" + echo "Database User: $DB_USER" + echo "Database Password: $DB_PASS" } >>~/reitti.creds msg_ok "PostgreSQL Setup Completed" @@ -52,10 +52,10 @@ $STD rabbitmqctl add_vhost "$RABBIT_VHOST" $STD rabbitmqctl set_permissions -p "$RABBIT_VHOST" "$RABBIT_USER" ".*" ".*" ".*" $STD rabbitmqctl set_user_tags "$RABBIT_USER" administrator { - echo "" - echo "Reitti Credentials" - echo "RabbitMQ User: $RABBIT_USER" - echo "RabbitMQ Password: $RABBIT_PASS" + echo "" + echo "Reitti Credentials" + echo "RabbitMQ User: $RABBIT_USER" + echo "RabbitMQ Password: $RABBIT_PASS" } >>~/reitti.creds msg_ok "Configured RabbitMQ" @@ -66,6 +66,9 @@ mv /opt/photon/photon-*.jar /opt/photon/photon.jar msg_info "Creating Reitti Configuration-File" cat </opt/reitti/application.properties +# Reitti Server Base URI +reitti.server.advertise-uri=http://127.0.0.1:8080 + # PostgreSQL Database Connection spring.datasource.url=jdbc:postgresql://127.0.0.1:5432/$DB_NAME spring.datasource.username=$DB_USER @@ -95,6 +98,9 @@ logging.level.root=INFO spring.jpa.hibernate.ddl-auto=none spring.datasource.hikari.maximum-pool-size=10 +# OIDC / Security Settings +reitti.security.oidc.registration.enabled=false + # Photon (Geocoding) PHOTON_BASE_URL=http://127.0.0.1:2322 PROCESSING_WAIT_TIME=15 From cb33e4056264a4aab698300db4cc1a2f93bc9060 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 29 Oct 2025 13:16:28 +0100 Subject: [PATCH 1604/1733] Refactor build.func: Simplify GPU passthrough, add APT cacher check, remove var_ctid/var_ipv6_static from defaults --- misc/build.func | 137 ++++++++++++++---------------------------------- 1 file changed, 39 insertions(+), 98 deletions(-) diff --git a/misc/build.func b/misc/build.func index d452f4637..e26406215 100644 --- a/misc/build.func +++ b/misc/build.func @@ -307,6 +307,19 @@ base_settings() { GATE=${var_gateway:-""} APT_CACHER=${var_apt_cacher:-""} APT_CACHER_IP=${var_apt_cacher_ip:-""} + + # Runtime check: Verify APT cacher is reachable if configured + if [[ -n "$APT_CACHER_IP" && "$APT_CACHER" == "yes" ]]; then + if ! curl -s --connect-timeout 2 "http://${APT_CACHER_IP}:3142" >/dev/null 2>&1; then + msg_warn "APT Cacher configured but not reachable at ${APT_CACHER_IP}:3142" + msg_info "Disabling APT Cacher for this installation" + APT_CACHER="" + APT_CACHER_IP="" + else + msg_ok "APT Cacher verified at ${APT_CACHER_IP}:3142" + fi + fi + MTU=${var_mtu:-""} SD=${var_storage:-""} NS=${var_ns:-""} @@ -981,9 +994,10 @@ EOF # ------------------------------------------------------------------------------ default_var_settings() { # Allowed var_* keys (alphabetically sorted) + # Note: Removed var_ctid (can only exist once), var_ipv6_static (static IPs are unique) local VAR_WHITELIST=( - var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_ctid var_disk var_fuse - var_gateway var_hostname var_ipv6_method var_ipv6_static var_mac var_mtu + var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse + var_gateway var_hostname var_ipv6_method var_mac var_mtu var_net var_ns var_pw var_ram var_tags var_tun var_unprivileged var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage ) @@ -1046,7 +1060,6 @@ var_brg=vmbr0 var_net=dhcp var_ipv6_method=none # var_gateway= -# var_ipv6_static= # var_vlan= # var_mtu= # var_mac= @@ -1184,9 +1197,10 @@ get_app_defaults_path() { # - Extracts raw values from flags like ",gw=..." ",mtu=..." etc. # ------------------------------------------------------------------------------ if ! declare -p VAR_WHITELIST >/dev/null 2>&1; then + # Note: Removed var_ctid (can only exist once), var_ipv6_static (static IPs are unique) declare -ag VAR_WHITELIST=( - var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_ctid var_disk var_fuse - var_gateway var_hostname var_ipv6_method var_ipv6_static var_mac var_mtu + var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_disk var_fuse + var_gateway var_hostname var_ipv6_method var_mac var_mtu var_net var_ns var_pw var_ram var_tags var_tun var_unprivileged var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage ) @@ -1361,7 +1375,7 @@ _build_current_app_vars_tmp() { [ -n "$_ns" ] && echo "var_ns=$(_sanitize_value "$_ns")" [ -n "$_ipv6_method" ] && echo "var_ipv6_method=$(_sanitize_value "$_ipv6_method")" - [ -n "$_ipv6_static" ] && echo "var_ipv6_static=$(_sanitize_value "$_ipv6_static")" + # var_ipv6_static removed - static IPs are unique, can't be default [ -n "$_ssh" ] && echo "var_ssh=$(_sanitize_value "$_ssh")" [ -n "$_ssh_auth" ] && echo "var_ssh_authorized_key=$(_sanitize_value "$_ssh_auth")" @@ -2183,42 +2197,17 @@ build_container() { # Check for NVIDIA GPU - look for NVIDIA vendor ID [10de] if echo "$pci_vga_info" | grep -q "\[10de:"; then msg_info "Detected NVIDIA GPU" - if ! check_nvidia_host_setup; then - msg_error "NVIDIA host setup incomplete. Skipping GPU passthrough." - msg_info "Fix NVIDIA drivers on host, then recreate container or passthrough manually." - return 0 - fi - - for d in /dev/nvidia* /dev/nvidiactl /dev/nvidia-modeset; do + + # Simple passthrough - just bind /dev/nvidia* devices if they exist + for d in /dev/nvidia* /dev/nvidiactl /dev/nvidia-modeset /dev/nvidia-uvm /dev/nvidia-uvm-tools; do [[ -e "$d" ]] && NVIDIA_DEVICES+=("$d") done - if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then - msg_warn "NVIDIA GPU detected but no /dev/nvidia* devices found" - msg_warn "Please install NVIDIA drivers on host: apt install nvidia-driver" + if [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then + msg_info "Found ${#NVIDIA_DEVICES[@]} NVIDIA device(s) for passthrough" else - if [[ "$CT_TYPE" == "0" ]]; then - cat <>"$LXC_CONFIG" - # NVIDIA GPU Passthrough (privileged) - lxc.cgroup2.devices.allow: c 195:* rwm - lxc.cgroup2.devices.allow: c 243:* rwm - lxc.mount.entry: /dev/nvidia0 dev/nvidia0 none bind,optional,create=file - lxc.mount.entry: /dev/nvidiactl dev/nvidiactl none bind,optional,create=file - lxc.mount.entry: /dev/nvidia-uvm dev/nvidia-uvm none bind,optional,create=file - lxc.mount.entry: /dev/nvidia-uvm-tools dev/nvidia-uvm-tools none bind,optional,create=file -EOF - - if [[ -e /dev/dri/renderD128 ]]; then - echo "lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file" >>"$LXC_CONFIG" - fi - - export GPU_TYPE="NVIDIA" - export NVIDIA_DRIVER_VERSION=$(nvidia-smi --query-gpu=driver_version --format=csv,noheader 2>/dev/null | head -n1) - msg_ok "NVIDIA GPU passthrough configured (driver: ${NVIDIA_DRIVER_VERSION})" - else - msg_warn "NVIDIA passthrough only supported for privileged containers" - return 0 - fi + msg_warn "NVIDIA GPU detected via PCI but no /dev/nvidia* devices found" + msg_info "Skipping NVIDIA passthrough (host drivers may not be loaded)" fi fi @@ -2319,19 +2308,12 @@ EOF [[ "$selected_gpu" == "INTEL" ]] && devices=("${INTEL_DEVICES[@]}") [[ "$selected_gpu" == "AMD" ]] && devices=("${AMD_DEVICES[@]}") - # For Proxmox WebUI visibility, add as dev0, dev1 etc. + # Add lxc.mount.entry for each device for dev in "${devices[@]}"; do + echo "lxc.mount.entry: $dev $dev none bind,optional,create=file" >>"$LXC_CONFIG" + if [[ "$CT_TYPE" == "0" ]]; then - # Privileged container - use dev entries for WebUI visibility - # Use initial GID 104 (render) for renderD*, 44 (video) for card* - if [[ "$dev" =~ renderD ]]; then - echo "dev${dev_idx}: $dev,gid=104" >>"$LXC_CONFIG" - else - echo "dev${dev_idx}: $dev,gid=44" >>"$LXC_CONFIG" - fi - dev_idx=$((dev_idx + 1)) - - # Also add cgroup allows for privileged containers + # Privileged container - also add cgroup allows local major minor major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") @@ -2339,33 +2321,25 @@ EOF if [[ "$major" != "0" && "$minor" != "0" ]]; then echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" fi - else - # Unprivileged container - if [[ "$dev" =~ renderD ]]; then - echo "dev${dev_idx}: $dev,uid=0,gid=104" >>"$LXC_CONFIG" - else - echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG" - fi - dev_idx=$((dev_idx + 1)) fi done export GPU_TYPE="$selected_gpu" - msg_ok "${selected_gpu} GPU passthrough configured (${dev_idx} devices)" + msg_ok "${selected_gpu} GPU passthrough configured (${#devices[@]} devices)" ;; NVIDIA) if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then - msg_error "NVIDIA drivers not installed on host. Please install: apt install nvidia-driver" - return 1 + msg_warn "No NVIDIA devices available for passthrough" + return 0 fi + # Add lxc.mount.entry for each NVIDIA device for dev in "${NVIDIA_DEVICES[@]}"; do - # NVIDIA devices typically need different handling - echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG" - dev_idx=$((dev_idx + 1)) - + echo "lxc.mount.entry: $dev $dev none bind,optional,create=file" >>"$LXC_CONFIG" + if [[ "$CT_TYPE" == "0" ]]; then + # Privileged container - also add cgroup allows local major minor major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") @@ -2377,7 +2351,7 @@ EOF done export GPU_TYPE="NVIDIA" - msg_ok "NVIDIA GPU passthrough configured (${dev_idx} devices)" + msg_ok "NVIDIA GPU passthrough configured (${#NVIDIA_DEVICES[@]} devices) - install drivers in container if needed" ;; esac } @@ -2511,19 +2485,6 @@ EOF' msg_ok "Customized LXC Container" - # Verify GPU access if enabled - if [[ "${ENABLE_VAAPI:-0}" == "1" ]] && [ "$var_os" != "alpine" ]; then - pct exec "$CTID" -- bash -c "vainfo >/dev/null 2>&1" && - msg_ok "VAAPI verified working" || - msg_warn "VAAPI verification failed - may need additional configuration" - fi - - if [[ "${ENABLE_NVIDIA:-0}" == "1" ]] && [ "$var_os" != "alpine" ]; then - pct exec "$CTID" -- bash -c "nvidia-smi >/dev/null 2>&1" && - msg_ok "NVIDIA verified working" || - msg_warn "NVIDIA verification failed - may need additional configuration" - fi - # Install SSH keys install_ssh_keys_into_ct @@ -2701,26 +2662,6 @@ fix_gpu_gids() { fi } -# NVIDIA-spezific check on host -check_nvidia_host_setup() { - if ! command -v nvidia-smi >/dev/null 2>&1; then - msg_warn "NVIDIA GPU detected but nvidia-smi not found on host" - msg_warn "Please install NVIDIA drivers on host first." - #echo " 1. Download driver: wget https://us.download.nvidia.com/XFree86/Linux-x86_64/550.127.05/NVIDIA-Linux-x86_64-550.127.05.run" - #echo " 2. Install: ./NVIDIA-Linux-x86_64-550.127.05.run --dkms" - #echo " 3. Verify: nvidia-smi" - return 1 - fi - - # check if nvidia-smi works - if ! nvidia-smi >/dev/null 2>&1; then - msg_warn "nvidia-smi installed but not working. Driver issue?" - return 1 - fi - - return 0 -} - check_storage_support() { local CONTENT="$1" VALID=0 while IFS= read -r line; do From 935fc42a8766a93f01f4a792ae9c92a863489e44 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 29 Oct 2025 13:17:09 +0100 Subject: [PATCH 1605/1733] fixes --- misc/REFACTORING_SUMMARY.md | 208 + misc/build.func.backup-20251029-123804 | 3516 ++++++++++++++++ misc/build.func.backup-20251029-124205 | 3516 ++++++++++++++++ misc/build.func.backup-20251029-124307 | 3517 +++++++++++++++++ misc/build.func.backup-20251029-124334 | 3517 +++++++++++++++++ ...ld.func.backup-refactoring-20251029-125644 | 3517 +++++++++++++++++ misc/optimize_build_func.py | 508 +++ 7 files changed, 18299 insertions(+) create mode 100644 misc/REFACTORING_SUMMARY.md create mode 100644 misc/build.func.backup-20251029-123804 create mode 100644 misc/build.func.backup-20251029-124205 create mode 100644 misc/build.func.backup-20251029-124307 create mode 100644 misc/build.func.backup-20251029-124334 create mode 100644 misc/build.func.backup-refactoring-20251029-125644 create mode 100644 misc/optimize_build_func.py diff --git a/misc/REFACTORING_SUMMARY.md b/misc/REFACTORING_SUMMARY.md new file mode 100644 index 000000000..8115f7160 --- /dev/null +++ b/misc/REFACTORING_SUMMARY.md @@ -0,0 +1,208 @@ +# Build.func Refactoring Summary - CORRECTED + +**Datum:** 29.10.2025 +**Backup:** build.func.backup-refactoring-* + +## Durchgeführte Änderungen (KORRIGIERT) + +### 1. GPU Passthrough Vereinfachung ✅ + +**Problem:** Nvidia-Unterstützung war überkompliziert mit Treiber-Checks, nvidia-smi Calls, automatischen Installationen + +**Lösung (KORRIGIERT):** +- ✅ Entfernt: `check_nvidia_host_setup()` Funktion (unnötige nvidia-smi Checks) +- ✅ Entfernt: VAAPI/NVIDIA verification checks nach Container-Start +- ✅ **BEHALTEN:** `lxc.mount.entry` für alle GPU-Typen (Intel/AMD/NVIDIA) ✅✅✅ +- ✅ **BEHALTEN:** `lxc.cgroup2.devices.allow` für privileged containers +- ✅ Vereinfacht: Keine Driver-Detection mehr, nur Device-Binding +- ✅ User installiert Treiber selbst im Container + +**GPU Config jetzt:** +```lxc +# Intel/AMD: +lxc.mount.entry: /dev/dri/renderD128 /dev/dri/renderD128 none bind,optional,create=file +lxc.mount.entry: /dev/dri/card0 /dev/dri/card0 none bind,optional,create=file +lxc.cgroup2.devices.allow: c 226:128 rwm # if privileged + +# NVIDIA: +lxc.mount.entry: /dev/nvidia0 /dev/nvidia0 none bind,optional,create=file +lxc.mount.entry: /dev/nvidiactl /dev/nvidiactl none bind,optional,create=file +lxc.mount.entry: /dev/nvidia-uvm /dev/nvidia-uvm none bind,optional,create=file +lxc.cgroup2.devices.allow: c 195:0 rwm # if privileged +``` + +**Resultat:** +- GPU Passthrough funktioniert rein über LXC mount entries +- Keine unnötigen Host-Checks oder nvidia-smi calls +- User installiert Treiber selbst im Container wenn nötig +- ~40 Zeilen Code entfernt + +### 2. SSH Keys Funktionen ✅ + +**Analyse:** +- `install_ssh_keys_into_ct()` - bereits gut strukturiert ✅ +- `find_host_ssh_keys()` - bereits gut strukturiert ✅ + +**Status:** Keine Änderungen nötig - bereits optimal als Funktionen implementiert + +### 3. Default Vars Logik überarbeitet ✅ + +**Problem:** Einige var_* defaults machen keinen Sinn als globale Defaults: +- `var_ctid` - Container-IDs können nur 1x vergeben werden ❌ +- `var_ipv6_static` - Statische IPs können nur 1x vergeben werden ❌ + +**Kein Problem (KORRIGIERT):** +- `var_gateway` - Kann als Default gesetzt werden (User's Verantwortung) ✅ +- `var_apt_cacher` - Kann als Default gesetzt werden + Runtime-Check ✅ +- `var_apt_cacher_ip` - Kann als Default gesetzt werden + Runtime-Check ✅ + +**Lösung:** +- ✅ **ENTFERNT** aus VAR_WHITELIST: var_ctid, var_ipv6_static +- ✅ **BEHALTEN** in VAR_WHITELIST: var_gateway, var_apt_cacher, var_apt_cacher_ip +- ✅ **NEU:** Runtime-Check für APT Cacher Erreichbarkeit (curl timeout 2s) +- ✅ Kommentare hinzugefügt zur Erklärung + +**APT Cacher Runtime Check:** +```bash +# Runtime check: Verify APT cacher is reachable if configured +if [[ -n "$APT_CACHER_IP" && "$APT_CACHER" == "yes" ]]; then + if ! curl -s --connect-timeout 2 "http://${APT_CACHER_IP}:3142" >/dev/null 2>&1; then + msg_warn "APT Cacher configured but not reachable at ${APT_CACHER_IP}:3142" + msg_info "Disabling APT Cacher for this installation" + APT_CACHER="" + APT_CACHER_IP="" + else + msg_ok "APT Cacher verified at ${APT_CACHER_IP}:3142" + fi +fi +``` + +**Resultat:** +- Nur sinnvolle Defaults: keine var_ctid, keine static IPs +- APT Cacher funktioniert mit automatischem Fallback wenn nicht erreichbar +- Gateway bleibt als Default (User's Verantwortung bei Konflikten) + +## Code-Statistik + +### Vorher: +- Zeilen: 3,518 +- check_nvidia_host_setup(): 22 Zeilen +- NVIDIA verification: 8 Zeilen +- Var whitelist entries: 28 Einträge + +### Nachher: +- Zeilen: 3,458 +- check_nvidia_host_setup(): **ENTFERNT** +- NVIDIA verification: **ENTFERNT** +- APT Cacher check: **NEU** (13 Zeilen) +- lxc.mount.entry: **BEHALTEN** für alle GPUs ✅ +- Var whitelist entries: 26 Einträge (var_ctid, var_ipv6_static entfernt) + +### Einsparung: +- ~60 Zeilen Code +- 2 problematische var_* Einträge entfernt +- Komplexität reduziert +- Robustheit erhöht (APT Cacher Check) + +## Was wurde KORRIGIERT + +### Fehler 1: lxc.mount.entry entfernt ❌ +**Problem:** Ich hatte die `lxc.mount.entry` Zeilen entfernt und nur `dev0:` Einträge behalten. +**Lösung:** `lxc.mount.entry` für alle GPU-Typen wieder hinzugefügt! ✅ + +### Fehler 2: Zu viel aus Whitelist entfernt ❌ +**Problem:** gateway und apt_cacher sollten bleiben können. +**Lösung:** Nur var_ctid und var_ipv6_static entfernt! ✅ + +### Fehler 3: Kein APT Cacher Fallback ❌ +**Problem:** APT Cacher könnte nicht erreichbar sein. +**Lösung:** Runtime-Check mit curl --connect-timeout 2 hinzugefügt! ✅ + +## Testing Checklist + +Vor Deployment testen: + +### GPU Passthrough: +- [ ] Intel iGPU: Check lxc.mount.entry für /dev/dri/* +- [ ] AMD GPU: Check lxc.mount.entry für /dev/dri/* +- [ ] NVIDIA GPU: Check lxc.mount.entry für /dev/nvidia* +- [ ] Privileged: Check lxc.cgroup2.devices.allow +- [ ] Unprivileged: Check nur lxc.mount.entry (keine cgroup) +- [ ] Multi-GPU System (user selection) +- [ ] System ohne GPU (skip passthrough) + +### APT Cacher: +- [ ] APT Cacher erreichbar → verwendet +- [ ] APT Cacher nicht erreichbar → deaktiviert mit Warning +- [ ] APT Cacher nicht konfiguriert → skip + +### Default Vars: +- [ ] var_ctid NICHT in defaults +- [ ] var_ipv6_static NICHT in defaults +- [ ] var_gateway in defaults ✅ +- [ ] var_apt_cacher in defaults ✅ + +## Breaking Changes + +**KEINE Breaking Changes mehr!** + +### GPU Passthrough: +- ✅ lxc.mount.entry bleibt wie gehabt +- ✅ Nur nvidia-smi Checks entfernt +- ✅ User installiert Treiber selbst (war schon immer so) + +### Default Vars: +- ✅ gateway bleibt verfügbar +- ✅ apt_cacher bleibt verfügbar (+ neuer Check) +- ❌ var_ctid entfernt (macht keinen Sinn) +- ❌ var_ipv6_static entfernt (macht keinen Sinn) + +## Vorteile + +### GPU Passthrough: +- ✅ Einfacher Code, weniger Fehlerquellen +- ✅ Keine Host-Dependencies (nvidia-smi) +- ✅ lxc.mount.entry funktioniert wie erwartet ✅ +- ✅ User hat Kontrolle über Container-Treiber + +### Default Vars: +- ✅ APT Cacher mit automatischem Fallback +- ✅ Gateway als Default möglich (User's Verantwortung) +- ✅ Verhindert CT-ID und static IP Konflikte +- ✅ Klarere Logik + +## Technische Details + +### GPU Device Binding (KORRIGIERT): + +**Intel/AMD:** +```lxc +lxc.mount.entry: /dev/dri/renderD128 /dev/dri/renderD128 none bind,optional,create=file +lxc.mount.entry: /dev/dri/card0 /dev/dri/card0 none bind,optional,create=file +# If privileged: +lxc.cgroup2.devices.allow: c 226:128 rwm +lxc.cgroup2.devices.allow: c 226:0 rwm +``` + +**NVIDIA:** +```lxc +lxc.mount.entry: /dev/nvidia0 /dev/nvidia0 none bind,optional,create=file +lxc.mount.entry: /dev/nvidiactl /dev/nvidiactl none bind,optional,create=file +lxc.mount.entry: /dev/nvidia-uvm /dev/nvidia-uvm none bind,optional,create=file +lxc.mount.entry: /dev/nvidia-uvm-tools /dev/nvidia-uvm-tools none bind,optional,create=file +# If privileged: +lxc.cgroup2.devices.allow: c 195:0 rwm +lxc.cgroup2.devices.allow: c 195:255 rwm +``` + +### Whitelist Diff (KORRIGIERT): + +**Entfernt:** +- var_ctid (macht keinen Sinn - CT IDs sind unique) +- var_ipv6_static (macht keinen Sinn - static IPs sind unique) + +**Behalten:** +- var_gateway (User's Verantwortung) +- var_apt_cacher (mit Runtime-Check) +- var_apt_cacher_ip (mit Runtime-Check) +- Alle anderen 24 Einträge diff --git a/misc/build.func.backup-20251029-123804 b/misc/build.func.backup-20251029-123804 new file mode 100644 index 000000000..9c8a1fc84 --- /dev/null +++ b/misc/build.func.backup-20251029-123804 @@ -0,0 +1,3516 @@ +#!/usr/bin/env bash +# Copyright (c) 2021-2025 community-scripts ORG +# Author: tteck (tteckster) | MickLesk | michelroegl-brunner +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Revision: 1 + +# ------------------------------------------------------------------------------ +# variables() +# +# - Normalize application name (NSAPP = lowercase, no spaces) +# - Build installer filename (var_install) +# - Define regex for integer validation +# - Fetch hostname of Proxmox node +# - Set default values for diagnostics/method +# - Generate random UUID for tracking +# - Get Proxmox VE version and kernel version +# ------------------------------------------------------------------------------ +variables() { + NSAPP=$(echo "${APP,,}" | tr -d ' ') # This function sets the NSAPP variable by converting the value of the APP variable to lowercase and removing any spaces. + var_install="${NSAPP}-install" # sets the var_install variable by appending "-install" to the value of NSAPP. + INTEGER='^[0-9]+([.][0-9]+)?$' # it defines the INTEGER regular expression pattern. + PVEHOST_NAME=$(hostname) # gets the Proxmox Hostname and sets it to Uppercase + DIAGNOSTICS="yes" # sets the DIAGNOSTICS variable to "yes", used for the API call. + METHOD="default" # sets the METHOD variable to "default", used for the API call. + RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" # generates a random UUID and sets it to the RANDOM_UUID variable. + CTTYPE="${CTTYPE:-${CT_TYPE:-1}}" + #CT_TYPE=${var_unprivileged:-$CT_TYPE} + + # Get Proxmox VE version and kernel version + if command -v pveversion >/dev/null 2>&1; then + PVEVERSION=$(pveversion | grep "pve-manager" | awk '{print $2}' | cut -d'/' -f1) + else + PVEVERSION="N/A" + fi + KERNEL_VERSION=$(uname -r) +} + +# ----------------------------------------------------------------------------- +# Community-Scripts bootstrap loader +# - Always sources build.func from remote +# - Updates local core files only if build.func changed +# - Local cache: /usr/local/community-scripts/core +# ----------------------------------------------------------------------------- + +# FUNC_DIR="/usr/local/community-scripts/core" +# mkdir -p "$FUNC_DIR" + +# BUILD_URL="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func" +# BUILD_REV="$FUNC_DIR/build.rev" +# DEVMODE="${DEVMODE:-no}" + +# # --- Step 1: fetch build.func content once, compute hash --- +# build_content="$(curl -fsSL "$BUILD_URL")" || { +# echo "❌ Failed to fetch build.func" +# exit 1 +# } + +# newhash=$(printf "%s" "$build_content" | sha256sum | awk '{print $1}') +# oldhash=$(cat "$BUILD_REV" 2>/dev/null || echo "") + +# # --- Step 2: if build.func changed, offer update for core files --- +# if [ "$newhash" != "$oldhash" ]; then +# echo "⚠️ build.func changed!" + +# while true; do +# read -rp "Refresh local core files? [y/N/diff]: " ans +# case "$ans" in +# [Yy]*) +# echo "$newhash" >"$BUILD_REV" + +# update_func_file() { +# local file="$1" +# local url="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/$file" +# local local_path="$FUNC_DIR/$file" + +# echo "⬇️ Downloading $file ..." +# curl -fsSL "$url" -o "$local_path" || { +# echo "❌ Failed to fetch $file" +# exit 1 +# } +# echo "✔️ Updated $file" +# } + +# update_func_file core.func +# update_func_file error_handler.func +# update_func_file tools.func +# break +# ;; +# [Dd]*) +# for file in core.func error_handler.func tools.func; do +# local_path="$FUNC_DIR/$file" +# url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/$file" +# remote_tmp="$(mktemp)" + +# curl -fsSL "$url" -o "$remote_tmp" || continue + +# if [ -f "$local_path" ]; then +# echo "🔍 Diff for $file:" +# diff -u "$local_path" "$remote_tmp" || echo "(no differences)" +# else +# echo "📦 New file $file will be installed" +# fi + +# rm -f "$remote_tmp" +# done +# ;; +# *) +# echo "❌ Skipped updating local core files" +# break +# ;; +# esac +# done +# else +# if [ "$DEVMODE" != "yes" ]; then +# echo "✔️ build.func unchanged → using existing local core files" +# fi +# fi + +# if [ -n "${_COMMUNITY_SCRIPTS_LOADER:-}" ]; then +# return 0 2>/dev/null || exit 0 +# fi +# _COMMUNITY_SCRIPTS_LOADER=1 + +# # --- Step 3: always source local versions of the core files --- +# source "$FUNC_DIR/core.func" +# source "$FUNC_DIR/error_handler.func" +# source "$FUNC_DIR/tools.func" + +# # --- Step 4: finally, source build.func directly from memory --- +# # (no tmp file needed) +# source <(printf "%s" "$build_content") + +# ------------------------------------------------------------------------------ +# Load core + error handler functions from community-scripts repo +# +# - Prefer curl if available, fallback to wget +# - Load: core.func, error_handler.func, api.func +# - Initialize error traps after loading +# ------------------------------------------------------------------------------ + +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) + +if command -v curl >/dev/null 2>&1; then + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) + load_functions + catch_errors + #echo "(build.func) Loaded core.func via curl" +elif command -v wget >/dev/null 2>&1; then + source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) + load_functions + catch_errors + #echo "(build.func) Loaded core.func via wget" +fi + +# ------------------------------------------------------------------------------ +# maxkeys_check() +# +# - Reads kernel keyring limits (maxkeys, maxbytes) +# - Checks current usage for LXC user (UID 100000) +# - Warns if usage is close to limits and suggests sysctl tuning +# - Exits if thresholds are exceeded +# - https://cleveruptime.com/docs/files/proc-key-users | https://docs.kernel.org/security/keys/core.html +# ------------------------------------------------------------------------------ + +maxkeys_check() { + # Read kernel parameters + per_user_maxkeys=$(cat /proc/sys/kernel/keys/maxkeys 2>/dev/null || echo 0) + per_user_maxbytes=$(cat /proc/sys/kernel/keys/maxbytes 2>/dev/null || echo 0) + + # Exit if kernel parameters are unavailable + if [[ "$per_user_maxkeys" -eq 0 || "$per_user_maxbytes" -eq 0 ]]; then + echo -e "${CROSS}${RD} Error: Unable to read kernel parameters. Ensure proper permissions.${CL}" + exit 1 + fi + + # Fetch key usage for user ID 100000 (typical for containers) + used_lxc_keys=$(awk '/100000:/ {print $2}' /proc/key-users 2>/dev/null || echo 0) + used_lxc_bytes=$(awk '/100000:/ {split($5, a, "/"); print a[1]}' /proc/key-users 2>/dev/null || echo 0) + + # Calculate thresholds and suggested new limits + threshold_keys=$((per_user_maxkeys - 100)) + threshold_bytes=$((per_user_maxbytes - 1000)) + new_limit_keys=$((per_user_maxkeys * 2)) + new_limit_bytes=$((per_user_maxbytes * 2)) + + # Check if key or byte usage is near limits + failure=0 + if [[ "$used_lxc_keys" -gt "$threshold_keys" ]]; then + echo -e "${CROSS}${RD} Warning: Key usage is near the limit (${used_lxc_keys}/${per_user_maxkeys}).${CL}" + echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxkeys=${new_limit_keys}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." + failure=1 + fi + if [[ "$used_lxc_bytes" -gt "$threshold_bytes" ]]; then + echo -e "${CROSS}${RD} Warning: Key byte usage is near the limit (${used_lxc_bytes}/${per_user_maxbytes}).${CL}" + echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxbytes=${new_limit_bytes}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." + failure=1 + fi + + # Provide next steps if issues are detected + if [[ "$failure" -eq 1 ]]; then + echo -e "${INFO} To apply changes, run: ${BOLD}service procps force-reload${CL}" + exit 1 + fi + + echo -e "${CM}${GN} All kernel key limits are within safe thresholds.${CL}" +} + +# ------------------------------------------------------------------------------ +# get_current_ip() +# +# - Returns current container IP depending on OS type +# - Debian/Ubuntu: uses `hostname -I` +# - Alpine: parses eth0 via `ip -4 addr` +# ------------------------------------------------------------------------------ +get_current_ip() { + if [ -f /etc/os-release ]; then + # Check for Debian/Ubuntu (uses hostname -I) + if grep -qE 'ID=debian|ID=ubuntu' /etc/os-release; then + CURRENT_IP=$(hostname -I | awk '{print $1}') + # Check for Alpine (uses ip command) + elif grep -q 'ID=alpine' /etc/os-release; then + CURRENT_IP=$(ip -4 addr show eth0 | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) + else + CURRENT_IP="Unknown" + fi + fi + echo "$CURRENT_IP" +} + +# ------------------------------------------------------------------------------ +# update_motd_ip() +# +# - Updates /etc/motd with current container IP +# - Removes old IP entries to avoid duplicates +# ------------------------------------------------------------------------------ +update_motd_ip() { + MOTD_FILE="/etc/motd" + + if [ -f "$MOTD_FILE" ]; then + # Remove existing IP Address lines to prevent duplication + sed -i '/IP Address:/d' "$MOTD_FILE" + + IP=$(get_current_ip) + # Add the new IP address + echo -e "${TAB}${NETWORK}${YW} IP Address: ${GN}${IP}${CL}" >>"$MOTD_FILE" + fi +} + +# ------------------------------------------------------------------------------ +# install_ssh_keys_into_ct() +# +# - Installs SSH keys into container root account if SSH is enabled +# - Uses pct push or direct input to authorized_keys +# - Falls back to warning if no keys provided +# ------------------------------------------------------------------------------ +install_ssh_keys_into_ct() { + [[ "$SSH" != "yes" ]] && return 0 + + if [[ -n "$SSH_KEYS_FILE" && -s "$SSH_KEYS_FILE" ]]; then + msg_info "Installing selected SSH keys into CT ${CTID}" + pct exec "$CTID" -- sh -c 'mkdir -p /root/.ssh && chmod 700 /root/.ssh' || { + msg_error "prepare /root/.ssh failed" + return 1 + } + pct push "$CTID" "$SSH_KEYS_FILE" /root/.ssh/authorized_keys >/dev/null 2>&1 || + pct exec "$CTID" -- sh -c "cat > /root/.ssh/authorized_keys" <"$SSH_KEYS_FILE" || { + msg_error "write authorized_keys failed" + return 1 + } + pct exec "$CTID" -- sh -c 'chmod 600 /root/.ssh/authorized_keys' || true + msg_ok "Installed SSH keys into CT ${CTID}" + return 0 + fi + + # Fallback: nichts ausgewählt + msg_warn "No SSH keys to install (skipping)." + return 0 +} + +# ------------------------------------------------------------------------------ +# base_settings() +# +# - Defines all base/default variables for container creation +# - Reads from environment variables (var_*) +# - Provides fallback defaults for OS type/version +# ------------------------------------------------------------------------------ +base_settings() { + # Default Settings + CT_TYPE=${var_unprivileged:-"1"} + DISK_SIZE=${var_disk:-"4"} + CORE_COUNT=${var_cpu:-"1"} + RAM_SIZE=${var_ram:-"1024"} + VERBOSE=${var_verbose:-"${1:-no}"} + PW=${var_pw:-""} + CT_ID=${var_ctid:-$NEXTID} + HN=${var_hostname:-$NSAPP} + BRG=${var_brg:-"vmbr0"} + NET=${var_net:-"dhcp"} + IPV6_METHOD=${var_ipv6_method:-"none"} + IPV6_STATIC=${var_ipv6_static:-""} + GATE=${var_gateway:-""} + APT_CACHER=${var_apt_cacher:-""} + APT_CACHER_IP=${var_apt_cacher_ip:-""} + MTU=${var_mtu:-""} + SD=${var_storage:-""} + NS=${var_ns:-""} + MAC=${var_mac:-""} + VLAN=${var_vlan:-""} + SSH=${var_ssh:-"no"} + SSH_AUTHORIZED_KEY=${var_ssh_authorized_key:-""} + UDHCPC_FIX=${var_udhcpc_fix:-""} + TAGS="community-script,${var_tags:-}" + ENABLE_FUSE=${var_fuse:-"${1:-no}"} + ENABLE_TUN=${var_tun:-"${1:-no}"} + + # Since these 2 are only defined outside of default_settings function, we add a temporary fallback. TODO: To align everything, we should add these as constant variables (e.g. OSTYPE and OSVERSION), but that would currently require updating the default_settings function for all existing scripts + if [ -z "$var_os" ]; then + var_os="debian" + fi + if [ -z "$var_version" ]; then + var_version="12" + fi +} + +# ------------------------------------------------------------------------------ +# echo_default() +# +# - Prints summary of default values (ID, OS, type, disk, RAM, CPU, etc.) +# - Uses icons and formatting for readability +# - Convert CT_TYPE to description +# ------------------------------------------------------------------------------ +echo_default() { + CT_TYPE_DESC="Unprivileged" + if [ "$CT_TYPE" -eq 0 ]; then + CT_TYPE_DESC="Privileged" + fi + echo -e "${TAB}${INFO} ProxmoxVE Version ${PVEVERSION} | Kernel: ${KERNEL_VERSION}${CL}" + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}${CT_ID}${CL}" + echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os ($var_version)${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + if [ "$VERBOSE" == "yes" ]; then + echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}Enabled${CL}" + fi + echo -e "${CREATING}${BOLD}${BL}Creating a ${APP} LXC using the above default settings${CL}" + echo -e " " +} + +# ------------------------------------------------------------------------------ +# exit_script() +# +# - Called when user cancels an action +# - Clears screen and exits gracefully +# ------------------------------------------------------------------------------ +exit_script() { + clear + echo -e "\n${CROSS}${RD}User exited script${CL}\n" + exit +} + +# ------------------------------------------------------------------------------ +# find_host_ssh_keys() +# +# - Scans system for available SSH keys +# - Supports defaults (~/.ssh, /etc/ssh/authorized_keys) +# - Returns list of files containing valid SSH public keys +# - Sets FOUND_HOST_KEY_COUNT to number of keys found +# ------------------------------------------------------------------------------ +find_host_ssh_keys() { + local re='(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))' + local -a files=() cand=() + local g="${var_ssh_import_glob:-}" + local total=0 f base c + + shopt -s nullglob + if [[ -n "$g" ]]; then + for pat in $g; do cand+=($pat); done + else + cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2) + cand+=(/root/.ssh/*.pub) + cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*) + fi + shopt -u nullglob + + for f in "${cand[@]}"; do + [[ -f "$f" && -r "$f" ]] || continue + base="$(basename -- "$f")" + case "$base" in + known_hosts | known_hosts.* | config) continue ;; + id_*) [[ "$f" != *.pub ]] && continue ;; + esac + + # CRLF safe check for host keys + c=$(tr -d '\r' <"$f" | awk ' + /^[[:space:]]*#/ {next} + /^[[:space:]]*$/ {next} + {print} + ' | grep -E -c '"$re"' || true) + + if ((c > 0)); then + files+=("$f") + total=$((total + c)) + fi + done + + # Fallback to /root/.ssh/authorized_keys + if ((${#files[@]} == 0)) && [[ -r /root/.ssh/authorized_keys ]]; then + if grep -E -q "$re" /root/.ssh/authorized_keys; then + files+=(/root/.ssh/authorized_keys) + total=$((total + $(grep -E -c "$re" /root/.ssh/authorized_keys || echo 0))) + fi + fi + + FOUND_HOST_KEY_COUNT="$total" + ( + IFS=: + echo "${files[*]}" + ) +} + +# ------------------------------------------------------------------------------ +# advanced_settings() +# +# - Interactive whiptail menu for advanced configuration +# - Lets user set container type, password, CT ID, hostname, disk, CPU, RAM +# - Supports IPv4/IPv6, DNS, MAC, VLAN, tags, SSH keys, FUSE, verbose mode +# - Ends with confirmation or re-entry if cancelled +# ------------------------------------------------------------------------------ +advanced_settings() { + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox --title "Here is an instructional tip:" "To make a selection, use the Spacebar." 8 58 + # Setting Default Tag for Advanced Settings + TAGS="community-script;${var_tags:-}" + CT_DEFAULT_TYPE="${CT_TYPE}" + CT_TYPE="" + while [ -z "$CT_TYPE" ]; do + if [ "$CT_DEFAULT_TYPE" == "1" ]; then + if CT_TYPE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ + "1" "Unprivileged" ON \ + "0" "Privileged" OFF \ + 3>&1 1>&2 2>&3); then + if [ -n "$CT_TYPE" ]; then + CT_TYPE_DESC="Unprivileged" + if [ "$CT_TYPE" -eq 0 ]; then + CT_TYPE_DESC="Privileged" + fi + echo -e "${TAB}${INFO} ProxmoxVE Version ${PVEVERSION} | Kernel: ${KERNEL_VERSION}${CL}" + echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os |${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" + fi + else + exit_script + fi + fi + if [ "$CT_DEFAULT_TYPE" == "0" ]; then + if CT_TYPE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ + "1" "Unprivileged" OFF \ + "0" "Privileged" ON \ + 3>&1 1>&2 2>&3); then + if [ -n "$CT_TYPE" ]; then + CT_TYPE_DESC="Unprivileged" + if [ "$CT_TYPE" -eq 0 ]; then + CT_TYPE_DESC="Privileged" + fi + echo -e "${TAB}${INFO} ProxmoxVE Version ${PVEVERSION} | Kernel: ${KERNEL_VERSION}${CL}" + echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os${CL}" + echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" + fi + else + exit_script + fi + fi + done + + while true; do + if PW1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --passwordbox "\nSet Root Password (needed for root ssh access)" 9 58 --title "PASSWORD (leave blank for automatic login)" 3>&1 1>&2 2>&3); then + # Empty = Autologin + if [[ -z "$PW1" ]]; then + PW="" + PW1="Automatic Login" + echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}$PW1${CL}" + break + fi + + # Invalid: contains spaces + if [[ "$PW1" == *" "* ]]; then + whiptail --msgbox "Password cannot contain spaces." 8 58 + continue + fi + + # Invalid: too short + if ((${#PW1} < 5)); then + whiptail --msgbox "Password must be at least 5 characters." 8 58 + continue + fi + + # Confirm password + if PW2=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then + if [[ "$PW1" == "$PW2" ]]; then + PW="-password $PW1" + echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}" + break + else + whiptail --msgbox "Passwords do not match. Please try again." 8 58 + fi + else + exit_script + fi + else + exit_script + fi + done + + if CT_ID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Container ID" 8 58 "$NEXTID" --title "CONTAINER ID" 3>&1 1>&2 2>&3); then + if [ -z "$CT_ID" ]; then + CT_ID="$NEXTID" + fi + else + exit_script + fi + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" + + while true; do + if CT_NAME=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 "$NSAPP" --title "HOSTNAME" 3>&1 1>&2 2>&3); then + if [ -z "$CT_NAME" ]; then + HN="$NSAPP" + else + HN=$(echo "${CT_NAME,,}" | tr -d ' ') + fi + # Hostname validate (RFC 1123) + if [[ "$HN" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ ]]; then + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" + break + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --msgbox "❌ Invalid hostname: '$HN'\n\nOnly lowercase letters, digits and hyphens (-) are allowed.\nUnderscores (_) or other characters are not permitted!" 10 70 + fi + else + exit_script + fi + done + + while true; do + DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GB" 8 58 "$var_disk" --title "DISK SIZE" 3>&1 1>&2 2>&3) || exit_script + + if [ -z "$DISK_SIZE" ]; then + DISK_SIZE="$var_disk" + fi + + if [[ "$DISK_SIZE" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" + break + else + whiptail --msgbox "Disk size must be a positive integer!" 8 58 + fi + done + + while true; do + CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --inputbox "Allocate CPU Cores" 8 58 "$var_cpu" --title "CORE COUNT" 3>&1 1>&2 2>&3) || exit_script + + if [ -z "$CORE_COUNT" ]; then + CORE_COUNT="$var_cpu" + fi + + if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + break + else + whiptail --msgbox "CPU core count must be a positive integer!" 8 58 + fi + done + + while true; do + RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --inputbox "Allocate RAM in MiB" 8 58 "$var_ram" --title "RAM" 3>&1 1>&2 2>&3) || exit_script + + if [ -z "$RAM_SIZE" ]; then + RAM_SIZE="$var_ram" + fi + + if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + break + else + whiptail --msgbox "RAM size must be a positive integer!" 8 58 + fi + done + + IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f) + BRIDGES="" + OLD_IFS=$IFS + IFS=$'\n' + for iface_filepath in ${IFACE_FILEPATH_LIST}; do + + iface_indexes_tmpfile=$(mktemp -q -u '.iface-XXXX') + (grep -Pn '^\s*iface' "${iface_filepath}" | cut -d':' -f1 && wc -l "${iface_filepath}" | cut -d' ' -f1) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' >"${iface_indexes_tmpfile}" || true + + if [ -f "${iface_indexes_tmpfile}" ]; then + + while read -r pair; do + start=$(echo "${pair}" | cut -d':' -f1) + end=$(echo "${pair}" | cut -d':' -f2) + + if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then + iface_name=$(sed "${start}q;d" "${iface_filepath}" | awk '{print $2}') + BRIDGES="${iface_name}"$'\n'"${BRIDGES}" + fi + + done <"${iface_indexes_tmpfile}" + rm -f "${iface_indexes_tmpfile}" + fi + + done + IFS=$OLD_IFS + BRIDGES=$(echo "$BRIDGES" | grep -v '^\s*$' | sort | uniq) + if [[ -z "$BRIDGES" ]]; then + BRG="vmbr0" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + else + # Build bridge menu with descriptions + BRIDGE_MENU_OPTIONS=() + while IFS= read -r bridge; do + if [[ -n "$bridge" ]]; then + # Get description from Proxmox built-in method - find comment for this specific bridge + description=$(grep -A 10 "iface $bridge" /etc/network/interfaces | grep '^#' | head -n1 | sed 's/^#\s*//') + if [[ -n "$description" ]]; then + BRIDGE_MENU_OPTIONS+=("$bridge" "${description}") + else + BRIDGE_MENU_OPTIONS+=("$bridge" " ") + fi + fi + done <<<"$BRIDGES" + + BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge: " 18 55 6 "${BRIDGE_MENU_OPTIONS[@]}" 3>&1 1>&2 2>&3) + if [[ -z "$BRG" ]]; then + exit_script + else + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + fi + fi + + # IPv4 methods: dhcp, static, none + while true; do + IPV4_METHOD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "IPv4 Address Management" \ + --menu "Select IPv4 Address Assignment Method:" 12 60 2 \ + "dhcp" "Automatic (DHCP, recommended)" \ + "static" "Static (manual entry)" \ + 3>&1 1>&2 2>&3) + + exit_status=$? + if [ $exit_status -ne 0 ]; then + exit_script + fi + + case "$IPV4_METHOD" in + dhcp) + NET="dhcp" + GATE="" + echo -e "${NETWORK}${BOLD}${DGN}IPv4: DHCP${CL}" + break + ;; + static) + # Static: call and validate CIDR address + while true; do + NET=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Enter Static IPv4 CIDR Address (e.g. 192.168.100.50/24)" 8 58 "" \ + --title "IPv4 ADDRESS" 3>&1 1>&2 2>&3) + if [ -z "$NET" ]; then + whiptail --msgbox "IPv4 address must not be empty." 8 58 + continue + elif [[ "$NET" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then + echo -e "${NETWORK}${BOLD}${DGN}IPv4 Address: ${BGN}$NET${CL}" + break + else + whiptail --msgbox "$NET is not a valid IPv4 CIDR address. Please enter a correct value!" 8 58 + fi + done + + # call and validate Gateway + while true; do + GATE1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Enter Gateway IP address for static IPv4" 8 58 "" \ + --title "Gateway IP" 3>&1 1>&2 2>&3) + if [ -z "$GATE1" ]; then + whiptail --msgbox "Gateway IP address cannot be empty." 8 58 + elif [[ ! "$GATE1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then + whiptail --msgbox "Invalid Gateway IP address format." 8 58 + else + GATE=",gw=$GATE1" + echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE1${CL}" + break + fi + done + break + ;; + esac + done + + # IPv6 Address Management selection + while true; do + IPV6_METHOD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --menu \ + "Select IPv6 Address Management Type:" 15 58 4 \ + "auto" "SLAAC/AUTO (recommended, default)" \ + "dhcp" "DHCPv6" \ + "static" "Static (manual entry)" \ + "none" "Disabled" \ + --default-item "auto" 3>&1 1>&2 2>&3) + [ $? -ne 0 ] && exit_script + + case "$IPV6_METHOD" in + auto) + echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}SLAAC/AUTO${CL}" + IPV6_ADDR="" + IPV6_GATE="" + break + ;; + dhcp) + echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}DHCPv6${CL}" + IPV6_ADDR="dhcp" + IPV6_GATE="" + break + ;; + static) + # Ask for static IPv6 address (CIDR notation, e.g., 2001:db8::1234/64) + while true; do + IPV6_ADDR=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox \ + "Set a static IPv6 CIDR address (e.g., 2001:db8::1234/64)" 8 58 "" \ + --title "IPv6 STATIC ADDRESS" 3>&1 1>&2 2>&3) || exit_script + if [[ "$IPV6_ADDR" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+(/[0-9]{1,3})$ ]]; then + echo -e "${NETWORK}${BOLD}${DGN}IPv6 Address: ${BGN}$IPV6_ADDR${CL}" + break + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox \ + "$IPV6_ADDR is an invalid IPv6 CIDR address. Please enter a valid IPv6 CIDR address (e.g., 2001:db8::1234/64)" 8 58 + fi + done + # Optional: ask for IPv6 gateway for static config + while true; do + IPV6_GATE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox \ + "Enter IPv6 gateway address (optional, leave blank for none)" 8 58 "" --title "IPv6 GATEWAY" 3>&1 1>&2 2>&3) + if [ -z "$IPV6_GATE" ]; then + IPV6_GATE="" + break + elif [[ "$IPV6_GATE" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+$ ]]; then + break + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox \ + "Invalid IPv6 gateway format." 8 58 + fi + done + break + ;; + none) + echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}Disabled${CL}" + IPV6_ADDR="none" + IPV6_GATE="" + break + ;; + *) + exit_script + ;; + esac + done + + if [ "$var_os" == "alpine" ]; then + APT_CACHER="" + APT_CACHER_IP="" + else + if APT_CACHER_IP=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set APT-Cacher IP (leave blank for none)" 8 58 --title "APT-Cacher IP" 3>&1 1>&2 2>&3); then + APT_CACHER="${APT_CACHER_IP:+yes}" + echo -e "${NETWORK}${BOLD}${DGN}APT-Cacher IP Address: ${BGN}${APT_CACHER_IP:-Default}${CL}" + else + exit_script + fi + fi + + # if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "IPv6" --yesno "Disable IPv6?" 10 58); then + # DISABLEIP6="yes" + # else + # DISABLEIP6="no" + # fi + # echo -e "${DISABLEIPV6}${BOLD}${DGN}Disable IPv6: ${BGN}$DISABLEIP6${CL}" + + if MTU1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default [The MTU of your selected vmbr, default is 1500])" 8 58 --title "MTU SIZE" 3>&1 1>&2 2>&3); then + if [ -z "$MTU1" ]; then + MTU1="Default" + MTU="" + else + MTU=",mtu=$MTU1" + fi + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" + else + exit_script + fi + + if SD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a DNS Search Domain (leave blank for HOST)" 8 58 --title "DNS Search Domain" 3>&1 1>&2 2>&3); then + if [ -z "$SD" ]; then + SX=Host + SD="" + else + SX=$SD + SD="-searchdomain=$SD" + fi + echo -e "${SEARCH}${BOLD}${DGN}DNS Search Domain: ${BGN}$SX${CL}" + else + exit_script + fi + + if NX=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a DNS Server IP (leave blank for HOST)" 8 58 --title "DNS SERVER IP" 3>&1 1>&2 2>&3); then + if [ -z "$NX" ]; then + NX=Host + NS="" + else + NS="-nameserver=$NX" + fi + echo -e "${NETWORK}${BOLD}${DGN}DNS Server IP Address: ${BGN}$NX${CL}" + else + exit_script + fi + + if [ "$var_os" == "alpine" ] && [ "$NET" == "dhcp" ] && [ "$NX" != "Host" ]; then + UDHCPC_FIX="yes" + else + UDHCPC_FIX="no" + fi + export UDHCPC_FIX + + if MAC1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a MAC Address(leave blank for generated MAC)" 8 58 --title "MAC ADDRESS" 3>&1 1>&2 2>&3); then + if [ -z "$MAC1" ]; then + MAC1="Default" + MAC="" + else + MAC=",hwaddr=$MAC1" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" + fi + else + exit_script + fi + + if VLAN1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for no VLAN)" 8 58 --title "VLAN" 3>&1 1>&2 2>&3); then + if [ -z "$VLAN1" ]; then + VLAN1="Default" + VLAN="" + else + VLAN=",tag=$VLAN1" + fi + echo -e "${VLANTAG}${BOLD}${DGN}Vlan: ${BGN}$VLAN1${CL}" + else + exit_script + fi + + if ADV_TAGS=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Custom Tags?[If you remove all, there will be no tags!]" 8 58 "${TAGS}" --title "Advanced Tags" 3>&1 1>&2 2>&3); then + if [ -n "${ADV_TAGS}" ]; then + ADV_TAGS=$(echo "$ADV_TAGS" | tr -d '[:space:]') + TAGS="${ADV_TAGS}" + else + TAGS=";" + fi + echo -e "${NETWORK}${BOLD}${DGN}Tags: ${BGN}$TAGS${CL}" + else + exit_script + fi + + configure_ssh_settings + export SSH_KEYS_FILE + echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}" + if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "FUSE Support" --yesno "Enable FUSE support?\nRequired for tools like rclone, mergerfs, AppImage, etc." 10 58); then + ENABLE_FUSE="yes" + else + ENABLE_FUSE="no" + fi + echo -e "${FUSE}${BOLD}${DGN}Enable FUSE Support: ${BGN}$ENABLE_FUSE${CL}" + + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then + VERBOSE="yes" + else + VERBOSE="no" + fi + echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}" + + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create ${APP} LXC?" 10 58); then + echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}" + else + clear + header_info + echo -e "${TAB}${INFO} ProxmoxVE Version ${PVEVERSION} | Kernel: ${KERNEL_VERSION}${CL}" + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings on node $PVEHOST_NAME${CL}" + advanced_settings + fi +} + +# ------------------------------------------------------------------------------ +# diagnostics_check() +# +# - Ensures diagnostics config file exists at /usr/local/community-scripts/diagnostics +# - Asks user whether to send anonymous diagnostic data +# - Saves DIAGNOSTICS=yes/no in the config file +# ------------------------------------------------------------------------------ +diagnostics_check() { + if ! [ -d "/usr/local/community-scripts" ]; then + mkdir -p /usr/local/community-scripts + fi + + if ! [ -f "/usr/local/community-scripts/diagnostics" ]; then + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS" --yesno "Send Diagnostics of LXC Installation?\n\n(This only transmits data without user data, just RAM, CPU, LXC name, ...)" 10 58); then + cat </usr/local/community-scripts/diagnostics +DIAGNOSTICS=yes + +#This file is used to store the diagnostics settings for the Community-Scripts API. +#https://github.com/community-scripts/ProxmoxVED/discussions/1836 +#Your diagnostics will be sent to the Community-Scripts API for troubleshooting/statistical purposes. +#You can review the data at https://community-scripts.github.io/ProxmoxVE/data +#If you do not wish to send diagnostics, please set the variable 'DIAGNOSTICS' to "no" in /usr/local/community-scripts/diagnostics, or use the menue. +#This will disable the diagnostics feature. +#To send diagnostics, set the variable 'DIAGNOSTICS' to "yes" in /usr/local/community-scripts/diagnostics, or use the menue. +#This will enable the diagnostics feature. +#The following information will be sent: +#"disk_size" +#"core_count" +#"ram_size" +#"os_type" +#"os_version" +#"nsapp" +#"method" +#"pve_version" +#"status" +#If you have any concerns, please review the source code at /misc/build.func +EOF + DIAGNOSTICS="yes" + else + cat </usr/local/community-scripts/diagnostics +DIAGNOSTICS=no + +#This file is used to store the diagnostics settings for the Community-Scripts API. +#https://github.com/community-scripts/ProxmoxVED/discussions/1836 +#Your diagnostics will be sent to the Community-Scripts API for troubleshooting/statistical purposes. +#You can review the data at https://community-scripts.github.io/ProxmoxVE/data +#If you do not wish to send diagnostics, please set the variable 'DIAGNOSTICS' to "no" in /usr/local/community-scripts/diagnostics, or use the menue. +#This will disable the diagnostics feature. +#To send diagnostics, set the variable 'DIAGNOSTICS' to "yes" in /usr/local/community-scripts/diagnostics, or use the menue. +#This will enable the diagnostics feature. +#The following information will be sent: +#"disk_size" +#"core_count" +#"ram_size" +#"os_type" +#"os_version" +#"nsapp" +#"method" +#"pve_version" +#"status" +#If you have any concerns, please review the source code at /misc/build.func +EOF + DIAGNOSTICS="no" + fi + else + DIAGNOSTICS=$(awk -F '=' '/^DIAGNOSTICS/ {print $2}' /usr/local/community-scripts/diagnostics) + + fi + +} + +# ------------------------------------------------------------------------------ +# default_var_settings +# +# - Ensures /usr/local/community-scripts/default.vars exists (creates if missing) +# - Loads var_* values from default.vars (safe parser, no source/eval) +# - Precedence: ENV var_* > default.vars > built-in defaults +# - Maps var_verbose → VERBOSE +# - Calls base_settings "$VERBOSE" and echo_default +# ------------------------------------------------------------------------------ +default_var_settings() { + # Allowed var_* keys (alphabetically sorted) + local VAR_WHITELIST=( + var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_ctid var_disk var_fuse + var_gateway var_hostname var_ipv6_method var_ipv6_static var_mac var_mtu + var_net var_ns var_pw var_ram var_tags var_tun var_unprivileged + var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage + ) + + # Snapshot: environment variables (highest precedence) + declare -A _HARD_ENV=() + local _k + for _k in "${VAR_WHITELIST[@]}"; do + if printenv "$_k" >/dev/null 2>&1; then _HARD_ENV["$_k"]=1; fi + done + + # Find default.vars location + local _find_default_vars + _find_default_vars() { + local f + for f in \ + /usr/local/community-scripts/default.vars \ + "$HOME/.config/community-scripts/default.vars" \ + "./default.vars"; do + [ -f "$f" ] && { + echo "$f" + return 0 + } + done + return 1 + } + # Allow override of storages via env (for non-interactive use cases) + [ -n "${var_template_storage:-}" ] && TEMPLATE_STORAGE="$var_template_storage" + [ -n "${var_container_storage:-}" ] && CONTAINER_STORAGE="$var_container_storage" + + # Create once, with storages already selected, no var_ctid/var_hostname lines + local _ensure_default_vars + _ensure_default_vars() { + _find_default_vars >/dev/null 2>&1 && return 0 + + local canonical="/usr/local/community-scripts/default.vars" + msg_info "No default.vars found. Creating ${canonical}" + mkdir -p /usr/local/community-scripts + + # Pick storages before writing the file (always ask unless only one) + # Create a minimal temp file to write into + : >"$canonical" + + # Base content (no var_ctid / var_hostname here) + cat >"$canonical" <<'EOF' +# Community-Scripts defaults (var_* only). Lines starting with # are comments. +# Precedence: ENV var_* > default.vars > built-ins. +# Keep keys alphabetically sorted. + +# Container type +var_unprivileged=1 + +# Resources +var_cpu=1 +var_disk=4 +var_ram=1024 + +# Network +var_brg=vmbr0 +var_net=dhcp +var_ipv6_method=none +# var_gateway= +# var_ipv6_static= +# var_vlan= +# var_mtu= +# var_mac= +# var_ns= + +# SSH +var_ssh=no +# var_ssh_authorized_key= + +# APT cacher (optional) +# var_apt_cacher=yes +# var_apt_cacher_ip=192.168.1.10 + +# Features/Tags/verbosity +var_fuse=no +var_tun=no +var_tags=community-script +var_verbose=no + +# Security (root PW) – empty => autologin +# var_pw= +EOF + + # Now choose storages (always prompt unless just one exists) + choose_and_set_storage_for_file "$canonical" template + choose_and_set_storage_for_file "$canonical" container + + chmod 0644 "$canonical" + msg_ok "Created ${canonical}" + } + + # Whitelist check + local _is_whitelisted_key + _is_whitelisted_key() { + local k="$1" + local w + for w in "${VAR_WHITELIST[@]}"; do [ "$k" = "$w" ] && return 0; done + return 1 + } + + # Safe parser for KEY=VALUE lines + local _load_vars_file + _load_vars_file() { + local file="$1" + [ -f "$file" ] || return 0 + msg_info "Loading defaults from ${file}" + local line key val + while IFS= read -r line || [ -n "$line" ]; do + line="${line#"${line%%[![:space:]]*}"}" + line="${line%"${line##*[![:space:]]}"}" + [[ -z "$line" || "$line" == \#* ]] && continue + if [[ "$line" =~ ^([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then + local var_key="${BASH_REMATCH[1]}" + local var_val="${BASH_REMATCH[2]}" + + [[ "$var_key" != var_* ]] && continue + _is_whitelisted_key "$var_key" || { + msg_debug "Ignore non-whitelisted ${var_key}" + continue + } + + # Strip quotes + if [[ "$var_val" =~ ^\"(.*)\"$ ]]; then + var_val="${BASH_REMATCH[1]}" + elif [[ "$var_val" =~ ^\'(.*)\'$ ]]; then + var_val="${BASH_REMATCH[1]}" + fi + + # Unsafe characters + case $var_val in + \"*\") + var_val=${var_val#\"} + var_val=${var_val%\"} + ;; + \'*\') + var_val=${var_val#\'} + var_val=${var_val%\'} + ;; + esac # Hard env wins + [[ -n "${_HARD_ENV[$var_key]:-}" ]] && continue + # Set only if not already exported + [[ -z "${!var_key+x}" ]] && export "${var_key}=${var_val}" + else + msg_warn "Malformed line in ${file}: ${line}" + fi + done <"$file" + msg_ok "Loaded ${file}" + } + + # 1) Ensure file exists + _ensure_default_vars + + # 2) Load file + local dv + dv="$(_find_default_vars)" || { + msg_error "default.vars not found after ensure step" + return 1 + } + _load_vars_file "$dv" + + # 3) Map var_verbose → VERBOSE + if [[ -n "${var_verbose:-}" ]]; then + case "${var_verbose,,}" in 1 | yes | true | on) VERBOSE="yes" ;; 0 | no | false | off) VERBOSE="no" ;; *) VERBOSE="${var_verbose}" ;; esac + else + VERBOSE="no" + fi + + # 4) Apply base settings and show summary + METHOD="mydefaults-global" + base_settings "$VERBOSE" + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using My Defaults (default.vars) on node $PVEHOST_NAME${CL}" + echo_default +} + +# ------------------------------------------------------------------------------ +# get_app_defaults_path() +# +# - Returns full path for app-specific defaults file +# - Example: /usr/local/community-scripts/defaults/.vars +# ------------------------------------------------------------------------------ + +get_app_defaults_path() { + local n="${NSAPP:-${APP,,}}" + echo "/usr/local/community-scripts/defaults/${n}.vars" +} + +# ------------------------------------------------------------------------------ +# maybe_offer_save_app_defaults +# +# - Called after advanced_settings returned with fully chosen values. +# - If no .vars exists, offers to persist current advanced settings +# into /usr/local/community-scripts/defaults/.vars +# - Only writes whitelisted var_* keys. +# - Extracts raw values from flags like ",gw=..." ",mtu=..." etc. +# ------------------------------------------------------------------------------ +if ! declare -p VAR_WHITELIST >/dev/null 2>&1; then + declare -ag VAR_WHITELIST=( + var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_ctid var_disk var_fuse + var_gateway var_hostname var_ipv6_method var_ipv6_static var_mac var_mtu + var_net var_ns var_pw var_ram var_tags var_tun var_unprivileged + var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage + ) +fi + +_is_whitelisted_key() { + local k="$1" + local w + for w in "${VAR_WHITELIST[@]}"; do [[ "$k" == "$w" ]] && return 0; done + return 1 +} + +_sanitize_value() { + # Disallow Command-Substitution / Shell-Meta + case "$1" in + *'$('* | *'`'* | *';'* | *'&'* | *'<('*) + echo "" + return 0 + ;; + esac + echo "$1" +} + +# Map-Parser: read var_* from file into _VARS_IN associative array +declare -A _VARS_IN +_load_vars_file() { + local file="$1" + [ -f "$file" ] || return 0 + msg_info "Loading defaults from ${file}" + local line key val + while IFS= read -r line || [ -n "$line" ]; do + line="${line#"${line%%[![:space:]]*}"}" + line="${line%"${line##*[![:space:]]}"}" + [ -z "$line" ] && continue + case "$line" in + \#*) continue ;; + esac + key=$(printf "%s" "$line" | cut -d= -f1) + val=$(printf "%s" "$line" | cut -d= -f2-) + case "$key" in + var_*) + if _is_whitelisted_key "$key"; then + [ -z "${!key+x}" ] && export "$key=$val" + fi + ;; + esac + done <"$file" + msg_ok "Loaded ${file}" +} + +# Diff function for two var_* files -> produces human-readable diff list for $1 (old) vs $2 (new) +_build_vars_diff() { + local oldf="$1" newf="$2" + local k + local -A OLD=() NEW=() + _load_vars_file_to_map "$oldf" + for k in "${!_VARS_IN[@]}"; do OLD["$k"]="${_VARS_IN[$k]}"; done + _load_vars_file_to_map "$newf" + for k in "${!_VARS_IN[@]}"; do NEW["$k"]="${_VARS_IN[$k]}"; done + + local out + out+="# Diff for ${APP} (${NSAPP})\n" + out+="# Old: ${oldf}\n# New: ${newf}\n\n" + + local found_change=0 + + # Changed & Removed + for k in "${!OLD[@]}"; do + if [[ -v NEW["$k"] ]]; then + if [[ "${OLD[$k]}" != "${NEW[$k]}" ]]; then + out+="~ ${k}\n - old: ${OLD[$k]}\n + new: ${NEW[$k]}\n" + found_change=1 + fi + else + out+="- ${k}\n - old: ${OLD[$k]}\n" + found_change=1 + fi + done + + # Added + for k in "${!NEW[@]}"; do + if [[ ! -v OLD["$k"] ]]; then + out+="+ ${k}\n + new: ${NEW[$k]}\n" + found_change=1 + fi + done + + if [[ $found_change -eq 0 ]]; then + out+="(No differences)\n" + fi + + printf "%b" "$out" +} + +# Build a temporary .vars file from current advanced settings +_build_current_app_vars_tmp() { + tmpf="$(mktemp /tmp/${NSAPP:-app}.vars.new.XXXXXX)" + + # NET/GW + _net="${NET:-}" + _gate="" + case "${GATE:-}" in + ,gw=*) _gate=$(echo "$GATE" | sed 's/^,gw=//') ;; + esac + + # IPv6 + _ipv6_method="${IPV6_METHOD:-auto}" + _ipv6_static="" + _ipv6_gateway="" + if [ "$_ipv6_method" = "static" ]; then + _ipv6_static="${IPV6_ADDR:-}" + _ipv6_gateway="${IPV6_GATE:-}" + fi + + # MTU/VLAN/MAC + _mtu="" + _vlan="" + _mac="" + case "${MTU:-}" in + ,mtu=*) _mtu=$(echo "$MTU" | sed 's/^,mtu=//') ;; + esac + case "${VLAN:-}" in + ,tag=*) _vlan=$(echo "$VLAN" | sed 's/^,tag=//') ;; + esac + case "${MAC:-}" in + ,hwaddr=*) _mac=$(echo "$MAC" | sed 's/^,hwaddr=//') ;; + esac + + # DNS / Searchdomain + _ns="" + _searchdomain="" + case "${NS:-}" in + -nameserver=*) _ns=$(echo "$NS" | sed 's/^-nameserver=//') ;; + esac + case "${SD:-}" in + -searchdomain=*) _searchdomain=$(echo "$SD" | sed 's/^-searchdomain=//') ;; + esac + + # SSH / APT / Features + _ssh="${SSH:-no}" + _ssh_auth="${SSH_AUTHORIZED_KEY:-}" + _apt_cacher="${APT_CACHER:-}" + _apt_cacher_ip="${APT_CACHER_IP:-}" + _fuse="${ENABLE_FUSE:-no}" + _tun="${ENABLE_TUN:-no}" + _tags="${TAGS:-}" + _verbose="${VERBOSE:-no}" + + # Type / Resources / Identity + _unpriv="${CT_TYPE:-1}" + _cpu="${CORE_COUNT:-1}" + _ram="${RAM_SIZE:-1024}" + _disk="${DISK_SIZE:-4}" + _hostname="${HN:-$NSAPP}" + + # Storage + _tpl_storage="${TEMPLATE_STORAGE:-${var_template_storage:-}}" + _ct_storage="${CONTAINER_STORAGE:-${var_container_storage:-}}" + + { + echo "# App-specific defaults for ${APP} (${NSAPP})" + echo "# Generated on $(date -u '+%Y-%m-%dT%H:%M:%SZ')" + echo + + echo "var_unprivileged=$(_sanitize_value "$_unpriv")" + echo "var_cpu=$(_sanitize_value "$_cpu")" + echo "var_ram=$(_sanitize_value "$_ram")" + echo "var_disk=$(_sanitize_value "$_disk")" + + [ -n "${BRG:-}" ] && echo "var_brg=$(_sanitize_value "$BRG")" + [ -n "$_net" ] && echo "var_net=$(_sanitize_value "$_net")" + [ -n "$_gate" ] && echo "var_gateway=$(_sanitize_value "$_gate")" + [ -n "$_mtu" ] && echo "var_mtu=$(_sanitize_value "$_mtu")" + [ -n "$_vlan" ] && echo "var_vlan=$(_sanitize_value "$_vlan")" + [ -n "$_mac" ] && echo "var_mac=$(_sanitize_value "$_mac")" + [ -n "$_ns" ] && echo "var_ns=$(_sanitize_value "$_ns")" + + [ -n "$_ipv6_method" ] && echo "var_ipv6_method=$(_sanitize_value "$_ipv6_method")" + [ -n "$_ipv6_static" ] && echo "var_ipv6_static=$(_sanitize_value "$_ipv6_static")" + + [ -n "$_ssh" ] && echo "var_ssh=$(_sanitize_value "$_ssh")" + [ -n "$_ssh_auth" ] && echo "var_ssh_authorized_key=$(_sanitize_value "$_ssh_auth")" + + [ -n "$_apt_cacher" ] && echo "var_apt_cacher=$(_sanitize_value "$_apt_cacher")" + [ -n "$_apt_cacher_ip" ] && echo "var_apt_cacher_ip=$(_sanitize_value "$_apt_cacher_ip")" + + [ -n "$_fuse" ] && echo "var_fuse=$(_sanitize_value "$_fuse")" + [ -n "$_tun" ] && echo "var_tun=$(_sanitize_value "$_tun")" + [ -n "$_tags" ] && echo "var_tags=$(_sanitize_value "$_tags")" + [ -n "$_verbose" ] && echo "var_verbose=$(_sanitize_value "$_verbose")" + + [ -n "$_hostname" ] && echo "var_hostname=$(_sanitize_value "$_hostname")" + [ -n "$_searchdomain" ] && echo "var_searchdomain=$(_sanitize_value "$_searchdomain")" + + [ -n "$_tpl_storage" ] && echo "var_template_storage=$(_sanitize_value "$_tpl_storage")" + [ -n "$_ct_storage" ] && echo "var_container_storage=$(_sanitize_value "$_ct_storage")" + } >"$tmpf" + + echo "$tmpf" +} + +# ------------------------------------------------------------------------------ +# maybe_offer_save_app_defaults() +# +# - Called after advanced_settings() +# - Offers to save current values as app defaults if not existing +# - If file exists: shows diff and allows Update, Keep, View Diff, or Cancel +# ------------------------------------------------------------------------------ +maybe_offer_save_app_defaults() { + local app_vars_path + app_vars_path="$(get_app_defaults_path)" + + # always build from current settings + local new_tmp diff_tmp + new_tmp="$(_build_current_app_vars_tmp)" + diff_tmp="$(mktemp -p /tmp "${NSAPP:-app}.vars.diff.XXXXXX")" + + # 1) if no file → offer to create + if [[ ! -f "$app_vars_path" ]]; then + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --yesno "Save these advanced settings as defaults for ${APP}?\n\nThis will create:\n${app_vars_path}" 12 72; then + mkdir -p "$(dirname "$app_vars_path")" + install -m 0644 "$new_tmp" "$app_vars_path" + msg_ok "Saved app defaults: ${app_vars_path}" + fi + rm -f "$new_tmp" "$diff_tmp" + return 0 + fi + + # 2) if file exists → build diff + _build_vars_diff "$app_vars_path" "$new_tmp" >"$diff_tmp" + + # if no differences → do nothing + if grep -q "^(No differences)$" "$diff_tmp"; then + rm -f "$new_tmp" "$diff_tmp" + return 0 + fi + + # 3) if file exists → show menu with default selection "Update Defaults" + local app_vars_file + app_vars_file="$(basename "$app_vars_path")" + + while true; do + local sel + sel="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "APP DEFAULTS – ${APP}" \ + --menu "Differences detected. What do you want to do?" 20 78 10 \ + "Update Defaults" "Write new values to ${app_vars_file}" \ + "Keep Current" "Keep existing defaults (no changes)" \ + "View Diff" "Show a detailed diff" \ + "Cancel" "Abort without changes" \ + --default-item "Update Defaults" \ + 3>&1 1>&2 2>&3)" || { sel="Cancel"; } + + case "$sel" in + "Update Defaults") + install -m 0644 "$new_tmp" "$app_vars_path" + msg_ok "Updated app defaults: ${app_vars_path}" + break + ;; + "Keep Current") + msg_info "Keeping current app defaults: ${app_vars_path}" + break + ;; + "View Diff") + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "Diff – ${APP}" \ + --scrolltext --textbox "$diff_tmp" 25 100 + ;; + "Cancel" | *) + msg_info "Canceled. No changes to app defaults." + break + ;; + esac + done + + rm -f "$new_tmp" "$diff_tmp" +} + +ensure_storage_selection_for_vars_file() { + local vf="$1" + + # Read stored values (if any) + local tpl ct + tpl=$(grep -E '^var_template_storage=' "$vf" | cut -d= -f2-) + ct=$(grep -E '^var_container_storage=' "$vf" | cut -d= -f2-) + + if [[ -n "$tpl" && -n "$ct" ]]; then + TEMPLATE_STORAGE="$tpl" + CONTAINER_STORAGE="$ct" + return 0 + fi + + choose_and_set_storage_for_file "$vf" template + choose_and_set_storage_for_file "$vf" container + + msg_ok "Storage configuration saved to $(basename "$vf")" +} + +diagnostics_menu() { + if [ "${DIAGNOSTICS:-no}" = "yes" ]; then + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "DIAGNOSTIC SETTINGS" \ + --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ + --yes-button "No" --no-button "Back"; then + DIAGNOSTICS="no" + sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=no/' /usr/local/community-scripts/diagnostics + whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 + fi + else + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "DIAGNOSTIC SETTINGS" \ + --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ + --yes-button "Yes" --no-button "Back"; then + DIAGNOSTICS="yes" + sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=yes/' /usr/local/community-scripts/diagnostics + whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 + fi + fi +} + +ensure_global_default_vars_file() { + local vars_path="/usr/local/community-scripts/default.vars" + if [[ ! -f "$vars_path" ]]; then + mkdir -p "$(dirname "$vars_path")" + touch "$vars_path" + fi + echo "$vars_path" +} + +# ------------------------------------------------------------------------------ +# install_script() +# +# - Main entrypoint for installation mode +# - Runs safety checks (pve_check, root_check, maxkeys_check, diagnostics_check) +# - Builds interactive menu (Default, Verbose, Advanced, My Defaults, App Defaults, Diagnostics, Storage, Exit) +# - Applies chosen settings and triggers container build +# ------------------------------------------------------------------------------ +install_script() { + pve_check + shell_check + root_check + arch_check + ssh_check + maxkeys_check + diagnostics_check + + if systemctl is-active -q ping-instances.service; then + systemctl -q stop ping-instances.service + fi + + NEXTID=$(pvesh get /cluster/nextid) + timezone=$(cat /etc/timezone) + + # Show APP Header + header_info + + # --- Support CLI argument as direct preset (default, advanced, …) --- + CHOICE="${mode:-${1:-}}" + + # If no CLI argument → show whiptail menu + # Build menu dynamically based on available options + local appdefaults_option="" + local settings_option="" + local menu_items=( + "1" "Default Install" + "2" "Advanced Install" + "3" "My Defaults" + ) + + if [ -f "$(get_app_defaults_path)" ]; then + appdefaults_option="4" + menu_items+=("4" "App Defaults for ${APP}") + settings_option="5" + menu_items+=("5" "Settings") + else + settings_option="4" + menu_items+=("4" "Settings") + fi + + if [ -z "$CHOICE" ]; then + + TMP_CHOICE=$(whiptail \ + --backtitle "Proxmox VE Helper Scripts" \ + --title "Community-Scripts Options" \ + --ok-button "Select" --cancel-button "Exit Script" \ + --notags \ + --menu "\nChoose an option:\n Use TAB or Arrow keys to navigate, ENTER to select.\n" \ + 20 60 9 \ + "${menu_items[@]}" \ + --default-item "1" \ + 3>&1 1>&2 2>&3) || exit_script + CHOICE="$TMP_CHOICE" + fi + + APPDEFAULTS_OPTION="$appdefaults_option" + SETTINGS_OPTION="$settings_option" + + # --- Main case --- + local defaults_target="" + local run_maybe_offer="no" + case "$CHOICE" in + 1 | default | DEFAULT) + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME${CL}" + VERBOSE="no" + METHOD="default" + base_settings "$VERBOSE" + echo_default + defaults_target="$(ensure_global_default_vars_file)" + ;; + 2 | advanced | ADVANCED) + header_info + echo -e "${TAB}${INFO} ProxmoxVE Version ${PVEVERSION} | Kernel: ${KERNEL_VERSION}${CL}" + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Install on node $PVEHOST_NAME${CL}" + METHOD="advanced" + base_settings + advanced_settings + defaults_target="$(ensure_global_default_vars_file)" + run_maybe_offer="yes" + ;; + 3 | mydefaults | MYDEFAULTS) + default_var_settings || { + msg_error "Failed to apply default.vars" + exit 1 + } + defaults_target="/usr/local/community-scripts/default.vars" + ;; + "$APPDEFAULTS_OPTION" | appdefaults | APPDEFAULTS) + if [ -f "$(get_app_defaults_path)" ]; then + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using App Defaults for ${APP} on node $PVEHOST_NAME${CL}" + METHOD="appdefaults" + base_settings + _load_vars_file "$(get_app_defaults_path)" + echo_default + defaults_target="$(get_app_defaults_path)" + else + msg_error "No App Defaults available for ${APP}" + exit 1 + fi + ;; + "$SETTINGS_OPTION" | settings | SETTINGS) + settings_menu + defaults_target="" + ;; + *) + echo -e "${CROSS}${RD}Invalid option: $CHOICE${CL}" + exit 1 + ;; + esac + + if [[ -n "$defaults_target" ]]; then + ensure_storage_selection_for_vars_file "$defaults_target" + fi + + if [[ "$run_maybe_offer" == "yes" ]]; then + maybe_offer_save_app_defaults + fi +} + +edit_default_storage() { + local vf="/usr/local/community-scripts/default.vars" + + # Ensure file exists + if [[ ! -f "$vf" ]]; then + mkdir -p "$(dirname "$vf")" + touch "$vf" + fi + + # Let ensure_storage_selection_for_vars_file handle everything + ensure_storage_selection_for_vars_file "$vf" +} + +settings_menu() { + while true; do + local settings_items=( + "1" "Manage API-Diagnostic Setting" + "2" "Edit Default.vars" + "3" "Edit Default Storage" + ) + if [ -f "$(get_app_defaults_path)" ]; then + settings_items+=("4" "Edit App.vars for ${APP}") + settings_items+=("5" "Exit") + else + settings_items+=("4" "Exit") + fi + + local choice + choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "Community-Scripts SETTINGS Menu" \ + --ok-button "OK" --cancel-button "Back" \ + --menu "\n\nChoose a settings option:\n\nUse TAB or Arrow keys to navigate, ENTER to select." 20 60 9 \ + "${settings_items[@]}" \ + 3>&1 1>&2 2>&3) || break + + case "$choice" in + 1) diagnostics_menu ;; + 2) ${EDITOR:-nano} /usr/local/community-scripts/default.vars ;; + 3) edit_default_storage ;; + 4) + if [ -f "$(get_app_defaults_path)" ]; then + ${EDITOR:-nano} "$(get_app_defaults_path)" + else + exit_script + fi + ;; + 5) exit_script ;; + esac + done +} + +# ===== Unified storage selection & writing to vars files ===== +_write_storage_to_vars() { + # $1 = vars_file, $2 = key (var_container_storage / var_template_storage), $3 = value + local vf="$1" key="$2" val="$3" + # remove uncommented and commented versions to avoid duplicates + sed -i "/^[#[:space:]]*${key}=/d" "$vf" + echo "${key}=${val}" >>"$vf" +} + +choose_and_set_storage_for_file() { + # $1 = vars_file, $2 = class ('container'|'template') + local vf="$1" class="$2" key="" current="" + case "$class" in + container) key="var_container_storage" ;; + template) key="var_template_storage" ;; + *) + msg_error "Unknown storage class: $class" + return 1 + ;; + esac + + current=$(awk -F= -v k="^${key}=" '$0 ~ k {print $2; exit}' "$vf") + + # If only one storage exists for the content type, auto-pick. Else always ask (your wish #4). + local content="rootdir" + [[ "$class" == "template" ]] && content="vztmpl" + local count + count=$(pvesm status -content "$content" | awk 'NR>1{print $1}' | wc -l) + + if [[ "$count" -eq 1 ]]; then + STORAGE_RESULT=$(pvesm status -content "$content" | awk 'NR>1{print $1; exit}') + STORAGE_INFO="" + else + # If the current value is preselectable, we could show it, but per your requirement we always offer selection + select_storage "$class" || return 1 + fi + + _write_storage_to_vars "$vf" "$key" "$STORAGE_RESULT" + + # Keep environment in sync for later steps (e.g. app-default save) + if [[ "$class" == "container" ]]; then + export var_container_storage="$STORAGE_RESULT" + export CONTAINER_STORAGE="$STORAGE_RESULT" + else + export var_template_storage="$STORAGE_RESULT" + export TEMPLATE_STORAGE="$STORAGE_RESULT" + fi + + msg_ok "Updated ${key} → ${STORAGE_RESULT}" +} + +# ------------------------------------------------------------------------------ +# check_container_resources() +# +# - Compares host RAM/CPU with required values +# - Warns if under-provisioned and asks user to continue or abort +# ------------------------------------------------------------------------------ +check_container_resources() { + current_ram=$(free -m | awk 'NR==2{print $2}') + current_cpu=$(nproc) + + if [[ "$current_ram" -lt "$var_ram" ]] || [[ "$current_cpu" -lt "$var_cpu" ]]; then + echo -e "\n${INFO}${HOLD} ${GN}Required: ${var_cpu} CPU, ${var_ram}MB RAM ${CL}| ${RD}Current: ${current_cpu} CPU, ${current_ram}MB RAM${CL}" + echo -e "${YWB}Please ensure that the ${APP} LXC is configured with at least ${var_cpu} vCPU and ${var_ram} MB RAM for the build process.${CL}\n" + echo -ne "${INFO}${HOLD} May cause data loss! ${INFO} Continue update with under-provisioned LXC? " + read -r prompt + if [[ ! ${prompt,,} =~ ^(yes)$ ]]; then + echo -e "${CROSS}${HOLD} ${YWB}Exiting based on user input.${CL}" + exit 1 + fi + else + echo -e "" + fi +} + +# ------------------------------------------------------------------------------ +# check_container_storage() +# +# - Checks /boot partition usage +# - Warns if usage >80% and asks user confirmation before proceeding +# ------------------------------------------------------------------------------ +check_container_storage() { + total_size=$(df /boot --output=size | tail -n 1) + local used_size=$(df /boot --output=used | tail -n 1) + usage=$((100 * used_size / total_size)) + if ((usage > 80)); then + echo -e "${INFO}${HOLD} ${YWB}Warning: Storage is dangerously low (${usage}%).${CL}" + echo -ne "Continue anyway? " + read -r prompt + if [[ ! ${prompt,,} =~ ^(y|yes)$ ]]; then + echo -e "${CROSS}${HOLD}${YWB}Exiting based on user input.${CL}" + exit 1 + fi + fi +} + +# ------------------------------------------------------------------------------ +# ssh_extract_keys_from_file() +# +# - Extracts valid SSH public keys from given file +# - Supports RSA, Ed25519, ECDSA and filters out comments/invalid lines +# ------------------------------------------------------------------------------ +ssh_extract_keys_from_file() { + local f="$1" + [[ -r "$f" ]] || return 0 + tr -d '\r' <"$f" | awk ' + /^[[:space:]]*#/ {next} + /^[[:space:]]*$/ {next} + # nackt: typ base64 [comment] + /^(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/ {print; next} + # mit Optionen: finde ab erstem Key-Typ + { + match($0, /(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/) + if (RSTART>0) { print substr($0, RSTART) } + } + ' +} + +# ------------------------------------------------------------------------------ +# ssh_build_choices_from_files() +# +# - Builds interactive whiptail checklist of available SSH keys +# - Generates fingerprint, type and comment for each key +# ------------------------------------------------------------------------------ +ssh_build_choices_from_files() { + local -a files=("$@") + CHOICES=() + COUNT=0 + MAPFILE="$(mktemp)" + local id key typ fp cmt base ln=0 + + for f in "${files[@]}"; do + [[ -f "$f" && -r "$f" ]] || continue + base="$(basename -- "$f")" + case "$base" in + known_hosts | known_hosts.* | config) continue ;; + id_*) [[ "$f" != *.pub ]] && continue ;; + esac + + # map every key in file + while IFS= read -r key; do + [[ -n "$key" ]] || continue + + typ="" + fp="" + cmt="" + # Only the pure key part (without options) is already included in ‘key’. + read -r _typ _b64 _cmt <<<"$key" + typ="${_typ:-key}" + cmt="${_cmt:-}" + # Fingerprint via ssh-keygen (if available) + if command -v ssh-keygen >/dev/null 2>&1; then + fp="$(printf '%s\n' "$key" | ssh-keygen -lf - 2>/dev/null | awk '{print $2}')" + fi + # Label shorten + [[ ${#cmt} -gt 40 ]] && cmt="${cmt:0:37}..." + + ln=$((ln + 1)) + COUNT=$((COUNT + 1)) + id="K${COUNT}" + echo "${id}|${key}" >>"$MAPFILE" + CHOICES+=("$id" "[$typ] ${fp:+$fp }${cmt:+$cmt }— ${base}" "OFF") + done < <(ssh_extract_keys_from_file "$f") + done +} + +# ------------------------------------------------------------------------------ +# ssh_discover_default_files() +# +# - Scans standard paths for SSH keys +# - Includes ~/.ssh/*.pub, /etc/ssh/authorized_keys, etc. +# ------------------------------------------------------------------------------ +ssh_discover_default_files() { + local -a cand=() + shopt -s nullglob + cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2) + cand+=(/root/.ssh/*.pub) + cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*) + shopt -u nullglob + printf '%s\0' "${cand[@]}" +} + +configure_ssh_settings() { + SSH_KEYS_FILE="$(mktemp)" + : >"$SSH_KEYS_FILE" + + IFS=$'\0' read -r -d '' -a _def_files < <(ssh_discover_default_files && printf '\0') + ssh_build_choices_from_files "${_def_files[@]}" + local default_key_count="$COUNT" + + local ssh_key_mode + if [[ "$default_key_count" -gt 0 ]]; then + ssh_key_mode=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ + "Provision SSH keys for root:" 14 72 4 \ + "found" "Select from detected keys (${default_key_count})" \ + "manual" "Paste a single public key" \ + "folder" "Scan another folder (path or glob)" \ + "none" "No keys" 3>&1 1>&2 2>&3) || exit_script + else + ssh_key_mode=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ + "No host keys detected; choose manual/none:" 12 72 2 \ + "manual" "Paste a single public key" \ + "none" "No keys" 3>&1 1>&2 2>&3) || exit_script + fi + + case "$ssh_key_mode" in + found) + local selection + selection=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT HOST KEYS" \ + --checklist "Select one or more keys to import:" 20 140 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script + for tag in $selection; do + tag="${tag%\"}" + tag="${tag#\"}" + local line + line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) + [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" + done + ;; + manual) + SSH_AUTHORIZED_KEY="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Paste one SSH public key line (ssh-ed25519/ssh-rsa/...)" 10 72 --title "SSH Public Key" 3>&1 1>&2 2>&3)" + [[ -n "$SSH_AUTHORIZED_KEY" ]] && printf '%s\n' "$SSH_AUTHORIZED_KEY" >>"$SSH_KEYS_FILE" + ;; + folder) + local glob_path + glob_path=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Enter a folder or glob to scan (e.g. /root/.ssh/*.pub)" 10 72 --title "Scan Folder/Glob" 3>&1 1>&2 2>&3) + if [[ -n "$glob_path" ]]; then + shopt -s nullglob + read -r -a _scan_files <<<"$glob_path" + shopt -u nullglob + if [[ "${#_scan_files[@]}" -gt 0 ]]; then + ssh_build_choices_from_files "${_scan_files[@]}" + if [[ "$COUNT" -gt 0 ]]; then + local folder_selection + folder_selection=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT FOLDER KEYS" \ + --checklist "Select key(s) to import:" 20 78 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script + for tag in $folder_selection; do + tag="${tag%\"}" + tag="${tag#\"}" + local line + line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) + [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" + done + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "No keys found in: $glob_path" 8 60 + fi + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "Path/glob returned no files." 8 60 + fi + fi + ;; + none) + : + ;; + esac + + if [[ -s "$SSH_KEYS_FILE" ]]; then + sort -u -o "$SSH_KEYS_FILE" "$SSH_KEYS_FILE" + printf '\n' >>"$SSH_KEYS_FILE" + fi + + if [[ -s "$SSH_KEYS_FILE" || "$PW" == -password* ]]; then + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable root SSH access?" 10 58); then + SSH="yes" + else + SSH="no" + fi + else + SSH="no" + fi +} + +# ------------------------------------------------------------------------------ +# start() +# +# - Entry point of script +# - On Proxmox host: calls install_script +# - In silent mode: runs update_script +# - Otherwise: shows update/setting menu +# ------------------------------------------------------------------------------ +start() { + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) + if command -v pveversion >/dev/null 2>&1; then + install_script || return 0 + return 0 + elif [ ! -z ${PHS_SILENT+x} ] && [[ "${PHS_SILENT}" == "1" ]]; then + VERBOSE="no" + set_std_mode + update_script + else + CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "${APP} LXC Update/Setting" --menu \ + "Support/Update functions for ${APP} LXC. Choose an option:" \ + 12 60 3 \ + "1" "YES (Silent Mode)" \ + "2" "YES (Verbose Mode)" \ + "3" "NO (Cancel Update)" --nocancel --default-item "1" 3>&1 1>&2 2>&3) + + case "$CHOICE" in + 1) + VERBOSE="no" + set_std_mode + ;; + 2) + VERBOSE="yes" + set_std_mode + ;; + 3) + clear + exit_script + exit + ;; + esac + update_script + fi +} + +# ------------------------------------------------------------------------------ +# build_container() +# +# - Creates and configures the LXC container +# - Builds network string and applies features (FUSE, TUN, VAAPI passthrough) +# - Starts container and waits for network connectivity +# - Installs base packages, SSH keys, and runs -install.sh +# ------------------------------------------------------------------------------ +build_container() { + # if [ "$VERBOSE" == "yes" ]; then set -x; fi + + NET_STRING="-net0 name=eth0,bridge=${BRG:-vmbr0}" + + # MAC + if [[ -n "$MAC" ]]; then + case "$MAC" in + ,hwaddr=*) NET_STRING+="$MAC" ;; + *) NET_STRING+=",hwaddr=$MAC" ;; + esac + fi + + # IP (immer zwingend, Standard dhcp) + NET_STRING+=",ip=${NET:-dhcp}" + + # Gateway + if [[ -n "$GATE" ]]; then + case "$GATE" in + ,gw=*) NET_STRING+="$GATE" ;; + *) NET_STRING+=",gw=$GATE" ;; + esac + fi + + # VLAN + if [[ -n "$VLAN" ]]; then + case "$VLAN" in + ,tag=*) NET_STRING+="$VLAN" ;; + *) NET_STRING+=",tag=$VLAN" ;; + esac + fi + + # MTU + if [[ -n "$MTU" ]]; then + case "$MTU" in + ,mtu=*) NET_STRING+="$MTU" ;; + *) NET_STRING+=",mtu=$MTU" ;; + esac + fi + + # IPv6 Handling + case "$IPV6_METHOD" in + auto) NET_STRING="$NET_STRING,ip6=auto" ;; + dhcp) NET_STRING="$NET_STRING,ip6=dhcp" ;; + static) + NET_STRING="$NET_STRING,ip6=$IPV6_ADDR" + [ -n "$IPV6_GATE" ] && NET_STRING="$NET_STRING,gw6=$IPV6_GATE" + ;; + none) ;; + esac + + if [ "$CT_TYPE" == "1" ]; then + FEATURES="keyctl=1,nesting=1" + else + FEATURES="nesting=1" + fi + + if [ "$ENABLE_FUSE" == "yes" ]; then + FEATURES="$FEATURES,fuse=1" + fi + + TEMP_DIR=$(mktemp -d) + pushd "$TEMP_DIR" >/dev/null + if [ "$var_os" == "alpine" ]; then + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-install.func)" + else + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" + fi + export DIAGNOSTICS="$DIAGNOSTICS" + export RANDOM_UUID="$RANDOM_UUID" + export CACHER="$APT_CACHER" + export CACHER_IP="$APT_CACHER_IP" + export tz="$timezone" + export APPLICATION="$APP" + export app="$NSAPP" + export PASSWORD="$PW" + export VERBOSE="$VERBOSE" + export SSH_ROOT="${SSH}" + export SSH_AUTHORIZED_KEY + export CTID="$CT_ID" + export CTTYPE="$CT_TYPE" + export ENABLE_FUSE="$ENABLE_FUSE" + export ENABLE_TUN="$ENABLE_TUN" + export PCT_OSTYPE="$var_os" + export PCT_OSVERSION="$var_version" + export PCT_DISK_SIZE="$DISK_SIZE" + export PCT_OPTIONS=" + -features $FEATURES + -hostname $HN + -tags $TAGS + $SD + $NS + $NET_STRING + -onboot 1 + -cores $CORE_COUNT + -memory $RAM_SIZE + -unprivileged $CT_TYPE + $PW +" + export TEMPLATE_STORAGE="${var_template_storage:-}" + export CONTAINER_STORAGE="${var_container_storage:-}" + create_lxc_container || exit $? + + LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" + + # ============================================================================ + # GPU/USB PASSTHROUGH CONFIGURATION + # ============================================================================ + + # List of applications that benefit from GPU acceleration + GPU_APPS=( + "immich" "channels" "emby" "ersatztv" "frigate" + "jellyfin" "plex" "scrypted" "tdarr" "unmanic" + "ollama" "fileflows" "open-webui" "tunarr" "debian" + "handbrake" "sunshine" "moonlight" "kodi" "stremio" + "viseron" + ) + + # Check if app needs GPU + is_gpu_app() { + local app="${1,,}" + for gpu_app in "${GPU_APPS[@]}"; do + [[ "$app" == "${gpu_app,,}" ]] && return 0 + done + return 1 + } + + # Detect all available GPU devices + detect_gpu_devices() { + INTEL_DEVICES=() + AMD_DEVICES=() + NVIDIA_DEVICES=() + + # Store PCI info to avoid multiple calls + local pci_vga_info=$(lspci -nn 2>/dev/null | grep -E "VGA|Display|3D") + + # Check for Intel GPU - look for Intel vendor ID [8086] + if echo "$pci_vga_info" | grep -q "\[8086:"; then + msg_info "Detected Intel GPU" + if [[ -d /dev/dri ]]; then + for d in /dev/dri/renderD* /dev/dri/card*; do + [[ -e "$d" ]] && INTEL_DEVICES+=("$d") + done + fi + fi + + # Check for AMD GPU - look for AMD vendor IDs [1002] (AMD/ATI) or [1022] (AMD) + if echo "$pci_vga_info" | grep -qE "\[1002:|\[1022:"; then + msg_info "Detected AMD GPU" + if [[ -d /dev/dri ]]; then + # Only add if not already claimed by Intel + if [[ ${#INTEL_DEVICES[@]} -eq 0 ]]; then + for d in /dev/dri/renderD* /dev/dri/card*; do + [[ -e "$d" ]] && AMD_DEVICES+=("$d") + done + fi + fi + fi + + # Check for NVIDIA GPU - look for NVIDIA vendor ID [10de] + if echo "$pci_vga_info" | grep -q "\[10de:"; then + msg_info "Detected NVIDIA GPU" + if ! check_nvidia_host_setup; then + msg_error "NVIDIA host setup incomplete. Skipping GPU passthrough." + msg_info "Fix NVIDIA drivers on host, then recreate container or passthrough manually." + return 0 + fi + + for d in /dev/nvidia* /dev/nvidiactl /dev/nvidia-modeset; do + [[ -e "$d" ]] && NVIDIA_DEVICES+=("$d") + done + + if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then + msg_warn "NVIDIA GPU detected but no /dev/nvidia* devices found" + msg_warn "Please install NVIDIA drivers on host: apt install nvidia-driver" + else + if [[ "$CT_TYPE" == "0" ]]; then + cat <>"$LXC_CONFIG" + # NVIDIA GPU Passthrough (privileged) + lxc.cgroup2.devices.allow: c 195:* rwm + lxc.cgroup2.devices.allow: c 243:* rwm + lxc.mount.entry: /dev/nvidia0 dev/nvidia0 none bind,optional,create=file + lxc.mount.entry: /dev/nvidiactl dev/nvidiactl none bind,optional,create=file + lxc.mount.entry: /dev/nvidia-uvm dev/nvidia-uvm none bind,optional,create=file + lxc.mount.entry: /dev/nvidia-uvm-tools dev/nvidia-uvm-tools none bind,optional,create=file +EOF + + if [[ -e /dev/dri/renderD128 ]]; then + echo "lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file" >>"$LXC_CONFIG" + fi + + export GPU_TYPE="NVIDIA" + export NVIDIA_DRIVER_VERSION=$(nvidia-smi --query-gpu=driver_version --format=csv,noheader 2>/dev/null | head -n1) + msg_ok "NVIDIA GPU passthrough configured (driver: ${NVIDIA_DRIVER_VERSION})" + else + msg_warn "NVIDIA passthrough only supported for privileged containers" + return 0 + fi + fi + fi + + # Debug output + msg_debug "Intel devices: ${INTEL_DEVICES[*]}" + msg_debug "AMD devices: ${AMD_DEVICES[*]}" + msg_debug "NVIDIA devices: ${NVIDIA_DEVICES[*]}" + } + + # Configure USB passthrough for privileged containers + configure_usb_passthrough() { + if [[ "$CT_TYPE" != "0" ]]; then + return 0 + fi + + msg_info "Configuring automatic USB passthrough (privileged container)" + cat <>"$LXC_CONFIG" +# Automatic USB passthrough (privileged container) +lxc.cgroup2.devices.allow: a +lxc.cap.drop: +lxc.cgroup2.devices.allow: c 188:* rwm +lxc.cgroup2.devices.allow: c 189:* rwm +lxc.mount.entry: /dev/serial/by-id dev/serial/by-id none bind,optional,create=dir +lxc.mount.entry: /dev/ttyUSB0 dev/ttyUSB0 none bind,optional,create=file +lxc.mount.entry: /dev/ttyUSB1 dev/ttyUSB1 none bind,optional,create=file +lxc.mount.entry: /dev/ttyACM0 dev/ttyACM0 none bind,optional,create=file +lxc.mount.entry: /dev/ttyACM1 dev/ttyACM1 none bind,optional,create=file +EOF + msg_ok "USB passthrough configured" + } + + # Configure GPU passthrough + configure_gpu_passthrough() { + # Skip if not a GPU app and not privileged + if [[ "$CT_TYPE" != "0" ]] && ! is_gpu_app "$APP"; then + return 0 + fi + + detect_gpu_devices + + # Count available GPU types + local gpu_count=0 + local available_gpus=() + + if [[ ${#INTEL_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("INTEL") + gpu_count=$((gpu_count + 1)) + fi + + if [[ ${#AMD_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("AMD") + gpu_count=$((gpu_count + 1)) + fi + + if [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("NVIDIA") + gpu_count=$((gpu_count + 1)) + fi + + if [[ $gpu_count -eq 0 ]]; then + msg_info "No GPU devices found for passthrough" + return 0 + fi + + local selected_gpu="" + + if [[ $gpu_count -eq 1 ]]; then + # Automatic selection for single GPU + selected_gpu="${available_gpus[0]}" + msg_info "Automatically configuring ${selected_gpu} GPU passthrough" + else + # Multiple GPUs - ask user + echo -e "\n${INFO} Multiple GPU types detected:" + for gpu in "${available_gpus[@]}"; do + echo " - $gpu" + done + read -rp "Which GPU type to passthrough? (${available_gpus[*]}): " selected_gpu + selected_gpu="${selected_gpu^^}" + + # Validate selection + local valid=0 + for gpu in "${available_gpus[@]}"; do + [[ "$selected_gpu" == "$gpu" ]] && valid=1 + done + + if [[ $valid -eq 0 ]]; then + msg_warn "Invalid selection. Skipping GPU passthrough." + return 0 + fi + fi + + # Apply passthrough configuration based on selection + local dev_idx=0 + + case "$selected_gpu" in + INTEL | AMD) + local devices=() + [[ "$selected_gpu" == "INTEL" ]] && devices=("${INTEL_DEVICES[@]}") + [[ "$selected_gpu" == "AMD" ]] && devices=("${AMD_DEVICES[@]}") + + # For Proxmox WebUI visibility, add as dev0, dev1 etc. + for dev in "${devices[@]}"; do + if [[ "$CT_TYPE" == "0" ]]; then + # Privileged container - use dev entries for WebUI visibility + # Use initial GID 104 (render) for renderD*, 44 (video) for card* + if [[ "$dev" =~ renderD ]]; then + echo "dev${dev_idx}: $dev,gid=104" >>"$LXC_CONFIG" + else + echo "dev${dev_idx}: $dev,gid=44" >>"$LXC_CONFIG" + fi + dev_idx=$((dev_idx + 1)) + + # Also add cgroup allows for privileged containers + local major minor + major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") + minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") + + if [[ "$major" != "0" && "$minor" != "0" ]]; then + echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" + fi + else + # Unprivileged container + if [[ "$dev" =~ renderD ]]; then + echo "dev${dev_idx}: $dev,uid=0,gid=104" >>"$LXC_CONFIG" + else + echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG" + fi + dev_idx=$((dev_idx + 1)) + fi + done + + export GPU_TYPE="$selected_gpu" + msg_ok "${selected_gpu} GPU passthrough configured (${dev_idx} devices)" + ;; + + NVIDIA) + if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then + msg_error "NVIDIA drivers not installed on host. Please install: apt install nvidia-driver" + return 1 + fi + + for dev in "${NVIDIA_DEVICES[@]}"; do + # NVIDIA devices typically need different handling + echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG" + dev_idx=$((dev_idx + 1)) + + if [[ "$CT_TYPE" == "0" ]]; then + local major minor + major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") + minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") + + if [[ "$major" != "0" && "$minor" != "0" ]]; then + echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" + fi + fi + done + + export GPU_TYPE="NVIDIA" + msg_ok "NVIDIA GPU passthrough configured (${dev_idx} devices)" + ;; + esac + } + + # Additional device passthrough + configure_additional_devices() { + # TUN device passthrough + if [ "$ENABLE_TUN" == "yes" ]; then + cat <>"$LXC_CONFIG" +lxc.cgroup2.devices.allow: c 10:200 rwm +lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file +EOF + fi + + # Coral TPU passthrough + if [[ -e /dev/apex_0 ]]; then + msg_info "Detected Coral TPU - configuring passthrough" + echo "lxc.mount.entry: /dev/apex_0 dev/apex_0 none bind,optional,create=file" >>"$LXC_CONFIG" + fi + } + + # Execute pre-start configurations + configure_usb_passthrough + configure_gpu_passthrough + configure_additional_devices + + # ============================================================================ + # START CONTAINER AND INSTALL USERLAND + # ============================================================================ + + msg_info "Starting LXC Container" + pct start "$CTID" + + # Wait for container to be running + for i in {1..10}; do + if pct status "$CTID" | grep -q "status: running"; then + msg_ok "Started LXC Container" + break + fi + sleep 1 + if [ "$i" -eq 10 ]; then + msg_error "LXC Container did not reach running state" + exit 1 + fi + done + + # Wait for network (skip for Alpine initially) + if [ "$var_os" != "alpine" ]; then + msg_info "Waiting for network in LXC container" + + # Wait for IP + for i in {1..20}; do + ip_in_lxc=$(pct exec "$CTID" -- ip -4 addr show dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) + [ -n "$ip_in_lxc" ] && break + sleep 1 + done + + if [ -z "$ip_in_lxc" ]; then + msg_error "No IP assigned to CT $CTID after 20s" + exit 1 + fi + + # Try to reach gateway + gw_ok=0 + for i in {1..10}; do + if pct exec "$CTID" -- ping -c1 -W1 "${GATEWAY:-8.8.8.8}" >/dev/null 2>&1; then + gw_ok=1 + break + fi + sleep 1 + done + + if [ "$gw_ok" -eq 1 ]; then + msg_ok "Network in LXC is reachable (IP $ip_in_lxc)" + else + msg_warn "Network reachable but gateway check failed" + fi + fi + # Function to get correct GID inside container + get_container_gid() { + local group="$1" + local gid=$(pct exec "$CTID" -- getent group "$group" 2>/dev/null | cut -d: -f3) + echo "${gid:-44}" # Default to 44 if not found + } + + fix_gpu_gids + + # Continue with standard container setup + msg_info "Customizing LXC Container" + + # # Install GPU userland if configured + # if [[ "${ENABLE_VAAPI:-0}" == "1" ]]; then + # install_gpu_userland "VAAPI" + # fi + + # if [[ "${ENABLE_NVIDIA:-0}" == "1" ]]; then + # install_gpu_userland "NVIDIA" + # fi + + # Continue with standard container setup + if [ "$var_os" == "alpine" ]; then + sleep 3 + pct exec "$CTID" -- /bin/sh -c 'cat </etc/apk/repositories +http://dl-cdn.alpinelinux.org/alpine/latest-stable/main +http://dl-cdn.alpinelinux.org/alpine/latest-stable/community +EOF' + pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses jq >/dev/null" + else + sleep 3 + pct exec "$CTID" -- bash -c "sed -i '/$LANG/ s/^# //' /etc/locale.gen" + pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \ + echo LANG=\$locale_line >/etc/default/locale && \ + locale-gen >/dev/null && \ + export LANG=\$locale_line" + + if [[ -z "${tz:-}" ]]; then + tz=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "Etc/UTC") + fi + + if pct exec "$CTID" -- test -e "/usr/share/zoneinfo/$tz"; then + pct exec "$CTID" -- bash -c "tz='$tz'; echo \"\$tz\" >/etc/timezone && ln -sf \"/usr/share/zoneinfo/\$tz\" /etc/localtime" + else + msg_warn "Skipping timezone setup – zone '$tz' not found in container" + fi + + pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y sudo curl mc gnupg2 jq >/dev/null" || { + msg_error "apt-get base packages installation failed" + exit 1 + } + fi + + msg_ok "Customized LXC Container" + + # Verify GPU access if enabled + if [[ "${ENABLE_VAAPI:-0}" == "1" ]] && [ "$var_os" != "alpine" ]; then + pct exec "$CTID" -- bash -c "vainfo >/dev/null 2>&1" && + msg_ok "VAAPI verified working" || + msg_warn "VAAPI verification failed - may need additional configuration" + fi + + if [[ "${ENABLE_NVIDIA:-0}" == "1" ]] && [ "$var_os" != "alpine" ]; then + pct exec "$CTID" -- bash -c "nvidia-smi >/dev/null 2>&1" && + msg_ok "NVIDIA verified working" || + msg_warn "NVIDIA verification failed - may need additional configuration" + fi + + # Install SSH keys + install_ssh_keys_into_ct + + # Run application installer + if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)"; then + exit $? + fi +} + +destroy_lxc() { + if [[ -z "$CT_ID" ]]; then + msg_error "No CT_ID found. Nothing to remove." + return 1 + fi + + # Abbruch bei Ctrl-C / Ctrl-D / ESC + trap 'echo; msg_error "Aborted by user (SIGINT/SIGQUIT)"; return 130' INT QUIT + + local prompt + if ! read -rp "Remove this Container? " prompt; then + # read gibt != 0 zurück bei Ctrl-D/ESC + msg_error "Aborted input (Ctrl-D/ESC)" + return 130 + fi + + case "${prompt,,}" in + y | yes) + if pct stop "$CT_ID" &>/dev/null && pct destroy "$CT_ID" &>/dev/null; then + msg_ok "Removed Container $CT_ID" + else + msg_error "Failed to remove Container $CT_ID" + return 1 + fi + ;; + "" | n | no) + msg_info "Container was not removed." + ;; + *) + msg_warn "Invalid response. Container was not removed." + ;; + esac +} + +# ------------------------------------------------------------------------------ +# Storage discovery / selection helpers +# ------------------------------------------------------------------------------ +# ===== Storage discovery / selection helpers (ported from create_lxc.sh) ===== +resolve_storage_preselect() { + local class="$1" preselect="$2" required_content="" + case "$class" in + template) required_content="vztmpl" ;; + container) required_content="rootdir" ;; + *) return 1 ;; + esac + [[ -z "$preselect" ]] && return 1 + if ! pvesm status -content "$required_content" | awk 'NR>1{print $1}' | grep -qx -- "$preselect"; then + msg_warn "Preselected storage '${preselect}' does not support content '${required_content}' (or not found)" + return 1 + fi + + local line total used free + line="$(pvesm status | awk -v s="$preselect" 'NR>1 && $1==s {print $0}')" + if [[ -z "$line" ]]; then + STORAGE_INFO="n/a" + else + total="$(awk '{print $4}' <<<"$line")" + used="$(awk '{print $5}' <<<"$line")" + free="$(awk '{print $6}' <<<"$line")" + local total_h used_h free_h + if command -v numfmt >/dev/null 2>&1; then + total_h="$(numfmt --to=iec --suffix=B --format %.1f "$total" 2>/dev/null || echo "$total")" + used_h="$(numfmt --to=iec --suffix=B --format %.1f "$used" 2>/dev/null || echo "$used")" + free_h="$(numfmt --to=iec --suffix=B --format %.1f "$free" 2>/dev/null || echo "$free")" + STORAGE_INFO="Free: ${free_h} Used: ${used_h}" + else + STORAGE_INFO="Free: ${free} Used: ${used}" + fi + fi + STORAGE_RESULT="$preselect" + return 0 +} + +fix_gpu_gids() { + if [[ -z "${GPU_TYPE:-}" ]]; then + return 0 + fi + + msg_info "Detecting and setting correct GPU group IDs" + + # Ermittle die tatsächlichen GIDs aus dem Container + local video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") + local render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") + + # Fallbacks wenn Gruppen nicht existieren + if [[ -z "$video_gid" ]]; then + # Versuche die video Gruppe zu erstellen + pct exec "$CTID" -- sh -c "groupadd -r video 2>/dev/null || true" + video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") + [[ -z "$video_gid" ]] && video_gid="44" # Ultimate fallback + fi + + if [[ -z "$render_gid" ]]; then + # Versuche die render Gruppe zu erstellen + pct exec "$CTID" -- sh -c "groupadd -r render 2>/dev/null || true" + render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") + [[ -z "$render_gid" ]] && render_gid="104" # Ultimate fallback + fi + + msg_info "Container GIDs detected - video:${video_gid}, render:${render_gid}" + + # Prüfe ob die GIDs von den Defaults abweichen + local need_update=0 + if [[ "$video_gid" != "44" ]] || [[ "$render_gid" != "104" ]]; then + need_update=1 + fi + + if [[ $need_update -eq 1 ]]; then + msg_info "Updating device GIDs in container config" + + # Stoppe Container für Config-Update + pct stop "$CTID" >/dev/null 2>&1 + + # Update die dev Einträge mit korrekten GIDs + # Backup der Config + cp "$LXC_CONFIG" "${LXC_CONFIG}.bak" + + # Parse und update jeden dev Eintrag + while IFS= read -r line; do + if [[ "$line" =~ ^dev[0-9]+: ]]; then + # Extract device path + local device_path=$(echo "$line" | sed -E 's/^dev[0-9]+: ([^,]+).*/\1/') + local dev_num=$(echo "$line" | sed -E 's/^(dev[0-9]+):.*/\1/') + + if [[ "$device_path" =~ renderD ]]; then + # RenderD device - use render GID + echo "${dev_num}: ${device_path},gid=${render_gid}" + elif [[ "$device_path" =~ card ]]; then + # Card device - use video GID + echo "${dev_num}: ${device_path},gid=${video_gid}" + else + # Keep original line + echo "$line" + fi + else + # Keep non-dev lines + echo "$line" + fi + done <"$LXC_CONFIG" >"${LXC_CONFIG}.new" + + mv "${LXC_CONFIG}.new" "$LXC_CONFIG" + + # Starte Container wieder + pct start "$CTID" >/dev/null 2>&1 + sleep 3 + + msg_ok "Device GIDs updated successfully" + else + msg_ok "Device GIDs are already correct" + fi + if [[ "$CT_TYPE" == "0" ]]; then + pct exec "$CTID" -- bash -c " + if [ -d /dev/dri ]; then + for dev in /dev/dri/*; do + if [ -e \"\$dev\" ]; then + if [[ \"\$dev\" =~ renderD ]]; then + chgrp ${render_gid} \"\$dev\" 2>/dev/null || true + else + chgrp ${video_gid} \"\$dev\" 2>/dev/null || true + fi + chmod 660 \"\$dev\" 2>/dev/null || true + fi + done + fi + " >/dev/null 2>&1 + fi +} + +# NVIDIA-spezific check on host +check_nvidia_host_setup() { + if ! command -v nvidia-smi >/dev/null 2>&1; then + msg_warn "NVIDIA GPU detected but nvidia-smi not found on host" + msg_warn "Please install NVIDIA drivers on host first." + #echo " 1. Download driver: wget https://us.download.nvidia.com/XFree86/Linux-x86_64/550.127.05/NVIDIA-Linux-x86_64-550.127.05.run" + #echo " 2. Install: ./NVIDIA-Linux-x86_64-550.127.05.run --dkms" + #echo " 3. Verify: nvidia-smi" + return 1 + fi + + # check if nvidia-smi works + if ! nvidia-smi >/dev/null 2>&1; then + msg_warn "nvidia-smi installed but not working. Driver issue?" + return 1 + fi + + return 0 +} + +check_storage_support() { + local CONTENT="$1" VALID=0 + while IFS= read -r line; do + local STORAGE_NAME + STORAGE_NAME=$(awk '{print $1}' <<<"$line") + [[ -n "$STORAGE_NAME" ]] && VALID=1 + done < <(pvesm status -content "$CONTENT" 2>/dev/null | awk 'NR>1') + [[ $VALID -eq 1 ]] +} + +select_storage() { + local CLASS=$1 CONTENT CONTENT_LABEL + case $CLASS in + container) + CONTENT='rootdir' + CONTENT_LABEL='Container' + ;; + template) + CONTENT='vztmpl' + CONTENT_LABEL='Container template' + ;; + iso) + CONTENT='iso' + CONTENT_LABEL='ISO image' + ;; + images) + CONTENT='images' + CONTENT_LABEL='VM Disk image' + ;; + backup) + CONTENT='backup' + CONTENT_LABEL='Backup' + ;; + snippets) + CONTENT='snippets' + CONTENT_LABEL='Snippets' + ;; + *) + msg_error "Invalid storage class '$CLASS'" + return 1 + ;; + esac + + declare -A STORAGE_MAP + local -a MENU=() + local COL_WIDTH=0 + + while read -r TAG TYPE _ TOTAL USED FREE _; do + [[ -n "$TAG" && -n "$TYPE" ]] || continue + local DISPLAY="${TAG} (${TYPE})" + local USED_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$USED") + local FREE_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$FREE") + local INFO="Free: ${FREE_FMT}B Used: ${USED_FMT}B" + STORAGE_MAP["$DISPLAY"]="$TAG" + MENU+=("$DISPLAY" "$INFO" "OFF") + ((${#DISPLAY} > COL_WIDTH)) && COL_WIDTH=${#DISPLAY} + done < <(pvesm status -content "$CONTENT" | awk 'NR>1') + + if [[ ${#MENU[@]} -eq 0 ]]; then + msg_error "No storage found for content type '$CONTENT'." + return 2 + fi + + if [[ $((${#MENU[@]} / 3)) -eq 1 ]]; then + STORAGE_RESULT="${STORAGE_MAP[${MENU[0]}]}" + STORAGE_INFO="${MENU[1]}" + return 0 + fi + + local WIDTH=$((COL_WIDTH + 42)) + while true; do + local DISPLAY_SELECTED + DISPLAY_SELECTED=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "Storage Pools" \ + --radiolist "Which storage pool for ${CONTENT_LABEL,,}?\n(Spacebar to select)" \ + 16 "$WIDTH" 6 "${MENU[@]}" 3>&1 1>&2 2>&3) || { exit_script; } + + DISPLAY_SELECTED=$(sed 's/[[:space:]]*$//' <<<"$DISPLAY_SELECTED") + if [[ -z "$DISPLAY_SELECTED" || -z "${STORAGE_MAP[$DISPLAY_SELECTED]+_}" ]]; then + whiptail --msgbox "No valid storage selected. Please try again." 8 58 + continue + fi + STORAGE_RESULT="${STORAGE_MAP[$DISPLAY_SELECTED]}" + for ((i = 0; i < ${#MENU[@]}; i += 3)); do + if [[ "${MENU[$i]}" == "$DISPLAY_SELECTED" ]]; then + STORAGE_INFO="${MENU[$i + 1]}" + break + fi + done + return 0 + done +} + +create_lxc_container() { + # ------------------------------------------------------------------------------ + # Optional verbose mode (debug tracing) + # ------------------------------------------------------------------------------ + if [[ "${CREATE_LXC_VERBOSE:-no}" == "yes" ]]; then set -x; fi + + # ------------------------------------------------------------------------------ + # Helpers (dynamic versioning / template parsing) + # ------------------------------------------------------------------------------ + pkg_ver() { dpkg-query -W -f='${Version}\n' "$1" 2>/dev/null || echo ""; } + pkg_cand() { apt-cache policy "$1" 2>/dev/null | awk '/Candidate:/ {print $2}'; } + + ver_ge() { dpkg --compare-versions "$1" ge "$2"; } + ver_gt() { dpkg --compare-versions "$1" gt "$2"; } + ver_lt() { dpkg --compare-versions "$1" lt "$2"; } + + # Extract Debian OS minor from template name: debian-13-standard_13.1-1_amd64.tar.zst => "13.1" + parse_template_osver() { sed -n 's/.*_\([0-9][0-9]*\(\.[0-9]\+\)\?\)-.*/\1/p' <<<"$1"; } + + # Offer upgrade for pve-container/lxc-pve if candidate > installed; optional auto-retry pct create + # Returns: + # 0 = no upgrade needed + # 1 = upgraded (and if do_retry=yes and retry succeeded, creation done) + # 2 = user declined + # 3 = upgrade attempted but failed OR retry failed + offer_lxc_stack_upgrade_and_maybe_retry() { + local do_retry="${1:-no}" # yes|no + local _pvec_i _pvec_c _lxcp_i _lxcp_c need=0 + + _pvec_i="$(pkg_ver pve-container)" + _lxcp_i="$(pkg_ver lxc-pve)" + _pvec_c="$(pkg_cand pve-container)" + _lxcp_c="$(pkg_cand lxc-pve)" + + if [[ -n "$_pvec_c" && "$_pvec_c" != "none" ]]; then + ver_gt "$_pvec_c" "${_pvec_i:-0}" && need=1 + fi + if [[ -n "$_lxcp_c" && "$_lxcp_c" != "none" ]]; then + ver_gt "$_lxcp_c" "${_lxcp_i:-0}" && need=1 + fi + if [[ $need -eq 0 ]]; then + msg_debug "No newer candidate for pve-container/lxc-pve (installed=$_pvec_i/$_lxcp_i, cand=$_pvec_c/$_lxcp_c)" + return 0 + fi + + echo + echo "An update for the Proxmox LXC stack is available:" + echo " pve-container: installed=${_pvec_i:-n/a} candidate=${_pvec_c:-n/a}" + echo " lxc-pve : installed=${_lxcp_i:-n/a} candidate=${_lxcp_c:-n/a}" + echo + read -rp "Do you want to upgrade now? [y/N] " _ans + case "${_ans,,}" in + y | yes) + msg_info "Upgrading Proxmox LXC stack (pve-container, lxc-pve)" + if apt-get update -qq >/dev/null && apt-get install -y --only-upgrade pve-container lxc-pve >/dev/null; then + msg_ok "LXC stack upgraded." + if [[ "$do_retry" == "yes" ]]; then + msg_info "Retrying container creation after upgrade" + if pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then + msg_ok "Container created successfully after upgrade." + return 0 + else + msg_error "pct create still failed after upgrade. See $LOGFILE" + return 3 + fi + fi + return 1 + else + msg_error "Upgrade failed. Please check APT output." + return 3 + fi + ;; + *) return 2 ;; + esac + } + + # ------------------------------------------------------------------------------ + # Required input variables + # ------------------------------------------------------------------------------ + [[ "${CTID:-}" ]] || { + msg_error "You need to set 'CTID' variable." + exit 203 + } + [[ "${PCT_OSTYPE:-}" ]] || { + msg_error "You need to set 'PCT_OSTYPE' variable." + exit 204 + } + + msg_debug "CTID=$CTID" + msg_debug "PCT_OSTYPE=$PCT_OSTYPE" + msg_debug "PCT_OSVERSION=${PCT_OSVERSION:-default}" + + # ID checks + [[ "$CTID" -ge 100 ]] || { + msg_error "ID cannot be less than 100." + exit 205 + } + if qm status "$CTID" &>/dev/null || pct status "$CTID" &>/dev/null; then + echo -e "ID '$CTID' is already in use." + unset CTID + msg_error "Cannot use ID that is already in use." + exit 206 + fi + + # Storage capability check + check_storage_support "rootdir" || { + msg_error "No valid storage found for 'rootdir' [Container]" + exit 1 + } + check_storage_support "vztmpl" || { + msg_error "No valid storage found for 'vztmpl' [Template]" + exit 1 + } + + # Template storage selection + if resolve_storage_preselect template "${TEMPLATE_STORAGE:-}"; then + TEMPLATE_STORAGE="$STORAGE_RESULT" + TEMPLATE_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${TEMPLATE_STORAGE}${CL} (${TEMPLATE_STORAGE_INFO}) [Template]" + else + while true; do + if [[ -z "${var_template_storage:-}" ]]; then + if select_storage template; then + TEMPLATE_STORAGE="$STORAGE_RESULT" + TEMPLATE_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${TEMPLATE_STORAGE}${CL} (${TEMPLATE_STORAGE_INFO}) [Template]" + break + fi + fi + done + fi + + # Container storage selection + if resolve_storage_preselect container "${CONTAINER_STORAGE:-}"; then + CONTAINER_STORAGE="$STORAGE_RESULT" + CONTAINER_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${CONTAINER_STORAGE}${CL} (${CONTAINER_STORAGE_INFO}) [Container]" + else + if [[ -z "${var_container_storage:-}" ]]; then + if select_storage container; then + CONTAINER_STORAGE="$STORAGE_RESULT" + CONTAINER_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${CONTAINER_STORAGE}${CL} (${CONTAINER_STORAGE_INFO}) [Container]" + fi + fi + fi + + # Validate content types + msg_info "Validating content types of storage '$CONTAINER_STORAGE'" + STORAGE_CONTENT=$(grep -A4 -E "^(zfspool|dir|lvmthin|lvm): $CONTAINER_STORAGE" /etc/pve/storage.cfg | grep content | awk '{$1=""; print $0}' | xargs) + msg_debug "Storage '$CONTAINER_STORAGE' has content types: $STORAGE_CONTENT" + grep -qw "rootdir" <<<"$STORAGE_CONTENT" || { + msg_error "Storage '$CONTAINER_STORAGE' does not support 'rootdir'. Cannot create LXC." + exit 217 + } + $STD msg_ok "Storage '$CONTAINER_STORAGE' supports 'rootdir'" + + msg_info "Validating content types of template storage '$TEMPLATE_STORAGE'" + TEMPLATE_CONTENT=$(grep -A4 -E "^[^:]+: $TEMPLATE_STORAGE" /etc/pve/storage.cfg | grep content | awk '{$1=""; print $0}' | xargs) + msg_debug "Template storage '$TEMPLATE_STORAGE' has content types: $TEMPLATE_CONTENT" + if ! grep -qw "vztmpl" <<<"$TEMPLATE_CONTENT"; then + msg_warn "Template storage '$TEMPLATE_STORAGE' does not declare 'vztmpl'. This may cause pct create to fail." + else + $STD msg_ok "Template storage '$TEMPLATE_STORAGE' supports 'vztmpl'" + fi + + # Free space check + STORAGE_FREE=$(pvesm status | awk -v s="$CONTAINER_STORAGE" '$1 == s { print $6 }') + REQUIRED_KB=$((${PCT_DISK_SIZE:-8} * 1024 * 1024)) + [[ "$STORAGE_FREE" -ge "$REQUIRED_KB" ]] || { + msg_error "Not enough space on '$CONTAINER_STORAGE'. Needed: ${PCT_DISK_SIZE:-8}G." + exit 214 + } + + # Cluster quorum (if cluster) + if [[ -f /etc/pve/corosync.conf ]]; then + msg_info "Checking cluster quorum" + if ! pvecm status | awk -F':' '/^Quorate/ { exit ($2 ~ /Yes/) ? 0 : 1 }'; then + msg_error "Cluster is not quorate. Start all nodes or configure quorum device (QDevice)." + exit 210 + fi + msg_ok "Cluster is quorate" + fi + + # ------------------------------------------------------------------------------ + # Template discovery & validation + # ------------------------------------------------------------------------------ + TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" + case "$PCT_OSTYPE" in + debian | ubuntu) TEMPLATE_PATTERN="-standard_" ;; + alpine | fedora | rocky | centos) TEMPLATE_PATTERN="-default_" ;; + *) TEMPLATE_PATTERN="" ;; + esac + + msg_info "Searching for template '$TEMPLATE_SEARCH'" + + # Build regex patterns outside awk/grep for clarity + SEARCH_PATTERN="^${TEMPLATE_SEARCH}" + + #echo "[DEBUG] TEMPLATE_SEARCH='$TEMPLATE_SEARCH'" + #echo "[DEBUG] SEARCH_PATTERN='$SEARCH_PATTERN'" + #echo "[DEBUG] TEMPLATE_PATTERN='$TEMPLATE_PATTERN'" + + mapfile -t LOCAL_TEMPLATES < <( + pveam list "$TEMPLATE_STORAGE" 2>/dev/null | + awk -v search="${SEARCH_PATTERN}" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' | + sed 's|.*/||' | sort -t - -k 2 -V + ) + + pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)." + + #echo "[DEBUG] pveam available output (first 5 lines with .tar files):" + #pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | head -5 | sed 's/^/ /' + + set +u + mapfile -t ONLINE_TEMPLATES < <(pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | awk '{print $2}' | grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V 2>/dev/null || true) + #echo "[DEBUG] After filtering: ${#ONLINE_TEMPLATES[@]} online templates found" + set -u + if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then + #echo "[DEBUG] Online templates:" + for tmpl in "${ONLINE_TEMPLATES[@]}"; do + echo " - $tmpl" + done + fi + + ONLINE_TEMPLATE="" + [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" + + #msg_debug "SEARCH_PATTERN='${SEARCH_PATTERN}' TEMPLATE_PATTERN='${TEMPLATE_PATTERN}'" + #msg_debug "Found ${#LOCAL_TEMPLATES[@]} local templates, ${#ONLINE_TEMPLATES[@]} online templates" + if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then + #msg_debug "First 3 online templates:" + count=0 + for idx in "${!ONLINE_TEMPLATES[@]}"; do + #msg_debug " [$idx]: ${ONLINE_TEMPLATES[$idx]}" + ((count++)) + [[ $count -ge 3 ]] && break + done + fi + #msg_debug "ONLINE_TEMPLATE='$ONLINE_TEMPLATE'" + + if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then + TEMPLATE="${LOCAL_TEMPLATES[-1]}" + TEMPLATE_SOURCE="local" + else + TEMPLATE="$ONLINE_TEMPLATE" + TEMPLATE_SOURCE="online" + fi + + # If still no template, try to find alternatives + if [[ -z "$TEMPLATE" ]]; then + echo "" + echo "[DEBUG] No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}, searching for alternatives..." + + # Get all available versions for this OS type + mapfile -t AVAILABLE_VERSIONS < <( + pveam available -section system 2>/dev/null | + grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | + awk -F'\t' '{print $1}' | + grep "^${PCT_OSTYPE}-" | + sed -E "s/.*${PCT_OSTYPE}-([0-9]+(\.[0-9]+)?).*/\1/" | + sort -u -V 2>/dev/null + ) + + if [[ ${#AVAILABLE_VERSIONS[@]} -gt 0 ]]; then + echo "" + echo "${BL}Available ${PCT_OSTYPE} versions:${CL}" + for i in "${!AVAILABLE_VERSIONS[@]}"; do + echo " [$((i + 1))] ${AVAILABLE_VERSIONS[$i]}" + done + echo "" + read -p "Select version [1-${#AVAILABLE_VERSIONS[@]}] or press Enter to cancel: " choice + + if [[ "$choice" =~ ^[0-9]+$ ]] && [[ "$choice" -ge 1 ]] && [[ "$choice" -le ${#AVAILABLE_VERSIONS[@]} ]]; then + PCT_OSVERSION="${AVAILABLE_VERSIONS[$((choice - 1))]}" + TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION}" + SEARCH_PATTERN="^${TEMPLATE_SEARCH}-" + + #echo "[DEBUG] Retrying with version: $PCT_OSVERSION" + + mapfile -t ONLINE_TEMPLATES < <( + pveam available -section system 2>/dev/null | + grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | + awk -F'\t' '{print $1}' | + grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | + sort -t - -k 2 -V 2>/dev/null || true + ) + + if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then + TEMPLATE="${ONLINE_TEMPLATES[-1]}" + TEMPLATE_SOURCE="online" + #echo "[DEBUG] Found alternative: $TEMPLATE" + else + msg_error "No templates available for ${PCT_OSTYPE} ${PCT_OSVERSION}" + exit 225 + fi + else + msg_info "Installation cancelled" + exit 0 + fi + else + msg_error "No ${PCT_OSTYPE} templates available at all" + exit 225 + fi + fi + + #echo "[DEBUG] Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'" + #msg_debug "Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'" + + TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" + if [[ -z "$TEMPLATE_PATH" ]]; then + TEMPLATE_BASE=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) + [[ -n "$TEMPLATE_BASE" ]] && TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE" + fi + + # If we still don't have a path but have a valid template name, construct it + if [[ -z "$TEMPLATE_PATH" && -n "$TEMPLATE" ]]; then + TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" + fi + + [[ -n "$TEMPLATE_PATH" ]] || { + if [[ -z "$TEMPLATE" ]]; then + msg_error "Template ${PCT_OSTYPE} ${PCT_OSVERSION} not available" + + # Get available versions + mapfile -t AVAILABLE_VERSIONS < <( + pveam available -section system 2>/dev/null | + grep "^${PCT_OSTYPE}-" | + sed -E 's/.*'"${PCT_OSTYPE}"'-([0-9]+\.[0-9]+).*/\1/' | + grep -E '^[0-9]+\.[0-9]+$' | + sort -u -V 2>/dev/null || sort -u + ) + + if [[ ${#AVAILABLE_VERSIONS[@]} -gt 0 ]]; then + echo -e "\n${BL}Available versions:${CL}" + for i in "${!AVAILABLE_VERSIONS[@]}"; do + echo " [$((i + 1))] ${AVAILABLE_VERSIONS[$i]}" + done + + echo "" + read -p "Select version [1-${#AVAILABLE_VERSIONS[@]}] or Enter to exit: " choice + + if [[ "$choice" =~ ^[0-9]+$ ]] && [[ "$choice" -ge 1 ]] && [[ "$choice" -le ${#AVAILABLE_VERSIONS[@]} ]]; then + export var_version="${AVAILABLE_VERSIONS[$((choice - 1))]}" + export PCT_OSVERSION="$var_version" + msg_ok "Switched to ${PCT_OSTYPE} ${var_version}" + + # Retry template search with new version + TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" + SEARCH_PATTERN="^${TEMPLATE_SEARCH}-" + + mapfile -t LOCAL_TEMPLATES < <( + pveam list "$TEMPLATE_STORAGE" 2>/dev/null | + awk -v search="${SEARCH_PATTERN}" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' | + sed 's|.*/||' | sort -t - -k 2 -V + ) + mapfile -t ONLINE_TEMPLATES < <( + pveam available -section system 2>/dev/null | + grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | + awk -F'\t' '{print $1}' | + grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | + sort -t - -k 2 -V 2>/dev/null || true + ) + ONLINE_TEMPLATE="" + [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" + + if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then + TEMPLATE="${LOCAL_TEMPLATES[-1]}" + TEMPLATE_SOURCE="local" + else + TEMPLATE="$ONLINE_TEMPLATE" + TEMPLATE_SOURCE="online" + fi + + TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" + if [[ -z "$TEMPLATE_PATH" ]]; then + TEMPLATE_BASE=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) + [[ -n "$TEMPLATE_BASE" ]] && TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE" + fi + + # If we still don't have a path but have a valid template name, construct it + if [[ -z "$TEMPLATE_PATH" && -n "$TEMPLATE" ]]; then + TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" + fi + + [[ -n "$TEMPLATE_PATH" ]] || { + msg_error "Template still not found after version change" + exit 220 + } + else + msg_info "Installation cancelled" + exit 1 + fi + else + msg_error "No ${PCT_OSTYPE} templates available" + exit 220 + fi + fi + } + + # Validate that we found a template + if [[ -z "$TEMPLATE" ]]; then + msg_error "No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}" + msg_info "Please check:" + msg_info " - Is pveam catalog available? (run: pveam available -section system)" + msg_info " - Does the template exist for your OS version?" + exit 225 + fi + + msg_ok "Template ${BL}$TEMPLATE${CL} [$TEMPLATE_SOURCE]" + msg_debug "Resolved TEMPLATE_PATH=$TEMPLATE_PATH" + + NEED_DOWNLOAD=0 + if [[ ! -f "$TEMPLATE_PATH" ]]; then + msg_info "Template not present locally – will download." + NEED_DOWNLOAD=1 + elif [[ ! -r "$TEMPLATE_PATH" ]]; then + msg_error "Template file exists but is not readable – check permissions." + exit 221 + elif [[ "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then + if [[ -n "$ONLINE_TEMPLATE" ]]; then + msg_warn "Template file too small (<1MB) – re-downloading." + NEED_DOWNLOAD=1 + else + msg_warn "Template looks too small, but no online version exists. Keeping local file." + fi + elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then + if [[ -n "$ONLINE_TEMPLATE" ]]; then + msg_warn "Template appears corrupted – re-downloading." + NEED_DOWNLOAD=1 + else + msg_warn "Template appears corrupted, but no online version exists. Keeping local file." + fi + else + $STD msg_ok "Template $TEMPLATE is present and valid." + fi + + if [[ "$TEMPLATE_SOURCE" == "local" && -n "$ONLINE_TEMPLATE" && "$TEMPLATE" != "$ONLINE_TEMPLATE" ]]; then + msg_warn "Local template is outdated: $TEMPLATE (latest available: $ONLINE_TEMPLATE)" + if whiptail --yesno "A newer template is available:\n$ONLINE_TEMPLATE\n\nDo you want to download and use it instead?" 12 70; then + TEMPLATE="$ONLINE_TEMPLATE" + NEED_DOWNLOAD=1 + else + msg_info "Continuing with local template $TEMPLATE" + fi + fi + + if [[ "$NEED_DOWNLOAD" -eq 1 ]]; then + [[ -f "$TEMPLATE_PATH" ]] && rm -f "$TEMPLATE_PATH" + for attempt in {1..3}; do + msg_info "Attempt $attempt: Downloading template $TEMPLATE to $TEMPLATE_STORAGE" + if pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null 2>&1; then + msg_ok "Template download successful." + break + fi + if [[ $attempt -eq 3 ]]; then + msg_error "Failed after 3 attempts. Please check network access, permissions, or manually run:\n pveam download $TEMPLATE_STORAGE $TEMPLATE" + exit 222 + fi + sleep $((attempt * 5)) + done + fi + + if ! pveam list "$TEMPLATE_STORAGE" 2>/dev/null | grep -q "$TEMPLATE"; then + msg_error "Template $TEMPLATE not available in storage $TEMPLATE_STORAGE after download." + exit 223 + fi + + # ------------------------------------------------------------------------------ + # Dynamic preflight for Debian 13.x: offer upgrade if available (no hard mins) + # ------------------------------------------------------------------------------ + if [[ "$PCT_OSTYPE" == "debian" ]]; then + OSVER="$(parse_template_osver "$TEMPLATE")" + if [[ -n "$OSVER" ]]; then + # Proactive, aber ohne Abbruch – nur Angebot + offer_lxc_stack_upgrade_and_maybe_retry "no" || true + fi + fi + + # ------------------------------------------------------------------------------ + # Create LXC Container + # ------------------------------------------------------------------------------ + msg_info "Creating LXC container" + + # Ensure subuid/subgid entries exist + grep -q "root:100000:65536" /etc/subuid || echo "root:100000:65536" >>/etc/subuid + grep -q "root:100000:65536" /etc/subgid || echo "root:100000:65536" >>/etc/subgid + + # Assemble pct options + PCT_OPTIONS=(${PCT_OPTIONS[@]:-${DEFAULT_PCT_OPTIONS[@]}}) + [[ " ${PCT_OPTIONS[*]} " =~ " -rootfs " ]] || PCT_OPTIONS+=(-rootfs "$CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}") + + # Lock by template file (avoid concurrent downloads/creates) + lockfile="/tmp/template.${TEMPLATE}.lock" + exec 9>"$lockfile" || { + msg_error "Failed to create lock file '$lockfile'." + exit 200 + } + flock -w 60 9 || { + msg_error "Timeout while waiting for template lock." + exit 211 + } + + LOGFILE="/tmp/pct_create_${CTID}.log" + msg_debug "pct create command: pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" + msg_debug "Logfile: $LOGFILE" + + # First attempt + if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >"$LOGFILE" 2>&1; then + msg_error "Container creation failed on ${TEMPLATE_STORAGE}. Checking template..." + + # Validate template file + if [[ ! -s "$TEMPLATE_PATH" || "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then + msg_warn "Template file too small or missing – re-downloading." + rm -f "$TEMPLATE_PATH" + pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" + elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then + if [[ -n "$ONLINE_TEMPLATE" ]]; then + msg_warn "Template appears corrupted – re-downloading." + rm -f "$TEMPLATE_PATH" + pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" + else + msg_warn "Template appears corrupted, but no online version exists. Skipping re-download." + fi + fi + + # Retry after repair + if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then + # Fallback to local storage + if [[ "$TEMPLATE_STORAGE" != "local" ]]; then + msg_warn "Retrying container creation with fallback to local storage..." + LOCAL_TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" + if [[ ! -f "$LOCAL_TEMPLATE_PATH" ]]; then + msg_info "Downloading template to local..." + pveam download local "$TEMPLATE" >/dev/null 2>&1 + fi + if pct create "$CTID" "local:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then + msg_ok "Container successfully created using local fallback." + else + # --- Dynamic stack upgrade + auto-retry on the well-known error pattern --- + if grep -qiE 'unsupported .* version' "$LOGFILE"; then + echo + echo "pct reported 'unsupported ... version' – your LXC stack might be too old for this template." + echo "We can try to upgrade 'pve-container' and 'lxc-pve' now and retry automatically." + offer_lxc_stack_upgrade_and_maybe_retry "yes" + rc=$? + case $rc in + 0) : ;; # success - container created, continue + 2) + echo "Upgrade was declined. Please update and re-run: + apt update && apt install --only-upgrade pve-container lxc-pve" + exit 231 + ;; + 3) + echo "Upgrade and/or retry failed. Please inspect: $LOGFILE" + exit 231 + ;; + esac + else + msg_error "Container creation failed even with local fallback. See $LOGFILE" + if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then + set -x + bash -x -c "pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" 2>&1 | tee -a "$LOGFILE" + set +x + fi + exit 209 + fi + fi + else + msg_error "Container creation failed on local storage. See $LOGFILE" + # --- Dynamic stack upgrade + auto-retry on the well-known error pattern --- + if grep -qiE 'unsupported .* version' "$LOGFILE"; then + echo + echo "pct reported 'unsupported ... version' – your LXC stack might be too old for this template." + echo "We can try to upgrade 'pve-container' and 'lxc-pve' now and retry automatically." + offer_lxc_stack_upgrade_and_maybe_retry "yes" + rc=$? + case $rc in + 0) : ;; # success - container created, continue + 2) + echo "Upgrade was declined. Please update and re-run: + apt update && apt install --only-upgrade pve-container lxc-pve" + exit 231 + ;; + 3) + echo "Upgrade and/or retry failed. Please inspect: $LOGFILE" + exit 231 + ;; + esac + else + msg_error "Container creation failed. See $LOGFILE" + if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then + set -x + bash -x -c "pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" 2>&1 | tee -a "$LOGFILE" + set +x + fi + exit 209 + fi + fi + fi + fi + + # Verify container exists + pct list | awk '{print $1}' | grep -qx "$CTID" || { + msg_error "Container ID $CTID not listed in 'pct list'. See $LOGFILE" + exit 215 + } + + # Verify config rootfs + grep -q '^rootfs:' "/etc/pve/lxc/$CTID.conf" || { + msg_error "RootFS entry missing in container config. See $LOGFILE" + exit 216 + } + + msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created." +} + +# ------------------------------------------------------------------------------ +# description() +# +# - Sets container description with HTML content (logo, links, badges) +# - Restarts ping-instances.service if present +# - Posts status "done" to API +# ------------------------------------------------------------------------------ +description() { + IP=$(pct exec "$CTID" ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) + + # Generate LXC Description + DESCRIPTION=$( + cat < + + Logo + + +

${APP} LXC

+ +

+ + spend Coffee + +

+ + + + GitHub + + + + Discussions + + + + Issues + + +EOF + ) + pct set "$CTID" -description "$DESCRIPTION" + + if [[ -f /etc/systemd/system/ping-instances.service ]]; then + systemctl start ping-instances.service + fi + + post_update_to_api "done" "none" +} + +# ------------------------------------------------------------------------------ +# api_exit_script() +# +# - Exit trap handler +# - Reports exit codes to API with detailed reason +# - Handles known codes (100–209) and maps them to errors +# ------------------------------------------------------------------------------ +api_exit_script() { + exit_code=$? + if [ $exit_code -ne 0 ]; then + case $exit_code in + 100) post_update_to_api "failed" "100: Unexpected error in create_lxc.sh" ;; + 101) post_update_to_api "failed" "101: No network connection detected in create_lxc.sh" ;; + 200) post_update_to_api "failed" "200: LXC creation failed in create_lxc.sh" ;; + 201) post_update_to_api "failed" "201: Invalid Storage class in create_lxc.sh" ;; + 202) post_update_to_api "failed" "202: User aborted menu in create_lxc.sh" ;; + 203) post_update_to_api "failed" "203: CTID not set in create_lxc.sh" ;; + 204) post_update_to_api "failed" "204: PCT_OSTYPE not set in create_lxc.sh" ;; + 205) post_update_to_api "failed" "205: CTID cannot be less than 100 in create_lxc.sh" ;; + 206) post_update_to_api "failed" "206: CTID already in use in create_lxc.sh" ;; + 207) post_update_to_api "failed" "207: Template not found in create_lxc.sh" ;; + 208) post_update_to_api "failed" "208: Error downloading template in create_lxc.sh" ;; + 209) post_update_to_api "failed" "209: Container creation failed, but template is intact in create_lxc.sh" ;; + *) post_update_to_api "failed" "Unknown error, exit code: $exit_code in create_lxc.sh" ;; + esac + fi +} + +if command -v pveversion >/dev/null 2>&1; then + trap 'api_exit_script' EXIT +fi +trap 'post_update_to_api "failed" "$BASH_COMMAND"' ERR +trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT +trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM diff --git a/misc/build.func.backup-20251029-124205 b/misc/build.func.backup-20251029-124205 new file mode 100644 index 000000000..7e0556d61 --- /dev/null +++ b/misc/build.func.backup-20251029-124205 @@ -0,0 +1,3516 @@ +#!/usr/bin/env bash +# Copyright (c) 2021-2025 community-scripts ORG +# Author: tteck (tteckster) | MickLesk | michelroegl-brunner +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Revision: 1 + +# ============================================================================== +# SECTION 1: CORE INITIALIZATION & VARIABLES +# ============================================================================== + +# ------------------------------------------------------------------------------ +# variables() +# +# - Normalize application name (NSAPP = lowercase, no spaces) +# - Build installer filename (var_install) +# - Define regex for integer validation +# - Fetch hostname of Proxmox node +# - Set default values for diagnostics/method +# - Generate random UUID for tracking +# - Get Proxmox VE version and kernel version +# ------------------------------------------------------------------------------ +variables() { + NSAPP=$(echo "${APP,,}" | tr -d ' ') # This function sets the NSAPP variable by converting the value of the APP variable to lowercase and removing any spaces. + var_install="${NSAPP}-install" # sets the var_install variable by appending "-install" to the value of NSAPP. + INTEGER='^[0-9]+([.][0-9]+)?$' # it defines the INTEGER regular expression pattern. + PVEHOST_NAME=$(hostname) # gets the Proxmox Hostname and sets it to Uppercase + DIAGNOSTICS="yes" # sets the DIAGNOSTICS variable to "yes", used for the API call. + METHOD="default" # sets the METHOD variable to "default", used for the API call. + RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" # generates a random UUID and sets it to the RANDOM_UUID variable. + CTTYPE="${CTTYPE:-${CT_TYPE:-1}}" + #CT_TYPE=${var_unprivileged:-$CT_TYPE} + + # Get Proxmox VE version and kernel version + if command -v pveversion >/dev/null 2>&1; then + PVEVERSION=$(pveversion | grep "pve-manager" | awk '{print $2}' | cut -d'/' -f1) + else + PVEVERSION="N/A" + fi + KERNEL_VERSION=$(uname -r) +} + +# ----------------------------------------------------------------------------- +# Community-Scripts bootstrap loader +# - Always sources build.func from remote +# - Updates local core files only if build.func changed +# - Local cache: /usr/local/community-scripts/core +# ----------------------------------------------------------------------------- + +# FUNC_DIR="/usr/local/community-scripts/core" +# mkdir -p "$FUNC_DIR" + +# BUILD_URL="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func" +# BUILD_REV="$FUNC_DIR/build.rev" +# DEVMODE="${DEVMODE:-no}" + +# # --- Step 1: fetch build.func content once, compute hash --- +# build_content="$(curl -fsSL "$BUILD_URL")" || { +# echo "❌ Failed to fetch build.func" +# exit 1 +# } + +# newhash=$(printf "%s" "$build_content" | sha256sum | awk '{print $1}') +# oldhash=$(cat "$BUILD_REV" 2>/dev/null || echo "") + +# # --- Step 2: if build.func changed, offer update for core files --- +# if [ "$newhash" != "$oldhash" ]; then +# echo "⚠️ build.func changed!" + +# while true; do +# read -rp "Refresh local core files? [y/N/diff]: " ans +# case "$ans" in +# [Yy]*) +# echo "$newhash" >"$BUILD_REV" + +# update_func_file() { +# local file="$1" +# local url="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/$file" +# local local_path="$FUNC_DIR/$file" + +# echo "⬇️ Downloading $file ..." +# curl -fsSL "$url" -o "$local_path" || { +# echo "❌ Failed to fetch $file" +# exit 1 +# } +# echo "✔️ Updated $file" +# } + +# update_func_file core.func +# update_func_file error_handler.func +# update_func_file tools.func +# break +# ;; +# [Dd]*) +# for file in core.func error_handler.func tools.func; do +# local_path="$FUNC_DIR/$file" +# url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/$file" +# remote_tmp="$(mktemp)" + +# curl -fsSL "$url" -o "$remote_tmp" || continue + +# if [ -f "$local_path" ]; then +# echo "🔍 Diff for $file:" +# diff -u "$local_path" "$remote_tmp" || echo "(no differences)" +# else +# echo "📦 New file $file will be installed" +# fi + +# rm -f "$remote_tmp" +# done +# ;; +# *) +# echo "❌ Skipped updating local core files" +# break +# ;; +# esac +# done +# else +# if [ "$DEVMODE" != "yes" ]; then +# echo "✔️ build.func unchanged → using existing local core files" +# fi +# fi + +# if [ -n "${_COMMUNITY_SCRIPTS_LOADER:-}" ]; then +# return 0 2>/dev/null || exit 0 +# fi +# _COMMUNITY_SCRIPTS_LOADER=1 + +# # --- Step 3: always source local versions of the core files --- +# source "$FUNC_DIR/core.func" +# source "$FUNC_DIR/error_handler.func" +# source "$FUNC_DIR/tools.func" + +# # --- Step 4: finally, source build.func directly from memory --- +# # (no tmp file needed) +# source <(printf "%s" "$build_content") + +# ------------------------------------------------------------------------------ +# Load core + error handler functions from community-scripts repo +# +# - Prefer curl if available, fallback to wget +# - Load: core.func, error_handler.func, api.func +# - Initialize error traps after loading +# ------------------------------------------------------------------------------ + +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) + +if command -v curl >/dev/null 2>&1; then + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) + load_functions + catch_errors + #echo "(build.func) Loaded core.func via curl" +elif command -v wget >/dev/null 2>&1; then + source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) + load_functions + catch_errors + #echo "(build.func) Loaded core.func via wget" +fi + +# ------------------------------------------------------------------------------ +# maxkeys_check() +# +# - Reads kernel keyring limits (maxkeys, maxbytes) +# - Checks current usage for LXC user (UID 100000) +# - Warns if usage is close to limits and suggests sysctl tuning +# - Exits if thresholds are exceeded +# - https://cleveruptime.com/docs/files/proc-key-users | https://docs.kernel.org/security/keys/core.html +# ------------------------------------------------------------------------------ + +maxkeys_check() { + # Read kernel parameters + per_user_maxkeys=$(cat /proc/sys/kernel/keys/maxkeys 2>/dev/null || echo 0) + per_user_maxbytes=$(cat /proc/sys/kernel/keys/maxbytes 2>/dev/null || echo 0) + + # Exit if kernel parameters are unavailable + if [[ "$per_user_maxkeys" -eq 0 || "$per_user_maxbytes" -eq 0 ]]; then + echo -e "${CROSS}${RD} Error: Unable to read kernel parameters. Ensure proper permissions.${CL}" + exit 1 + fi + + # Fetch key usage for user ID 100000 (typical for containers) + used_lxc_keys=$(awk '/100000:/ {print $2}' /proc/key-users 2>/dev/null || echo 0) + used_lxc_bytes=$(awk '/100000:/ {split($5, a, "/"); print a[1]}' /proc/key-users 2>/dev/null || echo 0) + + # Calculate thresholds and suggested new limits + threshold_keys=$((per_user_maxkeys - 100)) + threshold_bytes=$((per_user_maxbytes - 1000)) + new_limit_keys=$((per_user_maxkeys * 2)) + new_limit_bytes=$((per_user_maxbytes * 2)) + + # Check if key or byte usage is near limits + failure=0 + if [[ "$used_lxc_keys" -gt "$threshold_keys" ]]; then + echo -e "${CROSS}${RD} Warning: Key usage is near the limit (${used_lxc_keys}/${per_user_maxkeys}).${CL}" + echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxkeys=${new_limit_keys}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." + failure=1 + fi + if [[ "$used_lxc_bytes" -gt "$threshold_bytes" ]]; then + echo -e "${CROSS}${RD} Warning: Key byte usage is near the limit (${used_lxc_bytes}/${per_user_maxbytes}).${CL}" + echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxbytes=${new_limit_bytes}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." + failure=1 + fi + + # Provide next steps if issues are detected + if [[ "$failure" -eq 1 ]]; then + echo -e "${INFO} To apply changes, run: ${BOLD}service procps force-reload${CL}" + exit 1 + fi + + echo -e "${CM}${GN} All kernel key limits are within safe thresholds.${CL}" +} + +# ------------------------------------------------------------------------------ +# get_current_ip() +# +# - Returns current container IP depending on OS type +# - Debian/Ubuntu: uses `hostname -I` +# - Alpine: parses eth0 via `ip -4 addr` +# ------------------------------------------------------------------------------ +get_current_ip() { + if [ -f /etc/os-release ]; then + # Check for Debian/Ubuntu (uses hostname -I) + if grep -qE 'ID=debian|ID=ubuntu' /etc/os-release; then + CURRENT_IP=$(hostname -I | awk '{print $1}') + # Check for Alpine (uses ip command) + elif grep -q 'ID=alpine' /etc/os-release; then + CURRENT_IP=$(ip -4 addr show eth0 | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) + else + CURRENT_IP="Unknown" + fi + fi + echo "$CURRENT_IP" +} + +# ------------------------------------------------------------------------------ +# update_motd_ip() +# +# - Updates /etc/motd with current container IP +# - Removes old IP entries to avoid duplicates +# ------------------------------------------------------------------------------ +update_motd_ip() { + MOTD_FILE="/etc/motd" + + if [ -f "$MOTD_FILE" ]; then + # Remove existing IP Address lines to prevent duplication + sed -i '/IP Address:/d' "$MOTD_FILE" + + IP=$(get_current_ip) + # Add the new IP address + echo -e "${TAB}${NETWORK}${YW} IP Address: ${GN}${IP}${CL}" >>"$MOTD_FILE" + fi +} + +# ------------------------------------------------------------------------------ +# install_ssh_keys_into_ct() +# +# - Installs SSH keys into container root account if SSH is enabled +# - Uses pct push or direct input to authorized_keys +# - Falls back to warning if no keys provided +# ------------------------------------------------------------------------------ +install_ssh_keys_into_ct() { + [[ "$SSH" != "yes" ]] && return 0 + + if [[ -n "$SSH_KEYS_FILE" && -s "$SSH_KEYS_FILE" ]]; then + msg_info "Installing selected SSH keys into CT ${CTID}" + pct exec "$CTID" -- sh -c 'mkdir -p /root/.ssh && chmod 700 /root/.ssh' || { + msg_error "prepare /root/.ssh failed" + return 1 + } + pct push "$CTID" "$SSH_KEYS_FILE" /root/.ssh/authorized_keys >/dev/null 2>&1 || + pct exec "$CTID" -- sh -c "cat > /root/.ssh/authorized_keys" <"$SSH_KEYS_FILE" || { + msg_error "write authorized_keys failed" + return 1 + } + pct exec "$CTID" -- sh -c 'chmod 600 /root/.ssh/authorized_keys' || true + msg_ok "Installed SSH keys into CT ${CTID}" + return 0 + fi + + # Fallback: nichts ausgewählt + msg_warn "No SSH keys to install (skipping)." + return 0 +} + +# ------------------------------------------------------------------------------ +# base_settings() +# +# - Defines all base/default variables for container creation +# - Reads from environment variables (var_*) +# - Provides fallback defaults for OS type/version +# ------------------------------------------------------------------------------ +base_settings() { + # Default Settings + CT_TYPE=${var_unprivileged:-"1"} + DISK_SIZE=${var_disk:-"4"} + CORE_COUNT=${var_cpu:-"1"} + RAM_SIZE=${var_ram:-"1024"} + VERBOSE=${var_verbose:-"${1:-no}"} + PW=${var_pw:-""} + CT_ID=${var_ctid:-$NEXTID} + HN=${var_hostname:-$NSAPP} + BRG=${var_brg:-"vmbr0"} + NET=${var_net:-"dhcp"} + IPV6_METHOD=${var_ipv6_method:-"none"} + IPV6_STATIC=${var_ipv6_static:-""} + GATE=${var_gateway:-""} + APT_CACHER=${var_apt_cacher:-""} + APT_CACHER_IP=${var_apt_cacher_ip:-""} + MTU=${var_mtu:-""} + SD=${var_storage:-""} + NS=${var_ns:-""} + MAC=${var_mac:-""} + VLAN=${var_vlan:-""} + SSH=${var_ssh:-"no"} + SSH_AUTHORIZED_KEY=${var_ssh_authorized_key:-""} + UDHCPC_FIX=${var_udhcpc_fix:-""} + TAGS="community-script,${var_tags:-}" + ENABLE_FUSE=${var_fuse:-"${1:-no}"} + ENABLE_TUN=${var_tun:-"${1:-no}"} + + # Since these 2 are only defined outside of default_settings function, we add a temporary fallback. TODO: To align everything, we should add these as constant variables (e.g. OSTYPE and OSVERSION), but that would currently require updating the default_settings function for all existing scripts + if [ -z "$var_os" ]; then + var_os="debian" + fi + if [ -z "$var_version" ]; then + var_version="12" + fi +} + +# ------------------------------------------------------------------------------ +# echo_default() +# +# - Prints summary of default values (ID, OS, type, disk, RAM, CPU, etc.) +# - Uses icons and formatting for readability +# - Convert CT_TYPE to description +# ------------------------------------------------------------------------------ +echo_default() { + CT_TYPE_DESC="Unprivileged" + if [ "$CT_TYPE" -eq 0 ]; then + CT_TYPE_DESC="Privileged" + fi + echo -e "${INFO}${BOLD}${DGN}PVE Version ${PVEVERSION} (Kernel: ${KERNEL_VERSION}${CL}" + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}${CT_ID}${CL}" + echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os ($var_version)${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + if [ "$VERBOSE" == "yes" ]; then + echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}Enabled${CL}" + fi + echo -e "${CREATING}${BOLD}${BL}Creating a ${APP} LXC using the above default settings${CL}" + echo -e " " +} + +# ------------------------------------------------------------------------------ +# exit_script() +# +# - Called when user cancels an action +# - Clears screen and exits gracefully +# ------------------------------------------------------------------------------ +exit_script() { + clear + echo -e "\n${CROSS}${RD}User exited script${CL}\n" + exit +} + +# ------------------------------------------------------------------------------ +# find_host_ssh_keys() +# +# - Scans system for available SSH keys +# - Supports defaults (~/.ssh, /etc/ssh/authorized_keys) +# - Returns list of files containing valid SSH public keys +# - Sets FOUND_HOST_KEY_COUNT to number of keys found +# ------------------------------------------------------------------------------ +find_host_ssh_keys() { + local re='(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))' + local -a files=() cand=() + local g="${var_ssh_import_glob:-}" + local total=0 f base c + + shopt -s nullglob + if [[ -n "$g" ]]; then + for pat in $g; do cand+=($pat); done + else + cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2) + cand+=(/root/.ssh/*.pub) + cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*) + fi + shopt -u nullglob + + for f in "${cand[@]}"; do + [[ -f "$f" && -r "$f" ]] || continue + base="$(basename -- "$f")" + case "$base" in + known_hosts | known_hosts.* | config) continue ;; + id_*) [[ "$f" != *.pub ]] && continue ;; + esac + + # CRLF safe check for host keys + c=$(tr -d '\r' <"$f" | awk ' + /^[[:space:]]*#/ {next} + /^[[:space:]]*$/ {next} + {print} + ' | grep -E -c '"$re"' || true) + + if ((c > 0)); then + files+=("$f") + total=$((total + c)) + fi + done + + # Fallback to /root/.ssh/authorized_keys + if ((${#files[@]} == 0)) && [[ -r /root/.ssh/authorized_keys ]]; then + if grep -E -q "$re" /root/.ssh/authorized_keys; then + files+=(/root/.ssh/authorized_keys) + total=$((total + $(grep -E -c "$re" /root/.ssh/authorized_keys || echo 0))) + fi + fi + + FOUND_HOST_KEY_COUNT="$total" + ( + IFS=: + echo "${files[*]}" + ) +} + +# ------------------------------------------------------------------------------ +# advanced_settings() +# +# - Interactive whiptail menu for advanced configuration +# - Lets user set container type, password, CT ID, hostname, disk, CPU, RAM +# - Supports IPv4/IPv6, DNS, MAC, VLAN, tags, SSH keys, FUSE, verbose mode +# - Ends with confirmation or re-entry if cancelled +# ------------------------------------------------------------------------------ +advanced_settings() { + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox --title "Here is an instructional tip:" "To make a selection, use the Spacebar." 8 58 + # Setting Default Tag for Advanced Settings + TAGS="community-script;${var_tags:-}" + CT_DEFAULT_TYPE="${CT_TYPE}" + CT_TYPE="" + while [ -z "$CT_TYPE" ]; do + if [ "$CT_DEFAULT_TYPE" == "1" ]; then + if CT_TYPE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ + "1" "Unprivileged" ON \ + "0" "Privileged" OFF \ + 3>&1 1>&2 2>&3); then + if [ -n "$CT_TYPE" ]; then + CT_TYPE_DESC="Unprivileged" + if [ "$CT_TYPE" -eq 0 ]; then + CT_TYPE_DESC="Privileged" + fi + echo -e "${TAB}${INFO} ProxmoxVE Version ${PVEVERSION} | Kernel: ${KERNEL_VERSION}${CL}" + echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os |${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" + fi + else + exit_script + fi + fi + if [ "$CT_DEFAULT_TYPE" == "0" ]; then + if CT_TYPE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ + "1" "Unprivileged" OFF \ + "0" "Privileged" ON \ + 3>&1 1>&2 2>&3); then + if [ -n "$CT_TYPE" ]; then + CT_TYPE_DESC="Unprivileged" + if [ "$CT_TYPE" -eq 0 ]; then + CT_TYPE_DESC="Privileged" + fi + echo -e "${TAB}${INFO} ProxmoxVE Version ${PVEVERSION} | Kernel: ${KERNEL_VERSION}${CL}" + echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os${CL}" + echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" + fi + else + exit_script + fi + fi + done + + while true; do + if PW1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --passwordbox "\nSet Root Password (needed for root ssh access)" 9 58 --title "PASSWORD (leave blank for automatic login)" 3>&1 1>&2 2>&3); then + # Empty = Autologin + if [[ -z "$PW1" ]]; then + PW="" + PW1="Automatic Login" + echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}$PW1${CL}" + break + fi + + # Invalid: contains spaces + if [[ "$PW1" == *" "* ]]; then + whiptail --msgbox "Password cannot contain spaces." 8 58 + continue + fi + + # Invalid: too short + if ((${#PW1} < 5)); then + whiptail --msgbox "Password must be at least 5 characters." 8 58 + continue + fi + + # Confirm password + if PW2=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then + if [[ "$PW1" == "$PW2" ]]; then + PW="-password $PW1" + echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}" + break + else + whiptail --msgbox "Passwords do not match. Please try again." 8 58 + fi + else + exit_script + fi + else + exit_script + fi + done + + if CT_ID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Container ID" 8 58 "$NEXTID" --title "CONTAINER ID" 3>&1 1>&2 2>&3); then + if [ -z "$CT_ID" ]; then + CT_ID="$NEXTID" + fi + else + exit_script + fi + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" + + while true; do + if CT_NAME=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 "$NSAPP" --title "HOSTNAME" 3>&1 1>&2 2>&3); then + if [ -z "$CT_NAME" ]; then + HN="$NSAPP" + else + HN=$(echo "${CT_NAME,,}" | tr -d ' ') + fi + # Hostname validate (RFC 1123) + if [[ "$HN" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ ]]; then + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" + break + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --msgbox "❌ Invalid hostname: '$HN'\n\nOnly lowercase letters, digits and hyphens (-) are allowed.\nUnderscores (_) or other characters are not permitted!" 10 70 + fi + else + exit_script + fi + done + + while true; do + DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GB" 8 58 "$var_disk" --title "DISK SIZE" 3>&1 1>&2 2>&3) || exit_script + + if [ -z "$DISK_SIZE" ]; then + DISK_SIZE="$var_disk" + fi + + if [[ "$DISK_SIZE" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" + break + else + whiptail --msgbox "Disk size must be a positive integer!" 8 58 + fi + done + + while true; do + CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --inputbox "Allocate CPU Cores" 8 58 "$var_cpu" --title "CORE COUNT" 3>&1 1>&2 2>&3) || exit_script + + if [ -z "$CORE_COUNT" ]; then + CORE_COUNT="$var_cpu" + fi + + if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + break + else + whiptail --msgbox "CPU core count must be a positive integer!" 8 58 + fi + done + + while true; do + RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --inputbox "Allocate RAM in MiB" 8 58 "$var_ram" --title "RAM" 3>&1 1>&2 2>&3) || exit_script + + if [ -z "$RAM_SIZE" ]; then + RAM_SIZE="$var_ram" + fi + + if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + break + else + whiptail --msgbox "RAM size must be a positive integer!" 8 58 + fi + done + + IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f) + BRIDGES="" + OLD_IFS=$IFS + IFS=$'\n' + for iface_filepath in ${IFACE_FILEPATH_LIST}; do + + iface_indexes_tmpfile=$(mktemp -q -u '.iface-XXXX') + (grep -Pn '^\s*iface' "${iface_filepath}" | cut -d':' -f1 && wc -l "${iface_filepath}" | cut -d' ' -f1) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' >"${iface_indexes_tmpfile}" || true + + if [ -f "${iface_indexes_tmpfile}" ]; then + + while read -r pair; do + start=$(echo "${pair}" | cut -d':' -f1) + end=$(echo "${pair}" | cut -d':' -f2) + + if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then + iface_name=$(sed "${start}q;d" "${iface_filepath}" | awk '{print $2}') + BRIDGES="${iface_name}"$'\n'"${BRIDGES}" + fi + + done <"${iface_indexes_tmpfile}" + rm -f "${iface_indexes_tmpfile}" + fi + + done + IFS=$OLD_IFS + BRIDGES=$(echo "$BRIDGES" | grep -v '^\s*$' | sort | uniq) + if [[ -z "$BRIDGES" ]]; then + BRG="vmbr0" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + else + # Build bridge menu with descriptions + BRIDGE_MENU_OPTIONS=() + while IFS= read -r bridge; do + if [[ -n "$bridge" ]]; then + # Get description from Proxmox built-in method - find comment for this specific bridge + description=$(grep -A 10 "iface $bridge" /etc/network/interfaces | grep '^#' | head -n1 | sed 's/^#\s*//') + if [[ -n "$description" ]]; then + BRIDGE_MENU_OPTIONS+=("$bridge" "${description}") + else + BRIDGE_MENU_OPTIONS+=("$bridge" " ") + fi + fi + done <<<"$BRIDGES" + + BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge: " 18 55 6 "${BRIDGE_MENU_OPTIONS[@]}" 3>&1 1>&2 2>&3) + if [[ -z "$BRG" ]]; then + exit_script + else + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + fi + fi + + # IPv4 methods: dhcp, static, none + while true; do + IPV4_METHOD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "IPv4 Address Management" \ + --menu "Select IPv4 Address Assignment Method:" 12 60 2 \ + "dhcp" "Automatic (DHCP, recommended)" \ + "static" "Static (manual entry)" \ + 3>&1 1>&2 2>&3) + + exit_status=$? + if [ $exit_status -ne 0 ]; then + exit_script + fi + + case "$IPV4_METHOD" in + dhcp) + NET="dhcp" + GATE="" + echo -e "${NETWORK}${BOLD}${DGN}IPv4: DHCP${CL}" + break + ;; + static) + # Static: call and validate CIDR address + while true; do + NET=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Enter Static IPv4 CIDR Address (e.g. 192.168.100.50/24)" 8 58 "" \ + --title "IPv4 ADDRESS" 3>&1 1>&2 2>&3) + if [ -z "$NET" ]; then + whiptail --msgbox "IPv4 address must not be empty." 8 58 + continue + elif [[ "$NET" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then + echo -e "${NETWORK}${BOLD}${DGN}IPv4 Address: ${BGN}$NET${CL}" + break + else + whiptail --msgbox "$NET is not a valid IPv4 CIDR address. Please enter a correct value!" 8 58 + fi + done + + # call and validate Gateway + while true; do + GATE1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Enter Gateway IP address for static IPv4" 8 58 "" \ + --title "Gateway IP" 3>&1 1>&2 2>&3) + if [ -z "$GATE1" ]; then + whiptail --msgbox "Gateway IP address cannot be empty." 8 58 + elif [[ ! "$GATE1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then + whiptail --msgbox "Invalid Gateway IP address format." 8 58 + else + GATE=",gw=$GATE1" + echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE1${CL}" + break + fi + done + break + ;; + esac + done + + # IPv6 Address Management selection + while true; do + IPV6_METHOD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --menu \ + "Select IPv6 Address Management Type:" 15 58 4 \ + "auto" "SLAAC/AUTO (recommended, default)" \ + "dhcp" "DHCPv6" \ + "static" "Static (manual entry)" \ + "none" "Disabled" \ + --default-item "auto" 3>&1 1>&2 2>&3) + [ $? -ne 0 ] && exit_script + + case "$IPV6_METHOD" in + auto) + echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}SLAAC/AUTO${CL}" + IPV6_ADDR="" + IPV6_GATE="" + break + ;; + dhcp) + echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}DHCPv6${CL}" + IPV6_ADDR="dhcp" + IPV6_GATE="" + break + ;; + static) + # Ask for static IPv6 address (CIDR notation, e.g., 2001:db8::1234/64) + while true; do + IPV6_ADDR=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox \ + "Set a static IPv6 CIDR address (e.g., 2001:db8::1234/64)" 8 58 "" \ + --title "IPv6 STATIC ADDRESS" 3>&1 1>&2 2>&3) || exit_script + if [[ "$IPV6_ADDR" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+(/[0-9]{1,3})$ ]]; then + echo -e "${NETWORK}${BOLD}${DGN}IPv6 Address: ${BGN}$IPV6_ADDR${CL}" + break + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox \ + "$IPV6_ADDR is an invalid IPv6 CIDR address. Please enter a valid IPv6 CIDR address (e.g., 2001:db8::1234/64)" 8 58 + fi + done + # Optional: ask for IPv6 gateway for static config + while true; do + IPV6_GATE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox \ + "Enter IPv6 gateway address (optional, leave blank for none)" 8 58 "" --title "IPv6 GATEWAY" 3>&1 1>&2 2>&3) + if [ -z "$IPV6_GATE" ]; then + IPV6_GATE="" + break + elif [[ "$IPV6_GATE" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+$ ]]; then + break + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox \ + "Invalid IPv6 gateway format." 8 58 + fi + done + break + ;; + none) + echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}Disabled${CL}" + IPV6_ADDR="none" + IPV6_GATE="" + break + ;; + *) + exit_script + ;; + esac + done + + if [ "$var_os" == "alpine" ]; then + APT_CACHER="" + APT_CACHER_IP="" + else + if APT_CACHER_IP=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set APT-Cacher IP (leave blank for none)" 8 58 --title "APT-Cacher IP" 3>&1 1>&2 2>&3); then + APT_CACHER="${APT_CACHER_IP:+yes}" + echo -e "${NETWORK}${BOLD}${DGN}APT-Cacher IP Address: ${BGN}${APT_CACHER_IP:-Default}${CL}" + else + exit_script + fi + fi + + # if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "IPv6" --yesno "Disable IPv6?" 10 58); then + # DISABLEIP6="yes" + # else + # DISABLEIP6="no" + # fi + # echo -e "${DISABLEIPV6}${BOLD}${DGN}Disable IPv6: ${BGN}$DISABLEIP6${CL}" + + if MTU1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default [The MTU of your selected vmbr, default is 1500])" 8 58 --title "MTU SIZE" 3>&1 1>&2 2>&3); then + if [ -z "$MTU1" ]; then + MTU1="Default" + MTU="" + else + MTU=",mtu=$MTU1" + fi + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" + else + exit_script + fi + + if SD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a DNS Search Domain (leave blank for HOST)" 8 58 --title "DNS Search Domain" 3>&1 1>&2 2>&3); then + if [ -z "$SD" ]; then + SX=Host + SD="" + else + SX=$SD + SD="-searchdomain=$SD" + fi + echo -e "${SEARCH}${BOLD}${DGN}DNS Search Domain: ${BGN}$SX${CL}" + else + exit_script + fi + + if NX=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a DNS Server IP (leave blank for HOST)" 8 58 --title "DNS SERVER IP" 3>&1 1>&2 2>&3); then + if [ -z "$NX" ]; then + NX=Host + NS="" + else + NS="-nameserver=$NX" + fi + echo -e "${NETWORK}${BOLD}${DGN}DNS Server IP Address: ${BGN}$NX${CL}" + else + exit_script + fi + + if [ "$var_os" == "alpine" ] && [ "$NET" == "dhcp" ] && [ "$NX" != "Host" ]; then + UDHCPC_FIX="yes" + else + UDHCPC_FIX="no" + fi + export UDHCPC_FIX + + if MAC1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a MAC Address(leave blank for generated MAC)" 8 58 --title "MAC ADDRESS" 3>&1 1>&2 2>&3); then + if [ -z "$MAC1" ]; then + MAC1="Default" + MAC="" + else + MAC=",hwaddr=$MAC1" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" + fi + else + exit_script + fi + + if VLAN1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for no VLAN)" 8 58 --title "VLAN" 3>&1 1>&2 2>&3); then + if [ -z "$VLAN1" ]; then + VLAN1="Default" + VLAN="" + else + VLAN=",tag=$VLAN1" + fi + echo -e "${VLANTAG}${BOLD}${DGN}Vlan: ${BGN}$VLAN1${CL}" + else + exit_script + fi + + if ADV_TAGS=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Custom Tags?[If you remove all, there will be no tags!]" 8 58 "${TAGS}" --title "Advanced Tags" 3>&1 1>&2 2>&3); then + if [ -n "${ADV_TAGS}" ]; then + ADV_TAGS=$(echo "$ADV_TAGS" | tr -d '[:space:]') + TAGS="${ADV_TAGS}" + else + TAGS=";" + fi + echo -e "${NETWORK}${BOLD}${DGN}Tags: ${BGN}$TAGS${CL}" + else + exit_script + fi + + configure_ssh_settings + export SSH_KEYS_FILE + echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}" + if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "FUSE Support" --yesno "Enable FUSE support?\nRequired for tools like rclone, mergerfs, AppImage, etc." 10 58); then + ENABLE_FUSE="yes" + else + ENABLE_FUSE="no" + fi + echo -e "${FUSE}${BOLD}${DGN}Enable FUSE Support: ${BGN}$ENABLE_FUSE${CL}" + + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then + VERBOSE="yes" + else + VERBOSE="no" + fi + echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}" + + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create ${APP} LXC?" 10 58); then + echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}" + else + clear + header_info + echo -e "${TAB}${INFO} ProxmoxVE Version ${PVEVERSION} | Kernel: ${KERNEL_VERSION}${CL}" + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings on node $PVEHOST_NAME${CL}" + advanced_settings + fi +} + +# ------------------------------------------------------------------------------ +# diagnostics_check() +# +# - Ensures diagnostics config file exists at /usr/local/community-scripts/diagnostics +# - Asks user whether to send anonymous diagnostic data +# - Saves DIAGNOSTICS=yes/no in the config file +# ------------------------------------------------------------------------------ +diagnostics_check() { + if ! [ -d "/usr/local/community-scripts" ]; then + mkdir -p /usr/local/community-scripts + fi + + if ! [ -f "/usr/local/community-scripts/diagnostics" ]; then + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS" --yesno "Send Diagnostics of LXC Installation?\n\n(This only transmits data without user data, just RAM, CPU, LXC name, ...)" 10 58); then + cat </usr/local/community-scripts/diagnostics +DIAGNOSTICS=yes + +#This file is used to store the diagnostics settings for the Community-Scripts API. +#https://github.com/community-scripts/ProxmoxVED/discussions/1836 +#Your diagnostics will be sent to the Community-Scripts API for troubleshooting/statistical purposes. +#You can review the data at https://community-scripts.github.io/ProxmoxVE/data +#If you do not wish to send diagnostics, please set the variable 'DIAGNOSTICS' to "no" in /usr/local/community-scripts/diagnostics, or use the menue. +#This will disable the diagnostics feature. +#To send diagnostics, set the variable 'DIAGNOSTICS' to "yes" in /usr/local/community-scripts/diagnostics, or use the menue. +#This will enable the diagnostics feature. +#The following information will be sent: +#"disk_size" +#"core_count" +#"ram_size" +#"os_type" +#"os_version" +#"nsapp" +#"method" +#"pve_version" +#"status" +#If you have any concerns, please review the source code at /misc/build.func +EOF + DIAGNOSTICS="yes" + else + cat </usr/local/community-scripts/diagnostics +DIAGNOSTICS=no + +#This file is used to store the diagnostics settings for the Community-Scripts API. +#https://github.com/community-scripts/ProxmoxVED/discussions/1836 +#Your diagnostics will be sent to the Community-Scripts API for troubleshooting/statistical purposes. +#You can review the data at https://community-scripts.github.io/ProxmoxVE/data +#If you do not wish to send diagnostics, please set the variable 'DIAGNOSTICS' to "no" in /usr/local/community-scripts/diagnostics, or use the menue. +#This will disable the diagnostics feature. +#To send diagnostics, set the variable 'DIAGNOSTICS' to "yes" in /usr/local/community-scripts/diagnostics, or use the menue. +#This will enable the diagnostics feature. +#The following information will be sent: +#"disk_size" +#"core_count" +#"ram_size" +#"os_type" +#"os_version" +#"nsapp" +#"method" +#"pve_version" +#"status" +#If you have any concerns, please review the source code at /misc/build.func +EOF + DIAGNOSTICS="no" + fi + else + DIAGNOSTICS=$(awk -F '=' '/^DIAGNOSTICS/ {print $2}' /usr/local/community-scripts/diagnostics) + + fi + +} + +# ------------------------------------------------------------------------------ +# default_var_settings +# +# - Ensures /usr/local/community-scripts/default.vars exists (creates if missing) +# - Loads var_* values from default.vars (safe parser, no source/eval) +# - Precedence: ENV var_* > default.vars > built-in defaults +# - Maps var_verbose → VERBOSE +# - Calls base_settings "$VERBOSE" and echo_default +# ------------------------------------------------------------------------------ +default_var_settings() { + # Allowed var_* keys (alphabetically sorted) + local VAR_WHITELIST=( + var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_ctid var_disk var_fuse + var_gateway var_hostname var_ipv6_method var_ipv6_static var_mac var_mtu + var_net var_ns var_pw var_ram var_tags var_tun var_unprivileged + var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage + ) + + # Snapshot: environment variables (highest precedence) + declare -A _HARD_ENV=() + local _k + for _k in "${VAR_WHITELIST[@]}"; do + if printenv "$_k" >/dev/null 2>&1; then _HARD_ENV["$_k"]=1; fi + done + + # Find default.vars location + local _find_default_vars + _find_default_vars() { + local f + for f in \ + /usr/local/community-scripts/default.vars \ + "$HOME/.config/community-scripts/default.vars" \ + "./default.vars"; do + [ -f "$f" ] && { + echo "$f" + return 0 + } + done + return 1 + } + # Allow override of storages via env (for non-interactive use cases) + [ -n "${var_template_storage:-}" ] && TEMPLATE_STORAGE="$var_template_storage" + [ -n "${var_container_storage:-}" ] && CONTAINER_STORAGE="$var_container_storage" + + # Create once, with storages already selected, no var_ctid/var_hostname lines + local _ensure_default_vars + _ensure_default_vars() { + _find_default_vars >/dev/null 2>&1 && return 0 + + local canonical="/usr/local/community-scripts/default.vars" + msg_info "No default.vars found. Creating ${canonical}" + mkdir -p /usr/local/community-scripts + + # Pick storages before writing the file (always ask unless only one) + # Create a minimal temp file to write into + : >"$canonical" + + # Base content (no var_ctid / var_hostname here) + cat >"$canonical" <<'EOF' +# Community-Scripts defaults (var_* only). Lines starting with # are comments. +# Precedence: ENV var_* > default.vars > built-ins. +# Keep keys alphabetically sorted. + +# Container type +var_unprivileged=1 + +# Resources +var_cpu=1 +var_disk=4 +var_ram=1024 + +# Network +var_brg=vmbr0 +var_net=dhcp +var_ipv6_method=none +# var_gateway= +# var_ipv6_static= +# var_vlan= +# var_mtu= +# var_mac= +# var_ns= + +# SSH +var_ssh=no +# var_ssh_authorized_key= + +# APT cacher (optional) +# var_apt_cacher=yes +# var_apt_cacher_ip=192.168.1.10 + +# Features/Tags/verbosity +var_fuse=no +var_tun=no +var_tags=community-script +var_verbose=no + +# Security (root PW) – empty => autologin +# var_pw= +EOF + + # Now choose storages (always prompt unless just one exists) + choose_and_set_storage_for_file "$canonical" template + choose_and_set_storage_for_file "$canonical" container + + chmod 0644 "$canonical" + msg_ok "Created ${canonical}" + } + + # Whitelist check + local _is_whitelisted_key + _is_whitelisted_key() { + local k="$1" + local w + for w in "${VAR_WHITELIST[@]}"; do [ "$k" = "$w" ] && return 0; done + return 1 + } + + # Safe parser for KEY=VALUE lines + local _load_vars_file + _load_vars_file() { + local file="$1" + [ -f "$file" ] || return 0 + msg_info "Loading defaults from ${file}" + local line key val + while IFS= read -r line || [ -n "$line" ]; do + line="${line#"${line%%[![:space:]]*}"}" + line="${line%"${line##*[![:space:]]}"}" + [[ -z "$line" || "$line" == \#* ]] && continue + if [[ "$line" =~ ^([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then + local var_key="${BASH_REMATCH[1]}" + local var_val="${BASH_REMATCH[2]}" + + [[ "$var_key" != var_* ]] && continue + _is_whitelisted_key "$var_key" || { + msg_debug "Ignore non-whitelisted ${var_key}" + continue + } + + # Strip quotes + if [[ "$var_val" =~ ^\"(.*)\"$ ]]; then + var_val="${BASH_REMATCH[1]}" + elif [[ "$var_val" =~ ^\'(.*)\'$ ]]; then + var_val="${BASH_REMATCH[1]}" + fi + + # Unsafe characters + case $var_val in + \"*\") + var_val=${var_val#\"} + var_val=${var_val%\"} + ;; + \'*\') + var_val=${var_val#\'} + var_val=${var_val%\'} + ;; + esac # Hard env wins + [[ -n "${_HARD_ENV[$var_key]:-}" ]] && continue + # Set only if not already exported + [[ -z "${!var_key+x}" ]] && export "${var_key}=${var_val}" + else + msg_warn "Malformed line in ${file}: ${line}" + fi + done <"$file" + msg_ok "Loaded ${file}" + } + + # 1) Ensure file exists + _ensure_default_vars + + # 2) Load file + local dv + dv="$(_find_default_vars)" || { + msg_error "default.vars not found after ensure step" + return 1 + } + _load_vars_file "$dv" + + # 3) Map var_verbose → VERBOSE + if [[ -n "${var_verbose:-}" ]]; then + case "${var_verbose,,}" in 1 | yes | true | on) VERBOSE="yes" ;; 0 | no | false | off) VERBOSE="no" ;; *) VERBOSE="${var_verbose}" ;; esac + else + VERBOSE="no" + fi + + # 4) Apply base settings and show summary + METHOD="mydefaults-global" + base_settings "$VERBOSE" + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using My Defaults (default.vars) on node $PVEHOST_NAME${CL}" + echo_default +} + +# ------------------------------------------------------------------------------ +# get_app_defaults_path() +# +# - Returns full path for app-specific defaults file +# - Example: /usr/local/community-scripts/defaults/.vars +# ------------------------------------------------------------------------------ + +get_app_defaults_path() { + local n="${NSAPP:-${APP,,}}" + echo "/usr/local/community-scripts/defaults/${n}.vars" +} + +# ------------------------------------------------------------------------------ +# maybe_offer_save_app_defaults +# +# - Called after advanced_settings returned with fully chosen values. +# - If no .vars exists, offers to persist current advanced settings +# into /usr/local/community-scripts/defaults/.vars +# - Only writes whitelisted var_* keys. +# - Extracts raw values from flags like ",gw=..." ",mtu=..." etc. +# ------------------------------------------------------------------------------ +if ! declare -p VAR_WHITELIST >/dev/null 2>&1; then + declare -ag VAR_WHITELIST=( + var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_ctid var_disk var_fuse + var_gateway var_hostname var_ipv6_method var_ipv6_static var_mac var_mtu + var_net var_ns var_pw var_ram var_tags var_tun var_unprivileged + var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage + ) +fi + +# Note: _is_whitelisted_key() is defined above in default_var_settings section + +_sanitize_value() { + # Disallow Command-Substitution / Shell-Meta + case "$1" in + *'$('* | *'`'* | *';'* | *'&'* | *'<('*) + echo "" + return 0 + ;; + esac + echo "$1" +} + +# Map-Parser: read var_* from file into _VARS_IN associative array +# Note: Main _load_vars_file() with full validation is defined in default_var_settings section +# This simplified version is used specifically for diff operations via _VARS_IN array +declare -A _VARS_IN +_load_vars_file_to_map() { + local file="$1" + [ -f "$file" ] || return 0 + _VARS_IN=() # Clear array + local line key val + while IFS= read -r line || [ -n "$line" ]; do + line="${line#"${line%%[![:space:]]*}"}" + line="${line%"${line##*[![:space:]]}"}" + [ -z "$line" ] && continue + case "$line" in + \#*) continue ;; + esac + key=$(printf "%s" "$line" | cut -d= -f1) + val=$(printf "%s" "$line" | cut -d= -f2-) + case "$key" in + var_*) + if _is_whitelisted_key "$key"; then + _VARS_IN["$key"]="$val" + fi + ;; + esac + done <"$file" +} + +# Diff function for two var_* files -> produces human-readable diff list for $1 (old) vs $2 (new) +_build_vars_diff() { + local oldf="$1" newf="$2" + local k + local -A OLD=() NEW=() + _load_vars_file_to_map "$oldf" + for k in "${!_VARS_IN[@]}"; do OLD["$k"]="${_VARS_IN[$k]}"; done + _load_vars_file_to_map "$newf" + for k in "${!_VARS_IN[@]}"; do NEW["$k"]="${_VARS_IN[$k]}"; done + + local out + out+="# Diff for ${APP} (${NSAPP})\n" + out+="# Old: ${oldf}\n# New: ${newf}\n\n" + + local found_change=0 + + # Changed & Removed + for k in "${!OLD[@]}"; do + if [[ -v NEW["$k"] ]]; then + if [[ "${OLD[$k]}" != "${NEW[$k]}" ]]; then + out+="~ ${k}\n - old: ${OLD[$k]}\n + new: ${NEW[$k]}\n" + found_change=1 + fi + else + out+="- ${k}\n - old: ${OLD[$k]}\n" + found_change=1 + fi + done + + # Added + for k in "${!NEW[@]}"; do + if [[ ! -v OLD["$k"] ]]; then + out+="+ ${k}\n + new: ${NEW[$k]}\n" + found_change=1 + fi + done + + if [[ $found_change -eq 0 ]]; then + out+="(No differences)\n" + fi + + printf "%b" "$out" +} + +# Build a temporary .vars file from current advanced settings +_build_current_app_vars_tmp() { + tmpf="$(mktemp /tmp/${NSAPP:-app}.vars.new.XXXXXX)" + + # NET/GW + _net="${NET:-}" + _gate="" + case "${GATE:-}" in + ,gw=*) _gate=$(echo "$GATE" | sed 's/^,gw=//') ;; + esac + + # IPv6 + _ipv6_method="${IPV6_METHOD:-auto}" + _ipv6_static="" + _ipv6_gateway="" + if [ "$_ipv6_method" = "static" ]; then + _ipv6_static="${IPV6_ADDR:-}" + _ipv6_gateway="${IPV6_GATE:-}" + fi + + # MTU/VLAN/MAC + _mtu="" + _vlan="" + _mac="" + case "${MTU:-}" in + ,mtu=*) _mtu=$(echo "$MTU" | sed 's/^,mtu=//') ;; + esac + case "${VLAN:-}" in + ,tag=*) _vlan=$(echo "$VLAN" | sed 's/^,tag=//') ;; + esac + case "${MAC:-}" in + ,hwaddr=*) _mac=$(echo "$MAC" | sed 's/^,hwaddr=//') ;; + esac + + # DNS / Searchdomain + _ns="" + _searchdomain="" + case "${NS:-}" in + -nameserver=*) _ns=$(echo "$NS" | sed 's/^-nameserver=//') ;; + esac + case "${SD:-}" in + -searchdomain=*) _searchdomain=$(echo "$SD" | sed 's/^-searchdomain=//') ;; + esac + + # SSH / APT / Features + _ssh="${SSH:-no}" + _ssh_auth="${SSH_AUTHORIZED_KEY:-}" + _apt_cacher="${APT_CACHER:-}" + _apt_cacher_ip="${APT_CACHER_IP:-}" + _fuse="${ENABLE_FUSE:-no}" + _tun="${ENABLE_TUN:-no}" + _tags="${TAGS:-}" + _verbose="${VERBOSE:-no}" + + # Type / Resources / Identity + _unpriv="${CT_TYPE:-1}" + _cpu="${CORE_COUNT:-1}" + _ram="${RAM_SIZE:-1024}" + _disk="${DISK_SIZE:-4}" + _hostname="${HN:-$NSAPP}" + + # Storage + _tpl_storage="${TEMPLATE_STORAGE:-${var_template_storage:-}}" + _ct_storage="${CONTAINER_STORAGE:-${var_container_storage:-}}" + + { + echo "# App-specific defaults for ${APP} (${NSAPP})" + echo "# Generated on $(date -u '+%Y-%m-%dT%H:%M:%SZ')" + echo + + echo "var_unprivileged=$(_sanitize_value "$_unpriv")" + echo "var_cpu=$(_sanitize_value "$_cpu")" + echo "var_ram=$(_sanitize_value "$_ram")" + echo "var_disk=$(_sanitize_value "$_disk")" + + [ -n "${BRG:-}" ] && echo "var_brg=$(_sanitize_value "$BRG")" + [ -n "$_net" ] && echo "var_net=$(_sanitize_value "$_net")" + [ -n "$_gate" ] && echo "var_gateway=$(_sanitize_value "$_gate")" + [ -n "$_mtu" ] && echo "var_mtu=$(_sanitize_value "$_mtu")" + [ -n "$_vlan" ] && echo "var_vlan=$(_sanitize_value "$_vlan")" + [ -n "$_mac" ] && echo "var_mac=$(_sanitize_value "$_mac")" + [ -n "$_ns" ] && echo "var_ns=$(_sanitize_value "$_ns")" + + [ -n "$_ipv6_method" ] && echo "var_ipv6_method=$(_sanitize_value "$_ipv6_method")" + [ -n "$_ipv6_static" ] && echo "var_ipv6_static=$(_sanitize_value "$_ipv6_static")" + + [ -n "$_ssh" ] && echo "var_ssh=$(_sanitize_value "$_ssh")" + [ -n "$_ssh_auth" ] && echo "var_ssh_authorized_key=$(_sanitize_value "$_ssh_auth")" + + [ -n "$_apt_cacher" ] && echo "var_apt_cacher=$(_sanitize_value "$_apt_cacher")" + [ -n "$_apt_cacher_ip" ] && echo "var_apt_cacher_ip=$(_sanitize_value "$_apt_cacher_ip")" + + [ -n "$_fuse" ] && echo "var_fuse=$(_sanitize_value "$_fuse")" + [ -n "$_tun" ] && echo "var_tun=$(_sanitize_value "$_tun")" + [ -n "$_tags" ] && echo "var_tags=$(_sanitize_value "$_tags")" + [ -n "$_verbose" ] && echo "var_verbose=$(_sanitize_value "$_verbose")" + + [ -n "$_hostname" ] && echo "var_hostname=$(_sanitize_value "$_hostname")" + [ -n "$_searchdomain" ] && echo "var_searchdomain=$(_sanitize_value "$_searchdomain")" + + [ -n "$_tpl_storage" ] && echo "var_template_storage=$(_sanitize_value "$_tpl_storage")" + [ -n "$_ct_storage" ] && echo "var_container_storage=$(_sanitize_value "$_ct_storage")" + } >"$tmpf" + + echo "$tmpf" +} + +# ------------------------------------------------------------------------------ +# maybe_offer_save_app_defaults() +# +# - Called after advanced_settings() +# - Offers to save current values as app defaults if not existing +# - If file exists: shows diff and allows Update, Keep, View Diff, or Cancel +# ------------------------------------------------------------------------------ +maybe_offer_save_app_defaults() { + local app_vars_path + app_vars_path="$(get_app_defaults_path)" + + # always build from current settings + local new_tmp diff_tmp + new_tmp="$(_build_current_app_vars_tmp)" + diff_tmp="$(mktemp -p /tmp "${NSAPP:-app}.vars.diff.XXXXXX")" + + # 1) if no file → offer to create + if [[ ! -f "$app_vars_path" ]]; then + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --yesno "Save these advanced settings as defaults for ${APP}?\n\nThis will create:\n${app_vars_path}" 12 72; then + mkdir -p "$(dirname "$app_vars_path")" + install -m 0644 "$new_tmp" "$app_vars_path" + msg_ok "Saved app defaults: ${app_vars_path}" + fi + rm -f "$new_tmp" "$diff_tmp" + return 0 + fi + + # 2) if file exists → build diff + _build_vars_diff "$app_vars_path" "$new_tmp" >"$diff_tmp" + + # if no differences → do nothing + if grep -q "^(No differences)$" "$diff_tmp"; then + rm -f "$new_tmp" "$diff_tmp" + return 0 + fi + + # 3) if file exists → show menu with default selection "Update Defaults" + local app_vars_file + app_vars_file="$(basename "$app_vars_path")" + + while true; do + local sel + sel="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "APP DEFAULTS – ${APP}" \ + --menu "Differences detected. What do you want to do?" 20 78 10 \ + "Update Defaults" "Write new values to ${app_vars_file}" \ + "Keep Current" "Keep existing defaults (no changes)" \ + "View Diff" "Show a detailed diff" \ + "Cancel" "Abort without changes" \ + --default-item "Update Defaults" \ + 3>&1 1>&2 2>&3)" || { sel="Cancel"; } + + case "$sel" in + "Update Defaults") + install -m 0644 "$new_tmp" "$app_vars_path" + msg_ok "Updated app defaults: ${app_vars_path}" + break + ;; + "Keep Current") + msg_info "Keeping current app defaults: ${app_vars_path}" + break + ;; + "View Diff") + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "Diff – ${APP}" \ + --scrolltext --textbox "$diff_tmp" 25 100 + ;; + "Cancel" | *) + msg_info "Canceled. No changes to app defaults." + break + ;; + esac + done + + rm -f "$new_tmp" "$diff_tmp" +} + +ensure_storage_selection_for_vars_file() { + local vf="$1" + + # Read stored values (if any) + local tpl ct + tpl=$(grep -E '^var_template_storage=' "$vf" | cut -d= -f2-) + ct=$(grep -E '^var_container_storage=' "$vf" | cut -d= -f2-) + + if [[ -n "$tpl" && -n "$ct" ]]; then + TEMPLATE_STORAGE="$tpl" + CONTAINER_STORAGE="$ct" + return 0 + fi + + choose_and_set_storage_for_file "$vf" template + choose_and_set_storage_for_file "$vf" container + + msg_ok "Storage configuration saved to $(basename "$vf")" +} + +diagnostics_menu() { + if [ "${DIAGNOSTICS:-no}" = "yes" ]; then + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "DIAGNOSTIC SETTINGS" \ + --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ + --yes-button "No" --no-button "Back"; then + DIAGNOSTICS="no" + sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=no/' /usr/local/community-scripts/diagnostics + whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 + fi + else + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "DIAGNOSTIC SETTINGS" \ + --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ + --yes-button "Yes" --no-button "Back"; then + DIAGNOSTICS="yes" + sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=yes/' /usr/local/community-scripts/diagnostics + whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 + fi + fi +} + +ensure_global_default_vars_file() { + local vars_path="/usr/local/community-scripts/default.vars" + if [[ ! -f "$vars_path" ]]; then + mkdir -p "$(dirname "$vars_path")" + touch "$vars_path" + fi + echo "$vars_path" +} + +# ------------------------------------------------------------------------------ +# install_script() +# +# - Main entrypoint for installation mode +# - Runs safety checks (pve_check, root_check, maxkeys_check, diagnostics_check) +# - Builds interactive menu (Default, Verbose, Advanced, My Defaults, App Defaults, Diagnostics, Storage, Exit) +# - Applies chosen settings and triggers container build +# ------------------------------------------------------------------------------ +install_script() { + pve_check + shell_check + root_check + arch_check + ssh_check + maxkeys_check + diagnostics_check + + if systemctl is-active -q ping-instances.service; then + systemctl -q stop ping-instances.service + fi + + NEXTID=$(pvesh get /cluster/nextid) + timezone=$(cat /etc/timezone) + + # Show APP Header + header_info + + # --- Support CLI argument as direct preset (default, advanced, …) --- + CHOICE="${mode:-${1:-}}" + + # If no CLI argument → show whiptail menu + # Build menu dynamically based on available options + local appdefaults_option="" + local settings_option="" + local menu_items=( + "1" "Default Install" + "2" "Advanced Install" + "3" "My Defaults" + ) + + if [ -f "$(get_app_defaults_path)" ]; then + appdefaults_option="4" + menu_items+=("4" "App Defaults for ${APP}") + settings_option="5" + menu_items+=("5" "Settings") + else + settings_option="4" + menu_items+=("4" "Settings") + fi + + if [ -z "$CHOICE" ]; then + + TMP_CHOICE=$(whiptail \ + --backtitle "Proxmox VE Helper Scripts" \ + --title "Community-Scripts Options" \ + --ok-button "Select" --cancel-button "Exit Script" \ + --notags \ + --menu "\nChoose an option:\n Use TAB or Arrow keys to navigate, ENTER to select.\n" \ + 20 60 9 \ + "${menu_items[@]}" \ + --default-item "1" \ + 3>&1 1>&2 2>&3) || exit_script + CHOICE="$TMP_CHOICE" + fi + + APPDEFAULTS_OPTION="$appdefaults_option" + SETTINGS_OPTION="$settings_option" + + # --- Main case --- + local defaults_target="" + local run_maybe_offer="no" + case "$CHOICE" in + 1 | default | DEFAULT) + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME${CL}" + VERBOSE="no" + METHOD="default" + base_settings "$VERBOSE" + echo_default + defaults_target="$(ensure_global_default_vars_file)" + ;; + 2 | advanced | ADVANCED) + header_info + echo -e "${TAB}${INFO} ProxmoxVE Version ${PVEVERSION} | Kernel: ${KERNEL_VERSION}${CL}" + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Install on node $PVEHOST_NAME${CL}" + METHOD="advanced" + base_settings + advanced_settings + defaults_target="$(ensure_global_default_vars_file)" + run_maybe_offer="yes" + ;; + 3 | mydefaults | MYDEFAULTS) + default_var_settings || { + msg_error "Failed to apply default.vars" + exit 1 + } + defaults_target="/usr/local/community-scripts/default.vars" + ;; + "$APPDEFAULTS_OPTION" | appdefaults | APPDEFAULTS) + if [ -f "$(get_app_defaults_path)" ]; then + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using App Defaults for ${APP} on node $PVEHOST_NAME${CL}" + METHOD="appdefaults" + base_settings + _load_vars_file "$(get_app_defaults_path)" + echo_default + defaults_target="$(get_app_defaults_path)" + else + msg_error "No App Defaults available for ${APP}" + exit 1 + fi + ;; + "$SETTINGS_OPTION" | settings | SETTINGS) + settings_menu + defaults_target="" + ;; + *) + echo -e "${CROSS}${RD}Invalid option: $CHOICE${CL}" + exit 1 + ;; + esac + + if [[ -n "$defaults_target" ]]; then + ensure_storage_selection_for_vars_file "$defaults_target" + fi + + if [[ "$run_maybe_offer" == "yes" ]]; then + maybe_offer_save_app_defaults + fi +} + +edit_default_storage() { + local vf="/usr/local/community-scripts/default.vars" + + # Ensure file exists + if [[ ! -f "$vf" ]]; then + mkdir -p "$(dirname "$vf")" + touch "$vf" + fi + + # Let ensure_storage_selection_for_vars_file handle everything + ensure_storage_selection_for_vars_file "$vf" +} + +settings_menu() { + while true; do + local settings_items=( + "1" "Manage API-Diagnostic Setting" + "2" "Edit Default.vars" + "3" "Edit Default Storage" + ) + if [ -f "$(get_app_defaults_path)" ]; then + settings_items+=("4" "Edit App.vars for ${APP}") + settings_items+=("5" "Exit") + else + settings_items+=("4" "Exit") + fi + + local choice + choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "Community-Scripts SETTINGS Menu" \ + --ok-button "OK" --cancel-button "Back" \ + --menu "\n\nChoose a settings option:\n\nUse TAB or Arrow keys to navigate, ENTER to select." 20 60 9 \ + "${settings_items[@]}" \ + 3>&1 1>&2 2>&3) || break + + case "$choice" in + 1) diagnostics_menu ;; + 2) ${EDITOR:-nano} /usr/local/community-scripts/default.vars ;; + 3) edit_default_storage ;; + 4) + if [ -f "$(get_app_defaults_path)" ]; then + ${EDITOR:-nano} "$(get_app_defaults_path)" + else + exit_script + fi + ;; + 5) exit_script ;; + esac + done +} + +# ===== Unified storage selection & writing to vars files ===== +_write_storage_to_vars() { + # $1 = vars_file, $2 = key (var_container_storage / var_template_storage), $3 = value + local vf="$1" key="$2" val="$3" + # remove uncommented and commented versions to avoid duplicates + sed -i "/^[#[:space:]]*${key}=/d" "$vf" + echo "${key}=${val}" >>"$vf" +} + +choose_and_set_storage_for_file() { + # $1 = vars_file, $2 = class ('container'|'template') + local vf="$1" class="$2" key="" current="" + case "$class" in + container) key="var_container_storage" ;; + template) key="var_template_storage" ;; + *) + msg_error "Unknown storage class: $class" + return 1 + ;; + esac + + current=$(awk -F= -v k="^${key}=" '$0 ~ k {print $2; exit}' "$vf") + + # If only one storage exists for the content type, auto-pick. Else always ask (your wish #4). + local content="rootdir" + [[ "$class" == "template" ]] && content="vztmpl" + local count + count=$(pvesm status -content "$content" | awk 'NR>1{print $1}' | wc -l) + + if [[ "$count" -eq 1 ]]; then + STORAGE_RESULT=$(pvesm status -content "$content" | awk 'NR>1{print $1; exit}') + STORAGE_INFO="" + else + # If the current value is preselectable, we could show it, but per your requirement we always offer selection + select_storage "$class" || return 1 + fi + + _write_storage_to_vars "$vf" "$key" "$STORAGE_RESULT" + + # Keep environment in sync for later steps (e.g. app-default save) + if [[ "$class" == "container" ]]; then + export var_container_storage="$STORAGE_RESULT" + export CONTAINER_STORAGE="$STORAGE_RESULT" + else + export var_template_storage="$STORAGE_RESULT" + export TEMPLATE_STORAGE="$STORAGE_RESULT" + fi + + msg_ok "Updated ${key} → ${STORAGE_RESULT}" +} + +# ------------------------------------------------------------------------------ +# check_container_resources() +# +# - Compares host RAM/CPU with required values +# - Warns if under-provisioned and asks user to continue or abort +# ------------------------------------------------------------------------------ +check_container_resources() { + current_ram=$(free -m | awk 'NR==2{print $2}') + current_cpu=$(nproc) + + if [[ "$current_ram" -lt "$var_ram" ]] || [[ "$current_cpu" -lt "$var_cpu" ]]; then + echo -e "\n${INFO}${HOLD} ${GN}Required: ${var_cpu} CPU, ${var_ram}MB RAM ${CL}| ${RD}Current: ${current_cpu} CPU, ${current_ram}MB RAM${CL}" + echo -e "${YWB}Please ensure that the ${APP} LXC is configured with at least ${var_cpu} vCPU and ${var_ram} MB RAM for the build process.${CL}\n" + echo -ne "${INFO}${HOLD} May cause data loss! ${INFO} Continue update with under-provisioned LXC? " + read -r prompt + if [[ ! ${prompt,,} =~ ^(yes)$ ]]; then + echo -e "${CROSS}${HOLD} ${YWB}Exiting based on user input.${CL}" + exit 1 + fi + else + echo -e "" + fi +} + +# ------------------------------------------------------------------------------ +# check_container_storage() +# +# - Checks /boot partition usage +# - Warns if usage >80% and asks user confirmation before proceeding +# ------------------------------------------------------------------------------ +check_container_storage() { + total_size=$(df /boot --output=size | tail -n 1) + local used_size=$(df /boot --output=used | tail -n 1) + usage=$((100 * used_size / total_size)) + if ((usage > 80)); then + echo -e "${INFO}${HOLD} ${YWB}Warning: Storage is dangerously low (${usage}%).${CL}" + echo -ne "Continue anyway? " + read -r prompt + if [[ ! ${prompt,,} =~ ^(y|yes)$ ]]; then + echo -e "${CROSS}${HOLD}${YWB}Exiting based on user input.${CL}" + exit 1 + fi + fi +} + +# ------------------------------------------------------------------------------ +# ssh_extract_keys_from_file() +# +# - Extracts valid SSH public keys from given file +# - Supports RSA, Ed25519, ECDSA and filters out comments/invalid lines +# ------------------------------------------------------------------------------ +ssh_extract_keys_from_file() { + local f="$1" + [[ -r "$f" ]] || return 0 + tr -d '\r' <"$f" | awk ' + /^[[:space:]]*#/ {next} + /^[[:space:]]*$/ {next} + # nackt: typ base64 [comment] + /^(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/ {print; next} + # mit Optionen: finde ab erstem Key-Typ + { + match($0, /(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/) + if (RSTART>0) { print substr($0, RSTART) } + } + ' +} + +# ------------------------------------------------------------------------------ +# ssh_build_choices_from_files() +# +# - Builds interactive whiptail checklist of available SSH keys +# - Generates fingerprint, type and comment for each key +# ------------------------------------------------------------------------------ +ssh_build_choices_from_files() { + local -a files=("$@") + CHOICES=() + COUNT=0 + MAPFILE="$(mktemp)" + local id key typ fp cmt base ln=0 + + for f in "${files[@]}"; do + [[ -f "$f" && -r "$f" ]] || continue + base="$(basename -- "$f")" + case "$base" in + known_hosts | known_hosts.* | config) continue ;; + id_*) [[ "$f" != *.pub ]] && continue ;; + esac + + # map every key in file + while IFS= read -r key; do + [[ -n "$key" ]] || continue + + typ="" + fp="" + cmt="" + # Only the pure key part (without options) is already included in ‘key’. + read -r _typ _b64 _cmt <<<"$key" + typ="${_typ:-key}" + cmt="${_cmt:-}" + # Fingerprint via ssh-keygen (if available) + if command -v ssh-keygen >/dev/null 2>&1; then + fp="$(printf '%s\n' "$key" | ssh-keygen -lf - 2>/dev/null | awk '{print $2}')" + fi + # Label shorten + [[ ${#cmt} -gt 40 ]] && cmt="${cmt:0:37}..." + + ln=$((ln + 1)) + COUNT=$((COUNT + 1)) + id="K${COUNT}" + echo "${id}|${key}" >>"$MAPFILE" + CHOICES+=("$id" "[$typ] ${fp:+$fp }${cmt:+$cmt }— ${base}" "OFF") + done < <(ssh_extract_keys_from_file "$f") + done +} + +# ------------------------------------------------------------------------------ +# ssh_discover_default_files() +# +# - Scans standard paths for SSH keys +# - Includes ~/.ssh/*.pub, /etc/ssh/authorized_keys, etc. +# ------------------------------------------------------------------------------ +ssh_discover_default_files() { + local -a cand=() + shopt -s nullglob + cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2) + cand+=(/root/.ssh/*.pub) + cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*) + shopt -u nullglob + printf '%s\0' "${cand[@]}" +} + +configure_ssh_settings() { + SSH_KEYS_FILE="$(mktemp)" + : >"$SSH_KEYS_FILE" + + IFS=$'\0' read -r -d '' -a _def_files < <(ssh_discover_default_files && printf '\0') + ssh_build_choices_from_files "${_def_files[@]}" + local default_key_count="$COUNT" + + local ssh_key_mode + if [[ "$default_key_count" -gt 0 ]]; then + ssh_key_mode=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ + "Provision SSH keys for root:" 14 72 4 \ + "found" "Select from detected keys (${default_key_count})" \ + "manual" "Paste a single public key" \ + "folder" "Scan another folder (path or glob)" \ + "none" "No keys" 3>&1 1>&2 2>&3) || exit_script + else + ssh_key_mode=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ + "No host keys detected; choose manual/none:" 12 72 2 \ + "manual" "Paste a single public key" \ + "none" "No keys" 3>&1 1>&2 2>&3) || exit_script + fi + + case "$ssh_key_mode" in + found) + local selection + selection=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT HOST KEYS" \ + --checklist "Select one or more keys to import:" 20 140 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script + for tag in $selection; do + tag="${tag%\"}" + tag="${tag#\"}" + local line + line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) + [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" + done + ;; + manual) + SSH_AUTHORIZED_KEY="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Paste one SSH public key line (ssh-ed25519/ssh-rsa/...)" 10 72 --title "SSH Public Key" 3>&1 1>&2 2>&3)" + [[ -n "$SSH_AUTHORIZED_KEY" ]] && printf '%s\n' "$SSH_AUTHORIZED_KEY" >>"$SSH_KEYS_FILE" + ;; + folder) + local glob_path + glob_path=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Enter a folder or glob to scan (e.g. /root/.ssh/*.pub)" 10 72 --title "Scan Folder/Glob" 3>&1 1>&2 2>&3) + if [[ -n "$glob_path" ]]; then + shopt -s nullglob + read -r -a _scan_files <<<"$glob_path" + shopt -u nullglob + if [[ "${#_scan_files[@]}" -gt 0 ]]; then + ssh_build_choices_from_files "${_scan_files[@]}" + if [[ "$COUNT" -gt 0 ]]; then + local folder_selection + folder_selection=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT FOLDER KEYS" \ + --checklist "Select key(s) to import:" 20 78 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script + for tag in $folder_selection; do + tag="${tag%\"}" + tag="${tag#\"}" + local line + line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) + [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" + done + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "No keys found in: $glob_path" 8 60 + fi + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "Path/glob returned no files." 8 60 + fi + fi + ;; + none) + : + ;; + esac + + if [[ -s "$SSH_KEYS_FILE" ]]; then + sort -u -o "$SSH_KEYS_FILE" "$SSH_KEYS_FILE" + printf '\n' >>"$SSH_KEYS_FILE" + fi + + if [[ -s "$SSH_KEYS_FILE" || "$PW" == -password* ]]; then + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable root SSH access?" 10 58); then + SSH="yes" + else + SSH="no" + fi + else + SSH="no" + fi +} + +# ------------------------------------------------------------------------------ +# start() +# +# - Entry point of script +# - On Proxmox host: calls install_script +# - In silent mode: runs update_script +# - Otherwise: shows update/setting menu +# ------------------------------------------------------------------------------ +start() { + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) + if command -v pveversion >/dev/null 2>&1; then + install_script || return 0 + return 0 + elif [ ! -z ${PHS_SILENT+x} ] && [[ "${PHS_SILENT}" == "1" ]]; then + VERBOSE="no" + set_std_mode + update_script + else + CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "${APP} LXC Update/Setting" --menu \ + "Support/Update functions for ${APP} LXC. Choose an option:" \ + 12 60 3 \ + "1" "YES (Silent Mode)" \ + "2" "YES (Verbose Mode)" \ + "3" "NO (Cancel Update)" --nocancel --default-item "1" 3>&1 1>&2 2>&3) + + case "$CHOICE" in + 1) + VERBOSE="no" + set_std_mode + ;; + 2) + VERBOSE="yes" + set_std_mode + ;; + 3) + clear + exit_script + exit + ;; + esac + update_script + fi +} + +# ------------------------------------------------------------------------------ +# build_container() +# +# - Creates and configures the LXC container +# - Builds network string and applies features (FUSE, TUN, VAAPI passthrough) +# - Starts container and waits for network connectivity +# - Installs base packages, SSH keys, and runs -install.sh +# ------------------------------------------------------------------------------ +build_container() { + # if [ "$VERBOSE" == "yes" ]; then set -x; fi + + NET_STRING="-net0 name=eth0,bridge=${BRG:-vmbr0}" + + # MAC + if [[ -n "$MAC" ]]; then + case "$MAC" in + ,hwaddr=*) NET_STRING+="$MAC" ;; + *) NET_STRING+=",hwaddr=$MAC" ;; + esac + fi + + # IP (immer zwingend, Standard dhcp) + NET_STRING+=",ip=${NET:-dhcp}" + + # Gateway + if [[ -n "$GATE" ]]; then + case "$GATE" in + ,gw=*) NET_STRING+="$GATE" ;; + *) NET_STRING+=",gw=$GATE" ;; + esac + fi + + # VLAN + if [[ -n "$VLAN" ]]; then + case "$VLAN" in + ,tag=*) NET_STRING+="$VLAN" ;; + *) NET_STRING+=",tag=$VLAN" ;; + esac + fi + + # MTU + if [[ -n "$MTU" ]]; then + case "$MTU" in + ,mtu=*) NET_STRING+="$MTU" ;; + *) NET_STRING+=",mtu=$MTU" ;; + esac + fi + + # IPv6 Handling + case "$IPV6_METHOD" in + auto) NET_STRING="$NET_STRING,ip6=auto" ;; + dhcp) NET_STRING="$NET_STRING,ip6=dhcp" ;; + static) + NET_STRING="$NET_STRING,ip6=$IPV6_ADDR" + [ -n "$IPV6_GATE" ] && NET_STRING="$NET_STRING,gw6=$IPV6_GATE" + ;; + none) ;; + esac + + if [ "$CT_TYPE" == "1" ]; then + FEATURES="keyctl=1,nesting=1" + else + FEATURES="nesting=1" + fi + + if [ "$ENABLE_FUSE" == "yes" ]; then + FEATURES="$FEATURES,fuse=1" + fi + + TEMP_DIR=$(mktemp -d) + pushd "$TEMP_DIR" >/dev/null + if [ "$var_os" == "alpine" ]; then + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-install.func)" + else + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" + fi + export DIAGNOSTICS="$DIAGNOSTICS" + export RANDOM_UUID="$RANDOM_UUID" + export CACHER="$APT_CACHER" + export CACHER_IP="$APT_CACHER_IP" + export tz="$timezone" + export APPLICATION="$APP" + export app="$NSAPP" + export PASSWORD="$PW" + export VERBOSE="$VERBOSE" + export SSH_ROOT="${SSH}" + export SSH_AUTHORIZED_KEY + export CTID="$CT_ID" + export CTTYPE="$CT_TYPE" + export ENABLE_FUSE="$ENABLE_FUSE" + export ENABLE_TUN="$ENABLE_TUN" + export PCT_OSTYPE="$var_os" + export PCT_OSVERSION="$var_version" + export PCT_DISK_SIZE="$DISK_SIZE" + export PCT_OPTIONS=" + -features $FEATURES + -hostname $HN + -tags $TAGS + $SD + $NS + $NET_STRING + -onboot 1 + -cores $CORE_COUNT + -memory $RAM_SIZE + -unprivileged $CT_TYPE + $PW +" + export TEMPLATE_STORAGE="${var_template_storage:-}" + export CONTAINER_STORAGE="${var_container_storage:-}" + create_lxc_container || exit $? + + LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" + + # ============================================================================ + # GPU/USB PASSTHROUGH CONFIGURATION + # ============================================================================ + + # List of applications that benefit from GPU acceleration + GPU_APPS=( + "immich" "channels" "emby" "ersatztv" "frigate" + "jellyfin" "plex" "scrypted" "tdarr" "unmanic" + "ollama" "fileflows" "open-webui" "tunarr" "debian" + "handbrake" "sunshine" "moonlight" "kodi" "stremio" + "viseron" + ) + + # Check if app needs GPU + is_gpu_app() { + local app="${1,,}" + for gpu_app in "${GPU_APPS[@]}"; do + [[ "$app" == "${gpu_app,,}" ]] && return 0 + done + return 1 + } + + # Detect all available GPU devices + detect_gpu_devices() { + INTEL_DEVICES=() + AMD_DEVICES=() + NVIDIA_DEVICES=() + + # Store PCI info to avoid multiple calls + local pci_vga_info=$(lspci -nn 2>/dev/null | grep -E "VGA|Display|3D") + + # Check for Intel GPU - look for Intel vendor ID [8086] + if echo "$pci_vga_info" | grep -q "\[8086:"; then + msg_info "Detected Intel GPU" + if [[ -d /dev/dri ]]; then + for d in /dev/dri/renderD* /dev/dri/card*; do + [[ -e "$d" ]] && INTEL_DEVICES+=("$d") + done + fi + fi + + # Check for AMD GPU - look for AMD vendor IDs [1002] (AMD/ATI) or [1022] (AMD) + if echo "$pci_vga_info" | grep -qE "\[1002:|\[1022:"; then + msg_info "Detected AMD GPU" + if [[ -d /dev/dri ]]; then + # Only add if not already claimed by Intel + if [[ ${#INTEL_DEVICES[@]} -eq 0 ]]; then + for d in /dev/dri/renderD* /dev/dri/card*; do + [[ -e "$d" ]] && AMD_DEVICES+=("$d") + done + fi + fi + fi + + # Check for NVIDIA GPU - look for NVIDIA vendor ID [10de] + if echo "$pci_vga_info" | grep -q "\[10de:"; then + msg_info "Detected NVIDIA GPU" + if ! check_nvidia_host_setup; then + msg_error "NVIDIA host setup incomplete. Skipping GPU passthrough." + msg_info "Fix NVIDIA drivers on host, then recreate container or passthrough manually." + return 0 + fi + + for d in /dev/nvidia* /dev/nvidiactl /dev/nvidia-modeset; do + [[ -e "$d" ]] && NVIDIA_DEVICES+=("$d") + done + + if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then + msg_warn "NVIDIA GPU detected but no /dev/nvidia* devices found" + msg_warn "Please install NVIDIA drivers on host: apt install nvidia-driver" + else + if [[ "$CT_TYPE" == "0" ]]; then + cat <>"$LXC_CONFIG" + # NVIDIA GPU Passthrough (privileged) + lxc.cgroup2.devices.allow: c 195:* rwm + lxc.cgroup2.devices.allow: c 243:* rwm + lxc.mount.entry: /dev/nvidia0 dev/nvidia0 none bind,optional,create=file + lxc.mount.entry: /dev/nvidiactl dev/nvidiactl none bind,optional,create=file + lxc.mount.entry: /dev/nvidia-uvm dev/nvidia-uvm none bind,optional,create=file + lxc.mount.entry: /dev/nvidia-uvm-tools dev/nvidia-uvm-tools none bind,optional,create=file +EOF + + if [[ -e /dev/dri/renderD128 ]]; then + echo "lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file" >>"$LXC_CONFIG" + fi + + export GPU_TYPE="NVIDIA" + export NVIDIA_DRIVER_VERSION=$(nvidia-smi --query-gpu=driver_version --format=csv,noheader 2>/dev/null | head -n1) + msg_ok "NVIDIA GPU passthrough configured (driver: ${NVIDIA_DRIVER_VERSION})" + else + msg_warn "NVIDIA passthrough only supported for privileged containers" + return 0 + fi + fi + fi + + # Debug output + msg_debug "Intel devices: ${INTEL_DEVICES[*]}" + msg_debug "AMD devices: ${AMD_DEVICES[*]}" + msg_debug "NVIDIA devices: ${NVIDIA_DEVICES[*]}" + } + + # Configure USB passthrough for privileged containers + configure_usb_passthrough() { + if [[ "$CT_TYPE" != "0" ]]; then + return 0 + fi + + msg_info "Configuring automatic USB passthrough (privileged container)" + cat <>"$LXC_CONFIG" +# Automatic USB passthrough (privileged container) +lxc.cgroup2.devices.allow: a +lxc.cap.drop: +lxc.cgroup2.devices.allow: c 188:* rwm +lxc.cgroup2.devices.allow: c 189:* rwm +lxc.mount.entry: /dev/serial/by-id dev/serial/by-id none bind,optional,create=dir +lxc.mount.entry: /dev/ttyUSB0 dev/ttyUSB0 none bind,optional,create=file +lxc.mount.entry: /dev/ttyUSB1 dev/ttyUSB1 none bind,optional,create=file +lxc.mount.entry: /dev/ttyACM0 dev/ttyACM0 none bind,optional,create=file +lxc.mount.entry: /dev/ttyACM1 dev/ttyACM1 none bind,optional,create=file +EOF + msg_ok "USB passthrough configured" + } + + # Configure GPU passthrough + configure_gpu_passthrough() { + # Skip if not a GPU app and not privileged + if [[ "$CT_TYPE" != "0" ]] && ! is_gpu_app "$APP"; then + return 0 + fi + + detect_gpu_devices + + # Count available GPU types + local gpu_count=0 + local available_gpus=() + + if [[ ${#INTEL_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("INTEL") + gpu_count=$((gpu_count + 1)) + fi + + if [[ ${#AMD_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("AMD") + gpu_count=$((gpu_count + 1)) + fi + + if [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("NVIDIA") + gpu_count=$((gpu_count + 1)) + fi + + if [[ $gpu_count -eq 0 ]]; then + msg_info "No GPU devices found for passthrough" + return 0 + fi + + local selected_gpu="" + + if [[ $gpu_count -eq 1 ]]; then + # Automatic selection for single GPU + selected_gpu="${available_gpus[0]}" + msg_info "Automatically configuring ${selected_gpu} GPU passthrough" + else + # Multiple GPUs - ask user + echo -e "\n${INFO} Multiple GPU types detected:" + for gpu in "${available_gpus[@]}"; do + echo " - $gpu" + done + read -rp "Which GPU type to passthrough? (${available_gpus[*]}): " selected_gpu + selected_gpu="${selected_gpu^^}" + + # Validate selection + local valid=0 + for gpu in "${available_gpus[@]}"; do + [[ "$selected_gpu" == "$gpu" ]] && valid=1 + done + + if [[ $valid -eq 0 ]]; then + msg_warn "Invalid selection. Skipping GPU passthrough." + return 0 + fi + fi + + # Apply passthrough configuration based on selection + local dev_idx=0 + + case "$selected_gpu" in + INTEL | AMD) + local devices=() + [[ "$selected_gpu" == "INTEL" ]] && devices=("${INTEL_DEVICES[@]}") + [[ "$selected_gpu" == "AMD" ]] && devices=("${AMD_DEVICES[@]}") + + # For Proxmox WebUI visibility, add as dev0, dev1 etc. + for dev in "${devices[@]}"; do + if [[ "$CT_TYPE" == "0" ]]; then + # Privileged container - use dev entries for WebUI visibility + # Use initial GID 104 (render) for renderD*, 44 (video) for card* + if [[ "$dev" =~ renderD ]]; then + echo "dev${dev_idx}: $dev,gid=104" >>"$LXC_CONFIG" + else + echo "dev${dev_idx}: $dev,gid=44" >>"$LXC_CONFIG" + fi + dev_idx=$((dev_idx + 1)) + + # Also add cgroup allows for privileged containers + local major minor + major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") + minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") + + if [[ "$major" != "0" && "$minor" != "0" ]]; then + echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" + fi + else + # Unprivileged container + if [[ "$dev" =~ renderD ]]; then + echo "dev${dev_idx}: $dev,uid=0,gid=104" >>"$LXC_CONFIG" + else + echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG" + fi + dev_idx=$((dev_idx + 1)) + fi + done + + export GPU_TYPE="$selected_gpu" + msg_ok "${selected_gpu} GPU passthrough configured (${dev_idx} devices)" + ;; + + NVIDIA) + if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then + msg_error "NVIDIA drivers not installed on host. Please install: apt install nvidia-driver" + return 1 + fi + + for dev in "${NVIDIA_DEVICES[@]}"; do + # NVIDIA devices typically need different handling + echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG" + dev_idx=$((dev_idx + 1)) + + if [[ "$CT_TYPE" == "0" ]]; then + local major minor + major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") + minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") + + if [[ "$major" != "0" && "$minor" != "0" ]]; then + echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" + fi + fi + done + + export GPU_TYPE="NVIDIA" + msg_ok "NVIDIA GPU passthrough configured (${dev_idx} devices)" + ;; + esac + } + + # Additional device passthrough + configure_additional_devices() { + # TUN device passthrough + if [ "$ENABLE_TUN" == "yes" ]; then + cat <>"$LXC_CONFIG" +lxc.cgroup2.devices.allow: c 10:200 rwm +lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file +EOF + fi + + # Coral TPU passthrough + if [[ -e /dev/apex_0 ]]; then + msg_info "Detected Coral TPU - configuring passthrough" + echo "lxc.mount.entry: /dev/apex_0 dev/apex_0 none bind,optional,create=file" >>"$LXC_CONFIG" + fi + } + + # Execute pre-start configurations + configure_usb_passthrough + configure_gpu_passthrough + configure_additional_devices + + # ============================================================================ + # START CONTAINER AND INSTALL USERLAND + # ============================================================================ + + msg_info "Starting LXC Container" + pct start "$CTID" + + # Wait for container to be running + for i in {1..10}; do + if pct status "$CTID" | grep -q "status: running"; then + msg_ok "Started LXC Container" + break + fi + sleep 1 + if [ "$i" -eq 10 ]; then + msg_error "LXC Container did not reach running state" + exit 1 + fi + done + + # Wait for network (skip for Alpine initially) + if [ "$var_os" != "alpine" ]; then + msg_info "Waiting for network in LXC container" + + # Wait for IP + for i in {1..20}; do + ip_in_lxc=$(pct exec "$CTID" -- ip -4 addr show dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) + [ -n "$ip_in_lxc" ] && break + sleep 1 + done + + if [ -z "$ip_in_lxc" ]; then + msg_error "No IP assigned to CT $CTID after 20s" + exit 1 + fi + + # Try to reach gateway + gw_ok=0 + for i in {1..10}; do + if pct exec "$CTID" -- ping -c1 -W1 "${GATEWAY:-8.8.8.8}" >/dev/null 2>&1; then + gw_ok=1 + break + fi + sleep 1 + done + + if [ "$gw_ok" -eq 1 ]; then + msg_ok "Network in LXC is reachable (IP $ip_in_lxc)" + else + msg_warn "Network reachable but gateway check failed" + fi + fi + # Function to get correct GID inside container + get_container_gid() { + local group="$1" + local gid=$(pct exec "$CTID" -- getent group "$group" 2>/dev/null | cut -d: -f3) + echo "${gid:-44}" # Default to 44 if not found + } + + fix_gpu_gids + + # Continue with standard container setup + msg_info "Customizing LXC Container" + + # # Install GPU userland if configured + # if [[ "${ENABLE_VAAPI:-0}" == "1" ]]; then + # install_gpu_userland "VAAPI" + # fi + + # if [[ "${ENABLE_NVIDIA:-0}" == "1" ]]; then + # install_gpu_userland "NVIDIA" + # fi + + # Continue with standard container setup + if [ "$var_os" == "alpine" ]; then + sleep 3 + pct exec "$CTID" -- /bin/sh -c 'cat </etc/apk/repositories +http://dl-cdn.alpinelinux.org/alpine/latest-stable/main +http://dl-cdn.alpinelinux.org/alpine/latest-stable/community +EOF' + pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses jq >/dev/null" + else + sleep 3 + pct exec "$CTID" -- bash -c "sed -i '/$LANG/ s/^# //' /etc/locale.gen" + pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \ + echo LANG=\$locale_line >/etc/default/locale && \ + locale-gen >/dev/null && \ + export LANG=\$locale_line" + + if [[ -z "${tz:-}" ]]; then + tz=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "Etc/UTC") + fi + + if pct exec "$CTID" -- test -e "/usr/share/zoneinfo/$tz"; then + pct exec "$CTID" -- bash -c "tz='$tz'; echo \"\$tz\" >/etc/timezone && ln -sf \"/usr/share/zoneinfo/\$tz\" /etc/localtime" + else + msg_warn "Skipping timezone setup – zone '$tz' not found in container" + fi + + pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y sudo curl mc gnupg2 jq >/dev/null" || { + msg_error "apt-get base packages installation failed" + exit 1 + } + fi + + msg_ok "Customized LXC Container" + + # Verify GPU access if enabled + if [[ "${ENABLE_VAAPI:-0}" == "1" ]] && [ "$var_os" != "alpine" ]; then + pct exec "$CTID" -- bash -c "vainfo >/dev/null 2>&1" && + msg_ok "VAAPI verified working" || + msg_warn "VAAPI verification failed - may need additional configuration" + fi + + if [[ "${ENABLE_NVIDIA:-0}" == "1" ]] && [ "$var_os" != "alpine" ]; then + pct exec "$CTID" -- bash -c "nvidia-smi >/dev/null 2>&1" && + msg_ok "NVIDIA verified working" || + msg_warn "NVIDIA verification failed - may need additional configuration" + fi + + # Install SSH keys + install_ssh_keys_into_ct + + # Run application installer + if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)"; then + exit $? + fi +} + +destroy_lxc() { + if [[ -z "$CT_ID" ]]; then + msg_error "No CT_ID found. Nothing to remove." + return 1 + fi + + # Abbruch bei Ctrl-C / Ctrl-D / ESC + trap 'echo; msg_error "Aborted by user (SIGINT/SIGQUIT)"; return 130' INT QUIT + + local prompt + if ! read -rp "Remove this Container? " prompt; then + # read gibt != 0 zurück bei Ctrl-D/ESC + msg_error "Aborted input (Ctrl-D/ESC)" + return 130 + fi + + case "${prompt,,}" in + y | yes) + if pct stop "$CT_ID" &>/dev/null && pct destroy "$CT_ID" &>/dev/null; then + msg_ok "Removed Container $CT_ID" + else + msg_error "Failed to remove Container $CT_ID" + return 1 + fi + ;; + "" | n | no) + msg_info "Container was not removed." + ;; + *) + msg_warn "Invalid response. Container was not removed." + ;; + esac +} + +# ------------------------------------------------------------------------------ +# Storage discovery / selection helpers +# ------------------------------------------------------------------------------ +# ===== Storage discovery / selection helpers (ported from create_lxc.sh) ===== +resolve_storage_preselect() { + local class="$1" preselect="$2" required_content="" + case "$class" in + template) required_content="vztmpl" ;; + container) required_content="rootdir" ;; + *) return 1 ;; + esac + [[ -z "$preselect" ]] && return 1 + if ! pvesm status -content "$required_content" | awk 'NR>1{print $1}' | grep -qx -- "$preselect"; then + msg_warn "Preselected storage '${preselect}' does not support content '${required_content}' (or not found)" + return 1 + fi + + local line total used free + line="$(pvesm status | awk -v s="$preselect" 'NR>1 && $1==s {print $0}')" + if [[ -z "$line" ]]; then + STORAGE_INFO="n/a" + else + total="$(awk '{print $4}' <<<"$line")" + used="$(awk '{print $5}' <<<"$line")" + free="$(awk '{print $6}' <<<"$line")" + local total_h used_h free_h + if command -v numfmt >/dev/null 2>&1; then + total_h="$(numfmt --to=iec --suffix=B --format %.1f "$total" 2>/dev/null || echo "$total")" + used_h="$(numfmt --to=iec --suffix=B --format %.1f "$used" 2>/dev/null || echo "$used")" + free_h="$(numfmt --to=iec --suffix=B --format %.1f "$free" 2>/dev/null || echo "$free")" + STORAGE_INFO="Free: ${free_h} Used: ${used_h}" + else + STORAGE_INFO="Free: ${free} Used: ${used}" + fi + fi + STORAGE_RESULT="$preselect" + return 0 +} + +fix_gpu_gids() { + if [[ -z "${GPU_TYPE:-}" ]]; then + return 0 + fi + + msg_info "Detecting and setting correct GPU group IDs" + + # Ermittle die tatsächlichen GIDs aus dem Container + local video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") + local render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") + + # Fallbacks wenn Gruppen nicht existieren + if [[ -z "$video_gid" ]]; then + # Versuche die video Gruppe zu erstellen + pct exec "$CTID" -- sh -c "groupadd -r video 2>/dev/null || true" + video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") + [[ -z "$video_gid" ]] && video_gid="44" # Ultimate fallback + fi + + if [[ -z "$render_gid" ]]; then + # Versuche die render Gruppe zu erstellen + pct exec "$CTID" -- sh -c "groupadd -r render 2>/dev/null || true" + render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") + [[ -z "$render_gid" ]] && render_gid="104" # Ultimate fallback + fi + + msg_info "Container GIDs detected - video:${video_gid}, render:${render_gid}" + + # Prüfe ob die GIDs von den Defaults abweichen + local need_update=0 + if [[ "$video_gid" != "44" ]] || [[ "$render_gid" != "104" ]]; then + need_update=1 + fi + + if [[ $need_update -eq 1 ]]; then + msg_info "Updating device GIDs in container config" + + # Stoppe Container für Config-Update + pct stop "$CTID" >/dev/null 2>&1 + + # Update die dev Einträge mit korrekten GIDs + # Backup der Config + cp "$LXC_CONFIG" "${LXC_CONFIG}.bak" + + # Parse und update jeden dev Eintrag + while IFS= read -r line; do + if [[ "$line" =~ ^dev[0-9]+: ]]; then + # Extract device path + local device_path=$(echo "$line" | sed -E 's/^dev[0-9]+: ([^,]+).*/\1/') + local dev_num=$(echo "$line" | sed -E 's/^(dev[0-9]+):.*/\1/') + + if [[ "$device_path" =~ renderD ]]; then + # RenderD device - use render GID + echo "${dev_num}: ${device_path},gid=${render_gid}" + elif [[ "$device_path" =~ card ]]; then + # Card device - use video GID + echo "${dev_num}: ${device_path},gid=${video_gid}" + else + # Keep original line + echo "$line" + fi + else + # Keep non-dev lines + echo "$line" + fi + done <"$LXC_CONFIG" >"${LXC_CONFIG}.new" + + mv "${LXC_CONFIG}.new" "$LXC_CONFIG" + + # Starte Container wieder + pct start "$CTID" >/dev/null 2>&1 + sleep 3 + + msg_ok "Device GIDs updated successfully" + else + msg_ok "Device GIDs are already correct" + fi + if [[ "$CT_TYPE" == "0" ]]; then + pct exec "$CTID" -- bash -c " + if [ -d /dev/dri ]; then + for dev in /dev/dri/*; do + if [ -e \"\$dev\" ]; then + if [[ \"\$dev\" =~ renderD ]]; then + chgrp ${render_gid} \"\$dev\" 2>/dev/null || true + else + chgrp ${video_gid} \"\$dev\" 2>/dev/null || true + fi + chmod 660 \"\$dev\" 2>/dev/null || true + fi + done + fi + " >/dev/null 2>&1 + fi +} + +# NVIDIA-spezific check on host +check_nvidia_host_setup() { + if ! command -v nvidia-smi >/dev/null 2>&1; then + msg_warn "NVIDIA GPU detected but nvidia-smi not found on host" + msg_warn "Please install NVIDIA drivers on host first." + #echo " 1. Download driver: wget https://us.download.nvidia.com/XFree86/Linux-x86_64/550.127.05/NVIDIA-Linux-x86_64-550.127.05.run" + #echo " 2. Install: ./NVIDIA-Linux-x86_64-550.127.05.run --dkms" + #echo " 3. Verify: nvidia-smi" + return 1 + fi + + # check if nvidia-smi works + if ! nvidia-smi >/dev/null 2>&1; then + msg_warn "nvidia-smi installed but not working. Driver issue?" + return 1 + fi + + return 0 +} + +check_storage_support() { + local CONTENT="$1" VALID=0 + while IFS= read -r line; do + local STORAGE_NAME + STORAGE_NAME=$(awk '{print $1}' <<<"$line") + [[ -n "$STORAGE_NAME" ]] && VALID=1 + done < <(pvesm status -content "$CONTENT" 2>/dev/null | awk 'NR>1') + [[ $VALID -eq 1 ]] +} + +select_storage() { + local CLASS=$1 CONTENT CONTENT_LABEL + case $CLASS in + container) + CONTENT='rootdir' + CONTENT_LABEL='Container' + ;; + template) + CONTENT='vztmpl' + CONTENT_LABEL='Container template' + ;; + iso) + CONTENT='iso' + CONTENT_LABEL='ISO image' + ;; + images) + CONTENT='images' + CONTENT_LABEL='VM Disk image' + ;; + backup) + CONTENT='backup' + CONTENT_LABEL='Backup' + ;; + snippets) + CONTENT='snippets' + CONTENT_LABEL='Snippets' + ;; + *) + msg_error "Invalid storage class '$CLASS'" + return 1 + ;; + esac + + declare -A STORAGE_MAP + local -a MENU=() + local COL_WIDTH=0 + + while read -r TAG TYPE _ TOTAL USED FREE _; do + [[ -n "$TAG" && -n "$TYPE" ]] || continue + local DISPLAY="${TAG} (${TYPE})" + local USED_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$USED") + local FREE_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$FREE") + local INFO="Free: ${FREE_FMT}B Used: ${USED_FMT}B" + STORAGE_MAP["$DISPLAY"]="$TAG" + MENU+=("$DISPLAY" "$INFO" "OFF") + ((${#DISPLAY} > COL_WIDTH)) && COL_WIDTH=${#DISPLAY} + done < <(pvesm status -content "$CONTENT" | awk 'NR>1') + + if [[ ${#MENU[@]} -eq 0 ]]; then + msg_error "No storage found for content type '$CONTENT'." + return 2 + fi + + if [[ $((${#MENU[@]} / 3)) -eq 1 ]]; then + STORAGE_RESULT="${STORAGE_MAP[${MENU[0]}]}" + STORAGE_INFO="${MENU[1]}" + return 0 + fi + + local WIDTH=$((COL_WIDTH + 42)) + while true; do + local DISPLAY_SELECTED + DISPLAY_SELECTED=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "Storage Pools" \ + --radiolist "Which storage pool for ${CONTENT_LABEL,,}?\n(Spacebar to select)" \ + 16 "$WIDTH" 6 "${MENU[@]}" 3>&1 1>&2 2>&3) || { exit_script; } + + DISPLAY_SELECTED=$(sed 's/[[:space:]]*$//' <<<"$DISPLAY_SELECTED") + if [[ -z "$DISPLAY_SELECTED" || -z "${STORAGE_MAP[$DISPLAY_SELECTED]+_}" ]]; then + whiptail --msgbox "No valid storage selected. Please try again." 8 58 + continue + fi + STORAGE_RESULT="${STORAGE_MAP[$DISPLAY_SELECTED]}" + for ((i = 0; i < ${#MENU[@]}; i += 3)); do + if [[ "${MENU[$i]}" == "$DISPLAY_SELECTED" ]]; then + STORAGE_INFO="${MENU[$i + 1]}" + break + fi + done + return 0 + done +} + +create_lxc_container() { + # ------------------------------------------------------------------------------ + # Optional verbose mode (debug tracing) + # ------------------------------------------------------------------------------ + if [[ "${CREATE_LXC_VERBOSE:-no}" == "yes" ]]; then set -x; fi + + # ------------------------------------------------------------------------------ + # Helpers (dynamic versioning / template parsing) + # ------------------------------------------------------------------------------ + pkg_ver() { dpkg-query -W -f='${Version}\n' "$1" 2>/dev/null || echo ""; } + pkg_cand() { apt-cache policy "$1" 2>/dev/null | awk '/Candidate:/ {print $2}'; } + + ver_ge() { dpkg --compare-versions "$1" ge "$2"; } + ver_gt() { dpkg --compare-versions "$1" gt "$2"; } + ver_lt() { dpkg --compare-versions "$1" lt "$2"; } + + # Extract Debian OS minor from template name: debian-13-standard_13.1-1_amd64.tar.zst => "13.1" + parse_template_osver() { sed -n 's/.*_\([0-9][0-9]*\(\.[0-9]\+\)\?\)-.*/\1/p' <<<"$1"; } + + # Offer upgrade for pve-container/lxc-pve if candidate > installed; optional auto-retry pct create + # Returns: + # 0 = no upgrade needed + # 1 = upgraded (and if do_retry=yes and retry succeeded, creation done) + # 2 = user declined + # 3 = upgrade attempted but failed OR retry failed + offer_lxc_stack_upgrade_and_maybe_retry() { + local do_retry="${1:-no}" # yes|no + local _pvec_i _pvec_c _lxcp_i _lxcp_c need=0 + + _pvec_i="$(pkg_ver pve-container)" + _lxcp_i="$(pkg_ver lxc-pve)" + _pvec_c="$(pkg_cand pve-container)" + _lxcp_c="$(pkg_cand lxc-pve)" + + if [[ -n "$_pvec_c" && "$_pvec_c" != "none" ]]; then + ver_gt "$_pvec_c" "${_pvec_i:-0}" && need=1 + fi + if [[ -n "$_lxcp_c" && "$_lxcp_c" != "none" ]]; then + ver_gt "$_lxcp_c" "${_lxcp_i:-0}" && need=1 + fi + if [[ $need -eq 0 ]]; then + msg_debug "No newer candidate for pve-container/lxc-pve (installed=$_pvec_i/$_lxcp_i, cand=$_pvec_c/$_lxcp_c)" + return 0 + fi + + echo + echo "An update for the Proxmox LXC stack is available:" + echo " pve-container: installed=${_pvec_i:-n/a} candidate=${_pvec_c:-n/a}" + echo " lxc-pve : installed=${_lxcp_i:-n/a} candidate=${_lxcp_c:-n/a}" + echo + read -rp "Do you want to upgrade now? [y/N] " _ans + case "${_ans,,}" in + y | yes) + msg_info "Upgrading Proxmox LXC stack (pve-container, lxc-pve)" + if apt-get update -qq >/dev/null && apt-get install -y --only-upgrade pve-container lxc-pve >/dev/null; then + msg_ok "LXC stack upgraded." + if [[ "$do_retry" == "yes" ]]; then + msg_info "Retrying container creation after upgrade" + if pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then + msg_ok "Container created successfully after upgrade." + return 0 + else + msg_error "pct create still failed after upgrade. See $LOGFILE" + return 3 + fi + fi + return 1 + else + msg_error "Upgrade failed. Please check APT output." + return 3 + fi + ;; + *) return 2 ;; + esac + } + + # ------------------------------------------------------------------------------ + # Required input variables + # ------------------------------------------------------------------------------ + [[ "${CTID:-}" ]] || { + msg_error "You need to set 'CTID' variable." + exit 203 + } + [[ "${PCT_OSTYPE:-}" ]] || { + msg_error "You need to set 'PCT_OSTYPE' variable." + exit 204 + } + + msg_debug "CTID=$CTID" + msg_debug "PCT_OSTYPE=$PCT_OSTYPE" + msg_debug "PCT_OSVERSION=${PCT_OSVERSION:-default}" + + # ID checks + [[ "$CTID" -ge 100 ]] || { + msg_error "ID cannot be less than 100." + exit 205 + } + if qm status "$CTID" &>/dev/null || pct status "$CTID" &>/dev/null; then + echo -e "ID '$CTID' is already in use." + unset CTID + msg_error "Cannot use ID that is already in use." + exit 206 + fi + + # Storage capability check + check_storage_support "rootdir" || { + msg_error "No valid storage found for 'rootdir' [Container]" + exit 1 + } + check_storage_support "vztmpl" || { + msg_error "No valid storage found for 'vztmpl' [Template]" + exit 1 + } + + # Template storage selection + if resolve_storage_preselect template "${TEMPLATE_STORAGE:-}"; then + TEMPLATE_STORAGE="$STORAGE_RESULT" + TEMPLATE_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${TEMPLATE_STORAGE}${CL} (${TEMPLATE_STORAGE_INFO}) [Template]" + else + while true; do + if [[ -z "${var_template_storage:-}" ]]; then + if select_storage template; then + TEMPLATE_STORAGE="$STORAGE_RESULT" + TEMPLATE_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${TEMPLATE_STORAGE}${CL} (${TEMPLATE_STORAGE_INFO}) [Template]" + break + fi + fi + done + fi + + # Container storage selection + if resolve_storage_preselect container "${CONTAINER_STORAGE:-}"; then + CONTAINER_STORAGE="$STORAGE_RESULT" + CONTAINER_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${CONTAINER_STORAGE}${CL} (${CONTAINER_STORAGE_INFO}) [Container]" + else + if [[ -z "${var_container_storage:-}" ]]; then + if select_storage container; then + CONTAINER_STORAGE="$STORAGE_RESULT" + CONTAINER_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${CONTAINER_STORAGE}${CL} (${CONTAINER_STORAGE_INFO}) [Container]" + fi + fi + fi + + # Validate content types + msg_info "Validating content types of storage '$CONTAINER_STORAGE'" + STORAGE_CONTENT=$(grep -A4 -E "^(zfspool|dir|lvmthin|lvm): $CONTAINER_STORAGE" /etc/pve/storage.cfg | grep content | awk '{$1=""; print $0}' | xargs) + msg_debug "Storage '$CONTAINER_STORAGE' has content types: $STORAGE_CONTENT" + grep -qw "rootdir" <<<"$STORAGE_CONTENT" || { + msg_error "Storage '$CONTAINER_STORAGE' does not support 'rootdir'. Cannot create LXC." + exit 217 + } + $STD msg_ok "Storage '$CONTAINER_STORAGE' supports 'rootdir'" + + msg_info "Validating content types of template storage '$TEMPLATE_STORAGE'" + TEMPLATE_CONTENT=$(grep -A4 -E "^[^:]+: $TEMPLATE_STORAGE" /etc/pve/storage.cfg | grep content | awk '{$1=""; print $0}' | xargs) + msg_debug "Template storage '$TEMPLATE_STORAGE' has content types: $TEMPLATE_CONTENT" + if ! grep -qw "vztmpl" <<<"$TEMPLATE_CONTENT"; then + msg_warn "Template storage '$TEMPLATE_STORAGE' does not declare 'vztmpl'. This may cause pct create to fail." + else + $STD msg_ok "Template storage '$TEMPLATE_STORAGE' supports 'vztmpl'" + fi + + # Free space check + STORAGE_FREE=$(pvesm status | awk -v s="$CONTAINER_STORAGE" '$1 == s { print $6 }') + REQUIRED_KB=$((${PCT_DISK_SIZE:-8} * 1024 * 1024)) + [[ "$STORAGE_FREE" -ge "$REQUIRED_KB" ]] || { + msg_error "Not enough space on '$CONTAINER_STORAGE'. Needed: ${PCT_DISK_SIZE:-8}G." + exit 214 + } + + # Cluster quorum (if cluster) + if [[ -f /etc/pve/corosync.conf ]]; then + msg_info "Checking cluster quorum" + if ! pvecm status | awk -F':' '/^Quorate/ { exit ($2 ~ /Yes/) ? 0 : 1 }'; then + msg_error "Cluster is not quorate. Start all nodes or configure quorum device (QDevice)." + exit 210 + fi + msg_ok "Cluster is quorate" + fi + + # ------------------------------------------------------------------------------ + # Template discovery & validation + # ------------------------------------------------------------------------------ + TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" + case "$PCT_OSTYPE" in + debian | ubuntu) TEMPLATE_PATTERN="-standard_" ;; + alpine | fedora | rocky | centos) TEMPLATE_PATTERN="-default_" ;; + *) TEMPLATE_PATTERN="" ;; + esac + + msg_info "Searching for template '$TEMPLATE_SEARCH'" + + # Build regex patterns outside awk/grep for clarity + SEARCH_PATTERN="^${TEMPLATE_SEARCH}" + + #echo "[DEBUG] TEMPLATE_SEARCH='$TEMPLATE_SEARCH'" + #echo "[DEBUG] SEARCH_PATTERN='$SEARCH_PATTERN'" + #echo "[DEBUG] TEMPLATE_PATTERN='$TEMPLATE_PATTERN'" + + mapfile -t LOCAL_TEMPLATES < <( + pveam list "$TEMPLATE_STORAGE" 2>/dev/null | + awk -v search="${SEARCH_PATTERN}" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' | + sed 's|.*/||' | sort -t - -k 2 -V + ) + + pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)." + + #echo "[DEBUG] pveam available output (first 5 lines with .tar files):" + #pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | head -5 | sed 's/^/ /' + + set +u + mapfile -t ONLINE_TEMPLATES < <(pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | awk '{print $2}' | grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V 2>/dev/null || true) + #echo "[DEBUG] After filtering: ${#ONLINE_TEMPLATES[@]} online templates found" + set -u + if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then + #echo "[DEBUG] Online templates:" + for tmpl in "${ONLINE_TEMPLATES[@]}"; do + echo " - $tmpl" + done + fi + + ONLINE_TEMPLATE="" + [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" + + #msg_debug "SEARCH_PATTERN='${SEARCH_PATTERN}' TEMPLATE_PATTERN='${TEMPLATE_PATTERN}'" + #msg_debug "Found ${#LOCAL_TEMPLATES[@]} local templates, ${#ONLINE_TEMPLATES[@]} online templates" + if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then + #msg_debug "First 3 online templates:" + count=0 + for idx in "${!ONLINE_TEMPLATES[@]}"; do + #msg_debug " [$idx]: ${ONLINE_TEMPLATES[$idx]}" + ((count++)) + [[ $count -ge 3 ]] && break + done + fi + #msg_debug "ONLINE_TEMPLATE='$ONLINE_TEMPLATE'" + + if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then + TEMPLATE="${LOCAL_TEMPLATES[-1]}" + TEMPLATE_SOURCE="local" + else + TEMPLATE="$ONLINE_TEMPLATE" + TEMPLATE_SOURCE="online" + fi + + # If still no template, try to find alternatives + if [[ -z "$TEMPLATE" ]]; then + echo "" + echo "[DEBUG] No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}, searching for alternatives..." + + # Get all available versions for this OS type + mapfile -t AVAILABLE_VERSIONS < <( + pveam available -section system 2>/dev/null | + grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | + awk -F'\t' '{print $1}' | + grep "^${PCT_OSTYPE}-" | + sed -E "s/.*${PCT_OSTYPE}-([0-9]+(\.[0-9]+)?).*/\1/" | + sort -u -V 2>/dev/null + ) + + if [[ ${#AVAILABLE_VERSIONS[@]} -gt 0 ]]; then + echo "" + echo "${BL}Available ${PCT_OSTYPE} versions:${CL}" + for i in "${!AVAILABLE_VERSIONS[@]}"; do + echo " [$((i + 1))] ${AVAILABLE_VERSIONS[$i]}" + done + echo "" + read -p "Select version [1-${#AVAILABLE_VERSIONS[@]}] or press Enter to cancel: " choice + + if [[ "$choice" =~ ^[0-9]+$ ]] && [[ "$choice" -ge 1 ]] && [[ "$choice" -le ${#AVAILABLE_VERSIONS[@]} ]]; then + PCT_OSVERSION="${AVAILABLE_VERSIONS[$((choice - 1))]}" + TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION}" + SEARCH_PATTERN="^${TEMPLATE_SEARCH}-" + + #echo "[DEBUG] Retrying with version: $PCT_OSVERSION" + + mapfile -t ONLINE_TEMPLATES < <( + pveam available -section system 2>/dev/null | + grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | + awk -F'\t' '{print $1}' | + grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | + sort -t - -k 2 -V 2>/dev/null || true + ) + + if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then + TEMPLATE="${ONLINE_TEMPLATES[-1]}" + TEMPLATE_SOURCE="online" + #echo "[DEBUG] Found alternative: $TEMPLATE" + else + msg_error "No templates available for ${PCT_OSTYPE} ${PCT_OSVERSION}" + exit 225 + fi + else + msg_info "Installation cancelled" + exit 0 + fi + else + msg_error "No ${PCT_OSTYPE} templates available at all" + exit 225 + fi + fi + + #echo "[DEBUG] Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'" + #msg_debug "Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'" + + TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" + if [[ -z "$TEMPLATE_PATH" ]]; then + TEMPLATE_BASE=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) + [[ -n "$TEMPLATE_BASE" ]] && TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE" + fi + + # If we still don't have a path but have a valid template name, construct it + if [[ -z "$TEMPLATE_PATH" && -n "$TEMPLATE" ]]; then + TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" + fi + + [[ -n "$TEMPLATE_PATH" ]] || { + if [[ -z "$TEMPLATE" ]]; then + msg_error "Template ${PCT_OSTYPE} ${PCT_OSVERSION} not available" + + # Get available versions + mapfile -t AVAILABLE_VERSIONS < <( + pveam available -section system 2>/dev/null | + grep "^${PCT_OSTYPE}-" | + sed -E 's/.*'"${PCT_OSTYPE}"'-([0-9]+\.[0-9]+).*/\1/' | + grep -E '^[0-9]+\.[0-9]+$' | + sort -u -V 2>/dev/null || sort -u + ) + + if [[ ${#AVAILABLE_VERSIONS[@]} -gt 0 ]]; then + echo -e "\n${BL}Available versions:${CL}" + for i in "${!AVAILABLE_VERSIONS[@]}"; do + echo " [$((i + 1))] ${AVAILABLE_VERSIONS[$i]}" + done + + echo "" + read -p "Select version [1-${#AVAILABLE_VERSIONS[@]}] or Enter to exit: " choice + + if [[ "$choice" =~ ^[0-9]+$ ]] && [[ "$choice" -ge 1 ]] && [[ "$choice" -le ${#AVAILABLE_VERSIONS[@]} ]]; then + export var_version="${AVAILABLE_VERSIONS[$((choice - 1))]}" + export PCT_OSVERSION="$var_version" + msg_ok "Switched to ${PCT_OSTYPE} ${var_version}" + + # Retry template search with new version + TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" + SEARCH_PATTERN="^${TEMPLATE_SEARCH}-" + + mapfile -t LOCAL_TEMPLATES < <( + pveam list "$TEMPLATE_STORAGE" 2>/dev/null | + awk -v search="${SEARCH_PATTERN}" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' | + sed 's|.*/||' | sort -t - -k 2 -V + ) + mapfile -t ONLINE_TEMPLATES < <( + pveam available -section system 2>/dev/null | + grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | + awk -F'\t' '{print $1}' | + grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | + sort -t - -k 2 -V 2>/dev/null || true + ) + ONLINE_TEMPLATE="" + [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" + + if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then + TEMPLATE="${LOCAL_TEMPLATES[-1]}" + TEMPLATE_SOURCE="local" + else + TEMPLATE="$ONLINE_TEMPLATE" + TEMPLATE_SOURCE="online" + fi + + TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" + if [[ -z "$TEMPLATE_PATH" ]]; then + TEMPLATE_BASE=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) + [[ -n "$TEMPLATE_BASE" ]] && TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE" + fi + + # If we still don't have a path but have a valid template name, construct it + if [[ -z "$TEMPLATE_PATH" && -n "$TEMPLATE" ]]; then + TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" + fi + + [[ -n "$TEMPLATE_PATH" ]] || { + msg_error "Template still not found after version change" + exit 220 + } + else + msg_info "Installation cancelled" + exit 1 + fi + else + msg_error "No ${PCT_OSTYPE} templates available" + exit 220 + fi + fi + } + + # Validate that we found a template + if [[ -z "$TEMPLATE" ]]; then + msg_error "No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}" + msg_info "Please check:" + msg_info " - Is pveam catalog available? (run: pveam available -section system)" + msg_info " - Does the template exist for your OS version?" + exit 225 + fi + + msg_ok "Template ${BL}$TEMPLATE${CL} [$TEMPLATE_SOURCE]" + msg_debug "Resolved TEMPLATE_PATH=$TEMPLATE_PATH" + + NEED_DOWNLOAD=0 + if [[ ! -f "$TEMPLATE_PATH" ]]; then + msg_info "Template not present locally – will download." + NEED_DOWNLOAD=1 + elif [[ ! -r "$TEMPLATE_PATH" ]]; then + msg_error "Template file exists but is not readable – check permissions." + exit 221 + elif [[ "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then + if [[ -n "$ONLINE_TEMPLATE" ]]; then + msg_warn "Template file too small (<1MB) – re-downloading." + NEED_DOWNLOAD=1 + else + msg_warn "Template looks too small, but no online version exists. Keeping local file." + fi + elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then + if [[ -n "$ONLINE_TEMPLATE" ]]; then + msg_warn "Template appears corrupted – re-downloading." + NEED_DOWNLOAD=1 + else + msg_warn "Template appears corrupted, but no online version exists. Keeping local file." + fi + else + $STD msg_ok "Template $TEMPLATE is present and valid." + fi + + if [[ "$TEMPLATE_SOURCE" == "local" && -n "$ONLINE_TEMPLATE" && "$TEMPLATE" != "$ONLINE_TEMPLATE" ]]; then + msg_warn "Local template is outdated: $TEMPLATE (latest available: $ONLINE_TEMPLATE)" + if whiptail --yesno "A newer template is available:\n$ONLINE_TEMPLATE\n\nDo you want to download and use it instead?" 12 70; then + TEMPLATE="$ONLINE_TEMPLATE" + NEED_DOWNLOAD=1 + else + msg_info "Continuing with local template $TEMPLATE" + fi + fi + + if [[ "$NEED_DOWNLOAD" -eq 1 ]]; then + [[ -f "$TEMPLATE_PATH" ]] && rm -f "$TEMPLATE_PATH" + for attempt in {1..3}; do + msg_info "Attempt $attempt: Downloading template $TEMPLATE to $TEMPLATE_STORAGE" + if pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null 2>&1; then + msg_ok "Template download successful." + break + fi + if [[ $attempt -eq 3 ]]; then + msg_error "Failed after 3 attempts. Please check network access, permissions, or manually run:\n pveam download $TEMPLATE_STORAGE $TEMPLATE" + exit 222 + fi + sleep $((attempt * 5)) + done + fi + + if ! pveam list "$TEMPLATE_STORAGE" 2>/dev/null | grep -q "$TEMPLATE"; then + msg_error "Template $TEMPLATE not available in storage $TEMPLATE_STORAGE after download." + exit 223 + fi + + # ------------------------------------------------------------------------------ + # Dynamic preflight for Debian 13.x: offer upgrade if available (no hard mins) + # ------------------------------------------------------------------------------ + if [[ "$PCT_OSTYPE" == "debian" ]]; then + OSVER="$(parse_template_osver "$TEMPLATE")" + if [[ -n "$OSVER" ]]; then + # Proactive, aber ohne Abbruch – nur Angebot + offer_lxc_stack_upgrade_and_maybe_retry "no" || true + fi + fi + + # ------------------------------------------------------------------------------ + # Create LXC Container + # ------------------------------------------------------------------------------ + msg_info "Creating LXC container" + + # Ensure subuid/subgid entries exist + grep -q "root:100000:65536" /etc/subuid || echo "root:100000:65536" >>/etc/subuid + grep -q "root:100000:65536" /etc/subgid || echo "root:100000:65536" >>/etc/subgid + + # Assemble pct options + PCT_OPTIONS=(${PCT_OPTIONS[@]:-${DEFAULT_PCT_OPTIONS[@]}}) + [[ " ${PCT_OPTIONS[*]} " =~ " -rootfs " ]] || PCT_OPTIONS+=(-rootfs "$CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}") + + # Lock by template file (avoid concurrent downloads/creates) + lockfile="/tmp/template.${TEMPLATE}.lock" + exec 9>"$lockfile" || { + msg_error "Failed to create lock file '$lockfile'." + exit 200 + } + flock -w 60 9 || { + msg_error "Timeout while waiting for template lock." + exit 211 + } + + LOGFILE="/tmp/pct_create_${CTID}.log" + msg_debug "pct create command: pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" + msg_debug "Logfile: $LOGFILE" + + # First attempt + if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >"$LOGFILE" 2>&1; then + msg_error "Container creation failed on ${TEMPLATE_STORAGE}. Checking template..." + + # Validate template file + if [[ ! -s "$TEMPLATE_PATH" || "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then + msg_warn "Template file too small or missing – re-downloading." + rm -f "$TEMPLATE_PATH" + pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" + elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then + if [[ -n "$ONLINE_TEMPLATE" ]]; then + msg_warn "Template appears corrupted – re-downloading." + rm -f "$TEMPLATE_PATH" + pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" + else + msg_warn "Template appears corrupted, but no online version exists. Skipping re-download." + fi + fi + + # Retry after repair + if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then + # Fallback to local storage + if [[ "$TEMPLATE_STORAGE" != "local" ]]; then + msg_warn "Retrying container creation with fallback to local storage..." + LOCAL_TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" + if [[ ! -f "$LOCAL_TEMPLATE_PATH" ]]; then + msg_info "Downloading template to local..." + pveam download local "$TEMPLATE" >/dev/null 2>&1 + fi + if pct create "$CTID" "local:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then + msg_ok "Container successfully created using local fallback." + else + # --- Dynamic stack upgrade + auto-retry on the well-known error pattern --- + if grep -qiE 'unsupported .* version' "$LOGFILE"; then + echo + echo "pct reported 'unsupported ... version' – your LXC stack might be too old for this template." + echo "We can try to upgrade 'pve-container' and 'lxc-pve' now and retry automatically." + offer_lxc_stack_upgrade_and_maybe_retry "yes" + rc=$? + case $rc in + 0) : ;; # success - container created, continue + 2) + echo "Upgrade was declined. Please update and re-run: + apt update && apt install --only-upgrade pve-container lxc-pve" + exit 231 + ;; + 3) + echo "Upgrade and/or retry failed. Please inspect: $LOGFILE" + exit 231 + ;; + esac + else + msg_error "Container creation failed even with local fallback. See $LOGFILE" + if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then + set -x + bash -x -c "pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" 2>&1 | tee -a "$LOGFILE" + set +x + fi + exit 209 + fi + fi + else + msg_error "Container creation failed on local storage. See $LOGFILE" + # --- Dynamic stack upgrade + auto-retry on the well-known error pattern --- + if grep -qiE 'unsupported .* version' "$LOGFILE"; then + echo + echo "pct reported 'unsupported ... version' – your LXC stack might be too old for this template." + echo "We can try to upgrade 'pve-container' and 'lxc-pve' now and retry automatically." + offer_lxc_stack_upgrade_and_maybe_retry "yes" + rc=$? + case $rc in + 0) : ;; # success - container created, continue + 2) + echo "Upgrade was declined. Please update and re-run: + apt update && apt install --only-upgrade pve-container lxc-pve" + exit 231 + ;; + 3) + echo "Upgrade and/or retry failed. Please inspect: $LOGFILE" + exit 231 + ;; + esac + else + msg_error "Container creation failed. See $LOGFILE" + if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then + set -x + bash -x -c "pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" 2>&1 | tee -a "$LOGFILE" + set +x + fi + exit 209 + fi + fi + fi + fi + + # Verify container exists + pct list | awk '{print $1}' | grep -qx "$CTID" || { + msg_error "Container ID $CTID not listed in 'pct list'. See $LOGFILE" + exit 215 + } + + # Verify config rootfs + grep -q '^rootfs:' "/etc/pve/lxc/$CTID.conf" || { + msg_error "RootFS entry missing in container config. See $LOGFILE" + exit 216 + } + + msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created." +} + +# ------------------------------------------------------------------------------ +# description() +# +# - Sets container description with HTML content (logo, links, badges) +# - Restarts ping-instances.service if present +# - Posts status "done" to API +# ------------------------------------------------------------------------------ +description() { + IP=$(pct exec "$CTID" ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) + + # Generate LXC Description + DESCRIPTION=$( + cat < + + Logo + + +

${APP} LXC

+ +

+ + spend Coffee + +

+ + + + GitHub + + + + Discussions + + + + Issues + + +EOF + ) + pct set "$CTID" -description "$DESCRIPTION" + + if [[ -f /etc/systemd/system/ping-instances.service ]]; then + systemctl start ping-instances.service + fi + + post_update_to_api "done" "none" +} + +# ------------------------------------------------------------------------------ +# api_exit_script() +# +# - Exit trap handler +# - Reports exit codes to API with detailed reason +# - Handles known codes (100–209) and maps them to errors +# ------------------------------------------------------------------------------ +api_exit_script() { + exit_code=$? + if [ $exit_code -ne 0 ]; then + case $exit_code in + 100) post_update_to_api "failed" "100: Unexpected error in create_lxc.sh" ;; + 101) post_update_to_api "failed" "101: No network connection detected in create_lxc.sh" ;; + 200) post_update_to_api "failed" "200: LXC creation failed in create_lxc.sh" ;; + 201) post_update_to_api "failed" "201: Invalid Storage class in create_lxc.sh" ;; + 202) post_update_to_api "failed" "202: User aborted menu in create_lxc.sh" ;; + 203) post_update_to_api "failed" "203: CTID not set in create_lxc.sh" ;; + 204) post_update_to_api "failed" "204: PCT_OSTYPE not set in create_lxc.sh" ;; + 205) post_update_to_api "failed" "205: CTID cannot be less than 100 in create_lxc.sh" ;; + 206) post_update_to_api "failed" "206: CTID already in use in create_lxc.sh" ;; + 207) post_update_to_api "failed" "207: Template not found in create_lxc.sh" ;; + 208) post_update_to_api "failed" "208: Error downloading template in create_lxc.sh" ;; + 209) post_update_to_api "failed" "209: Container creation failed, but template is intact in create_lxc.sh" ;; + *) post_update_to_api "failed" "Unknown error, exit code: $exit_code in create_lxc.sh" ;; + esac + fi +} + +if command -v pveversion >/dev/null 2>&1; then + trap 'api_exit_script' EXIT +fi +trap 'post_update_to_api "failed" "$BASH_COMMAND"' ERR +trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT +trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM diff --git a/misc/build.func.backup-20251029-124307 b/misc/build.func.backup-20251029-124307 new file mode 100644 index 000000000..d452f4637 --- /dev/null +++ b/misc/build.func.backup-20251029-124307 @@ -0,0 +1,3517 @@ +#!/usr/bin/env bash +# Copyright (c) 2021-2025 community-scripts ORG +# Author: tteck (tteckster) | MickLesk | michelroegl-brunner +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Revision: 1 + +# ============================================================================== +# SECTION 1: CORE INITIALIZATION & VARIABLES +# ============================================================================== + +# ------------------------------------------------------------------------------ +# variables() +# +# - Normalize application name (NSAPP = lowercase, no spaces) +# - Build installer filename (var_install) +# - Define regex for integer validation +# - Fetch hostname of Proxmox node +# - Set default values for diagnostics/method +# - Generate random UUID for tracking +# - Get Proxmox VE version and kernel version +# ------------------------------------------------------------------------------ +variables() { + NSAPP=$(echo "${APP,,}" | tr -d ' ') # This function sets the NSAPP variable by converting the value of the APP variable to lowercase and removing any spaces. + var_install="${NSAPP}-install" # sets the var_install variable by appending "-install" to the value of NSAPP. + INTEGER='^[0-9]+([.][0-9]+)?$' # it defines the INTEGER regular expression pattern. + PVEHOST_NAME=$(hostname) # gets the Proxmox Hostname and sets it to Uppercase + DIAGNOSTICS="yes" # sets the DIAGNOSTICS variable to "yes", used for the API call. + METHOD="default" # sets the METHOD variable to "default", used for the API call. + RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" # generates a random UUID and sets it to the RANDOM_UUID variable. + CTTYPE="${CTTYPE:-${CT_TYPE:-1}}" + #CT_TYPE=${var_unprivileged:-$CT_TYPE} + + # Get Proxmox VE version and kernel version + if command -v pveversion >/dev/null 2>&1; then + PVEVERSION=$(pveversion | grep "pve-manager" | awk '{print $2}' | cut -d'/' -f1) + else + PVEVERSION="N/A" + fi + KERNEL_VERSION=$(uname -r) +} + +# ----------------------------------------------------------------------------- +# Community-Scripts bootstrap loader +# - Always sources build.func from remote +# - Updates local core files only if build.func changed +# - Local cache: /usr/local/community-scripts/core +# ----------------------------------------------------------------------------- + +# FUNC_DIR="/usr/local/community-scripts/core" +# mkdir -p "$FUNC_DIR" + +# BUILD_URL="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func" +# BUILD_REV="$FUNC_DIR/build.rev" +# DEVMODE="${DEVMODE:-no}" + +# # --- Step 1: fetch build.func content once, compute hash --- +# build_content="$(curl -fsSL "$BUILD_URL")" || { +# echo "❌ Failed to fetch build.func" +# exit 1 +# } + +# newhash=$(printf "%s" "$build_content" | sha256sum | awk '{print $1}') +# oldhash=$(cat "$BUILD_REV" 2>/dev/null || echo "") + +# # --- Step 2: if build.func changed, offer update for core files --- +# if [ "$newhash" != "$oldhash" ]; then +# echo "⚠️ build.func changed!" + +# while true; do +# read -rp "Refresh local core files? [y/N/diff]: " ans +# case "$ans" in +# [Yy]*) +# echo "$newhash" >"$BUILD_REV" + +# update_func_file() { +# local file="$1" +# local url="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/$file" +# local local_path="$FUNC_DIR/$file" + +# echo "⬇️ Downloading $file ..." +# curl -fsSL "$url" -o "$local_path" || { +# echo "❌ Failed to fetch $file" +# exit 1 +# } +# echo "✔️ Updated $file" +# } + +# update_func_file core.func +# update_func_file error_handler.func +# update_func_file tools.func +# break +# ;; +# [Dd]*) +# for file in core.func error_handler.func tools.func; do +# local_path="$FUNC_DIR/$file" +# url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/$file" +# remote_tmp="$(mktemp)" + +# curl -fsSL "$url" -o "$remote_tmp" || continue + +# if [ -f "$local_path" ]; then +# echo "🔍 Diff for $file:" +# diff -u "$local_path" "$remote_tmp" || echo "(no differences)" +# else +# echo "📦 New file $file will be installed" +# fi + +# rm -f "$remote_tmp" +# done +# ;; +# *) +# echo "❌ Skipped updating local core files" +# break +# ;; +# esac +# done +# else +# if [ "$DEVMODE" != "yes" ]; then +# echo "✔️ build.func unchanged → using existing local core files" +# fi +# fi + +# if [ -n "${_COMMUNITY_SCRIPTS_LOADER:-}" ]; then +# return 0 2>/dev/null || exit 0 +# fi +# _COMMUNITY_SCRIPTS_LOADER=1 + +# # --- Step 3: always source local versions of the core files --- +# source "$FUNC_DIR/core.func" +# source "$FUNC_DIR/error_handler.func" +# source "$FUNC_DIR/tools.func" + +# # --- Step 4: finally, source build.func directly from memory --- +# # (no tmp file needed) +# source <(printf "%s" "$build_content") + +# ------------------------------------------------------------------------------ +# Load core + error handler functions from community-scripts repo +# +# - Prefer curl if available, fallback to wget +# - Load: core.func, error_handler.func, api.func +# - Initialize error traps after loading +# ------------------------------------------------------------------------------ + +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) + +if command -v curl >/dev/null 2>&1; then + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) + load_functions + catch_errors + #echo "(build.func) Loaded core.func via curl" +elif command -v wget >/dev/null 2>&1; then + source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) + load_functions + catch_errors + #echo "(build.func) Loaded core.func via wget" +fi + +# ------------------------------------------------------------------------------ +# maxkeys_check() +# +# - Reads kernel keyring limits (maxkeys, maxbytes) +# - Checks current usage for LXC user (UID 100000) +# - Warns if usage is close to limits and suggests sysctl tuning +# - Exits if thresholds are exceeded +# - https://cleveruptime.com/docs/files/proc-key-users | https://docs.kernel.org/security/keys/core.html +# ------------------------------------------------------------------------------ + +maxkeys_check() { + # Read kernel parameters + per_user_maxkeys=$(cat /proc/sys/kernel/keys/maxkeys 2>/dev/null || echo 0) + per_user_maxbytes=$(cat /proc/sys/kernel/keys/maxbytes 2>/dev/null || echo 0) + + # Exit if kernel parameters are unavailable + if [[ "$per_user_maxkeys" -eq 0 || "$per_user_maxbytes" -eq 0 ]]; then + echo -e "${CROSS}${RD} Error: Unable to read kernel parameters. Ensure proper permissions.${CL}" + exit 1 + fi + + # Fetch key usage for user ID 100000 (typical for containers) + used_lxc_keys=$(awk '/100000:/ {print $2}' /proc/key-users 2>/dev/null || echo 0) + used_lxc_bytes=$(awk '/100000:/ {split($5, a, "/"); print a[1]}' /proc/key-users 2>/dev/null || echo 0) + + # Calculate thresholds and suggested new limits + threshold_keys=$((per_user_maxkeys - 100)) + threshold_bytes=$((per_user_maxbytes - 1000)) + new_limit_keys=$((per_user_maxkeys * 2)) + new_limit_bytes=$((per_user_maxbytes * 2)) + + # Check if key or byte usage is near limits + failure=0 + if [[ "$used_lxc_keys" -gt "$threshold_keys" ]]; then + echo -e "${CROSS}${RD} Warning: Key usage is near the limit (${used_lxc_keys}/${per_user_maxkeys}).${CL}" + echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxkeys=${new_limit_keys}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." + failure=1 + fi + if [[ "$used_lxc_bytes" -gt "$threshold_bytes" ]]; then + echo -e "${CROSS}${RD} Warning: Key byte usage is near the limit (${used_lxc_bytes}/${per_user_maxbytes}).${CL}" + echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxbytes=${new_limit_bytes}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." + failure=1 + fi + + # Provide next steps if issues are detected + if [[ "$failure" -eq 1 ]]; then + echo -e "${INFO} To apply changes, run: ${BOLD}service procps force-reload${CL}" + exit 1 + fi + + echo -e "${CM}${GN} All kernel key limits are within safe thresholds.${CL}" +} + +# ------------------------------------------------------------------------------ +# get_current_ip() +# +# - Returns current container IP depending on OS type +# - Debian/Ubuntu: uses `hostname -I` +# - Alpine: parses eth0 via `ip -4 addr` +# ------------------------------------------------------------------------------ +get_current_ip() { + if [ -f /etc/os-release ]; then + # Check for Debian/Ubuntu (uses hostname -I) + if grep -qE 'ID=debian|ID=ubuntu' /etc/os-release; then + CURRENT_IP=$(hostname -I | awk '{print $1}') + # Check for Alpine (uses ip command) + elif grep -q 'ID=alpine' /etc/os-release; then + CURRENT_IP=$(ip -4 addr show eth0 | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) + else + CURRENT_IP="Unknown" + fi + fi + echo "$CURRENT_IP" +} + +# ------------------------------------------------------------------------------ +# update_motd_ip() +# +# - Updates /etc/motd with current container IP +# - Removes old IP entries to avoid duplicates +# ------------------------------------------------------------------------------ +update_motd_ip() { + MOTD_FILE="/etc/motd" + + if [ -f "$MOTD_FILE" ]; then + # Remove existing IP Address lines to prevent duplication + sed -i '/IP Address:/d' "$MOTD_FILE" + + IP=$(get_current_ip) + # Add the new IP address + echo -e "${TAB}${NETWORK}${YW} IP Address: ${GN}${IP}${CL}" >>"$MOTD_FILE" + fi +} + +# ------------------------------------------------------------------------------ +# install_ssh_keys_into_ct() +# +# - Installs SSH keys into container root account if SSH is enabled +# - Uses pct push or direct input to authorized_keys +# - Falls back to warning if no keys provided +# ------------------------------------------------------------------------------ +install_ssh_keys_into_ct() { + [[ "$SSH" != "yes" ]] && return 0 + + if [[ -n "$SSH_KEYS_FILE" && -s "$SSH_KEYS_FILE" ]]; then + msg_info "Installing selected SSH keys into CT ${CTID}" + pct exec "$CTID" -- sh -c 'mkdir -p /root/.ssh && chmod 700 /root/.ssh' || { + msg_error "prepare /root/.ssh failed" + return 1 + } + pct push "$CTID" "$SSH_KEYS_FILE" /root/.ssh/authorized_keys >/dev/null 2>&1 || + pct exec "$CTID" -- sh -c "cat > /root/.ssh/authorized_keys" <"$SSH_KEYS_FILE" || { + msg_error "write authorized_keys failed" + return 1 + } + pct exec "$CTID" -- sh -c 'chmod 600 /root/.ssh/authorized_keys' || true + msg_ok "Installed SSH keys into CT ${CTID}" + return 0 + fi + + # Fallback: nichts ausgewählt + msg_warn "No SSH keys to install (skipping)." + return 0 +} + +# ------------------------------------------------------------------------------ +# base_settings() +# +# - Defines all base/default variables for container creation +# - Reads from environment variables (var_*) +# - Provides fallback defaults for OS type/version +# ------------------------------------------------------------------------------ +base_settings() { + # Default Settings + CT_TYPE=${var_unprivileged:-"1"} + DISK_SIZE=${var_disk:-"4"} + CORE_COUNT=${var_cpu:-"1"} + RAM_SIZE=${var_ram:-"1024"} + VERBOSE=${var_verbose:-"${1:-no}"} + PW=${var_pw:-""} + CT_ID=${var_ctid:-$NEXTID} + HN=${var_hostname:-$NSAPP} + BRG=${var_brg:-"vmbr0"} + NET=${var_net:-"dhcp"} + IPV6_METHOD=${var_ipv6_method:-"none"} + IPV6_STATIC=${var_ipv6_static:-""} + GATE=${var_gateway:-""} + APT_CACHER=${var_apt_cacher:-""} + APT_CACHER_IP=${var_apt_cacher_ip:-""} + MTU=${var_mtu:-""} + SD=${var_storage:-""} + NS=${var_ns:-""} + MAC=${var_mac:-""} + VLAN=${var_vlan:-""} + SSH=${var_ssh:-"no"} + SSH_AUTHORIZED_KEY=${var_ssh_authorized_key:-""} + UDHCPC_FIX=${var_udhcpc_fix:-""} + TAGS="community-script,${var_tags:-}" + ENABLE_FUSE=${var_fuse:-"${1:-no}"} + ENABLE_TUN=${var_tun:-"${1:-no}"} + + # Since these 2 are only defined outside of default_settings function, we add a temporary fallback. TODO: To align everything, we should add these as constant variables (e.g. OSTYPE and OSVERSION), but that would currently require updating the default_settings function for all existing scripts + if [ -z "$var_os" ]; then + var_os="debian" + fi + if [ -z "$var_version" ]; then + var_version="12" + fi +} + +# ------------------------------------------------------------------------------ +# echo_default() +# +# - Prints summary of default values (ID, OS, type, disk, RAM, CPU, etc.) +# - Uses icons and formatting for readability +# - Convert CT_TYPE to description +# ------------------------------------------------------------------------------ +echo_default() { + CT_TYPE_DESC="Unprivileged" + if [ "$CT_TYPE" -eq 0 ]; then + CT_TYPE_DESC="Privileged" + fi + echo -e "${INFO}${BOLD}${DGN}PVE Version ${PVEVERSION} (Kernel: ${KERNEL_VERSION})${CL}" + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}${CT_ID}${CL}" + echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os ($var_version)${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + if [ "$VERBOSE" == "yes" ]; then + echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}Enabled${CL}" + fi + echo -e "${CREATING}${BOLD}${BL}Creating a ${APP} LXC using the above default settings${CL}" + echo -e " " +} + +# ------------------------------------------------------------------------------ +# exit_script() +# +# - Called when user cancels an action +# - Clears screen and exits gracefully +# ------------------------------------------------------------------------------ +exit_script() { + clear + echo -e "\n${CROSS}${RD}User exited script${CL}\n" + exit +} + +# ------------------------------------------------------------------------------ +# find_host_ssh_keys() +# +# - Scans system for available SSH keys +# - Supports defaults (~/.ssh, /etc/ssh/authorized_keys) +# - Returns list of files containing valid SSH public keys +# - Sets FOUND_HOST_KEY_COUNT to number of keys found +# ------------------------------------------------------------------------------ +find_host_ssh_keys() { + local re='(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))' + local -a files=() cand=() + local g="${var_ssh_import_glob:-}" + local total=0 f base c + + shopt -s nullglob + if [[ -n "$g" ]]; then + for pat in $g; do cand+=($pat); done + else + cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2) + cand+=(/root/.ssh/*.pub) + cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*) + fi + shopt -u nullglob + + for f in "${cand[@]}"; do + [[ -f "$f" && -r "$f" ]] || continue + base="$(basename -- "$f")" + case "$base" in + known_hosts | known_hosts.* | config) continue ;; + id_*) [[ "$f" != *.pub ]] && continue ;; + esac + + # CRLF safe check for host keys + c=$(tr -d '\r' <"$f" | awk ' + /^[[:space:]]*#/ {next} + /^[[:space:]]*$/ {next} + {print} + ' | grep -E -c '"$re"' || true) + + if ((c > 0)); then + files+=("$f") + total=$((total + c)) + fi + done + + # Fallback to /root/.ssh/authorized_keys + if ((${#files[@]} == 0)) && [[ -r /root/.ssh/authorized_keys ]]; then + if grep -E -q "$re" /root/.ssh/authorized_keys; then + files+=(/root/.ssh/authorized_keys) + total=$((total + $(grep -E -c "$re" /root/.ssh/authorized_keys || echo 0))) + fi + fi + + FOUND_HOST_KEY_COUNT="$total" + ( + IFS=: + echo "${files[*]}" + ) +} + +# ------------------------------------------------------------------------------ +# advanced_settings() +# +# - Interactive whiptail menu for advanced configuration +# - Lets user set container type, password, CT ID, hostname, disk, CPU, RAM +# - Supports IPv4/IPv6, DNS, MAC, VLAN, tags, SSH keys, FUSE, verbose mode +# - Ends with confirmation or re-entry if cancelled +# ------------------------------------------------------------------------------ +advanced_settings() { + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox --title "Here is an instructional tip:" "To make a selection, use the Spacebar." 8 58 + # Setting Default Tag for Advanced Settings + TAGS="community-script;${var_tags:-}" + CT_DEFAULT_TYPE="${CT_TYPE}" + CT_TYPE="" + while [ -z "$CT_TYPE" ]; do + if [ "$CT_DEFAULT_TYPE" == "1" ]; then + if CT_TYPE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ + "1" "Unprivileged" ON \ + "0" "Privileged" OFF \ + 3>&1 1>&2 2>&3); then + if [ -n "$CT_TYPE" ]; then + CT_TYPE_DESC="Unprivileged" + if [ "$CT_TYPE" -eq 0 ]; then + CT_TYPE_DESC="Privileged" + fi + echo -e "${INFO}${BOLD}${DGN}PVE Version ${PVEVERSION} (Kernel: ${KERNEL_VERSION})${CL}" + echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os |${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" + fi + else + exit_script + fi + fi + if [ "$CT_DEFAULT_TYPE" == "0" ]; then + if CT_TYPE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ + "1" "Unprivileged" OFF \ + "0" "Privileged" ON \ + 3>&1 1>&2 2>&3); then + if [ -n "$CT_TYPE" ]; then + CT_TYPE_DESC="Unprivileged" + if [ "$CT_TYPE" -eq 0 ]; then + CT_TYPE_DESC="Privileged" + fi + echo -e "${INFO}${BOLD}${DGN}PVE Version ${PVEVERSION} (Kernel: ${KERNEL_VERSION})${CL}" + echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os${CL}" + echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" + fi + else + exit_script + fi + fi + done + + while true; do + if PW1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --passwordbox "\nSet Root Password (needed for root ssh access)" 9 58 --title "PASSWORD (leave blank for automatic login)" 3>&1 1>&2 2>&3); then + # Empty = Autologin + if [[ -z "$PW1" ]]; then + PW="" + PW1="Automatic Login" + echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}$PW1${CL}" + break + fi + + # Invalid: contains spaces + if [[ "$PW1" == *" "* ]]; then + whiptail --msgbox "Password cannot contain spaces." 8 58 + continue + fi + + # Invalid: too short + if ((${#PW1} < 5)); then + whiptail --msgbox "Password must be at least 5 characters." 8 58 + continue + fi + + # Confirm password + if PW2=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then + if [[ "$PW1" == "$PW2" ]]; then + PW="-password $PW1" + echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}" + break + else + whiptail --msgbox "Passwords do not match. Please try again." 8 58 + fi + else + exit_script + fi + else + exit_script + fi + done + + if CT_ID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Container ID" 8 58 "$NEXTID" --title "CONTAINER ID" 3>&1 1>&2 2>&3); then + if [ -z "$CT_ID" ]; then + CT_ID="$NEXTID" + fi + else + exit_script + fi + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" + + while true; do + if CT_NAME=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 "$NSAPP" --title "HOSTNAME" 3>&1 1>&2 2>&3); then + if [ -z "$CT_NAME" ]; then + HN="$NSAPP" + else + HN=$(echo "${CT_NAME,,}" | tr -d ' ') + fi + # Hostname validate (RFC 1123) + if [[ "$HN" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ ]]; then + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" + break + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --msgbox "❌ Invalid hostname: '$HN'\n\nOnly lowercase letters, digits and hyphens (-) are allowed.\nUnderscores (_) or other characters are not permitted!" 10 70 + fi + else + exit_script + fi + done + + while true; do + DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GB" 8 58 "$var_disk" --title "DISK SIZE" 3>&1 1>&2 2>&3) || exit_script + + if [ -z "$DISK_SIZE" ]; then + DISK_SIZE="$var_disk" + fi + + if [[ "$DISK_SIZE" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" + break + else + whiptail --msgbox "Disk size must be a positive integer!" 8 58 + fi + done + + while true; do + CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --inputbox "Allocate CPU Cores" 8 58 "$var_cpu" --title "CORE COUNT" 3>&1 1>&2 2>&3) || exit_script + + if [ -z "$CORE_COUNT" ]; then + CORE_COUNT="$var_cpu" + fi + + if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + break + else + whiptail --msgbox "CPU core count must be a positive integer!" 8 58 + fi + done + + while true; do + RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --inputbox "Allocate RAM in MiB" 8 58 "$var_ram" --title "RAM" 3>&1 1>&2 2>&3) || exit_script + + if [ -z "$RAM_SIZE" ]; then + RAM_SIZE="$var_ram" + fi + + if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + break + else + whiptail --msgbox "RAM size must be a positive integer!" 8 58 + fi + done + + IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f) + BRIDGES="" + OLD_IFS=$IFS + IFS=$'\n' + for iface_filepath in ${IFACE_FILEPATH_LIST}; do + + iface_indexes_tmpfile=$(mktemp -q -u '.iface-XXXX') + (grep -Pn '^\s*iface' "${iface_filepath}" | cut -d':' -f1 && wc -l "${iface_filepath}" | cut -d' ' -f1) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' >"${iface_indexes_tmpfile}" || true + + if [ -f "${iface_indexes_tmpfile}" ]; then + + while read -r pair; do + start=$(echo "${pair}" | cut -d':' -f1) + end=$(echo "${pair}" | cut -d':' -f2) + + if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then + iface_name=$(sed "${start}q;d" "${iface_filepath}" | awk '{print $2}') + BRIDGES="${iface_name}"$'\n'"${BRIDGES}" + fi + + done <"${iface_indexes_tmpfile}" + rm -f "${iface_indexes_tmpfile}" + fi + + done + IFS=$OLD_IFS + BRIDGES=$(echo "$BRIDGES" | grep -v '^\s*$' | sort | uniq) + if [[ -z "$BRIDGES" ]]; then + BRG="vmbr0" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + else + # Build bridge menu with descriptions + BRIDGE_MENU_OPTIONS=() + while IFS= read -r bridge; do + if [[ -n "$bridge" ]]; then + # Get description from Proxmox built-in method - find comment for this specific bridge + description=$(grep -A 10 "iface $bridge" /etc/network/interfaces | grep '^#' | head -n1 | sed 's/^#\s*//') + if [[ -n "$description" ]]; then + BRIDGE_MENU_OPTIONS+=("$bridge" "${description}") + else + BRIDGE_MENU_OPTIONS+=("$bridge" " ") + fi + fi + done <<<"$BRIDGES" + + BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge: " 18 55 6 "${BRIDGE_MENU_OPTIONS[@]}" 3>&1 1>&2 2>&3) + if [[ -z "$BRG" ]]; then + exit_script + else + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + fi + fi + + # IPv4 methods: dhcp, static, none + while true; do + IPV4_METHOD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "IPv4 Address Management" \ + --menu "Select IPv4 Address Assignment Method:" 12 60 2 \ + "dhcp" "Automatic (DHCP, recommended)" \ + "static" "Static (manual entry)" \ + 3>&1 1>&2 2>&3) + + exit_status=$? + if [ $exit_status -ne 0 ]; then + exit_script + fi + + case "$IPV4_METHOD" in + dhcp) + NET="dhcp" + GATE="" + echo -e "${NETWORK}${BOLD}${DGN}IPv4: DHCP${CL}" + break + ;; + static) + # Static: call and validate CIDR address + while true; do + NET=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Enter Static IPv4 CIDR Address (e.g. 192.168.100.50/24)" 8 58 "" \ + --title "IPv4 ADDRESS" 3>&1 1>&2 2>&3) + if [ -z "$NET" ]; then + whiptail --msgbox "IPv4 address must not be empty." 8 58 + continue + elif [[ "$NET" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then + echo -e "${NETWORK}${BOLD}${DGN}IPv4 Address: ${BGN}$NET${CL}" + break + else + whiptail --msgbox "$NET is not a valid IPv4 CIDR address. Please enter a correct value!" 8 58 + fi + done + + # call and validate Gateway + while true; do + GATE1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Enter Gateway IP address for static IPv4" 8 58 "" \ + --title "Gateway IP" 3>&1 1>&2 2>&3) + if [ -z "$GATE1" ]; then + whiptail --msgbox "Gateway IP address cannot be empty." 8 58 + elif [[ ! "$GATE1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then + whiptail --msgbox "Invalid Gateway IP address format." 8 58 + else + GATE=",gw=$GATE1" + echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE1${CL}" + break + fi + done + break + ;; + esac + done + + # IPv6 Address Management selection + while true; do + IPV6_METHOD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --menu \ + "Select IPv6 Address Management Type:" 15 58 4 \ + "auto" "SLAAC/AUTO (recommended, default)" \ + "dhcp" "DHCPv6" \ + "static" "Static (manual entry)" \ + "none" "Disabled" \ + --default-item "auto" 3>&1 1>&2 2>&3) + [ $? -ne 0 ] && exit_script + + case "$IPV6_METHOD" in + auto) + echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}SLAAC/AUTO${CL}" + IPV6_ADDR="" + IPV6_GATE="" + break + ;; + dhcp) + echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}DHCPv6${CL}" + IPV6_ADDR="dhcp" + IPV6_GATE="" + break + ;; + static) + # Ask for static IPv6 address (CIDR notation, e.g., 2001:db8::1234/64) + while true; do + IPV6_ADDR=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox \ + "Set a static IPv6 CIDR address (e.g., 2001:db8::1234/64)" 8 58 "" \ + --title "IPv6 STATIC ADDRESS" 3>&1 1>&2 2>&3) || exit_script + if [[ "$IPV6_ADDR" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+(/[0-9]{1,3})$ ]]; then + echo -e "${NETWORK}${BOLD}${DGN}IPv6 Address: ${BGN}$IPV6_ADDR${CL}" + break + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox \ + "$IPV6_ADDR is an invalid IPv6 CIDR address. Please enter a valid IPv6 CIDR address (e.g., 2001:db8::1234/64)" 8 58 + fi + done + # Optional: ask for IPv6 gateway for static config + while true; do + IPV6_GATE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox \ + "Enter IPv6 gateway address (optional, leave blank for none)" 8 58 "" --title "IPv6 GATEWAY" 3>&1 1>&2 2>&3) + if [ -z "$IPV6_GATE" ]; then + IPV6_GATE="" + break + elif [[ "$IPV6_GATE" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+$ ]]; then + break + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox \ + "Invalid IPv6 gateway format." 8 58 + fi + done + break + ;; + none) + echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}Disabled${CL}" + IPV6_ADDR="none" + IPV6_GATE="" + break + ;; + *) + exit_script + ;; + esac + done + + if [ "$var_os" == "alpine" ]; then + APT_CACHER="" + APT_CACHER_IP="" + else + if APT_CACHER_IP=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set APT-Cacher IP (leave blank for none)" 8 58 --title "APT-Cacher IP" 3>&1 1>&2 2>&3); then + APT_CACHER="${APT_CACHER_IP:+yes}" + echo -e "${NETWORK}${BOLD}${DGN}APT-Cacher IP Address: ${BGN}${APT_CACHER_IP:-Default}${CL}" + else + exit_script + fi + fi + + # if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "IPv6" --yesno "Disable IPv6?" 10 58); then + # DISABLEIP6="yes" + # else + # DISABLEIP6="no" + # fi + # echo -e "${DISABLEIPV6}${BOLD}${DGN}Disable IPv6: ${BGN}$DISABLEIP6${CL}" + + if MTU1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default [The MTU of your selected vmbr, default is 1500])" 8 58 --title "MTU SIZE" 3>&1 1>&2 2>&3); then + if [ -z "$MTU1" ]; then + MTU1="Default" + MTU="" + else + MTU=",mtu=$MTU1" + fi + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" + else + exit_script + fi + + if SD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a DNS Search Domain (leave blank for HOST)" 8 58 --title "DNS Search Domain" 3>&1 1>&2 2>&3); then + if [ -z "$SD" ]; then + SX=Host + SD="" + else + SX=$SD + SD="-searchdomain=$SD" + fi + echo -e "${SEARCH}${BOLD}${DGN}DNS Search Domain: ${BGN}$SX${CL}" + else + exit_script + fi + + if NX=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a DNS Server IP (leave blank for HOST)" 8 58 --title "DNS SERVER IP" 3>&1 1>&2 2>&3); then + if [ -z "$NX" ]; then + NX=Host + NS="" + else + NS="-nameserver=$NX" + fi + echo -e "${NETWORK}${BOLD}${DGN}DNS Server IP Address: ${BGN}$NX${CL}" + else + exit_script + fi + + if [ "$var_os" == "alpine" ] && [ "$NET" == "dhcp" ] && [ "$NX" != "Host" ]; then + UDHCPC_FIX="yes" + else + UDHCPC_FIX="no" + fi + export UDHCPC_FIX + + if MAC1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a MAC Address(leave blank for generated MAC)" 8 58 --title "MAC ADDRESS" 3>&1 1>&2 2>&3); then + if [ -z "$MAC1" ]; then + MAC1="Default" + MAC="" + else + MAC=",hwaddr=$MAC1" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" + fi + else + exit_script + fi + + if VLAN1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for no VLAN)" 8 58 --title "VLAN" 3>&1 1>&2 2>&3); then + if [ -z "$VLAN1" ]; then + VLAN1="Default" + VLAN="" + else + VLAN=",tag=$VLAN1" + fi + echo -e "${VLANTAG}${BOLD}${DGN}Vlan: ${BGN}$VLAN1${CL}" + else + exit_script + fi + + if ADV_TAGS=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Custom Tags?[If you remove all, there will be no tags!]" 8 58 "${TAGS}" --title "Advanced Tags" 3>&1 1>&2 2>&3); then + if [ -n "${ADV_TAGS}" ]; then + ADV_TAGS=$(echo "$ADV_TAGS" | tr -d '[:space:]') + TAGS="${ADV_TAGS}" + else + TAGS=";" + fi + echo -e "${NETWORK}${BOLD}${DGN}Tags: ${BGN}$TAGS${CL}" + else + exit_script + fi + + configure_ssh_settings + export SSH_KEYS_FILE + echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}" + if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "FUSE Support" --yesno "Enable FUSE support?\nRequired for tools like rclone, mergerfs, AppImage, etc." 10 58); then + ENABLE_FUSE="yes" + else + ENABLE_FUSE="no" + fi + echo -e "${FUSE}${BOLD}${DGN}Enable FUSE Support: ${BGN}$ENABLE_FUSE${CL}" + + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then + VERBOSE="yes" + else + VERBOSE="no" + fi + echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}" + + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create ${APP} LXC?" 10 58); then + echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}" + else + clear + header_info + echo -e "${INFO}${BOLD}${DGN}PVE Version ${PVEVERSION} (Kernel: ${KERNEL_VERSION})${CL}" + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings on node $PVEHOST_NAME${CL}" + advanced_settings + fi +} + +# ------------------------------------------------------------------------------ +# diagnostics_check() +# +# - Ensures diagnostics config file exists at /usr/local/community-scripts/diagnostics +# - Asks user whether to send anonymous diagnostic data +# - Saves DIAGNOSTICS=yes/no in the config file +# ------------------------------------------------------------------------------ +diagnostics_check() { + if ! [ -d "/usr/local/community-scripts" ]; then + mkdir -p /usr/local/community-scripts + fi + + if ! [ -f "/usr/local/community-scripts/diagnostics" ]; then + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS" --yesno "Send Diagnostics of LXC Installation?\n\n(This only transmits data without user data, just RAM, CPU, LXC name, ...)" 10 58); then + cat </usr/local/community-scripts/diagnostics +DIAGNOSTICS=yes + +#This file is used to store the diagnostics settings for the Community-Scripts API. +#https://github.com/community-scripts/ProxmoxVED/discussions/1836 +#Your diagnostics will be sent to the Community-Scripts API for troubleshooting/statistical purposes. +#You can review the data at https://community-scripts.github.io/ProxmoxVE/data +#If you do not wish to send diagnostics, please set the variable 'DIAGNOSTICS' to "no" in /usr/local/community-scripts/diagnostics, or use the menue. +#This will disable the diagnostics feature. +#To send diagnostics, set the variable 'DIAGNOSTICS' to "yes" in /usr/local/community-scripts/diagnostics, or use the menue. +#This will enable the diagnostics feature. +#The following information will be sent: +#"disk_size" +#"core_count" +#"ram_size" +#"os_type" +#"os_version" +#"nsapp" +#"method" +#"pve_version" +#"status" +#If you have any concerns, please review the source code at /misc/build.func +EOF + DIAGNOSTICS="yes" + else + cat </usr/local/community-scripts/diagnostics +DIAGNOSTICS=no + +#This file is used to store the diagnostics settings for the Community-Scripts API. +#https://github.com/community-scripts/ProxmoxVED/discussions/1836 +#Your diagnostics will be sent to the Community-Scripts API for troubleshooting/statistical purposes. +#You can review the data at https://community-scripts.github.io/ProxmoxVE/data +#If you do not wish to send diagnostics, please set the variable 'DIAGNOSTICS' to "no" in /usr/local/community-scripts/diagnostics, or use the menue. +#This will disable the diagnostics feature. +#To send diagnostics, set the variable 'DIAGNOSTICS' to "yes" in /usr/local/community-scripts/diagnostics, or use the menue. +#This will enable the diagnostics feature. +#The following information will be sent: +#"disk_size" +#"core_count" +#"ram_size" +#"os_type" +#"os_version" +#"nsapp" +#"method" +#"pve_version" +#"status" +#If you have any concerns, please review the source code at /misc/build.func +EOF + DIAGNOSTICS="no" + fi + else + DIAGNOSTICS=$(awk -F '=' '/^DIAGNOSTICS/ {print $2}' /usr/local/community-scripts/diagnostics) + + fi + +} + +# ------------------------------------------------------------------------------ +# default_var_settings +# +# - Ensures /usr/local/community-scripts/default.vars exists (creates if missing) +# - Loads var_* values from default.vars (safe parser, no source/eval) +# - Precedence: ENV var_* > default.vars > built-in defaults +# - Maps var_verbose → VERBOSE +# - Calls base_settings "$VERBOSE" and echo_default +# ------------------------------------------------------------------------------ +default_var_settings() { + # Allowed var_* keys (alphabetically sorted) + local VAR_WHITELIST=( + var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_ctid var_disk var_fuse + var_gateway var_hostname var_ipv6_method var_ipv6_static var_mac var_mtu + var_net var_ns var_pw var_ram var_tags var_tun var_unprivileged + var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage + ) + + # Snapshot: environment variables (highest precedence) + declare -A _HARD_ENV=() + local _k + for _k in "${VAR_WHITELIST[@]}"; do + if printenv "$_k" >/dev/null 2>&1; then _HARD_ENV["$_k"]=1; fi + done + + # Find default.vars location + local _find_default_vars + _find_default_vars() { + local f + for f in \ + /usr/local/community-scripts/default.vars \ + "$HOME/.config/community-scripts/default.vars" \ + "./default.vars"; do + [ -f "$f" ] && { + echo "$f" + return 0 + } + done + return 1 + } + # Allow override of storages via env (for non-interactive use cases) + [ -n "${var_template_storage:-}" ] && TEMPLATE_STORAGE="$var_template_storage" + [ -n "${var_container_storage:-}" ] && CONTAINER_STORAGE="$var_container_storage" + + # Create once, with storages already selected, no var_ctid/var_hostname lines + local _ensure_default_vars + _ensure_default_vars() { + _find_default_vars >/dev/null 2>&1 && return 0 + + local canonical="/usr/local/community-scripts/default.vars" + msg_info "No default.vars found. Creating ${canonical}" + mkdir -p /usr/local/community-scripts + + # Pick storages before writing the file (always ask unless only one) + # Create a minimal temp file to write into + : >"$canonical" + + # Base content (no var_ctid / var_hostname here) + cat >"$canonical" <<'EOF' +# Community-Scripts defaults (var_* only). Lines starting with # are comments. +# Precedence: ENV var_* > default.vars > built-ins. +# Keep keys alphabetically sorted. + +# Container type +var_unprivileged=1 + +# Resources +var_cpu=1 +var_disk=4 +var_ram=1024 + +# Network +var_brg=vmbr0 +var_net=dhcp +var_ipv6_method=none +# var_gateway= +# var_ipv6_static= +# var_vlan= +# var_mtu= +# var_mac= +# var_ns= + +# SSH +var_ssh=no +# var_ssh_authorized_key= + +# APT cacher (optional) +# var_apt_cacher=yes +# var_apt_cacher_ip=192.168.1.10 + +# Features/Tags/verbosity +var_fuse=no +var_tun=no +var_tags=community-script +var_verbose=no + +# Security (root PW) – empty => autologin +# var_pw= +EOF + + # Now choose storages (always prompt unless just one exists) + choose_and_set_storage_for_file "$canonical" template + choose_and_set_storage_for_file "$canonical" container + + chmod 0644 "$canonical" + msg_ok "Created ${canonical}" + } + + # Whitelist check + local _is_whitelisted_key + _is_whitelisted_key() { + local k="$1" + local w + for w in "${VAR_WHITELIST[@]}"; do [ "$k" = "$w" ] && return 0; done + return 1 + } + + # Safe parser for KEY=VALUE lines + local _load_vars_file + _load_vars_file() { + local file="$1" + [ -f "$file" ] || return 0 + msg_info "Loading defaults from ${file}" + local line key val + while IFS= read -r line || [ -n "$line" ]; do + line="${line#"${line%%[![:space:]]*}"}" + line="${line%"${line##*[![:space:]]}"}" + [[ -z "$line" || "$line" == \#* ]] && continue + if [[ "$line" =~ ^([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then + local var_key="${BASH_REMATCH[1]}" + local var_val="${BASH_REMATCH[2]}" + + [[ "$var_key" != var_* ]] && continue + _is_whitelisted_key "$var_key" || { + msg_debug "Ignore non-whitelisted ${var_key}" + continue + } + + # Strip quotes + if [[ "$var_val" =~ ^\"(.*)\"$ ]]; then + var_val="${BASH_REMATCH[1]}" + elif [[ "$var_val" =~ ^\'(.*)\'$ ]]; then + var_val="${BASH_REMATCH[1]}" + fi + + # Unsafe characters + case $var_val in + \"*\") + var_val=${var_val#\"} + var_val=${var_val%\"} + ;; + \'*\') + var_val=${var_val#\'} + var_val=${var_val%\'} + ;; + esac # Hard env wins + [[ -n "${_HARD_ENV[$var_key]:-}" ]] && continue + # Set only if not already exported + [[ -z "${!var_key+x}" ]] && export "${var_key}=${var_val}" + else + msg_warn "Malformed line in ${file}: ${line}" + fi + done <"$file" + msg_ok "Loaded ${file}" + } + + # 1) Ensure file exists + _ensure_default_vars + + # 2) Load file + local dv + dv="$(_find_default_vars)" || { + msg_error "default.vars not found after ensure step" + return 1 + } + _load_vars_file "$dv" + + # 3) Map var_verbose → VERBOSE + if [[ -n "${var_verbose:-}" ]]; then + case "${var_verbose,,}" in 1 | yes | true | on) VERBOSE="yes" ;; 0 | no | false | off) VERBOSE="no" ;; *) VERBOSE="${var_verbose}" ;; esac + else + VERBOSE="no" + fi + + # 4) Apply base settings and show summary + METHOD="mydefaults-global" + base_settings "$VERBOSE" + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using My Defaults (default.vars) on node $PVEHOST_NAME${CL}" + echo_default +} + +# ------------------------------------------------------------------------------ +# get_app_defaults_path() +# +# - Returns full path for app-specific defaults file +# - Example: /usr/local/community-scripts/defaults/.vars +# ------------------------------------------------------------------------------ + +get_app_defaults_path() { + local n="${NSAPP:-${APP,,}}" + echo "/usr/local/community-scripts/defaults/${n}.vars" +} + +# ------------------------------------------------------------------------------ +# maybe_offer_save_app_defaults +# +# - Called after advanced_settings returned with fully chosen values. +# - If no .vars exists, offers to persist current advanced settings +# into /usr/local/community-scripts/defaults/.vars +# - Only writes whitelisted var_* keys. +# - Extracts raw values from flags like ",gw=..." ",mtu=..." etc. +# ------------------------------------------------------------------------------ +if ! declare -p VAR_WHITELIST >/dev/null 2>&1; then + declare -ag VAR_WHITELIST=( + var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_ctid var_disk var_fuse + var_gateway var_hostname var_ipv6_method var_ipv6_static var_mac var_mtu + var_net var_ns var_pw var_ram var_tags var_tun var_unprivileged + var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage + ) +fi + +# Note: _is_whitelisted_key() is defined above in default_var_settings section + +_sanitize_value() { + # Disallow Command-Substitution / Shell-Meta + case "$1" in + *'$('* | *'`'* | *';'* | *'&'* | *'<('*) + echo "" + return 0 + ;; + esac + echo "$1" +} + +# Map-Parser: read var_* from file into _VARS_IN associative array +# Note: Main _load_vars_file() with full validation is defined in default_var_settings section +# This simplified version is used specifically for diff operations via _VARS_IN array +declare -A _VARS_IN +_load_vars_file_to_map() { + local file="$1" + [ -f "$file" ] || return 0 + _VARS_IN=() # Clear array + local line key val + while IFS= read -r line || [ -n "$line" ]; do + line="${line#"${line%%[![:space:]]*}"}" + line="${line%"${line##*[![:space:]]}"}" + [ -z "$line" ] && continue + case "$line" in + \#*) continue ;; + esac + key=$(printf "%s" "$line" | cut -d= -f1) + val=$(printf "%s" "$line" | cut -d= -f2-) + case "$key" in + var_*) + if _is_whitelisted_key "$key"; then + _VARS_IN["$key"]="$val" + fi + ;; + esac + done <"$file" +} + +# Diff function for two var_* files -> produces human-readable diff list for $1 (old) vs $2 (new) +_build_vars_diff() { + local oldf="$1" newf="$2" + local k + local -A OLD=() NEW=() + _load_vars_file_to_map "$oldf" + for k in "${!_VARS_IN[@]}"; do OLD["$k"]="${_VARS_IN[$k]}"; done + _load_vars_file_to_map "$newf" + for k in "${!_VARS_IN[@]}"; do NEW["$k"]="${_VARS_IN[$k]}"; done + + local out + out+="# Diff for ${APP} (${NSAPP})\n" + out+="# Old: ${oldf}\n# New: ${newf}\n\n" + + local found_change=0 + + # Changed & Removed + for k in "${!OLD[@]}"; do + if [[ -v NEW["$k"] ]]; then + if [[ "${OLD[$k]}" != "${NEW[$k]}" ]]; then + out+="~ ${k}\n - old: ${OLD[$k]}\n + new: ${NEW[$k]}\n" + found_change=1 + fi + else + out+="- ${k}\n - old: ${OLD[$k]}\n" + found_change=1 + fi + done + + # Added + for k in "${!NEW[@]}"; do + if [[ ! -v OLD["$k"] ]]; then + out+="+ ${k}\n + new: ${NEW[$k]}\n" + found_change=1 + fi + done + + if [[ $found_change -eq 0 ]]; then + out+="(No differences)\n" + fi + + printf "%b" "$out" +} + +# Build a temporary .vars file from current advanced settings +_build_current_app_vars_tmp() { + tmpf="$(mktemp /tmp/${NSAPP:-app}.vars.new.XXXXXX)" + + # NET/GW + _net="${NET:-}" + _gate="" + case "${GATE:-}" in + ,gw=*) _gate=$(echo "$GATE" | sed 's/^,gw=//') ;; + esac + + # IPv6 + _ipv6_method="${IPV6_METHOD:-auto}" + _ipv6_static="" + _ipv6_gateway="" + if [ "$_ipv6_method" = "static" ]; then + _ipv6_static="${IPV6_ADDR:-}" + _ipv6_gateway="${IPV6_GATE:-}" + fi + + # MTU/VLAN/MAC + _mtu="" + _vlan="" + _mac="" + case "${MTU:-}" in + ,mtu=*) _mtu=$(echo "$MTU" | sed 's/^,mtu=//') ;; + esac + case "${VLAN:-}" in + ,tag=*) _vlan=$(echo "$VLAN" | sed 's/^,tag=//') ;; + esac + case "${MAC:-}" in + ,hwaddr=*) _mac=$(echo "$MAC" | sed 's/^,hwaddr=//') ;; + esac + + # DNS / Searchdomain + _ns="" + _searchdomain="" + case "${NS:-}" in + -nameserver=*) _ns=$(echo "$NS" | sed 's/^-nameserver=//') ;; + esac + case "${SD:-}" in + -searchdomain=*) _searchdomain=$(echo "$SD" | sed 's/^-searchdomain=//') ;; + esac + + # SSH / APT / Features + _ssh="${SSH:-no}" + _ssh_auth="${SSH_AUTHORIZED_KEY:-}" + _apt_cacher="${APT_CACHER:-}" + _apt_cacher_ip="${APT_CACHER_IP:-}" + _fuse="${ENABLE_FUSE:-no}" + _tun="${ENABLE_TUN:-no}" + _tags="${TAGS:-}" + _verbose="${VERBOSE:-no}" + + # Type / Resources / Identity + _unpriv="${CT_TYPE:-1}" + _cpu="${CORE_COUNT:-1}" + _ram="${RAM_SIZE:-1024}" + _disk="${DISK_SIZE:-4}" + _hostname="${HN:-$NSAPP}" + + # Storage + _tpl_storage="${TEMPLATE_STORAGE:-${var_template_storage:-}}" + _ct_storage="${CONTAINER_STORAGE:-${var_container_storage:-}}" + + { + echo "# App-specific defaults for ${APP} (${NSAPP})" + echo "# Generated on $(date -u '+%Y-%m-%dT%H:%M:%SZ')" + echo + + echo "var_unprivileged=$(_sanitize_value "$_unpriv")" + echo "var_cpu=$(_sanitize_value "$_cpu")" + echo "var_ram=$(_sanitize_value "$_ram")" + echo "var_disk=$(_sanitize_value "$_disk")" + + [ -n "${BRG:-}" ] && echo "var_brg=$(_sanitize_value "$BRG")" + [ -n "$_net" ] && echo "var_net=$(_sanitize_value "$_net")" + [ -n "$_gate" ] && echo "var_gateway=$(_sanitize_value "$_gate")" + [ -n "$_mtu" ] && echo "var_mtu=$(_sanitize_value "$_mtu")" + [ -n "$_vlan" ] && echo "var_vlan=$(_sanitize_value "$_vlan")" + [ -n "$_mac" ] && echo "var_mac=$(_sanitize_value "$_mac")" + [ -n "$_ns" ] && echo "var_ns=$(_sanitize_value "$_ns")" + + [ -n "$_ipv6_method" ] && echo "var_ipv6_method=$(_sanitize_value "$_ipv6_method")" + [ -n "$_ipv6_static" ] && echo "var_ipv6_static=$(_sanitize_value "$_ipv6_static")" + + [ -n "$_ssh" ] && echo "var_ssh=$(_sanitize_value "$_ssh")" + [ -n "$_ssh_auth" ] && echo "var_ssh_authorized_key=$(_sanitize_value "$_ssh_auth")" + + [ -n "$_apt_cacher" ] && echo "var_apt_cacher=$(_sanitize_value "$_apt_cacher")" + [ -n "$_apt_cacher_ip" ] && echo "var_apt_cacher_ip=$(_sanitize_value "$_apt_cacher_ip")" + + [ -n "$_fuse" ] && echo "var_fuse=$(_sanitize_value "$_fuse")" + [ -n "$_tun" ] && echo "var_tun=$(_sanitize_value "$_tun")" + [ -n "$_tags" ] && echo "var_tags=$(_sanitize_value "$_tags")" + [ -n "$_verbose" ] && echo "var_verbose=$(_sanitize_value "$_verbose")" + + [ -n "$_hostname" ] && echo "var_hostname=$(_sanitize_value "$_hostname")" + [ -n "$_searchdomain" ] && echo "var_searchdomain=$(_sanitize_value "$_searchdomain")" + + [ -n "$_tpl_storage" ] && echo "var_template_storage=$(_sanitize_value "$_tpl_storage")" + [ -n "$_ct_storage" ] && echo "var_container_storage=$(_sanitize_value "$_ct_storage")" + } >"$tmpf" + + echo "$tmpf" +} + +# ------------------------------------------------------------------------------ +# maybe_offer_save_app_defaults() +# +# - Called after advanced_settings() +# - Offers to save current values as app defaults if not existing +# - If file exists: shows diff and allows Update, Keep, View Diff, or Cancel +# ------------------------------------------------------------------------------ +maybe_offer_save_app_defaults() { + local app_vars_path + app_vars_path="$(get_app_defaults_path)" + + # always build from current settings + local new_tmp diff_tmp + new_tmp="$(_build_current_app_vars_tmp)" + diff_tmp="$(mktemp -p /tmp "${NSAPP:-app}.vars.diff.XXXXXX")" + + # 1) if no file → offer to create + if [[ ! -f "$app_vars_path" ]]; then + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --yesno "Save these advanced settings as defaults for ${APP}?\n\nThis will create:\n${app_vars_path}" 12 72; then + mkdir -p "$(dirname "$app_vars_path")" + install -m 0644 "$new_tmp" "$app_vars_path" + msg_ok "Saved app defaults: ${app_vars_path}" + fi + rm -f "$new_tmp" "$diff_tmp" + return 0 + fi + + # 2) if file exists → build diff + _build_vars_diff "$app_vars_path" "$new_tmp" >"$diff_tmp" + + # if no differences → do nothing + if grep -q "^(No differences)$" "$diff_tmp"; then + rm -f "$new_tmp" "$diff_tmp" + return 0 + fi + + # 3) if file exists → show menu with default selection "Update Defaults" + local app_vars_file + app_vars_file="$(basename "$app_vars_path")" + + while true; do + local sel + sel="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "APP DEFAULTS – ${APP}" \ + --menu "Differences detected. What do you want to do?" 20 78 10 \ + "Update Defaults" "Write new values to ${app_vars_file}" \ + "Keep Current" "Keep existing defaults (no changes)" \ + "View Diff" "Show a detailed diff" \ + "Cancel" "Abort without changes" \ + --default-item "Update Defaults" \ + 3>&1 1>&2 2>&3)" || { sel="Cancel"; } + + case "$sel" in + "Update Defaults") + install -m 0644 "$new_tmp" "$app_vars_path" + msg_ok "Updated app defaults: ${app_vars_path}" + break + ;; + "Keep Current") + msg_info "Keeping current app defaults: ${app_vars_path}" + break + ;; + "View Diff") + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "Diff – ${APP}" \ + --scrolltext --textbox "$diff_tmp" 25 100 + ;; + "Cancel" | *) + msg_info "Canceled. No changes to app defaults." + break + ;; + esac + done + + rm -f "$new_tmp" "$diff_tmp" +} + +ensure_storage_selection_for_vars_file() { + local vf="$1" + + # Read stored values (if any) + local tpl ct + tpl=$(grep -E '^var_template_storage=' "$vf" | cut -d= -f2-) + ct=$(grep -E '^var_container_storage=' "$vf" | cut -d= -f2-) + + if [[ -n "$tpl" && -n "$ct" ]]; then + TEMPLATE_STORAGE="$tpl" + CONTAINER_STORAGE="$ct" + return 0 + fi + + choose_and_set_storage_for_file "$vf" template + choose_and_set_storage_for_file "$vf" container + + msg_ok "Storage configuration saved to $(basename "$vf")" +} + +diagnostics_menu() { + if [ "${DIAGNOSTICS:-no}" = "yes" ]; then + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "DIAGNOSTIC SETTINGS" \ + --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ + --yes-button "No" --no-button "Back"; then + DIAGNOSTICS="no" + sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=no/' /usr/local/community-scripts/diagnostics + whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 + fi + else + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "DIAGNOSTIC SETTINGS" \ + --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ + --yes-button "Yes" --no-button "Back"; then + DIAGNOSTICS="yes" + sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=yes/' /usr/local/community-scripts/diagnostics + whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 + fi + fi +} + +ensure_global_default_vars_file() { + local vars_path="/usr/local/community-scripts/default.vars" + if [[ ! -f "$vars_path" ]]; then + mkdir -p "$(dirname "$vars_path")" + touch "$vars_path" + fi + echo "$vars_path" +} + +# ------------------------------------------------------------------------------ +# install_script() +# +# - Main entrypoint for installation mode +# - Runs safety checks (pve_check, root_check, maxkeys_check, diagnostics_check) +# - Builds interactive menu (Default, Verbose, Advanced, My Defaults, App Defaults, Diagnostics, Storage, Exit) +# - Applies chosen settings and triggers container build +# ------------------------------------------------------------------------------ +install_script() { + pve_check + shell_check + root_check + arch_check + ssh_check + maxkeys_check + diagnostics_check + + if systemctl is-active -q ping-instances.service; then + systemctl -q stop ping-instances.service + fi + + NEXTID=$(pvesh get /cluster/nextid) + timezone=$(cat /etc/timezone) + + # Show APP Header + header_info + + # --- Support CLI argument as direct preset (default, advanced, …) --- + CHOICE="${mode:-${1:-}}" + + # If no CLI argument → show whiptail menu + # Build menu dynamically based on available options + local appdefaults_option="" + local settings_option="" + local menu_items=( + "1" "Default Install" + "2" "Advanced Install" + "3" "My Defaults" + ) + + if [ -f "$(get_app_defaults_path)" ]; then + appdefaults_option="4" + menu_items+=("4" "App Defaults for ${APP}") + settings_option="5" + menu_items+=("5" "Settings") + else + settings_option="4" + menu_items+=("4" "Settings") + fi + + if [ -z "$CHOICE" ]; then + + TMP_CHOICE=$(whiptail \ + --backtitle "Proxmox VE Helper Scripts" \ + --title "Community-Scripts Options" \ + --ok-button "Select" --cancel-button "Exit Script" \ + --notags \ + --menu "\nChoose an option:\n Use TAB or Arrow keys to navigate, ENTER to select.\n" \ + 20 60 9 \ + "${menu_items[@]}" \ + --default-item "1" \ + 3>&1 1>&2 2>&3) || exit_script + CHOICE="$TMP_CHOICE" + fi + + APPDEFAULTS_OPTION="$appdefaults_option" + SETTINGS_OPTION="$settings_option" + + # --- Main case --- + local defaults_target="" + local run_maybe_offer="no" + case "$CHOICE" in + 1 | default | DEFAULT) + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME${CL}" + VERBOSE="no" + METHOD="default" + base_settings "$VERBOSE" + echo_default + defaults_target="$(ensure_global_default_vars_file)" + ;; + 2 | advanced | ADVANCED) + header_info + + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Install on node $PVEHOST_NAME${CL}" + echo -e "${INFO}${BOLD}${DGN}PVE Version ${PVEVERSION} (Kernel: ${KERNEL_VERSION})${CL}" + METHOD="advanced" + base_settings + advanced_settings + defaults_target="$(ensure_global_default_vars_file)" + run_maybe_offer="yes" + ;; + 3 | mydefaults | MYDEFAULTS) + default_var_settings || { + msg_error "Failed to apply default.vars" + exit 1 + } + defaults_target="/usr/local/community-scripts/default.vars" + ;; + "$APPDEFAULTS_OPTION" | appdefaults | APPDEFAULTS) + if [ -f "$(get_app_defaults_path)" ]; then + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using App Defaults for ${APP} on node $PVEHOST_NAME${CL}" + METHOD="appdefaults" + base_settings + _load_vars_file "$(get_app_defaults_path)" + echo_default + defaults_target="$(get_app_defaults_path)" + else + msg_error "No App Defaults available for ${APP}" + exit 1 + fi + ;; + "$SETTINGS_OPTION" | settings | SETTINGS) + settings_menu + defaults_target="" + ;; + *) + echo -e "${CROSS}${RD}Invalid option: $CHOICE${CL}" + exit 1 + ;; + esac + + if [[ -n "$defaults_target" ]]; then + ensure_storage_selection_for_vars_file "$defaults_target" + fi + + if [[ "$run_maybe_offer" == "yes" ]]; then + maybe_offer_save_app_defaults + fi +} + +edit_default_storage() { + local vf="/usr/local/community-scripts/default.vars" + + # Ensure file exists + if [[ ! -f "$vf" ]]; then + mkdir -p "$(dirname "$vf")" + touch "$vf" + fi + + # Let ensure_storage_selection_for_vars_file handle everything + ensure_storage_selection_for_vars_file "$vf" +} + +settings_menu() { + while true; do + local settings_items=( + "1" "Manage API-Diagnostic Setting" + "2" "Edit Default.vars" + "3" "Edit Default Storage" + ) + if [ -f "$(get_app_defaults_path)" ]; then + settings_items+=("4" "Edit App.vars for ${APP}") + settings_items+=("5" "Exit") + else + settings_items+=("4" "Exit") + fi + + local choice + choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "Community-Scripts SETTINGS Menu" \ + --ok-button "OK" --cancel-button "Back" \ + --menu "\n\nChoose a settings option:\n\nUse TAB or Arrow keys to navigate, ENTER to select." 20 60 9 \ + "${settings_items[@]}" \ + 3>&1 1>&2 2>&3) || break + + case "$choice" in + 1) diagnostics_menu ;; + 2) ${EDITOR:-nano} /usr/local/community-scripts/default.vars ;; + 3) edit_default_storage ;; + 4) + if [ -f "$(get_app_defaults_path)" ]; then + ${EDITOR:-nano} "$(get_app_defaults_path)" + else + exit_script + fi + ;; + 5) exit_script ;; + esac + done +} + +# ===== Unified storage selection & writing to vars files ===== +_write_storage_to_vars() { + # $1 = vars_file, $2 = key (var_container_storage / var_template_storage), $3 = value + local vf="$1" key="$2" val="$3" + # remove uncommented and commented versions to avoid duplicates + sed -i "/^[#[:space:]]*${key}=/d" "$vf" + echo "${key}=${val}" >>"$vf" +} + +choose_and_set_storage_for_file() { + # $1 = vars_file, $2 = class ('container'|'template') + local vf="$1" class="$2" key="" current="" + case "$class" in + container) key="var_container_storage" ;; + template) key="var_template_storage" ;; + *) + msg_error "Unknown storage class: $class" + return 1 + ;; + esac + + current=$(awk -F= -v k="^${key}=" '$0 ~ k {print $2; exit}' "$vf") + + # If only one storage exists for the content type, auto-pick. Else always ask (your wish #4). + local content="rootdir" + [[ "$class" == "template" ]] && content="vztmpl" + local count + count=$(pvesm status -content "$content" | awk 'NR>1{print $1}' | wc -l) + + if [[ "$count" -eq 1 ]]; then + STORAGE_RESULT=$(pvesm status -content "$content" | awk 'NR>1{print $1; exit}') + STORAGE_INFO="" + else + # If the current value is preselectable, we could show it, but per your requirement we always offer selection + select_storage "$class" || return 1 + fi + + _write_storage_to_vars "$vf" "$key" "$STORAGE_RESULT" + + # Keep environment in sync for later steps (e.g. app-default save) + if [[ "$class" == "container" ]]; then + export var_container_storage="$STORAGE_RESULT" + export CONTAINER_STORAGE="$STORAGE_RESULT" + else + export var_template_storage="$STORAGE_RESULT" + export TEMPLATE_STORAGE="$STORAGE_RESULT" + fi + + msg_ok "Updated ${key} → ${STORAGE_RESULT}" +} + +# ------------------------------------------------------------------------------ +# check_container_resources() +# +# - Compares host RAM/CPU with required values +# - Warns if under-provisioned and asks user to continue or abort +# ------------------------------------------------------------------------------ +check_container_resources() { + current_ram=$(free -m | awk 'NR==2{print $2}') + current_cpu=$(nproc) + + if [[ "$current_ram" -lt "$var_ram" ]] || [[ "$current_cpu" -lt "$var_cpu" ]]; then + echo -e "\n${INFO}${HOLD} ${GN}Required: ${var_cpu} CPU, ${var_ram}MB RAM ${CL}| ${RD}Current: ${current_cpu} CPU, ${current_ram}MB RAM${CL}" + echo -e "${YWB}Please ensure that the ${APP} LXC is configured with at least ${var_cpu} vCPU and ${var_ram} MB RAM for the build process.${CL}\n" + echo -ne "${INFO}${HOLD} May cause data loss! ${INFO} Continue update with under-provisioned LXC? " + read -r prompt + if [[ ! ${prompt,,} =~ ^(yes)$ ]]; then + echo -e "${CROSS}${HOLD} ${YWB}Exiting based on user input.${CL}" + exit 1 + fi + else + echo -e "" + fi +} + +# ------------------------------------------------------------------------------ +# check_container_storage() +# +# - Checks /boot partition usage +# - Warns if usage >80% and asks user confirmation before proceeding +# ------------------------------------------------------------------------------ +check_container_storage() { + total_size=$(df /boot --output=size | tail -n 1) + local used_size=$(df /boot --output=used | tail -n 1) + usage=$((100 * used_size / total_size)) + if ((usage > 80)); then + echo -e "${INFO}${HOLD} ${YWB}Warning: Storage is dangerously low (${usage}%).${CL}" + echo -ne "Continue anyway? " + read -r prompt + if [[ ! ${prompt,,} =~ ^(y|yes)$ ]]; then + echo -e "${CROSS}${HOLD}${YWB}Exiting based on user input.${CL}" + exit 1 + fi + fi +} + +# ------------------------------------------------------------------------------ +# ssh_extract_keys_from_file() +# +# - Extracts valid SSH public keys from given file +# - Supports RSA, Ed25519, ECDSA and filters out comments/invalid lines +# ------------------------------------------------------------------------------ +ssh_extract_keys_from_file() { + local f="$1" + [[ -r "$f" ]] || return 0 + tr -d '\r' <"$f" | awk ' + /^[[:space:]]*#/ {next} + /^[[:space:]]*$/ {next} + # nackt: typ base64 [comment] + /^(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/ {print; next} + # mit Optionen: finde ab erstem Key-Typ + { + match($0, /(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/) + if (RSTART>0) { print substr($0, RSTART) } + } + ' +} + +# ------------------------------------------------------------------------------ +# ssh_build_choices_from_files() +# +# - Builds interactive whiptail checklist of available SSH keys +# - Generates fingerprint, type and comment for each key +# ------------------------------------------------------------------------------ +ssh_build_choices_from_files() { + local -a files=("$@") + CHOICES=() + COUNT=0 + MAPFILE="$(mktemp)" + local id key typ fp cmt base ln=0 + + for f in "${files[@]}"; do + [[ -f "$f" && -r "$f" ]] || continue + base="$(basename -- "$f")" + case "$base" in + known_hosts | known_hosts.* | config) continue ;; + id_*) [[ "$f" != *.pub ]] && continue ;; + esac + + # map every key in file + while IFS= read -r key; do + [[ -n "$key" ]] || continue + + typ="" + fp="" + cmt="" + # Only the pure key part (without options) is already included in ‘key’. + read -r _typ _b64 _cmt <<<"$key" + typ="${_typ:-key}" + cmt="${_cmt:-}" + # Fingerprint via ssh-keygen (if available) + if command -v ssh-keygen >/dev/null 2>&1; then + fp="$(printf '%s\n' "$key" | ssh-keygen -lf - 2>/dev/null | awk '{print $2}')" + fi + # Label shorten + [[ ${#cmt} -gt 40 ]] && cmt="${cmt:0:37}..." + + ln=$((ln + 1)) + COUNT=$((COUNT + 1)) + id="K${COUNT}" + echo "${id}|${key}" >>"$MAPFILE" + CHOICES+=("$id" "[$typ] ${fp:+$fp }${cmt:+$cmt }— ${base}" "OFF") + done < <(ssh_extract_keys_from_file "$f") + done +} + +# ------------------------------------------------------------------------------ +# ssh_discover_default_files() +# +# - Scans standard paths for SSH keys +# - Includes ~/.ssh/*.pub, /etc/ssh/authorized_keys, etc. +# ------------------------------------------------------------------------------ +ssh_discover_default_files() { + local -a cand=() + shopt -s nullglob + cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2) + cand+=(/root/.ssh/*.pub) + cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*) + shopt -u nullglob + printf '%s\0' "${cand[@]}" +} + +configure_ssh_settings() { + SSH_KEYS_FILE="$(mktemp)" + : >"$SSH_KEYS_FILE" + + IFS=$'\0' read -r -d '' -a _def_files < <(ssh_discover_default_files && printf '\0') + ssh_build_choices_from_files "${_def_files[@]}" + local default_key_count="$COUNT" + + local ssh_key_mode + if [[ "$default_key_count" -gt 0 ]]; then + ssh_key_mode=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ + "Provision SSH keys for root:" 14 72 4 \ + "found" "Select from detected keys (${default_key_count})" \ + "manual" "Paste a single public key" \ + "folder" "Scan another folder (path or glob)" \ + "none" "No keys" 3>&1 1>&2 2>&3) || exit_script + else + ssh_key_mode=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ + "No host keys detected; choose manual/none:" 12 72 2 \ + "manual" "Paste a single public key" \ + "none" "No keys" 3>&1 1>&2 2>&3) || exit_script + fi + + case "$ssh_key_mode" in + found) + local selection + selection=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT HOST KEYS" \ + --checklist "Select one or more keys to import:" 20 140 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script + for tag in $selection; do + tag="${tag%\"}" + tag="${tag#\"}" + local line + line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) + [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" + done + ;; + manual) + SSH_AUTHORIZED_KEY="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Paste one SSH public key line (ssh-ed25519/ssh-rsa/...)" 10 72 --title "SSH Public Key" 3>&1 1>&2 2>&3)" + [[ -n "$SSH_AUTHORIZED_KEY" ]] && printf '%s\n' "$SSH_AUTHORIZED_KEY" >>"$SSH_KEYS_FILE" + ;; + folder) + local glob_path + glob_path=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Enter a folder or glob to scan (e.g. /root/.ssh/*.pub)" 10 72 --title "Scan Folder/Glob" 3>&1 1>&2 2>&3) + if [[ -n "$glob_path" ]]; then + shopt -s nullglob + read -r -a _scan_files <<<"$glob_path" + shopt -u nullglob + if [[ "${#_scan_files[@]}" -gt 0 ]]; then + ssh_build_choices_from_files "${_scan_files[@]}" + if [[ "$COUNT" -gt 0 ]]; then + local folder_selection + folder_selection=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT FOLDER KEYS" \ + --checklist "Select key(s) to import:" 20 78 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script + for tag in $folder_selection; do + tag="${tag%\"}" + tag="${tag#\"}" + local line + line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) + [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" + done + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "No keys found in: $glob_path" 8 60 + fi + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "Path/glob returned no files." 8 60 + fi + fi + ;; + none) + : + ;; + esac + + if [[ -s "$SSH_KEYS_FILE" ]]; then + sort -u -o "$SSH_KEYS_FILE" "$SSH_KEYS_FILE" + printf '\n' >>"$SSH_KEYS_FILE" + fi + + if [[ -s "$SSH_KEYS_FILE" || "$PW" == -password* ]]; then + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable root SSH access?" 10 58); then + SSH="yes" + else + SSH="no" + fi + else + SSH="no" + fi +} + +# ------------------------------------------------------------------------------ +# start() +# +# - Entry point of script +# - On Proxmox host: calls install_script +# - In silent mode: runs update_script +# - Otherwise: shows update/setting menu +# ------------------------------------------------------------------------------ +start() { + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) + if command -v pveversion >/dev/null 2>&1; then + install_script || return 0 + return 0 + elif [ ! -z ${PHS_SILENT+x} ] && [[ "${PHS_SILENT}" == "1" ]]; then + VERBOSE="no" + set_std_mode + update_script + else + CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "${APP} LXC Update/Setting" --menu \ + "Support/Update functions for ${APP} LXC. Choose an option:" \ + 12 60 3 \ + "1" "YES (Silent Mode)" \ + "2" "YES (Verbose Mode)" \ + "3" "NO (Cancel Update)" --nocancel --default-item "1" 3>&1 1>&2 2>&3) + + case "$CHOICE" in + 1) + VERBOSE="no" + set_std_mode + ;; + 2) + VERBOSE="yes" + set_std_mode + ;; + 3) + clear + exit_script + exit + ;; + esac + update_script + fi +} + +# ------------------------------------------------------------------------------ +# build_container() +# +# - Creates and configures the LXC container +# - Builds network string and applies features (FUSE, TUN, VAAPI passthrough) +# - Starts container and waits for network connectivity +# - Installs base packages, SSH keys, and runs -install.sh +# ------------------------------------------------------------------------------ +build_container() { + # if [ "$VERBOSE" == "yes" ]; then set -x; fi + + NET_STRING="-net0 name=eth0,bridge=${BRG:-vmbr0}" + + # MAC + if [[ -n "$MAC" ]]; then + case "$MAC" in + ,hwaddr=*) NET_STRING+="$MAC" ;; + *) NET_STRING+=",hwaddr=$MAC" ;; + esac + fi + + # IP (immer zwingend, Standard dhcp) + NET_STRING+=",ip=${NET:-dhcp}" + + # Gateway + if [[ -n "$GATE" ]]; then + case "$GATE" in + ,gw=*) NET_STRING+="$GATE" ;; + *) NET_STRING+=",gw=$GATE" ;; + esac + fi + + # VLAN + if [[ -n "$VLAN" ]]; then + case "$VLAN" in + ,tag=*) NET_STRING+="$VLAN" ;; + *) NET_STRING+=",tag=$VLAN" ;; + esac + fi + + # MTU + if [[ -n "$MTU" ]]; then + case "$MTU" in + ,mtu=*) NET_STRING+="$MTU" ;; + *) NET_STRING+=",mtu=$MTU" ;; + esac + fi + + # IPv6 Handling + case "$IPV6_METHOD" in + auto) NET_STRING="$NET_STRING,ip6=auto" ;; + dhcp) NET_STRING="$NET_STRING,ip6=dhcp" ;; + static) + NET_STRING="$NET_STRING,ip6=$IPV6_ADDR" + [ -n "$IPV6_GATE" ] && NET_STRING="$NET_STRING,gw6=$IPV6_GATE" + ;; + none) ;; + esac + + if [ "$CT_TYPE" == "1" ]; then + FEATURES="keyctl=1,nesting=1" + else + FEATURES="nesting=1" + fi + + if [ "$ENABLE_FUSE" == "yes" ]; then + FEATURES="$FEATURES,fuse=1" + fi + + TEMP_DIR=$(mktemp -d) + pushd "$TEMP_DIR" >/dev/null + if [ "$var_os" == "alpine" ]; then + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-install.func)" + else + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" + fi + export DIAGNOSTICS="$DIAGNOSTICS" + export RANDOM_UUID="$RANDOM_UUID" + export CACHER="$APT_CACHER" + export CACHER_IP="$APT_CACHER_IP" + export tz="$timezone" + export APPLICATION="$APP" + export app="$NSAPP" + export PASSWORD="$PW" + export VERBOSE="$VERBOSE" + export SSH_ROOT="${SSH}" + export SSH_AUTHORIZED_KEY + export CTID="$CT_ID" + export CTTYPE="$CT_TYPE" + export ENABLE_FUSE="$ENABLE_FUSE" + export ENABLE_TUN="$ENABLE_TUN" + export PCT_OSTYPE="$var_os" + export PCT_OSVERSION="$var_version" + export PCT_DISK_SIZE="$DISK_SIZE" + export PCT_OPTIONS=" + -features $FEATURES + -hostname $HN + -tags $TAGS + $SD + $NS + $NET_STRING + -onboot 1 + -cores $CORE_COUNT + -memory $RAM_SIZE + -unprivileged $CT_TYPE + $PW +" + export TEMPLATE_STORAGE="${var_template_storage:-}" + export CONTAINER_STORAGE="${var_container_storage:-}" + create_lxc_container || exit $? + + LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" + + # ============================================================================ + # GPU/USB PASSTHROUGH CONFIGURATION + # ============================================================================ + + # List of applications that benefit from GPU acceleration + GPU_APPS=( + "immich" "channels" "emby" "ersatztv" "frigate" + "jellyfin" "plex" "scrypted" "tdarr" "unmanic" + "ollama" "fileflows" "open-webui" "tunarr" "debian" + "handbrake" "sunshine" "moonlight" "kodi" "stremio" + "viseron" + ) + + # Check if app needs GPU + is_gpu_app() { + local app="${1,,}" + for gpu_app in "${GPU_APPS[@]}"; do + [[ "$app" == "${gpu_app,,}" ]] && return 0 + done + return 1 + } + + # Detect all available GPU devices + detect_gpu_devices() { + INTEL_DEVICES=() + AMD_DEVICES=() + NVIDIA_DEVICES=() + + # Store PCI info to avoid multiple calls + local pci_vga_info=$(lspci -nn 2>/dev/null | grep -E "VGA|Display|3D") + + # Check for Intel GPU - look for Intel vendor ID [8086] + if echo "$pci_vga_info" | grep -q "\[8086:"; then + msg_info "Detected Intel GPU" + if [[ -d /dev/dri ]]; then + for d in /dev/dri/renderD* /dev/dri/card*; do + [[ -e "$d" ]] && INTEL_DEVICES+=("$d") + done + fi + fi + + # Check for AMD GPU - look for AMD vendor IDs [1002] (AMD/ATI) or [1022] (AMD) + if echo "$pci_vga_info" | grep -qE "\[1002:|\[1022:"; then + msg_info "Detected AMD GPU" + if [[ -d /dev/dri ]]; then + # Only add if not already claimed by Intel + if [[ ${#INTEL_DEVICES[@]} -eq 0 ]]; then + for d in /dev/dri/renderD* /dev/dri/card*; do + [[ -e "$d" ]] && AMD_DEVICES+=("$d") + done + fi + fi + fi + + # Check for NVIDIA GPU - look for NVIDIA vendor ID [10de] + if echo "$pci_vga_info" | grep -q "\[10de:"; then + msg_info "Detected NVIDIA GPU" + if ! check_nvidia_host_setup; then + msg_error "NVIDIA host setup incomplete. Skipping GPU passthrough." + msg_info "Fix NVIDIA drivers on host, then recreate container or passthrough manually." + return 0 + fi + + for d in /dev/nvidia* /dev/nvidiactl /dev/nvidia-modeset; do + [[ -e "$d" ]] && NVIDIA_DEVICES+=("$d") + done + + if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then + msg_warn "NVIDIA GPU detected but no /dev/nvidia* devices found" + msg_warn "Please install NVIDIA drivers on host: apt install nvidia-driver" + else + if [[ "$CT_TYPE" == "0" ]]; then + cat <>"$LXC_CONFIG" + # NVIDIA GPU Passthrough (privileged) + lxc.cgroup2.devices.allow: c 195:* rwm + lxc.cgroup2.devices.allow: c 243:* rwm + lxc.mount.entry: /dev/nvidia0 dev/nvidia0 none bind,optional,create=file + lxc.mount.entry: /dev/nvidiactl dev/nvidiactl none bind,optional,create=file + lxc.mount.entry: /dev/nvidia-uvm dev/nvidia-uvm none bind,optional,create=file + lxc.mount.entry: /dev/nvidia-uvm-tools dev/nvidia-uvm-tools none bind,optional,create=file +EOF + + if [[ -e /dev/dri/renderD128 ]]; then + echo "lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file" >>"$LXC_CONFIG" + fi + + export GPU_TYPE="NVIDIA" + export NVIDIA_DRIVER_VERSION=$(nvidia-smi --query-gpu=driver_version --format=csv,noheader 2>/dev/null | head -n1) + msg_ok "NVIDIA GPU passthrough configured (driver: ${NVIDIA_DRIVER_VERSION})" + else + msg_warn "NVIDIA passthrough only supported for privileged containers" + return 0 + fi + fi + fi + + # Debug output + msg_debug "Intel devices: ${INTEL_DEVICES[*]}" + msg_debug "AMD devices: ${AMD_DEVICES[*]}" + msg_debug "NVIDIA devices: ${NVIDIA_DEVICES[*]}" + } + + # Configure USB passthrough for privileged containers + configure_usb_passthrough() { + if [[ "$CT_TYPE" != "0" ]]; then + return 0 + fi + + msg_info "Configuring automatic USB passthrough (privileged container)" + cat <>"$LXC_CONFIG" +# Automatic USB passthrough (privileged container) +lxc.cgroup2.devices.allow: a +lxc.cap.drop: +lxc.cgroup2.devices.allow: c 188:* rwm +lxc.cgroup2.devices.allow: c 189:* rwm +lxc.mount.entry: /dev/serial/by-id dev/serial/by-id none bind,optional,create=dir +lxc.mount.entry: /dev/ttyUSB0 dev/ttyUSB0 none bind,optional,create=file +lxc.mount.entry: /dev/ttyUSB1 dev/ttyUSB1 none bind,optional,create=file +lxc.mount.entry: /dev/ttyACM0 dev/ttyACM0 none bind,optional,create=file +lxc.mount.entry: /dev/ttyACM1 dev/ttyACM1 none bind,optional,create=file +EOF + msg_ok "USB passthrough configured" + } + + # Configure GPU passthrough + configure_gpu_passthrough() { + # Skip if not a GPU app and not privileged + if [[ "$CT_TYPE" != "0" ]] && ! is_gpu_app "$APP"; then + return 0 + fi + + detect_gpu_devices + + # Count available GPU types + local gpu_count=0 + local available_gpus=() + + if [[ ${#INTEL_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("INTEL") + gpu_count=$((gpu_count + 1)) + fi + + if [[ ${#AMD_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("AMD") + gpu_count=$((gpu_count + 1)) + fi + + if [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("NVIDIA") + gpu_count=$((gpu_count + 1)) + fi + + if [[ $gpu_count -eq 0 ]]; then + msg_info "No GPU devices found for passthrough" + return 0 + fi + + local selected_gpu="" + + if [[ $gpu_count -eq 1 ]]; then + # Automatic selection for single GPU + selected_gpu="${available_gpus[0]}" + msg_info "Automatically configuring ${selected_gpu} GPU passthrough" + else + # Multiple GPUs - ask user + echo -e "\n${INFO} Multiple GPU types detected:" + for gpu in "${available_gpus[@]}"; do + echo " - $gpu" + done + read -rp "Which GPU type to passthrough? (${available_gpus[*]}): " selected_gpu + selected_gpu="${selected_gpu^^}" + + # Validate selection + local valid=0 + for gpu in "${available_gpus[@]}"; do + [[ "$selected_gpu" == "$gpu" ]] && valid=1 + done + + if [[ $valid -eq 0 ]]; then + msg_warn "Invalid selection. Skipping GPU passthrough." + return 0 + fi + fi + + # Apply passthrough configuration based on selection + local dev_idx=0 + + case "$selected_gpu" in + INTEL | AMD) + local devices=() + [[ "$selected_gpu" == "INTEL" ]] && devices=("${INTEL_DEVICES[@]}") + [[ "$selected_gpu" == "AMD" ]] && devices=("${AMD_DEVICES[@]}") + + # For Proxmox WebUI visibility, add as dev0, dev1 etc. + for dev in "${devices[@]}"; do + if [[ "$CT_TYPE" == "0" ]]; then + # Privileged container - use dev entries for WebUI visibility + # Use initial GID 104 (render) for renderD*, 44 (video) for card* + if [[ "$dev" =~ renderD ]]; then + echo "dev${dev_idx}: $dev,gid=104" >>"$LXC_CONFIG" + else + echo "dev${dev_idx}: $dev,gid=44" >>"$LXC_CONFIG" + fi + dev_idx=$((dev_idx + 1)) + + # Also add cgroup allows for privileged containers + local major minor + major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") + minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") + + if [[ "$major" != "0" && "$minor" != "0" ]]; then + echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" + fi + else + # Unprivileged container + if [[ "$dev" =~ renderD ]]; then + echo "dev${dev_idx}: $dev,uid=0,gid=104" >>"$LXC_CONFIG" + else + echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG" + fi + dev_idx=$((dev_idx + 1)) + fi + done + + export GPU_TYPE="$selected_gpu" + msg_ok "${selected_gpu} GPU passthrough configured (${dev_idx} devices)" + ;; + + NVIDIA) + if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then + msg_error "NVIDIA drivers not installed on host. Please install: apt install nvidia-driver" + return 1 + fi + + for dev in "${NVIDIA_DEVICES[@]}"; do + # NVIDIA devices typically need different handling + echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG" + dev_idx=$((dev_idx + 1)) + + if [[ "$CT_TYPE" == "0" ]]; then + local major minor + major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") + minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") + + if [[ "$major" != "0" && "$minor" != "0" ]]; then + echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" + fi + fi + done + + export GPU_TYPE="NVIDIA" + msg_ok "NVIDIA GPU passthrough configured (${dev_idx} devices)" + ;; + esac + } + + # Additional device passthrough + configure_additional_devices() { + # TUN device passthrough + if [ "$ENABLE_TUN" == "yes" ]; then + cat <>"$LXC_CONFIG" +lxc.cgroup2.devices.allow: c 10:200 rwm +lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file +EOF + fi + + # Coral TPU passthrough + if [[ -e /dev/apex_0 ]]; then + msg_info "Detected Coral TPU - configuring passthrough" + echo "lxc.mount.entry: /dev/apex_0 dev/apex_0 none bind,optional,create=file" >>"$LXC_CONFIG" + fi + } + + # Execute pre-start configurations + configure_usb_passthrough + configure_gpu_passthrough + configure_additional_devices + + # ============================================================================ + # START CONTAINER AND INSTALL USERLAND + # ============================================================================ + + msg_info "Starting LXC Container" + pct start "$CTID" + + # Wait for container to be running + for i in {1..10}; do + if pct status "$CTID" | grep -q "status: running"; then + msg_ok "Started LXC Container" + break + fi + sleep 1 + if [ "$i" -eq 10 ]; then + msg_error "LXC Container did not reach running state" + exit 1 + fi + done + + # Wait for network (skip for Alpine initially) + if [ "$var_os" != "alpine" ]; then + msg_info "Waiting for network in LXC container" + + # Wait for IP + for i in {1..20}; do + ip_in_lxc=$(pct exec "$CTID" -- ip -4 addr show dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) + [ -n "$ip_in_lxc" ] && break + sleep 1 + done + + if [ -z "$ip_in_lxc" ]; then + msg_error "No IP assigned to CT $CTID after 20s" + exit 1 + fi + + # Try to reach gateway + gw_ok=0 + for i in {1..10}; do + if pct exec "$CTID" -- ping -c1 -W1 "${GATEWAY:-8.8.8.8}" >/dev/null 2>&1; then + gw_ok=1 + break + fi + sleep 1 + done + + if [ "$gw_ok" -eq 1 ]; then + msg_ok "Network in LXC is reachable (IP $ip_in_lxc)" + else + msg_warn "Network reachable but gateway check failed" + fi + fi + # Function to get correct GID inside container + get_container_gid() { + local group="$1" + local gid=$(pct exec "$CTID" -- getent group "$group" 2>/dev/null | cut -d: -f3) + echo "${gid:-44}" # Default to 44 if not found + } + + fix_gpu_gids + + # Continue with standard container setup + msg_info "Customizing LXC Container" + + # # Install GPU userland if configured + # if [[ "${ENABLE_VAAPI:-0}" == "1" ]]; then + # install_gpu_userland "VAAPI" + # fi + + # if [[ "${ENABLE_NVIDIA:-0}" == "1" ]]; then + # install_gpu_userland "NVIDIA" + # fi + + # Continue with standard container setup + if [ "$var_os" == "alpine" ]; then + sleep 3 + pct exec "$CTID" -- /bin/sh -c 'cat </etc/apk/repositories +http://dl-cdn.alpinelinux.org/alpine/latest-stable/main +http://dl-cdn.alpinelinux.org/alpine/latest-stable/community +EOF' + pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses jq >/dev/null" + else + sleep 3 + pct exec "$CTID" -- bash -c "sed -i '/$LANG/ s/^# //' /etc/locale.gen" + pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \ + echo LANG=\$locale_line >/etc/default/locale && \ + locale-gen >/dev/null && \ + export LANG=\$locale_line" + + if [[ -z "${tz:-}" ]]; then + tz=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "Etc/UTC") + fi + + if pct exec "$CTID" -- test -e "/usr/share/zoneinfo/$tz"; then + pct exec "$CTID" -- bash -c "tz='$tz'; echo \"\$tz\" >/etc/timezone && ln -sf \"/usr/share/zoneinfo/\$tz\" /etc/localtime" + else + msg_warn "Skipping timezone setup – zone '$tz' not found in container" + fi + + pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y sudo curl mc gnupg2 jq >/dev/null" || { + msg_error "apt-get base packages installation failed" + exit 1 + } + fi + + msg_ok "Customized LXC Container" + + # Verify GPU access if enabled + if [[ "${ENABLE_VAAPI:-0}" == "1" ]] && [ "$var_os" != "alpine" ]; then + pct exec "$CTID" -- bash -c "vainfo >/dev/null 2>&1" && + msg_ok "VAAPI verified working" || + msg_warn "VAAPI verification failed - may need additional configuration" + fi + + if [[ "${ENABLE_NVIDIA:-0}" == "1" ]] && [ "$var_os" != "alpine" ]; then + pct exec "$CTID" -- bash -c "nvidia-smi >/dev/null 2>&1" && + msg_ok "NVIDIA verified working" || + msg_warn "NVIDIA verification failed - may need additional configuration" + fi + + # Install SSH keys + install_ssh_keys_into_ct + + # Run application installer + if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)"; then + exit $? + fi +} + +destroy_lxc() { + if [[ -z "$CT_ID" ]]; then + msg_error "No CT_ID found. Nothing to remove." + return 1 + fi + + # Abbruch bei Ctrl-C / Ctrl-D / ESC + trap 'echo; msg_error "Aborted by user (SIGINT/SIGQUIT)"; return 130' INT QUIT + + local prompt + if ! read -rp "Remove this Container? " prompt; then + # read gibt != 0 zurück bei Ctrl-D/ESC + msg_error "Aborted input (Ctrl-D/ESC)" + return 130 + fi + + case "${prompt,,}" in + y | yes) + if pct stop "$CT_ID" &>/dev/null && pct destroy "$CT_ID" &>/dev/null; then + msg_ok "Removed Container $CT_ID" + else + msg_error "Failed to remove Container $CT_ID" + return 1 + fi + ;; + "" | n | no) + msg_info "Container was not removed." + ;; + *) + msg_warn "Invalid response. Container was not removed." + ;; + esac +} + +# ------------------------------------------------------------------------------ +# Storage discovery / selection helpers +# ------------------------------------------------------------------------------ +# ===== Storage discovery / selection helpers (ported from create_lxc.sh) ===== +resolve_storage_preselect() { + local class="$1" preselect="$2" required_content="" + case "$class" in + template) required_content="vztmpl" ;; + container) required_content="rootdir" ;; + *) return 1 ;; + esac + [[ -z "$preselect" ]] && return 1 + if ! pvesm status -content "$required_content" | awk 'NR>1{print $1}' | grep -qx -- "$preselect"; then + msg_warn "Preselected storage '${preselect}' does not support content '${required_content}' (or not found)" + return 1 + fi + + local line total used free + line="$(pvesm status | awk -v s="$preselect" 'NR>1 && $1==s {print $0}')" + if [[ -z "$line" ]]; then + STORAGE_INFO="n/a" + else + total="$(awk '{print $4}' <<<"$line")" + used="$(awk '{print $5}' <<<"$line")" + free="$(awk '{print $6}' <<<"$line")" + local total_h used_h free_h + if command -v numfmt >/dev/null 2>&1; then + total_h="$(numfmt --to=iec --suffix=B --format %.1f "$total" 2>/dev/null || echo "$total")" + used_h="$(numfmt --to=iec --suffix=B --format %.1f "$used" 2>/dev/null || echo "$used")" + free_h="$(numfmt --to=iec --suffix=B --format %.1f "$free" 2>/dev/null || echo "$free")" + STORAGE_INFO="Free: ${free_h} Used: ${used_h}" + else + STORAGE_INFO="Free: ${free} Used: ${used}" + fi + fi + STORAGE_RESULT="$preselect" + return 0 +} + +fix_gpu_gids() { + if [[ -z "${GPU_TYPE:-}" ]]; then + return 0 + fi + + msg_info "Detecting and setting correct GPU group IDs" + + # Ermittle die tatsächlichen GIDs aus dem Container + local video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") + local render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") + + # Fallbacks wenn Gruppen nicht existieren + if [[ -z "$video_gid" ]]; then + # Versuche die video Gruppe zu erstellen + pct exec "$CTID" -- sh -c "groupadd -r video 2>/dev/null || true" + video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") + [[ -z "$video_gid" ]] && video_gid="44" # Ultimate fallback + fi + + if [[ -z "$render_gid" ]]; then + # Versuche die render Gruppe zu erstellen + pct exec "$CTID" -- sh -c "groupadd -r render 2>/dev/null || true" + render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") + [[ -z "$render_gid" ]] && render_gid="104" # Ultimate fallback + fi + + msg_info "Container GIDs detected - video:${video_gid}, render:${render_gid}" + + # Prüfe ob die GIDs von den Defaults abweichen + local need_update=0 + if [[ "$video_gid" != "44" ]] || [[ "$render_gid" != "104" ]]; then + need_update=1 + fi + + if [[ $need_update -eq 1 ]]; then + msg_info "Updating device GIDs in container config" + + # Stoppe Container für Config-Update + pct stop "$CTID" >/dev/null 2>&1 + + # Update die dev Einträge mit korrekten GIDs + # Backup der Config + cp "$LXC_CONFIG" "${LXC_CONFIG}.bak" + + # Parse und update jeden dev Eintrag + while IFS= read -r line; do + if [[ "$line" =~ ^dev[0-9]+: ]]; then + # Extract device path + local device_path=$(echo "$line" | sed -E 's/^dev[0-9]+: ([^,]+).*/\1/') + local dev_num=$(echo "$line" | sed -E 's/^(dev[0-9]+):.*/\1/') + + if [[ "$device_path" =~ renderD ]]; then + # RenderD device - use render GID + echo "${dev_num}: ${device_path},gid=${render_gid}" + elif [[ "$device_path" =~ card ]]; then + # Card device - use video GID + echo "${dev_num}: ${device_path},gid=${video_gid}" + else + # Keep original line + echo "$line" + fi + else + # Keep non-dev lines + echo "$line" + fi + done <"$LXC_CONFIG" >"${LXC_CONFIG}.new" + + mv "${LXC_CONFIG}.new" "$LXC_CONFIG" + + # Starte Container wieder + pct start "$CTID" >/dev/null 2>&1 + sleep 3 + + msg_ok "Device GIDs updated successfully" + else + msg_ok "Device GIDs are already correct" + fi + if [[ "$CT_TYPE" == "0" ]]; then + pct exec "$CTID" -- bash -c " + if [ -d /dev/dri ]; then + for dev in /dev/dri/*; do + if [ -e \"\$dev\" ]; then + if [[ \"\$dev\" =~ renderD ]]; then + chgrp ${render_gid} \"\$dev\" 2>/dev/null || true + else + chgrp ${video_gid} \"\$dev\" 2>/dev/null || true + fi + chmod 660 \"\$dev\" 2>/dev/null || true + fi + done + fi + " >/dev/null 2>&1 + fi +} + +# NVIDIA-spezific check on host +check_nvidia_host_setup() { + if ! command -v nvidia-smi >/dev/null 2>&1; then + msg_warn "NVIDIA GPU detected but nvidia-smi not found on host" + msg_warn "Please install NVIDIA drivers on host first." + #echo " 1. Download driver: wget https://us.download.nvidia.com/XFree86/Linux-x86_64/550.127.05/NVIDIA-Linux-x86_64-550.127.05.run" + #echo " 2. Install: ./NVIDIA-Linux-x86_64-550.127.05.run --dkms" + #echo " 3. Verify: nvidia-smi" + return 1 + fi + + # check if nvidia-smi works + if ! nvidia-smi >/dev/null 2>&1; then + msg_warn "nvidia-smi installed but not working. Driver issue?" + return 1 + fi + + return 0 +} + +check_storage_support() { + local CONTENT="$1" VALID=0 + while IFS= read -r line; do + local STORAGE_NAME + STORAGE_NAME=$(awk '{print $1}' <<<"$line") + [[ -n "$STORAGE_NAME" ]] && VALID=1 + done < <(pvesm status -content "$CONTENT" 2>/dev/null | awk 'NR>1') + [[ $VALID -eq 1 ]] +} + +select_storage() { + local CLASS=$1 CONTENT CONTENT_LABEL + case $CLASS in + container) + CONTENT='rootdir' + CONTENT_LABEL='Container' + ;; + template) + CONTENT='vztmpl' + CONTENT_LABEL='Container template' + ;; + iso) + CONTENT='iso' + CONTENT_LABEL='ISO image' + ;; + images) + CONTENT='images' + CONTENT_LABEL='VM Disk image' + ;; + backup) + CONTENT='backup' + CONTENT_LABEL='Backup' + ;; + snippets) + CONTENT='snippets' + CONTENT_LABEL='Snippets' + ;; + *) + msg_error "Invalid storage class '$CLASS'" + return 1 + ;; + esac + + declare -A STORAGE_MAP + local -a MENU=() + local COL_WIDTH=0 + + while read -r TAG TYPE _ TOTAL USED FREE _; do + [[ -n "$TAG" && -n "$TYPE" ]] || continue + local DISPLAY="${TAG} (${TYPE})" + local USED_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$USED") + local FREE_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$FREE") + local INFO="Free: ${FREE_FMT}B Used: ${USED_FMT}B" + STORAGE_MAP["$DISPLAY"]="$TAG" + MENU+=("$DISPLAY" "$INFO" "OFF") + ((${#DISPLAY} > COL_WIDTH)) && COL_WIDTH=${#DISPLAY} + done < <(pvesm status -content "$CONTENT" | awk 'NR>1') + + if [[ ${#MENU[@]} -eq 0 ]]; then + msg_error "No storage found for content type '$CONTENT'." + return 2 + fi + + if [[ $((${#MENU[@]} / 3)) -eq 1 ]]; then + STORAGE_RESULT="${STORAGE_MAP[${MENU[0]}]}" + STORAGE_INFO="${MENU[1]}" + return 0 + fi + + local WIDTH=$((COL_WIDTH + 42)) + while true; do + local DISPLAY_SELECTED + DISPLAY_SELECTED=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "Storage Pools" \ + --radiolist "Which storage pool for ${CONTENT_LABEL,,}?\n(Spacebar to select)" \ + 16 "$WIDTH" 6 "${MENU[@]}" 3>&1 1>&2 2>&3) || { exit_script; } + + DISPLAY_SELECTED=$(sed 's/[[:space:]]*$//' <<<"$DISPLAY_SELECTED") + if [[ -z "$DISPLAY_SELECTED" || -z "${STORAGE_MAP[$DISPLAY_SELECTED]+_}" ]]; then + whiptail --msgbox "No valid storage selected. Please try again." 8 58 + continue + fi + STORAGE_RESULT="${STORAGE_MAP[$DISPLAY_SELECTED]}" + for ((i = 0; i < ${#MENU[@]}; i += 3)); do + if [[ "${MENU[$i]}" == "$DISPLAY_SELECTED" ]]; then + STORAGE_INFO="${MENU[$i + 1]}" + break + fi + done + return 0 + done +} + +create_lxc_container() { + # ------------------------------------------------------------------------------ + # Optional verbose mode (debug tracing) + # ------------------------------------------------------------------------------ + if [[ "${CREATE_LXC_VERBOSE:-no}" == "yes" ]]; then set -x; fi + + # ------------------------------------------------------------------------------ + # Helpers (dynamic versioning / template parsing) + # ------------------------------------------------------------------------------ + pkg_ver() { dpkg-query -W -f='${Version}\n' "$1" 2>/dev/null || echo ""; } + pkg_cand() { apt-cache policy "$1" 2>/dev/null | awk '/Candidate:/ {print $2}'; } + + ver_ge() { dpkg --compare-versions "$1" ge "$2"; } + ver_gt() { dpkg --compare-versions "$1" gt "$2"; } + ver_lt() { dpkg --compare-versions "$1" lt "$2"; } + + # Extract Debian OS minor from template name: debian-13-standard_13.1-1_amd64.tar.zst => "13.1" + parse_template_osver() { sed -n 's/.*_\([0-9][0-9]*\(\.[0-9]\+\)\?\)-.*/\1/p' <<<"$1"; } + + # Offer upgrade for pve-container/lxc-pve if candidate > installed; optional auto-retry pct create + # Returns: + # 0 = no upgrade needed + # 1 = upgraded (and if do_retry=yes and retry succeeded, creation done) + # 2 = user declined + # 3 = upgrade attempted but failed OR retry failed + offer_lxc_stack_upgrade_and_maybe_retry() { + local do_retry="${1:-no}" # yes|no + local _pvec_i _pvec_c _lxcp_i _lxcp_c need=0 + + _pvec_i="$(pkg_ver pve-container)" + _lxcp_i="$(pkg_ver lxc-pve)" + _pvec_c="$(pkg_cand pve-container)" + _lxcp_c="$(pkg_cand lxc-pve)" + + if [[ -n "$_pvec_c" && "$_pvec_c" != "none" ]]; then + ver_gt "$_pvec_c" "${_pvec_i:-0}" && need=1 + fi + if [[ -n "$_lxcp_c" && "$_lxcp_c" != "none" ]]; then + ver_gt "$_lxcp_c" "${_lxcp_i:-0}" && need=1 + fi + if [[ $need -eq 0 ]]; then + msg_debug "No newer candidate for pve-container/lxc-pve (installed=$_pvec_i/$_lxcp_i, cand=$_pvec_c/$_lxcp_c)" + return 0 + fi + + echo + echo "An update for the Proxmox LXC stack is available:" + echo " pve-container: installed=${_pvec_i:-n/a} candidate=${_pvec_c:-n/a}" + echo " lxc-pve : installed=${_lxcp_i:-n/a} candidate=${_lxcp_c:-n/a}" + echo + read -rp "Do you want to upgrade now? [y/N] " _ans + case "${_ans,,}" in + y | yes) + msg_info "Upgrading Proxmox LXC stack (pve-container, lxc-pve)" + if apt-get update -qq >/dev/null && apt-get install -y --only-upgrade pve-container lxc-pve >/dev/null; then + msg_ok "LXC stack upgraded." + if [[ "$do_retry" == "yes" ]]; then + msg_info "Retrying container creation after upgrade" + if pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then + msg_ok "Container created successfully after upgrade." + return 0 + else + msg_error "pct create still failed after upgrade. See $LOGFILE" + return 3 + fi + fi + return 1 + else + msg_error "Upgrade failed. Please check APT output." + return 3 + fi + ;; + *) return 2 ;; + esac + } + + # ------------------------------------------------------------------------------ + # Required input variables + # ------------------------------------------------------------------------------ + [[ "${CTID:-}" ]] || { + msg_error "You need to set 'CTID' variable." + exit 203 + } + [[ "${PCT_OSTYPE:-}" ]] || { + msg_error "You need to set 'PCT_OSTYPE' variable." + exit 204 + } + + msg_debug "CTID=$CTID" + msg_debug "PCT_OSTYPE=$PCT_OSTYPE" + msg_debug "PCT_OSVERSION=${PCT_OSVERSION:-default}" + + # ID checks + [[ "$CTID" -ge 100 ]] || { + msg_error "ID cannot be less than 100." + exit 205 + } + if qm status "$CTID" &>/dev/null || pct status "$CTID" &>/dev/null; then + echo -e "ID '$CTID' is already in use." + unset CTID + msg_error "Cannot use ID that is already in use." + exit 206 + fi + + # Storage capability check + check_storage_support "rootdir" || { + msg_error "No valid storage found for 'rootdir' [Container]" + exit 1 + } + check_storage_support "vztmpl" || { + msg_error "No valid storage found for 'vztmpl' [Template]" + exit 1 + } + + # Template storage selection + if resolve_storage_preselect template "${TEMPLATE_STORAGE:-}"; then + TEMPLATE_STORAGE="$STORAGE_RESULT" + TEMPLATE_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${TEMPLATE_STORAGE}${CL} (${TEMPLATE_STORAGE_INFO}) [Template]" + else + while true; do + if [[ -z "${var_template_storage:-}" ]]; then + if select_storage template; then + TEMPLATE_STORAGE="$STORAGE_RESULT" + TEMPLATE_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${TEMPLATE_STORAGE}${CL} (${TEMPLATE_STORAGE_INFO}) [Template]" + break + fi + fi + done + fi + + # Container storage selection + if resolve_storage_preselect container "${CONTAINER_STORAGE:-}"; then + CONTAINER_STORAGE="$STORAGE_RESULT" + CONTAINER_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${CONTAINER_STORAGE}${CL} (${CONTAINER_STORAGE_INFO}) [Container]" + else + if [[ -z "${var_container_storage:-}" ]]; then + if select_storage container; then + CONTAINER_STORAGE="$STORAGE_RESULT" + CONTAINER_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${CONTAINER_STORAGE}${CL} (${CONTAINER_STORAGE_INFO}) [Container]" + fi + fi + fi + + # Validate content types + msg_info "Validating content types of storage '$CONTAINER_STORAGE'" + STORAGE_CONTENT=$(grep -A4 -E "^(zfspool|dir|lvmthin|lvm): $CONTAINER_STORAGE" /etc/pve/storage.cfg | grep content | awk '{$1=""; print $0}' | xargs) + msg_debug "Storage '$CONTAINER_STORAGE' has content types: $STORAGE_CONTENT" + grep -qw "rootdir" <<<"$STORAGE_CONTENT" || { + msg_error "Storage '$CONTAINER_STORAGE' does not support 'rootdir'. Cannot create LXC." + exit 217 + } + $STD msg_ok "Storage '$CONTAINER_STORAGE' supports 'rootdir'" + + msg_info "Validating content types of template storage '$TEMPLATE_STORAGE'" + TEMPLATE_CONTENT=$(grep -A4 -E "^[^:]+: $TEMPLATE_STORAGE" /etc/pve/storage.cfg | grep content | awk '{$1=""; print $0}' | xargs) + msg_debug "Template storage '$TEMPLATE_STORAGE' has content types: $TEMPLATE_CONTENT" + if ! grep -qw "vztmpl" <<<"$TEMPLATE_CONTENT"; then + msg_warn "Template storage '$TEMPLATE_STORAGE' does not declare 'vztmpl'. This may cause pct create to fail." + else + $STD msg_ok "Template storage '$TEMPLATE_STORAGE' supports 'vztmpl'" + fi + + # Free space check + STORAGE_FREE=$(pvesm status | awk -v s="$CONTAINER_STORAGE" '$1 == s { print $6 }') + REQUIRED_KB=$((${PCT_DISK_SIZE:-8} * 1024 * 1024)) + [[ "$STORAGE_FREE" -ge "$REQUIRED_KB" ]] || { + msg_error "Not enough space on '$CONTAINER_STORAGE'. Needed: ${PCT_DISK_SIZE:-8}G." + exit 214 + } + + # Cluster quorum (if cluster) + if [[ -f /etc/pve/corosync.conf ]]; then + msg_info "Checking cluster quorum" + if ! pvecm status | awk -F':' '/^Quorate/ { exit ($2 ~ /Yes/) ? 0 : 1 }'; then + msg_error "Cluster is not quorate. Start all nodes or configure quorum device (QDevice)." + exit 210 + fi + msg_ok "Cluster is quorate" + fi + + # ------------------------------------------------------------------------------ + # Template discovery & validation + # ------------------------------------------------------------------------------ + TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" + case "$PCT_OSTYPE" in + debian | ubuntu) TEMPLATE_PATTERN="-standard_" ;; + alpine | fedora | rocky | centos) TEMPLATE_PATTERN="-default_" ;; + *) TEMPLATE_PATTERN="" ;; + esac + + msg_info "Searching for template '$TEMPLATE_SEARCH'" + + # Build regex patterns outside awk/grep for clarity + SEARCH_PATTERN="^${TEMPLATE_SEARCH}" + + #echo "[DEBUG] TEMPLATE_SEARCH='$TEMPLATE_SEARCH'" + #echo "[DEBUG] SEARCH_PATTERN='$SEARCH_PATTERN'" + #echo "[DEBUG] TEMPLATE_PATTERN='$TEMPLATE_PATTERN'" + + mapfile -t LOCAL_TEMPLATES < <( + pveam list "$TEMPLATE_STORAGE" 2>/dev/null | + awk -v search="${SEARCH_PATTERN}" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' | + sed 's|.*/||' | sort -t - -k 2 -V + ) + + pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)." + + #echo "[DEBUG] pveam available output (first 5 lines with .tar files):" + #pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | head -5 | sed 's/^/ /' + + set +u + mapfile -t ONLINE_TEMPLATES < <(pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | awk '{print $2}' | grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V 2>/dev/null || true) + #echo "[DEBUG] After filtering: ${#ONLINE_TEMPLATES[@]} online templates found" + set -u + if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then + #echo "[DEBUG] Online templates:" + for tmpl in "${ONLINE_TEMPLATES[@]}"; do + echo " - $tmpl" + done + fi + + ONLINE_TEMPLATE="" + [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" + + #msg_debug "SEARCH_PATTERN='${SEARCH_PATTERN}' TEMPLATE_PATTERN='${TEMPLATE_PATTERN}'" + #msg_debug "Found ${#LOCAL_TEMPLATES[@]} local templates, ${#ONLINE_TEMPLATES[@]} online templates" + if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then + #msg_debug "First 3 online templates:" + count=0 + for idx in "${!ONLINE_TEMPLATES[@]}"; do + #msg_debug " [$idx]: ${ONLINE_TEMPLATES[$idx]}" + ((count++)) + [[ $count -ge 3 ]] && break + done + fi + #msg_debug "ONLINE_TEMPLATE='$ONLINE_TEMPLATE'" + + if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then + TEMPLATE="${LOCAL_TEMPLATES[-1]}" + TEMPLATE_SOURCE="local" + else + TEMPLATE="$ONLINE_TEMPLATE" + TEMPLATE_SOURCE="online" + fi + + # If still no template, try to find alternatives + if [[ -z "$TEMPLATE" ]]; then + echo "" + echo "[DEBUG] No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}, searching for alternatives..." + + # Get all available versions for this OS type + mapfile -t AVAILABLE_VERSIONS < <( + pveam available -section system 2>/dev/null | + grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | + awk -F'\t' '{print $1}' | + grep "^${PCT_OSTYPE}-" | + sed -E "s/.*${PCT_OSTYPE}-([0-9]+(\.[0-9]+)?).*/\1/" | + sort -u -V 2>/dev/null + ) + + if [[ ${#AVAILABLE_VERSIONS[@]} -gt 0 ]]; then + echo "" + echo "${BL}Available ${PCT_OSTYPE} versions:${CL}" + for i in "${!AVAILABLE_VERSIONS[@]}"; do + echo " [$((i + 1))] ${AVAILABLE_VERSIONS[$i]}" + done + echo "" + read -p "Select version [1-${#AVAILABLE_VERSIONS[@]}] or press Enter to cancel: " choice + + if [[ "$choice" =~ ^[0-9]+$ ]] && [[ "$choice" -ge 1 ]] && [[ "$choice" -le ${#AVAILABLE_VERSIONS[@]} ]]; then + PCT_OSVERSION="${AVAILABLE_VERSIONS[$((choice - 1))]}" + TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION}" + SEARCH_PATTERN="^${TEMPLATE_SEARCH}-" + + #echo "[DEBUG] Retrying with version: $PCT_OSVERSION" + + mapfile -t ONLINE_TEMPLATES < <( + pveam available -section system 2>/dev/null | + grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | + awk -F'\t' '{print $1}' | + grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | + sort -t - -k 2 -V 2>/dev/null || true + ) + + if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then + TEMPLATE="${ONLINE_TEMPLATES[-1]}" + TEMPLATE_SOURCE="online" + #echo "[DEBUG] Found alternative: $TEMPLATE" + else + msg_error "No templates available for ${PCT_OSTYPE} ${PCT_OSVERSION}" + exit 225 + fi + else + msg_info "Installation cancelled" + exit 0 + fi + else + msg_error "No ${PCT_OSTYPE} templates available at all" + exit 225 + fi + fi + + #echo "[DEBUG] Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'" + #msg_debug "Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'" + + TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" + if [[ -z "$TEMPLATE_PATH" ]]; then + TEMPLATE_BASE=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) + [[ -n "$TEMPLATE_BASE" ]] && TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE" + fi + + # If we still don't have a path but have a valid template name, construct it + if [[ -z "$TEMPLATE_PATH" && -n "$TEMPLATE" ]]; then + TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" + fi + + [[ -n "$TEMPLATE_PATH" ]] || { + if [[ -z "$TEMPLATE" ]]; then + msg_error "Template ${PCT_OSTYPE} ${PCT_OSVERSION} not available" + + # Get available versions + mapfile -t AVAILABLE_VERSIONS < <( + pveam available -section system 2>/dev/null | + grep "^${PCT_OSTYPE}-" | + sed -E 's/.*'"${PCT_OSTYPE}"'-([0-9]+\.[0-9]+).*/\1/' | + grep -E '^[0-9]+\.[0-9]+$' | + sort -u -V 2>/dev/null || sort -u + ) + + if [[ ${#AVAILABLE_VERSIONS[@]} -gt 0 ]]; then + echo -e "\n${BL}Available versions:${CL}" + for i in "${!AVAILABLE_VERSIONS[@]}"; do + echo " [$((i + 1))] ${AVAILABLE_VERSIONS[$i]}" + done + + echo "" + read -p "Select version [1-${#AVAILABLE_VERSIONS[@]}] or Enter to exit: " choice + + if [[ "$choice" =~ ^[0-9]+$ ]] && [[ "$choice" -ge 1 ]] && [[ "$choice" -le ${#AVAILABLE_VERSIONS[@]} ]]; then + export var_version="${AVAILABLE_VERSIONS[$((choice - 1))]}" + export PCT_OSVERSION="$var_version" + msg_ok "Switched to ${PCT_OSTYPE} ${var_version}" + + # Retry template search with new version + TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" + SEARCH_PATTERN="^${TEMPLATE_SEARCH}-" + + mapfile -t LOCAL_TEMPLATES < <( + pveam list "$TEMPLATE_STORAGE" 2>/dev/null | + awk -v search="${SEARCH_PATTERN}" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' | + sed 's|.*/||' | sort -t - -k 2 -V + ) + mapfile -t ONLINE_TEMPLATES < <( + pveam available -section system 2>/dev/null | + grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | + awk -F'\t' '{print $1}' | + grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | + sort -t - -k 2 -V 2>/dev/null || true + ) + ONLINE_TEMPLATE="" + [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" + + if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then + TEMPLATE="${LOCAL_TEMPLATES[-1]}" + TEMPLATE_SOURCE="local" + else + TEMPLATE="$ONLINE_TEMPLATE" + TEMPLATE_SOURCE="online" + fi + + TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" + if [[ -z "$TEMPLATE_PATH" ]]; then + TEMPLATE_BASE=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) + [[ -n "$TEMPLATE_BASE" ]] && TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE" + fi + + # If we still don't have a path but have a valid template name, construct it + if [[ -z "$TEMPLATE_PATH" && -n "$TEMPLATE" ]]; then + TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" + fi + + [[ -n "$TEMPLATE_PATH" ]] || { + msg_error "Template still not found after version change" + exit 220 + } + else + msg_info "Installation cancelled" + exit 1 + fi + else + msg_error "No ${PCT_OSTYPE} templates available" + exit 220 + fi + fi + } + + # Validate that we found a template + if [[ -z "$TEMPLATE" ]]; then + msg_error "No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}" + msg_info "Please check:" + msg_info " - Is pveam catalog available? (run: pveam available -section system)" + msg_info " - Does the template exist for your OS version?" + exit 225 + fi + + msg_ok "Template ${BL}$TEMPLATE${CL} [$TEMPLATE_SOURCE]" + msg_debug "Resolved TEMPLATE_PATH=$TEMPLATE_PATH" + + NEED_DOWNLOAD=0 + if [[ ! -f "$TEMPLATE_PATH" ]]; then + msg_info "Template not present locally – will download." + NEED_DOWNLOAD=1 + elif [[ ! -r "$TEMPLATE_PATH" ]]; then + msg_error "Template file exists but is not readable – check permissions." + exit 221 + elif [[ "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then + if [[ -n "$ONLINE_TEMPLATE" ]]; then + msg_warn "Template file too small (<1MB) – re-downloading." + NEED_DOWNLOAD=1 + else + msg_warn "Template looks too small, but no online version exists. Keeping local file." + fi + elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then + if [[ -n "$ONLINE_TEMPLATE" ]]; then + msg_warn "Template appears corrupted – re-downloading." + NEED_DOWNLOAD=1 + else + msg_warn "Template appears corrupted, but no online version exists. Keeping local file." + fi + else + $STD msg_ok "Template $TEMPLATE is present and valid." + fi + + if [[ "$TEMPLATE_SOURCE" == "local" && -n "$ONLINE_TEMPLATE" && "$TEMPLATE" != "$ONLINE_TEMPLATE" ]]; then + msg_warn "Local template is outdated: $TEMPLATE (latest available: $ONLINE_TEMPLATE)" + if whiptail --yesno "A newer template is available:\n$ONLINE_TEMPLATE\n\nDo you want to download and use it instead?" 12 70; then + TEMPLATE="$ONLINE_TEMPLATE" + NEED_DOWNLOAD=1 + else + msg_info "Continuing with local template $TEMPLATE" + fi + fi + + if [[ "$NEED_DOWNLOAD" -eq 1 ]]; then + [[ -f "$TEMPLATE_PATH" ]] && rm -f "$TEMPLATE_PATH" + for attempt in {1..3}; do + msg_info "Attempt $attempt: Downloading template $TEMPLATE to $TEMPLATE_STORAGE" + if pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null 2>&1; then + msg_ok "Template download successful." + break + fi + if [[ $attempt -eq 3 ]]; then + msg_error "Failed after 3 attempts. Please check network access, permissions, or manually run:\n pveam download $TEMPLATE_STORAGE $TEMPLATE" + exit 222 + fi + sleep $((attempt * 5)) + done + fi + + if ! pveam list "$TEMPLATE_STORAGE" 2>/dev/null | grep -q "$TEMPLATE"; then + msg_error "Template $TEMPLATE not available in storage $TEMPLATE_STORAGE after download." + exit 223 + fi + + # ------------------------------------------------------------------------------ + # Dynamic preflight for Debian 13.x: offer upgrade if available (no hard mins) + # ------------------------------------------------------------------------------ + if [[ "$PCT_OSTYPE" == "debian" ]]; then + OSVER="$(parse_template_osver "$TEMPLATE")" + if [[ -n "$OSVER" ]]; then + # Proactive, aber ohne Abbruch – nur Angebot + offer_lxc_stack_upgrade_and_maybe_retry "no" || true + fi + fi + + # ------------------------------------------------------------------------------ + # Create LXC Container + # ------------------------------------------------------------------------------ + msg_info "Creating LXC container" + + # Ensure subuid/subgid entries exist + grep -q "root:100000:65536" /etc/subuid || echo "root:100000:65536" >>/etc/subuid + grep -q "root:100000:65536" /etc/subgid || echo "root:100000:65536" >>/etc/subgid + + # Assemble pct options + PCT_OPTIONS=(${PCT_OPTIONS[@]:-${DEFAULT_PCT_OPTIONS[@]}}) + [[ " ${PCT_OPTIONS[*]} " =~ " -rootfs " ]] || PCT_OPTIONS+=(-rootfs "$CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}") + + # Lock by template file (avoid concurrent downloads/creates) + lockfile="/tmp/template.${TEMPLATE}.lock" + exec 9>"$lockfile" || { + msg_error "Failed to create lock file '$lockfile'." + exit 200 + } + flock -w 60 9 || { + msg_error "Timeout while waiting for template lock." + exit 211 + } + + LOGFILE="/tmp/pct_create_${CTID}.log" + msg_debug "pct create command: pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" + msg_debug "Logfile: $LOGFILE" + + # First attempt + if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >"$LOGFILE" 2>&1; then + msg_error "Container creation failed on ${TEMPLATE_STORAGE}. Checking template..." + + # Validate template file + if [[ ! -s "$TEMPLATE_PATH" || "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then + msg_warn "Template file too small or missing – re-downloading." + rm -f "$TEMPLATE_PATH" + pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" + elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then + if [[ -n "$ONLINE_TEMPLATE" ]]; then + msg_warn "Template appears corrupted – re-downloading." + rm -f "$TEMPLATE_PATH" + pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" + else + msg_warn "Template appears corrupted, but no online version exists. Skipping re-download." + fi + fi + + # Retry after repair + if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then + # Fallback to local storage + if [[ "$TEMPLATE_STORAGE" != "local" ]]; then + msg_warn "Retrying container creation with fallback to local storage..." + LOCAL_TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" + if [[ ! -f "$LOCAL_TEMPLATE_PATH" ]]; then + msg_info "Downloading template to local..." + pveam download local "$TEMPLATE" >/dev/null 2>&1 + fi + if pct create "$CTID" "local:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then + msg_ok "Container successfully created using local fallback." + else + # --- Dynamic stack upgrade + auto-retry on the well-known error pattern --- + if grep -qiE 'unsupported .* version' "$LOGFILE"; then + echo + echo "pct reported 'unsupported ... version' – your LXC stack might be too old for this template." + echo "We can try to upgrade 'pve-container' and 'lxc-pve' now and retry automatically." + offer_lxc_stack_upgrade_and_maybe_retry "yes" + rc=$? + case $rc in + 0) : ;; # success - container created, continue + 2) + echo "Upgrade was declined. Please update and re-run: + apt update && apt install --only-upgrade pve-container lxc-pve" + exit 231 + ;; + 3) + echo "Upgrade and/or retry failed. Please inspect: $LOGFILE" + exit 231 + ;; + esac + else + msg_error "Container creation failed even with local fallback. See $LOGFILE" + if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then + set -x + bash -x -c "pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" 2>&1 | tee -a "$LOGFILE" + set +x + fi + exit 209 + fi + fi + else + msg_error "Container creation failed on local storage. See $LOGFILE" + # --- Dynamic stack upgrade + auto-retry on the well-known error pattern --- + if grep -qiE 'unsupported .* version' "$LOGFILE"; then + echo + echo "pct reported 'unsupported ... version' – your LXC stack might be too old for this template." + echo "We can try to upgrade 'pve-container' and 'lxc-pve' now and retry automatically." + offer_lxc_stack_upgrade_and_maybe_retry "yes" + rc=$? + case $rc in + 0) : ;; # success - container created, continue + 2) + echo "Upgrade was declined. Please update and re-run: + apt update && apt install --only-upgrade pve-container lxc-pve" + exit 231 + ;; + 3) + echo "Upgrade and/or retry failed. Please inspect: $LOGFILE" + exit 231 + ;; + esac + else + msg_error "Container creation failed. See $LOGFILE" + if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then + set -x + bash -x -c "pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" 2>&1 | tee -a "$LOGFILE" + set +x + fi + exit 209 + fi + fi + fi + fi + + # Verify container exists + pct list | awk '{print $1}' | grep -qx "$CTID" || { + msg_error "Container ID $CTID not listed in 'pct list'. See $LOGFILE" + exit 215 + } + + # Verify config rootfs + grep -q '^rootfs:' "/etc/pve/lxc/$CTID.conf" || { + msg_error "RootFS entry missing in container config. See $LOGFILE" + exit 216 + } + + msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created." +} + +# ------------------------------------------------------------------------------ +# description() +# +# - Sets container description with HTML content (logo, links, badges) +# - Restarts ping-instances.service if present +# - Posts status "done" to API +# ------------------------------------------------------------------------------ +description() { + IP=$(pct exec "$CTID" ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) + + # Generate LXC Description + DESCRIPTION=$( + cat < + + Logo + + +

${APP} LXC

+ +

+ + spend Coffee + +

+ + + + GitHub + + + + Discussions + + + + Issues + + +EOF + ) + pct set "$CTID" -description "$DESCRIPTION" + + if [[ -f /etc/systemd/system/ping-instances.service ]]; then + systemctl start ping-instances.service + fi + + post_update_to_api "done" "none" +} + +# ------------------------------------------------------------------------------ +# api_exit_script() +# +# - Exit trap handler +# - Reports exit codes to API with detailed reason +# - Handles known codes (100–209) and maps them to errors +# ------------------------------------------------------------------------------ +api_exit_script() { + exit_code=$? + if [ $exit_code -ne 0 ]; then + case $exit_code in + 100) post_update_to_api "failed" "100: Unexpected error in create_lxc.sh" ;; + 101) post_update_to_api "failed" "101: No network connection detected in create_lxc.sh" ;; + 200) post_update_to_api "failed" "200: LXC creation failed in create_lxc.sh" ;; + 201) post_update_to_api "failed" "201: Invalid Storage class in create_lxc.sh" ;; + 202) post_update_to_api "failed" "202: User aborted menu in create_lxc.sh" ;; + 203) post_update_to_api "failed" "203: CTID not set in create_lxc.sh" ;; + 204) post_update_to_api "failed" "204: PCT_OSTYPE not set in create_lxc.sh" ;; + 205) post_update_to_api "failed" "205: CTID cannot be less than 100 in create_lxc.sh" ;; + 206) post_update_to_api "failed" "206: CTID already in use in create_lxc.sh" ;; + 207) post_update_to_api "failed" "207: Template not found in create_lxc.sh" ;; + 208) post_update_to_api "failed" "208: Error downloading template in create_lxc.sh" ;; + 209) post_update_to_api "failed" "209: Container creation failed, but template is intact in create_lxc.sh" ;; + *) post_update_to_api "failed" "Unknown error, exit code: $exit_code in create_lxc.sh" ;; + esac + fi +} + +if command -v pveversion >/dev/null 2>&1; then + trap 'api_exit_script' EXIT +fi +trap 'post_update_to_api "failed" "$BASH_COMMAND"' ERR +trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT +trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM diff --git a/misc/build.func.backup-20251029-124334 b/misc/build.func.backup-20251029-124334 new file mode 100644 index 000000000..d452f4637 --- /dev/null +++ b/misc/build.func.backup-20251029-124334 @@ -0,0 +1,3517 @@ +#!/usr/bin/env bash +# Copyright (c) 2021-2025 community-scripts ORG +# Author: tteck (tteckster) | MickLesk | michelroegl-brunner +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Revision: 1 + +# ============================================================================== +# SECTION 1: CORE INITIALIZATION & VARIABLES +# ============================================================================== + +# ------------------------------------------------------------------------------ +# variables() +# +# - Normalize application name (NSAPP = lowercase, no spaces) +# - Build installer filename (var_install) +# - Define regex for integer validation +# - Fetch hostname of Proxmox node +# - Set default values for diagnostics/method +# - Generate random UUID for tracking +# - Get Proxmox VE version and kernel version +# ------------------------------------------------------------------------------ +variables() { + NSAPP=$(echo "${APP,,}" | tr -d ' ') # This function sets the NSAPP variable by converting the value of the APP variable to lowercase and removing any spaces. + var_install="${NSAPP}-install" # sets the var_install variable by appending "-install" to the value of NSAPP. + INTEGER='^[0-9]+([.][0-9]+)?$' # it defines the INTEGER regular expression pattern. + PVEHOST_NAME=$(hostname) # gets the Proxmox Hostname and sets it to Uppercase + DIAGNOSTICS="yes" # sets the DIAGNOSTICS variable to "yes", used for the API call. + METHOD="default" # sets the METHOD variable to "default", used for the API call. + RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" # generates a random UUID and sets it to the RANDOM_UUID variable. + CTTYPE="${CTTYPE:-${CT_TYPE:-1}}" + #CT_TYPE=${var_unprivileged:-$CT_TYPE} + + # Get Proxmox VE version and kernel version + if command -v pveversion >/dev/null 2>&1; then + PVEVERSION=$(pveversion | grep "pve-manager" | awk '{print $2}' | cut -d'/' -f1) + else + PVEVERSION="N/A" + fi + KERNEL_VERSION=$(uname -r) +} + +# ----------------------------------------------------------------------------- +# Community-Scripts bootstrap loader +# - Always sources build.func from remote +# - Updates local core files only if build.func changed +# - Local cache: /usr/local/community-scripts/core +# ----------------------------------------------------------------------------- + +# FUNC_DIR="/usr/local/community-scripts/core" +# mkdir -p "$FUNC_DIR" + +# BUILD_URL="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func" +# BUILD_REV="$FUNC_DIR/build.rev" +# DEVMODE="${DEVMODE:-no}" + +# # --- Step 1: fetch build.func content once, compute hash --- +# build_content="$(curl -fsSL "$BUILD_URL")" || { +# echo "❌ Failed to fetch build.func" +# exit 1 +# } + +# newhash=$(printf "%s" "$build_content" | sha256sum | awk '{print $1}') +# oldhash=$(cat "$BUILD_REV" 2>/dev/null || echo "") + +# # --- Step 2: if build.func changed, offer update for core files --- +# if [ "$newhash" != "$oldhash" ]; then +# echo "⚠️ build.func changed!" + +# while true; do +# read -rp "Refresh local core files? [y/N/diff]: " ans +# case "$ans" in +# [Yy]*) +# echo "$newhash" >"$BUILD_REV" + +# update_func_file() { +# local file="$1" +# local url="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/$file" +# local local_path="$FUNC_DIR/$file" + +# echo "⬇️ Downloading $file ..." +# curl -fsSL "$url" -o "$local_path" || { +# echo "❌ Failed to fetch $file" +# exit 1 +# } +# echo "✔️ Updated $file" +# } + +# update_func_file core.func +# update_func_file error_handler.func +# update_func_file tools.func +# break +# ;; +# [Dd]*) +# for file in core.func error_handler.func tools.func; do +# local_path="$FUNC_DIR/$file" +# url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/$file" +# remote_tmp="$(mktemp)" + +# curl -fsSL "$url" -o "$remote_tmp" || continue + +# if [ -f "$local_path" ]; then +# echo "🔍 Diff for $file:" +# diff -u "$local_path" "$remote_tmp" || echo "(no differences)" +# else +# echo "📦 New file $file will be installed" +# fi + +# rm -f "$remote_tmp" +# done +# ;; +# *) +# echo "❌ Skipped updating local core files" +# break +# ;; +# esac +# done +# else +# if [ "$DEVMODE" != "yes" ]; then +# echo "✔️ build.func unchanged → using existing local core files" +# fi +# fi + +# if [ -n "${_COMMUNITY_SCRIPTS_LOADER:-}" ]; then +# return 0 2>/dev/null || exit 0 +# fi +# _COMMUNITY_SCRIPTS_LOADER=1 + +# # --- Step 3: always source local versions of the core files --- +# source "$FUNC_DIR/core.func" +# source "$FUNC_DIR/error_handler.func" +# source "$FUNC_DIR/tools.func" + +# # --- Step 4: finally, source build.func directly from memory --- +# # (no tmp file needed) +# source <(printf "%s" "$build_content") + +# ------------------------------------------------------------------------------ +# Load core + error handler functions from community-scripts repo +# +# - Prefer curl if available, fallback to wget +# - Load: core.func, error_handler.func, api.func +# - Initialize error traps after loading +# ------------------------------------------------------------------------------ + +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) + +if command -v curl >/dev/null 2>&1; then + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) + load_functions + catch_errors + #echo "(build.func) Loaded core.func via curl" +elif command -v wget >/dev/null 2>&1; then + source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) + load_functions + catch_errors + #echo "(build.func) Loaded core.func via wget" +fi + +# ------------------------------------------------------------------------------ +# maxkeys_check() +# +# - Reads kernel keyring limits (maxkeys, maxbytes) +# - Checks current usage for LXC user (UID 100000) +# - Warns if usage is close to limits and suggests sysctl tuning +# - Exits if thresholds are exceeded +# - https://cleveruptime.com/docs/files/proc-key-users | https://docs.kernel.org/security/keys/core.html +# ------------------------------------------------------------------------------ + +maxkeys_check() { + # Read kernel parameters + per_user_maxkeys=$(cat /proc/sys/kernel/keys/maxkeys 2>/dev/null || echo 0) + per_user_maxbytes=$(cat /proc/sys/kernel/keys/maxbytes 2>/dev/null || echo 0) + + # Exit if kernel parameters are unavailable + if [[ "$per_user_maxkeys" -eq 0 || "$per_user_maxbytes" -eq 0 ]]; then + echo -e "${CROSS}${RD} Error: Unable to read kernel parameters. Ensure proper permissions.${CL}" + exit 1 + fi + + # Fetch key usage for user ID 100000 (typical for containers) + used_lxc_keys=$(awk '/100000:/ {print $2}' /proc/key-users 2>/dev/null || echo 0) + used_lxc_bytes=$(awk '/100000:/ {split($5, a, "/"); print a[1]}' /proc/key-users 2>/dev/null || echo 0) + + # Calculate thresholds and suggested new limits + threshold_keys=$((per_user_maxkeys - 100)) + threshold_bytes=$((per_user_maxbytes - 1000)) + new_limit_keys=$((per_user_maxkeys * 2)) + new_limit_bytes=$((per_user_maxbytes * 2)) + + # Check if key or byte usage is near limits + failure=0 + if [[ "$used_lxc_keys" -gt "$threshold_keys" ]]; then + echo -e "${CROSS}${RD} Warning: Key usage is near the limit (${used_lxc_keys}/${per_user_maxkeys}).${CL}" + echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxkeys=${new_limit_keys}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." + failure=1 + fi + if [[ "$used_lxc_bytes" -gt "$threshold_bytes" ]]; then + echo -e "${CROSS}${RD} Warning: Key byte usage is near the limit (${used_lxc_bytes}/${per_user_maxbytes}).${CL}" + echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxbytes=${new_limit_bytes}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." + failure=1 + fi + + # Provide next steps if issues are detected + if [[ "$failure" -eq 1 ]]; then + echo -e "${INFO} To apply changes, run: ${BOLD}service procps force-reload${CL}" + exit 1 + fi + + echo -e "${CM}${GN} All kernel key limits are within safe thresholds.${CL}" +} + +# ------------------------------------------------------------------------------ +# get_current_ip() +# +# - Returns current container IP depending on OS type +# - Debian/Ubuntu: uses `hostname -I` +# - Alpine: parses eth0 via `ip -4 addr` +# ------------------------------------------------------------------------------ +get_current_ip() { + if [ -f /etc/os-release ]; then + # Check for Debian/Ubuntu (uses hostname -I) + if grep -qE 'ID=debian|ID=ubuntu' /etc/os-release; then + CURRENT_IP=$(hostname -I | awk '{print $1}') + # Check for Alpine (uses ip command) + elif grep -q 'ID=alpine' /etc/os-release; then + CURRENT_IP=$(ip -4 addr show eth0 | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) + else + CURRENT_IP="Unknown" + fi + fi + echo "$CURRENT_IP" +} + +# ------------------------------------------------------------------------------ +# update_motd_ip() +# +# - Updates /etc/motd with current container IP +# - Removes old IP entries to avoid duplicates +# ------------------------------------------------------------------------------ +update_motd_ip() { + MOTD_FILE="/etc/motd" + + if [ -f "$MOTD_FILE" ]; then + # Remove existing IP Address lines to prevent duplication + sed -i '/IP Address:/d' "$MOTD_FILE" + + IP=$(get_current_ip) + # Add the new IP address + echo -e "${TAB}${NETWORK}${YW} IP Address: ${GN}${IP}${CL}" >>"$MOTD_FILE" + fi +} + +# ------------------------------------------------------------------------------ +# install_ssh_keys_into_ct() +# +# - Installs SSH keys into container root account if SSH is enabled +# - Uses pct push or direct input to authorized_keys +# - Falls back to warning if no keys provided +# ------------------------------------------------------------------------------ +install_ssh_keys_into_ct() { + [[ "$SSH" != "yes" ]] && return 0 + + if [[ -n "$SSH_KEYS_FILE" && -s "$SSH_KEYS_FILE" ]]; then + msg_info "Installing selected SSH keys into CT ${CTID}" + pct exec "$CTID" -- sh -c 'mkdir -p /root/.ssh && chmod 700 /root/.ssh' || { + msg_error "prepare /root/.ssh failed" + return 1 + } + pct push "$CTID" "$SSH_KEYS_FILE" /root/.ssh/authorized_keys >/dev/null 2>&1 || + pct exec "$CTID" -- sh -c "cat > /root/.ssh/authorized_keys" <"$SSH_KEYS_FILE" || { + msg_error "write authorized_keys failed" + return 1 + } + pct exec "$CTID" -- sh -c 'chmod 600 /root/.ssh/authorized_keys' || true + msg_ok "Installed SSH keys into CT ${CTID}" + return 0 + fi + + # Fallback: nichts ausgewählt + msg_warn "No SSH keys to install (skipping)." + return 0 +} + +# ------------------------------------------------------------------------------ +# base_settings() +# +# - Defines all base/default variables for container creation +# - Reads from environment variables (var_*) +# - Provides fallback defaults for OS type/version +# ------------------------------------------------------------------------------ +base_settings() { + # Default Settings + CT_TYPE=${var_unprivileged:-"1"} + DISK_SIZE=${var_disk:-"4"} + CORE_COUNT=${var_cpu:-"1"} + RAM_SIZE=${var_ram:-"1024"} + VERBOSE=${var_verbose:-"${1:-no}"} + PW=${var_pw:-""} + CT_ID=${var_ctid:-$NEXTID} + HN=${var_hostname:-$NSAPP} + BRG=${var_brg:-"vmbr0"} + NET=${var_net:-"dhcp"} + IPV6_METHOD=${var_ipv6_method:-"none"} + IPV6_STATIC=${var_ipv6_static:-""} + GATE=${var_gateway:-""} + APT_CACHER=${var_apt_cacher:-""} + APT_CACHER_IP=${var_apt_cacher_ip:-""} + MTU=${var_mtu:-""} + SD=${var_storage:-""} + NS=${var_ns:-""} + MAC=${var_mac:-""} + VLAN=${var_vlan:-""} + SSH=${var_ssh:-"no"} + SSH_AUTHORIZED_KEY=${var_ssh_authorized_key:-""} + UDHCPC_FIX=${var_udhcpc_fix:-""} + TAGS="community-script,${var_tags:-}" + ENABLE_FUSE=${var_fuse:-"${1:-no}"} + ENABLE_TUN=${var_tun:-"${1:-no}"} + + # Since these 2 are only defined outside of default_settings function, we add a temporary fallback. TODO: To align everything, we should add these as constant variables (e.g. OSTYPE and OSVERSION), but that would currently require updating the default_settings function for all existing scripts + if [ -z "$var_os" ]; then + var_os="debian" + fi + if [ -z "$var_version" ]; then + var_version="12" + fi +} + +# ------------------------------------------------------------------------------ +# echo_default() +# +# - Prints summary of default values (ID, OS, type, disk, RAM, CPU, etc.) +# - Uses icons and formatting for readability +# - Convert CT_TYPE to description +# ------------------------------------------------------------------------------ +echo_default() { + CT_TYPE_DESC="Unprivileged" + if [ "$CT_TYPE" -eq 0 ]; then + CT_TYPE_DESC="Privileged" + fi + echo -e "${INFO}${BOLD}${DGN}PVE Version ${PVEVERSION} (Kernel: ${KERNEL_VERSION})${CL}" + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}${CT_ID}${CL}" + echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os ($var_version)${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + if [ "$VERBOSE" == "yes" ]; then + echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}Enabled${CL}" + fi + echo -e "${CREATING}${BOLD}${BL}Creating a ${APP} LXC using the above default settings${CL}" + echo -e " " +} + +# ------------------------------------------------------------------------------ +# exit_script() +# +# - Called when user cancels an action +# - Clears screen and exits gracefully +# ------------------------------------------------------------------------------ +exit_script() { + clear + echo -e "\n${CROSS}${RD}User exited script${CL}\n" + exit +} + +# ------------------------------------------------------------------------------ +# find_host_ssh_keys() +# +# - Scans system for available SSH keys +# - Supports defaults (~/.ssh, /etc/ssh/authorized_keys) +# - Returns list of files containing valid SSH public keys +# - Sets FOUND_HOST_KEY_COUNT to number of keys found +# ------------------------------------------------------------------------------ +find_host_ssh_keys() { + local re='(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))' + local -a files=() cand=() + local g="${var_ssh_import_glob:-}" + local total=0 f base c + + shopt -s nullglob + if [[ -n "$g" ]]; then + for pat in $g; do cand+=($pat); done + else + cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2) + cand+=(/root/.ssh/*.pub) + cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*) + fi + shopt -u nullglob + + for f in "${cand[@]}"; do + [[ -f "$f" && -r "$f" ]] || continue + base="$(basename -- "$f")" + case "$base" in + known_hosts | known_hosts.* | config) continue ;; + id_*) [[ "$f" != *.pub ]] && continue ;; + esac + + # CRLF safe check for host keys + c=$(tr -d '\r' <"$f" | awk ' + /^[[:space:]]*#/ {next} + /^[[:space:]]*$/ {next} + {print} + ' | grep -E -c '"$re"' || true) + + if ((c > 0)); then + files+=("$f") + total=$((total + c)) + fi + done + + # Fallback to /root/.ssh/authorized_keys + if ((${#files[@]} == 0)) && [[ -r /root/.ssh/authorized_keys ]]; then + if grep -E -q "$re" /root/.ssh/authorized_keys; then + files+=(/root/.ssh/authorized_keys) + total=$((total + $(grep -E -c "$re" /root/.ssh/authorized_keys || echo 0))) + fi + fi + + FOUND_HOST_KEY_COUNT="$total" + ( + IFS=: + echo "${files[*]}" + ) +} + +# ------------------------------------------------------------------------------ +# advanced_settings() +# +# - Interactive whiptail menu for advanced configuration +# - Lets user set container type, password, CT ID, hostname, disk, CPU, RAM +# - Supports IPv4/IPv6, DNS, MAC, VLAN, tags, SSH keys, FUSE, verbose mode +# - Ends with confirmation or re-entry if cancelled +# ------------------------------------------------------------------------------ +advanced_settings() { + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox --title "Here is an instructional tip:" "To make a selection, use the Spacebar." 8 58 + # Setting Default Tag for Advanced Settings + TAGS="community-script;${var_tags:-}" + CT_DEFAULT_TYPE="${CT_TYPE}" + CT_TYPE="" + while [ -z "$CT_TYPE" ]; do + if [ "$CT_DEFAULT_TYPE" == "1" ]; then + if CT_TYPE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ + "1" "Unprivileged" ON \ + "0" "Privileged" OFF \ + 3>&1 1>&2 2>&3); then + if [ -n "$CT_TYPE" ]; then + CT_TYPE_DESC="Unprivileged" + if [ "$CT_TYPE" -eq 0 ]; then + CT_TYPE_DESC="Privileged" + fi + echo -e "${INFO}${BOLD}${DGN}PVE Version ${PVEVERSION} (Kernel: ${KERNEL_VERSION})${CL}" + echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os |${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" + fi + else + exit_script + fi + fi + if [ "$CT_DEFAULT_TYPE" == "0" ]; then + if CT_TYPE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ + "1" "Unprivileged" OFF \ + "0" "Privileged" ON \ + 3>&1 1>&2 2>&3); then + if [ -n "$CT_TYPE" ]; then + CT_TYPE_DESC="Unprivileged" + if [ "$CT_TYPE" -eq 0 ]; then + CT_TYPE_DESC="Privileged" + fi + echo -e "${INFO}${BOLD}${DGN}PVE Version ${PVEVERSION} (Kernel: ${KERNEL_VERSION})${CL}" + echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os${CL}" + echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" + fi + else + exit_script + fi + fi + done + + while true; do + if PW1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --passwordbox "\nSet Root Password (needed for root ssh access)" 9 58 --title "PASSWORD (leave blank for automatic login)" 3>&1 1>&2 2>&3); then + # Empty = Autologin + if [[ -z "$PW1" ]]; then + PW="" + PW1="Automatic Login" + echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}$PW1${CL}" + break + fi + + # Invalid: contains spaces + if [[ "$PW1" == *" "* ]]; then + whiptail --msgbox "Password cannot contain spaces." 8 58 + continue + fi + + # Invalid: too short + if ((${#PW1} < 5)); then + whiptail --msgbox "Password must be at least 5 characters." 8 58 + continue + fi + + # Confirm password + if PW2=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then + if [[ "$PW1" == "$PW2" ]]; then + PW="-password $PW1" + echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}" + break + else + whiptail --msgbox "Passwords do not match. Please try again." 8 58 + fi + else + exit_script + fi + else + exit_script + fi + done + + if CT_ID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Container ID" 8 58 "$NEXTID" --title "CONTAINER ID" 3>&1 1>&2 2>&3); then + if [ -z "$CT_ID" ]; then + CT_ID="$NEXTID" + fi + else + exit_script + fi + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" + + while true; do + if CT_NAME=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 "$NSAPP" --title "HOSTNAME" 3>&1 1>&2 2>&3); then + if [ -z "$CT_NAME" ]; then + HN="$NSAPP" + else + HN=$(echo "${CT_NAME,,}" | tr -d ' ') + fi + # Hostname validate (RFC 1123) + if [[ "$HN" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ ]]; then + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" + break + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --msgbox "❌ Invalid hostname: '$HN'\n\nOnly lowercase letters, digits and hyphens (-) are allowed.\nUnderscores (_) or other characters are not permitted!" 10 70 + fi + else + exit_script + fi + done + + while true; do + DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GB" 8 58 "$var_disk" --title "DISK SIZE" 3>&1 1>&2 2>&3) || exit_script + + if [ -z "$DISK_SIZE" ]; then + DISK_SIZE="$var_disk" + fi + + if [[ "$DISK_SIZE" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" + break + else + whiptail --msgbox "Disk size must be a positive integer!" 8 58 + fi + done + + while true; do + CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --inputbox "Allocate CPU Cores" 8 58 "$var_cpu" --title "CORE COUNT" 3>&1 1>&2 2>&3) || exit_script + + if [ -z "$CORE_COUNT" ]; then + CORE_COUNT="$var_cpu" + fi + + if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + break + else + whiptail --msgbox "CPU core count must be a positive integer!" 8 58 + fi + done + + while true; do + RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --inputbox "Allocate RAM in MiB" 8 58 "$var_ram" --title "RAM" 3>&1 1>&2 2>&3) || exit_script + + if [ -z "$RAM_SIZE" ]; then + RAM_SIZE="$var_ram" + fi + + if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + break + else + whiptail --msgbox "RAM size must be a positive integer!" 8 58 + fi + done + + IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f) + BRIDGES="" + OLD_IFS=$IFS + IFS=$'\n' + for iface_filepath in ${IFACE_FILEPATH_LIST}; do + + iface_indexes_tmpfile=$(mktemp -q -u '.iface-XXXX') + (grep -Pn '^\s*iface' "${iface_filepath}" | cut -d':' -f1 && wc -l "${iface_filepath}" | cut -d' ' -f1) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' >"${iface_indexes_tmpfile}" || true + + if [ -f "${iface_indexes_tmpfile}" ]; then + + while read -r pair; do + start=$(echo "${pair}" | cut -d':' -f1) + end=$(echo "${pair}" | cut -d':' -f2) + + if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then + iface_name=$(sed "${start}q;d" "${iface_filepath}" | awk '{print $2}') + BRIDGES="${iface_name}"$'\n'"${BRIDGES}" + fi + + done <"${iface_indexes_tmpfile}" + rm -f "${iface_indexes_tmpfile}" + fi + + done + IFS=$OLD_IFS + BRIDGES=$(echo "$BRIDGES" | grep -v '^\s*$' | sort | uniq) + if [[ -z "$BRIDGES" ]]; then + BRG="vmbr0" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + else + # Build bridge menu with descriptions + BRIDGE_MENU_OPTIONS=() + while IFS= read -r bridge; do + if [[ -n "$bridge" ]]; then + # Get description from Proxmox built-in method - find comment for this specific bridge + description=$(grep -A 10 "iface $bridge" /etc/network/interfaces | grep '^#' | head -n1 | sed 's/^#\s*//') + if [[ -n "$description" ]]; then + BRIDGE_MENU_OPTIONS+=("$bridge" "${description}") + else + BRIDGE_MENU_OPTIONS+=("$bridge" " ") + fi + fi + done <<<"$BRIDGES" + + BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge: " 18 55 6 "${BRIDGE_MENU_OPTIONS[@]}" 3>&1 1>&2 2>&3) + if [[ -z "$BRG" ]]; then + exit_script + else + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + fi + fi + + # IPv4 methods: dhcp, static, none + while true; do + IPV4_METHOD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "IPv4 Address Management" \ + --menu "Select IPv4 Address Assignment Method:" 12 60 2 \ + "dhcp" "Automatic (DHCP, recommended)" \ + "static" "Static (manual entry)" \ + 3>&1 1>&2 2>&3) + + exit_status=$? + if [ $exit_status -ne 0 ]; then + exit_script + fi + + case "$IPV4_METHOD" in + dhcp) + NET="dhcp" + GATE="" + echo -e "${NETWORK}${BOLD}${DGN}IPv4: DHCP${CL}" + break + ;; + static) + # Static: call and validate CIDR address + while true; do + NET=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Enter Static IPv4 CIDR Address (e.g. 192.168.100.50/24)" 8 58 "" \ + --title "IPv4 ADDRESS" 3>&1 1>&2 2>&3) + if [ -z "$NET" ]; then + whiptail --msgbox "IPv4 address must not be empty." 8 58 + continue + elif [[ "$NET" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then + echo -e "${NETWORK}${BOLD}${DGN}IPv4 Address: ${BGN}$NET${CL}" + break + else + whiptail --msgbox "$NET is not a valid IPv4 CIDR address. Please enter a correct value!" 8 58 + fi + done + + # call and validate Gateway + while true; do + GATE1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Enter Gateway IP address for static IPv4" 8 58 "" \ + --title "Gateway IP" 3>&1 1>&2 2>&3) + if [ -z "$GATE1" ]; then + whiptail --msgbox "Gateway IP address cannot be empty." 8 58 + elif [[ ! "$GATE1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then + whiptail --msgbox "Invalid Gateway IP address format." 8 58 + else + GATE=",gw=$GATE1" + echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE1${CL}" + break + fi + done + break + ;; + esac + done + + # IPv6 Address Management selection + while true; do + IPV6_METHOD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --menu \ + "Select IPv6 Address Management Type:" 15 58 4 \ + "auto" "SLAAC/AUTO (recommended, default)" \ + "dhcp" "DHCPv6" \ + "static" "Static (manual entry)" \ + "none" "Disabled" \ + --default-item "auto" 3>&1 1>&2 2>&3) + [ $? -ne 0 ] && exit_script + + case "$IPV6_METHOD" in + auto) + echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}SLAAC/AUTO${CL}" + IPV6_ADDR="" + IPV6_GATE="" + break + ;; + dhcp) + echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}DHCPv6${CL}" + IPV6_ADDR="dhcp" + IPV6_GATE="" + break + ;; + static) + # Ask for static IPv6 address (CIDR notation, e.g., 2001:db8::1234/64) + while true; do + IPV6_ADDR=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox \ + "Set a static IPv6 CIDR address (e.g., 2001:db8::1234/64)" 8 58 "" \ + --title "IPv6 STATIC ADDRESS" 3>&1 1>&2 2>&3) || exit_script + if [[ "$IPV6_ADDR" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+(/[0-9]{1,3})$ ]]; then + echo -e "${NETWORK}${BOLD}${DGN}IPv6 Address: ${BGN}$IPV6_ADDR${CL}" + break + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox \ + "$IPV6_ADDR is an invalid IPv6 CIDR address. Please enter a valid IPv6 CIDR address (e.g., 2001:db8::1234/64)" 8 58 + fi + done + # Optional: ask for IPv6 gateway for static config + while true; do + IPV6_GATE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox \ + "Enter IPv6 gateway address (optional, leave blank for none)" 8 58 "" --title "IPv6 GATEWAY" 3>&1 1>&2 2>&3) + if [ -z "$IPV6_GATE" ]; then + IPV6_GATE="" + break + elif [[ "$IPV6_GATE" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+$ ]]; then + break + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox \ + "Invalid IPv6 gateway format." 8 58 + fi + done + break + ;; + none) + echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}Disabled${CL}" + IPV6_ADDR="none" + IPV6_GATE="" + break + ;; + *) + exit_script + ;; + esac + done + + if [ "$var_os" == "alpine" ]; then + APT_CACHER="" + APT_CACHER_IP="" + else + if APT_CACHER_IP=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set APT-Cacher IP (leave blank for none)" 8 58 --title "APT-Cacher IP" 3>&1 1>&2 2>&3); then + APT_CACHER="${APT_CACHER_IP:+yes}" + echo -e "${NETWORK}${BOLD}${DGN}APT-Cacher IP Address: ${BGN}${APT_CACHER_IP:-Default}${CL}" + else + exit_script + fi + fi + + # if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "IPv6" --yesno "Disable IPv6?" 10 58); then + # DISABLEIP6="yes" + # else + # DISABLEIP6="no" + # fi + # echo -e "${DISABLEIPV6}${BOLD}${DGN}Disable IPv6: ${BGN}$DISABLEIP6${CL}" + + if MTU1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default [The MTU of your selected vmbr, default is 1500])" 8 58 --title "MTU SIZE" 3>&1 1>&2 2>&3); then + if [ -z "$MTU1" ]; then + MTU1="Default" + MTU="" + else + MTU=",mtu=$MTU1" + fi + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" + else + exit_script + fi + + if SD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a DNS Search Domain (leave blank for HOST)" 8 58 --title "DNS Search Domain" 3>&1 1>&2 2>&3); then + if [ -z "$SD" ]; then + SX=Host + SD="" + else + SX=$SD + SD="-searchdomain=$SD" + fi + echo -e "${SEARCH}${BOLD}${DGN}DNS Search Domain: ${BGN}$SX${CL}" + else + exit_script + fi + + if NX=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a DNS Server IP (leave blank for HOST)" 8 58 --title "DNS SERVER IP" 3>&1 1>&2 2>&3); then + if [ -z "$NX" ]; then + NX=Host + NS="" + else + NS="-nameserver=$NX" + fi + echo -e "${NETWORK}${BOLD}${DGN}DNS Server IP Address: ${BGN}$NX${CL}" + else + exit_script + fi + + if [ "$var_os" == "alpine" ] && [ "$NET" == "dhcp" ] && [ "$NX" != "Host" ]; then + UDHCPC_FIX="yes" + else + UDHCPC_FIX="no" + fi + export UDHCPC_FIX + + if MAC1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a MAC Address(leave blank for generated MAC)" 8 58 --title "MAC ADDRESS" 3>&1 1>&2 2>&3); then + if [ -z "$MAC1" ]; then + MAC1="Default" + MAC="" + else + MAC=",hwaddr=$MAC1" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" + fi + else + exit_script + fi + + if VLAN1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for no VLAN)" 8 58 --title "VLAN" 3>&1 1>&2 2>&3); then + if [ -z "$VLAN1" ]; then + VLAN1="Default" + VLAN="" + else + VLAN=",tag=$VLAN1" + fi + echo -e "${VLANTAG}${BOLD}${DGN}Vlan: ${BGN}$VLAN1${CL}" + else + exit_script + fi + + if ADV_TAGS=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Custom Tags?[If you remove all, there will be no tags!]" 8 58 "${TAGS}" --title "Advanced Tags" 3>&1 1>&2 2>&3); then + if [ -n "${ADV_TAGS}" ]; then + ADV_TAGS=$(echo "$ADV_TAGS" | tr -d '[:space:]') + TAGS="${ADV_TAGS}" + else + TAGS=";" + fi + echo -e "${NETWORK}${BOLD}${DGN}Tags: ${BGN}$TAGS${CL}" + else + exit_script + fi + + configure_ssh_settings + export SSH_KEYS_FILE + echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}" + if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "FUSE Support" --yesno "Enable FUSE support?\nRequired for tools like rclone, mergerfs, AppImage, etc." 10 58); then + ENABLE_FUSE="yes" + else + ENABLE_FUSE="no" + fi + echo -e "${FUSE}${BOLD}${DGN}Enable FUSE Support: ${BGN}$ENABLE_FUSE${CL}" + + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then + VERBOSE="yes" + else + VERBOSE="no" + fi + echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}" + + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create ${APP} LXC?" 10 58); then + echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}" + else + clear + header_info + echo -e "${INFO}${BOLD}${DGN}PVE Version ${PVEVERSION} (Kernel: ${KERNEL_VERSION})${CL}" + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings on node $PVEHOST_NAME${CL}" + advanced_settings + fi +} + +# ------------------------------------------------------------------------------ +# diagnostics_check() +# +# - Ensures diagnostics config file exists at /usr/local/community-scripts/diagnostics +# - Asks user whether to send anonymous diagnostic data +# - Saves DIAGNOSTICS=yes/no in the config file +# ------------------------------------------------------------------------------ +diagnostics_check() { + if ! [ -d "/usr/local/community-scripts" ]; then + mkdir -p /usr/local/community-scripts + fi + + if ! [ -f "/usr/local/community-scripts/diagnostics" ]; then + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS" --yesno "Send Diagnostics of LXC Installation?\n\n(This only transmits data without user data, just RAM, CPU, LXC name, ...)" 10 58); then + cat </usr/local/community-scripts/diagnostics +DIAGNOSTICS=yes + +#This file is used to store the diagnostics settings for the Community-Scripts API. +#https://github.com/community-scripts/ProxmoxVED/discussions/1836 +#Your diagnostics will be sent to the Community-Scripts API for troubleshooting/statistical purposes. +#You can review the data at https://community-scripts.github.io/ProxmoxVE/data +#If you do not wish to send diagnostics, please set the variable 'DIAGNOSTICS' to "no" in /usr/local/community-scripts/diagnostics, or use the menue. +#This will disable the diagnostics feature. +#To send diagnostics, set the variable 'DIAGNOSTICS' to "yes" in /usr/local/community-scripts/diagnostics, or use the menue. +#This will enable the diagnostics feature. +#The following information will be sent: +#"disk_size" +#"core_count" +#"ram_size" +#"os_type" +#"os_version" +#"nsapp" +#"method" +#"pve_version" +#"status" +#If you have any concerns, please review the source code at /misc/build.func +EOF + DIAGNOSTICS="yes" + else + cat </usr/local/community-scripts/diagnostics +DIAGNOSTICS=no + +#This file is used to store the diagnostics settings for the Community-Scripts API. +#https://github.com/community-scripts/ProxmoxVED/discussions/1836 +#Your diagnostics will be sent to the Community-Scripts API for troubleshooting/statistical purposes. +#You can review the data at https://community-scripts.github.io/ProxmoxVE/data +#If you do not wish to send diagnostics, please set the variable 'DIAGNOSTICS' to "no" in /usr/local/community-scripts/diagnostics, or use the menue. +#This will disable the diagnostics feature. +#To send diagnostics, set the variable 'DIAGNOSTICS' to "yes" in /usr/local/community-scripts/diagnostics, or use the menue. +#This will enable the diagnostics feature. +#The following information will be sent: +#"disk_size" +#"core_count" +#"ram_size" +#"os_type" +#"os_version" +#"nsapp" +#"method" +#"pve_version" +#"status" +#If you have any concerns, please review the source code at /misc/build.func +EOF + DIAGNOSTICS="no" + fi + else + DIAGNOSTICS=$(awk -F '=' '/^DIAGNOSTICS/ {print $2}' /usr/local/community-scripts/diagnostics) + + fi + +} + +# ------------------------------------------------------------------------------ +# default_var_settings +# +# - Ensures /usr/local/community-scripts/default.vars exists (creates if missing) +# - Loads var_* values from default.vars (safe parser, no source/eval) +# - Precedence: ENV var_* > default.vars > built-in defaults +# - Maps var_verbose → VERBOSE +# - Calls base_settings "$VERBOSE" and echo_default +# ------------------------------------------------------------------------------ +default_var_settings() { + # Allowed var_* keys (alphabetically sorted) + local VAR_WHITELIST=( + var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_ctid var_disk var_fuse + var_gateway var_hostname var_ipv6_method var_ipv6_static var_mac var_mtu + var_net var_ns var_pw var_ram var_tags var_tun var_unprivileged + var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage + ) + + # Snapshot: environment variables (highest precedence) + declare -A _HARD_ENV=() + local _k + for _k in "${VAR_WHITELIST[@]}"; do + if printenv "$_k" >/dev/null 2>&1; then _HARD_ENV["$_k"]=1; fi + done + + # Find default.vars location + local _find_default_vars + _find_default_vars() { + local f + for f in \ + /usr/local/community-scripts/default.vars \ + "$HOME/.config/community-scripts/default.vars" \ + "./default.vars"; do + [ -f "$f" ] && { + echo "$f" + return 0 + } + done + return 1 + } + # Allow override of storages via env (for non-interactive use cases) + [ -n "${var_template_storage:-}" ] && TEMPLATE_STORAGE="$var_template_storage" + [ -n "${var_container_storage:-}" ] && CONTAINER_STORAGE="$var_container_storage" + + # Create once, with storages already selected, no var_ctid/var_hostname lines + local _ensure_default_vars + _ensure_default_vars() { + _find_default_vars >/dev/null 2>&1 && return 0 + + local canonical="/usr/local/community-scripts/default.vars" + msg_info "No default.vars found. Creating ${canonical}" + mkdir -p /usr/local/community-scripts + + # Pick storages before writing the file (always ask unless only one) + # Create a minimal temp file to write into + : >"$canonical" + + # Base content (no var_ctid / var_hostname here) + cat >"$canonical" <<'EOF' +# Community-Scripts defaults (var_* only). Lines starting with # are comments. +# Precedence: ENV var_* > default.vars > built-ins. +# Keep keys alphabetically sorted. + +# Container type +var_unprivileged=1 + +# Resources +var_cpu=1 +var_disk=4 +var_ram=1024 + +# Network +var_brg=vmbr0 +var_net=dhcp +var_ipv6_method=none +# var_gateway= +# var_ipv6_static= +# var_vlan= +# var_mtu= +# var_mac= +# var_ns= + +# SSH +var_ssh=no +# var_ssh_authorized_key= + +# APT cacher (optional) +# var_apt_cacher=yes +# var_apt_cacher_ip=192.168.1.10 + +# Features/Tags/verbosity +var_fuse=no +var_tun=no +var_tags=community-script +var_verbose=no + +# Security (root PW) – empty => autologin +# var_pw= +EOF + + # Now choose storages (always prompt unless just one exists) + choose_and_set_storage_for_file "$canonical" template + choose_and_set_storage_for_file "$canonical" container + + chmod 0644 "$canonical" + msg_ok "Created ${canonical}" + } + + # Whitelist check + local _is_whitelisted_key + _is_whitelisted_key() { + local k="$1" + local w + for w in "${VAR_WHITELIST[@]}"; do [ "$k" = "$w" ] && return 0; done + return 1 + } + + # Safe parser for KEY=VALUE lines + local _load_vars_file + _load_vars_file() { + local file="$1" + [ -f "$file" ] || return 0 + msg_info "Loading defaults from ${file}" + local line key val + while IFS= read -r line || [ -n "$line" ]; do + line="${line#"${line%%[![:space:]]*}"}" + line="${line%"${line##*[![:space:]]}"}" + [[ -z "$line" || "$line" == \#* ]] && continue + if [[ "$line" =~ ^([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then + local var_key="${BASH_REMATCH[1]}" + local var_val="${BASH_REMATCH[2]}" + + [[ "$var_key" != var_* ]] && continue + _is_whitelisted_key "$var_key" || { + msg_debug "Ignore non-whitelisted ${var_key}" + continue + } + + # Strip quotes + if [[ "$var_val" =~ ^\"(.*)\"$ ]]; then + var_val="${BASH_REMATCH[1]}" + elif [[ "$var_val" =~ ^\'(.*)\'$ ]]; then + var_val="${BASH_REMATCH[1]}" + fi + + # Unsafe characters + case $var_val in + \"*\") + var_val=${var_val#\"} + var_val=${var_val%\"} + ;; + \'*\') + var_val=${var_val#\'} + var_val=${var_val%\'} + ;; + esac # Hard env wins + [[ -n "${_HARD_ENV[$var_key]:-}" ]] && continue + # Set only if not already exported + [[ -z "${!var_key+x}" ]] && export "${var_key}=${var_val}" + else + msg_warn "Malformed line in ${file}: ${line}" + fi + done <"$file" + msg_ok "Loaded ${file}" + } + + # 1) Ensure file exists + _ensure_default_vars + + # 2) Load file + local dv + dv="$(_find_default_vars)" || { + msg_error "default.vars not found after ensure step" + return 1 + } + _load_vars_file "$dv" + + # 3) Map var_verbose → VERBOSE + if [[ -n "${var_verbose:-}" ]]; then + case "${var_verbose,,}" in 1 | yes | true | on) VERBOSE="yes" ;; 0 | no | false | off) VERBOSE="no" ;; *) VERBOSE="${var_verbose}" ;; esac + else + VERBOSE="no" + fi + + # 4) Apply base settings and show summary + METHOD="mydefaults-global" + base_settings "$VERBOSE" + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using My Defaults (default.vars) on node $PVEHOST_NAME${CL}" + echo_default +} + +# ------------------------------------------------------------------------------ +# get_app_defaults_path() +# +# - Returns full path for app-specific defaults file +# - Example: /usr/local/community-scripts/defaults/.vars +# ------------------------------------------------------------------------------ + +get_app_defaults_path() { + local n="${NSAPP:-${APP,,}}" + echo "/usr/local/community-scripts/defaults/${n}.vars" +} + +# ------------------------------------------------------------------------------ +# maybe_offer_save_app_defaults +# +# - Called after advanced_settings returned with fully chosen values. +# - If no .vars exists, offers to persist current advanced settings +# into /usr/local/community-scripts/defaults/.vars +# - Only writes whitelisted var_* keys. +# - Extracts raw values from flags like ",gw=..." ",mtu=..." etc. +# ------------------------------------------------------------------------------ +if ! declare -p VAR_WHITELIST >/dev/null 2>&1; then + declare -ag VAR_WHITELIST=( + var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_ctid var_disk var_fuse + var_gateway var_hostname var_ipv6_method var_ipv6_static var_mac var_mtu + var_net var_ns var_pw var_ram var_tags var_tun var_unprivileged + var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage + ) +fi + +# Note: _is_whitelisted_key() is defined above in default_var_settings section + +_sanitize_value() { + # Disallow Command-Substitution / Shell-Meta + case "$1" in + *'$('* | *'`'* | *';'* | *'&'* | *'<('*) + echo "" + return 0 + ;; + esac + echo "$1" +} + +# Map-Parser: read var_* from file into _VARS_IN associative array +# Note: Main _load_vars_file() with full validation is defined in default_var_settings section +# This simplified version is used specifically for diff operations via _VARS_IN array +declare -A _VARS_IN +_load_vars_file_to_map() { + local file="$1" + [ -f "$file" ] || return 0 + _VARS_IN=() # Clear array + local line key val + while IFS= read -r line || [ -n "$line" ]; do + line="${line#"${line%%[![:space:]]*}"}" + line="${line%"${line##*[![:space:]]}"}" + [ -z "$line" ] && continue + case "$line" in + \#*) continue ;; + esac + key=$(printf "%s" "$line" | cut -d= -f1) + val=$(printf "%s" "$line" | cut -d= -f2-) + case "$key" in + var_*) + if _is_whitelisted_key "$key"; then + _VARS_IN["$key"]="$val" + fi + ;; + esac + done <"$file" +} + +# Diff function for two var_* files -> produces human-readable diff list for $1 (old) vs $2 (new) +_build_vars_diff() { + local oldf="$1" newf="$2" + local k + local -A OLD=() NEW=() + _load_vars_file_to_map "$oldf" + for k in "${!_VARS_IN[@]}"; do OLD["$k"]="${_VARS_IN[$k]}"; done + _load_vars_file_to_map "$newf" + for k in "${!_VARS_IN[@]}"; do NEW["$k"]="${_VARS_IN[$k]}"; done + + local out + out+="# Diff for ${APP} (${NSAPP})\n" + out+="# Old: ${oldf}\n# New: ${newf}\n\n" + + local found_change=0 + + # Changed & Removed + for k in "${!OLD[@]}"; do + if [[ -v NEW["$k"] ]]; then + if [[ "${OLD[$k]}" != "${NEW[$k]}" ]]; then + out+="~ ${k}\n - old: ${OLD[$k]}\n + new: ${NEW[$k]}\n" + found_change=1 + fi + else + out+="- ${k}\n - old: ${OLD[$k]}\n" + found_change=1 + fi + done + + # Added + for k in "${!NEW[@]}"; do + if [[ ! -v OLD["$k"] ]]; then + out+="+ ${k}\n + new: ${NEW[$k]}\n" + found_change=1 + fi + done + + if [[ $found_change -eq 0 ]]; then + out+="(No differences)\n" + fi + + printf "%b" "$out" +} + +# Build a temporary .vars file from current advanced settings +_build_current_app_vars_tmp() { + tmpf="$(mktemp /tmp/${NSAPP:-app}.vars.new.XXXXXX)" + + # NET/GW + _net="${NET:-}" + _gate="" + case "${GATE:-}" in + ,gw=*) _gate=$(echo "$GATE" | sed 's/^,gw=//') ;; + esac + + # IPv6 + _ipv6_method="${IPV6_METHOD:-auto}" + _ipv6_static="" + _ipv6_gateway="" + if [ "$_ipv6_method" = "static" ]; then + _ipv6_static="${IPV6_ADDR:-}" + _ipv6_gateway="${IPV6_GATE:-}" + fi + + # MTU/VLAN/MAC + _mtu="" + _vlan="" + _mac="" + case "${MTU:-}" in + ,mtu=*) _mtu=$(echo "$MTU" | sed 's/^,mtu=//') ;; + esac + case "${VLAN:-}" in + ,tag=*) _vlan=$(echo "$VLAN" | sed 's/^,tag=//') ;; + esac + case "${MAC:-}" in + ,hwaddr=*) _mac=$(echo "$MAC" | sed 's/^,hwaddr=//') ;; + esac + + # DNS / Searchdomain + _ns="" + _searchdomain="" + case "${NS:-}" in + -nameserver=*) _ns=$(echo "$NS" | sed 's/^-nameserver=//') ;; + esac + case "${SD:-}" in + -searchdomain=*) _searchdomain=$(echo "$SD" | sed 's/^-searchdomain=//') ;; + esac + + # SSH / APT / Features + _ssh="${SSH:-no}" + _ssh_auth="${SSH_AUTHORIZED_KEY:-}" + _apt_cacher="${APT_CACHER:-}" + _apt_cacher_ip="${APT_CACHER_IP:-}" + _fuse="${ENABLE_FUSE:-no}" + _tun="${ENABLE_TUN:-no}" + _tags="${TAGS:-}" + _verbose="${VERBOSE:-no}" + + # Type / Resources / Identity + _unpriv="${CT_TYPE:-1}" + _cpu="${CORE_COUNT:-1}" + _ram="${RAM_SIZE:-1024}" + _disk="${DISK_SIZE:-4}" + _hostname="${HN:-$NSAPP}" + + # Storage + _tpl_storage="${TEMPLATE_STORAGE:-${var_template_storage:-}}" + _ct_storage="${CONTAINER_STORAGE:-${var_container_storage:-}}" + + { + echo "# App-specific defaults for ${APP} (${NSAPP})" + echo "# Generated on $(date -u '+%Y-%m-%dT%H:%M:%SZ')" + echo + + echo "var_unprivileged=$(_sanitize_value "$_unpriv")" + echo "var_cpu=$(_sanitize_value "$_cpu")" + echo "var_ram=$(_sanitize_value "$_ram")" + echo "var_disk=$(_sanitize_value "$_disk")" + + [ -n "${BRG:-}" ] && echo "var_brg=$(_sanitize_value "$BRG")" + [ -n "$_net" ] && echo "var_net=$(_sanitize_value "$_net")" + [ -n "$_gate" ] && echo "var_gateway=$(_sanitize_value "$_gate")" + [ -n "$_mtu" ] && echo "var_mtu=$(_sanitize_value "$_mtu")" + [ -n "$_vlan" ] && echo "var_vlan=$(_sanitize_value "$_vlan")" + [ -n "$_mac" ] && echo "var_mac=$(_sanitize_value "$_mac")" + [ -n "$_ns" ] && echo "var_ns=$(_sanitize_value "$_ns")" + + [ -n "$_ipv6_method" ] && echo "var_ipv6_method=$(_sanitize_value "$_ipv6_method")" + [ -n "$_ipv6_static" ] && echo "var_ipv6_static=$(_sanitize_value "$_ipv6_static")" + + [ -n "$_ssh" ] && echo "var_ssh=$(_sanitize_value "$_ssh")" + [ -n "$_ssh_auth" ] && echo "var_ssh_authorized_key=$(_sanitize_value "$_ssh_auth")" + + [ -n "$_apt_cacher" ] && echo "var_apt_cacher=$(_sanitize_value "$_apt_cacher")" + [ -n "$_apt_cacher_ip" ] && echo "var_apt_cacher_ip=$(_sanitize_value "$_apt_cacher_ip")" + + [ -n "$_fuse" ] && echo "var_fuse=$(_sanitize_value "$_fuse")" + [ -n "$_tun" ] && echo "var_tun=$(_sanitize_value "$_tun")" + [ -n "$_tags" ] && echo "var_tags=$(_sanitize_value "$_tags")" + [ -n "$_verbose" ] && echo "var_verbose=$(_sanitize_value "$_verbose")" + + [ -n "$_hostname" ] && echo "var_hostname=$(_sanitize_value "$_hostname")" + [ -n "$_searchdomain" ] && echo "var_searchdomain=$(_sanitize_value "$_searchdomain")" + + [ -n "$_tpl_storage" ] && echo "var_template_storage=$(_sanitize_value "$_tpl_storage")" + [ -n "$_ct_storage" ] && echo "var_container_storage=$(_sanitize_value "$_ct_storage")" + } >"$tmpf" + + echo "$tmpf" +} + +# ------------------------------------------------------------------------------ +# maybe_offer_save_app_defaults() +# +# - Called after advanced_settings() +# - Offers to save current values as app defaults if not existing +# - If file exists: shows diff and allows Update, Keep, View Diff, or Cancel +# ------------------------------------------------------------------------------ +maybe_offer_save_app_defaults() { + local app_vars_path + app_vars_path="$(get_app_defaults_path)" + + # always build from current settings + local new_tmp diff_tmp + new_tmp="$(_build_current_app_vars_tmp)" + diff_tmp="$(mktemp -p /tmp "${NSAPP:-app}.vars.diff.XXXXXX")" + + # 1) if no file → offer to create + if [[ ! -f "$app_vars_path" ]]; then + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --yesno "Save these advanced settings as defaults for ${APP}?\n\nThis will create:\n${app_vars_path}" 12 72; then + mkdir -p "$(dirname "$app_vars_path")" + install -m 0644 "$new_tmp" "$app_vars_path" + msg_ok "Saved app defaults: ${app_vars_path}" + fi + rm -f "$new_tmp" "$diff_tmp" + return 0 + fi + + # 2) if file exists → build diff + _build_vars_diff "$app_vars_path" "$new_tmp" >"$diff_tmp" + + # if no differences → do nothing + if grep -q "^(No differences)$" "$diff_tmp"; then + rm -f "$new_tmp" "$diff_tmp" + return 0 + fi + + # 3) if file exists → show menu with default selection "Update Defaults" + local app_vars_file + app_vars_file="$(basename "$app_vars_path")" + + while true; do + local sel + sel="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "APP DEFAULTS – ${APP}" \ + --menu "Differences detected. What do you want to do?" 20 78 10 \ + "Update Defaults" "Write new values to ${app_vars_file}" \ + "Keep Current" "Keep existing defaults (no changes)" \ + "View Diff" "Show a detailed diff" \ + "Cancel" "Abort without changes" \ + --default-item "Update Defaults" \ + 3>&1 1>&2 2>&3)" || { sel="Cancel"; } + + case "$sel" in + "Update Defaults") + install -m 0644 "$new_tmp" "$app_vars_path" + msg_ok "Updated app defaults: ${app_vars_path}" + break + ;; + "Keep Current") + msg_info "Keeping current app defaults: ${app_vars_path}" + break + ;; + "View Diff") + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "Diff – ${APP}" \ + --scrolltext --textbox "$diff_tmp" 25 100 + ;; + "Cancel" | *) + msg_info "Canceled. No changes to app defaults." + break + ;; + esac + done + + rm -f "$new_tmp" "$diff_tmp" +} + +ensure_storage_selection_for_vars_file() { + local vf="$1" + + # Read stored values (if any) + local tpl ct + tpl=$(grep -E '^var_template_storage=' "$vf" | cut -d= -f2-) + ct=$(grep -E '^var_container_storage=' "$vf" | cut -d= -f2-) + + if [[ -n "$tpl" && -n "$ct" ]]; then + TEMPLATE_STORAGE="$tpl" + CONTAINER_STORAGE="$ct" + return 0 + fi + + choose_and_set_storage_for_file "$vf" template + choose_and_set_storage_for_file "$vf" container + + msg_ok "Storage configuration saved to $(basename "$vf")" +} + +diagnostics_menu() { + if [ "${DIAGNOSTICS:-no}" = "yes" ]; then + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "DIAGNOSTIC SETTINGS" \ + --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ + --yes-button "No" --no-button "Back"; then + DIAGNOSTICS="no" + sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=no/' /usr/local/community-scripts/diagnostics + whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 + fi + else + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "DIAGNOSTIC SETTINGS" \ + --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ + --yes-button "Yes" --no-button "Back"; then + DIAGNOSTICS="yes" + sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=yes/' /usr/local/community-scripts/diagnostics + whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 + fi + fi +} + +ensure_global_default_vars_file() { + local vars_path="/usr/local/community-scripts/default.vars" + if [[ ! -f "$vars_path" ]]; then + mkdir -p "$(dirname "$vars_path")" + touch "$vars_path" + fi + echo "$vars_path" +} + +# ------------------------------------------------------------------------------ +# install_script() +# +# - Main entrypoint for installation mode +# - Runs safety checks (pve_check, root_check, maxkeys_check, diagnostics_check) +# - Builds interactive menu (Default, Verbose, Advanced, My Defaults, App Defaults, Diagnostics, Storage, Exit) +# - Applies chosen settings and triggers container build +# ------------------------------------------------------------------------------ +install_script() { + pve_check + shell_check + root_check + arch_check + ssh_check + maxkeys_check + diagnostics_check + + if systemctl is-active -q ping-instances.service; then + systemctl -q stop ping-instances.service + fi + + NEXTID=$(pvesh get /cluster/nextid) + timezone=$(cat /etc/timezone) + + # Show APP Header + header_info + + # --- Support CLI argument as direct preset (default, advanced, …) --- + CHOICE="${mode:-${1:-}}" + + # If no CLI argument → show whiptail menu + # Build menu dynamically based on available options + local appdefaults_option="" + local settings_option="" + local menu_items=( + "1" "Default Install" + "2" "Advanced Install" + "3" "My Defaults" + ) + + if [ -f "$(get_app_defaults_path)" ]; then + appdefaults_option="4" + menu_items+=("4" "App Defaults for ${APP}") + settings_option="5" + menu_items+=("5" "Settings") + else + settings_option="4" + menu_items+=("4" "Settings") + fi + + if [ -z "$CHOICE" ]; then + + TMP_CHOICE=$(whiptail \ + --backtitle "Proxmox VE Helper Scripts" \ + --title "Community-Scripts Options" \ + --ok-button "Select" --cancel-button "Exit Script" \ + --notags \ + --menu "\nChoose an option:\n Use TAB or Arrow keys to navigate, ENTER to select.\n" \ + 20 60 9 \ + "${menu_items[@]}" \ + --default-item "1" \ + 3>&1 1>&2 2>&3) || exit_script + CHOICE="$TMP_CHOICE" + fi + + APPDEFAULTS_OPTION="$appdefaults_option" + SETTINGS_OPTION="$settings_option" + + # --- Main case --- + local defaults_target="" + local run_maybe_offer="no" + case "$CHOICE" in + 1 | default | DEFAULT) + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME${CL}" + VERBOSE="no" + METHOD="default" + base_settings "$VERBOSE" + echo_default + defaults_target="$(ensure_global_default_vars_file)" + ;; + 2 | advanced | ADVANCED) + header_info + + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Install on node $PVEHOST_NAME${CL}" + echo -e "${INFO}${BOLD}${DGN}PVE Version ${PVEVERSION} (Kernel: ${KERNEL_VERSION})${CL}" + METHOD="advanced" + base_settings + advanced_settings + defaults_target="$(ensure_global_default_vars_file)" + run_maybe_offer="yes" + ;; + 3 | mydefaults | MYDEFAULTS) + default_var_settings || { + msg_error "Failed to apply default.vars" + exit 1 + } + defaults_target="/usr/local/community-scripts/default.vars" + ;; + "$APPDEFAULTS_OPTION" | appdefaults | APPDEFAULTS) + if [ -f "$(get_app_defaults_path)" ]; then + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using App Defaults for ${APP} on node $PVEHOST_NAME${CL}" + METHOD="appdefaults" + base_settings + _load_vars_file "$(get_app_defaults_path)" + echo_default + defaults_target="$(get_app_defaults_path)" + else + msg_error "No App Defaults available for ${APP}" + exit 1 + fi + ;; + "$SETTINGS_OPTION" | settings | SETTINGS) + settings_menu + defaults_target="" + ;; + *) + echo -e "${CROSS}${RD}Invalid option: $CHOICE${CL}" + exit 1 + ;; + esac + + if [[ -n "$defaults_target" ]]; then + ensure_storage_selection_for_vars_file "$defaults_target" + fi + + if [[ "$run_maybe_offer" == "yes" ]]; then + maybe_offer_save_app_defaults + fi +} + +edit_default_storage() { + local vf="/usr/local/community-scripts/default.vars" + + # Ensure file exists + if [[ ! -f "$vf" ]]; then + mkdir -p "$(dirname "$vf")" + touch "$vf" + fi + + # Let ensure_storage_selection_for_vars_file handle everything + ensure_storage_selection_for_vars_file "$vf" +} + +settings_menu() { + while true; do + local settings_items=( + "1" "Manage API-Diagnostic Setting" + "2" "Edit Default.vars" + "3" "Edit Default Storage" + ) + if [ -f "$(get_app_defaults_path)" ]; then + settings_items+=("4" "Edit App.vars for ${APP}") + settings_items+=("5" "Exit") + else + settings_items+=("4" "Exit") + fi + + local choice + choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "Community-Scripts SETTINGS Menu" \ + --ok-button "OK" --cancel-button "Back" \ + --menu "\n\nChoose a settings option:\n\nUse TAB or Arrow keys to navigate, ENTER to select." 20 60 9 \ + "${settings_items[@]}" \ + 3>&1 1>&2 2>&3) || break + + case "$choice" in + 1) diagnostics_menu ;; + 2) ${EDITOR:-nano} /usr/local/community-scripts/default.vars ;; + 3) edit_default_storage ;; + 4) + if [ -f "$(get_app_defaults_path)" ]; then + ${EDITOR:-nano} "$(get_app_defaults_path)" + else + exit_script + fi + ;; + 5) exit_script ;; + esac + done +} + +# ===== Unified storage selection & writing to vars files ===== +_write_storage_to_vars() { + # $1 = vars_file, $2 = key (var_container_storage / var_template_storage), $3 = value + local vf="$1" key="$2" val="$3" + # remove uncommented and commented versions to avoid duplicates + sed -i "/^[#[:space:]]*${key}=/d" "$vf" + echo "${key}=${val}" >>"$vf" +} + +choose_and_set_storage_for_file() { + # $1 = vars_file, $2 = class ('container'|'template') + local vf="$1" class="$2" key="" current="" + case "$class" in + container) key="var_container_storage" ;; + template) key="var_template_storage" ;; + *) + msg_error "Unknown storage class: $class" + return 1 + ;; + esac + + current=$(awk -F= -v k="^${key}=" '$0 ~ k {print $2; exit}' "$vf") + + # If only one storage exists for the content type, auto-pick. Else always ask (your wish #4). + local content="rootdir" + [[ "$class" == "template" ]] && content="vztmpl" + local count + count=$(pvesm status -content "$content" | awk 'NR>1{print $1}' | wc -l) + + if [[ "$count" -eq 1 ]]; then + STORAGE_RESULT=$(pvesm status -content "$content" | awk 'NR>1{print $1; exit}') + STORAGE_INFO="" + else + # If the current value is preselectable, we could show it, but per your requirement we always offer selection + select_storage "$class" || return 1 + fi + + _write_storage_to_vars "$vf" "$key" "$STORAGE_RESULT" + + # Keep environment in sync for later steps (e.g. app-default save) + if [[ "$class" == "container" ]]; then + export var_container_storage="$STORAGE_RESULT" + export CONTAINER_STORAGE="$STORAGE_RESULT" + else + export var_template_storage="$STORAGE_RESULT" + export TEMPLATE_STORAGE="$STORAGE_RESULT" + fi + + msg_ok "Updated ${key} → ${STORAGE_RESULT}" +} + +# ------------------------------------------------------------------------------ +# check_container_resources() +# +# - Compares host RAM/CPU with required values +# - Warns if under-provisioned and asks user to continue or abort +# ------------------------------------------------------------------------------ +check_container_resources() { + current_ram=$(free -m | awk 'NR==2{print $2}') + current_cpu=$(nproc) + + if [[ "$current_ram" -lt "$var_ram" ]] || [[ "$current_cpu" -lt "$var_cpu" ]]; then + echo -e "\n${INFO}${HOLD} ${GN}Required: ${var_cpu} CPU, ${var_ram}MB RAM ${CL}| ${RD}Current: ${current_cpu} CPU, ${current_ram}MB RAM${CL}" + echo -e "${YWB}Please ensure that the ${APP} LXC is configured with at least ${var_cpu} vCPU and ${var_ram} MB RAM for the build process.${CL}\n" + echo -ne "${INFO}${HOLD} May cause data loss! ${INFO} Continue update with under-provisioned LXC? " + read -r prompt + if [[ ! ${prompt,,} =~ ^(yes)$ ]]; then + echo -e "${CROSS}${HOLD} ${YWB}Exiting based on user input.${CL}" + exit 1 + fi + else + echo -e "" + fi +} + +# ------------------------------------------------------------------------------ +# check_container_storage() +# +# - Checks /boot partition usage +# - Warns if usage >80% and asks user confirmation before proceeding +# ------------------------------------------------------------------------------ +check_container_storage() { + total_size=$(df /boot --output=size | tail -n 1) + local used_size=$(df /boot --output=used | tail -n 1) + usage=$((100 * used_size / total_size)) + if ((usage > 80)); then + echo -e "${INFO}${HOLD} ${YWB}Warning: Storage is dangerously low (${usage}%).${CL}" + echo -ne "Continue anyway? " + read -r prompt + if [[ ! ${prompt,,} =~ ^(y|yes)$ ]]; then + echo -e "${CROSS}${HOLD}${YWB}Exiting based on user input.${CL}" + exit 1 + fi + fi +} + +# ------------------------------------------------------------------------------ +# ssh_extract_keys_from_file() +# +# - Extracts valid SSH public keys from given file +# - Supports RSA, Ed25519, ECDSA and filters out comments/invalid lines +# ------------------------------------------------------------------------------ +ssh_extract_keys_from_file() { + local f="$1" + [[ -r "$f" ]] || return 0 + tr -d '\r' <"$f" | awk ' + /^[[:space:]]*#/ {next} + /^[[:space:]]*$/ {next} + # nackt: typ base64 [comment] + /^(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/ {print; next} + # mit Optionen: finde ab erstem Key-Typ + { + match($0, /(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/) + if (RSTART>0) { print substr($0, RSTART) } + } + ' +} + +# ------------------------------------------------------------------------------ +# ssh_build_choices_from_files() +# +# - Builds interactive whiptail checklist of available SSH keys +# - Generates fingerprint, type and comment for each key +# ------------------------------------------------------------------------------ +ssh_build_choices_from_files() { + local -a files=("$@") + CHOICES=() + COUNT=0 + MAPFILE="$(mktemp)" + local id key typ fp cmt base ln=0 + + for f in "${files[@]}"; do + [[ -f "$f" && -r "$f" ]] || continue + base="$(basename -- "$f")" + case "$base" in + known_hosts | known_hosts.* | config) continue ;; + id_*) [[ "$f" != *.pub ]] && continue ;; + esac + + # map every key in file + while IFS= read -r key; do + [[ -n "$key" ]] || continue + + typ="" + fp="" + cmt="" + # Only the pure key part (without options) is already included in ‘key’. + read -r _typ _b64 _cmt <<<"$key" + typ="${_typ:-key}" + cmt="${_cmt:-}" + # Fingerprint via ssh-keygen (if available) + if command -v ssh-keygen >/dev/null 2>&1; then + fp="$(printf '%s\n' "$key" | ssh-keygen -lf - 2>/dev/null | awk '{print $2}')" + fi + # Label shorten + [[ ${#cmt} -gt 40 ]] && cmt="${cmt:0:37}..." + + ln=$((ln + 1)) + COUNT=$((COUNT + 1)) + id="K${COUNT}" + echo "${id}|${key}" >>"$MAPFILE" + CHOICES+=("$id" "[$typ] ${fp:+$fp }${cmt:+$cmt }— ${base}" "OFF") + done < <(ssh_extract_keys_from_file "$f") + done +} + +# ------------------------------------------------------------------------------ +# ssh_discover_default_files() +# +# - Scans standard paths for SSH keys +# - Includes ~/.ssh/*.pub, /etc/ssh/authorized_keys, etc. +# ------------------------------------------------------------------------------ +ssh_discover_default_files() { + local -a cand=() + shopt -s nullglob + cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2) + cand+=(/root/.ssh/*.pub) + cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*) + shopt -u nullglob + printf '%s\0' "${cand[@]}" +} + +configure_ssh_settings() { + SSH_KEYS_FILE="$(mktemp)" + : >"$SSH_KEYS_FILE" + + IFS=$'\0' read -r -d '' -a _def_files < <(ssh_discover_default_files && printf '\0') + ssh_build_choices_from_files "${_def_files[@]}" + local default_key_count="$COUNT" + + local ssh_key_mode + if [[ "$default_key_count" -gt 0 ]]; then + ssh_key_mode=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ + "Provision SSH keys for root:" 14 72 4 \ + "found" "Select from detected keys (${default_key_count})" \ + "manual" "Paste a single public key" \ + "folder" "Scan another folder (path or glob)" \ + "none" "No keys" 3>&1 1>&2 2>&3) || exit_script + else + ssh_key_mode=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ + "No host keys detected; choose manual/none:" 12 72 2 \ + "manual" "Paste a single public key" \ + "none" "No keys" 3>&1 1>&2 2>&3) || exit_script + fi + + case "$ssh_key_mode" in + found) + local selection + selection=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT HOST KEYS" \ + --checklist "Select one or more keys to import:" 20 140 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script + for tag in $selection; do + tag="${tag%\"}" + tag="${tag#\"}" + local line + line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) + [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" + done + ;; + manual) + SSH_AUTHORIZED_KEY="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Paste one SSH public key line (ssh-ed25519/ssh-rsa/...)" 10 72 --title "SSH Public Key" 3>&1 1>&2 2>&3)" + [[ -n "$SSH_AUTHORIZED_KEY" ]] && printf '%s\n' "$SSH_AUTHORIZED_KEY" >>"$SSH_KEYS_FILE" + ;; + folder) + local glob_path + glob_path=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Enter a folder or glob to scan (e.g. /root/.ssh/*.pub)" 10 72 --title "Scan Folder/Glob" 3>&1 1>&2 2>&3) + if [[ -n "$glob_path" ]]; then + shopt -s nullglob + read -r -a _scan_files <<<"$glob_path" + shopt -u nullglob + if [[ "${#_scan_files[@]}" -gt 0 ]]; then + ssh_build_choices_from_files "${_scan_files[@]}" + if [[ "$COUNT" -gt 0 ]]; then + local folder_selection + folder_selection=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT FOLDER KEYS" \ + --checklist "Select key(s) to import:" 20 78 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script + for tag in $folder_selection; do + tag="${tag%\"}" + tag="${tag#\"}" + local line + line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) + [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" + done + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "No keys found in: $glob_path" 8 60 + fi + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "Path/glob returned no files." 8 60 + fi + fi + ;; + none) + : + ;; + esac + + if [[ -s "$SSH_KEYS_FILE" ]]; then + sort -u -o "$SSH_KEYS_FILE" "$SSH_KEYS_FILE" + printf '\n' >>"$SSH_KEYS_FILE" + fi + + if [[ -s "$SSH_KEYS_FILE" || "$PW" == -password* ]]; then + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable root SSH access?" 10 58); then + SSH="yes" + else + SSH="no" + fi + else + SSH="no" + fi +} + +# ------------------------------------------------------------------------------ +# start() +# +# - Entry point of script +# - On Proxmox host: calls install_script +# - In silent mode: runs update_script +# - Otherwise: shows update/setting menu +# ------------------------------------------------------------------------------ +start() { + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) + if command -v pveversion >/dev/null 2>&1; then + install_script || return 0 + return 0 + elif [ ! -z ${PHS_SILENT+x} ] && [[ "${PHS_SILENT}" == "1" ]]; then + VERBOSE="no" + set_std_mode + update_script + else + CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "${APP} LXC Update/Setting" --menu \ + "Support/Update functions for ${APP} LXC. Choose an option:" \ + 12 60 3 \ + "1" "YES (Silent Mode)" \ + "2" "YES (Verbose Mode)" \ + "3" "NO (Cancel Update)" --nocancel --default-item "1" 3>&1 1>&2 2>&3) + + case "$CHOICE" in + 1) + VERBOSE="no" + set_std_mode + ;; + 2) + VERBOSE="yes" + set_std_mode + ;; + 3) + clear + exit_script + exit + ;; + esac + update_script + fi +} + +# ------------------------------------------------------------------------------ +# build_container() +# +# - Creates and configures the LXC container +# - Builds network string and applies features (FUSE, TUN, VAAPI passthrough) +# - Starts container and waits for network connectivity +# - Installs base packages, SSH keys, and runs -install.sh +# ------------------------------------------------------------------------------ +build_container() { + # if [ "$VERBOSE" == "yes" ]; then set -x; fi + + NET_STRING="-net0 name=eth0,bridge=${BRG:-vmbr0}" + + # MAC + if [[ -n "$MAC" ]]; then + case "$MAC" in + ,hwaddr=*) NET_STRING+="$MAC" ;; + *) NET_STRING+=",hwaddr=$MAC" ;; + esac + fi + + # IP (immer zwingend, Standard dhcp) + NET_STRING+=",ip=${NET:-dhcp}" + + # Gateway + if [[ -n "$GATE" ]]; then + case "$GATE" in + ,gw=*) NET_STRING+="$GATE" ;; + *) NET_STRING+=",gw=$GATE" ;; + esac + fi + + # VLAN + if [[ -n "$VLAN" ]]; then + case "$VLAN" in + ,tag=*) NET_STRING+="$VLAN" ;; + *) NET_STRING+=",tag=$VLAN" ;; + esac + fi + + # MTU + if [[ -n "$MTU" ]]; then + case "$MTU" in + ,mtu=*) NET_STRING+="$MTU" ;; + *) NET_STRING+=",mtu=$MTU" ;; + esac + fi + + # IPv6 Handling + case "$IPV6_METHOD" in + auto) NET_STRING="$NET_STRING,ip6=auto" ;; + dhcp) NET_STRING="$NET_STRING,ip6=dhcp" ;; + static) + NET_STRING="$NET_STRING,ip6=$IPV6_ADDR" + [ -n "$IPV6_GATE" ] && NET_STRING="$NET_STRING,gw6=$IPV6_GATE" + ;; + none) ;; + esac + + if [ "$CT_TYPE" == "1" ]; then + FEATURES="keyctl=1,nesting=1" + else + FEATURES="nesting=1" + fi + + if [ "$ENABLE_FUSE" == "yes" ]; then + FEATURES="$FEATURES,fuse=1" + fi + + TEMP_DIR=$(mktemp -d) + pushd "$TEMP_DIR" >/dev/null + if [ "$var_os" == "alpine" ]; then + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-install.func)" + else + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" + fi + export DIAGNOSTICS="$DIAGNOSTICS" + export RANDOM_UUID="$RANDOM_UUID" + export CACHER="$APT_CACHER" + export CACHER_IP="$APT_CACHER_IP" + export tz="$timezone" + export APPLICATION="$APP" + export app="$NSAPP" + export PASSWORD="$PW" + export VERBOSE="$VERBOSE" + export SSH_ROOT="${SSH}" + export SSH_AUTHORIZED_KEY + export CTID="$CT_ID" + export CTTYPE="$CT_TYPE" + export ENABLE_FUSE="$ENABLE_FUSE" + export ENABLE_TUN="$ENABLE_TUN" + export PCT_OSTYPE="$var_os" + export PCT_OSVERSION="$var_version" + export PCT_DISK_SIZE="$DISK_SIZE" + export PCT_OPTIONS=" + -features $FEATURES + -hostname $HN + -tags $TAGS + $SD + $NS + $NET_STRING + -onboot 1 + -cores $CORE_COUNT + -memory $RAM_SIZE + -unprivileged $CT_TYPE + $PW +" + export TEMPLATE_STORAGE="${var_template_storage:-}" + export CONTAINER_STORAGE="${var_container_storage:-}" + create_lxc_container || exit $? + + LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" + + # ============================================================================ + # GPU/USB PASSTHROUGH CONFIGURATION + # ============================================================================ + + # List of applications that benefit from GPU acceleration + GPU_APPS=( + "immich" "channels" "emby" "ersatztv" "frigate" + "jellyfin" "plex" "scrypted" "tdarr" "unmanic" + "ollama" "fileflows" "open-webui" "tunarr" "debian" + "handbrake" "sunshine" "moonlight" "kodi" "stremio" + "viseron" + ) + + # Check if app needs GPU + is_gpu_app() { + local app="${1,,}" + for gpu_app in "${GPU_APPS[@]}"; do + [[ "$app" == "${gpu_app,,}" ]] && return 0 + done + return 1 + } + + # Detect all available GPU devices + detect_gpu_devices() { + INTEL_DEVICES=() + AMD_DEVICES=() + NVIDIA_DEVICES=() + + # Store PCI info to avoid multiple calls + local pci_vga_info=$(lspci -nn 2>/dev/null | grep -E "VGA|Display|3D") + + # Check for Intel GPU - look for Intel vendor ID [8086] + if echo "$pci_vga_info" | grep -q "\[8086:"; then + msg_info "Detected Intel GPU" + if [[ -d /dev/dri ]]; then + for d in /dev/dri/renderD* /dev/dri/card*; do + [[ -e "$d" ]] && INTEL_DEVICES+=("$d") + done + fi + fi + + # Check for AMD GPU - look for AMD vendor IDs [1002] (AMD/ATI) or [1022] (AMD) + if echo "$pci_vga_info" | grep -qE "\[1002:|\[1022:"; then + msg_info "Detected AMD GPU" + if [[ -d /dev/dri ]]; then + # Only add if not already claimed by Intel + if [[ ${#INTEL_DEVICES[@]} -eq 0 ]]; then + for d in /dev/dri/renderD* /dev/dri/card*; do + [[ -e "$d" ]] && AMD_DEVICES+=("$d") + done + fi + fi + fi + + # Check for NVIDIA GPU - look for NVIDIA vendor ID [10de] + if echo "$pci_vga_info" | grep -q "\[10de:"; then + msg_info "Detected NVIDIA GPU" + if ! check_nvidia_host_setup; then + msg_error "NVIDIA host setup incomplete. Skipping GPU passthrough." + msg_info "Fix NVIDIA drivers on host, then recreate container or passthrough manually." + return 0 + fi + + for d in /dev/nvidia* /dev/nvidiactl /dev/nvidia-modeset; do + [[ -e "$d" ]] && NVIDIA_DEVICES+=("$d") + done + + if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then + msg_warn "NVIDIA GPU detected but no /dev/nvidia* devices found" + msg_warn "Please install NVIDIA drivers on host: apt install nvidia-driver" + else + if [[ "$CT_TYPE" == "0" ]]; then + cat <>"$LXC_CONFIG" + # NVIDIA GPU Passthrough (privileged) + lxc.cgroup2.devices.allow: c 195:* rwm + lxc.cgroup2.devices.allow: c 243:* rwm + lxc.mount.entry: /dev/nvidia0 dev/nvidia0 none bind,optional,create=file + lxc.mount.entry: /dev/nvidiactl dev/nvidiactl none bind,optional,create=file + lxc.mount.entry: /dev/nvidia-uvm dev/nvidia-uvm none bind,optional,create=file + lxc.mount.entry: /dev/nvidia-uvm-tools dev/nvidia-uvm-tools none bind,optional,create=file +EOF + + if [[ -e /dev/dri/renderD128 ]]; then + echo "lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file" >>"$LXC_CONFIG" + fi + + export GPU_TYPE="NVIDIA" + export NVIDIA_DRIVER_VERSION=$(nvidia-smi --query-gpu=driver_version --format=csv,noheader 2>/dev/null | head -n1) + msg_ok "NVIDIA GPU passthrough configured (driver: ${NVIDIA_DRIVER_VERSION})" + else + msg_warn "NVIDIA passthrough only supported for privileged containers" + return 0 + fi + fi + fi + + # Debug output + msg_debug "Intel devices: ${INTEL_DEVICES[*]}" + msg_debug "AMD devices: ${AMD_DEVICES[*]}" + msg_debug "NVIDIA devices: ${NVIDIA_DEVICES[*]}" + } + + # Configure USB passthrough for privileged containers + configure_usb_passthrough() { + if [[ "$CT_TYPE" != "0" ]]; then + return 0 + fi + + msg_info "Configuring automatic USB passthrough (privileged container)" + cat <>"$LXC_CONFIG" +# Automatic USB passthrough (privileged container) +lxc.cgroup2.devices.allow: a +lxc.cap.drop: +lxc.cgroup2.devices.allow: c 188:* rwm +lxc.cgroup2.devices.allow: c 189:* rwm +lxc.mount.entry: /dev/serial/by-id dev/serial/by-id none bind,optional,create=dir +lxc.mount.entry: /dev/ttyUSB0 dev/ttyUSB0 none bind,optional,create=file +lxc.mount.entry: /dev/ttyUSB1 dev/ttyUSB1 none bind,optional,create=file +lxc.mount.entry: /dev/ttyACM0 dev/ttyACM0 none bind,optional,create=file +lxc.mount.entry: /dev/ttyACM1 dev/ttyACM1 none bind,optional,create=file +EOF + msg_ok "USB passthrough configured" + } + + # Configure GPU passthrough + configure_gpu_passthrough() { + # Skip if not a GPU app and not privileged + if [[ "$CT_TYPE" != "0" ]] && ! is_gpu_app "$APP"; then + return 0 + fi + + detect_gpu_devices + + # Count available GPU types + local gpu_count=0 + local available_gpus=() + + if [[ ${#INTEL_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("INTEL") + gpu_count=$((gpu_count + 1)) + fi + + if [[ ${#AMD_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("AMD") + gpu_count=$((gpu_count + 1)) + fi + + if [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("NVIDIA") + gpu_count=$((gpu_count + 1)) + fi + + if [[ $gpu_count -eq 0 ]]; then + msg_info "No GPU devices found for passthrough" + return 0 + fi + + local selected_gpu="" + + if [[ $gpu_count -eq 1 ]]; then + # Automatic selection for single GPU + selected_gpu="${available_gpus[0]}" + msg_info "Automatically configuring ${selected_gpu} GPU passthrough" + else + # Multiple GPUs - ask user + echo -e "\n${INFO} Multiple GPU types detected:" + for gpu in "${available_gpus[@]}"; do + echo " - $gpu" + done + read -rp "Which GPU type to passthrough? (${available_gpus[*]}): " selected_gpu + selected_gpu="${selected_gpu^^}" + + # Validate selection + local valid=0 + for gpu in "${available_gpus[@]}"; do + [[ "$selected_gpu" == "$gpu" ]] && valid=1 + done + + if [[ $valid -eq 0 ]]; then + msg_warn "Invalid selection. Skipping GPU passthrough." + return 0 + fi + fi + + # Apply passthrough configuration based on selection + local dev_idx=0 + + case "$selected_gpu" in + INTEL | AMD) + local devices=() + [[ "$selected_gpu" == "INTEL" ]] && devices=("${INTEL_DEVICES[@]}") + [[ "$selected_gpu" == "AMD" ]] && devices=("${AMD_DEVICES[@]}") + + # For Proxmox WebUI visibility, add as dev0, dev1 etc. + for dev in "${devices[@]}"; do + if [[ "$CT_TYPE" == "0" ]]; then + # Privileged container - use dev entries for WebUI visibility + # Use initial GID 104 (render) for renderD*, 44 (video) for card* + if [[ "$dev" =~ renderD ]]; then + echo "dev${dev_idx}: $dev,gid=104" >>"$LXC_CONFIG" + else + echo "dev${dev_idx}: $dev,gid=44" >>"$LXC_CONFIG" + fi + dev_idx=$((dev_idx + 1)) + + # Also add cgroup allows for privileged containers + local major minor + major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") + minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") + + if [[ "$major" != "0" && "$minor" != "0" ]]; then + echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" + fi + else + # Unprivileged container + if [[ "$dev" =~ renderD ]]; then + echo "dev${dev_idx}: $dev,uid=0,gid=104" >>"$LXC_CONFIG" + else + echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG" + fi + dev_idx=$((dev_idx + 1)) + fi + done + + export GPU_TYPE="$selected_gpu" + msg_ok "${selected_gpu} GPU passthrough configured (${dev_idx} devices)" + ;; + + NVIDIA) + if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then + msg_error "NVIDIA drivers not installed on host. Please install: apt install nvidia-driver" + return 1 + fi + + for dev in "${NVIDIA_DEVICES[@]}"; do + # NVIDIA devices typically need different handling + echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG" + dev_idx=$((dev_idx + 1)) + + if [[ "$CT_TYPE" == "0" ]]; then + local major minor + major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") + minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") + + if [[ "$major" != "0" && "$minor" != "0" ]]; then + echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" + fi + fi + done + + export GPU_TYPE="NVIDIA" + msg_ok "NVIDIA GPU passthrough configured (${dev_idx} devices)" + ;; + esac + } + + # Additional device passthrough + configure_additional_devices() { + # TUN device passthrough + if [ "$ENABLE_TUN" == "yes" ]; then + cat <>"$LXC_CONFIG" +lxc.cgroup2.devices.allow: c 10:200 rwm +lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file +EOF + fi + + # Coral TPU passthrough + if [[ -e /dev/apex_0 ]]; then + msg_info "Detected Coral TPU - configuring passthrough" + echo "lxc.mount.entry: /dev/apex_0 dev/apex_0 none bind,optional,create=file" >>"$LXC_CONFIG" + fi + } + + # Execute pre-start configurations + configure_usb_passthrough + configure_gpu_passthrough + configure_additional_devices + + # ============================================================================ + # START CONTAINER AND INSTALL USERLAND + # ============================================================================ + + msg_info "Starting LXC Container" + pct start "$CTID" + + # Wait for container to be running + for i in {1..10}; do + if pct status "$CTID" | grep -q "status: running"; then + msg_ok "Started LXC Container" + break + fi + sleep 1 + if [ "$i" -eq 10 ]; then + msg_error "LXC Container did not reach running state" + exit 1 + fi + done + + # Wait for network (skip for Alpine initially) + if [ "$var_os" != "alpine" ]; then + msg_info "Waiting for network in LXC container" + + # Wait for IP + for i in {1..20}; do + ip_in_lxc=$(pct exec "$CTID" -- ip -4 addr show dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) + [ -n "$ip_in_lxc" ] && break + sleep 1 + done + + if [ -z "$ip_in_lxc" ]; then + msg_error "No IP assigned to CT $CTID after 20s" + exit 1 + fi + + # Try to reach gateway + gw_ok=0 + for i in {1..10}; do + if pct exec "$CTID" -- ping -c1 -W1 "${GATEWAY:-8.8.8.8}" >/dev/null 2>&1; then + gw_ok=1 + break + fi + sleep 1 + done + + if [ "$gw_ok" -eq 1 ]; then + msg_ok "Network in LXC is reachable (IP $ip_in_lxc)" + else + msg_warn "Network reachable but gateway check failed" + fi + fi + # Function to get correct GID inside container + get_container_gid() { + local group="$1" + local gid=$(pct exec "$CTID" -- getent group "$group" 2>/dev/null | cut -d: -f3) + echo "${gid:-44}" # Default to 44 if not found + } + + fix_gpu_gids + + # Continue with standard container setup + msg_info "Customizing LXC Container" + + # # Install GPU userland if configured + # if [[ "${ENABLE_VAAPI:-0}" == "1" ]]; then + # install_gpu_userland "VAAPI" + # fi + + # if [[ "${ENABLE_NVIDIA:-0}" == "1" ]]; then + # install_gpu_userland "NVIDIA" + # fi + + # Continue with standard container setup + if [ "$var_os" == "alpine" ]; then + sleep 3 + pct exec "$CTID" -- /bin/sh -c 'cat </etc/apk/repositories +http://dl-cdn.alpinelinux.org/alpine/latest-stable/main +http://dl-cdn.alpinelinux.org/alpine/latest-stable/community +EOF' + pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses jq >/dev/null" + else + sleep 3 + pct exec "$CTID" -- bash -c "sed -i '/$LANG/ s/^# //' /etc/locale.gen" + pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \ + echo LANG=\$locale_line >/etc/default/locale && \ + locale-gen >/dev/null && \ + export LANG=\$locale_line" + + if [[ -z "${tz:-}" ]]; then + tz=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "Etc/UTC") + fi + + if pct exec "$CTID" -- test -e "/usr/share/zoneinfo/$tz"; then + pct exec "$CTID" -- bash -c "tz='$tz'; echo \"\$tz\" >/etc/timezone && ln -sf \"/usr/share/zoneinfo/\$tz\" /etc/localtime" + else + msg_warn "Skipping timezone setup – zone '$tz' not found in container" + fi + + pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y sudo curl mc gnupg2 jq >/dev/null" || { + msg_error "apt-get base packages installation failed" + exit 1 + } + fi + + msg_ok "Customized LXC Container" + + # Verify GPU access if enabled + if [[ "${ENABLE_VAAPI:-0}" == "1" ]] && [ "$var_os" != "alpine" ]; then + pct exec "$CTID" -- bash -c "vainfo >/dev/null 2>&1" && + msg_ok "VAAPI verified working" || + msg_warn "VAAPI verification failed - may need additional configuration" + fi + + if [[ "${ENABLE_NVIDIA:-0}" == "1" ]] && [ "$var_os" != "alpine" ]; then + pct exec "$CTID" -- bash -c "nvidia-smi >/dev/null 2>&1" && + msg_ok "NVIDIA verified working" || + msg_warn "NVIDIA verification failed - may need additional configuration" + fi + + # Install SSH keys + install_ssh_keys_into_ct + + # Run application installer + if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)"; then + exit $? + fi +} + +destroy_lxc() { + if [[ -z "$CT_ID" ]]; then + msg_error "No CT_ID found. Nothing to remove." + return 1 + fi + + # Abbruch bei Ctrl-C / Ctrl-D / ESC + trap 'echo; msg_error "Aborted by user (SIGINT/SIGQUIT)"; return 130' INT QUIT + + local prompt + if ! read -rp "Remove this Container? " prompt; then + # read gibt != 0 zurück bei Ctrl-D/ESC + msg_error "Aborted input (Ctrl-D/ESC)" + return 130 + fi + + case "${prompt,,}" in + y | yes) + if pct stop "$CT_ID" &>/dev/null && pct destroy "$CT_ID" &>/dev/null; then + msg_ok "Removed Container $CT_ID" + else + msg_error "Failed to remove Container $CT_ID" + return 1 + fi + ;; + "" | n | no) + msg_info "Container was not removed." + ;; + *) + msg_warn "Invalid response. Container was not removed." + ;; + esac +} + +# ------------------------------------------------------------------------------ +# Storage discovery / selection helpers +# ------------------------------------------------------------------------------ +# ===== Storage discovery / selection helpers (ported from create_lxc.sh) ===== +resolve_storage_preselect() { + local class="$1" preselect="$2" required_content="" + case "$class" in + template) required_content="vztmpl" ;; + container) required_content="rootdir" ;; + *) return 1 ;; + esac + [[ -z "$preselect" ]] && return 1 + if ! pvesm status -content "$required_content" | awk 'NR>1{print $1}' | grep -qx -- "$preselect"; then + msg_warn "Preselected storage '${preselect}' does not support content '${required_content}' (or not found)" + return 1 + fi + + local line total used free + line="$(pvesm status | awk -v s="$preselect" 'NR>1 && $1==s {print $0}')" + if [[ -z "$line" ]]; then + STORAGE_INFO="n/a" + else + total="$(awk '{print $4}' <<<"$line")" + used="$(awk '{print $5}' <<<"$line")" + free="$(awk '{print $6}' <<<"$line")" + local total_h used_h free_h + if command -v numfmt >/dev/null 2>&1; then + total_h="$(numfmt --to=iec --suffix=B --format %.1f "$total" 2>/dev/null || echo "$total")" + used_h="$(numfmt --to=iec --suffix=B --format %.1f "$used" 2>/dev/null || echo "$used")" + free_h="$(numfmt --to=iec --suffix=B --format %.1f "$free" 2>/dev/null || echo "$free")" + STORAGE_INFO="Free: ${free_h} Used: ${used_h}" + else + STORAGE_INFO="Free: ${free} Used: ${used}" + fi + fi + STORAGE_RESULT="$preselect" + return 0 +} + +fix_gpu_gids() { + if [[ -z "${GPU_TYPE:-}" ]]; then + return 0 + fi + + msg_info "Detecting and setting correct GPU group IDs" + + # Ermittle die tatsächlichen GIDs aus dem Container + local video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") + local render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") + + # Fallbacks wenn Gruppen nicht existieren + if [[ -z "$video_gid" ]]; then + # Versuche die video Gruppe zu erstellen + pct exec "$CTID" -- sh -c "groupadd -r video 2>/dev/null || true" + video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") + [[ -z "$video_gid" ]] && video_gid="44" # Ultimate fallback + fi + + if [[ -z "$render_gid" ]]; then + # Versuche die render Gruppe zu erstellen + pct exec "$CTID" -- sh -c "groupadd -r render 2>/dev/null || true" + render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") + [[ -z "$render_gid" ]] && render_gid="104" # Ultimate fallback + fi + + msg_info "Container GIDs detected - video:${video_gid}, render:${render_gid}" + + # Prüfe ob die GIDs von den Defaults abweichen + local need_update=0 + if [[ "$video_gid" != "44" ]] || [[ "$render_gid" != "104" ]]; then + need_update=1 + fi + + if [[ $need_update -eq 1 ]]; then + msg_info "Updating device GIDs in container config" + + # Stoppe Container für Config-Update + pct stop "$CTID" >/dev/null 2>&1 + + # Update die dev Einträge mit korrekten GIDs + # Backup der Config + cp "$LXC_CONFIG" "${LXC_CONFIG}.bak" + + # Parse und update jeden dev Eintrag + while IFS= read -r line; do + if [[ "$line" =~ ^dev[0-9]+: ]]; then + # Extract device path + local device_path=$(echo "$line" | sed -E 's/^dev[0-9]+: ([^,]+).*/\1/') + local dev_num=$(echo "$line" | sed -E 's/^(dev[0-9]+):.*/\1/') + + if [[ "$device_path" =~ renderD ]]; then + # RenderD device - use render GID + echo "${dev_num}: ${device_path},gid=${render_gid}" + elif [[ "$device_path" =~ card ]]; then + # Card device - use video GID + echo "${dev_num}: ${device_path},gid=${video_gid}" + else + # Keep original line + echo "$line" + fi + else + # Keep non-dev lines + echo "$line" + fi + done <"$LXC_CONFIG" >"${LXC_CONFIG}.new" + + mv "${LXC_CONFIG}.new" "$LXC_CONFIG" + + # Starte Container wieder + pct start "$CTID" >/dev/null 2>&1 + sleep 3 + + msg_ok "Device GIDs updated successfully" + else + msg_ok "Device GIDs are already correct" + fi + if [[ "$CT_TYPE" == "0" ]]; then + pct exec "$CTID" -- bash -c " + if [ -d /dev/dri ]; then + for dev in /dev/dri/*; do + if [ -e \"\$dev\" ]; then + if [[ \"\$dev\" =~ renderD ]]; then + chgrp ${render_gid} \"\$dev\" 2>/dev/null || true + else + chgrp ${video_gid} \"\$dev\" 2>/dev/null || true + fi + chmod 660 \"\$dev\" 2>/dev/null || true + fi + done + fi + " >/dev/null 2>&1 + fi +} + +# NVIDIA-spezific check on host +check_nvidia_host_setup() { + if ! command -v nvidia-smi >/dev/null 2>&1; then + msg_warn "NVIDIA GPU detected but nvidia-smi not found on host" + msg_warn "Please install NVIDIA drivers on host first." + #echo " 1. Download driver: wget https://us.download.nvidia.com/XFree86/Linux-x86_64/550.127.05/NVIDIA-Linux-x86_64-550.127.05.run" + #echo " 2. Install: ./NVIDIA-Linux-x86_64-550.127.05.run --dkms" + #echo " 3. Verify: nvidia-smi" + return 1 + fi + + # check if nvidia-smi works + if ! nvidia-smi >/dev/null 2>&1; then + msg_warn "nvidia-smi installed but not working. Driver issue?" + return 1 + fi + + return 0 +} + +check_storage_support() { + local CONTENT="$1" VALID=0 + while IFS= read -r line; do + local STORAGE_NAME + STORAGE_NAME=$(awk '{print $1}' <<<"$line") + [[ -n "$STORAGE_NAME" ]] && VALID=1 + done < <(pvesm status -content "$CONTENT" 2>/dev/null | awk 'NR>1') + [[ $VALID -eq 1 ]] +} + +select_storage() { + local CLASS=$1 CONTENT CONTENT_LABEL + case $CLASS in + container) + CONTENT='rootdir' + CONTENT_LABEL='Container' + ;; + template) + CONTENT='vztmpl' + CONTENT_LABEL='Container template' + ;; + iso) + CONTENT='iso' + CONTENT_LABEL='ISO image' + ;; + images) + CONTENT='images' + CONTENT_LABEL='VM Disk image' + ;; + backup) + CONTENT='backup' + CONTENT_LABEL='Backup' + ;; + snippets) + CONTENT='snippets' + CONTENT_LABEL='Snippets' + ;; + *) + msg_error "Invalid storage class '$CLASS'" + return 1 + ;; + esac + + declare -A STORAGE_MAP + local -a MENU=() + local COL_WIDTH=0 + + while read -r TAG TYPE _ TOTAL USED FREE _; do + [[ -n "$TAG" && -n "$TYPE" ]] || continue + local DISPLAY="${TAG} (${TYPE})" + local USED_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$USED") + local FREE_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$FREE") + local INFO="Free: ${FREE_FMT}B Used: ${USED_FMT}B" + STORAGE_MAP["$DISPLAY"]="$TAG" + MENU+=("$DISPLAY" "$INFO" "OFF") + ((${#DISPLAY} > COL_WIDTH)) && COL_WIDTH=${#DISPLAY} + done < <(pvesm status -content "$CONTENT" | awk 'NR>1') + + if [[ ${#MENU[@]} -eq 0 ]]; then + msg_error "No storage found for content type '$CONTENT'." + return 2 + fi + + if [[ $((${#MENU[@]} / 3)) -eq 1 ]]; then + STORAGE_RESULT="${STORAGE_MAP[${MENU[0]}]}" + STORAGE_INFO="${MENU[1]}" + return 0 + fi + + local WIDTH=$((COL_WIDTH + 42)) + while true; do + local DISPLAY_SELECTED + DISPLAY_SELECTED=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "Storage Pools" \ + --radiolist "Which storage pool for ${CONTENT_LABEL,,}?\n(Spacebar to select)" \ + 16 "$WIDTH" 6 "${MENU[@]}" 3>&1 1>&2 2>&3) || { exit_script; } + + DISPLAY_SELECTED=$(sed 's/[[:space:]]*$//' <<<"$DISPLAY_SELECTED") + if [[ -z "$DISPLAY_SELECTED" || -z "${STORAGE_MAP[$DISPLAY_SELECTED]+_}" ]]; then + whiptail --msgbox "No valid storage selected. Please try again." 8 58 + continue + fi + STORAGE_RESULT="${STORAGE_MAP[$DISPLAY_SELECTED]}" + for ((i = 0; i < ${#MENU[@]}; i += 3)); do + if [[ "${MENU[$i]}" == "$DISPLAY_SELECTED" ]]; then + STORAGE_INFO="${MENU[$i + 1]}" + break + fi + done + return 0 + done +} + +create_lxc_container() { + # ------------------------------------------------------------------------------ + # Optional verbose mode (debug tracing) + # ------------------------------------------------------------------------------ + if [[ "${CREATE_LXC_VERBOSE:-no}" == "yes" ]]; then set -x; fi + + # ------------------------------------------------------------------------------ + # Helpers (dynamic versioning / template parsing) + # ------------------------------------------------------------------------------ + pkg_ver() { dpkg-query -W -f='${Version}\n' "$1" 2>/dev/null || echo ""; } + pkg_cand() { apt-cache policy "$1" 2>/dev/null | awk '/Candidate:/ {print $2}'; } + + ver_ge() { dpkg --compare-versions "$1" ge "$2"; } + ver_gt() { dpkg --compare-versions "$1" gt "$2"; } + ver_lt() { dpkg --compare-versions "$1" lt "$2"; } + + # Extract Debian OS minor from template name: debian-13-standard_13.1-1_amd64.tar.zst => "13.1" + parse_template_osver() { sed -n 's/.*_\([0-9][0-9]*\(\.[0-9]\+\)\?\)-.*/\1/p' <<<"$1"; } + + # Offer upgrade for pve-container/lxc-pve if candidate > installed; optional auto-retry pct create + # Returns: + # 0 = no upgrade needed + # 1 = upgraded (and if do_retry=yes and retry succeeded, creation done) + # 2 = user declined + # 3 = upgrade attempted but failed OR retry failed + offer_lxc_stack_upgrade_and_maybe_retry() { + local do_retry="${1:-no}" # yes|no + local _pvec_i _pvec_c _lxcp_i _lxcp_c need=0 + + _pvec_i="$(pkg_ver pve-container)" + _lxcp_i="$(pkg_ver lxc-pve)" + _pvec_c="$(pkg_cand pve-container)" + _lxcp_c="$(pkg_cand lxc-pve)" + + if [[ -n "$_pvec_c" && "$_pvec_c" != "none" ]]; then + ver_gt "$_pvec_c" "${_pvec_i:-0}" && need=1 + fi + if [[ -n "$_lxcp_c" && "$_lxcp_c" != "none" ]]; then + ver_gt "$_lxcp_c" "${_lxcp_i:-0}" && need=1 + fi + if [[ $need -eq 0 ]]; then + msg_debug "No newer candidate for pve-container/lxc-pve (installed=$_pvec_i/$_lxcp_i, cand=$_pvec_c/$_lxcp_c)" + return 0 + fi + + echo + echo "An update for the Proxmox LXC stack is available:" + echo " pve-container: installed=${_pvec_i:-n/a} candidate=${_pvec_c:-n/a}" + echo " lxc-pve : installed=${_lxcp_i:-n/a} candidate=${_lxcp_c:-n/a}" + echo + read -rp "Do you want to upgrade now? [y/N] " _ans + case "${_ans,,}" in + y | yes) + msg_info "Upgrading Proxmox LXC stack (pve-container, lxc-pve)" + if apt-get update -qq >/dev/null && apt-get install -y --only-upgrade pve-container lxc-pve >/dev/null; then + msg_ok "LXC stack upgraded." + if [[ "$do_retry" == "yes" ]]; then + msg_info "Retrying container creation after upgrade" + if pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then + msg_ok "Container created successfully after upgrade." + return 0 + else + msg_error "pct create still failed after upgrade. See $LOGFILE" + return 3 + fi + fi + return 1 + else + msg_error "Upgrade failed. Please check APT output." + return 3 + fi + ;; + *) return 2 ;; + esac + } + + # ------------------------------------------------------------------------------ + # Required input variables + # ------------------------------------------------------------------------------ + [[ "${CTID:-}" ]] || { + msg_error "You need to set 'CTID' variable." + exit 203 + } + [[ "${PCT_OSTYPE:-}" ]] || { + msg_error "You need to set 'PCT_OSTYPE' variable." + exit 204 + } + + msg_debug "CTID=$CTID" + msg_debug "PCT_OSTYPE=$PCT_OSTYPE" + msg_debug "PCT_OSVERSION=${PCT_OSVERSION:-default}" + + # ID checks + [[ "$CTID" -ge 100 ]] || { + msg_error "ID cannot be less than 100." + exit 205 + } + if qm status "$CTID" &>/dev/null || pct status "$CTID" &>/dev/null; then + echo -e "ID '$CTID' is already in use." + unset CTID + msg_error "Cannot use ID that is already in use." + exit 206 + fi + + # Storage capability check + check_storage_support "rootdir" || { + msg_error "No valid storage found for 'rootdir' [Container]" + exit 1 + } + check_storage_support "vztmpl" || { + msg_error "No valid storage found for 'vztmpl' [Template]" + exit 1 + } + + # Template storage selection + if resolve_storage_preselect template "${TEMPLATE_STORAGE:-}"; then + TEMPLATE_STORAGE="$STORAGE_RESULT" + TEMPLATE_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${TEMPLATE_STORAGE}${CL} (${TEMPLATE_STORAGE_INFO}) [Template]" + else + while true; do + if [[ -z "${var_template_storage:-}" ]]; then + if select_storage template; then + TEMPLATE_STORAGE="$STORAGE_RESULT" + TEMPLATE_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${TEMPLATE_STORAGE}${CL} (${TEMPLATE_STORAGE_INFO}) [Template]" + break + fi + fi + done + fi + + # Container storage selection + if resolve_storage_preselect container "${CONTAINER_STORAGE:-}"; then + CONTAINER_STORAGE="$STORAGE_RESULT" + CONTAINER_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${CONTAINER_STORAGE}${CL} (${CONTAINER_STORAGE_INFO}) [Container]" + else + if [[ -z "${var_container_storage:-}" ]]; then + if select_storage container; then + CONTAINER_STORAGE="$STORAGE_RESULT" + CONTAINER_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${CONTAINER_STORAGE}${CL} (${CONTAINER_STORAGE_INFO}) [Container]" + fi + fi + fi + + # Validate content types + msg_info "Validating content types of storage '$CONTAINER_STORAGE'" + STORAGE_CONTENT=$(grep -A4 -E "^(zfspool|dir|lvmthin|lvm): $CONTAINER_STORAGE" /etc/pve/storage.cfg | grep content | awk '{$1=""; print $0}' | xargs) + msg_debug "Storage '$CONTAINER_STORAGE' has content types: $STORAGE_CONTENT" + grep -qw "rootdir" <<<"$STORAGE_CONTENT" || { + msg_error "Storage '$CONTAINER_STORAGE' does not support 'rootdir'. Cannot create LXC." + exit 217 + } + $STD msg_ok "Storage '$CONTAINER_STORAGE' supports 'rootdir'" + + msg_info "Validating content types of template storage '$TEMPLATE_STORAGE'" + TEMPLATE_CONTENT=$(grep -A4 -E "^[^:]+: $TEMPLATE_STORAGE" /etc/pve/storage.cfg | grep content | awk '{$1=""; print $0}' | xargs) + msg_debug "Template storage '$TEMPLATE_STORAGE' has content types: $TEMPLATE_CONTENT" + if ! grep -qw "vztmpl" <<<"$TEMPLATE_CONTENT"; then + msg_warn "Template storage '$TEMPLATE_STORAGE' does not declare 'vztmpl'. This may cause pct create to fail." + else + $STD msg_ok "Template storage '$TEMPLATE_STORAGE' supports 'vztmpl'" + fi + + # Free space check + STORAGE_FREE=$(pvesm status | awk -v s="$CONTAINER_STORAGE" '$1 == s { print $6 }') + REQUIRED_KB=$((${PCT_DISK_SIZE:-8} * 1024 * 1024)) + [[ "$STORAGE_FREE" -ge "$REQUIRED_KB" ]] || { + msg_error "Not enough space on '$CONTAINER_STORAGE'. Needed: ${PCT_DISK_SIZE:-8}G." + exit 214 + } + + # Cluster quorum (if cluster) + if [[ -f /etc/pve/corosync.conf ]]; then + msg_info "Checking cluster quorum" + if ! pvecm status | awk -F':' '/^Quorate/ { exit ($2 ~ /Yes/) ? 0 : 1 }'; then + msg_error "Cluster is not quorate. Start all nodes or configure quorum device (QDevice)." + exit 210 + fi + msg_ok "Cluster is quorate" + fi + + # ------------------------------------------------------------------------------ + # Template discovery & validation + # ------------------------------------------------------------------------------ + TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" + case "$PCT_OSTYPE" in + debian | ubuntu) TEMPLATE_PATTERN="-standard_" ;; + alpine | fedora | rocky | centos) TEMPLATE_PATTERN="-default_" ;; + *) TEMPLATE_PATTERN="" ;; + esac + + msg_info "Searching for template '$TEMPLATE_SEARCH'" + + # Build regex patterns outside awk/grep for clarity + SEARCH_PATTERN="^${TEMPLATE_SEARCH}" + + #echo "[DEBUG] TEMPLATE_SEARCH='$TEMPLATE_SEARCH'" + #echo "[DEBUG] SEARCH_PATTERN='$SEARCH_PATTERN'" + #echo "[DEBUG] TEMPLATE_PATTERN='$TEMPLATE_PATTERN'" + + mapfile -t LOCAL_TEMPLATES < <( + pveam list "$TEMPLATE_STORAGE" 2>/dev/null | + awk -v search="${SEARCH_PATTERN}" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' | + sed 's|.*/||' | sort -t - -k 2 -V + ) + + pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)." + + #echo "[DEBUG] pveam available output (first 5 lines with .tar files):" + #pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | head -5 | sed 's/^/ /' + + set +u + mapfile -t ONLINE_TEMPLATES < <(pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | awk '{print $2}' | grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V 2>/dev/null || true) + #echo "[DEBUG] After filtering: ${#ONLINE_TEMPLATES[@]} online templates found" + set -u + if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then + #echo "[DEBUG] Online templates:" + for tmpl in "${ONLINE_TEMPLATES[@]}"; do + echo " - $tmpl" + done + fi + + ONLINE_TEMPLATE="" + [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" + + #msg_debug "SEARCH_PATTERN='${SEARCH_PATTERN}' TEMPLATE_PATTERN='${TEMPLATE_PATTERN}'" + #msg_debug "Found ${#LOCAL_TEMPLATES[@]} local templates, ${#ONLINE_TEMPLATES[@]} online templates" + if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then + #msg_debug "First 3 online templates:" + count=0 + for idx in "${!ONLINE_TEMPLATES[@]}"; do + #msg_debug " [$idx]: ${ONLINE_TEMPLATES[$idx]}" + ((count++)) + [[ $count -ge 3 ]] && break + done + fi + #msg_debug "ONLINE_TEMPLATE='$ONLINE_TEMPLATE'" + + if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then + TEMPLATE="${LOCAL_TEMPLATES[-1]}" + TEMPLATE_SOURCE="local" + else + TEMPLATE="$ONLINE_TEMPLATE" + TEMPLATE_SOURCE="online" + fi + + # If still no template, try to find alternatives + if [[ -z "$TEMPLATE" ]]; then + echo "" + echo "[DEBUG] No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}, searching for alternatives..." + + # Get all available versions for this OS type + mapfile -t AVAILABLE_VERSIONS < <( + pveam available -section system 2>/dev/null | + grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | + awk -F'\t' '{print $1}' | + grep "^${PCT_OSTYPE}-" | + sed -E "s/.*${PCT_OSTYPE}-([0-9]+(\.[0-9]+)?).*/\1/" | + sort -u -V 2>/dev/null + ) + + if [[ ${#AVAILABLE_VERSIONS[@]} -gt 0 ]]; then + echo "" + echo "${BL}Available ${PCT_OSTYPE} versions:${CL}" + for i in "${!AVAILABLE_VERSIONS[@]}"; do + echo " [$((i + 1))] ${AVAILABLE_VERSIONS[$i]}" + done + echo "" + read -p "Select version [1-${#AVAILABLE_VERSIONS[@]}] or press Enter to cancel: " choice + + if [[ "$choice" =~ ^[0-9]+$ ]] && [[ "$choice" -ge 1 ]] && [[ "$choice" -le ${#AVAILABLE_VERSIONS[@]} ]]; then + PCT_OSVERSION="${AVAILABLE_VERSIONS[$((choice - 1))]}" + TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION}" + SEARCH_PATTERN="^${TEMPLATE_SEARCH}-" + + #echo "[DEBUG] Retrying with version: $PCT_OSVERSION" + + mapfile -t ONLINE_TEMPLATES < <( + pveam available -section system 2>/dev/null | + grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | + awk -F'\t' '{print $1}' | + grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | + sort -t - -k 2 -V 2>/dev/null || true + ) + + if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then + TEMPLATE="${ONLINE_TEMPLATES[-1]}" + TEMPLATE_SOURCE="online" + #echo "[DEBUG] Found alternative: $TEMPLATE" + else + msg_error "No templates available for ${PCT_OSTYPE} ${PCT_OSVERSION}" + exit 225 + fi + else + msg_info "Installation cancelled" + exit 0 + fi + else + msg_error "No ${PCT_OSTYPE} templates available at all" + exit 225 + fi + fi + + #echo "[DEBUG] Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'" + #msg_debug "Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'" + + TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" + if [[ -z "$TEMPLATE_PATH" ]]; then + TEMPLATE_BASE=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) + [[ -n "$TEMPLATE_BASE" ]] && TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE" + fi + + # If we still don't have a path but have a valid template name, construct it + if [[ -z "$TEMPLATE_PATH" && -n "$TEMPLATE" ]]; then + TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" + fi + + [[ -n "$TEMPLATE_PATH" ]] || { + if [[ -z "$TEMPLATE" ]]; then + msg_error "Template ${PCT_OSTYPE} ${PCT_OSVERSION} not available" + + # Get available versions + mapfile -t AVAILABLE_VERSIONS < <( + pveam available -section system 2>/dev/null | + grep "^${PCT_OSTYPE}-" | + sed -E 's/.*'"${PCT_OSTYPE}"'-([0-9]+\.[0-9]+).*/\1/' | + grep -E '^[0-9]+\.[0-9]+$' | + sort -u -V 2>/dev/null || sort -u + ) + + if [[ ${#AVAILABLE_VERSIONS[@]} -gt 0 ]]; then + echo -e "\n${BL}Available versions:${CL}" + for i in "${!AVAILABLE_VERSIONS[@]}"; do + echo " [$((i + 1))] ${AVAILABLE_VERSIONS[$i]}" + done + + echo "" + read -p "Select version [1-${#AVAILABLE_VERSIONS[@]}] or Enter to exit: " choice + + if [[ "$choice" =~ ^[0-9]+$ ]] && [[ "$choice" -ge 1 ]] && [[ "$choice" -le ${#AVAILABLE_VERSIONS[@]} ]]; then + export var_version="${AVAILABLE_VERSIONS[$((choice - 1))]}" + export PCT_OSVERSION="$var_version" + msg_ok "Switched to ${PCT_OSTYPE} ${var_version}" + + # Retry template search with new version + TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" + SEARCH_PATTERN="^${TEMPLATE_SEARCH}-" + + mapfile -t LOCAL_TEMPLATES < <( + pveam list "$TEMPLATE_STORAGE" 2>/dev/null | + awk -v search="${SEARCH_PATTERN}" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' | + sed 's|.*/||' | sort -t - -k 2 -V + ) + mapfile -t ONLINE_TEMPLATES < <( + pveam available -section system 2>/dev/null | + grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | + awk -F'\t' '{print $1}' | + grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | + sort -t - -k 2 -V 2>/dev/null || true + ) + ONLINE_TEMPLATE="" + [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" + + if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then + TEMPLATE="${LOCAL_TEMPLATES[-1]}" + TEMPLATE_SOURCE="local" + else + TEMPLATE="$ONLINE_TEMPLATE" + TEMPLATE_SOURCE="online" + fi + + TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" + if [[ -z "$TEMPLATE_PATH" ]]; then + TEMPLATE_BASE=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) + [[ -n "$TEMPLATE_BASE" ]] && TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE" + fi + + # If we still don't have a path but have a valid template name, construct it + if [[ -z "$TEMPLATE_PATH" && -n "$TEMPLATE" ]]; then + TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" + fi + + [[ -n "$TEMPLATE_PATH" ]] || { + msg_error "Template still not found after version change" + exit 220 + } + else + msg_info "Installation cancelled" + exit 1 + fi + else + msg_error "No ${PCT_OSTYPE} templates available" + exit 220 + fi + fi + } + + # Validate that we found a template + if [[ -z "$TEMPLATE" ]]; then + msg_error "No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}" + msg_info "Please check:" + msg_info " - Is pveam catalog available? (run: pveam available -section system)" + msg_info " - Does the template exist for your OS version?" + exit 225 + fi + + msg_ok "Template ${BL}$TEMPLATE${CL} [$TEMPLATE_SOURCE]" + msg_debug "Resolved TEMPLATE_PATH=$TEMPLATE_PATH" + + NEED_DOWNLOAD=0 + if [[ ! -f "$TEMPLATE_PATH" ]]; then + msg_info "Template not present locally – will download." + NEED_DOWNLOAD=1 + elif [[ ! -r "$TEMPLATE_PATH" ]]; then + msg_error "Template file exists but is not readable – check permissions." + exit 221 + elif [[ "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then + if [[ -n "$ONLINE_TEMPLATE" ]]; then + msg_warn "Template file too small (<1MB) – re-downloading." + NEED_DOWNLOAD=1 + else + msg_warn "Template looks too small, but no online version exists. Keeping local file." + fi + elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then + if [[ -n "$ONLINE_TEMPLATE" ]]; then + msg_warn "Template appears corrupted – re-downloading." + NEED_DOWNLOAD=1 + else + msg_warn "Template appears corrupted, but no online version exists. Keeping local file." + fi + else + $STD msg_ok "Template $TEMPLATE is present and valid." + fi + + if [[ "$TEMPLATE_SOURCE" == "local" && -n "$ONLINE_TEMPLATE" && "$TEMPLATE" != "$ONLINE_TEMPLATE" ]]; then + msg_warn "Local template is outdated: $TEMPLATE (latest available: $ONLINE_TEMPLATE)" + if whiptail --yesno "A newer template is available:\n$ONLINE_TEMPLATE\n\nDo you want to download and use it instead?" 12 70; then + TEMPLATE="$ONLINE_TEMPLATE" + NEED_DOWNLOAD=1 + else + msg_info "Continuing with local template $TEMPLATE" + fi + fi + + if [[ "$NEED_DOWNLOAD" -eq 1 ]]; then + [[ -f "$TEMPLATE_PATH" ]] && rm -f "$TEMPLATE_PATH" + for attempt in {1..3}; do + msg_info "Attempt $attempt: Downloading template $TEMPLATE to $TEMPLATE_STORAGE" + if pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null 2>&1; then + msg_ok "Template download successful." + break + fi + if [[ $attempt -eq 3 ]]; then + msg_error "Failed after 3 attempts. Please check network access, permissions, or manually run:\n pveam download $TEMPLATE_STORAGE $TEMPLATE" + exit 222 + fi + sleep $((attempt * 5)) + done + fi + + if ! pveam list "$TEMPLATE_STORAGE" 2>/dev/null | grep -q "$TEMPLATE"; then + msg_error "Template $TEMPLATE not available in storage $TEMPLATE_STORAGE after download." + exit 223 + fi + + # ------------------------------------------------------------------------------ + # Dynamic preflight for Debian 13.x: offer upgrade if available (no hard mins) + # ------------------------------------------------------------------------------ + if [[ "$PCT_OSTYPE" == "debian" ]]; then + OSVER="$(parse_template_osver "$TEMPLATE")" + if [[ -n "$OSVER" ]]; then + # Proactive, aber ohne Abbruch – nur Angebot + offer_lxc_stack_upgrade_and_maybe_retry "no" || true + fi + fi + + # ------------------------------------------------------------------------------ + # Create LXC Container + # ------------------------------------------------------------------------------ + msg_info "Creating LXC container" + + # Ensure subuid/subgid entries exist + grep -q "root:100000:65536" /etc/subuid || echo "root:100000:65536" >>/etc/subuid + grep -q "root:100000:65536" /etc/subgid || echo "root:100000:65536" >>/etc/subgid + + # Assemble pct options + PCT_OPTIONS=(${PCT_OPTIONS[@]:-${DEFAULT_PCT_OPTIONS[@]}}) + [[ " ${PCT_OPTIONS[*]} " =~ " -rootfs " ]] || PCT_OPTIONS+=(-rootfs "$CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}") + + # Lock by template file (avoid concurrent downloads/creates) + lockfile="/tmp/template.${TEMPLATE}.lock" + exec 9>"$lockfile" || { + msg_error "Failed to create lock file '$lockfile'." + exit 200 + } + flock -w 60 9 || { + msg_error "Timeout while waiting for template lock." + exit 211 + } + + LOGFILE="/tmp/pct_create_${CTID}.log" + msg_debug "pct create command: pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" + msg_debug "Logfile: $LOGFILE" + + # First attempt + if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >"$LOGFILE" 2>&1; then + msg_error "Container creation failed on ${TEMPLATE_STORAGE}. Checking template..." + + # Validate template file + if [[ ! -s "$TEMPLATE_PATH" || "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then + msg_warn "Template file too small or missing – re-downloading." + rm -f "$TEMPLATE_PATH" + pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" + elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then + if [[ -n "$ONLINE_TEMPLATE" ]]; then + msg_warn "Template appears corrupted – re-downloading." + rm -f "$TEMPLATE_PATH" + pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" + else + msg_warn "Template appears corrupted, but no online version exists. Skipping re-download." + fi + fi + + # Retry after repair + if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then + # Fallback to local storage + if [[ "$TEMPLATE_STORAGE" != "local" ]]; then + msg_warn "Retrying container creation with fallback to local storage..." + LOCAL_TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" + if [[ ! -f "$LOCAL_TEMPLATE_PATH" ]]; then + msg_info "Downloading template to local..." + pveam download local "$TEMPLATE" >/dev/null 2>&1 + fi + if pct create "$CTID" "local:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then + msg_ok "Container successfully created using local fallback." + else + # --- Dynamic stack upgrade + auto-retry on the well-known error pattern --- + if grep -qiE 'unsupported .* version' "$LOGFILE"; then + echo + echo "pct reported 'unsupported ... version' – your LXC stack might be too old for this template." + echo "We can try to upgrade 'pve-container' and 'lxc-pve' now and retry automatically." + offer_lxc_stack_upgrade_and_maybe_retry "yes" + rc=$? + case $rc in + 0) : ;; # success - container created, continue + 2) + echo "Upgrade was declined. Please update and re-run: + apt update && apt install --only-upgrade pve-container lxc-pve" + exit 231 + ;; + 3) + echo "Upgrade and/or retry failed. Please inspect: $LOGFILE" + exit 231 + ;; + esac + else + msg_error "Container creation failed even with local fallback. See $LOGFILE" + if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then + set -x + bash -x -c "pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" 2>&1 | tee -a "$LOGFILE" + set +x + fi + exit 209 + fi + fi + else + msg_error "Container creation failed on local storage. See $LOGFILE" + # --- Dynamic stack upgrade + auto-retry on the well-known error pattern --- + if grep -qiE 'unsupported .* version' "$LOGFILE"; then + echo + echo "pct reported 'unsupported ... version' – your LXC stack might be too old for this template." + echo "We can try to upgrade 'pve-container' and 'lxc-pve' now and retry automatically." + offer_lxc_stack_upgrade_and_maybe_retry "yes" + rc=$? + case $rc in + 0) : ;; # success - container created, continue + 2) + echo "Upgrade was declined. Please update and re-run: + apt update && apt install --only-upgrade pve-container lxc-pve" + exit 231 + ;; + 3) + echo "Upgrade and/or retry failed. Please inspect: $LOGFILE" + exit 231 + ;; + esac + else + msg_error "Container creation failed. See $LOGFILE" + if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then + set -x + bash -x -c "pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" 2>&1 | tee -a "$LOGFILE" + set +x + fi + exit 209 + fi + fi + fi + fi + + # Verify container exists + pct list | awk '{print $1}' | grep -qx "$CTID" || { + msg_error "Container ID $CTID not listed in 'pct list'. See $LOGFILE" + exit 215 + } + + # Verify config rootfs + grep -q '^rootfs:' "/etc/pve/lxc/$CTID.conf" || { + msg_error "RootFS entry missing in container config. See $LOGFILE" + exit 216 + } + + msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created." +} + +# ------------------------------------------------------------------------------ +# description() +# +# - Sets container description with HTML content (logo, links, badges) +# - Restarts ping-instances.service if present +# - Posts status "done" to API +# ------------------------------------------------------------------------------ +description() { + IP=$(pct exec "$CTID" ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) + + # Generate LXC Description + DESCRIPTION=$( + cat < + + Logo + + +

${APP} LXC

+ +

+ + spend Coffee + +

+ + + + GitHub + + + + Discussions + + + + Issues + + +EOF + ) + pct set "$CTID" -description "$DESCRIPTION" + + if [[ -f /etc/systemd/system/ping-instances.service ]]; then + systemctl start ping-instances.service + fi + + post_update_to_api "done" "none" +} + +# ------------------------------------------------------------------------------ +# api_exit_script() +# +# - Exit trap handler +# - Reports exit codes to API with detailed reason +# - Handles known codes (100–209) and maps them to errors +# ------------------------------------------------------------------------------ +api_exit_script() { + exit_code=$? + if [ $exit_code -ne 0 ]; then + case $exit_code in + 100) post_update_to_api "failed" "100: Unexpected error in create_lxc.sh" ;; + 101) post_update_to_api "failed" "101: No network connection detected in create_lxc.sh" ;; + 200) post_update_to_api "failed" "200: LXC creation failed in create_lxc.sh" ;; + 201) post_update_to_api "failed" "201: Invalid Storage class in create_lxc.sh" ;; + 202) post_update_to_api "failed" "202: User aborted menu in create_lxc.sh" ;; + 203) post_update_to_api "failed" "203: CTID not set in create_lxc.sh" ;; + 204) post_update_to_api "failed" "204: PCT_OSTYPE not set in create_lxc.sh" ;; + 205) post_update_to_api "failed" "205: CTID cannot be less than 100 in create_lxc.sh" ;; + 206) post_update_to_api "failed" "206: CTID already in use in create_lxc.sh" ;; + 207) post_update_to_api "failed" "207: Template not found in create_lxc.sh" ;; + 208) post_update_to_api "failed" "208: Error downloading template in create_lxc.sh" ;; + 209) post_update_to_api "failed" "209: Container creation failed, but template is intact in create_lxc.sh" ;; + *) post_update_to_api "failed" "Unknown error, exit code: $exit_code in create_lxc.sh" ;; + esac + fi +} + +if command -v pveversion >/dev/null 2>&1; then + trap 'api_exit_script' EXIT +fi +trap 'post_update_to_api "failed" "$BASH_COMMAND"' ERR +trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT +trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM diff --git a/misc/build.func.backup-refactoring-20251029-125644 b/misc/build.func.backup-refactoring-20251029-125644 new file mode 100644 index 000000000..d452f4637 --- /dev/null +++ b/misc/build.func.backup-refactoring-20251029-125644 @@ -0,0 +1,3517 @@ +#!/usr/bin/env bash +# Copyright (c) 2021-2025 community-scripts ORG +# Author: tteck (tteckster) | MickLesk | michelroegl-brunner +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Revision: 1 + +# ============================================================================== +# SECTION 1: CORE INITIALIZATION & VARIABLES +# ============================================================================== + +# ------------------------------------------------------------------------------ +# variables() +# +# - Normalize application name (NSAPP = lowercase, no spaces) +# - Build installer filename (var_install) +# - Define regex for integer validation +# - Fetch hostname of Proxmox node +# - Set default values for diagnostics/method +# - Generate random UUID for tracking +# - Get Proxmox VE version and kernel version +# ------------------------------------------------------------------------------ +variables() { + NSAPP=$(echo "${APP,,}" | tr -d ' ') # This function sets the NSAPP variable by converting the value of the APP variable to lowercase and removing any spaces. + var_install="${NSAPP}-install" # sets the var_install variable by appending "-install" to the value of NSAPP. + INTEGER='^[0-9]+([.][0-9]+)?$' # it defines the INTEGER regular expression pattern. + PVEHOST_NAME=$(hostname) # gets the Proxmox Hostname and sets it to Uppercase + DIAGNOSTICS="yes" # sets the DIAGNOSTICS variable to "yes", used for the API call. + METHOD="default" # sets the METHOD variable to "default", used for the API call. + RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" # generates a random UUID and sets it to the RANDOM_UUID variable. + CTTYPE="${CTTYPE:-${CT_TYPE:-1}}" + #CT_TYPE=${var_unprivileged:-$CT_TYPE} + + # Get Proxmox VE version and kernel version + if command -v pveversion >/dev/null 2>&1; then + PVEVERSION=$(pveversion | grep "pve-manager" | awk '{print $2}' | cut -d'/' -f1) + else + PVEVERSION="N/A" + fi + KERNEL_VERSION=$(uname -r) +} + +# ----------------------------------------------------------------------------- +# Community-Scripts bootstrap loader +# - Always sources build.func from remote +# - Updates local core files only if build.func changed +# - Local cache: /usr/local/community-scripts/core +# ----------------------------------------------------------------------------- + +# FUNC_DIR="/usr/local/community-scripts/core" +# mkdir -p "$FUNC_DIR" + +# BUILD_URL="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func" +# BUILD_REV="$FUNC_DIR/build.rev" +# DEVMODE="${DEVMODE:-no}" + +# # --- Step 1: fetch build.func content once, compute hash --- +# build_content="$(curl -fsSL "$BUILD_URL")" || { +# echo "❌ Failed to fetch build.func" +# exit 1 +# } + +# newhash=$(printf "%s" "$build_content" | sha256sum | awk '{print $1}') +# oldhash=$(cat "$BUILD_REV" 2>/dev/null || echo "") + +# # --- Step 2: if build.func changed, offer update for core files --- +# if [ "$newhash" != "$oldhash" ]; then +# echo "⚠️ build.func changed!" + +# while true; do +# read -rp "Refresh local core files? [y/N/diff]: " ans +# case "$ans" in +# [Yy]*) +# echo "$newhash" >"$BUILD_REV" + +# update_func_file() { +# local file="$1" +# local url="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/$file" +# local local_path="$FUNC_DIR/$file" + +# echo "⬇️ Downloading $file ..." +# curl -fsSL "$url" -o "$local_path" || { +# echo "❌ Failed to fetch $file" +# exit 1 +# } +# echo "✔️ Updated $file" +# } + +# update_func_file core.func +# update_func_file error_handler.func +# update_func_file tools.func +# break +# ;; +# [Dd]*) +# for file in core.func error_handler.func tools.func; do +# local_path="$FUNC_DIR/$file" +# url="https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/$file" +# remote_tmp="$(mktemp)" + +# curl -fsSL "$url" -o "$remote_tmp" || continue + +# if [ -f "$local_path" ]; then +# echo "🔍 Diff for $file:" +# diff -u "$local_path" "$remote_tmp" || echo "(no differences)" +# else +# echo "📦 New file $file will be installed" +# fi + +# rm -f "$remote_tmp" +# done +# ;; +# *) +# echo "❌ Skipped updating local core files" +# break +# ;; +# esac +# done +# else +# if [ "$DEVMODE" != "yes" ]; then +# echo "✔️ build.func unchanged → using existing local core files" +# fi +# fi + +# if [ -n "${_COMMUNITY_SCRIPTS_LOADER:-}" ]; then +# return 0 2>/dev/null || exit 0 +# fi +# _COMMUNITY_SCRIPTS_LOADER=1 + +# # --- Step 3: always source local versions of the core files --- +# source "$FUNC_DIR/core.func" +# source "$FUNC_DIR/error_handler.func" +# source "$FUNC_DIR/tools.func" + +# # --- Step 4: finally, source build.func directly from memory --- +# # (no tmp file needed) +# source <(printf "%s" "$build_content") + +# ------------------------------------------------------------------------------ +# Load core + error handler functions from community-scripts repo +# +# - Prefer curl if available, fallback to wget +# - Load: core.func, error_handler.func, api.func +# - Initialize error traps after loading +# ------------------------------------------------------------------------------ + +source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/api.func) + +if command -v curl >/dev/null 2>&1; then + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) + load_functions + catch_errors + #echo "(build.func) Loaded core.func via curl" +elif command -v wget >/dev/null 2>&1; then + source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/core.func) + source <(wget -qO- https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/error_handler.func) + load_functions + catch_errors + #echo "(build.func) Loaded core.func via wget" +fi + +# ------------------------------------------------------------------------------ +# maxkeys_check() +# +# - Reads kernel keyring limits (maxkeys, maxbytes) +# - Checks current usage for LXC user (UID 100000) +# - Warns if usage is close to limits and suggests sysctl tuning +# - Exits if thresholds are exceeded +# - https://cleveruptime.com/docs/files/proc-key-users | https://docs.kernel.org/security/keys/core.html +# ------------------------------------------------------------------------------ + +maxkeys_check() { + # Read kernel parameters + per_user_maxkeys=$(cat /proc/sys/kernel/keys/maxkeys 2>/dev/null || echo 0) + per_user_maxbytes=$(cat /proc/sys/kernel/keys/maxbytes 2>/dev/null || echo 0) + + # Exit if kernel parameters are unavailable + if [[ "$per_user_maxkeys" -eq 0 || "$per_user_maxbytes" -eq 0 ]]; then + echo -e "${CROSS}${RD} Error: Unable to read kernel parameters. Ensure proper permissions.${CL}" + exit 1 + fi + + # Fetch key usage for user ID 100000 (typical for containers) + used_lxc_keys=$(awk '/100000:/ {print $2}' /proc/key-users 2>/dev/null || echo 0) + used_lxc_bytes=$(awk '/100000:/ {split($5, a, "/"); print a[1]}' /proc/key-users 2>/dev/null || echo 0) + + # Calculate thresholds and suggested new limits + threshold_keys=$((per_user_maxkeys - 100)) + threshold_bytes=$((per_user_maxbytes - 1000)) + new_limit_keys=$((per_user_maxkeys * 2)) + new_limit_bytes=$((per_user_maxbytes * 2)) + + # Check if key or byte usage is near limits + failure=0 + if [[ "$used_lxc_keys" -gt "$threshold_keys" ]]; then + echo -e "${CROSS}${RD} Warning: Key usage is near the limit (${used_lxc_keys}/${per_user_maxkeys}).${CL}" + echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxkeys=${new_limit_keys}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." + failure=1 + fi + if [[ "$used_lxc_bytes" -gt "$threshold_bytes" ]]; then + echo -e "${CROSS}${RD} Warning: Key byte usage is near the limit (${used_lxc_bytes}/${per_user_maxbytes}).${CL}" + echo -e "${INFO} Suggested action: Set ${GN}kernel.keys.maxbytes=${new_limit_bytes}${CL} in ${BOLD}/etc/sysctl.d/98-community-scripts.conf${CL}." + failure=1 + fi + + # Provide next steps if issues are detected + if [[ "$failure" -eq 1 ]]; then + echo -e "${INFO} To apply changes, run: ${BOLD}service procps force-reload${CL}" + exit 1 + fi + + echo -e "${CM}${GN} All kernel key limits are within safe thresholds.${CL}" +} + +# ------------------------------------------------------------------------------ +# get_current_ip() +# +# - Returns current container IP depending on OS type +# - Debian/Ubuntu: uses `hostname -I` +# - Alpine: parses eth0 via `ip -4 addr` +# ------------------------------------------------------------------------------ +get_current_ip() { + if [ -f /etc/os-release ]; then + # Check for Debian/Ubuntu (uses hostname -I) + if grep -qE 'ID=debian|ID=ubuntu' /etc/os-release; then + CURRENT_IP=$(hostname -I | awk '{print $1}') + # Check for Alpine (uses ip command) + elif grep -q 'ID=alpine' /etc/os-release; then + CURRENT_IP=$(ip -4 addr show eth0 | awk '/inet / {print $2}' | cut -d/ -f1 | head -n 1) + else + CURRENT_IP="Unknown" + fi + fi + echo "$CURRENT_IP" +} + +# ------------------------------------------------------------------------------ +# update_motd_ip() +# +# - Updates /etc/motd with current container IP +# - Removes old IP entries to avoid duplicates +# ------------------------------------------------------------------------------ +update_motd_ip() { + MOTD_FILE="/etc/motd" + + if [ -f "$MOTD_FILE" ]; then + # Remove existing IP Address lines to prevent duplication + sed -i '/IP Address:/d' "$MOTD_FILE" + + IP=$(get_current_ip) + # Add the new IP address + echo -e "${TAB}${NETWORK}${YW} IP Address: ${GN}${IP}${CL}" >>"$MOTD_FILE" + fi +} + +# ------------------------------------------------------------------------------ +# install_ssh_keys_into_ct() +# +# - Installs SSH keys into container root account if SSH is enabled +# - Uses pct push or direct input to authorized_keys +# - Falls back to warning if no keys provided +# ------------------------------------------------------------------------------ +install_ssh_keys_into_ct() { + [[ "$SSH" != "yes" ]] && return 0 + + if [[ -n "$SSH_KEYS_FILE" && -s "$SSH_KEYS_FILE" ]]; then + msg_info "Installing selected SSH keys into CT ${CTID}" + pct exec "$CTID" -- sh -c 'mkdir -p /root/.ssh && chmod 700 /root/.ssh' || { + msg_error "prepare /root/.ssh failed" + return 1 + } + pct push "$CTID" "$SSH_KEYS_FILE" /root/.ssh/authorized_keys >/dev/null 2>&1 || + pct exec "$CTID" -- sh -c "cat > /root/.ssh/authorized_keys" <"$SSH_KEYS_FILE" || { + msg_error "write authorized_keys failed" + return 1 + } + pct exec "$CTID" -- sh -c 'chmod 600 /root/.ssh/authorized_keys' || true + msg_ok "Installed SSH keys into CT ${CTID}" + return 0 + fi + + # Fallback: nichts ausgewählt + msg_warn "No SSH keys to install (skipping)." + return 0 +} + +# ------------------------------------------------------------------------------ +# base_settings() +# +# - Defines all base/default variables for container creation +# - Reads from environment variables (var_*) +# - Provides fallback defaults for OS type/version +# ------------------------------------------------------------------------------ +base_settings() { + # Default Settings + CT_TYPE=${var_unprivileged:-"1"} + DISK_SIZE=${var_disk:-"4"} + CORE_COUNT=${var_cpu:-"1"} + RAM_SIZE=${var_ram:-"1024"} + VERBOSE=${var_verbose:-"${1:-no}"} + PW=${var_pw:-""} + CT_ID=${var_ctid:-$NEXTID} + HN=${var_hostname:-$NSAPP} + BRG=${var_brg:-"vmbr0"} + NET=${var_net:-"dhcp"} + IPV6_METHOD=${var_ipv6_method:-"none"} + IPV6_STATIC=${var_ipv6_static:-""} + GATE=${var_gateway:-""} + APT_CACHER=${var_apt_cacher:-""} + APT_CACHER_IP=${var_apt_cacher_ip:-""} + MTU=${var_mtu:-""} + SD=${var_storage:-""} + NS=${var_ns:-""} + MAC=${var_mac:-""} + VLAN=${var_vlan:-""} + SSH=${var_ssh:-"no"} + SSH_AUTHORIZED_KEY=${var_ssh_authorized_key:-""} + UDHCPC_FIX=${var_udhcpc_fix:-""} + TAGS="community-script,${var_tags:-}" + ENABLE_FUSE=${var_fuse:-"${1:-no}"} + ENABLE_TUN=${var_tun:-"${1:-no}"} + + # Since these 2 are only defined outside of default_settings function, we add a temporary fallback. TODO: To align everything, we should add these as constant variables (e.g. OSTYPE and OSVERSION), but that would currently require updating the default_settings function for all existing scripts + if [ -z "$var_os" ]; then + var_os="debian" + fi + if [ -z "$var_version" ]; then + var_version="12" + fi +} + +# ------------------------------------------------------------------------------ +# echo_default() +# +# - Prints summary of default values (ID, OS, type, disk, RAM, CPU, etc.) +# - Uses icons and formatting for readability +# - Convert CT_TYPE to description +# ------------------------------------------------------------------------------ +echo_default() { + CT_TYPE_DESC="Unprivileged" + if [ "$CT_TYPE" -eq 0 ]; then + CT_TYPE_DESC="Privileged" + fi + echo -e "${INFO}${BOLD}${DGN}PVE Version ${PVEVERSION} (Kernel: ${KERNEL_VERSION})${CL}" + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}${CT_ID}${CL}" + echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os ($var_version)${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}" + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + if [ "$VERBOSE" == "yes" ]; then + echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}Enabled${CL}" + fi + echo -e "${CREATING}${BOLD}${BL}Creating a ${APP} LXC using the above default settings${CL}" + echo -e " " +} + +# ------------------------------------------------------------------------------ +# exit_script() +# +# - Called when user cancels an action +# - Clears screen and exits gracefully +# ------------------------------------------------------------------------------ +exit_script() { + clear + echo -e "\n${CROSS}${RD}User exited script${CL}\n" + exit +} + +# ------------------------------------------------------------------------------ +# find_host_ssh_keys() +# +# - Scans system for available SSH keys +# - Supports defaults (~/.ssh, /etc/ssh/authorized_keys) +# - Returns list of files containing valid SSH public keys +# - Sets FOUND_HOST_KEY_COUNT to number of keys found +# ------------------------------------------------------------------------------ +find_host_ssh_keys() { + local re='(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))' + local -a files=() cand=() + local g="${var_ssh_import_glob:-}" + local total=0 f base c + + shopt -s nullglob + if [[ -n "$g" ]]; then + for pat in $g; do cand+=($pat); done + else + cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2) + cand+=(/root/.ssh/*.pub) + cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*) + fi + shopt -u nullglob + + for f in "${cand[@]}"; do + [[ -f "$f" && -r "$f" ]] || continue + base="$(basename -- "$f")" + case "$base" in + known_hosts | known_hosts.* | config) continue ;; + id_*) [[ "$f" != *.pub ]] && continue ;; + esac + + # CRLF safe check for host keys + c=$(tr -d '\r' <"$f" | awk ' + /^[[:space:]]*#/ {next} + /^[[:space:]]*$/ {next} + {print} + ' | grep -E -c '"$re"' || true) + + if ((c > 0)); then + files+=("$f") + total=$((total + c)) + fi + done + + # Fallback to /root/.ssh/authorized_keys + if ((${#files[@]} == 0)) && [[ -r /root/.ssh/authorized_keys ]]; then + if grep -E -q "$re" /root/.ssh/authorized_keys; then + files+=(/root/.ssh/authorized_keys) + total=$((total + $(grep -E -c "$re" /root/.ssh/authorized_keys || echo 0))) + fi + fi + + FOUND_HOST_KEY_COUNT="$total" + ( + IFS=: + echo "${files[*]}" + ) +} + +# ------------------------------------------------------------------------------ +# advanced_settings() +# +# - Interactive whiptail menu for advanced configuration +# - Lets user set container type, password, CT ID, hostname, disk, CPU, RAM +# - Supports IPv4/IPv6, DNS, MAC, VLAN, tags, SSH keys, FUSE, verbose mode +# - Ends with confirmation or re-entry if cancelled +# ------------------------------------------------------------------------------ +advanced_settings() { + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox --title "Here is an instructional tip:" "To make a selection, use the Spacebar." 8 58 + # Setting Default Tag for Advanced Settings + TAGS="community-script;${var_tags:-}" + CT_DEFAULT_TYPE="${CT_TYPE}" + CT_TYPE="" + while [ -z "$CT_TYPE" ]; do + if [ "$CT_DEFAULT_TYPE" == "1" ]; then + if CT_TYPE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ + "1" "Unprivileged" ON \ + "0" "Privileged" OFF \ + 3>&1 1>&2 2>&3); then + if [ -n "$CT_TYPE" ]; then + CT_TYPE_DESC="Unprivileged" + if [ "$CT_TYPE" -eq 0 ]; then + CT_TYPE_DESC="Privileged" + fi + echo -e "${INFO}${BOLD}${DGN}PVE Version ${PVEVERSION} (Kernel: ${KERNEL_VERSION})${CL}" + echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os |${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" + fi + else + exit_script + fi + fi + if [ "$CT_DEFAULT_TYPE" == "0" ]; then + if CT_TYPE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "CONTAINER TYPE" --radiolist "Choose Type" 10 58 2 \ + "1" "Unprivileged" OFF \ + "0" "Privileged" ON \ + 3>&1 1>&2 2>&3); then + if [ -n "$CT_TYPE" ]; then + CT_TYPE_DESC="Unprivileged" + if [ "$CT_TYPE" -eq 0 ]; then + CT_TYPE_DESC="Privileged" + fi + echo -e "${INFO}${BOLD}${DGN}PVE Version ${PVEVERSION} (Kernel: ${KERNEL_VERSION})${CL}" + echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}$var_os${CL}" + echo -e "${OSVERSION}${BOLD}${DGN}Version: ${BGN}$var_version${CL}" + echo -e "${CONTAINERTYPE}${BOLD}${DGN}Container Type: ${BGN}$CT_TYPE_DESC${CL}" + fi + else + exit_script + fi + fi + done + + while true; do + if PW1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --passwordbox "\nSet Root Password (needed for root ssh access)" 9 58 --title "PASSWORD (leave blank for automatic login)" 3>&1 1>&2 2>&3); then + # Empty = Autologin + if [[ -z "$PW1" ]]; then + PW="" + PW1="Automatic Login" + echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}$PW1${CL}" + break + fi + + # Invalid: contains spaces + if [[ "$PW1" == *" "* ]]; then + whiptail --msgbox "Password cannot contain spaces." 8 58 + continue + fi + + # Invalid: too short + if ((${#PW1} < 5)); then + whiptail --msgbox "Password must be at least 5 characters." 8 58 + continue + fi + + # Confirm password + if PW2=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --passwordbox "\nVerify Root Password" 9 58 --title "PASSWORD VERIFICATION" 3>&1 1>&2 2>&3); then + if [[ "$PW1" == "$PW2" ]]; then + PW="-password $PW1" + echo -e "${VERIFYPW}${BOLD}${DGN}Root Password: ${BGN}********${CL}" + break + else + whiptail --msgbox "Passwords do not match. Please try again." 8 58 + fi + else + exit_script + fi + else + exit_script + fi + done + + if CT_ID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Container ID" 8 58 "$NEXTID" --title "CONTAINER ID" 3>&1 1>&2 2>&3); then + if [ -z "$CT_ID" ]; then + CT_ID="$NEXTID" + fi + else + exit_script + fi + echo -e "${CONTAINERID}${BOLD}${DGN}Container ID: ${BGN}$CT_ID${CL}" + + while true; do + if CT_NAME=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 "$NSAPP" --title "HOSTNAME" 3>&1 1>&2 2>&3); then + if [ -z "$CT_NAME" ]; then + HN="$NSAPP" + else + HN=$(echo "${CT_NAME,,}" | tr -d ' ') + fi + # Hostname validate (RFC 1123) + if [[ "$HN" =~ ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ ]]; then + echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}" + break + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --msgbox "❌ Invalid hostname: '$HN'\n\nOnly lowercase letters, digits and hyphens (-) are allowed.\nUnderscores (_) or other characters are not permitted!" 10 70 + fi + else + exit_script + fi + done + + while true; do + DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GB" 8 58 "$var_disk" --title "DISK SIZE" 3>&1 1>&2 2>&3) || exit_script + + if [ -z "$DISK_SIZE" ]; then + DISK_SIZE="$var_disk" + fi + + if [[ "$DISK_SIZE" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE} GB${CL}" + break + else + whiptail --msgbox "Disk size must be a positive integer!" 8 58 + fi + done + + while true; do + CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --inputbox "Allocate CPU Cores" 8 58 "$var_cpu" --title "CORE COUNT" 3>&1 1>&2 2>&3) || exit_script + + if [ -z "$CORE_COUNT" ]; then + CORE_COUNT="$var_cpu" + fi + + if [[ "$CORE_COUNT" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}" + break + else + whiptail --msgbox "CPU core count must be a positive integer!" 8 58 + fi + done + + while true; do + RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --inputbox "Allocate RAM in MiB" 8 58 "$var_ram" --title "RAM" 3>&1 1>&2 2>&3) || exit_script + + if [ -z "$RAM_SIZE" ]; then + RAM_SIZE="$var_ram" + fi + + if [[ "$RAM_SIZE" =~ ^[1-9][0-9]*$ ]]; then + echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE} MiB${CL}" + break + else + whiptail --msgbox "RAM size must be a positive integer!" 8 58 + fi + done + + IFACE_FILEPATH_LIST="/etc/network/interfaces"$'\n'$(find "/etc/network/interfaces.d/" -type f) + BRIDGES="" + OLD_IFS=$IFS + IFS=$'\n' + for iface_filepath in ${IFACE_FILEPATH_LIST}; do + + iface_indexes_tmpfile=$(mktemp -q -u '.iface-XXXX') + (grep -Pn '^\s*iface' "${iface_filepath}" | cut -d':' -f1 && wc -l "${iface_filepath}" | cut -d' ' -f1) | awk 'FNR==1 {line=$0; next} {print line":"$0-1; line=$0}' >"${iface_indexes_tmpfile}" || true + + if [ -f "${iface_indexes_tmpfile}" ]; then + + while read -r pair; do + start=$(echo "${pair}" | cut -d':' -f1) + end=$(echo "${pair}" | cut -d':' -f2) + + if awk "NR >= ${start} && NR <= ${end}" "${iface_filepath}" | grep -qP '^\s*(bridge[-_](ports|stp|fd|vlan-aware|vids)|ovs_type\s+OVSBridge)\b'; then + iface_name=$(sed "${start}q;d" "${iface_filepath}" | awk '{print $2}') + BRIDGES="${iface_name}"$'\n'"${BRIDGES}" + fi + + done <"${iface_indexes_tmpfile}" + rm -f "${iface_indexes_tmpfile}" + fi + + done + IFS=$OLD_IFS + BRIDGES=$(echo "$BRIDGES" | grep -v '^\s*$' | sort | uniq) + if [[ -z "$BRIDGES" ]]; then + BRG="vmbr0" + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + else + # Build bridge menu with descriptions + BRIDGE_MENU_OPTIONS=() + while IFS= read -r bridge; do + if [[ -n "$bridge" ]]; then + # Get description from Proxmox built-in method - find comment for this specific bridge + description=$(grep -A 10 "iface $bridge" /etc/network/interfaces | grep '^#' | head -n1 | sed 's/^#\s*//') + if [[ -n "$description" ]]; then + BRIDGE_MENU_OPTIONS+=("$bridge" "${description}") + else + BRIDGE_MENU_OPTIONS+=("$bridge" " ") + fi + fi + done <<<"$BRIDGES" + + BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --menu "Select network bridge: " 18 55 6 "${BRIDGE_MENU_OPTIONS[@]}" 3>&1 1>&2 2>&3) + if [[ -z "$BRG" ]]; then + exit_script + else + echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}" + fi + fi + + # IPv4 methods: dhcp, static, none + while true; do + IPV4_METHOD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "IPv4 Address Management" \ + --menu "Select IPv4 Address Assignment Method:" 12 60 2 \ + "dhcp" "Automatic (DHCP, recommended)" \ + "static" "Static (manual entry)" \ + 3>&1 1>&2 2>&3) + + exit_status=$? + if [ $exit_status -ne 0 ]; then + exit_script + fi + + case "$IPV4_METHOD" in + dhcp) + NET="dhcp" + GATE="" + echo -e "${NETWORK}${BOLD}${DGN}IPv4: DHCP${CL}" + break + ;; + static) + # Static: call and validate CIDR address + while true; do + NET=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Enter Static IPv4 CIDR Address (e.g. 192.168.100.50/24)" 8 58 "" \ + --title "IPv4 ADDRESS" 3>&1 1>&2 2>&3) + if [ -z "$NET" ]; then + whiptail --msgbox "IPv4 address must not be empty." 8 58 + continue + elif [[ "$NET" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/([0-9]|[1-2][0-9]|3[0-2])$ ]]; then + echo -e "${NETWORK}${BOLD}${DGN}IPv4 Address: ${BGN}$NET${CL}" + break + else + whiptail --msgbox "$NET is not a valid IPv4 CIDR address. Please enter a correct value!" 8 58 + fi + done + + # call and validate Gateway + while true; do + GATE1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Enter Gateway IP address for static IPv4" 8 58 "" \ + --title "Gateway IP" 3>&1 1>&2 2>&3) + if [ -z "$GATE1" ]; then + whiptail --msgbox "Gateway IP address cannot be empty." 8 58 + elif [[ ! "$GATE1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then + whiptail --msgbox "Invalid Gateway IP address format." 8 58 + else + GATE=",gw=$GATE1" + echo -e "${GATEWAY}${BOLD}${DGN}Gateway IP Address: ${BGN}$GATE1${CL}" + break + fi + done + break + ;; + esac + done + + # IPv6 Address Management selection + while true; do + IPV6_METHOD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --menu \ + "Select IPv6 Address Management Type:" 15 58 4 \ + "auto" "SLAAC/AUTO (recommended, default)" \ + "dhcp" "DHCPv6" \ + "static" "Static (manual entry)" \ + "none" "Disabled" \ + --default-item "auto" 3>&1 1>&2 2>&3) + [ $? -ne 0 ] && exit_script + + case "$IPV6_METHOD" in + auto) + echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}SLAAC/AUTO${CL}" + IPV6_ADDR="" + IPV6_GATE="" + break + ;; + dhcp) + echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}DHCPv6${CL}" + IPV6_ADDR="dhcp" + IPV6_GATE="" + break + ;; + static) + # Ask for static IPv6 address (CIDR notation, e.g., 2001:db8::1234/64) + while true; do + IPV6_ADDR=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox \ + "Set a static IPv6 CIDR address (e.g., 2001:db8::1234/64)" 8 58 "" \ + --title "IPv6 STATIC ADDRESS" 3>&1 1>&2 2>&3) || exit_script + if [[ "$IPV6_ADDR" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+(/[0-9]{1,3})$ ]]; then + echo -e "${NETWORK}${BOLD}${DGN}IPv6 Address: ${BGN}$IPV6_ADDR${CL}" + break + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox \ + "$IPV6_ADDR is an invalid IPv6 CIDR address. Please enter a valid IPv6 CIDR address (e.g., 2001:db8::1234/64)" 8 58 + fi + done + # Optional: ask for IPv6 gateway for static config + while true; do + IPV6_GATE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox \ + "Enter IPv6 gateway address (optional, leave blank for none)" 8 58 "" --title "IPv6 GATEWAY" 3>&1 1>&2 2>&3) + if [ -z "$IPV6_GATE" ]; then + IPV6_GATE="" + break + elif [[ "$IPV6_GATE" =~ ^([0-9a-fA-F:]+:+)+[0-9a-fA-F]+$ ]]; then + break + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox \ + "Invalid IPv6 gateway format." 8 58 + fi + done + break + ;; + none) + echo -e "${NETWORK}${BOLD}${DGN}IPv6: ${BGN}Disabled${CL}" + IPV6_ADDR="none" + IPV6_GATE="" + break + ;; + *) + exit_script + ;; + esac + done + + if [ "$var_os" == "alpine" ]; then + APT_CACHER="" + APT_CACHER_IP="" + else + if APT_CACHER_IP=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set APT-Cacher IP (leave blank for none)" 8 58 --title "APT-Cacher IP" 3>&1 1>&2 2>&3); then + APT_CACHER="${APT_CACHER_IP:+yes}" + echo -e "${NETWORK}${BOLD}${DGN}APT-Cacher IP Address: ${BGN}${APT_CACHER_IP:-Default}${CL}" + else + exit_script + fi + fi + + # if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "IPv6" --yesno "Disable IPv6?" 10 58); then + # DISABLEIP6="yes" + # else + # DISABLEIP6="no" + # fi + # echo -e "${DISABLEIPV6}${BOLD}${DGN}Disable IPv6: ${BGN}$DISABLEIP6${CL}" + + if MTU1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Interface MTU Size (leave blank for default [The MTU of your selected vmbr, default is 1500])" 8 58 --title "MTU SIZE" 3>&1 1>&2 2>&3); then + if [ -z "$MTU1" ]; then + MTU1="Default" + MTU="" + else + MTU=",mtu=$MTU1" + fi + echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}" + else + exit_script + fi + + if SD=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a DNS Search Domain (leave blank for HOST)" 8 58 --title "DNS Search Domain" 3>&1 1>&2 2>&3); then + if [ -z "$SD" ]; then + SX=Host + SD="" + else + SX=$SD + SD="-searchdomain=$SD" + fi + echo -e "${SEARCH}${BOLD}${DGN}DNS Search Domain: ${BGN}$SX${CL}" + else + exit_script + fi + + if NX=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a DNS Server IP (leave blank for HOST)" 8 58 --title "DNS SERVER IP" 3>&1 1>&2 2>&3); then + if [ -z "$NX" ]; then + NX=Host + NS="" + else + NS="-nameserver=$NX" + fi + echo -e "${NETWORK}${BOLD}${DGN}DNS Server IP Address: ${BGN}$NX${CL}" + else + exit_script + fi + + if [ "$var_os" == "alpine" ] && [ "$NET" == "dhcp" ] && [ "$NX" != "Host" ]; then + UDHCPC_FIX="yes" + else + UDHCPC_FIX="no" + fi + export UDHCPC_FIX + + if MAC1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a MAC Address(leave blank for generated MAC)" 8 58 --title "MAC ADDRESS" 3>&1 1>&2 2>&3); then + if [ -z "$MAC1" ]; then + MAC1="Default" + MAC="" + else + MAC=",hwaddr=$MAC1" + echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC1${CL}" + fi + else + exit_script + fi + + if VLAN1=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set a Vlan(leave blank for no VLAN)" 8 58 --title "VLAN" 3>&1 1>&2 2>&3); then + if [ -z "$VLAN1" ]; then + VLAN1="Default" + VLAN="" + else + VLAN=",tag=$VLAN1" + fi + echo -e "${VLANTAG}${BOLD}${DGN}Vlan: ${BGN}$VLAN1${CL}" + else + exit_script + fi + + if ADV_TAGS=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --inputbox "Set Custom Tags?[If you remove all, there will be no tags!]" 8 58 "${TAGS}" --title "Advanced Tags" 3>&1 1>&2 2>&3); then + if [ -n "${ADV_TAGS}" ]; then + ADV_TAGS=$(echo "$ADV_TAGS" | tr -d '[:space:]') + TAGS="${ADV_TAGS}" + else + TAGS=";" + fi + echo -e "${NETWORK}${BOLD}${DGN}Tags: ${BGN}$TAGS${CL}" + else + exit_script + fi + + configure_ssh_settings + export SSH_KEYS_FILE + echo -e "${ROOTSSH}${BOLD}${DGN}Root SSH Access: ${BGN}$SSH${CL}" + if (whiptail --backtitle "Proxmox VE Helper Scripts" --defaultno --title "FUSE Support" --yesno "Enable FUSE support?\nRequired for tools like rclone, mergerfs, AppImage, etc." 10 58); then + ENABLE_FUSE="yes" + else + ENABLE_FUSE="no" + fi + echo -e "${FUSE}${BOLD}${DGN}Enable FUSE Support: ${BGN}$ENABLE_FUSE${CL}" + + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "VERBOSE MODE" --yesno "Enable Verbose Mode?" 10 58); then + VERBOSE="yes" + else + VERBOSE="no" + fi + echo -e "${SEARCH}${BOLD}${DGN}Verbose Mode: ${BGN}$VERBOSE${CL}" + + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create ${APP} LXC?" 10 58); then + echo -e "${CREATING}${BOLD}${RD}Creating a ${APP} LXC using the above advanced settings${CL}" + else + clear + header_info + echo -e "${INFO}${BOLD}${DGN}PVE Version ${PVEVERSION} (Kernel: ${KERNEL_VERSION})${CL}" + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings on node $PVEHOST_NAME${CL}" + advanced_settings + fi +} + +# ------------------------------------------------------------------------------ +# diagnostics_check() +# +# - Ensures diagnostics config file exists at /usr/local/community-scripts/diagnostics +# - Asks user whether to send anonymous diagnostic data +# - Saves DIAGNOSTICS=yes/no in the config file +# ------------------------------------------------------------------------------ +diagnostics_check() { + if ! [ -d "/usr/local/community-scripts" ]; then + mkdir -p /usr/local/community-scripts + fi + + if ! [ -f "/usr/local/community-scripts/diagnostics" ]; then + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "DIAGNOSTICS" --yesno "Send Diagnostics of LXC Installation?\n\n(This only transmits data without user data, just RAM, CPU, LXC name, ...)" 10 58); then + cat </usr/local/community-scripts/diagnostics +DIAGNOSTICS=yes + +#This file is used to store the diagnostics settings for the Community-Scripts API. +#https://github.com/community-scripts/ProxmoxVED/discussions/1836 +#Your diagnostics will be sent to the Community-Scripts API for troubleshooting/statistical purposes. +#You can review the data at https://community-scripts.github.io/ProxmoxVE/data +#If you do not wish to send diagnostics, please set the variable 'DIAGNOSTICS' to "no" in /usr/local/community-scripts/diagnostics, or use the menue. +#This will disable the diagnostics feature. +#To send diagnostics, set the variable 'DIAGNOSTICS' to "yes" in /usr/local/community-scripts/diagnostics, or use the menue. +#This will enable the diagnostics feature. +#The following information will be sent: +#"disk_size" +#"core_count" +#"ram_size" +#"os_type" +#"os_version" +#"nsapp" +#"method" +#"pve_version" +#"status" +#If you have any concerns, please review the source code at /misc/build.func +EOF + DIAGNOSTICS="yes" + else + cat </usr/local/community-scripts/diagnostics +DIAGNOSTICS=no + +#This file is used to store the diagnostics settings for the Community-Scripts API. +#https://github.com/community-scripts/ProxmoxVED/discussions/1836 +#Your diagnostics will be sent to the Community-Scripts API for troubleshooting/statistical purposes. +#You can review the data at https://community-scripts.github.io/ProxmoxVE/data +#If you do not wish to send diagnostics, please set the variable 'DIAGNOSTICS' to "no" in /usr/local/community-scripts/diagnostics, or use the menue. +#This will disable the diagnostics feature. +#To send diagnostics, set the variable 'DIAGNOSTICS' to "yes" in /usr/local/community-scripts/diagnostics, or use the menue. +#This will enable the diagnostics feature. +#The following information will be sent: +#"disk_size" +#"core_count" +#"ram_size" +#"os_type" +#"os_version" +#"nsapp" +#"method" +#"pve_version" +#"status" +#If you have any concerns, please review the source code at /misc/build.func +EOF + DIAGNOSTICS="no" + fi + else + DIAGNOSTICS=$(awk -F '=' '/^DIAGNOSTICS/ {print $2}' /usr/local/community-scripts/diagnostics) + + fi + +} + +# ------------------------------------------------------------------------------ +# default_var_settings +# +# - Ensures /usr/local/community-scripts/default.vars exists (creates if missing) +# - Loads var_* values from default.vars (safe parser, no source/eval) +# - Precedence: ENV var_* > default.vars > built-in defaults +# - Maps var_verbose → VERBOSE +# - Calls base_settings "$VERBOSE" and echo_default +# ------------------------------------------------------------------------------ +default_var_settings() { + # Allowed var_* keys (alphabetically sorted) + local VAR_WHITELIST=( + var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_ctid var_disk var_fuse + var_gateway var_hostname var_ipv6_method var_ipv6_static var_mac var_mtu + var_net var_ns var_pw var_ram var_tags var_tun var_unprivileged + var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage + ) + + # Snapshot: environment variables (highest precedence) + declare -A _HARD_ENV=() + local _k + for _k in "${VAR_WHITELIST[@]}"; do + if printenv "$_k" >/dev/null 2>&1; then _HARD_ENV["$_k"]=1; fi + done + + # Find default.vars location + local _find_default_vars + _find_default_vars() { + local f + for f in \ + /usr/local/community-scripts/default.vars \ + "$HOME/.config/community-scripts/default.vars" \ + "./default.vars"; do + [ -f "$f" ] && { + echo "$f" + return 0 + } + done + return 1 + } + # Allow override of storages via env (for non-interactive use cases) + [ -n "${var_template_storage:-}" ] && TEMPLATE_STORAGE="$var_template_storage" + [ -n "${var_container_storage:-}" ] && CONTAINER_STORAGE="$var_container_storage" + + # Create once, with storages already selected, no var_ctid/var_hostname lines + local _ensure_default_vars + _ensure_default_vars() { + _find_default_vars >/dev/null 2>&1 && return 0 + + local canonical="/usr/local/community-scripts/default.vars" + msg_info "No default.vars found. Creating ${canonical}" + mkdir -p /usr/local/community-scripts + + # Pick storages before writing the file (always ask unless only one) + # Create a minimal temp file to write into + : >"$canonical" + + # Base content (no var_ctid / var_hostname here) + cat >"$canonical" <<'EOF' +# Community-Scripts defaults (var_* only). Lines starting with # are comments. +# Precedence: ENV var_* > default.vars > built-ins. +# Keep keys alphabetically sorted. + +# Container type +var_unprivileged=1 + +# Resources +var_cpu=1 +var_disk=4 +var_ram=1024 + +# Network +var_brg=vmbr0 +var_net=dhcp +var_ipv6_method=none +# var_gateway= +# var_ipv6_static= +# var_vlan= +# var_mtu= +# var_mac= +# var_ns= + +# SSH +var_ssh=no +# var_ssh_authorized_key= + +# APT cacher (optional) +# var_apt_cacher=yes +# var_apt_cacher_ip=192.168.1.10 + +# Features/Tags/verbosity +var_fuse=no +var_tun=no +var_tags=community-script +var_verbose=no + +# Security (root PW) – empty => autologin +# var_pw= +EOF + + # Now choose storages (always prompt unless just one exists) + choose_and_set_storage_for_file "$canonical" template + choose_and_set_storage_for_file "$canonical" container + + chmod 0644 "$canonical" + msg_ok "Created ${canonical}" + } + + # Whitelist check + local _is_whitelisted_key + _is_whitelisted_key() { + local k="$1" + local w + for w in "${VAR_WHITELIST[@]}"; do [ "$k" = "$w" ] && return 0; done + return 1 + } + + # Safe parser for KEY=VALUE lines + local _load_vars_file + _load_vars_file() { + local file="$1" + [ -f "$file" ] || return 0 + msg_info "Loading defaults from ${file}" + local line key val + while IFS= read -r line || [ -n "$line" ]; do + line="${line#"${line%%[![:space:]]*}"}" + line="${line%"${line##*[![:space:]]}"}" + [[ -z "$line" || "$line" == \#* ]] && continue + if [[ "$line" =~ ^([A-Za-z_][A-Za-z0-9_]*)=(.*)$ ]]; then + local var_key="${BASH_REMATCH[1]}" + local var_val="${BASH_REMATCH[2]}" + + [[ "$var_key" != var_* ]] && continue + _is_whitelisted_key "$var_key" || { + msg_debug "Ignore non-whitelisted ${var_key}" + continue + } + + # Strip quotes + if [[ "$var_val" =~ ^\"(.*)\"$ ]]; then + var_val="${BASH_REMATCH[1]}" + elif [[ "$var_val" =~ ^\'(.*)\'$ ]]; then + var_val="${BASH_REMATCH[1]}" + fi + + # Unsafe characters + case $var_val in + \"*\") + var_val=${var_val#\"} + var_val=${var_val%\"} + ;; + \'*\') + var_val=${var_val#\'} + var_val=${var_val%\'} + ;; + esac # Hard env wins + [[ -n "${_HARD_ENV[$var_key]:-}" ]] && continue + # Set only if not already exported + [[ -z "${!var_key+x}" ]] && export "${var_key}=${var_val}" + else + msg_warn "Malformed line in ${file}: ${line}" + fi + done <"$file" + msg_ok "Loaded ${file}" + } + + # 1) Ensure file exists + _ensure_default_vars + + # 2) Load file + local dv + dv="$(_find_default_vars)" || { + msg_error "default.vars not found after ensure step" + return 1 + } + _load_vars_file "$dv" + + # 3) Map var_verbose → VERBOSE + if [[ -n "${var_verbose:-}" ]]; then + case "${var_verbose,,}" in 1 | yes | true | on) VERBOSE="yes" ;; 0 | no | false | off) VERBOSE="no" ;; *) VERBOSE="${var_verbose}" ;; esac + else + VERBOSE="no" + fi + + # 4) Apply base settings and show summary + METHOD="mydefaults-global" + base_settings "$VERBOSE" + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using My Defaults (default.vars) on node $PVEHOST_NAME${CL}" + echo_default +} + +# ------------------------------------------------------------------------------ +# get_app_defaults_path() +# +# - Returns full path for app-specific defaults file +# - Example: /usr/local/community-scripts/defaults/.vars +# ------------------------------------------------------------------------------ + +get_app_defaults_path() { + local n="${NSAPP:-${APP,,}}" + echo "/usr/local/community-scripts/defaults/${n}.vars" +} + +# ------------------------------------------------------------------------------ +# maybe_offer_save_app_defaults +# +# - Called after advanced_settings returned with fully chosen values. +# - If no .vars exists, offers to persist current advanced settings +# into /usr/local/community-scripts/defaults/.vars +# - Only writes whitelisted var_* keys. +# - Extracts raw values from flags like ",gw=..." ",mtu=..." etc. +# ------------------------------------------------------------------------------ +if ! declare -p VAR_WHITELIST >/dev/null 2>&1; then + declare -ag VAR_WHITELIST=( + var_apt_cacher var_apt_cacher_ip var_brg var_cpu var_ctid var_disk var_fuse + var_gateway var_hostname var_ipv6_method var_ipv6_static var_mac var_mtu + var_net var_ns var_pw var_ram var_tags var_tun var_unprivileged + var_verbose var_vlan var_ssh var_ssh_authorized_key var_container_storage var_template_storage + ) +fi + +# Note: _is_whitelisted_key() is defined above in default_var_settings section + +_sanitize_value() { + # Disallow Command-Substitution / Shell-Meta + case "$1" in + *'$('* | *'`'* | *';'* | *'&'* | *'<('*) + echo "" + return 0 + ;; + esac + echo "$1" +} + +# Map-Parser: read var_* from file into _VARS_IN associative array +# Note: Main _load_vars_file() with full validation is defined in default_var_settings section +# This simplified version is used specifically for diff operations via _VARS_IN array +declare -A _VARS_IN +_load_vars_file_to_map() { + local file="$1" + [ -f "$file" ] || return 0 + _VARS_IN=() # Clear array + local line key val + while IFS= read -r line || [ -n "$line" ]; do + line="${line#"${line%%[![:space:]]*}"}" + line="${line%"${line##*[![:space:]]}"}" + [ -z "$line" ] && continue + case "$line" in + \#*) continue ;; + esac + key=$(printf "%s" "$line" | cut -d= -f1) + val=$(printf "%s" "$line" | cut -d= -f2-) + case "$key" in + var_*) + if _is_whitelisted_key "$key"; then + _VARS_IN["$key"]="$val" + fi + ;; + esac + done <"$file" +} + +# Diff function for two var_* files -> produces human-readable diff list for $1 (old) vs $2 (new) +_build_vars_diff() { + local oldf="$1" newf="$2" + local k + local -A OLD=() NEW=() + _load_vars_file_to_map "$oldf" + for k in "${!_VARS_IN[@]}"; do OLD["$k"]="${_VARS_IN[$k]}"; done + _load_vars_file_to_map "$newf" + for k in "${!_VARS_IN[@]}"; do NEW["$k"]="${_VARS_IN[$k]}"; done + + local out + out+="# Diff for ${APP} (${NSAPP})\n" + out+="# Old: ${oldf}\n# New: ${newf}\n\n" + + local found_change=0 + + # Changed & Removed + for k in "${!OLD[@]}"; do + if [[ -v NEW["$k"] ]]; then + if [[ "${OLD[$k]}" != "${NEW[$k]}" ]]; then + out+="~ ${k}\n - old: ${OLD[$k]}\n + new: ${NEW[$k]}\n" + found_change=1 + fi + else + out+="- ${k}\n - old: ${OLD[$k]}\n" + found_change=1 + fi + done + + # Added + for k in "${!NEW[@]}"; do + if [[ ! -v OLD["$k"] ]]; then + out+="+ ${k}\n + new: ${NEW[$k]}\n" + found_change=1 + fi + done + + if [[ $found_change -eq 0 ]]; then + out+="(No differences)\n" + fi + + printf "%b" "$out" +} + +# Build a temporary .vars file from current advanced settings +_build_current_app_vars_tmp() { + tmpf="$(mktemp /tmp/${NSAPP:-app}.vars.new.XXXXXX)" + + # NET/GW + _net="${NET:-}" + _gate="" + case "${GATE:-}" in + ,gw=*) _gate=$(echo "$GATE" | sed 's/^,gw=//') ;; + esac + + # IPv6 + _ipv6_method="${IPV6_METHOD:-auto}" + _ipv6_static="" + _ipv6_gateway="" + if [ "$_ipv6_method" = "static" ]; then + _ipv6_static="${IPV6_ADDR:-}" + _ipv6_gateway="${IPV6_GATE:-}" + fi + + # MTU/VLAN/MAC + _mtu="" + _vlan="" + _mac="" + case "${MTU:-}" in + ,mtu=*) _mtu=$(echo "$MTU" | sed 's/^,mtu=//') ;; + esac + case "${VLAN:-}" in + ,tag=*) _vlan=$(echo "$VLAN" | sed 's/^,tag=//') ;; + esac + case "${MAC:-}" in + ,hwaddr=*) _mac=$(echo "$MAC" | sed 's/^,hwaddr=//') ;; + esac + + # DNS / Searchdomain + _ns="" + _searchdomain="" + case "${NS:-}" in + -nameserver=*) _ns=$(echo "$NS" | sed 's/^-nameserver=//') ;; + esac + case "${SD:-}" in + -searchdomain=*) _searchdomain=$(echo "$SD" | sed 's/^-searchdomain=//') ;; + esac + + # SSH / APT / Features + _ssh="${SSH:-no}" + _ssh_auth="${SSH_AUTHORIZED_KEY:-}" + _apt_cacher="${APT_CACHER:-}" + _apt_cacher_ip="${APT_CACHER_IP:-}" + _fuse="${ENABLE_FUSE:-no}" + _tun="${ENABLE_TUN:-no}" + _tags="${TAGS:-}" + _verbose="${VERBOSE:-no}" + + # Type / Resources / Identity + _unpriv="${CT_TYPE:-1}" + _cpu="${CORE_COUNT:-1}" + _ram="${RAM_SIZE:-1024}" + _disk="${DISK_SIZE:-4}" + _hostname="${HN:-$NSAPP}" + + # Storage + _tpl_storage="${TEMPLATE_STORAGE:-${var_template_storage:-}}" + _ct_storage="${CONTAINER_STORAGE:-${var_container_storage:-}}" + + { + echo "# App-specific defaults for ${APP} (${NSAPP})" + echo "# Generated on $(date -u '+%Y-%m-%dT%H:%M:%SZ')" + echo + + echo "var_unprivileged=$(_sanitize_value "$_unpriv")" + echo "var_cpu=$(_sanitize_value "$_cpu")" + echo "var_ram=$(_sanitize_value "$_ram")" + echo "var_disk=$(_sanitize_value "$_disk")" + + [ -n "${BRG:-}" ] && echo "var_brg=$(_sanitize_value "$BRG")" + [ -n "$_net" ] && echo "var_net=$(_sanitize_value "$_net")" + [ -n "$_gate" ] && echo "var_gateway=$(_sanitize_value "$_gate")" + [ -n "$_mtu" ] && echo "var_mtu=$(_sanitize_value "$_mtu")" + [ -n "$_vlan" ] && echo "var_vlan=$(_sanitize_value "$_vlan")" + [ -n "$_mac" ] && echo "var_mac=$(_sanitize_value "$_mac")" + [ -n "$_ns" ] && echo "var_ns=$(_sanitize_value "$_ns")" + + [ -n "$_ipv6_method" ] && echo "var_ipv6_method=$(_sanitize_value "$_ipv6_method")" + [ -n "$_ipv6_static" ] && echo "var_ipv6_static=$(_sanitize_value "$_ipv6_static")" + + [ -n "$_ssh" ] && echo "var_ssh=$(_sanitize_value "$_ssh")" + [ -n "$_ssh_auth" ] && echo "var_ssh_authorized_key=$(_sanitize_value "$_ssh_auth")" + + [ -n "$_apt_cacher" ] && echo "var_apt_cacher=$(_sanitize_value "$_apt_cacher")" + [ -n "$_apt_cacher_ip" ] && echo "var_apt_cacher_ip=$(_sanitize_value "$_apt_cacher_ip")" + + [ -n "$_fuse" ] && echo "var_fuse=$(_sanitize_value "$_fuse")" + [ -n "$_tun" ] && echo "var_tun=$(_sanitize_value "$_tun")" + [ -n "$_tags" ] && echo "var_tags=$(_sanitize_value "$_tags")" + [ -n "$_verbose" ] && echo "var_verbose=$(_sanitize_value "$_verbose")" + + [ -n "$_hostname" ] && echo "var_hostname=$(_sanitize_value "$_hostname")" + [ -n "$_searchdomain" ] && echo "var_searchdomain=$(_sanitize_value "$_searchdomain")" + + [ -n "$_tpl_storage" ] && echo "var_template_storage=$(_sanitize_value "$_tpl_storage")" + [ -n "$_ct_storage" ] && echo "var_container_storage=$(_sanitize_value "$_ct_storage")" + } >"$tmpf" + + echo "$tmpf" +} + +# ------------------------------------------------------------------------------ +# maybe_offer_save_app_defaults() +# +# - Called after advanced_settings() +# - Offers to save current values as app defaults if not existing +# - If file exists: shows diff and allows Update, Keep, View Diff, or Cancel +# ------------------------------------------------------------------------------ +maybe_offer_save_app_defaults() { + local app_vars_path + app_vars_path="$(get_app_defaults_path)" + + # always build from current settings + local new_tmp diff_tmp + new_tmp="$(_build_current_app_vars_tmp)" + diff_tmp="$(mktemp -p /tmp "${NSAPP:-app}.vars.diff.XXXXXX")" + + # 1) if no file → offer to create + if [[ ! -f "$app_vars_path" ]]; then + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --yesno "Save these advanced settings as defaults for ${APP}?\n\nThis will create:\n${app_vars_path}" 12 72; then + mkdir -p "$(dirname "$app_vars_path")" + install -m 0644 "$new_tmp" "$app_vars_path" + msg_ok "Saved app defaults: ${app_vars_path}" + fi + rm -f "$new_tmp" "$diff_tmp" + return 0 + fi + + # 2) if file exists → build diff + _build_vars_diff "$app_vars_path" "$new_tmp" >"$diff_tmp" + + # if no differences → do nothing + if grep -q "^(No differences)$" "$diff_tmp"; then + rm -f "$new_tmp" "$diff_tmp" + return 0 + fi + + # 3) if file exists → show menu with default selection "Update Defaults" + local app_vars_file + app_vars_file="$(basename "$app_vars_path")" + + while true; do + local sel + sel="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "APP DEFAULTS – ${APP}" \ + --menu "Differences detected. What do you want to do?" 20 78 10 \ + "Update Defaults" "Write new values to ${app_vars_file}" \ + "Keep Current" "Keep existing defaults (no changes)" \ + "View Diff" "Show a detailed diff" \ + "Cancel" "Abort without changes" \ + --default-item "Update Defaults" \ + 3>&1 1>&2 2>&3)" || { sel="Cancel"; } + + case "$sel" in + "Update Defaults") + install -m 0644 "$new_tmp" "$app_vars_path" + msg_ok "Updated app defaults: ${app_vars_path}" + break + ;; + "Keep Current") + msg_info "Keeping current app defaults: ${app_vars_path}" + break + ;; + "View Diff") + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "Diff – ${APP}" \ + --scrolltext --textbox "$diff_tmp" 25 100 + ;; + "Cancel" | *) + msg_info "Canceled. No changes to app defaults." + break + ;; + esac + done + + rm -f "$new_tmp" "$diff_tmp" +} + +ensure_storage_selection_for_vars_file() { + local vf="$1" + + # Read stored values (if any) + local tpl ct + tpl=$(grep -E '^var_template_storage=' "$vf" | cut -d= -f2-) + ct=$(grep -E '^var_container_storage=' "$vf" | cut -d= -f2-) + + if [[ -n "$tpl" && -n "$ct" ]]; then + TEMPLATE_STORAGE="$tpl" + CONTAINER_STORAGE="$ct" + return 0 + fi + + choose_and_set_storage_for_file "$vf" template + choose_and_set_storage_for_file "$vf" container + + msg_ok "Storage configuration saved to $(basename "$vf")" +} + +diagnostics_menu() { + if [ "${DIAGNOSTICS:-no}" = "yes" ]; then + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "DIAGNOSTIC SETTINGS" \ + --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ + --yes-button "No" --no-button "Back"; then + DIAGNOSTICS="no" + sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=no/' /usr/local/community-scripts/diagnostics + whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 + fi + else + if whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "DIAGNOSTIC SETTINGS" \ + --yesno "Send Diagnostics?\n\nCurrent: ${DIAGNOSTICS}" 10 58 \ + --yes-button "Yes" --no-button "Back"; then + DIAGNOSTICS="yes" + sed -i 's/^DIAGNOSTICS=.*/DIAGNOSTICS=yes/' /usr/local/community-scripts/diagnostics + whiptail --msgbox "Diagnostics set to ${DIAGNOSTICS}." 8 58 + fi + fi +} + +ensure_global_default_vars_file() { + local vars_path="/usr/local/community-scripts/default.vars" + if [[ ! -f "$vars_path" ]]; then + mkdir -p "$(dirname "$vars_path")" + touch "$vars_path" + fi + echo "$vars_path" +} + +# ------------------------------------------------------------------------------ +# install_script() +# +# - Main entrypoint for installation mode +# - Runs safety checks (pve_check, root_check, maxkeys_check, diagnostics_check) +# - Builds interactive menu (Default, Verbose, Advanced, My Defaults, App Defaults, Diagnostics, Storage, Exit) +# - Applies chosen settings and triggers container build +# ------------------------------------------------------------------------------ +install_script() { + pve_check + shell_check + root_check + arch_check + ssh_check + maxkeys_check + diagnostics_check + + if systemctl is-active -q ping-instances.service; then + systemctl -q stop ping-instances.service + fi + + NEXTID=$(pvesh get /cluster/nextid) + timezone=$(cat /etc/timezone) + + # Show APP Header + header_info + + # --- Support CLI argument as direct preset (default, advanced, …) --- + CHOICE="${mode:-${1:-}}" + + # If no CLI argument → show whiptail menu + # Build menu dynamically based on available options + local appdefaults_option="" + local settings_option="" + local menu_items=( + "1" "Default Install" + "2" "Advanced Install" + "3" "My Defaults" + ) + + if [ -f "$(get_app_defaults_path)" ]; then + appdefaults_option="4" + menu_items+=("4" "App Defaults for ${APP}") + settings_option="5" + menu_items+=("5" "Settings") + else + settings_option="4" + menu_items+=("4" "Settings") + fi + + if [ -z "$CHOICE" ]; then + + TMP_CHOICE=$(whiptail \ + --backtitle "Proxmox VE Helper Scripts" \ + --title "Community-Scripts Options" \ + --ok-button "Select" --cancel-button "Exit Script" \ + --notags \ + --menu "\nChoose an option:\n Use TAB or Arrow keys to navigate, ENTER to select.\n" \ + 20 60 9 \ + "${menu_items[@]}" \ + --default-item "1" \ + 3>&1 1>&2 2>&3) || exit_script + CHOICE="$TMP_CHOICE" + fi + + APPDEFAULTS_OPTION="$appdefaults_option" + SETTINGS_OPTION="$settings_option" + + # --- Main case --- + local defaults_target="" + local run_maybe_offer="no" + case "$CHOICE" in + 1 | default | DEFAULT) + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings on node $PVEHOST_NAME${CL}" + VERBOSE="no" + METHOD="default" + base_settings "$VERBOSE" + echo_default + defaults_target="$(ensure_global_default_vars_file)" + ;; + 2 | advanced | ADVANCED) + header_info + + echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Install on node $PVEHOST_NAME${CL}" + echo -e "${INFO}${BOLD}${DGN}PVE Version ${PVEVERSION} (Kernel: ${KERNEL_VERSION})${CL}" + METHOD="advanced" + base_settings + advanced_settings + defaults_target="$(ensure_global_default_vars_file)" + run_maybe_offer="yes" + ;; + 3 | mydefaults | MYDEFAULTS) + default_var_settings || { + msg_error "Failed to apply default.vars" + exit 1 + } + defaults_target="/usr/local/community-scripts/default.vars" + ;; + "$APPDEFAULTS_OPTION" | appdefaults | APPDEFAULTS) + if [ -f "$(get_app_defaults_path)" ]; then + header_info + echo -e "${DEFAULT}${BOLD}${BL}Using App Defaults for ${APP} on node $PVEHOST_NAME${CL}" + METHOD="appdefaults" + base_settings + _load_vars_file "$(get_app_defaults_path)" + echo_default + defaults_target="$(get_app_defaults_path)" + else + msg_error "No App Defaults available for ${APP}" + exit 1 + fi + ;; + "$SETTINGS_OPTION" | settings | SETTINGS) + settings_menu + defaults_target="" + ;; + *) + echo -e "${CROSS}${RD}Invalid option: $CHOICE${CL}" + exit 1 + ;; + esac + + if [[ -n "$defaults_target" ]]; then + ensure_storage_selection_for_vars_file "$defaults_target" + fi + + if [[ "$run_maybe_offer" == "yes" ]]; then + maybe_offer_save_app_defaults + fi +} + +edit_default_storage() { + local vf="/usr/local/community-scripts/default.vars" + + # Ensure file exists + if [[ ! -f "$vf" ]]; then + mkdir -p "$(dirname "$vf")" + touch "$vf" + fi + + # Let ensure_storage_selection_for_vars_file handle everything + ensure_storage_selection_for_vars_file "$vf" +} + +settings_menu() { + while true; do + local settings_items=( + "1" "Manage API-Diagnostic Setting" + "2" "Edit Default.vars" + "3" "Edit Default Storage" + ) + if [ -f "$(get_app_defaults_path)" ]; then + settings_items+=("4" "Edit App.vars for ${APP}") + settings_items+=("5" "Exit") + else + settings_items+=("4" "Exit") + fi + + local choice + choice=$(whiptail --backtitle "Proxmox VE Helper Scripts" \ + --title "Community-Scripts SETTINGS Menu" \ + --ok-button "OK" --cancel-button "Back" \ + --menu "\n\nChoose a settings option:\n\nUse TAB or Arrow keys to navigate, ENTER to select." 20 60 9 \ + "${settings_items[@]}" \ + 3>&1 1>&2 2>&3) || break + + case "$choice" in + 1) diagnostics_menu ;; + 2) ${EDITOR:-nano} /usr/local/community-scripts/default.vars ;; + 3) edit_default_storage ;; + 4) + if [ -f "$(get_app_defaults_path)" ]; then + ${EDITOR:-nano} "$(get_app_defaults_path)" + else + exit_script + fi + ;; + 5) exit_script ;; + esac + done +} + +# ===== Unified storage selection & writing to vars files ===== +_write_storage_to_vars() { + # $1 = vars_file, $2 = key (var_container_storage / var_template_storage), $3 = value + local vf="$1" key="$2" val="$3" + # remove uncommented and commented versions to avoid duplicates + sed -i "/^[#[:space:]]*${key}=/d" "$vf" + echo "${key}=${val}" >>"$vf" +} + +choose_and_set_storage_for_file() { + # $1 = vars_file, $2 = class ('container'|'template') + local vf="$1" class="$2" key="" current="" + case "$class" in + container) key="var_container_storage" ;; + template) key="var_template_storage" ;; + *) + msg_error "Unknown storage class: $class" + return 1 + ;; + esac + + current=$(awk -F= -v k="^${key}=" '$0 ~ k {print $2; exit}' "$vf") + + # If only one storage exists for the content type, auto-pick. Else always ask (your wish #4). + local content="rootdir" + [[ "$class" == "template" ]] && content="vztmpl" + local count + count=$(pvesm status -content "$content" | awk 'NR>1{print $1}' | wc -l) + + if [[ "$count" -eq 1 ]]; then + STORAGE_RESULT=$(pvesm status -content "$content" | awk 'NR>1{print $1; exit}') + STORAGE_INFO="" + else + # If the current value is preselectable, we could show it, but per your requirement we always offer selection + select_storage "$class" || return 1 + fi + + _write_storage_to_vars "$vf" "$key" "$STORAGE_RESULT" + + # Keep environment in sync for later steps (e.g. app-default save) + if [[ "$class" == "container" ]]; then + export var_container_storage="$STORAGE_RESULT" + export CONTAINER_STORAGE="$STORAGE_RESULT" + else + export var_template_storage="$STORAGE_RESULT" + export TEMPLATE_STORAGE="$STORAGE_RESULT" + fi + + msg_ok "Updated ${key} → ${STORAGE_RESULT}" +} + +# ------------------------------------------------------------------------------ +# check_container_resources() +# +# - Compares host RAM/CPU with required values +# - Warns if under-provisioned and asks user to continue or abort +# ------------------------------------------------------------------------------ +check_container_resources() { + current_ram=$(free -m | awk 'NR==2{print $2}') + current_cpu=$(nproc) + + if [[ "$current_ram" -lt "$var_ram" ]] || [[ "$current_cpu" -lt "$var_cpu" ]]; then + echo -e "\n${INFO}${HOLD} ${GN}Required: ${var_cpu} CPU, ${var_ram}MB RAM ${CL}| ${RD}Current: ${current_cpu} CPU, ${current_ram}MB RAM${CL}" + echo -e "${YWB}Please ensure that the ${APP} LXC is configured with at least ${var_cpu} vCPU and ${var_ram} MB RAM for the build process.${CL}\n" + echo -ne "${INFO}${HOLD} May cause data loss! ${INFO} Continue update with under-provisioned LXC? " + read -r prompt + if [[ ! ${prompt,,} =~ ^(yes)$ ]]; then + echo -e "${CROSS}${HOLD} ${YWB}Exiting based on user input.${CL}" + exit 1 + fi + else + echo -e "" + fi +} + +# ------------------------------------------------------------------------------ +# check_container_storage() +# +# - Checks /boot partition usage +# - Warns if usage >80% and asks user confirmation before proceeding +# ------------------------------------------------------------------------------ +check_container_storage() { + total_size=$(df /boot --output=size | tail -n 1) + local used_size=$(df /boot --output=used | tail -n 1) + usage=$((100 * used_size / total_size)) + if ((usage > 80)); then + echo -e "${INFO}${HOLD} ${YWB}Warning: Storage is dangerously low (${usage}%).${CL}" + echo -ne "Continue anyway? " + read -r prompt + if [[ ! ${prompt,,} =~ ^(y|yes)$ ]]; then + echo -e "${CROSS}${HOLD}${YWB}Exiting based on user input.${CL}" + exit 1 + fi + fi +} + +# ------------------------------------------------------------------------------ +# ssh_extract_keys_from_file() +# +# - Extracts valid SSH public keys from given file +# - Supports RSA, Ed25519, ECDSA and filters out comments/invalid lines +# ------------------------------------------------------------------------------ +ssh_extract_keys_from_file() { + local f="$1" + [[ -r "$f" ]] || return 0 + tr -d '\r' <"$f" | awk ' + /^[[:space:]]*#/ {next} + /^[[:space:]]*$/ {next} + # nackt: typ base64 [comment] + /^(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/ {print; next} + # mit Optionen: finde ab erstem Key-Typ + { + match($0, /(ssh-(rsa|ed25519)|ecdsa-sha2-nistp256|sk-(ssh-ed25519|ecdsa-sha2-nistp256))[[:space:]]+/) + if (RSTART>0) { print substr($0, RSTART) } + } + ' +} + +# ------------------------------------------------------------------------------ +# ssh_build_choices_from_files() +# +# - Builds interactive whiptail checklist of available SSH keys +# - Generates fingerprint, type and comment for each key +# ------------------------------------------------------------------------------ +ssh_build_choices_from_files() { + local -a files=("$@") + CHOICES=() + COUNT=0 + MAPFILE="$(mktemp)" + local id key typ fp cmt base ln=0 + + for f in "${files[@]}"; do + [[ -f "$f" && -r "$f" ]] || continue + base="$(basename -- "$f")" + case "$base" in + known_hosts | known_hosts.* | config) continue ;; + id_*) [[ "$f" != *.pub ]] && continue ;; + esac + + # map every key in file + while IFS= read -r key; do + [[ -n "$key" ]] || continue + + typ="" + fp="" + cmt="" + # Only the pure key part (without options) is already included in ‘key’. + read -r _typ _b64 _cmt <<<"$key" + typ="${_typ:-key}" + cmt="${_cmt:-}" + # Fingerprint via ssh-keygen (if available) + if command -v ssh-keygen >/dev/null 2>&1; then + fp="$(printf '%s\n' "$key" | ssh-keygen -lf - 2>/dev/null | awk '{print $2}')" + fi + # Label shorten + [[ ${#cmt} -gt 40 ]] && cmt="${cmt:0:37}..." + + ln=$((ln + 1)) + COUNT=$((COUNT + 1)) + id="K${COUNT}" + echo "${id}|${key}" >>"$MAPFILE" + CHOICES+=("$id" "[$typ] ${fp:+$fp }${cmt:+$cmt }— ${base}" "OFF") + done < <(ssh_extract_keys_from_file "$f") + done +} + +# ------------------------------------------------------------------------------ +# ssh_discover_default_files() +# +# - Scans standard paths for SSH keys +# - Includes ~/.ssh/*.pub, /etc/ssh/authorized_keys, etc. +# ------------------------------------------------------------------------------ +ssh_discover_default_files() { + local -a cand=() + shopt -s nullglob + cand+=(/root/.ssh/authorized_keys /root/.ssh/authorized_keys2) + cand+=(/root/.ssh/*.pub) + cand+=(/etc/ssh/authorized_keys /etc/ssh/authorized_keys.d/*) + shopt -u nullglob + printf '%s\0' "${cand[@]}" +} + +configure_ssh_settings() { + SSH_KEYS_FILE="$(mktemp)" + : >"$SSH_KEYS_FILE" + + IFS=$'\0' read -r -d '' -a _def_files < <(ssh_discover_default_files && printf '\0') + ssh_build_choices_from_files "${_def_files[@]}" + local default_key_count="$COUNT" + + local ssh_key_mode + if [[ "$default_key_count" -gt 0 ]]; then + ssh_key_mode=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ + "Provision SSH keys for root:" 14 72 4 \ + "found" "Select from detected keys (${default_key_count})" \ + "manual" "Paste a single public key" \ + "folder" "Scan another folder (path or glob)" \ + "none" "No keys" 3>&1 1>&2 2>&3) || exit_script + else + ssh_key_mode=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SSH KEY SOURCE" --menu \ + "No host keys detected; choose manual/none:" 12 72 2 \ + "manual" "Paste a single public key" \ + "none" "No keys" 3>&1 1>&2 2>&3) || exit_script + fi + + case "$ssh_key_mode" in + found) + local selection + selection=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT HOST KEYS" \ + --checklist "Select one or more keys to import:" 20 140 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script + for tag in $selection; do + tag="${tag%\"}" + tag="${tag#\"}" + local line + line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) + [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" + done + ;; + manual) + SSH_AUTHORIZED_KEY="$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Paste one SSH public key line (ssh-ed25519/ssh-rsa/...)" 10 72 --title "SSH Public Key" 3>&1 1>&2 2>&3)" + [[ -n "$SSH_AUTHORIZED_KEY" ]] && printf '%s\n' "$SSH_AUTHORIZED_KEY" >>"$SSH_KEYS_FILE" + ;; + folder) + local glob_path + glob_path=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --inputbox "Enter a folder or glob to scan (e.g. /root/.ssh/*.pub)" 10 72 --title "Scan Folder/Glob" 3>&1 1>&2 2>&3) + if [[ -n "$glob_path" ]]; then + shopt -s nullglob + read -r -a _scan_files <<<"$glob_path" + shopt -u nullglob + if [[ "${#_scan_files[@]}" -gt 0 ]]; then + ssh_build_choices_from_files "${_scan_files[@]}" + if [[ "$COUNT" -gt 0 ]]; then + local folder_selection + folder_selection=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "SELECT FOLDER KEYS" \ + --checklist "Select key(s) to import:" 20 78 10 "${CHOICES[@]}" 3>&1 1>&2 2>&3) || exit_script + for tag in $folder_selection; do + tag="${tag%\"}" + tag="${tag#\"}" + local line + line=$(grep -E "^${tag}\|" "$MAPFILE" | head -n1 | cut -d'|' -f2-) + [[ -n "$line" ]] && printf '%s\n' "$line" >>"$SSH_KEYS_FILE" + done + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "No keys found in: $glob_path" 8 60 + fi + else + whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --msgbox "Path/glob returned no files." 8 60 + fi + fi + ;; + none) + : + ;; + esac + + if [[ -s "$SSH_KEYS_FILE" ]]; then + sort -u -o "$SSH_KEYS_FILE" "$SSH_KEYS_FILE" + printf '\n' >>"$SSH_KEYS_FILE" + fi + + if [[ -s "$SSH_KEYS_FILE" || "$PW" == -password* ]]; then + if (whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --defaultno --title "SSH ACCESS" --yesno "Enable root SSH access?" 10 58); then + SSH="yes" + else + SSH="no" + fi + else + SSH="no" + fi +} + +# ------------------------------------------------------------------------------ +# start() +# +# - Entry point of script +# - On Proxmox host: calls install_script +# - In silent mode: runs update_script +# - Otherwise: shows update/setting menu +# ------------------------------------------------------------------------------ +start() { + source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/tools.func) + if command -v pveversion >/dev/null 2>&1; then + install_script || return 0 + return 0 + elif [ ! -z ${PHS_SILENT+x} ] && [[ "${PHS_SILENT}" == "1" ]]; then + VERBOSE="no" + set_std_mode + update_script + else + CHOICE=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" --title "${APP} LXC Update/Setting" --menu \ + "Support/Update functions for ${APP} LXC. Choose an option:" \ + 12 60 3 \ + "1" "YES (Silent Mode)" \ + "2" "YES (Verbose Mode)" \ + "3" "NO (Cancel Update)" --nocancel --default-item "1" 3>&1 1>&2 2>&3) + + case "$CHOICE" in + 1) + VERBOSE="no" + set_std_mode + ;; + 2) + VERBOSE="yes" + set_std_mode + ;; + 3) + clear + exit_script + exit + ;; + esac + update_script + fi +} + +# ------------------------------------------------------------------------------ +# build_container() +# +# - Creates and configures the LXC container +# - Builds network string and applies features (FUSE, TUN, VAAPI passthrough) +# - Starts container and waits for network connectivity +# - Installs base packages, SSH keys, and runs -install.sh +# ------------------------------------------------------------------------------ +build_container() { + # if [ "$VERBOSE" == "yes" ]; then set -x; fi + + NET_STRING="-net0 name=eth0,bridge=${BRG:-vmbr0}" + + # MAC + if [[ -n "$MAC" ]]; then + case "$MAC" in + ,hwaddr=*) NET_STRING+="$MAC" ;; + *) NET_STRING+=",hwaddr=$MAC" ;; + esac + fi + + # IP (immer zwingend, Standard dhcp) + NET_STRING+=",ip=${NET:-dhcp}" + + # Gateway + if [[ -n "$GATE" ]]; then + case "$GATE" in + ,gw=*) NET_STRING+="$GATE" ;; + *) NET_STRING+=",gw=$GATE" ;; + esac + fi + + # VLAN + if [[ -n "$VLAN" ]]; then + case "$VLAN" in + ,tag=*) NET_STRING+="$VLAN" ;; + *) NET_STRING+=",tag=$VLAN" ;; + esac + fi + + # MTU + if [[ -n "$MTU" ]]; then + case "$MTU" in + ,mtu=*) NET_STRING+="$MTU" ;; + *) NET_STRING+=",mtu=$MTU" ;; + esac + fi + + # IPv6 Handling + case "$IPV6_METHOD" in + auto) NET_STRING="$NET_STRING,ip6=auto" ;; + dhcp) NET_STRING="$NET_STRING,ip6=dhcp" ;; + static) + NET_STRING="$NET_STRING,ip6=$IPV6_ADDR" + [ -n "$IPV6_GATE" ] && NET_STRING="$NET_STRING,gw6=$IPV6_GATE" + ;; + none) ;; + esac + + if [ "$CT_TYPE" == "1" ]; then + FEATURES="keyctl=1,nesting=1" + else + FEATURES="nesting=1" + fi + + if [ "$ENABLE_FUSE" == "yes" ]; then + FEATURES="$FEATURES,fuse=1" + fi + + TEMP_DIR=$(mktemp -d) + pushd "$TEMP_DIR" >/dev/null + if [ "$var_os" == "alpine" ]; then + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/alpine-install.func)" + else + export FUNCTIONS_FILE_PATH="$(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/install.func)" + fi + export DIAGNOSTICS="$DIAGNOSTICS" + export RANDOM_UUID="$RANDOM_UUID" + export CACHER="$APT_CACHER" + export CACHER_IP="$APT_CACHER_IP" + export tz="$timezone" + export APPLICATION="$APP" + export app="$NSAPP" + export PASSWORD="$PW" + export VERBOSE="$VERBOSE" + export SSH_ROOT="${SSH}" + export SSH_AUTHORIZED_KEY + export CTID="$CT_ID" + export CTTYPE="$CT_TYPE" + export ENABLE_FUSE="$ENABLE_FUSE" + export ENABLE_TUN="$ENABLE_TUN" + export PCT_OSTYPE="$var_os" + export PCT_OSVERSION="$var_version" + export PCT_DISK_SIZE="$DISK_SIZE" + export PCT_OPTIONS=" + -features $FEATURES + -hostname $HN + -tags $TAGS + $SD + $NS + $NET_STRING + -onboot 1 + -cores $CORE_COUNT + -memory $RAM_SIZE + -unprivileged $CT_TYPE + $PW +" + export TEMPLATE_STORAGE="${var_template_storage:-}" + export CONTAINER_STORAGE="${var_container_storage:-}" + create_lxc_container || exit $? + + LXC_CONFIG="/etc/pve/lxc/${CTID}.conf" + + # ============================================================================ + # GPU/USB PASSTHROUGH CONFIGURATION + # ============================================================================ + + # List of applications that benefit from GPU acceleration + GPU_APPS=( + "immich" "channels" "emby" "ersatztv" "frigate" + "jellyfin" "plex" "scrypted" "tdarr" "unmanic" + "ollama" "fileflows" "open-webui" "tunarr" "debian" + "handbrake" "sunshine" "moonlight" "kodi" "stremio" + "viseron" + ) + + # Check if app needs GPU + is_gpu_app() { + local app="${1,,}" + for gpu_app in "${GPU_APPS[@]}"; do + [[ "$app" == "${gpu_app,,}" ]] && return 0 + done + return 1 + } + + # Detect all available GPU devices + detect_gpu_devices() { + INTEL_DEVICES=() + AMD_DEVICES=() + NVIDIA_DEVICES=() + + # Store PCI info to avoid multiple calls + local pci_vga_info=$(lspci -nn 2>/dev/null | grep -E "VGA|Display|3D") + + # Check for Intel GPU - look for Intel vendor ID [8086] + if echo "$pci_vga_info" | grep -q "\[8086:"; then + msg_info "Detected Intel GPU" + if [[ -d /dev/dri ]]; then + for d in /dev/dri/renderD* /dev/dri/card*; do + [[ -e "$d" ]] && INTEL_DEVICES+=("$d") + done + fi + fi + + # Check for AMD GPU - look for AMD vendor IDs [1002] (AMD/ATI) or [1022] (AMD) + if echo "$pci_vga_info" | grep -qE "\[1002:|\[1022:"; then + msg_info "Detected AMD GPU" + if [[ -d /dev/dri ]]; then + # Only add if not already claimed by Intel + if [[ ${#INTEL_DEVICES[@]} -eq 0 ]]; then + for d in /dev/dri/renderD* /dev/dri/card*; do + [[ -e "$d" ]] && AMD_DEVICES+=("$d") + done + fi + fi + fi + + # Check for NVIDIA GPU - look for NVIDIA vendor ID [10de] + if echo "$pci_vga_info" | grep -q "\[10de:"; then + msg_info "Detected NVIDIA GPU" + if ! check_nvidia_host_setup; then + msg_error "NVIDIA host setup incomplete. Skipping GPU passthrough." + msg_info "Fix NVIDIA drivers on host, then recreate container or passthrough manually." + return 0 + fi + + for d in /dev/nvidia* /dev/nvidiactl /dev/nvidia-modeset; do + [[ -e "$d" ]] && NVIDIA_DEVICES+=("$d") + done + + if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then + msg_warn "NVIDIA GPU detected but no /dev/nvidia* devices found" + msg_warn "Please install NVIDIA drivers on host: apt install nvidia-driver" + else + if [[ "$CT_TYPE" == "0" ]]; then + cat <>"$LXC_CONFIG" + # NVIDIA GPU Passthrough (privileged) + lxc.cgroup2.devices.allow: c 195:* rwm + lxc.cgroup2.devices.allow: c 243:* rwm + lxc.mount.entry: /dev/nvidia0 dev/nvidia0 none bind,optional,create=file + lxc.mount.entry: /dev/nvidiactl dev/nvidiactl none bind,optional,create=file + lxc.mount.entry: /dev/nvidia-uvm dev/nvidia-uvm none bind,optional,create=file + lxc.mount.entry: /dev/nvidia-uvm-tools dev/nvidia-uvm-tools none bind,optional,create=file +EOF + + if [[ -e /dev/dri/renderD128 ]]; then + echo "lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file" >>"$LXC_CONFIG" + fi + + export GPU_TYPE="NVIDIA" + export NVIDIA_DRIVER_VERSION=$(nvidia-smi --query-gpu=driver_version --format=csv,noheader 2>/dev/null | head -n1) + msg_ok "NVIDIA GPU passthrough configured (driver: ${NVIDIA_DRIVER_VERSION})" + else + msg_warn "NVIDIA passthrough only supported for privileged containers" + return 0 + fi + fi + fi + + # Debug output + msg_debug "Intel devices: ${INTEL_DEVICES[*]}" + msg_debug "AMD devices: ${AMD_DEVICES[*]}" + msg_debug "NVIDIA devices: ${NVIDIA_DEVICES[*]}" + } + + # Configure USB passthrough for privileged containers + configure_usb_passthrough() { + if [[ "$CT_TYPE" != "0" ]]; then + return 0 + fi + + msg_info "Configuring automatic USB passthrough (privileged container)" + cat <>"$LXC_CONFIG" +# Automatic USB passthrough (privileged container) +lxc.cgroup2.devices.allow: a +lxc.cap.drop: +lxc.cgroup2.devices.allow: c 188:* rwm +lxc.cgroup2.devices.allow: c 189:* rwm +lxc.mount.entry: /dev/serial/by-id dev/serial/by-id none bind,optional,create=dir +lxc.mount.entry: /dev/ttyUSB0 dev/ttyUSB0 none bind,optional,create=file +lxc.mount.entry: /dev/ttyUSB1 dev/ttyUSB1 none bind,optional,create=file +lxc.mount.entry: /dev/ttyACM0 dev/ttyACM0 none bind,optional,create=file +lxc.mount.entry: /dev/ttyACM1 dev/ttyACM1 none bind,optional,create=file +EOF + msg_ok "USB passthrough configured" + } + + # Configure GPU passthrough + configure_gpu_passthrough() { + # Skip if not a GPU app and not privileged + if [[ "$CT_TYPE" != "0" ]] && ! is_gpu_app "$APP"; then + return 0 + fi + + detect_gpu_devices + + # Count available GPU types + local gpu_count=0 + local available_gpus=() + + if [[ ${#INTEL_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("INTEL") + gpu_count=$((gpu_count + 1)) + fi + + if [[ ${#AMD_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("AMD") + gpu_count=$((gpu_count + 1)) + fi + + if [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then + available_gpus+=("NVIDIA") + gpu_count=$((gpu_count + 1)) + fi + + if [[ $gpu_count -eq 0 ]]; then + msg_info "No GPU devices found for passthrough" + return 0 + fi + + local selected_gpu="" + + if [[ $gpu_count -eq 1 ]]; then + # Automatic selection for single GPU + selected_gpu="${available_gpus[0]}" + msg_info "Automatically configuring ${selected_gpu} GPU passthrough" + else + # Multiple GPUs - ask user + echo -e "\n${INFO} Multiple GPU types detected:" + for gpu in "${available_gpus[@]}"; do + echo " - $gpu" + done + read -rp "Which GPU type to passthrough? (${available_gpus[*]}): " selected_gpu + selected_gpu="${selected_gpu^^}" + + # Validate selection + local valid=0 + for gpu in "${available_gpus[@]}"; do + [[ "$selected_gpu" == "$gpu" ]] && valid=1 + done + + if [[ $valid -eq 0 ]]; then + msg_warn "Invalid selection. Skipping GPU passthrough." + return 0 + fi + fi + + # Apply passthrough configuration based on selection + local dev_idx=0 + + case "$selected_gpu" in + INTEL | AMD) + local devices=() + [[ "$selected_gpu" == "INTEL" ]] && devices=("${INTEL_DEVICES[@]}") + [[ "$selected_gpu" == "AMD" ]] && devices=("${AMD_DEVICES[@]}") + + # For Proxmox WebUI visibility, add as dev0, dev1 etc. + for dev in "${devices[@]}"; do + if [[ "$CT_TYPE" == "0" ]]; then + # Privileged container - use dev entries for WebUI visibility + # Use initial GID 104 (render) for renderD*, 44 (video) for card* + if [[ "$dev" =~ renderD ]]; then + echo "dev${dev_idx}: $dev,gid=104" >>"$LXC_CONFIG" + else + echo "dev${dev_idx}: $dev,gid=44" >>"$LXC_CONFIG" + fi + dev_idx=$((dev_idx + 1)) + + # Also add cgroup allows for privileged containers + local major minor + major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") + minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") + + if [[ "$major" != "0" && "$minor" != "0" ]]; then + echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" + fi + else + # Unprivileged container + if [[ "$dev" =~ renderD ]]; then + echo "dev${dev_idx}: $dev,uid=0,gid=104" >>"$LXC_CONFIG" + else + echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG" + fi + dev_idx=$((dev_idx + 1)) + fi + done + + export GPU_TYPE="$selected_gpu" + msg_ok "${selected_gpu} GPU passthrough configured (${dev_idx} devices)" + ;; + + NVIDIA) + if [[ ${#NVIDIA_DEVICES[@]} -eq 0 ]]; then + msg_error "NVIDIA drivers not installed on host. Please install: apt install nvidia-driver" + return 1 + fi + + for dev in "${NVIDIA_DEVICES[@]}"; do + # NVIDIA devices typically need different handling + echo "dev${dev_idx}: $dev,uid=0,gid=44" >>"$LXC_CONFIG" + dev_idx=$((dev_idx + 1)) + + if [[ "$CT_TYPE" == "0" ]]; then + local major minor + major=$(stat -c '%t' "$dev" 2>/dev/null || echo "0") + minor=$(stat -c '%T' "$dev" 2>/dev/null || echo "0") + + if [[ "$major" != "0" && "$minor" != "0" ]]; then + echo "lxc.cgroup2.devices.allow: c $((0x$major)):$((0x$minor)) rwm" >>"$LXC_CONFIG" + fi + fi + done + + export GPU_TYPE="NVIDIA" + msg_ok "NVIDIA GPU passthrough configured (${dev_idx} devices)" + ;; + esac + } + + # Additional device passthrough + configure_additional_devices() { + # TUN device passthrough + if [ "$ENABLE_TUN" == "yes" ]; then + cat <>"$LXC_CONFIG" +lxc.cgroup2.devices.allow: c 10:200 rwm +lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file +EOF + fi + + # Coral TPU passthrough + if [[ -e /dev/apex_0 ]]; then + msg_info "Detected Coral TPU - configuring passthrough" + echo "lxc.mount.entry: /dev/apex_0 dev/apex_0 none bind,optional,create=file" >>"$LXC_CONFIG" + fi + } + + # Execute pre-start configurations + configure_usb_passthrough + configure_gpu_passthrough + configure_additional_devices + + # ============================================================================ + # START CONTAINER AND INSTALL USERLAND + # ============================================================================ + + msg_info "Starting LXC Container" + pct start "$CTID" + + # Wait for container to be running + for i in {1..10}; do + if pct status "$CTID" | grep -q "status: running"; then + msg_ok "Started LXC Container" + break + fi + sleep 1 + if [ "$i" -eq 10 ]; then + msg_error "LXC Container did not reach running state" + exit 1 + fi + done + + # Wait for network (skip for Alpine initially) + if [ "$var_os" != "alpine" ]; then + msg_info "Waiting for network in LXC container" + + # Wait for IP + for i in {1..20}; do + ip_in_lxc=$(pct exec "$CTID" -- ip -4 addr show dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) + [ -n "$ip_in_lxc" ] && break + sleep 1 + done + + if [ -z "$ip_in_lxc" ]; then + msg_error "No IP assigned to CT $CTID after 20s" + exit 1 + fi + + # Try to reach gateway + gw_ok=0 + for i in {1..10}; do + if pct exec "$CTID" -- ping -c1 -W1 "${GATEWAY:-8.8.8.8}" >/dev/null 2>&1; then + gw_ok=1 + break + fi + sleep 1 + done + + if [ "$gw_ok" -eq 1 ]; then + msg_ok "Network in LXC is reachable (IP $ip_in_lxc)" + else + msg_warn "Network reachable but gateway check failed" + fi + fi + # Function to get correct GID inside container + get_container_gid() { + local group="$1" + local gid=$(pct exec "$CTID" -- getent group "$group" 2>/dev/null | cut -d: -f3) + echo "${gid:-44}" # Default to 44 if not found + } + + fix_gpu_gids + + # Continue with standard container setup + msg_info "Customizing LXC Container" + + # # Install GPU userland if configured + # if [[ "${ENABLE_VAAPI:-0}" == "1" ]]; then + # install_gpu_userland "VAAPI" + # fi + + # if [[ "${ENABLE_NVIDIA:-0}" == "1" ]]; then + # install_gpu_userland "NVIDIA" + # fi + + # Continue with standard container setup + if [ "$var_os" == "alpine" ]; then + sleep 3 + pct exec "$CTID" -- /bin/sh -c 'cat </etc/apk/repositories +http://dl-cdn.alpinelinux.org/alpine/latest-stable/main +http://dl-cdn.alpinelinux.org/alpine/latest-stable/community +EOF' + pct exec "$CTID" -- ash -c "apk add bash newt curl openssh nano mc ncurses jq >/dev/null" + else + sleep 3 + pct exec "$CTID" -- bash -c "sed -i '/$LANG/ s/^# //' /etc/locale.gen" + pct exec "$CTID" -- bash -c "locale_line=\$(grep -v '^#' /etc/locale.gen | grep -E '^[a-zA-Z]' | awk '{print \$1}' | head -n 1) && \ + echo LANG=\$locale_line >/etc/default/locale && \ + locale-gen >/dev/null && \ + export LANG=\$locale_line" + + if [[ -z "${tz:-}" ]]; then + tz=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "Etc/UTC") + fi + + if pct exec "$CTID" -- test -e "/usr/share/zoneinfo/$tz"; then + pct exec "$CTID" -- bash -c "tz='$tz'; echo \"\$tz\" >/etc/timezone && ln -sf \"/usr/share/zoneinfo/\$tz\" /etc/localtime" + else + msg_warn "Skipping timezone setup – zone '$tz' not found in container" + fi + + pct exec "$CTID" -- bash -c "apt-get update >/dev/null && apt-get install -y sudo curl mc gnupg2 jq >/dev/null" || { + msg_error "apt-get base packages installation failed" + exit 1 + } + fi + + msg_ok "Customized LXC Container" + + # Verify GPU access if enabled + if [[ "${ENABLE_VAAPI:-0}" == "1" ]] && [ "$var_os" != "alpine" ]; then + pct exec "$CTID" -- bash -c "vainfo >/dev/null 2>&1" && + msg_ok "VAAPI verified working" || + msg_warn "VAAPI verification failed - may need additional configuration" + fi + + if [[ "${ENABLE_NVIDIA:-0}" == "1" ]] && [ "$var_os" != "alpine" ]; then + pct exec "$CTID" -- bash -c "nvidia-smi >/dev/null 2>&1" && + msg_ok "NVIDIA verified working" || + msg_warn "NVIDIA verification failed - may need additional configuration" + fi + + # Install SSH keys + install_ssh_keys_into_ct + + # Run application installer + if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)"; then + exit $? + fi +} + +destroy_lxc() { + if [[ -z "$CT_ID" ]]; then + msg_error "No CT_ID found. Nothing to remove." + return 1 + fi + + # Abbruch bei Ctrl-C / Ctrl-D / ESC + trap 'echo; msg_error "Aborted by user (SIGINT/SIGQUIT)"; return 130' INT QUIT + + local prompt + if ! read -rp "Remove this Container? " prompt; then + # read gibt != 0 zurück bei Ctrl-D/ESC + msg_error "Aborted input (Ctrl-D/ESC)" + return 130 + fi + + case "${prompt,,}" in + y | yes) + if pct stop "$CT_ID" &>/dev/null && pct destroy "$CT_ID" &>/dev/null; then + msg_ok "Removed Container $CT_ID" + else + msg_error "Failed to remove Container $CT_ID" + return 1 + fi + ;; + "" | n | no) + msg_info "Container was not removed." + ;; + *) + msg_warn "Invalid response. Container was not removed." + ;; + esac +} + +# ------------------------------------------------------------------------------ +# Storage discovery / selection helpers +# ------------------------------------------------------------------------------ +# ===== Storage discovery / selection helpers (ported from create_lxc.sh) ===== +resolve_storage_preselect() { + local class="$1" preselect="$2" required_content="" + case "$class" in + template) required_content="vztmpl" ;; + container) required_content="rootdir" ;; + *) return 1 ;; + esac + [[ -z "$preselect" ]] && return 1 + if ! pvesm status -content "$required_content" | awk 'NR>1{print $1}' | grep -qx -- "$preselect"; then + msg_warn "Preselected storage '${preselect}' does not support content '${required_content}' (or not found)" + return 1 + fi + + local line total used free + line="$(pvesm status | awk -v s="$preselect" 'NR>1 && $1==s {print $0}')" + if [[ -z "$line" ]]; then + STORAGE_INFO="n/a" + else + total="$(awk '{print $4}' <<<"$line")" + used="$(awk '{print $5}' <<<"$line")" + free="$(awk '{print $6}' <<<"$line")" + local total_h used_h free_h + if command -v numfmt >/dev/null 2>&1; then + total_h="$(numfmt --to=iec --suffix=B --format %.1f "$total" 2>/dev/null || echo "$total")" + used_h="$(numfmt --to=iec --suffix=B --format %.1f "$used" 2>/dev/null || echo "$used")" + free_h="$(numfmt --to=iec --suffix=B --format %.1f "$free" 2>/dev/null || echo "$free")" + STORAGE_INFO="Free: ${free_h} Used: ${used_h}" + else + STORAGE_INFO="Free: ${free} Used: ${used}" + fi + fi + STORAGE_RESULT="$preselect" + return 0 +} + +fix_gpu_gids() { + if [[ -z "${GPU_TYPE:-}" ]]; then + return 0 + fi + + msg_info "Detecting and setting correct GPU group IDs" + + # Ermittle die tatsächlichen GIDs aus dem Container + local video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") + local render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") + + # Fallbacks wenn Gruppen nicht existieren + if [[ -z "$video_gid" ]]; then + # Versuche die video Gruppe zu erstellen + pct exec "$CTID" -- sh -c "groupadd -r video 2>/dev/null || true" + video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") + [[ -z "$video_gid" ]] && video_gid="44" # Ultimate fallback + fi + + if [[ -z "$render_gid" ]]; then + # Versuche die render Gruppe zu erstellen + pct exec "$CTID" -- sh -c "groupadd -r render 2>/dev/null || true" + render_gid=$(pct exec "$CTID" -- sh -c "getent group render 2>/dev/null | cut -d: -f3") + [[ -z "$render_gid" ]] && render_gid="104" # Ultimate fallback + fi + + msg_info "Container GIDs detected - video:${video_gid}, render:${render_gid}" + + # Prüfe ob die GIDs von den Defaults abweichen + local need_update=0 + if [[ "$video_gid" != "44" ]] || [[ "$render_gid" != "104" ]]; then + need_update=1 + fi + + if [[ $need_update -eq 1 ]]; then + msg_info "Updating device GIDs in container config" + + # Stoppe Container für Config-Update + pct stop "$CTID" >/dev/null 2>&1 + + # Update die dev Einträge mit korrekten GIDs + # Backup der Config + cp "$LXC_CONFIG" "${LXC_CONFIG}.bak" + + # Parse und update jeden dev Eintrag + while IFS= read -r line; do + if [[ "$line" =~ ^dev[0-9]+: ]]; then + # Extract device path + local device_path=$(echo "$line" | sed -E 's/^dev[0-9]+: ([^,]+).*/\1/') + local dev_num=$(echo "$line" | sed -E 's/^(dev[0-9]+):.*/\1/') + + if [[ "$device_path" =~ renderD ]]; then + # RenderD device - use render GID + echo "${dev_num}: ${device_path},gid=${render_gid}" + elif [[ "$device_path" =~ card ]]; then + # Card device - use video GID + echo "${dev_num}: ${device_path},gid=${video_gid}" + else + # Keep original line + echo "$line" + fi + else + # Keep non-dev lines + echo "$line" + fi + done <"$LXC_CONFIG" >"${LXC_CONFIG}.new" + + mv "${LXC_CONFIG}.new" "$LXC_CONFIG" + + # Starte Container wieder + pct start "$CTID" >/dev/null 2>&1 + sleep 3 + + msg_ok "Device GIDs updated successfully" + else + msg_ok "Device GIDs are already correct" + fi + if [[ "$CT_TYPE" == "0" ]]; then + pct exec "$CTID" -- bash -c " + if [ -d /dev/dri ]; then + for dev in /dev/dri/*; do + if [ -e \"\$dev\" ]; then + if [[ \"\$dev\" =~ renderD ]]; then + chgrp ${render_gid} \"\$dev\" 2>/dev/null || true + else + chgrp ${video_gid} \"\$dev\" 2>/dev/null || true + fi + chmod 660 \"\$dev\" 2>/dev/null || true + fi + done + fi + " >/dev/null 2>&1 + fi +} + +# NVIDIA-spezific check on host +check_nvidia_host_setup() { + if ! command -v nvidia-smi >/dev/null 2>&1; then + msg_warn "NVIDIA GPU detected but nvidia-smi not found on host" + msg_warn "Please install NVIDIA drivers on host first." + #echo " 1. Download driver: wget https://us.download.nvidia.com/XFree86/Linux-x86_64/550.127.05/NVIDIA-Linux-x86_64-550.127.05.run" + #echo " 2. Install: ./NVIDIA-Linux-x86_64-550.127.05.run --dkms" + #echo " 3. Verify: nvidia-smi" + return 1 + fi + + # check if nvidia-smi works + if ! nvidia-smi >/dev/null 2>&1; then + msg_warn "nvidia-smi installed but not working. Driver issue?" + return 1 + fi + + return 0 +} + +check_storage_support() { + local CONTENT="$1" VALID=0 + while IFS= read -r line; do + local STORAGE_NAME + STORAGE_NAME=$(awk '{print $1}' <<<"$line") + [[ -n "$STORAGE_NAME" ]] && VALID=1 + done < <(pvesm status -content "$CONTENT" 2>/dev/null | awk 'NR>1') + [[ $VALID -eq 1 ]] +} + +select_storage() { + local CLASS=$1 CONTENT CONTENT_LABEL + case $CLASS in + container) + CONTENT='rootdir' + CONTENT_LABEL='Container' + ;; + template) + CONTENT='vztmpl' + CONTENT_LABEL='Container template' + ;; + iso) + CONTENT='iso' + CONTENT_LABEL='ISO image' + ;; + images) + CONTENT='images' + CONTENT_LABEL='VM Disk image' + ;; + backup) + CONTENT='backup' + CONTENT_LABEL='Backup' + ;; + snippets) + CONTENT='snippets' + CONTENT_LABEL='Snippets' + ;; + *) + msg_error "Invalid storage class '$CLASS'" + return 1 + ;; + esac + + declare -A STORAGE_MAP + local -a MENU=() + local COL_WIDTH=0 + + while read -r TAG TYPE _ TOTAL USED FREE _; do + [[ -n "$TAG" && -n "$TYPE" ]] || continue + local DISPLAY="${TAG} (${TYPE})" + local USED_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$USED") + local FREE_FMT=$(numfmt --to=iec --from-unit=K --format %.1f <<<"$FREE") + local INFO="Free: ${FREE_FMT}B Used: ${USED_FMT}B" + STORAGE_MAP["$DISPLAY"]="$TAG" + MENU+=("$DISPLAY" "$INFO" "OFF") + ((${#DISPLAY} > COL_WIDTH)) && COL_WIDTH=${#DISPLAY} + done < <(pvesm status -content "$CONTENT" | awk 'NR>1') + + if [[ ${#MENU[@]} -eq 0 ]]; then + msg_error "No storage found for content type '$CONTENT'." + return 2 + fi + + if [[ $((${#MENU[@]} / 3)) -eq 1 ]]; then + STORAGE_RESULT="${STORAGE_MAP[${MENU[0]}]}" + STORAGE_INFO="${MENU[1]}" + return 0 + fi + + local WIDTH=$((COL_WIDTH + 42)) + while true; do + local DISPLAY_SELECTED + DISPLAY_SELECTED=$(whiptail --backtitle "[dev] Proxmox VE Helper Scripts" \ + --title "Storage Pools" \ + --radiolist "Which storage pool for ${CONTENT_LABEL,,}?\n(Spacebar to select)" \ + 16 "$WIDTH" 6 "${MENU[@]}" 3>&1 1>&2 2>&3) || { exit_script; } + + DISPLAY_SELECTED=$(sed 's/[[:space:]]*$//' <<<"$DISPLAY_SELECTED") + if [[ -z "$DISPLAY_SELECTED" || -z "${STORAGE_MAP[$DISPLAY_SELECTED]+_}" ]]; then + whiptail --msgbox "No valid storage selected. Please try again." 8 58 + continue + fi + STORAGE_RESULT="${STORAGE_MAP[$DISPLAY_SELECTED]}" + for ((i = 0; i < ${#MENU[@]}; i += 3)); do + if [[ "${MENU[$i]}" == "$DISPLAY_SELECTED" ]]; then + STORAGE_INFO="${MENU[$i + 1]}" + break + fi + done + return 0 + done +} + +create_lxc_container() { + # ------------------------------------------------------------------------------ + # Optional verbose mode (debug tracing) + # ------------------------------------------------------------------------------ + if [[ "${CREATE_LXC_VERBOSE:-no}" == "yes" ]]; then set -x; fi + + # ------------------------------------------------------------------------------ + # Helpers (dynamic versioning / template parsing) + # ------------------------------------------------------------------------------ + pkg_ver() { dpkg-query -W -f='${Version}\n' "$1" 2>/dev/null || echo ""; } + pkg_cand() { apt-cache policy "$1" 2>/dev/null | awk '/Candidate:/ {print $2}'; } + + ver_ge() { dpkg --compare-versions "$1" ge "$2"; } + ver_gt() { dpkg --compare-versions "$1" gt "$2"; } + ver_lt() { dpkg --compare-versions "$1" lt "$2"; } + + # Extract Debian OS minor from template name: debian-13-standard_13.1-1_amd64.tar.zst => "13.1" + parse_template_osver() { sed -n 's/.*_\([0-9][0-9]*\(\.[0-9]\+\)\?\)-.*/\1/p' <<<"$1"; } + + # Offer upgrade for pve-container/lxc-pve if candidate > installed; optional auto-retry pct create + # Returns: + # 0 = no upgrade needed + # 1 = upgraded (and if do_retry=yes and retry succeeded, creation done) + # 2 = user declined + # 3 = upgrade attempted but failed OR retry failed + offer_lxc_stack_upgrade_and_maybe_retry() { + local do_retry="${1:-no}" # yes|no + local _pvec_i _pvec_c _lxcp_i _lxcp_c need=0 + + _pvec_i="$(pkg_ver pve-container)" + _lxcp_i="$(pkg_ver lxc-pve)" + _pvec_c="$(pkg_cand pve-container)" + _lxcp_c="$(pkg_cand lxc-pve)" + + if [[ -n "$_pvec_c" && "$_pvec_c" != "none" ]]; then + ver_gt "$_pvec_c" "${_pvec_i:-0}" && need=1 + fi + if [[ -n "$_lxcp_c" && "$_lxcp_c" != "none" ]]; then + ver_gt "$_lxcp_c" "${_lxcp_i:-0}" && need=1 + fi + if [[ $need -eq 0 ]]; then + msg_debug "No newer candidate for pve-container/lxc-pve (installed=$_pvec_i/$_lxcp_i, cand=$_pvec_c/$_lxcp_c)" + return 0 + fi + + echo + echo "An update for the Proxmox LXC stack is available:" + echo " pve-container: installed=${_pvec_i:-n/a} candidate=${_pvec_c:-n/a}" + echo " lxc-pve : installed=${_lxcp_i:-n/a} candidate=${_lxcp_c:-n/a}" + echo + read -rp "Do you want to upgrade now? [y/N] " _ans + case "${_ans,,}" in + y | yes) + msg_info "Upgrading Proxmox LXC stack (pve-container, lxc-pve)" + if apt-get update -qq >/dev/null && apt-get install -y --only-upgrade pve-container lxc-pve >/dev/null; then + msg_ok "LXC stack upgraded." + if [[ "$do_retry" == "yes" ]]; then + msg_info "Retrying container creation after upgrade" + if pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then + msg_ok "Container created successfully after upgrade." + return 0 + else + msg_error "pct create still failed after upgrade. See $LOGFILE" + return 3 + fi + fi + return 1 + else + msg_error "Upgrade failed. Please check APT output." + return 3 + fi + ;; + *) return 2 ;; + esac + } + + # ------------------------------------------------------------------------------ + # Required input variables + # ------------------------------------------------------------------------------ + [[ "${CTID:-}" ]] || { + msg_error "You need to set 'CTID' variable." + exit 203 + } + [[ "${PCT_OSTYPE:-}" ]] || { + msg_error "You need to set 'PCT_OSTYPE' variable." + exit 204 + } + + msg_debug "CTID=$CTID" + msg_debug "PCT_OSTYPE=$PCT_OSTYPE" + msg_debug "PCT_OSVERSION=${PCT_OSVERSION:-default}" + + # ID checks + [[ "$CTID" -ge 100 ]] || { + msg_error "ID cannot be less than 100." + exit 205 + } + if qm status "$CTID" &>/dev/null || pct status "$CTID" &>/dev/null; then + echo -e "ID '$CTID' is already in use." + unset CTID + msg_error "Cannot use ID that is already in use." + exit 206 + fi + + # Storage capability check + check_storage_support "rootdir" || { + msg_error "No valid storage found for 'rootdir' [Container]" + exit 1 + } + check_storage_support "vztmpl" || { + msg_error "No valid storage found for 'vztmpl' [Template]" + exit 1 + } + + # Template storage selection + if resolve_storage_preselect template "${TEMPLATE_STORAGE:-}"; then + TEMPLATE_STORAGE="$STORAGE_RESULT" + TEMPLATE_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${TEMPLATE_STORAGE}${CL} (${TEMPLATE_STORAGE_INFO}) [Template]" + else + while true; do + if [[ -z "${var_template_storage:-}" ]]; then + if select_storage template; then + TEMPLATE_STORAGE="$STORAGE_RESULT" + TEMPLATE_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${TEMPLATE_STORAGE}${CL} (${TEMPLATE_STORAGE_INFO}) [Template]" + break + fi + fi + done + fi + + # Container storage selection + if resolve_storage_preselect container "${CONTAINER_STORAGE:-}"; then + CONTAINER_STORAGE="$STORAGE_RESULT" + CONTAINER_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${CONTAINER_STORAGE}${CL} (${CONTAINER_STORAGE_INFO}) [Container]" + else + if [[ -z "${var_container_storage:-}" ]]; then + if select_storage container; then + CONTAINER_STORAGE="$STORAGE_RESULT" + CONTAINER_STORAGE_INFO="$STORAGE_INFO" + msg_ok "Storage ${BL}${CONTAINER_STORAGE}${CL} (${CONTAINER_STORAGE_INFO}) [Container]" + fi + fi + fi + + # Validate content types + msg_info "Validating content types of storage '$CONTAINER_STORAGE'" + STORAGE_CONTENT=$(grep -A4 -E "^(zfspool|dir|lvmthin|lvm): $CONTAINER_STORAGE" /etc/pve/storage.cfg | grep content | awk '{$1=""; print $0}' | xargs) + msg_debug "Storage '$CONTAINER_STORAGE' has content types: $STORAGE_CONTENT" + grep -qw "rootdir" <<<"$STORAGE_CONTENT" || { + msg_error "Storage '$CONTAINER_STORAGE' does not support 'rootdir'. Cannot create LXC." + exit 217 + } + $STD msg_ok "Storage '$CONTAINER_STORAGE' supports 'rootdir'" + + msg_info "Validating content types of template storage '$TEMPLATE_STORAGE'" + TEMPLATE_CONTENT=$(grep -A4 -E "^[^:]+: $TEMPLATE_STORAGE" /etc/pve/storage.cfg | grep content | awk '{$1=""; print $0}' | xargs) + msg_debug "Template storage '$TEMPLATE_STORAGE' has content types: $TEMPLATE_CONTENT" + if ! grep -qw "vztmpl" <<<"$TEMPLATE_CONTENT"; then + msg_warn "Template storage '$TEMPLATE_STORAGE' does not declare 'vztmpl'. This may cause pct create to fail." + else + $STD msg_ok "Template storage '$TEMPLATE_STORAGE' supports 'vztmpl'" + fi + + # Free space check + STORAGE_FREE=$(pvesm status | awk -v s="$CONTAINER_STORAGE" '$1 == s { print $6 }') + REQUIRED_KB=$((${PCT_DISK_SIZE:-8} * 1024 * 1024)) + [[ "$STORAGE_FREE" -ge "$REQUIRED_KB" ]] || { + msg_error "Not enough space on '$CONTAINER_STORAGE'. Needed: ${PCT_DISK_SIZE:-8}G." + exit 214 + } + + # Cluster quorum (if cluster) + if [[ -f /etc/pve/corosync.conf ]]; then + msg_info "Checking cluster quorum" + if ! pvecm status | awk -F':' '/^Quorate/ { exit ($2 ~ /Yes/) ? 0 : 1 }'; then + msg_error "Cluster is not quorate. Start all nodes or configure quorum device (QDevice)." + exit 210 + fi + msg_ok "Cluster is quorate" + fi + + # ------------------------------------------------------------------------------ + # Template discovery & validation + # ------------------------------------------------------------------------------ + TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" + case "$PCT_OSTYPE" in + debian | ubuntu) TEMPLATE_PATTERN="-standard_" ;; + alpine | fedora | rocky | centos) TEMPLATE_PATTERN="-default_" ;; + *) TEMPLATE_PATTERN="" ;; + esac + + msg_info "Searching for template '$TEMPLATE_SEARCH'" + + # Build regex patterns outside awk/grep for clarity + SEARCH_PATTERN="^${TEMPLATE_SEARCH}" + + #echo "[DEBUG] TEMPLATE_SEARCH='$TEMPLATE_SEARCH'" + #echo "[DEBUG] SEARCH_PATTERN='$SEARCH_PATTERN'" + #echo "[DEBUG] TEMPLATE_PATTERN='$TEMPLATE_PATTERN'" + + mapfile -t LOCAL_TEMPLATES < <( + pveam list "$TEMPLATE_STORAGE" 2>/dev/null | + awk -v search="${SEARCH_PATTERN}" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' | + sed 's|.*/||' | sort -t - -k 2 -V + ) + + pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)." + + #echo "[DEBUG] pveam available output (first 5 lines with .tar files):" + #pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | head -5 | sed 's/^/ /' + + set +u + mapfile -t ONLINE_TEMPLATES < <(pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | awk '{print $2}' | grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V 2>/dev/null || true) + #echo "[DEBUG] After filtering: ${#ONLINE_TEMPLATES[@]} online templates found" + set -u + if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then + #echo "[DEBUG] Online templates:" + for tmpl in "${ONLINE_TEMPLATES[@]}"; do + echo " - $tmpl" + done + fi + + ONLINE_TEMPLATE="" + [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" + + #msg_debug "SEARCH_PATTERN='${SEARCH_PATTERN}' TEMPLATE_PATTERN='${TEMPLATE_PATTERN}'" + #msg_debug "Found ${#LOCAL_TEMPLATES[@]} local templates, ${#ONLINE_TEMPLATES[@]} online templates" + if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then + #msg_debug "First 3 online templates:" + count=0 + for idx in "${!ONLINE_TEMPLATES[@]}"; do + #msg_debug " [$idx]: ${ONLINE_TEMPLATES[$idx]}" + ((count++)) + [[ $count -ge 3 ]] && break + done + fi + #msg_debug "ONLINE_TEMPLATE='$ONLINE_TEMPLATE'" + + if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then + TEMPLATE="${LOCAL_TEMPLATES[-1]}" + TEMPLATE_SOURCE="local" + else + TEMPLATE="$ONLINE_TEMPLATE" + TEMPLATE_SOURCE="online" + fi + + # If still no template, try to find alternatives + if [[ -z "$TEMPLATE" ]]; then + echo "" + echo "[DEBUG] No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}, searching for alternatives..." + + # Get all available versions for this OS type + mapfile -t AVAILABLE_VERSIONS < <( + pveam available -section system 2>/dev/null | + grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | + awk -F'\t' '{print $1}' | + grep "^${PCT_OSTYPE}-" | + sed -E "s/.*${PCT_OSTYPE}-([0-9]+(\.[0-9]+)?).*/\1/" | + sort -u -V 2>/dev/null + ) + + if [[ ${#AVAILABLE_VERSIONS[@]} -gt 0 ]]; then + echo "" + echo "${BL}Available ${PCT_OSTYPE} versions:${CL}" + for i in "${!AVAILABLE_VERSIONS[@]}"; do + echo " [$((i + 1))] ${AVAILABLE_VERSIONS[$i]}" + done + echo "" + read -p "Select version [1-${#AVAILABLE_VERSIONS[@]}] or press Enter to cancel: " choice + + if [[ "$choice" =~ ^[0-9]+$ ]] && [[ "$choice" -ge 1 ]] && [[ "$choice" -le ${#AVAILABLE_VERSIONS[@]} ]]; then + PCT_OSVERSION="${AVAILABLE_VERSIONS[$((choice - 1))]}" + TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION}" + SEARCH_PATTERN="^${TEMPLATE_SEARCH}-" + + #echo "[DEBUG] Retrying with version: $PCT_OSVERSION" + + mapfile -t ONLINE_TEMPLATES < <( + pveam available -section system 2>/dev/null | + grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | + awk -F'\t' '{print $1}' | + grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | + sort -t - -k 2 -V 2>/dev/null || true + ) + + if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then + TEMPLATE="${ONLINE_TEMPLATES[-1]}" + TEMPLATE_SOURCE="online" + #echo "[DEBUG] Found alternative: $TEMPLATE" + else + msg_error "No templates available for ${PCT_OSTYPE} ${PCT_OSVERSION}" + exit 225 + fi + else + msg_info "Installation cancelled" + exit 0 + fi + else + msg_error "No ${PCT_OSTYPE} templates available at all" + exit 225 + fi + fi + + #echo "[DEBUG] Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'" + #msg_debug "Selected TEMPLATE='$TEMPLATE' SOURCE='$TEMPLATE_SOURCE'" + + TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" + if [[ -z "$TEMPLATE_PATH" ]]; then + TEMPLATE_BASE=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) + [[ -n "$TEMPLATE_BASE" ]] && TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE" + fi + + # If we still don't have a path but have a valid template name, construct it + if [[ -z "$TEMPLATE_PATH" && -n "$TEMPLATE" ]]; then + TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" + fi + + [[ -n "$TEMPLATE_PATH" ]] || { + if [[ -z "$TEMPLATE" ]]; then + msg_error "Template ${PCT_OSTYPE} ${PCT_OSVERSION} not available" + + # Get available versions + mapfile -t AVAILABLE_VERSIONS < <( + pveam available -section system 2>/dev/null | + grep "^${PCT_OSTYPE}-" | + sed -E 's/.*'"${PCT_OSTYPE}"'-([0-9]+\.[0-9]+).*/\1/' | + grep -E '^[0-9]+\.[0-9]+$' | + sort -u -V 2>/dev/null || sort -u + ) + + if [[ ${#AVAILABLE_VERSIONS[@]} -gt 0 ]]; then + echo -e "\n${BL}Available versions:${CL}" + for i in "${!AVAILABLE_VERSIONS[@]}"; do + echo " [$((i + 1))] ${AVAILABLE_VERSIONS[$i]}" + done + + echo "" + read -p "Select version [1-${#AVAILABLE_VERSIONS[@]}] or Enter to exit: " choice + + if [[ "$choice" =~ ^[0-9]+$ ]] && [[ "$choice" -ge 1 ]] && [[ "$choice" -le ${#AVAILABLE_VERSIONS[@]} ]]; then + export var_version="${AVAILABLE_VERSIONS[$((choice - 1))]}" + export PCT_OSVERSION="$var_version" + msg_ok "Switched to ${PCT_OSTYPE} ${var_version}" + + # Retry template search with new version + TEMPLATE_SEARCH="${PCT_OSTYPE}-${PCT_OSVERSION:-}" + SEARCH_PATTERN="^${TEMPLATE_SEARCH}-" + + mapfile -t LOCAL_TEMPLATES < <( + pveam list "$TEMPLATE_STORAGE" 2>/dev/null | + awk -v search="${SEARCH_PATTERN}" -v pattern="${TEMPLATE_PATTERN}" '$1 ~ search && $1 ~ pattern {print $1}' | + sed 's|.*/||' | sort -t - -k 2 -V + ) + mapfile -t ONLINE_TEMPLATES < <( + pveam available -section system 2>/dev/null | + grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | + awk -F'\t' '{print $1}' | + grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | + sort -t - -k 2 -V 2>/dev/null || true + ) + ONLINE_TEMPLATE="" + [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" + + if [[ ${#LOCAL_TEMPLATES[@]} -gt 0 ]]; then + TEMPLATE="${LOCAL_TEMPLATES[-1]}" + TEMPLATE_SOURCE="local" + else + TEMPLATE="$ONLINE_TEMPLATE" + TEMPLATE_SOURCE="online" + fi + + TEMPLATE_PATH="$(pvesm path $TEMPLATE_STORAGE:vztmpl/$TEMPLATE 2>/dev/null || true)" + if [[ -z "$TEMPLATE_PATH" ]]; then + TEMPLATE_BASE=$(awk -v s="$TEMPLATE_STORAGE" '$1==s {f=1} f && /path/ {print $2; exit}' /etc/pve/storage.cfg) + [[ -n "$TEMPLATE_BASE" ]] && TEMPLATE_PATH="$TEMPLATE_BASE/template/cache/$TEMPLATE" + fi + + # If we still don't have a path but have a valid template name, construct it + if [[ -z "$TEMPLATE_PATH" && -n "$TEMPLATE" ]]; then + TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" + fi + + [[ -n "$TEMPLATE_PATH" ]] || { + msg_error "Template still not found after version change" + exit 220 + } + else + msg_info "Installation cancelled" + exit 1 + fi + else + msg_error "No ${PCT_OSTYPE} templates available" + exit 220 + fi + fi + } + + # Validate that we found a template + if [[ -z "$TEMPLATE" ]]; then + msg_error "No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}" + msg_info "Please check:" + msg_info " - Is pveam catalog available? (run: pveam available -section system)" + msg_info " - Does the template exist for your OS version?" + exit 225 + fi + + msg_ok "Template ${BL}$TEMPLATE${CL} [$TEMPLATE_SOURCE]" + msg_debug "Resolved TEMPLATE_PATH=$TEMPLATE_PATH" + + NEED_DOWNLOAD=0 + if [[ ! -f "$TEMPLATE_PATH" ]]; then + msg_info "Template not present locally – will download." + NEED_DOWNLOAD=1 + elif [[ ! -r "$TEMPLATE_PATH" ]]; then + msg_error "Template file exists but is not readable – check permissions." + exit 221 + elif [[ "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then + if [[ -n "$ONLINE_TEMPLATE" ]]; then + msg_warn "Template file too small (<1MB) – re-downloading." + NEED_DOWNLOAD=1 + else + msg_warn "Template looks too small, but no online version exists. Keeping local file." + fi + elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then + if [[ -n "$ONLINE_TEMPLATE" ]]; then + msg_warn "Template appears corrupted – re-downloading." + NEED_DOWNLOAD=1 + else + msg_warn "Template appears corrupted, but no online version exists. Keeping local file." + fi + else + $STD msg_ok "Template $TEMPLATE is present and valid." + fi + + if [[ "$TEMPLATE_SOURCE" == "local" && -n "$ONLINE_TEMPLATE" && "$TEMPLATE" != "$ONLINE_TEMPLATE" ]]; then + msg_warn "Local template is outdated: $TEMPLATE (latest available: $ONLINE_TEMPLATE)" + if whiptail --yesno "A newer template is available:\n$ONLINE_TEMPLATE\n\nDo you want to download and use it instead?" 12 70; then + TEMPLATE="$ONLINE_TEMPLATE" + NEED_DOWNLOAD=1 + else + msg_info "Continuing with local template $TEMPLATE" + fi + fi + + if [[ "$NEED_DOWNLOAD" -eq 1 ]]; then + [[ -f "$TEMPLATE_PATH" ]] && rm -f "$TEMPLATE_PATH" + for attempt in {1..3}; do + msg_info "Attempt $attempt: Downloading template $TEMPLATE to $TEMPLATE_STORAGE" + if pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" >/dev/null 2>&1; then + msg_ok "Template download successful." + break + fi + if [[ $attempt -eq 3 ]]; then + msg_error "Failed after 3 attempts. Please check network access, permissions, or manually run:\n pveam download $TEMPLATE_STORAGE $TEMPLATE" + exit 222 + fi + sleep $((attempt * 5)) + done + fi + + if ! pveam list "$TEMPLATE_STORAGE" 2>/dev/null | grep -q "$TEMPLATE"; then + msg_error "Template $TEMPLATE not available in storage $TEMPLATE_STORAGE after download." + exit 223 + fi + + # ------------------------------------------------------------------------------ + # Dynamic preflight for Debian 13.x: offer upgrade if available (no hard mins) + # ------------------------------------------------------------------------------ + if [[ "$PCT_OSTYPE" == "debian" ]]; then + OSVER="$(parse_template_osver "$TEMPLATE")" + if [[ -n "$OSVER" ]]; then + # Proactive, aber ohne Abbruch – nur Angebot + offer_lxc_stack_upgrade_and_maybe_retry "no" || true + fi + fi + + # ------------------------------------------------------------------------------ + # Create LXC Container + # ------------------------------------------------------------------------------ + msg_info "Creating LXC container" + + # Ensure subuid/subgid entries exist + grep -q "root:100000:65536" /etc/subuid || echo "root:100000:65536" >>/etc/subuid + grep -q "root:100000:65536" /etc/subgid || echo "root:100000:65536" >>/etc/subgid + + # Assemble pct options + PCT_OPTIONS=(${PCT_OPTIONS[@]:-${DEFAULT_PCT_OPTIONS[@]}}) + [[ " ${PCT_OPTIONS[*]} " =~ " -rootfs " ]] || PCT_OPTIONS+=(-rootfs "$CONTAINER_STORAGE:${PCT_DISK_SIZE:-8}") + + # Lock by template file (avoid concurrent downloads/creates) + lockfile="/tmp/template.${TEMPLATE}.lock" + exec 9>"$lockfile" || { + msg_error "Failed to create lock file '$lockfile'." + exit 200 + } + flock -w 60 9 || { + msg_error "Timeout while waiting for template lock." + exit 211 + } + + LOGFILE="/tmp/pct_create_${CTID}.log" + msg_debug "pct create command: pct create $CTID ${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" + msg_debug "Logfile: $LOGFILE" + + # First attempt + if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >"$LOGFILE" 2>&1; then + msg_error "Container creation failed on ${TEMPLATE_STORAGE}. Checking template..." + + # Validate template file + if [[ ! -s "$TEMPLATE_PATH" || "$(stat -c%s "$TEMPLATE_PATH")" -lt 1000000 ]]; then + msg_warn "Template file too small or missing – re-downloading." + rm -f "$TEMPLATE_PATH" + pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" + elif ! tar -tf "$TEMPLATE_PATH" &>/dev/null; then + if [[ -n "$ONLINE_TEMPLATE" ]]; then + msg_warn "Template appears corrupted – re-downloading." + rm -f "$TEMPLATE_PATH" + pveam download "$TEMPLATE_STORAGE" "$TEMPLATE" + else + msg_warn "Template appears corrupted, but no online version exists. Skipping re-download." + fi + fi + + # Retry after repair + if ! pct create "$CTID" "${TEMPLATE_STORAGE}:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then + # Fallback to local storage + if [[ "$TEMPLATE_STORAGE" != "local" ]]; then + msg_warn "Retrying container creation with fallback to local storage..." + LOCAL_TEMPLATE_PATH="/var/lib/vz/template/cache/$TEMPLATE" + if [[ ! -f "$LOCAL_TEMPLATE_PATH" ]]; then + msg_info "Downloading template to local..." + pveam download local "$TEMPLATE" >/dev/null 2>&1 + fi + if pct create "$CTID" "local:vztmpl/${TEMPLATE}" "${PCT_OPTIONS[@]}" >>"$LOGFILE" 2>&1; then + msg_ok "Container successfully created using local fallback." + else + # --- Dynamic stack upgrade + auto-retry on the well-known error pattern --- + if grep -qiE 'unsupported .* version' "$LOGFILE"; then + echo + echo "pct reported 'unsupported ... version' – your LXC stack might be too old for this template." + echo "We can try to upgrade 'pve-container' and 'lxc-pve' now and retry automatically." + offer_lxc_stack_upgrade_and_maybe_retry "yes" + rc=$? + case $rc in + 0) : ;; # success - container created, continue + 2) + echo "Upgrade was declined. Please update and re-run: + apt update && apt install --only-upgrade pve-container lxc-pve" + exit 231 + ;; + 3) + echo "Upgrade and/or retry failed. Please inspect: $LOGFILE" + exit 231 + ;; + esac + else + msg_error "Container creation failed even with local fallback. See $LOGFILE" + if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then + set -x + bash -x -c "pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" 2>&1 | tee -a "$LOGFILE" + set +x + fi + exit 209 + fi + fi + else + msg_error "Container creation failed on local storage. See $LOGFILE" + # --- Dynamic stack upgrade + auto-retry on the well-known error pattern --- + if grep -qiE 'unsupported .* version' "$LOGFILE"; then + echo + echo "pct reported 'unsupported ... version' – your LXC stack might be too old for this template." + echo "We can try to upgrade 'pve-container' and 'lxc-pve' now and retry automatically." + offer_lxc_stack_upgrade_and_maybe_retry "yes" + rc=$? + case $rc in + 0) : ;; # success - container created, continue + 2) + echo "Upgrade was declined. Please update and re-run: + apt update && apt install --only-upgrade pve-container lxc-pve" + exit 231 + ;; + 3) + echo "Upgrade and/or retry failed. Please inspect: $LOGFILE" + exit 231 + ;; + esac + else + msg_error "Container creation failed. See $LOGFILE" + if whiptail --yesno "pct create failed.\nDo you want to enable verbose debug mode and view detailed logs?" 12 70; then + set -x + bash -x -c "pct create $CTID local:vztmpl/${TEMPLATE} ${PCT_OPTIONS[*]}" 2>&1 | tee -a "$LOGFILE" + set +x + fi + exit 209 + fi + fi + fi + fi + + # Verify container exists + pct list | awk '{print $1}' | grep -qx "$CTID" || { + msg_error "Container ID $CTID not listed in 'pct list'. See $LOGFILE" + exit 215 + } + + # Verify config rootfs + grep -q '^rootfs:' "/etc/pve/lxc/$CTID.conf" || { + msg_error "RootFS entry missing in container config. See $LOGFILE" + exit 216 + } + + msg_ok "LXC Container ${BL}$CTID${CL} ${GN}was successfully created." +} + +# ------------------------------------------------------------------------------ +# description() +# +# - Sets container description with HTML content (logo, links, badges) +# - Restarts ping-instances.service if present +# - Posts status "done" to API +# ------------------------------------------------------------------------------ +description() { + IP=$(pct exec "$CTID" ip a s dev eth0 | awk '/inet / {print $2}' | cut -d/ -f1) + + # Generate LXC Description + DESCRIPTION=$( + cat < + + Logo + + +

${APP} LXC

+ +

+ + spend Coffee + +

+ + + + GitHub + + + + Discussions + + + + Issues + + +EOF + ) + pct set "$CTID" -description "$DESCRIPTION" + + if [[ -f /etc/systemd/system/ping-instances.service ]]; then + systemctl start ping-instances.service + fi + + post_update_to_api "done" "none" +} + +# ------------------------------------------------------------------------------ +# api_exit_script() +# +# - Exit trap handler +# - Reports exit codes to API with detailed reason +# - Handles known codes (100–209) and maps them to errors +# ------------------------------------------------------------------------------ +api_exit_script() { + exit_code=$? + if [ $exit_code -ne 0 ]; then + case $exit_code in + 100) post_update_to_api "failed" "100: Unexpected error in create_lxc.sh" ;; + 101) post_update_to_api "failed" "101: No network connection detected in create_lxc.sh" ;; + 200) post_update_to_api "failed" "200: LXC creation failed in create_lxc.sh" ;; + 201) post_update_to_api "failed" "201: Invalid Storage class in create_lxc.sh" ;; + 202) post_update_to_api "failed" "202: User aborted menu in create_lxc.sh" ;; + 203) post_update_to_api "failed" "203: CTID not set in create_lxc.sh" ;; + 204) post_update_to_api "failed" "204: PCT_OSTYPE not set in create_lxc.sh" ;; + 205) post_update_to_api "failed" "205: CTID cannot be less than 100 in create_lxc.sh" ;; + 206) post_update_to_api "failed" "206: CTID already in use in create_lxc.sh" ;; + 207) post_update_to_api "failed" "207: Template not found in create_lxc.sh" ;; + 208) post_update_to_api "failed" "208: Error downloading template in create_lxc.sh" ;; + 209) post_update_to_api "failed" "209: Container creation failed, but template is intact in create_lxc.sh" ;; + *) post_update_to_api "failed" "Unknown error, exit code: $exit_code in create_lxc.sh" ;; + esac + fi +} + +if command -v pveversion >/dev/null 2>&1; then + trap 'api_exit_script' EXIT +fi +trap 'post_update_to_api "failed" "$BASH_COMMAND"' ERR +trap 'post_update_to_api "failed" "INTERRUPTED"' SIGINT +trap 'post_update_to_api "failed" "TERMINATED"' SIGTERM diff --git a/misc/optimize_build_func.py b/misc/optimize_build_func.py new file mode 100644 index 000000000..92fe03000 --- /dev/null +++ b/misc/optimize_build_func.py @@ -0,0 +1,508 @@ +#!/usr/bin/env python3 +""" +Build.func Optimizer +==================== +Optimizes the build.func file by: +- Removing duplicate functions +- Sorting and grouping functions logically +- Adding section headers +- Improving readability +""" + +import re +import sys +from pathlib import Path +from datetime import datetime +from typing import List, Tuple, Dict + +# ============================================================================== +# CONFIGURATION +# ============================================================================== + +# Define function groups in desired order +FUNCTION_GROUPS = { + "CORE_INIT": { + "title": "CORE INITIALIZATION & VARIABLES", + "functions": [ + "variables", + ] + }, + "DEPENDENCIES": { + "title": "DEPENDENCY LOADING", + "functions": [ + # Bootstrap loader section (commented code) + ] + }, + "VALIDATION": { + "title": "SYSTEM VALIDATION & CHECKS", + "functions": [ + "maxkeys_check", + "check_container_resources", + "check_container_storage", + "check_nvidia_host_setup", + "check_storage_support", + ] + }, + "NETWORK": { + "title": "NETWORK & IP MANAGEMENT", + "functions": [ + "get_current_ip", + "update_motd_ip", + ] + }, + "SSH": { + "title": "SSH KEY MANAGEMENT", + "functions": [ + "find_host_ssh_keys", + "ssh_discover_default_files", + "ssh_extract_keys_from_file", + "ssh_build_choices_from_files", + "configure_ssh_settings", + "install_ssh_keys_into_ct", + ] + }, + "SETTINGS": { + "title": "SETTINGS & CONFIGURATION", + "functions": [ + "base_settings", + "echo_default", + "exit_script", + "advanced_settings", + "diagnostics_check", + "diagnostics_menu", + "default_var_settings", + "ensure_global_default_vars_file", + "settings_menu", + "edit_default_storage", + ] + }, + "DEFAULTS": { + "title": "DEFAULTS MANAGEMENT (VAR_* FILES)", + "functions": [ + "get_app_defaults_path", + "_is_whitelisted_key", + "_sanitize_value", + "_load_vars_file", + "_load_vars_file_to_map", + "_build_vars_diff", + "_build_current_app_vars_tmp", + "maybe_offer_save_app_defaults", + "ensure_storage_selection_for_vars_file", + ] + }, + "STORAGE": { + "title": "STORAGE DISCOVERY & SELECTION", + "functions": [ + "resolve_storage_preselect", + "select_storage", + "choose_and_set_storage_for_file", + "_write_storage_to_vars", + ] + }, + "GPU": { + "title": "GPU & HARDWARE PASSTHROUGH", + "functions": [ + "is_gpu_app", + "detect_gpu_devices", + "configure_gpu_passthrough", + "configure_usb_passthrough", + "configure_additional_devices", + "fix_gpu_gids", + "get_container_gid", + ] + }, + "CONTAINER": { + "title": "CONTAINER LIFECYCLE & CREATION", + "functions": [ + "create_lxc_container", + "offer_lxc_stack_upgrade_and_maybe_retry", + "parse_template_osver", + "pkg_ver", + "pkg_cand", + "ver_ge", + "ver_gt", + "ver_lt", + "build_container", + "destroy_lxc", + "description", + ] + }, + "MAIN": { + "title": "MAIN ENTRY POINTS & ERROR HANDLING", + "functions": [ + "install_script", + "start", + "api_exit_script", + ] + }, +} + +# Functions to exclude from duplication check (intentionally similar) +EXCLUDE_FROM_DEDUP = { + "_load_vars_file", + "_load_vars_file_to_map", +} + +# ============================================================================== +# HELPER FUNCTIONS +# ============================================================================== + +def extract_functions(content: str) -> Dict[str, Tuple[str, int, int]]: + """ + Extract all function definitions from the content. + Returns dict: {function_name: (full_code, start_line, end_line)} + """ + functions = {} + lines = content.split('\n') + + i = 0 + while i < len(lines): + line = lines[i] + + # Match function definition: function_name() { + match = re.match(r'^([a-zA-Z_][a-zA-Z0-9_]*)\s*\(\)\s*\{', line) + if match: + func_name = match.group(1) + start_line = i + + # Find function end by counting braces + brace_count = 1 + func_lines = [line] + i += 1 + + while i < len(lines) and brace_count > 0: + current_line = lines[i] + func_lines.append(current_line) + + # Count braces (simple method, doesn't handle strings/comments perfectly) + brace_count += current_line.count('{') - current_line.count('}') + i += 1 + + end_line = i + functions[func_name] = ('\n'.join(func_lines), start_line, end_line) + continue + + i += 1 + + return functions + +def extract_header_comments(content: str, func_name: str, func_code: str) -> str: + """Extract comment block before function if exists""" + lines = content.split('\n') + + # Find function start in original content + for i, line in enumerate(lines): + if line.strip().startswith(f"{func_name}()"): + # Look backwards for comment block + comments = [] + j = i - 1 + while j >= 0: + prev_line = lines[j] + stripped = prev_line.strip() + + # SKIP section headers and copyright - we add our own + if (stripped.startswith('# ===') or + stripped.startswith('#!/usr/bin/env') or + 'Copyright' in stripped or + 'Author:' in stripped or + 'License:' in stripped or + 'Revision:' in stripped or + 'SECTION' in stripped): + j -= 1 + continue + + # Include function-specific comment lines + if (stripped.startswith('# ---') or + stripped.startswith('#')): + comments.insert(0, prev_line) + j -= 1 + elif stripped == '': + # Keep collecting through empty lines + comments.insert(0, prev_line) + j -= 1 + else: + break + + # Remove leading empty lines from comments + while comments and comments[0].strip() == '': + comments.pop(0) + + # Remove trailing empty lines from comments + while comments and comments[-1].strip() == '': + comments.pop() + + if comments: + return '\n'.join(comments) + '\n' + + return '' + +def find_duplicate_functions(functions: Dict[str, Tuple[str, int, int]]) -> List[str]: + """Find duplicate function definitions""" + seen = {} + duplicates = [] + + for func_name, (code, start, end) in functions.items(): + if func_name in EXCLUDE_FROM_DEDUP: + continue + + # Normalize code for comparison (remove whitespace variations) + normalized = re.sub(r'\s+', ' ', code).strip() + + if normalized in seen: + duplicates.append(func_name) + print(f" ⚠️ Duplicate found: {func_name} (also defined as {seen[normalized]})") + else: + seen[normalized] = func_name + + return duplicates + +def create_section_header(title: str) -> str: + """Create a formatted section header""" + return f""" +# ============================================================================== +# {title} +# ============================================================================== +""" + +def get_function_group(func_name: str) -> str: + """Determine which group a function belongs to""" + for group_key, group_data in FUNCTION_GROUPS.items(): + if func_name in group_data["functions"]: + return group_key + return "UNKNOWN" + +# ============================================================================== +# MAIN OPTIMIZATION LOGIC +# ============================================================================== + +def optimize_build_func(input_file: Path, output_file: Path): + """Main optimization function""" + + print("=" * 80) + print("BUILD.FUNC OPTIMIZER") + print("=" * 80) + print() + + # Read input file + print(f"📖 Reading: {input_file}") + content = input_file.read_text(encoding='utf-8') + original_lines = len(content.split('\n')) + print(f" Lines: {original_lines:,}") + print() + + # Extract functions + print("🔍 Extracting functions...") + functions = extract_functions(content) + print(f" Found {len(functions)} functions") + print() + + # Find duplicates + print("🔎 Checking for duplicates...") + duplicates = find_duplicate_functions(functions) + if duplicates: + print(f" Found {len(duplicates)} duplicate(s)") + else: + print(" ✓ No duplicates found") + print() + + # Extract header (copyright, etc) + print("📝 Extracting file header...") + lines = content.split('\n') + header_lines = [] + + # Extract only the first copyright block + in_header = True + for i, line in enumerate(lines): + if in_header: + # Keep copyright and license lines + if (line.strip().startswith('#!') or + line.strip().startswith('# Copyright') or + line.strip().startswith('# Author:') or + line.strip().startswith('# License:') or + line.strip().startswith('# Revision:') or + line.strip() == ''): + header_lines.append(line) + else: + in_header = False + break + + # Remove trailing empty lines + while header_lines and header_lines[-1].strip() == '': + header_lines.pop() + + header = '\n'.join(header_lines) + print() + + # Build optimized content + print("🔨 Building optimized structure...") + + optimized_parts = [header] + + # Group functions + grouped_functions = {key: [] for key in FUNCTION_GROUPS.keys()} + grouped_functions["UNKNOWN"] = [] + + for func_name, (func_code, start, end) in functions.items(): + if func_name in duplicates: + continue # Skip duplicates + + group = get_function_group(func_name) + + # Extract comments before function + comments = extract_header_comments(content, func_name, func_code) + + grouped_functions[group].append((func_name, comments + func_code)) + + # Add grouped sections + for group_key, group_data in FUNCTION_GROUPS.items(): + if grouped_functions[group_key]: + optimized_parts.append(create_section_header(group_data["title"])) + + for func_name, func_code in grouped_functions[group_key]: + optimized_parts.append(func_code) + optimized_parts.append('') # Empty line between functions + + # Add unknown functions at the end + if grouped_functions["UNKNOWN"]: + optimized_parts.append(create_section_header("UNCATEGORIZED FUNCTIONS")) + print(f" ⚠️ {len(grouped_functions['UNKNOWN'])} uncategorized functions:") + for func_name, func_code in grouped_functions["UNKNOWN"]: + print(f" - {func_name}") + optimized_parts.append(func_code) + optimized_parts.append('') + + # Add any remaining non-function code (bootstrap, source commands, traps, etc) + print("📌 Adding remaining code...") + + # Extract bootstrap/source section + bootstrap_lines = [] + trap_lines = [] + other_lines = [] + + in_function = False + brace_count = 0 + in_bootstrap_comment = False + + for line in lines: + stripped = line.strip() + + # Skip the header we already extracted + if (stripped.startswith('#!/usr/bin/env bash') or + stripped.startswith('# Copyright') or + stripped.startswith('# Author:') or + stripped.startswith('# License:') or + stripped.startswith('# Revision:')): + continue + + # Check if we're in a function + if re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*\s*\(\)\s*\{', line): + in_function = True + brace_count = 1 + elif in_function: + brace_count += line.count('{') - line.count('}') + if brace_count == 0: + in_function = False + elif not in_function: + # Collect non-function lines + + # Bootstrap/loader section + if ('Community-Scripts bootstrap' in line or + 'Load core' in line or + in_bootstrap_comment): + bootstrap_lines.append(line) + if '# ---' in line or '# ===' in line: + in_bootstrap_comment = not in_bootstrap_comment + continue + + # Source commands + if (stripped.startswith('source <(') or + stripped.startswith('if command -v curl') or + stripped.startswith('elif command -v wget') or + 'load_functions' in stripped or + 'catch_errors' in stripped): + bootstrap_lines.append(line) + continue + + # Traps + if stripped.startswith('trap '): + trap_lines.append(line) + continue + + # VAR_WHITELIST declaration + if 'declare -ag VAR_WHITELIST' in line or (other_lines and 'VAR_WHITELIST' in other_lines[-1]): + other_lines.append(line) + continue + + # Empty lines between sections - keep some + if stripped == '' and (bootstrap_lines or trap_lines or other_lines): + if bootstrap_lines and bootstrap_lines[-1].strip() != '': + bootstrap_lines.append(line) + elif trap_lines and trap_lines[-1].strip() != '': + trap_lines.append(line) + + # Add bootstrap section if exists + if bootstrap_lines: + optimized_parts.append(create_section_header("DEPENDENCY LOADING")) + optimized_parts.extend(bootstrap_lines) + optimized_parts.append('') + + # Add other declarations + if other_lines: + optimized_parts.extend(other_lines) + optimized_parts.append('') + + # Write output + optimized_content = '\n'.join(optimized_parts) + optimized_lines = len(optimized_content.split('\n')) + + print() + print(f"💾 Writing optimized file: {output_file}") + output_file.write_text(optimized_content, encoding='utf-8') + + print() + print("=" * 80) + print("✅ OPTIMIZATION COMPLETE") + print("=" * 80) + print(f"Original lines: {original_lines:,}") + print(f"Optimized lines: {optimized_lines:,}") + print(f"Difference: {original_lines - optimized_lines:+,}") + print(f"Functions: {len(functions) - len(duplicates)}") + print(f"Duplicates removed: {len(duplicates)}") + print() + +# ============================================================================== +# ENTRY POINT +# ============================================================================== + +def main(): + """Main entry point""" + + # Set paths + script_dir = Path(__file__).parent + input_file = script_dir / "build.func" + + # Create backup first + timestamp = datetime.now().strftime("%Y%m%d-%H%M%S") + backup_file = script_dir / f"build.func.backup-{timestamp}" + + if not input_file.exists(): + print(f"❌ Error: {input_file} not found!") + sys.exit(1) + + print(f"📦 Creating backup: {backup_file.name}") + backup_file.write_text(input_file.read_text(encoding='utf-8'), encoding='utf-8') + print() + + # Optimize + output_file = script_dir / "build.func.optimized" + optimize_build_func(input_file, output_file) + + print("📋 Next steps:") + print(f" 1. Review: {output_file.name}") + print(f" 2. Test the optimized version") + print(f" 3. If OK: mv build.func.optimized build.func") + print(f" 4. Backup available at: {backup_file.name}") + print() + +if __name__ == "__main__": + main() From e97a1ec9442b7bfaecd98f7b2bff3905e800e66c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 29 Oct 2025 13:17:35 +0100 Subject: [PATCH 1606/1733] fixes --- install/reitti-install.sh | 3 +-- misc/REFACTORING_SUMMARY.md | 46 +++++++++++++++++++++++++++++-------- misc/build.func | 10 ++++---- 3 files changed, 42 insertions(+), 17 deletions(-) diff --git a/install/reitti-install.sh b/install/reitti-install.sh index 270134054..021cdc3be 100644 --- a/install/reitti-install.sh +++ b/install/reitti-install.sh @@ -122,8 +122,7 @@ Wants=postgresql.service redis-server.service rabbitmq-server.service photon.ser [Service] Type=simple WorkingDirectory=/opt/reitti/ -ExecStart=/usr/bin/java -jar /opt/reitti/reitti.jar \ - --spring.config.location=file:/opt/reitti/application.properties +ExecStart=/usr/bin/java --enable-native-access=ALL-UNNAMED -jar -Xmx2g reitti.jar TimeoutStopSec=20 KillMode=process Restart=on-failure diff --git a/misc/REFACTORING_SUMMARY.md b/misc/REFACTORING_SUMMARY.md index 8115f7160..8365a50d3 100644 --- a/misc/REFACTORING_SUMMARY.md +++ b/misc/REFACTORING_SUMMARY.md @@ -1,7 +1,7 @@ # Build.func Refactoring Summary - CORRECTED -**Datum:** 29.10.2025 -**Backup:** build.func.backup-refactoring-* +**Datum:** 29.10.2025 +**Backup:** build.func.backup-refactoring-\* ## Durchgeführte Änderungen (KORRIGIERT) @@ -9,7 +9,8 @@ **Problem:** Nvidia-Unterstützung war überkompliziert mit Treiber-Checks, nvidia-smi Calls, automatischen Installationen -**Lösung (KORRIGIERT):** +**Lösung (KORRIGIERT):** + - ✅ Entfernt: `check_nvidia_host_setup()` Funktion (unnötige nvidia-smi Checks) - ✅ Entfernt: VAAPI/NVIDIA verification checks nach Container-Start - ✅ **BEHALTEN:** `lxc.mount.entry` für alle GPU-Typen (Intel/AMD/NVIDIA) ✅✅✅ @@ -18,6 +19,7 @@ - ✅ User installiert Treiber selbst im Container **GPU Config jetzt:** + ```lxc # Intel/AMD: lxc.mount.entry: /dev/dri/renderD128 /dev/dri/renderD128 none bind,optional,create=file @@ -31,7 +33,8 @@ lxc.mount.entry: /dev/nvidia-uvm /dev/nvidia-uvm none bind,optional,create=file lxc.cgroup2.devices.allow: c 195:0 rwm # if privileged ``` -**Resultat:** +**Resultat:** + - GPU Passthrough funktioniert rein über LXC mount entries - Keine unnötigen Host-Checks oder nvidia-smi calls - User installiert Treiber selbst im Container wenn nötig @@ -39,7 +42,8 @@ lxc.cgroup2.devices.allow: c 195:0 rwm # if privileged ### 2. SSH Keys Funktionen ✅ -**Analyse:** +**Analyse:** + - `install_ssh_keys_into_ct()` - bereits gut strukturiert ✅ - `find_host_ssh_keys()` - bereits gut strukturiert ✅ @@ -47,22 +51,26 @@ lxc.cgroup2.devices.allow: c 195:0 rwm # if privileged ### 3. Default Vars Logik überarbeitet ✅ -**Problem:** Einige var_* defaults machen keinen Sinn als globale Defaults: +**Problem:** Einige var\_\* defaults machen keinen Sinn als globale Defaults: + - `var_ctid` - Container-IDs können nur 1x vergeben werden ❌ - `var_ipv6_static` - Statische IPs können nur 1x vergeben werden ❌ **Kein Problem (KORRIGIERT):** + - `var_gateway` - Kann als Default gesetzt werden (User's Verantwortung) ✅ - `var_apt_cacher` - Kann als Default gesetzt werden + Runtime-Check ✅ - `var_apt_cacher_ip` - Kann als Default gesetzt werden + Runtime-Check ✅ **Lösung:** + - ✅ **ENTFERNT** aus VAR_WHITELIST: var_ctid, var_ipv6_static - ✅ **BEHALTEN** in VAR_WHITELIST: var_gateway, var_apt_cacher, var_apt_cacher_ip - ✅ **NEU:** Runtime-Check für APT Cacher Erreichbarkeit (curl timeout 2s) - ✅ Kommentare hinzugefügt zur Erklärung **APT Cacher Runtime Check:** + ```bash # Runtime check: Verify APT cacher is reachable if configured if [[ -n "$APT_CACHER_IP" && "$APT_CACHER" == "yes" ]]; then @@ -78,6 +86,7 @@ fi ``` **Resultat:** + - Nur sinnvolle Defaults: keine var_ctid, keine static IPs - APT Cacher funktioniert mit automatischem Fallback wenn nicht erreichbar - Gateway bleibt als Default (User's Verantwortung bei Konflikten) @@ -85,12 +94,14 @@ fi ## Code-Statistik ### Vorher: + - Zeilen: 3,518 - check_nvidia_host_setup(): 22 Zeilen - NVIDIA verification: 8 Zeilen - Var whitelist entries: 28 Einträge ### Nachher: + - Zeilen: 3,458 - check_nvidia_host_setup(): **ENTFERNT** - NVIDIA verification: **ENTFERNT** @@ -99,22 +110,26 @@ fi - Var whitelist entries: 26 Einträge (var_ctid, var_ipv6_static entfernt) ### Einsparung: + - ~60 Zeilen Code -- 2 problematische var_* Einträge entfernt +- 2 problematische var\_\* Einträge entfernt - Komplexität reduziert - Robustheit erhöht (APT Cacher Check) ## Was wurde KORRIGIERT ### Fehler 1: lxc.mount.entry entfernt ❌ + **Problem:** Ich hatte die `lxc.mount.entry` Zeilen entfernt und nur `dev0:` Einträge behalten. **Lösung:** `lxc.mount.entry` für alle GPU-Typen wieder hinzugefügt! ✅ ### Fehler 2: Zu viel aus Whitelist entfernt ❌ + **Problem:** gateway und apt_cacher sollten bleiben können. **Lösung:** Nur var_ctid und var_ipv6_static entfernt! ✅ ### Fehler 3: Kein APT Cacher Fallback ❌ + **Problem:** APT Cacher könnte nicht erreichbar sein. **Lösung:** Runtime-Check mit curl --connect-timeout 2 hinzugefügt! ✅ @@ -123,20 +138,23 @@ fi Vor Deployment testen: ### GPU Passthrough: -- [ ] Intel iGPU: Check lxc.mount.entry für /dev/dri/* -- [ ] AMD GPU: Check lxc.mount.entry für /dev/dri/* -- [ ] NVIDIA GPU: Check lxc.mount.entry für /dev/nvidia* + +- [ ] Intel iGPU: Check lxc.mount.entry für /dev/dri/\* +- [ ] AMD GPU: Check lxc.mount.entry für /dev/dri/\* +- [ ] NVIDIA GPU: Check lxc.mount.entry für /dev/nvidia\* - [ ] Privileged: Check lxc.cgroup2.devices.allow - [ ] Unprivileged: Check nur lxc.mount.entry (keine cgroup) - [ ] Multi-GPU System (user selection) - [ ] System ohne GPU (skip passthrough) ### APT Cacher: + - [ ] APT Cacher erreichbar → verwendet - [ ] APT Cacher nicht erreichbar → deaktiviert mit Warning - [ ] APT Cacher nicht konfiguriert → skip ### Default Vars: + - [ ] var_ctid NICHT in defaults - [ ] var_ipv6_static NICHT in defaults - [ ] var_gateway in defaults ✅ @@ -147,11 +165,13 @@ Vor Deployment testen: **KEINE Breaking Changes mehr!** ### GPU Passthrough: + - ✅ lxc.mount.entry bleibt wie gehabt - ✅ Nur nvidia-smi Checks entfernt - ✅ User installiert Treiber selbst (war schon immer so) ### Default Vars: + - ✅ gateway bleibt verfügbar - ✅ apt_cacher bleibt verfügbar (+ neuer Check) - ❌ var_ctid entfernt (macht keinen Sinn) @@ -160,12 +180,14 @@ Vor Deployment testen: ## Vorteile ### GPU Passthrough: + - ✅ Einfacher Code, weniger Fehlerquellen - ✅ Keine Host-Dependencies (nvidia-smi) - ✅ lxc.mount.entry funktioniert wie erwartet ✅ - ✅ User hat Kontrolle über Container-Treiber ### Default Vars: + - ✅ APT Cacher mit automatischem Fallback - ✅ Gateway als Default möglich (User's Verantwortung) - ✅ Verhindert CT-ID und static IP Konflikte @@ -176,6 +198,7 @@ Vor Deployment testen: ### GPU Device Binding (KORRIGIERT): **Intel/AMD:** + ```lxc lxc.mount.entry: /dev/dri/renderD128 /dev/dri/renderD128 none bind,optional,create=file lxc.mount.entry: /dev/dri/card0 /dev/dri/card0 none bind,optional,create=file @@ -185,6 +208,7 @@ lxc.cgroup2.devices.allow: c 226:0 rwm ``` **NVIDIA:** + ```lxc lxc.mount.entry: /dev/nvidia0 /dev/nvidia0 none bind,optional,create=file lxc.mount.entry: /dev/nvidiactl /dev/nvidiactl none bind,optional,create=file @@ -198,10 +222,12 @@ lxc.cgroup2.devices.allow: c 195:255 rwm ### Whitelist Diff (KORRIGIERT): **Entfernt:** + - var_ctid (macht keinen Sinn - CT IDs sind unique) - var_ipv6_static (macht keinen Sinn - static IPs sind unique) **Behalten:** + - var_gateway (User's Verantwortung) - var_apt_cacher (mit Runtime-Check) - var_apt_cacher_ip (mit Runtime-Check) diff --git a/misc/build.func b/misc/build.func index e26406215..1ec46a2f6 100644 --- a/misc/build.func +++ b/misc/build.func @@ -307,7 +307,7 @@ base_settings() { GATE=${var_gateway:-""} APT_CACHER=${var_apt_cacher:-""} APT_CACHER_IP=${var_apt_cacher_ip:-""} - + # Runtime check: Verify APT cacher is reachable if configured if [[ -n "$APT_CACHER_IP" && "$APT_CACHER" == "yes" ]]; then if ! curl -s --connect-timeout 2 "http://${APT_CACHER_IP}:3142" >/dev/null 2>&1; then @@ -319,7 +319,7 @@ base_settings() { msg_ok "APT Cacher verified at ${APT_CACHER_IP}:3142" fi fi - + MTU=${var_mtu:-""} SD=${var_storage:-""} NS=${var_ns:-""} @@ -2197,7 +2197,7 @@ build_container() { # Check for NVIDIA GPU - look for NVIDIA vendor ID [10de] if echo "$pci_vga_info" | grep -q "\[10de:"; then msg_info "Detected NVIDIA GPU" - + # Simple passthrough - just bind /dev/nvidia* devices if they exist for d in /dev/nvidia* /dev/nvidiactl /dev/nvidia-modeset /dev/nvidia-uvm /dev/nvidia-uvm-tools; do [[ -e "$d" ]] && NVIDIA_DEVICES+=("$d") @@ -2311,7 +2311,7 @@ EOF # Add lxc.mount.entry for each device for dev in "${devices[@]}"; do echo "lxc.mount.entry: $dev $dev none bind,optional,create=file" >>"$LXC_CONFIG" - + if [[ "$CT_TYPE" == "0" ]]; then # Privileged container - also add cgroup allows local major minor @@ -2337,7 +2337,7 @@ EOF # Add lxc.mount.entry for each NVIDIA device for dev in "${NVIDIA_DEVICES[@]}"; do echo "lxc.mount.entry: $dev $dev none bind,optional,create=file" >>"$LXC_CONFIG" - + if [[ "$CT_TYPE" == "0" ]]; then # Privileged container - also add cgroup allows local major minor From acbaa7ce7d4fe2d1b5daf42e41f337fc078b1a7e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 29 Oct 2025 13:20:11 +0100 Subject: [PATCH 1607/1733] improve pversion check --- misc/build.func | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/misc/build.func b/misc/build.func index 1ec46a2f6..eb2183872 100644 --- a/misc/build.func +++ b/misc/build.func @@ -28,11 +28,10 @@ variables() { METHOD="default" # sets the METHOD variable to "default", used for the API call. RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" # generates a random UUID and sets it to the RANDOM_UUID variable. CTTYPE="${CTTYPE:-${CT_TYPE:-1}}" - #CT_TYPE=${var_unprivileged:-$CT_TYPE} # Get Proxmox VE version and kernel version if command -v pveversion >/dev/null 2>&1; then - PVEVERSION=$(pveversion | grep "pve-manager" | awk '{print $2}' | cut -d'/' -f1) + PVEVERSION="$(pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}')" else PVEVERSION="N/A" fi From ffe61d75900c7e5d3ff555251db1a527f5f8df4c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 30 Oct 2025 08:35:10 +0100 Subject: [PATCH 1608/1733] finalize livebook --- ct/livebook.sh | 55 +++++++++++++++++++------------------ install/livebook-install.sh | 21 +++++++------- 2 files changed, 39 insertions(+), 37 deletions(-) diff --git a/ct/livebook.sh b/ct/livebook.sh index 3afc43531..496503a30 100755 --- a/ct/livebook.sh +++ b/ct/livebook.sh @@ -20,35 +20,36 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources + header_info + check_container_storage + check_container_resources - if [[ ! -f /opt/livebook/.mix/escripts/livebook ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if check_for_gh_release "livebook" "livebook-dev/livebook"; then - msg_info "Stopping ${APP}" - systemctl stop livebook - msg_info "Service stopped" - - msg_info "Updating container" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "Updated container" - - msg_info "Updating ${APP}" - source /opt/livebook/.env - cd /opt/livebook - $STD mix escript.install hex livebook --force - - chown -R livebook:livebook /opt/livebook /data - systemctl start livebook - msg_ok "Updated ${APP}" - fi + if [[ ! -f /opt/livebook/.mix/escripts/livebook ]]; then + msg_error "No ${APP} Installation Found!" exit + fi + + if check_for_gh_release "livebook" "livebook-dev/livebook"; then + msg_info "Stopping Service" + systemctl stop livebook + msg_info "Stopped Service" + + msg_info "Updating Container" + $STD apt update + $STD apt upgrade -y + msg_ok "Updated Container" + + msg_info "Updating Livebook" + source /opt/livebook/.env + cd /opt/livebook + $STD mix escript.install hex livebook --force + + chown -R livebook:livebook /opt/livebook /data + systemctl start livebook + msg_ok "Updated Livebook" + msg_ok "Updated Successfully!" + fi + exit } start diff --git a/install/livebook-install.sh b/install/livebook-install.sh index 902d86d2b..95f46c137 100644 --- a/install/livebook-install.sh +++ b/install/livebook-install.sh @@ -15,11 +15,11 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - build-essential \ - ca-certificates \ - cmake \ - git \ - libncurses5-dev + build-essential \ + ca-certificates \ + cmake \ + git \ + libncurses5-dev msg_ok "Installed Dependencies" msg_info "Creating livebook user" @@ -28,7 +28,6 @@ export HOME=/opt/livebook $STD adduser --system --group --home /opt/livebook --shell /bin/bash livebook msg_ok "Created livebook user" - msg_warn "WARNING: This script will run an external installer from a third-party source (https://elixir-lang.org)." msg_warn "The following code is NOT maintained or audited by our repository." msg_warn "If you have any doubts or concerns, please review the installer code before proceeding:" @@ -39,7 +38,8 @@ if [[ ! "$CONFIRM" =~ ^([yY][eE][sS]|[yY])$ ]]; then msg_error "Aborted by user. No changes have been made." exit 10 fi -bash <(curl -sL https://elixir-lang.org/install.sh) +curl -fsSO https://elixir-lang.org/install.sh +$STD sh install.sh elixir@latest otp@latest msg_info "Setup Erlang and Elixir" ERLANG_VERSION=$(ls /opt/livebook/.elixir-install/installs/otp/ | head -n1) @@ -99,7 +99,8 @@ msg_ok "Installed Livebook" motd_ssh customize -msg_info "Cleaning Up" -$STD apt-get autoremove -y -$STD apt-get autoclean +msg_info "Cleaning Up" +$STD apt autoremove -y +$STD apt autoclean -y +$STD apt clean -y msg_ok "Cleaned Up" From 718559bcc99a39dcaddaa09d6ccb3f62646c5cd7 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 30 Oct 2025 09:15:03 +0100 Subject: [PATCH 1609/1733] finalize --- frontend/public/json/livebook.json | 10 +++++----- install/livebook-install.sh | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/frontend/public/json/livebook.json b/frontend/public/json/livebook.json index 8b0a3589d..0bad4f4f3 100644 --- a/frontend/public/json/livebook.json +++ b/frontend/public/json/livebook.json @@ -4,15 +4,15 @@ "categories": [ 20 ], - "date_created": "2025-08-12", + "date_created": "2025-10-30", "type": "ct", "updateable": true, "privileged": false, "interface_port": 8080, - "documentation": null, - "config_path": "/opt/.env", + "documentation": "https://hexdocs.pm/livebook/readme.html", + "config_path": null, "website": "https://livebook.dev", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/svg/livebook.svg", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/livebook.webp", "description": "Elixir Livebook is an interactive, web-based notebook platform for Elixir that combines code, documentation, and visualizations in a single document. Similar to Jupyter notebooks, it allows developers to write and execute Elixir code in real-time, making it ideal for data exploration, prototyping, learning, and collaborative development. Livebook features rich markdown support, built-in charting capabilities, and seamless integration with the Elixir ecosystem.", "install_methods": [ { @@ -33,7 +33,7 @@ }, "notes": [ { - "text": "Show Livebook password: `cat /opt/livebook.creds`", + "text": "Show initial Livebook password: `cat ~/livebook.creds`", "type": "info" } ] diff --git a/install/livebook-install.sh b/install/livebook-install.sh index 95f46c137..1d24e25eb 100644 --- a/install/livebook-install.sh +++ b/install/livebook-install.sh @@ -67,10 +67,10 @@ export ERLANG_BIN="/opt/livebook/.elixir-install/installs/otp/\${ERLANG_VERSION} export ELIXIR_BIN="/opt/livebook/.elixir-install/installs/elixir/\${ELIXIR_VERSION}/bin" export PATH="\$ESCRIPTS_BIN:\$ERLANG_BIN:\$ELIXIR_BIN:\$PATH" EOF -cat </opt/livebook/livebook.creds -Livebook-Credentials -Livebook Password: $LIVEBOOK_PASSWORD -EOF +{ + echo "Livebook-Credentials" + echo "Livebook Password: $LIVEBOOK_PASSWORD" +} >>~/livebook.creds msg_ok "Installed Erlang $ERLANG_VERSION and Elixir $ELIXIR_VERSION" msg_info "Installing Livebook" From 546130b6e0a36569695aeb6b99a319066e9294ff Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 30 Oct 2025 09:53:51 +0100 Subject: [PATCH 1610/1733] Add Infiscal scritp --- ct/infiscal.sh | 0 install/infiscal-install.sh | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 ct/infiscal.sh create mode 100644 install/infiscal-install.sh diff --git a/ct/infiscal.sh b/ct/infiscal.sh new file mode 100644 index 000000000..e69de29bb diff --git a/install/infiscal-install.sh b/install/infiscal-install.sh new file mode 100644 index 000000000..e69de29bb From a870fa373ca15bcf4e5e9b8640acb7b93263243a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 30 Oct 2025 09:58:08 +0100 Subject: [PATCH 1611/1733] cleanup --- ct/bentopdf.sh | 62 ------- ct/bookstack.sh | 81 --------- ct/jellyfin.sh | 53 ------ ct/livebook.sh | 61 ------- ct/reitti.sh | 68 -------- frontend/public/json/bentopdf.json | 35 ---- frontend/public/json/livebook.json | 40 ----- frontend/public/json/reitti.json | 40 ----- install/bentopdf-install.sh | 53 ------ install/livebook-install.sh | 106 ----------- install/proxmox-datacenter-manager-install.sh | 33 ---- install/reitti-install.sh | 165 ------------------ 12 files changed, 797 deletions(-) delete mode 100644 ct/bentopdf.sh delete mode 100644 ct/bookstack.sh delete mode 100644 ct/jellyfin.sh delete mode 100755 ct/livebook.sh delete mode 100644 ct/reitti.sh delete mode 100644 frontend/public/json/bentopdf.json delete mode 100644 frontend/public/json/livebook.json delete mode 100644 frontend/public/json/reitti.json delete mode 100644 install/bentopdf-install.sh delete mode 100644 install/livebook-install.sh delete mode 100644 install/proxmox-datacenter-manager-install.sh delete mode 100644 install/reitti-install.sh diff --git a/ct/bentopdf.sh b/ct/bentopdf.sh deleted file mode 100644 index 0a9650efb..000000000 --- a/ct/bentopdf.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: vhsdream -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/alam00000/bentopdf - -APP="BentoPDF" -var_tags="${var_tags:-pdf-editor}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-4}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /opt/bentopdf ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - NODE_VERSION="24" setup_nodejs - - if check_for_gh_release "bentopdf" "alam00000/bentopdf"; then - msg_info "Stopping Service" - systemctl stop bentopdf - msg_ok "Stopped Service" - - CLEAN_INSTALL=1 fetch_and_deploy_gh_release "bentopdf" "alam00000/bentopdf" "tarball" "latest" "/opt/bentopdf" - - msg_info "Updating BentoPDF" - cd /opt/bentopdf - $STD npm ci --no-audit --no-fund - export SIMPLE_MODE=true - $STD npm run build -- --mode production - msg_ok "Updated BentoPDF" - - msg_info "Starting Service" - systemctl start bentopdf - msg_ok "Started Service" - msg_ok "Updated Successfully!" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" diff --git a/ct/bookstack.sh b/ct/bookstack.sh deleted file mode 100644 index 2034adbbc..000000000 --- a/ct/bookstack.sh +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: MickLesk (Canbiz) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/BookStackApp/BookStack - -APP="Bookstack" -var_tags="${var_tags:-organizer}" -var_cpu="${var_cpu:-1}" -var_ram="${var_ram:-1024}" -var_disk="${var_disk:-4}" -var_os="${var_os:-debian}" -var_version="${var_version:-12}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - - if [[ ! -d /opt/bookstack ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - if check_for_gh_release "bookstack" "BookStackApp/BookStack"; then - msg_info "Stopping Apache2" - systemctl stop apache2 - msg_ok "Services Stopped" - - msg_info "Backing up data" - mv /opt/bookstack /opt/bookstack-backup - msg_ok "Backup finished" - - setup_mariadb - fetch_and_deploy_gh_release "bookstack" "BookStackApp/BookStack" - PHP_MODULE="ldap,tidy,bz2,mysqli" PHP_FPM="YES" PHP_APACHE="YES" PHP_VERSION="8.3" setup_php - setup_composer - - msg_info "Restoring backup" - cp /opt/bookstack-backup/.env /opt/bookstack/.env - [[ -d /opt/bookstack-backup/public/uploads ]] && cp -a /opt/bookstack-backup/public/uploads/. /opt/bookstack/public/uploads/ - [[ -d /opt/bookstack-backup/storage/uploads ]] && cp -a /opt/bookstack-backup/storage/uploads/. /opt/bookstack/storage/uploads/ - [[ -d /opt/bookstack-backup/themes ]] && cp -a /opt/bookstack-backup/themes/. /opt/bookstack/themes/ - msg_ok "Backup restored" - - msg_info "Configuring BookStack" - cd /opt/bookstack - export COMPOSER_ALLOW_SUPERUSER=1 - $STD composer install --no-dev - $STD php artisan migrate --force - chown www-data:www-data -R /opt/bookstack /opt/bookstack/bootstrap/cache /opt/bookstack/public/uploads /opt/bookstack/storage - chmod -R 755 /opt/bookstack /opt/bookstack/bootstrap/cache /opt/bookstack/public/uploads /opt/bookstack/storage - chmod -R 775 /opt/bookstack/storage /opt/bookstack/bootstrap/cache /opt/bookstack/public/uploads - chmod -R 640 /opt/bookstack/.env - msg_ok "Configured BookStack" - - msg_info "Starting Apache2" - systemctl start apache2 - msg_ok "Started Apache2" - - msg_info "Cleaning Up" - rm -rf /opt/bookstack-backup - msg_ok "Cleaned" - msg_ok "Updated Successfully" - fi - exit -} -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}${CL}" diff --git a/ct/jellyfin.sh b/ct/jellyfin.sh deleted file mode 100644 index 4593ab21c..000000000 --- a/ct/jellyfin.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://jellyfin.org/ - -APP="Jellyfin" -var_tags="${var_tags:-media}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-8}" -var_os="${var_os:-ubuntu}" -var_version="${var_version:-24.10}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /usr/lib/jellyfin ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - msg_info "Updating Intel Dependencies" - fetch_and_deploy_gh_release "intel-igc-core-2" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-core-2_*_amd64.deb" - fetch_and_deploy_gh_release "intel-igc-opencl-2" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-opencl-2_*_amd64.deb" - fetch_and_deploy_gh_release "intel-libgdgmm12" "intel/compute-runtime" "binary" "latest" "" "libigdgmm12_*_amd64.deb" - fetch_and_deploy_gh_release "intel-opencl-icd" "intel/compute-runtime" "binary" "latest" "" "intel-opencl-icd_*_amd64.deb" - msg_ok "Updated Intel Dependencies" - - msg_info "Updating ${APP} LXC" - $STD apt-get update - $STD apt-get -y upgrade - $STD apt-get -y --with-new-pkgs upgrade jellyfin jellyfin-server - msg_ok "Updated ${APP} LXC" - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8096${CL}" diff --git a/ct/livebook.sh b/ct/livebook.sh deleted file mode 100755 index 496503a30..000000000 --- a/ct/livebook.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/refs/heads/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: dkuku -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/livebook-dev/livebook - -APP="Livebook" -var_tags="${var_tags:-development}" -var_disk="${var_disk:-4}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-1024}" -var_os="${var_os:-ubuntu}" -var_version="${var_version:-24.04}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - - if [[ ! -f /opt/livebook/.mix/escripts/livebook ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if check_for_gh_release "livebook" "livebook-dev/livebook"; then - msg_info "Stopping Service" - systemctl stop livebook - msg_info "Stopped Service" - - msg_info "Updating Container" - $STD apt update - $STD apt upgrade -y - msg_ok "Updated Container" - - msg_info "Updating Livebook" - source /opt/livebook/.env - cd /opt/livebook - $STD mix escript.install hex livebook --force - - chown -R livebook:livebook /opt/livebook /data - systemctl start livebook - msg_ok "Updated Livebook" - msg_ok "Updated Successfully!" - fi - exit -} - -start -build_container -description - -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" diff --git a/ct/reitti.sh b/ct/reitti.sh deleted file mode 100644 index 1025051cb..000000000 --- a/ct/reitti.sh +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 community-scripts ORG -# Author: MickLesk (CanbiZ) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/dedicatedcode/reitti - -APP="Reitti" -var_tags="${var_tags:-location-tracker}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-4096}" -var_disk="${var_disk:-15}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -f /opt/reitti/reitti.jar ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - if check_for_gh_release "reitti" "dedicatedcode/reitti"; then - msg_info "Stopping Service" - systemctl stop reitti - msg_ok "Stopped Service" - - rm -f /opt/reitti/reitti.jar - USE_ORIGINAL_FILENAME="true" fetch_and_deploy_gh_release "reitti" "dedicatedcode/reitti" "singlefile" "latest" "/opt/reitti" "reitti-app.jar" - mv /opt/reitti/reitti-*.jar /opt/reitti/reitti.jar - - msg_info "Starting Service" - systemctl start reitti - msg_ok "Started Service" - msg_ok "Updated Successfully!" - fi - if check_for_gh_release "photon" "dedicatedcode/reitti"; then - msg_info "Stopping Service" - systemctl stop photon - msg_ok "Stopped Service" - - rm -f /opt/photon/photon.jar - USE_ORIGINAL_FILENAME="true" fetch_and_deploy_gh_release "photon" "komoot/photon" "singlefile" "latest" "/opt/photon" "photon-0*.jar" - mv /opt/photon/photon-*.jar /opt/photon/photon.jar - - msg_info "Starting Service" - systemctl start photon - msg_ok "Started Service" - msg_ok "Updated Successfully!" - fi - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" diff --git a/frontend/public/json/bentopdf.json b/frontend/public/json/bentopdf.json deleted file mode 100644 index 2a09b34cf..000000000 --- a/frontend/public/json/bentopdf.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "BentoPDF", - "slug": "bentopdf", - "categories": [ - 12 - ], - "date_created": "2025-10-30", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 8080, - "documentation": "https://github.com/alam00000/bentopdf", - "website": "https://www.bentopdf.com", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/bentopdf.webp", - "config_path": "", - "description": "A privacy-first, 100% client-side PDF Toolkit. No signups/accounts, works in the browser, online or offline.", - "install_methods": [ - { - "type": "default", - "script": "ct/bentopdf.sh", - "resources": { - "cpu": 1, - "ram": 2048, - "hdd": 4, - "os": "debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [] -} diff --git a/frontend/public/json/livebook.json b/frontend/public/json/livebook.json deleted file mode 100644 index 0bad4f4f3..000000000 --- a/frontend/public/json/livebook.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "Livebook", - "slug": "livebook", - "categories": [ - 20 - ], - "date_created": "2025-10-30", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 8080, - "documentation": "https://hexdocs.pm/livebook/readme.html", - "config_path": null, - "website": "https://livebook.dev", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/livebook.webp", - "description": "Elixir Livebook is an interactive, web-based notebook platform for Elixir that combines code, documentation, and visualizations in a single document. Similar to Jupyter notebooks, it allows developers to write and execute Elixir code in real-time, making it ideal for data exploration, prototyping, learning, and collaborative development. Livebook features rich markdown support, built-in charting capabilities, and seamless integration with the Elixir ecosystem.", - "install_methods": [ - { - "type": "default", - "script": "ct/livebook.sh", - "resources": { - "cpu": 1, - "ram": 1024, - "hdd": 4, - "os": "Ubuntu", - "version": "24.04" - } - } - ], - "default_credentials": { - "username": null, - "password": null - }, - "notes": [ - { - "text": "Show initial Livebook password: `cat ~/livebook.creds`", - "type": "info" - } - ] -} diff --git a/frontend/public/json/reitti.json b/frontend/public/json/reitti.json deleted file mode 100644 index e97428ca3..000000000 --- a/frontend/public/json/reitti.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "Reitti", - "slug": "reitti", - "categories": [ - 21 - ], - "date_created": "2025-10-28", - "type": "ct", - "updateable": true, - "privileged": false, - "interface_port": 8080, - "documentation": "https://github.com/dedicatedcode/reitti", - "config_path": "/opt/reitti/.env", - "website": "https://www.dedicatedcode.com/projects/reitti/", - "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/reitti.webp", - "description": "Reitti is a self-hosted location tracking and analysis platform that detects significant places, trip patterns, and integrates with OwnTracks, GPSLogger, and Immich. It uses PostgreSQL + PostGIS, RabbitMQ, Redis, and an optional Photon geocoder.", - "install_methods": [ - { - "type": "default", - "script": "ct/reitti.sh", - "resources": { - "cpu": 4, - "ram": 6144, - "hdd": 20, - "os": "debian", - "version": "13" - } - } - ], - "default_credentials": { - "username": "admin", - "password": "admin" - }, - "notes": [ - { - "text": "Photon Geocoder must be running at http://127.0.0.1:2322. The installer sets this up Photon automatically, but without sample data. (filesize is big).", - "type": "info" - } - ] -} diff --git a/install/bentopdf-install.sh b/install/bentopdf-install.sh deleted file mode 100644 index f426200b2..000000000 --- a/install/bentopdf-install.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: vhsdream -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/alam00000/bentopdf - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -NODE_VERSION="24" setup_nodejs -fetch_and_deploy_gh_release "bentopdf" "alam00000/bentopdf" "tarball" "latest" "/opt/bentopdf" - -msg_info "Setup BentoPDF" -cd /opt/bentopdf -$STD npm ci --no-audit --no-fund -export SIMPLE_MODE=true -$STD npm run build -- --mode production -msg_ok "Setup BentoPDF" - -msg_info "Creating Service" -cat </etc/systemd/system/bentopdf.service -[Unit] -Description=BentoPDF Service -After=network.target - -[Service] -Type=simple -WorkingDirectory=/opt/bentopdf -ExecStart=/usr/bin/npx serve dist -p 8080 -Restart=always -RestartSec=10 - -[Install] -WantedBy=multi-user.target -EOF - -systemctl -q enable --now bentopdf -msg_ok "Created & started service" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -$STD apt-get -y clean -msg_ok "Cleaned" diff --git a/install/livebook-install.sh b/install/livebook-install.sh deleted file mode 100644 index 1d24e25eb..000000000 --- a/install/livebook-install.sh +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: dkuku -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://github.com/livebook-dev/livebook - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt-get install -y \ - build-essential \ - ca-certificates \ - cmake \ - git \ - libncurses5-dev -msg_ok "Installed Dependencies" - -msg_info "Creating livebook user" -mkdir -p /opt/livebook /data -export HOME=/opt/livebook -$STD adduser --system --group --home /opt/livebook --shell /bin/bash livebook -msg_ok "Created livebook user" - -msg_warn "WARNING: This script will run an external installer from a third-party source (https://elixir-lang.org)." -msg_warn "The following code is NOT maintained or audited by our repository." -msg_warn "If you have any doubts or concerns, please review the installer code before proceeding:" -msg_custom "${TAB3}${GATEWAY}${BGN}${CL}" "\e[1;34m" "→ https://elixir-lang.org/install.sh" -echo -read -r -p "${TAB3}Do you want to continue? [y/N]: " CONFIRM -if [[ ! "$CONFIRM" =~ ^([yY][eE][sS]|[yY])$ ]]; then - msg_error "Aborted by user. No changes have been made." - exit 10 -fi -curl -fsSO https://elixir-lang.org/install.sh -$STD sh install.sh elixir@latest otp@latest - -msg_info "Setup Erlang and Elixir" -ERLANG_VERSION=$(ls /opt/livebook/.elixir-install/installs/otp/ | head -n1) -ELIXIR_VERSION=$(ls /opt/livebook/.elixir-install/installs/elixir/ | head -n1) -LIVEBOOK_PASSWORD=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c16) - -export ERLANG_BIN="/opt/livebook/.elixir-install/installs/otp/$ERLANG_VERSION/bin" -export ELIXIR_BIN="/opt/livebook/.elixir-install/installs/elixir/$ELIXIR_VERSION/bin" -export PATH="$ERLANG_BIN:$ELIXIR_BIN:$PATH" - -$STD mix local.hex --force -$STD mix local.rebar --force -$STD mix escript.install hex livebook --force - -cat </opt/livebook/.env -export HOME=/opt/livebook -export ERLANG_VERSION=$ERLANG_VERSION -export ELIXIR_VERSION=$ELIXIR_VERSION -export LIVEBOOK_PORT=8080 -export LIVEBOOK_IP="::" -export LIVEBOOK_HOME=/data -export LIVEBOOK_PASSWORD="$LIVEBOOK_PASSWORD" -export ESCRIPTS_BIN=/opt/livebook/.mix/escripts -export ERLANG_BIN="/opt/livebook/.elixir-install/installs/otp/\${ERLANG_VERSION}/bin" -export ELIXIR_BIN="/opt/livebook/.elixir-install/installs/elixir/\${ELIXIR_VERSION}/bin" -export PATH="\$ESCRIPTS_BIN:\$ERLANG_BIN:\$ELIXIR_BIN:\$PATH" -EOF -{ - echo "Livebook-Credentials" - echo "Livebook Password: $LIVEBOOK_PASSWORD" -} >>~/livebook.creds -msg_ok "Installed Erlang $ERLANG_VERSION and Elixir $ELIXIR_VERSION" - -msg_info "Installing Livebook" -cat </etc/systemd/system/livebook.service -[Unit] -Description=Livebook -After=network.target - -[Service] -Type=exec -User=livebook -Group=livebook -WorkingDirectory=/data -EnvironmentFile=-/opt/livebook/.env -ExecStart=/bin/bash -c 'source /opt/livebook/.env && cd /opt/livebook && livebook server' -Restart=always -RestartSec=5 - -[Install] -WantedBy=multi-user.target -EOF -chown -R livebook:livebook /opt/livebook /data -systemctl enable -q --now livebook -msg_ok "Installed Livebook" - -motd_ssh -customize - -msg_info "Cleaning Up" -$STD apt autoremove -y -$STD apt autoclean -y -$STD apt clean -y -msg_ok "Cleaned Up" diff --git a/install/proxmox-datacenter-manager-install.sh b/install/proxmox-datacenter-manager-install.sh deleted file mode 100644 index e9a3c3006..000000000 --- a/install/proxmox-datacenter-manager-install.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: CrazyWolf13 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: Proxmox Server Solution GmbH - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Proxmox Datacenter Manager" -curl -fsSL https://enterprise.proxmox.com/debian/proxmox-archive-keyring-trixie.gpg -o /usr/share/keyrings/proxmox-archive-keyring.gpg -echo "deb [signed-by=/usr/share/keyrings/proxmox-archive-keyring.gpg] http://download.proxmox.com/debian/pdm bookworm pdm-test " >/etc/apt/sources.list.d/pdm-test.list -$STD apt-get update -DEBIAN_FRONTEND=noninteractive -$STD apt-get -o Dpkg::Options::="--force-confdef" \ - -o Dpkg::Options::="--force-confold" \ - install -y proxmox-datacenter-manager \ - proxmox-datacenter-manager-ui -msg_ok "Installed Proxmox Datacenter Manager" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" diff --git a/install/reitti-install.sh b/install/reitti-install.sh deleted file mode 100644 index 021cdc3be..000000000 --- a/install/reitti-install.sh +++ /dev/null @@ -1,165 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 community-scripts ORG -# Author: MickLesk (CanbiZ) -# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://github.com/dedicatedcode/reitti - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt install -y \ - redis-server \ - rabbitmq-server \ - libpq-dev \ - zstd -msg_ok "Installed Dependencies" - -JAVA_VERSION="24" setup_java -PG_VERSION="17" PG_MODULES="postgis" setup_postgresql - -msg_info "Setting up PostgreSQL" -DB_NAME="reitti_db" -DB_USER="reitti" -DB_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" -$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" -$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" -$STD sudo -u postgres psql -d "$DB_NAME" -c "CREATE EXTENSION IF NOT EXISTS postgis;" -$STD sudo -u postgres psql -d "$DB_NAME" -c "CREATE EXTENSION IF NOT EXISTS postgis_topology;" -{ - echo "Reitti Credentials" - echo "Database Name: $DB_NAME" - echo "Database User: $DB_USER" - echo "Database Password: $DB_PASS" -} >>~/reitti.creds -msg_ok "PostgreSQL Setup Completed" - -msg_info "Configuring RabbitMQ" -RABBIT_USER="reitti" -RABBIT_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" -RABBIT_VHOST="/" -$STD rabbitmqctl add_user "$RABBIT_USER" "$RABBIT_PASS" -$STD rabbitmqctl add_vhost "$RABBIT_VHOST" -$STD rabbitmqctl set_permissions -p "$RABBIT_VHOST" "$RABBIT_USER" ".*" ".*" ".*" -$STD rabbitmqctl set_user_tags "$RABBIT_USER" administrator -{ - echo "" - echo "Reitti Credentials" - echo "RabbitMQ User: $RABBIT_USER" - echo "RabbitMQ Password: $RABBIT_PASS" -} >>~/reitti.creds -msg_ok "Configured RabbitMQ" - -USE_ORIGINAL_FILENAME="true" fetch_and_deploy_gh_release "reitti" "dedicatedcode/reitti" "singlefile" "latest" "/opt/reitti" "reitti-app.jar" -mv /opt/reitti/reitti-*.jar /opt/reitti/reitti.jar -USE_ORIGINAL_FILENAME="true" fetch_and_deploy_gh_release "photon" "komoot/photon" "singlefile" "latest" "/opt/photon" "photon-0*.jar" -mv /opt/photon/photon-*.jar /opt/photon/photon.jar - -msg_info "Creating Reitti Configuration-File" -cat </opt/reitti/application.properties -# Reitti Server Base URI -reitti.server.advertise-uri=http://127.0.0.1:8080 - -# PostgreSQL Database Connection -spring.datasource.url=jdbc:postgresql://127.0.0.1:5432/$DB_NAME -spring.datasource.username=$DB_USER -spring.datasource.password=$DB_PASS -spring.datasource.driver-class-name=org.postgresql.Driver - -# Flyway Database Migrations -spring.flyway.enabled=true -spring.flyway.locations=classpath:db/migration -spring.flyway.baseline-on-migrate=true - -# RabbitMQ (Message Queue) -spring.rabbitmq.host=127.0.0.1 -spring.rabbitmq.port=5672 -spring.rabbitmq.username=$RABBIT_USER -spring.rabbitmq.password=$RABBIT_PASS - -# Redis (Cache) -spring.data.redis.host=127.0.0.1 -spring.data.redis.port=6379 - -# Server Port -server.port=8080 - -# Optional: Logging & Performance -logging.level.root=INFO -spring.jpa.hibernate.ddl-auto=none -spring.datasource.hikari.maximum-pool-size=10 - -# OIDC / Security Settings -reitti.security.oidc.registration.enabled=false - -# Photon (Geocoding) -PHOTON_BASE_URL=http://127.0.0.1:2322 -PROCESSING_WAIT_TIME=15 -PROCESSING_BATCH_SIZE=1000 -PROCESSING_WORKERS_PER_QUEUE=4-16 - -# Disable potentially dangerous features unless needed -DANGEROUS_LIFE=false -EOF -msg_ok "Created Configuration-File for Reitti" - -msg_info "Creating Services" -cat </etc/systemd/system/reitti.service -[Unit] -Description=Reitti -After=network.target postgresql.service redis-server.service rabbitmq-server.service photon.service -Wants=postgresql.service redis-server.service rabbitmq-server.service photon.service - -[Service] -Type=simple -WorkingDirectory=/opt/reitti/ -ExecStart=/usr/bin/java --enable-native-access=ALL-UNNAMED -jar -Xmx2g reitti.jar -TimeoutStopSec=20 -KillMode=process -Restart=on-failure - -[Install] -WantedBy=multi-user.target -EOF - -cat <<'EOF' >/etc/systemd/system/photon.service -[Unit] -Description=Photon Geocoding Service (Germany, OpenSearch) -After=network.target - -[Service] -Type=simple -WorkingDirectory=/opt/photon -ExecStart=/usr/bin/java -Xmx4g -jar photon.jar \ - -data-dir /opt/photon \ - -listen-port 2322 \ - -listen-ip 0.0.0.0 \ - -cors-any -Restart=on-failure -TimeoutStopSec=20 - -[Install] -WantedBy=multi-user.target -EOF - -systemctl enable -q --now photon -systemctl enable -q --now reitti -msg_ok "Created Services" - -motd_ssh -customize - -msg_info "Cleaning up" -$STD apt -y autoremove -$STD apt -y autoclean -$STD apt -y clean -msg_ok "Cleaned" From 6cea4d643feb82c0a7b4a0720e57ef6a895cb239 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 30 Oct 2025 08:58:31 +0000 Subject: [PATCH 1612/1733] Update .app files --- ct/headers/bentopdf | 6 ------ ct/headers/bookstack | 6 ------ ct/headers/jellyfin | 6 ------ ct/headers/livebook | 6 ------ ct/headers/reitti | 6 ------ ct/headers/tracktor | 6 ++++++ 6 files changed, 6 insertions(+), 30 deletions(-) delete mode 100644 ct/headers/bentopdf delete mode 100644 ct/headers/bookstack delete mode 100644 ct/headers/jellyfin delete mode 100644 ct/headers/livebook delete mode 100644 ct/headers/reitti create mode 100644 ct/headers/tracktor diff --git a/ct/headers/bentopdf b/ct/headers/bentopdf deleted file mode 100644 index 692eff64b..000000000 --- a/ct/headers/bentopdf +++ /dev/null @@ -1,6 +0,0 @@ - ____ __ ____ ____ ______ - / __ )___ ____ / /_____ / __ \/ __ \/ ____/ - / __ / _ \/ __ \/ __/ __ \/ /_/ / / / / /_ - / /_/ / __/ / / / /_/ /_/ / ____/ /_/ / __/ -/_____/\___/_/ /_/\__/\____/_/ /_____/_/ - diff --git a/ct/headers/bookstack b/ct/headers/bookstack deleted file mode 100644 index f68646662..000000000 --- a/ct/headers/bookstack +++ /dev/null @@ -1,6 +0,0 @@ - ____ __ __ __ - / __ )____ ____ / /_______/ /_____ ______/ /__ - / __ / __ \/ __ \/ //_/ ___/ __/ __ `/ ___/ //_/ - / /_/ / /_/ / /_/ / ,< (__ ) /_/ /_/ / /__/ ,< -/_____/\____/\____/_/|_/____/\__/\__,_/\___/_/|_| - diff --git a/ct/headers/jellyfin b/ct/headers/jellyfin deleted file mode 100644 index d905c4dba..000000000 --- a/ct/headers/jellyfin +++ /dev/null @@ -1,6 +0,0 @@ - __ ____ _____ - / /__ / / /_ __/ __(_)___ - __ / / _ \/ / / / / / /_/ / __ \ -/ /_/ / __/ / / /_/ / __/ / / / / -\____/\___/_/_/\__, /_/ /_/_/ /_/ - /____/ diff --git a/ct/headers/livebook b/ct/headers/livebook deleted file mode 100644 index 6ff6b47ef..000000000 --- a/ct/headers/livebook +++ /dev/null @@ -1,6 +0,0 @@ - __ _ __ __ - / / (_) _____ / /_ ____ ____ / /__ - / / / / | / / _ \/ __ \/ __ \/ __ \/ //_/ - / /___/ /| |/ / __/ /_/ / /_/ / /_/ / ,< -/_____/_/ |___/\___/_.___/\____/\____/_/|_| - diff --git a/ct/headers/reitti b/ct/headers/reitti deleted file mode 100644 index 8e7627609..000000000 --- a/ct/headers/reitti +++ /dev/null @@ -1,6 +0,0 @@ - ____ _ __ __ _ - / __ \___ (_) /_/ /_(_) - / /_/ / _ \/ / __/ __/ / - / _, _/ __/ / /_/ /_/ / -/_/ |_|\___/_/\__/\__/_/ - diff --git a/ct/headers/tracktor b/ct/headers/tracktor new file mode 100644 index 000000000..d4802c5aa --- /dev/null +++ b/ct/headers/tracktor @@ -0,0 +1,6 @@ + __ __ __ + / /__________ ______/ /__/ /_____ _____ + / __/ ___/ __ `/ ___/ //_/ __/ __ \/ ___/ +/ /_/ / / /_/ / /__/ ,< / /_/ /_/ / / +\__/_/ \__,_/\___/_/|_|\__/\____/_/ + From 23e08b5f26401034f5bd819678177a0bc397492a Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 30 Oct 2025 11:12:40 +0100 Subject: [PATCH 1613/1733] Update Infisical --- ct/{infiscal.sh => infisical.sh} | 0 install/infiscal-install.sh | 0 install/infisical-install.sh | 72 ++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+) rename ct/{infiscal.sh => infisical.sh} (100%) delete mode 100644 install/infiscal-install.sh create mode 100644 install/infisical-install.sh diff --git a/ct/infiscal.sh b/ct/infisical.sh similarity index 100% rename from ct/infiscal.sh rename to ct/infisical.sh diff --git a/install/infiscal-install.sh b/install/infiscal-install.sh deleted file mode 100644 index e69de29bb..000000000 diff --git a/install/infisical-install.sh b/install/infisical-install.sh new file mode 100644 index 000000000..92467fb64 --- /dev/null +++ b/install/infisical-install.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: https://infisical.com/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt install -y \ + apt-transport-https \ + ca-certificates +msg_ok "Installed Dependencies" + +msg_info "Setting up Infisical repository" +curl -fsSL "https://artifacts-infisical-core.infisical.com/infisical.gpg" | gpg --dearmor >/etc/apt/trusted.gpg.d/infisical.gpg +cat </etc/apt/sources.list.d/infisical.sources +Types: deb +URIs: https://artifacts-infisical-core.infisical.com/deb +Suites: stable +Components: main +Signed-By: /etc/apt/trusted.gpg.d/infisical.gpg +EOF +msg_ok "Setup Infisical repository" + +PG_VERSION="17" setup_postgresql + +msg_info "Setting up PostgreSQL" +DB_NAME="infiscal_db" +DB_USER="infiscal" +DB_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" +$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" +$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" +{ + echo "Infiscal Credentials" + echo "Database Name: $DB_NAME" + echo "Database User: $DB_USER" + echo "Database Password: $DB_PASS" +} >>~/infiscal.creds +msg_ok "Setup PostgreSQL" + +msg_info "Setting up Infisical" +$STD apt install -y infisical-core +mkdir -p /etc/infisical +cat </etc/infisical/infisical.rb +infisical_core['ENCRYPTION_KEY'] = '6c1fe4e407b8911c104518103505b218' +infisical_core['AUTH_SECRET'] = '5lrMXKKWCVocS/uerPsl7V+TX/aaUaI7iDkgl3tSmLE=' + +infisical_core['DB_CONNECTION_URI'] = 'postgres://${DB_USER}:${DB_PASS}@localhost:5432/${DB_NAME}' +infisical_core['REDIS_URL'] = 'redis://localhost:6379' +EOF +$STD infisical-ctl reconfigure +msg_ok "Setup Infisical" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt -y autoremove +$STD apt -y autoclean +$STD apt -y clean +msg_ok "Cleaned" From 82e96b7dba483f429471b5cd6ce427f23c27424d Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 30 Oct 2025 11:27:45 +0100 Subject: [PATCH 1614/1733] Update infisical --- ct/infisical.sh | 60 ++++++++++++++++++++++++++++++++++++ install/infisical-install.sh | 6 ++-- 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/ct/infisical.sh b/ct/infisical.sh index e69de29bb..4413f0c75 100644 --- a/ct/infisical.sh +++ b/ct/infisical.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://infisical.com/ + +APP="Infisical" +var_tags="${var_tags:-auth}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /etc/infisical ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + msg_info "Stopping service" + $STD inisical-ctl stop + msg_ok "Service stopped" + + msg_info "Creating backup" + DB_PASS=$(grep -Po '(?<=^Database Password:\s).*' ~/infisical.creds | head -n1) + PGPASSWORD=$DB_PASS pg_dump -U infisical -h localhost -d infisical_db > /opt/infisical_backup.sql + msg_ok "Created backup" + + msg_info "Updating Infisical" + $STD apt update + $STD apt install -y infisical-core + $STD infisical-ctl reconfigure + msg_ok "Updated Infisical" + + msg_info "Starting service" + infisical-ctl start + msg_ok "Started service" + msg_ok "Updated successfully" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" diff --git a/install/infisical-install.sh b/install/infisical-install.sh index 92467fb64..3a8c77f99 100644 --- a/install/infisical-install.sh +++ b/install/infisical-install.sh @@ -33,8 +33,8 @@ msg_ok "Setup Infisical repository" PG_VERSION="17" setup_postgresql msg_info "Setting up PostgreSQL" -DB_NAME="infiscal_db" -DB_USER="infiscal" +DB_NAME="infisical_db" +DB_USER="infisical" DB_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" $STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" @@ -46,7 +46,7 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" echo "Database Name: $DB_NAME" echo "Database User: $DB_USER" echo "Database Password: $DB_PASS" -} >>~/infiscal.creds +} >>~/infisical.creds msg_ok "Setup PostgreSQL" msg_info "Setting up Infisical" From d3d6d98e550a63cc20e0e8083fc3f96fedc0e3b5 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 30 Oct 2025 10:28:01 +0000 Subject: [PATCH 1615/1733] Update .app files --- ct/headers/infisical | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/infisical diff --git a/ct/headers/infisical b/ct/headers/infisical new file mode 100644 index 000000000..d378f9dcb --- /dev/null +++ b/ct/headers/infisical @@ -0,0 +1,6 @@ + ____ _____ _ __ + / _/___ / __(_)____(_)________ _/ / + / // __ \/ /_/ / ___/ / ___/ __ `/ / + _/ // / / / __/ (__ ) / /__/ /_/ / / +/___/_/ /_/_/ /_/____/_/\___/\__,_/_/ + From 67d5281add15d1147138d5a8ddf535bae2c4b74e Mon Sep 17 00:00:00 2001 From: tremor021 Date: Thu, 30 Oct 2025 11:53:43 +0100 Subject: [PATCH 1616/1733] Update infisical --- ct/infisical.sh | 2 +- frontend/public/json/infisical.json | 35 +++++++++++++++++++++++++++++ install/infisical-install.sh | 10 +++++---- 3 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 frontend/public/json/infisical.json diff --git a/ct/infisical.sh b/ct/infisical.sh index 4413f0c75..9af6940af 100644 --- a/ct/infisical.sh +++ b/ct/infisical.sh @@ -57,4 +57,4 @@ description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" diff --git a/frontend/public/json/infisical.json b/frontend/public/json/infisical.json new file mode 100644 index 000000000..777b22ea8 --- /dev/null +++ b/frontend/public/json/infisical.json @@ -0,0 +1,35 @@ +{ + "name": "Infisical", + "slug": "infisical", + "categories": [ + 6 + ], + "date_created": "2025-09-04", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 8080, + "documentation": "https://infisical.com/docs/documentation/getting-started/overview", + "config_path": "/etc/infisical/infisical.rb", + "website": "https://infisical.com/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/infisical.webp", + "description": "Secrets, certificates, and access management on autopilot. All-in-one platform to securely manage application secrets, certificates, SSH keys, and configurations across your team and infrastructure.", + "install_methods": [ + { + "type": "default", + "script": "ct/infisical.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 4, + "os": "Debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} diff --git a/install/infisical-install.sh b/install/infisical-install.sh index 3a8c77f99..01b22112c 100644 --- a/install/infisical-install.sh +++ b/install/infisical-install.sh @@ -16,7 +16,8 @@ update_os msg_info "Installing Dependencies" $STD apt install -y \ apt-transport-https \ - ca-certificates + ca-certificates \ + redis msg_ok "Installed Dependencies" msg_info "Setting up Infisical repository" @@ -32,7 +33,7 @@ msg_ok "Setup Infisical repository" PG_VERSION="17" setup_postgresql -msg_info "Setting up PostgreSQL" +msg_info "Configuring PostgreSQL" DB_NAME="infisical_db" DB_USER="infisical" DB_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | cut -c1-13)" @@ -47,15 +48,16 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" echo "Database User: $DB_USER" echo "Database Password: $DB_PASS" } >>~/infisical.creds -msg_ok "Setup PostgreSQL" +msg_ok "Configured PostgreSQL" msg_info "Setting up Infisical" +IP_ADDR=$(hostname -I | awk '{print $1}') $STD apt install -y infisical-core mkdir -p /etc/infisical cat </etc/infisical/infisical.rb infisical_core['ENCRYPTION_KEY'] = '6c1fe4e407b8911c104518103505b218' infisical_core['AUTH_SECRET'] = '5lrMXKKWCVocS/uerPsl7V+TX/aaUaI7iDkgl3tSmLE=' - +infisical_core['HOST'] = '$IP_ADDR' infisical_core['DB_CONNECTION_URI'] = 'postgres://${DB_USER}:${DB_PASS}@localhost:5432/${DB_NAME}' infisical_core['REDIS_URL'] = 'redis://localhost:6379' EOF From b17aebea6099ee9f7e36e7ea47e489c863645f55 Mon Sep 17 00:00:00 2001 From: Romain PINSOLLE Date: Thu, 30 Oct 2025 10:56:46 +0000 Subject: [PATCH 1617/1733] add snowshare --- ct/snowshare.sh | 79 ++++++++++++++++++ frontend/public/json/snowshare.json | 35 ++++++++ install/snowshare-install.sh | 122 ++++++++++++++++++++++++++++ misc/build.func | 2 +- misc/install.func | 2 +- 5 files changed, 238 insertions(+), 2 deletions(-) create mode 100644 ct/snowshare.sh create mode 100644 frontend/public/json/snowshare.json create mode 100644 install/snowshare-install.sh diff --git a/ct/snowshare.sh b/ct/snowshare.sh new file mode 100644 index 000000000..816036f49 --- /dev/null +++ b/ct/snowshare.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash +source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: TuroYT +# License: MIT +# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE + +function header_info { +clear +cat <<"EOF" + _____ _____ __ + / ___/____ ____ _ __ / ___// /_ ____ ___________ + \__ \/ __ \/ __ \ | /| / / \__ \/ __ \/ __ `/ ___/ _ \ + ___/ / / / / /_/ / |/ |/ / ___/ / / / / /_/ / / / __/ +/____/_/ /_/\____/|__/|__/ /____/_/ /_/\__,_/_/ \___/ + +EOF +} +header_info +echo -e "Loading..." +APP="SnowShare" +var_disk="8" +var_cpu="2" +var_ram="2048" +var_os="debian" +var_version="12" +variables +color +catch_errors + +function default_settings() { + CT_TYPE="1" + PW="" + CT_ID=$NEXTID + HN=$NSAPP + DISK_SIZE="$var_disk" + CORE_COUNT="$var_cpu" + RAM_SIZE="$var_ram" + BRG="vmbr0" + NET="dhcp" + GATE="" + APT_CACHER="" + APT_CACHER_IP="" + DISABLEIP6="no" + MTU="" + SD="" + NS="" + MAC="" + VLAN="" + SSH="no" + VERB="no" + echo_default +} + +function update_script() { +header_info +if [[ ! -d /opt/snowshare ]]; then + msg_error "No ${APP} Installation Found!" + exit +fi +msg_info "Updating ${APP}" +systemctl stop snowshare +cd /opt/snowshare +git pull +npm ci +npx prisma generate +npm run build +systemctl start snowshare +msg_ok "Updated ${APP}" +exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${APP} should be reachable by going to the following URL. + ${BL}http://${IP}:3000${CL} \n" \ No newline at end of file diff --git a/frontend/public/json/snowshare.json b/frontend/public/json/snowshare.json new file mode 100644 index 000000000..553971987 --- /dev/null +++ b/frontend/public/json/snowshare.json @@ -0,0 +1,35 @@ +{ + "name": "SnowShare", + "slug": "snowshare", + "categories": [ + 11 + ], + "date_created": "2025-09-24", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 3000, + "documentation": "https://github.com/TuroYT/snowshare", + "config_path": "/opt/snowshare/.env", + "website": "https://github.com/TuroYT/snowshare", + "logo": "https://github.com/TuroYT/snowshare/raw/main/public/logo.svg", + "description": "A modern, secure file and link sharing platform built with Next.js, Prisma, and NextAuth. Share URLs, code snippets, and files with customizable expiration, privacy, and QR codes.", + "install_methods": [ + { + "type": "default", + "script": "ct/snowshare.sh", + "resources": { + "cpu": 1, + "ram": 1024, + "hdd": 5, + "os": "Debian", + "version": "12" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} \ No newline at end of file diff --git a/install/snowshare-install.sh b/install/snowshare-install.sh new file mode 100644 index 000000000..f1f82aa03 --- /dev/null +++ b/install/snowshare-install.sh @@ -0,0 +1,122 @@ +#!/usr/bin/env bash + +# Couleurs pour les messages +YW=$(echo "\033[33m") +BL=$(echo "\033[36m") +RD=$(echo "\033[01;31m") +BGN=$(echo "\033[4;92m") +GN=$(echo "\033[1;92m") +DGN=$(echo "\033[32m") +CL=$(echo "\033[m") +BFR="\\r\\033[K" +HOLD="-" +CM="${GN}✓${CL}" +CROSS="${RD}✗${CL}" + +msg_info() { + local msg="$1" + echo -ne " ${HOLD} ${YW}${msg}..." +} + +msg_ok() { + local msg="$1" + echo -e "${BFR} ${CM} ${GN}${msg}${CL}" +} + +msg_error() { + local msg="$1" + echo -e "${BFR} ${CROSS} ${RD}${msg}${CL}" +} + +# Installation des dépendances système +msg_info "Updating system packages" +apt-get update &>/dev/null +apt-get upgrade -y &>/dev/null +msg_ok "Updated system packages" + +msg_info "Installing dependencies" +apt-get install -y curl sudo git wget postgresql postgresql-contrib &>/dev/null +msg_ok "Installed dependencies" + +# Installation de Node.js 20 +msg_info "Installing Node.js" +curl -fsSL https://deb.nodesource.com/setup_20.x | bash - &>/dev/null +apt-get install -y nodejs &>/dev/null +msg_ok "Installed Node.js $(node --version)" + +# Configuration de PostgreSQL +msg_info "Configuring PostgreSQL" +systemctl enable --now postgresql &>/dev/null +sudo -u postgres psql -c "CREATE DATABASE snowshare;" &>/dev/null +sudo -u postgres psql -c "CREATE USER snowshare WITH ENCRYPTED PASSWORD 'snowshare';" &>/dev/null +sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE snowshare TO snowshare;" &>/dev/null +sudo -u postgres psql -c "ALTER DATABASE snowshare OWNER TO snowshare;" &>/dev/null +msg_ok "Configured PostgreSQL" + +# Clonage du dépôt +msg_info "Cloning SnowShare repository" +git clone https://github.com/TuroYT/snowshare.git /opt/snowshare &>/dev/null +cd /opt/snowshare +msg_ok "Cloned repository" + +# Installation des dépendances NPM +msg_info "Installing NPM dependencies" +npm ci &>/dev/null +msg_ok "Installed NPM dependencies" + +# Configuration de l'environnement +msg_info "Configuring environment" +cat < /opt/snowshare/.env +DATABASE_URL="postgresql://snowshare:snowshare@localhost:5432/snowshare" +NEXTAUTH_URL="http://localhost:3000" +NEXTAUTH_SECRET="$(openssl rand -base64 32)" +ALLOW_SIGNUP=true +NODE_ENV=production +EOF +msg_ok "Configured environment" + +# Génération Prisma et migrations +msg_info "Running Prisma migrations" +npx prisma generate &>/dev/null +npx prisma migrate deploy &>/dev/null +msg_ok "Ran Prisma migrations" + +# Build de l'application +msg_info "Building SnowShare" +npm run build &>/dev/null +msg_ok "Built SnowShare" + +# Création du service systemd +msg_info "Creating systemd service" +cat </etc/systemd/system/snowshare.service +[Unit] +Description=SnowShare - Modern File Sharing Platform +After=network.target postgresql.service +Requires=postgresql.service + +[Service] +Type=simple +User=root +WorkingDirectory=/opt/snowshare +Environment=NODE_ENV=production +ExecStart=/usr/bin/npm start +Restart=on-failure +RestartSec=10 + +[Install] +WantedBy=multi-user.target +EOF +systemctl daemon-reload +systemctl enable --now snowshare.service &>/dev/null +msg_ok "Created systemd service" + +# Configuration du cron pour le nettoyage +msg_info "Setting up cleanup cron job" +(crontab -l 2>/dev/null; echo "0 2 * * * cd /opt/snowshare && /usr/bin/npm run cleanup:expired >> /var/log/snowshare-cleanup.log 2>&1") | crontab - +msg_ok "Setup cleanup cron job" + +# Nettoyage +msg_info "Cleaning up" +apt-get autoremove -y &>/dev/null +apt-get autoclean -y &>/dev/null +msg_ok "Cleaned up" \ No newline at end of file diff --git a/misc/build.func b/misc/build.func index eb2183872..dcb257d75 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2488,7 +2488,7 @@ EOF' install_ssh_keys_into_ct # Run application installer - if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)"; then + if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/TuroYT/ProxmoxVED/refs/heads/add-snowshare/install/${var_install}.sh)"; then exit $? fi } diff --git a/misc/install.func b/misc/install.func index f741b921d..84d4e6fb4 100644 --- a/misc/install.func +++ b/misc/install.func @@ -195,7 +195,7 @@ EOF systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//') msg_ok "Customized Container" fi - echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update + echo "bash -c \"\$(curl -fsSL https://github.com/TuroYT/ProxmoxVED/raw/add-snowshare/ct/${app}.sh)\"" >/usr/bin/update chmod +x /usr/bin/update if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then mkdir -p /root/.ssh From d60c38a32d649b19ecc3e9613e612818d912ac06 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 30 Oct 2025 12:21:58 +0100 Subject: [PATCH 1618/1733] Add and use cleanup_lxc function for system cleanup Introduced a new cleanup_lxc function in core.func to standardize and enhance system cleanup across scripts. Updated debian.sh and debian-install.sh to use this function instead of inline cleanup commands, improving maintainability and consistency. Also updated author and copyright information. --- ct/debian.sh | 11 +++++----- install/debian-install.sh | 19 ++++++----------- misc/core.func | 45 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 17 deletions(-) diff --git a/ct/debian.sh b/ct/debian.sh index 78010dc3d..198a0bf01 100644 --- a/ct/debian.sh +++ b/ct/debian.sh @@ -1,9 +1,9 @@ #!/usr/bin/env bash source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/build.func) -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Source: https://www.debian.org/ +# Source: APP="Debian" var_tags="${var_tags:-}" @@ -30,9 +30,10 @@ function update_script() { exit fi msg_info "Updating $APP LXC" - $STD apt-get update - $STD apt-get -y upgrade + $STD apt update + $STD apt upgrade -y msg_ok "Updated $APP LXC" + cleanup_lxc exit } diff --git a/install/debian-install.sh b/install/debian-install.sh index b5864b09c..aedf72fc0 100644 --- a/install/debian-install.sh +++ b/install/debian-install.sh @@ -1,10 +1,9 @@ #!/usr/bin/env bash # Copyright (c) 2021-2025 community-scripts ORG -# Author: Test Suite for tools.func -# License: MIT -# https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE -# Purpose: Run comprehensive test suite for all setup_* functions from tools.func +# Author: MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE +# Source: source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color @@ -18,14 +17,10 @@ msg_info "Installing Base Dependencies" $STD apt-get install -y curl wget ca-certificates msg_ok "Installed Base Dependencies" -msg_info "Downloading and executing tools.func test suite" -bash <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/test-tools-func.sh) -msg_ok "Test suite completed" +# msg_info "Downloading and executing tools.func test suite" +# bash <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVED/raw/branch/main/misc/test-tools-func.sh) +# msg_ok "Test suite completed" motd_ssh customize - -msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" +cleanup_lxc diff --git a/misc/core.func b/misc/core.func index c2c1ea0c6..333b0bac2 100644 --- a/misc/core.func +++ b/misc/core.func @@ -407,6 +407,51 @@ function msg_debug() { fi } +cleanup_lxc() { + msg_info "Cleaning up" + if is_alpine; then + $STD apk cache clean || true + rm -rf /var/cache/apk/* + else + $STD apt -y autoremove || true + $STD apt -y autoclean || true + $STD apt -y clean || true + fi + + rm -rf /tmp/* /var/tmp/* + + # Remove temp files created by mktemp/tempfile + find /tmp /var/tmp -type f -name 'tmp*' -delete 2>/dev/null || true + find /tmp /var/tmp -type f -name 'tempfile*' -delete 2>/dev/null || true + + find /var/log -type f -exec truncate -s 0 {} + + + # Python pip + if command -v pip &>/dev/null; then pip cache purge || true; fi + # Python uv + if command -v uv &>/dev/null; then uv cache clear || true; fi + # Node.js npm + if command -v npm &>/dev/null; then npm cache clean --force || true; fi + # Node.js yarn + if command -v yarn &>/dev/null; then yarn cache clean || true; fi + # Node.js pnpm + if command -v pnpm &>/dev/null; then pnpm store prune || true; fi + # Go + if command -v go &>/dev/null; then go clean -cache -modcache || true; fi + # Rust cargo + if command -v cargo &>/dev/null; then cargo clean || true; fi + # Ruby gem + if command -v gem &>/dev/null; then gem cleanup || true; fi + # Composer (PHP) + if command -v composer &>/dev/null; then composer clear-cache || true; fi + + if command -v journalctl &>/dev/null; then + journalctl --rotate + journalctl --vacuum-time=10m + fi + msg_ok "Cleaned" +} + check_or_create_swap() { msg_info "Checking for active swap" From 44eb138ed4ca17416de3365b00dee957581f9cf0 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 30 Oct 2025 13:45:13 +0100 Subject: [PATCH 1619/1733] Update core.func --- misc/core.func | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/misc/core.func b/misc/core.func index 333b0bac2..d4e288483 100644 --- a/misc/core.func +++ b/misc/core.func @@ -446,8 +446,8 @@ cleanup_lxc() { if command -v composer &>/dev/null; then composer clear-cache || true; fi if command -v journalctl &>/dev/null; then - journalctl --rotate - journalctl --vacuum-time=10m + $STD journalctl --rotate + $STD journalctl --vacuum-time=10m fi msg_ok "Cleaned" } From abef12462a5baecef7fd43e5fbb8ffe461e8ccee Mon Sep 17 00:00:00 2001 From: Romain PINSOLLE Date: Thu, 30 Oct 2025 14:18:05 +0000 Subject: [PATCH 1620/1733] some fix --- ct/snowshare.sh | 93 ++++++++------------- install/snowshare-install.sh | 152 ++++++++++++++++------------------- 2 files changed, 103 insertions(+), 142 deletions(-) diff --git a/ct/snowshare.sh b/ct/snowshare.sh index 816036f49..a7363f88b 100644 --- a/ct/snowshare.sh +++ b/ct/snowshare.sh @@ -1,73 +1,43 @@ #!/usr/bin/env bash -source <(curl -s https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: TuroYT -# License: MIT -# https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/TuroYT/snowshare -function header_info { -clear -cat <<"EOF" - _____ _____ __ - / ___/____ ____ _ __ / ___// /_ ____ ___________ - \__ \/ __ \/ __ \ | /| / / \__ \/ __ \/ __ `/ ___/ _ \ - ___/ / / / / /_/ / |/ |/ / ___/ / / / / /_/ / / / __/ -/____/_/ /_/\____/|__/|__/ /____/_/ /_/\__,_/_/ \___/ - -EOF -} -header_info -echo -e "Loading..." APP="SnowShare" -var_disk="8" -var_cpu="2" -var_ram="2048" -var_os="debian" -var_version="12" +var_tags="${var_tags:-file-sharing}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-8}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" + variables color catch_errors -function default_settings() { - CT_TYPE="1" - PW="" - CT_ID=$NEXTID - HN=$NSAPP - DISK_SIZE="$var_disk" - CORE_COUNT="$var_cpu" - RAM_SIZE="$var_ram" - BRG="vmbr0" - NET="dhcp" - GATE="" - APT_CACHER="" - APT_CACHER_IP="" - DISABLEIP6="no" - MTU="" - SD="" - NS="" - MAC="" - VLAN="" - SSH="no" - VERB="no" - echo_default -} - function update_script() { -header_info -if [[ ! -d /opt/snowshare ]]; then - msg_error "No ${APP} Installation Found!" + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/snowshare ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + msg_info "Updating ${APP}" + systemctl stop snowshare + cd /opt/snowshare + git pull + npm ci + npx prisma generate + npm run build + systemctl start snowshare + msg_ok "Updated ${APP}" exit -fi -msg_info "Updating ${APP}" -systemctl stop snowshare -cd /opt/snowshare -git pull -npm ci -npx prisma generate -npm run build -systemctl start snowshare -msg_ok "Updated ${APP}" -exit } start @@ -75,5 +45,6 @@ build_container description msg_ok "Completed Successfully!\n" -echo -e "${APP} should be reachable by going to the following URL. - ${BL}http://${IP}:3000${CL} \n" \ No newline at end of file +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" \ No newline at end of file diff --git a/install/snowshare-install.sh b/install/snowshare-install.sh index f1f82aa03..a107c8556 100644 --- a/install/snowshare-install.sh +++ b/install/snowshare-install.sh @@ -1,93 +1,81 @@ #!/usr/bin/env bash -# Couleurs pour les messages -YW=$(echo "\033[33m") -BL=$(echo "\033[36m") -RD=$(echo "\033[01;31m") -BGN=$(echo "\033[4;92m") -GN=$(echo "\033[1;92m") -DGN=$(echo "\033[32m") -CL=$(echo "\033[m") -BFR="\\r\\033[K" -HOLD="-" -CM="${GN}✓${CL}" -CROSS="${RD}✗${CL}" +# Copyright (c) 2021-2025 community-scripts ORG +# Author: TuroYT +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -msg_info() { - local msg="$1" - echo -ne " ${HOLD} ${YW}${msg}..." -} +source /dev/stdin <<< "$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os -msg_ok() { - local msg="$1" - echo -e "${BFR} ${CM} ${GN}${msg}${CL}" -} +msg_info "Installing Dependencies" +$STD apt-get install -y \ + curl \ + sudo \ + git \ + make \ + gnupg \ + ca-certificates \ + postgresql \ + postgresql-contrib +msg_ok "Installed Dependencies" -msg_error() { - local msg="$1" - echo -e "${BFR} ${CROSS} ${RD}${msg}${CL}" -} - -# Installation des dépendances système -msg_info "Updating system packages" -apt-get update &>/dev/null -apt-get upgrade -y &>/dev/null -msg_ok "Updated system packages" - -msg_info "Installing dependencies" -apt-get install -y curl sudo git wget postgresql postgresql-contrib &>/dev/null -msg_ok "Installed dependencies" - -# Installation de Node.js 20 msg_info "Installing Node.js" -curl -fsSL https://deb.nodesource.com/setup_20.x | bash - &>/dev/null -apt-get install -y nodejs &>/dev/null -msg_ok "Installed Node.js $(node --version)" +mkdir -p /etc/apt/keyrings +curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg +echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" >/etc/apt/sources.list.d/nodesource.list +$STD apt-get update +$STD apt-get install -y nodejs +msg_ok "Installed Node.js $(node -v)" -# Configuration de PostgreSQL -msg_info "Configuring PostgreSQL" -systemctl enable --now postgresql &>/dev/null -sudo -u postgres psql -c "CREATE DATABASE snowshare;" &>/dev/null -sudo -u postgres psql -c "CREATE USER snowshare WITH ENCRYPTED PASSWORD 'snowshare';" &>/dev/null -sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE snowshare TO snowshare;" &>/dev/null -sudo -u postgres psql -c "ALTER DATABASE snowshare OWNER TO snowshare;" &>/dev/null -msg_ok "Configured PostgreSQL" +msg_info "Setting up PostgreSQL Database" +DB_NAME=snowshare +DB_USER=snowshare +DB_PASS="$(openssl rand -base64 18 | cut -c1-13)" +$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" +$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC'" +echo "" >>~/snowshare.creds +echo -e "SnowShare Database User: \e[32m$DB_USER\e[0m" >>~/snowshare.creds +echo -e "SnowShare Database Password: \e[32m$DB_PASS\e[0m" >>~/snowshare.creds +echo -e "SnowShare Database Name: \e[32m$DB_NAME\e[0m" >>~/snowshare.creds +msg_ok "Set up PostgreSQL Database" -# Clonage du dépôt -msg_info "Cloning SnowShare repository" -git clone https://github.com/TuroYT/snowshare.git /opt/snowshare &>/dev/null +msg_info "Installing SnowShare (Patience)" +cd /opt +RELEASE=$(curl -s https://api.github.com/repos/TuroYT/snowshare/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') +$STD git clone https://github.com/TuroYT/snowshare.git cd /opt/snowshare -msg_ok "Cloned repository" +$STD npm ci +msg_ok "Installed SnowShare" -# Installation des dépendances NPM -msg_info "Installing NPM dependencies" -npm ci &>/dev/null -msg_ok "Installed NPM dependencies" - -# Configuration de l'environnement -msg_info "Configuring environment" -cat < /opt/snowshare/.env -DATABASE_URL="postgresql://snowshare:snowshare@localhost:5432/snowshare" +msg_info "Creating Environment Configuration" +cat </opt/snowshare/.env +DATABASE_URL="postgresql://$DB_USER:$DB_PASS@localhost:5432/$DB_NAME" NEXTAUTH_URL="http://localhost:3000" NEXTAUTH_SECRET="$(openssl rand -base64 32)" ALLOW_SIGNUP=true NODE_ENV=production EOF -msg_ok "Configured environment" +msg_ok "Created Environment Configuration" -# Génération Prisma et migrations -msg_info "Running Prisma migrations" -npx prisma generate &>/dev/null -npx prisma migrate deploy &>/dev/null -msg_ok "Ran Prisma migrations" +msg_info "Running Database Migrations" +cd /opt/snowshare +$STD npx prisma generate +$STD npx prisma migrate deploy +msg_ok "Ran Database Migrations" -# Build de l'application msg_info "Building SnowShare" -npm run build &>/dev/null +$STD npm run build msg_ok "Built SnowShare" -# Création du service systemd -msg_info "Creating systemd service" +msg_info "Creating Service" cat </etc/systemd/system/snowshare.service [Unit] Description=SnowShare - Modern File Sharing Platform @@ -106,17 +94,19 @@ RestartSec=10 [Install] WantedBy=multi-user.target EOF -systemctl daemon-reload -systemctl enable --now snowshare.service &>/dev/null -msg_ok "Created systemd service" +systemctl enable -q --now snowshare.service +msg_ok "Created Service" -# Configuration du cron pour le nettoyage -msg_info "Setting up cleanup cron job" -(crontab -l 2>/dev/null; echo "0 2 * * * cd /opt/snowshare && /usr/bin/npm run cleanup:expired >> /var/log/snowshare-cleanup.log 2>&1") | crontab - -msg_ok "Setup cleanup cron job" +msg_info "Setting up Cleanup Cron Job" +cat </etc/cron.d/snowshare-cleanup +0 2 * * * root cd /opt/snowshare && /usr/bin/npm run cleanup:expired >> /var/log/snowshare-cleanup.log 2>&1 +EOF +msg_ok "Set up Cleanup Cron Job" + +motd_ssh +customize -# Nettoyage msg_info "Cleaning up" -apt-get autoremove -y &>/dev/null -apt-get autoclean -y &>/dev/null -msg_ok "Cleaned up" \ No newline at end of file +$STD apt-get -y autoremove +$STD apt-get -y autoclean +msg_ok "Cleaned" \ No newline at end of file From 2fe4119e49713ff3059d5f7bc878b3177e24b2e1 Mon Sep 17 00:00:00 2001 From: Romain PINSOLLE Date: Thu, 30 Oct 2025 14:33:50 +0000 Subject: [PATCH 1621/1733] link --- ct/snowshare.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ct/snowshare.sh b/ct/snowshare.sh index a7363f88b..ff993bfbe 100644 --- a/ct/snowshare.sh +++ b/ct/snowshare.sh @@ -1,5 +1,7 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +#source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) + +source <(curl -fsSL https://raw.githubusercontent.com/TuroYT/ProxmoxVED/refs/heads/add-snowshare/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: TuroYT # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE From 21d595ddc7d0aa66756f09bb403654a06790becb Mon Sep 17 00:00:00 2001 From: Romain PINSOLLE Date: Thu, 30 Oct 2025 14:47:50 +0000 Subject: [PATCH 1622/1733] ok --- install/snowshare-install.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/install/snowshare-install.sh b/install/snowshare-install.sh index a107c8556..0b5d19783 100644 --- a/install/snowshare-install.sh +++ b/install/snowshare-install.sh @@ -36,11 +36,12 @@ msg_info "Setting up PostgreSQL Database" DB_NAME=snowshare DB_USER=snowshare DB_PASS="$(openssl rand -base64 18 | cut -c1-13)" +systemctl enable -q --now postgresql $STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" -$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC'" +$STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" echo "" >>~/snowshare.creds echo -e "SnowShare Database User: \e[32m$DB_USER\e[0m" >>~/snowshare.creds echo -e "SnowShare Database Password: \e[32m$DB_PASS\e[0m" >>~/snowshare.creds @@ -49,7 +50,6 @@ msg_ok "Set up PostgreSQL Database" msg_info "Installing SnowShare (Patience)" cd /opt -RELEASE=$(curl -s https://api.github.com/repos/TuroYT/snowshare/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') $STD git clone https://github.com/TuroYT/snowshare.git cd /opt/snowshare $STD npm ci @@ -72,7 +72,8 @@ $STD npx prisma migrate deploy msg_ok "Ran Database Migrations" msg_info "Building SnowShare" -$STD npm run build +cd /opt/snowshare +npm run build msg_ok "Built SnowShare" msg_info "Creating Service" From 17c77913e27ee3a9d99bf91d2bd5aadcf2fec704 Mon Sep 17 00:00:00 2001 From: Romain PINSOLLE Date: Thu, 30 Oct 2025 14:54:22 +0000 Subject: [PATCH 1623/1733] ready to pr --- ct/snowshare.sh | 10 +++++----- install/snowshare-install.sh | 2 +- misc/build.func | 2 +- misc/install.func | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ct/snowshare.sh b/ct/snowshare.sh index ff993bfbe..c8998b809 100644 --- a/ct/snowshare.sh +++ b/ct/snowshare.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash -#source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) -source <(curl -fsSL https://raw.githubusercontent.com/TuroYT/ProxmoxVED/refs/heads/add-snowshare/misc/build.func) +#source <(curl -fsSL https://raw.githubusercontent.com/TuroYT/ProxmoxVED/refs/heads/add-snowshare/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: TuroYT # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -9,9 +9,9 @@ source <(curl -fsSL https://raw.githubusercontent.com/TuroYT/ProxmoxVED/refs/hea APP="SnowShare" var_tags="${var_tags:-file-sharing}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-8}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-1024}" +var_disk="${var_disk:-5}" var_os="${var_os:-debian}" var_version="${var_version:-12}" var_unprivileged="${var_unprivileged:-1}" diff --git a/install/snowshare-install.sh b/install/snowshare-install.sh index 0b5d19783..8debd978f 100644 --- a/install/snowshare-install.sh +++ b/install/snowshare-install.sh @@ -73,7 +73,7 @@ msg_ok "Ran Database Migrations" msg_info "Building SnowShare" cd /opt/snowshare -npm run build +$STD npm run build msg_ok "Built SnowShare" msg_info "Creating Service" diff --git a/misc/build.func b/misc/build.func index dcb257d75..eb2183872 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2488,7 +2488,7 @@ EOF' install_ssh_keys_into_ct # Run application installer - if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/TuroYT/ProxmoxVED/refs/heads/add-snowshare/install/${var_install}.sh)"; then + if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)"; then exit $? fi } diff --git a/misc/install.func b/misc/install.func index 84d4e6fb4..f741b921d 100644 --- a/misc/install.func +++ b/misc/install.func @@ -195,7 +195,7 @@ EOF systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//') msg_ok "Customized Container" fi - echo "bash -c \"\$(curl -fsSL https://github.com/TuroYT/ProxmoxVED/raw/add-snowshare/ct/${app}.sh)\"" >/usr/bin/update + echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update chmod +x /usr/bin/update if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then mkdir -p /root/.ssh From 50eaac6b2a57912d1e4e1b6ecf79e7ba77abbe00 Mon Sep 17 00:00:00 2001 From: Romain PINSOLLE <53913510+TuroYT@users.noreply.github.com> Date: Thu, 30 Oct 2025 16:04:40 +0100 Subject: [PATCH 1624/1733] Add jq and fetch latest SnowShare release tag --- install/snowshare-install.sh | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/install/snowshare-install.sh b/install/snowshare-install.sh index 8debd978f..a00494e49 100644 --- a/install/snowshare-install.sh +++ b/install/snowshare-install.sh @@ -17,6 +17,7 @@ $STD apt-get install -y \ curl \ sudo \ git \ + jq \ make \ gnupg \ ca-certificates \ @@ -49,11 +50,23 @@ echo -e "SnowShare Database Name: \e[32m$DB_NAME\e[0m" >>~/snowshare.creds msg_ok "Set up PostgreSQL Database" msg_info "Installing SnowShare (Patience)" +# Find the latest release tag using the GitHub API +LATEST_TAG=$(curl -s "https://api.github.com/repos/TuroYT/snowshare/releases/latest" | jq -r .tag_name) + +if [ -z "$LATEST_TAG" ] || [ "$LATEST_TAG" == "null" ]; then + msg_error "Failed to fetch the latest release tag from GitHub." + exit 1 +fi +msg_ok "Fetching latest release: $LATEST_TAG" + cd /opt $STD git clone https://github.com/TuroYT/snowshare.git cd /opt/snowshare +$STD git checkout $LATEST_TAG +msg_ok "Checked out $LATEST_TAG" + $STD npm ci -msg_ok "Installed SnowShare" +msg_ok "Installed SnowShare dependencies" msg_info "Creating Environment Configuration" cat </opt/snowshare/.env @@ -110,4 +123,4 @@ customize msg_info "Cleaning up" $STD apt-get -y autoremove $STD apt-get -y autoclean -msg_ok "Cleaned" \ No newline at end of file +msg_ok "Cleaned" From c8b36f3ec50952f5a16ae336158486a598fc0348 Mon Sep 17 00:00:00 2001 From: Romain PINSOLLE <53913510+TuroYT@users.noreply.github.com> Date: Thu, 30 Oct 2025 16:07:00 +0100 Subject: [PATCH 1625/1733] fix update to latest --- ct/snowshare.sh | 66 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 61 insertions(+), 5 deletions(-) diff --git a/ct/snowshare.sh b/ct/snowshare.sh index c8998b809..32c1b48b9 100644 --- a/ct/snowshare.sh +++ b/ct/snowshare.sh @@ -30,15 +30,71 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - msg_info "Updating ${APP}" - systemctl stop snowshare + + # S'assurer que jq est installé pour l'analyse de l'API + if ! command -v jq &> /dev/null; then + msg_info "Installing 'jq' (required for update check)..." + apt-get update &>/dev/null + apt-get install -y jq &>/dev/null + if ! command -v jq &> /dev/null; then + msg_error "Failed to install 'jq'. Cannot proceed with update." + exit 1 + fi + msg_ok "Installed 'jq'" + fi + + msg_info "Checking for ${APP} updates..." cd /opt/snowshare - git pull + + # Obtenir le tag local actuel + CURRENT_TAG=$(git describe --tags 2>/dev/null) + if [ $? -ne 0 ]; then + msg_warn "Could not determine current version tag. Fetching latest..." + CURRENT_TAG="unknown" + fi + + # Obtenir le tag de la dernière release depuis GitHub + LATEST_TAG=$(curl -s "https://api.github.com/repos/TuroYT/snowshare/releases/latest" | jq -r .tag_name) + + if [ -z "$LATEST_TAG" ] || [ "$LATEST_TAG" == "null" ]; then + msg_error "Failed to fetch the latest release tag from GitHub." + exit 1 + fi + + msg_info "Current version: $CURRENT_TAG" + msg_info "Latest version: $LATEST_TAG" + + if [ "$CURRENT_TAG" == "$LATEST_TAG" ]; then + msg_ok "${APP} is already up to date." + exit + fi + + msg_info "Updating ${APP} to $LATEST_TAG..." + systemctl stop snowshare + + # Récupérer les nouveaux tags + git fetch --tags + + # Se placer sur le dernier tag + git checkout $LATEST_TAG + if [ $? -ne 0 ]; then + msg_error "Failed to checkout tag $LATEST_TAG. Aborting update." + systemctl start snowshare + exit 1 + fi + + # Relancer les étapes d'installation et de build + msg_info "Installing dependencies..." npm ci + msg_info "Generating Prisma client..." npx prisma generate + msg_info "Applying database migrations..." + npx prisma migrate deploy # Important pour les changements de schéma + msg_info "Building application..." npm run build + systemctl start snowshare - msg_ok "Updated ${APP}" + msg_ok "Updated ${APP} to $LATEST_TAG" exit } @@ -49,4 +105,4 @@ description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" \ No newline at end of file +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" From 72ad956cd90cca130e7adc172c87365f4b45e8dd Mon Sep 17 00:00:00 2001 From: Omer Naveed <198643919+omernaveedxyz@users.noreply.github.com> Date: Thu, 30 Oct 2025 17:14:51 -0500 Subject: [PATCH 1626/1733] Add Miniflux script (#935) * Add Miniflux script * Delete ct/headers/miniflux This will get auto-generated by our func * Minor fixes to Miniflux scripts - run `apt update` before `apt upgrade miniflux` - use selfh.st/icons for logo - remove `[trusted=yes]` from miniflux source - remove extra spaces - change `apt-get` to `apt` * Move Miniflux DB creds Move Miniflux database credentials from a separate file, to directly inside of the `/etc/minflux.conf` file. * Update Miniflux source to be trusted automatically * Store ~/.pgpass for db backups using pg_dump * Use GitHub Release binary instead of UNSIGNED apt source * Add apt -y clean * Make recommended changes * Added notes * Set `LISTEN_ADDR=0.0.0.0:8080` by default * Finishing touches. Removed some unneeded msg blocks --------- Co-authored-by: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Co-authored-by: Michel Roegl-Brunner <73236783+michelroegl-brunner@users.noreply.github.com> --- ct/miniflux.sh | 48 +++++++++++++++++++++++ frontend/public/json/miniflux.json | 40 +++++++++++++++++++ install/miniflux-install.sh | 63 ++++++++++++++++++++++++++++++ 3 files changed, 151 insertions(+) create mode 100644 ct/miniflux.sh create mode 100644 frontend/public/json/miniflux.json create mode 100644 install/miniflux-install.sh diff --git a/ct/miniflux.sh b/ct/miniflux.sh new file mode 100644 index 000000000..bb79d9a8e --- /dev/null +++ b/ct/miniflux.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: omernaveedxyz +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://miniflux.app/ + +APP="Miniflux" +var_tags="${var_tags:-media}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-8}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -f /etc/systemd/system/miniflux.service ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + msg_info "Updating ${APP} LXC" + $STD miniflux -flush-sessions -config-file /etc/miniflux.conf + $STD systemctl stop miniflux + fetch_and_deploy_gh_release "miniflux" "miniflux/v2" "binary" "latest" + $STD miniflux -migrate -config-file /etc/miniflux.conf + $STD systemctl start miniflux + msg_ok "Updated Successfully" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" diff --git a/frontend/public/json/miniflux.json b/frontend/public/json/miniflux.json new file mode 100644 index 000000000..75a6f2551 --- /dev/null +++ b/frontend/public/json/miniflux.json @@ -0,0 +1,40 @@ +{ + "name": "Miniflux", + "slug": "miniflux", + "categories": [ + 13 + ], + "date_created": "2025-09-24", + "type": "ct", + "updateable": true, + "privileged": false, + "config_path": "/etc/miniflux.conf", + "interface_port": 8080, + "documentation": "https://miniflux.app/docs/index.html", + "website": "https://miniflux.app/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/miniflux-light.webp", + "description": "Miniflux is a minimalist and opinionated feed reader.", + "install_methods": [ + { + "type": "default", + "script": "ct/miniflux.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 8, + "os": "Debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": "admin", + "password": "randomly generated during installation process" + }, + "notes": [ + { + "text": "Admin password available as `ADMIN_PASSWORD` in `~/miniflux.creds`", + "type": "info" + } + ] +} diff --git a/install/miniflux-install.sh b/install/miniflux-install.sh new file mode 100644 index 000000000..84ea96660 --- /dev/null +++ b/install/miniflux-install.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: omernaveedxyz +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://miniflux.app/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + + +PG_VERSION=17 setup_postgresql +DB_NAME=miniflux +DB_USER=miniflux +DB_PASS="$(openssl rand -base64 18 | cut -c1-13)" +$STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" +$STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER;" +$STD sudo -u postgres psql -d "$DB_NAME" -c "CREATE EXTENSION hstore;" + + + +fetch_and_deploy_gh_release "miniflux" "miniflux/v2" "binary" "latest" + + +msg_info "Configuring Miniflux" +ADMIN_NAME=admin +ADMIN_PASS="$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13)" +cat </etc/miniflux.conf +# See https://miniflux.app/docs/configuration.html +DATABASE_URL=postgres://$DB_USER:$DB_PASS@localhost/$DB_NAME?sslmode=disable +CREATE_ADMIN=1 +ADMIN_USERNAME=$ADMIN_NAME +ADMIN_PASSWORD=$ADMIN_PASS +LISTEN_ADDR=0.0.0.0:8080 +EOF + +{ + echo "Application Credentials" + echo "DB_NAME: $DB_NAME" + echo "DB_USER: $DB_USER" + echo "DB_PASS: $DB_PASS" + echo "ADMIN_USERNAME: $ADMIN_NAME" + echo "ADMIN_PASSWORD: $ADMIN_PASS" +} >>~/miniflux.creds + +miniflux -migrate -config-file /etc/miniflux.conf + +systemctl enable -q --now miniflux +msg_ok "Configured Miniflux" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt -y autoremove +$STD apt -y autoclean +$STD apt -y clean +msg_ok "Cleaned" From 28b6a601c403e5c95c1ad14db28c849ed3c76ea4 Mon Sep 17 00:00:00 2001 From: Romain PINSOLLE <53913510+TuroYT@users.noreply.github.com> Date: Fri, 31 Oct 2025 09:37:11 +0100 Subject: [PATCH 1627/1733] Update ct/snowshare.sh Co-authored-by: Michel Roegl-Brunner <73236783+michelroegl-brunner@users.noreply.github.com> --- ct/snowshare.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/snowshare.sh b/ct/snowshare.sh index 32c1b48b9..f515ebd18 100644 --- a/ct/snowshare.sh +++ b/ct/snowshare.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) #source <(curl -fsSL https://raw.githubusercontent.com/TuroYT/ProxmoxVED/refs/heads/add-snowshare/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG From 77a82c78e04e7b06b20494c7f9ec9f72fca1c117 Mon Sep 17 00:00:00 2001 From: Romain PINSOLLE <53913510+TuroYT@users.noreply.github.com> Date: Fri, 31 Oct 2025 09:37:20 +0100 Subject: [PATCH 1628/1733] Update ct/snowshare.sh Co-authored-by: Michel Roegl-Brunner <73236783+michelroegl-brunner@users.noreply.github.com> --- ct/snowshare.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/ct/snowshare.sh b/ct/snowshare.sh index f515ebd18..f5a76507f 100644 --- a/ct/snowshare.sh +++ b/ct/snowshare.sh @@ -1,7 +1,6 @@ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -#source <(curl -fsSL https://raw.githubusercontent.com/TuroYT/ProxmoxVED/refs/heads/add-snowshare/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: TuroYT # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE From 24ddf4fc34a244e242c1b4ffae3af3f23c081df4 Mon Sep 17 00:00:00 2001 From: Romain PINSOLLE Date: Fri, 31 Oct 2025 09:38:37 +0000 Subject: [PATCH 1629/1733] testing --- ct/snowshare.sh | 35 +++++++++++++-------- frontend/public/json/snowshare.json | 2 +- install/snowshare-install.sh | 48 ++++++++++++----------------- 3 files changed, 42 insertions(+), 43 deletions(-) diff --git a/ct/snowshare.sh b/ct/snowshare.sh index c8998b809..bd2de2f6f 100644 --- a/ct/snowshare.sh +++ b/ct/snowshare.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +#source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) -#source <(curl -fsSL https://raw.githubusercontent.com/TuroYT/ProxmoxVED/refs/heads/add-snowshare/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/TuroYT/ProxmoxVED/refs/heads/add-snowshare/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: TuroYT # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE @@ -13,7 +13,7 @@ var_cpu="${var_cpu:-1}" var_ram="${var_ram:-1024}" var_disk="${var_disk:-5}" var_os="${var_os:-debian}" -var_version="${var_version:-12}" +var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" @@ -30,15 +30,24 @@ function update_script() { msg_error "No ${APP} Installation Found!" exit fi - msg_info "Updating ${APP}" - systemctl stop snowshare - cd /opt/snowshare - git pull - npm ci - npx prisma generate - npm run build - systemctl start snowshare - msg_ok "Updated ${APP}" + + if check_for_gh_release "snowshare" "TuroYT/snowshare"; then + msg_info "Updating ${APP} to v${RELEASE}" + + systemctl stop snowshare + cd /opt/ + fetch_and_deploy_gh_release "snowshare" "TuroYT/snowshare" + cd /opt/snowshare + npm ci + npx prisma generate + npm run build + systemctl start snowshare + msg_ok "Updated ${APP}" + exit + + else + msg_ok "No update required. ${APP} is already at v${RELEASE}." + fi exit } @@ -49,4 +58,4 @@ description msg_ok "Completed Successfully!\n" echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" \ No newline at end of file +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:3000${CL}" diff --git a/frontend/public/json/snowshare.json b/frontend/public/json/snowshare.json index 553971987..f952b4b18 100644 --- a/frontend/public/json/snowshare.json +++ b/frontend/public/json/snowshare.json @@ -23,7 +23,7 @@ "ram": 1024, "hdd": 5, "os": "Debian", - "version": "12" + "version": "13" } } ], diff --git a/install/snowshare-install.sh b/install/snowshare-install.sh index 8debd978f..28dd3f3bc 100644 --- a/install/snowshare-install.sh +++ b/install/snowshare-install.sh @@ -4,7 +4,7 @@ # Author: TuroYT # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -source /dev/stdin <<< "$FUNCTIONS_FILE_PATH" +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 catch_errors @@ -14,48 +14,41 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ - curl \ - sudo \ - git \ make \ gnupg \ - ca-certificates \ - postgresql \ - postgresql-contrib + ca-certificates + msg_ok "Installed Dependencies" -msg_info "Installing Node.js" -mkdir -p /etc/apt/keyrings -curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg -echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" >/etc/apt/sources.list.d/nodesource.list -$STD apt-get update -$STD apt-get install -y nodejs -msg_ok "Installed Node.js $(node -v)" +setup_nodejs msg_info "Setting up PostgreSQL Database" DB_NAME=snowshare DB_USER=snowshare DB_PASS="$(openssl rand -base64 18 | cut -c1-13)" -systemctl enable -q --now postgresql +setup_postgresql $STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" echo "" >>~/snowshare.creds -echo -e "SnowShare Database User: \e[32m$DB_USER\e[0m" >>~/snowshare.creds -echo -e "SnowShare Database Password: \e[32m$DB_PASS\e[0m" >>~/snowshare.creds -echo -e "SnowShare Database Name: \e[32m$DB_NAME\e[0m" >>~/snowshare.creds +echo -e "Database Username: $DB_USER" >>~/snowshare.creds +echo -e "Database Password: $DB_PASS" >>~/snowshare.creds +echo -e "Database Name: $DB_NAME" >>~/snowshare.creds msg_ok "Set up PostgreSQL Database" msg_info "Installing SnowShare (Patience)" +APP="snowshare" cd /opt -$STD git clone https://github.com/TuroYT/snowshare.git + +fetch_and_deploy_gh_release "snowshare" "TuroYT/snowshare" + cd /opt/snowshare $STD npm ci -msg_ok "Installed SnowShare" -msg_info "Creating Environment Configuration" +echo "${RELEASE}" >/opt/${APP}_version.txt + cat </opt/snowshare/.env DATABASE_URL="postgresql://$DB_USER:$DB_PASS@localhost:5432/$DB_NAME" NEXTAUTH_URL="http://localhost:3000" @@ -63,18 +56,14 @@ NEXTAUTH_SECRET="$(openssl rand -base64 32)" ALLOW_SIGNUP=true NODE_ENV=production EOF -msg_ok "Created Environment Configuration" -msg_info "Running Database Migrations" cd /opt/snowshare $STD npx prisma generate $STD npx prisma migrate deploy msg_ok "Ran Database Migrations" -msg_info "Building SnowShare" cd /opt/snowshare $STD npm run build -msg_ok "Built SnowShare" msg_info "Creating Service" cat </etc/systemd/system/snowshare.service @@ -96,7 +85,7 @@ RestartSec=10 WantedBy=multi-user.target EOF systemctl enable -q --now snowshare.service -msg_ok "Created Service" +msg_ok "Installed SnowShare v${RELEASE}" msg_info "Setting up Cleanup Cron Job" cat </etc/cron.d/snowshare-cleanup @@ -108,6 +97,7 @@ motd_ssh customize msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean -msg_ok "Cleaned" \ No newline at end of file +$STD apt -y autoremove +$STD apt -y autoclean +$STD apt -y clean +msg_ok "Cleaned" From 89a062bb0b308b85bbb1639cb8bd45c982b40bfb Mon Sep 17 00:00:00 2001 From: Romain PINSOLLE <53913510+TuroYT@users.noreply.github.com> Date: Fri, 31 Oct 2025 11:39:15 +0100 Subject: [PATCH 1630/1733] Refactor installation script for SnowShare --- install/snowshare-install.sh | 34 +++++----------------------------- 1 file changed, 5 insertions(+), 29 deletions(-) diff --git a/install/snowshare-install.sh b/install/snowshare-install.sh index 692215aaa..959719998 100644 --- a/install/snowshare-install.sh +++ b/install/snowshare-install.sh @@ -14,13 +14,6 @@ update_os msg_info "Installing Dependencies" $STD apt-get install -y \ -<<<<<<< HEAD -======= - curl \ - sudo \ - git \ - jq \ ->>>>>>> 77a82c78e04e7b06b20494c7f9ec9f72fca1c117 make \ gnupg \ ca-certificates @@ -46,32 +39,18 @@ echo -e "Database Name: $DB_NAME" >>~/snowshare.creds msg_ok "Set up PostgreSQL Database" msg_info "Installing SnowShare (Patience)" -<<<<<<< HEAD + APP="snowshare" -======= -# Find the latest release tag using the GitHub API -LATEST_TAG=$(curl -s "https://api.github.com/repos/TuroYT/snowshare/releases/latest" | jq -r .tag_name) -if [ -z "$LATEST_TAG" ] || [ "$LATEST_TAG" == "null" ]; then - msg_error "Failed to fetch the latest release tag from GitHub." - exit 1 -fi -msg_ok "Fetching latest release: $LATEST_TAG" - ->>>>>>> 77a82c78e04e7b06b20494c7f9ec9f72fca1c117 cd /opt fetch_and_deploy_gh_release "snowshare" "TuroYT/snowshare" cd /opt/snowshare -$STD git checkout $LATEST_TAG -msg_ok "Checked out $LATEST_TAG" + $STD npm ci -<<<<<<< HEAD -======= -msg_ok "Installed SnowShare dependencies" ->>>>>>> 77a82c78e04e7b06b20494c7f9ec9f72fca1c117 + echo "${RELEASE}" >/opt/${APP}_version.txt @@ -123,12 +102,9 @@ motd_ssh customize msg_info "Cleaning up" -<<<<<<< HEAD + $STD apt -y autoremove $STD apt -y autoclean $STD apt -y clean -======= -$STD apt-get -y autoremove -$STD apt-get -y autoclean ->>>>>>> 77a82c78e04e7b06b20494c7f9ec9f72fca1c117 + msg_ok "Cleaned" From 89d5c096db5d83c504e150ed281214e829b46160 Mon Sep 17 00:00:00 2001 From: Romain PINSOLLE <53913510+TuroYT@users.noreply.github.com> Date: Fri, 31 Oct 2025 11:40:38 +0100 Subject: [PATCH 1631/1733] Remove log messages from installation script Removed logging messages for database migrations and service creation. --- install/snowshare-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/snowshare-install.sh b/install/snowshare-install.sh index 959719998..c1b7c5a82 100644 --- a/install/snowshare-install.sh +++ b/install/snowshare-install.sh @@ -65,12 +65,12 @@ EOF cd /opt/snowshare $STD npx prisma generate $STD npx prisma migrate deploy -msg_ok "Ran Database Migrations" + cd /opt/snowshare $STD npm run build -msg_info "Creating Service" + cat </etc/systemd/system/snowshare.service [Unit] Description=SnowShare - Modern File Sharing Platform From 477ae6cb86e636ce301b3768d9513d4c6e8ae06a Mon Sep 17 00:00:00 2001 From: Romain PINSOLLE Date: Fri, 31 Oct 2025 10:50:57 +0000 Subject: [PATCH 1632/1733] tests --- ct/snowshare.sh | 3 ++- misc/build.func | 2 +- misc/install.func | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ct/snowshare.sh b/ct/snowshare.sh index 7b1123c15..e92f30296 100644 --- a/ct/snowshare.sh +++ b/ct/snowshare.sh @@ -1,5 +1,6 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +#source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +source <(curl -fsSL https://github.com/TuroYT/ProxmoxVED/raw/refs/heads/add-snowshare/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: TuroYT diff --git a/misc/build.func b/misc/build.func index eb2183872..77e40ce10 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2488,7 +2488,7 @@ EOF' install_ssh_keys_into_ct # Run application installer - if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)"; then + if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://github.com/TuroYT/ProxmoxVED/raw/refs/heads/add-snowshare/install/${var_install}.sh)"; then exit $? fi } diff --git a/misc/install.func b/misc/install.func index f741b921d..1cadbf0c0 100644 --- a/misc/install.func +++ b/misc/install.func @@ -195,7 +195,7 @@ EOF systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//') msg_ok "Customized Container" fi - echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update + echo "bash -c \"\$(curl -fsSL https://github.com/TuroYT/ProxmoxVED/raw/refs/heads/add-snowshare/ct/${app}.sh)\"" >/usr/bin/update chmod +x /usr/bin/update if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then mkdir -p /root/.ssh From a44c9364ae6a742064f982e4c97696217ee78f02 Mon Sep 17 00:00:00 2001 From: Romain PINSOLLE Date: Fri, 31 Oct 2025 11:03:31 +0000 Subject: [PATCH 1633/1733] fixed --- ct/snowshare.sh | 9 +++------ install/snowshare-install.sh | 25 ++++++------------------- 2 files changed, 9 insertions(+), 25 deletions(-) diff --git a/ct/snowshare.sh b/ct/snowshare.sh index e92f30296..5cd6aaa53 100644 --- a/ct/snowshare.sh +++ b/ct/snowshare.sh @@ -32,21 +32,18 @@ function update_script() { fi if check_for_gh_release "snowshare" "TuroYT/snowshare"; then - msg_info "Updating ${APP} to v${RELEASE}" - + msg_info "Downloading ${APP}" systemctl stop snowshare - cd /opt/ fetch_and_deploy_gh_release "snowshare" "TuroYT/snowshare" cd /opt/snowshare + msg_ok "Downloaded ${APP}" + msg_info "Installing ${APP}" npm ci npx prisma generate npm run build systemctl start snowshare msg_ok "Updated ${APP}" exit - - else - msg_ok "No update required. ${APP} is already at v${RELEASE}." fi exit } diff --git a/install/snowshare-install.sh b/install/snowshare-install.sh index c1b7c5a82..fa2f3ecb4 100644 --- a/install/snowshare-install.sh +++ b/install/snowshare-install.sh @@ -23,6 +23,11 @@ msg_ok "Installed Dependencies" setup_nodejs msg_info "Setting up PostgreSQL Database" +cd /opt + +fetch_and_deploy_gh_release "snowshare" "TuroYT/snowshare" + +cd /opt/snowshare DB_NAME=snowshare DB_USER=snowshare DB_PASS="$(openssl rand -base64 18 | cut -c1-13)" @@ -42,18 +47,8 @@ msg_info "Installing SnowShare (Patience)" APP="snowshare" -cd /opt - -fetch_and_deploy_gh_release "snowshare" "TuroYT/snowshare" - -cd /opt/snowshare - - $STD npm ci - -echo "${RELEASE}" >/opt/${APP}_version.txt - cat </opt/snowshare/.env DATABASE_URL="postgresql://$DB_USER:$DB_PASS@localhost:5432/$DB_NAME" NEXTAUTH_URL="http://localhost:3000" @@ -65,12 +60,8 @@ EOF cd /opt/snowshare $STD npx prisma generate $STD npx prisma migrate deploy - - -cd /opt/snowshare $STD npm run build - cat </etc/systemd/system/snowshare.service [Unit] Description=SnowShare - Modern File Sharing Platform @@ -90,21 +81,17 @@ RestartSec=10 WantedBy=multi-user.target EOF systemctl enable -q --now snowshare.service -msg_ok "Installed SnowShare v${RELEASE}" +msg_ok "Installed SnowShare" msg_info "Setting up Cleanup Cron Job" cat </etc/cron.d/snowshare-cleanup 0 2 * * * root cd /opt/snowshare && /usr/bin/npm run cleanup:expired >> /var/log/snowshare-cleanup.log 2>&1 EOF msg_ok "Set up Cleanup Cron Job" - motd_ssh customize - msg_info "Cleaning up" - $STD apt -y autoremove $STD apt -y autoclean $STD apt -y clean - msg_ok "Cleaned" From f8c397cae1ef70b3ed4a7c7a9f1b0b098dd779bf Mon Sep 17 00:00:00 2001 From: Romain PINSOLLE Date: Fri, 31 Oct 2025 11:10:10 +0000 Subject: [PATCH 1634/1733] ok --- ct/snowshare.sh | 4 +--- misc/build.func | 2 +- misc/install.func | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/ct/snowshare.sh b/ct/snowshare.sh index 5cd6aaa53..d91c8c4b5 100644 --- a/ct/snowshare.sh +++ b/ct/snowshare.sh @@ -1,7 +1,5 @@ #!/usr/bin/env bash -#source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -source <(curl -fsSL https://github.com/TuroYT/ProxmoxVED/raw/refs/heads/add-snowshare/misc/build.func) - +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: TuroYT # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/misc/build.func b/misc/build.func index 77e40ce10..eb2183872 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2488,7 +2488,7 @@ EOF' install_ssh_keys_into_ct # Run application installer - if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://github.com/TuroYT/ProxmoxVED/raw/refs/heads/add-snowshare/install/${var_install}.sh)"; then + if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)"; then exit $? fi } diff --git a/misc/install.func b/misc/install.func index 1cadbf0c0..f741b921d 100644 --- a/misc/install.func +++ b/misc/install.func @@ -195,7 +195,7 @@ EOF systemctl restart $(basename $(dirname $GETTY_OVERRIDE) | sed 's/\.d//') msg_ok "Customized Container" fi - echo "bash -c \"\$(curl -fsSL https://github.com/TuroYT/ProxmoxVED/raw/refs/heads/add-snowshare/ct/${app}.sh)\"" >/usr/bin/update + echo "bash -c \"\$(curl -fsSL https://github.com/community-scripts/ProxmoxVED/raw/main/ct/${app}.sh)\"" >/usr/bin/update chmod +x /usr/bin/update if [[ -n "${SSH_AUTHORIZED_KEY}" ]]; then mkdir -p /root/.ssh From 896299aedff213d4ffd16c1da71874f271aef2e7 Mon Sep 17 00:00:00 2001 From: Romain PINSOLLE Date: Fri, 31 Oct 2025 13:47:31 +0000 Subject: [PATCH 1635/1733] fixed install --- ct/snowshare.sh | 21 +++++++++++++-------- install/snowshare-install.sh | 25 +++++++------------------ 2 files changed, 20 insertions(+), 26 deletions(-) diff --git a/ct/snowshare.sh b/ct/snowshare.sh index d91c8c4b5..093d265ed 100644 --- a/ct/snowshare.sh +++ b/ct/snowshare.sh @@ -30,18 +30,23 @@ function update_script() { fi if check_for_gh_release "snowshare" "TuroYT/snowshare"; then - msg_info "Downloading ${APP}" + msg_info "Stopping Service" systemctl stop snowshare + msg_ok "Stopped Service" + fetch_and_deploy_gh_release "snowshare" "TuroYT/snowshare" + + msg_info "Updating Snowshare" cd /opt/snowshare - msg_ok "Downloaded ${APP}" - msg_info "Installing ${APP}" - npm ci - npx prisma generate - npm run build + $STD npm ci + $STD npx prisma generate + $STD npm run build + msg_ok "Updated Snowshare" + + msg_info "Starting Service" systemctl start snowshare - msg_ok "Updated ${APP}" - exit + msg_ok "Started Service" + msg_ok "Updated successfully!" fi exit } diff --git a/install/snowshare-install.sh b/install/snowshare-install.sh index fa2f3ecb4..0d8590f8f 100644 --- a/install/snowshare-install.sh +++ b/install/snowshare-install.sh @@ -12,26 +12,18 @@ setting_up_container network_check update_os -msg_info "Installing Dependencies" -$STD apt-get install -y \ - make \ - gnupg \ - ca-certificates +NODE_VERSION="22" setup_nodejs -msg_ok "Installed Dependencies" - -setup_nodejs +cd /opt +msg_info "Downloading" +fetch_and_deploy_gh_release "snowshare" "TuroYT/snowshare" +msg_ok "Snowshare Downloaded" msg_info "Setting up PostgreSQL Database" -cd /opt - -fetch_and_deploy_gh_release "snowshare" "TuroYT/snowshare" - -cd /opt/snowshare +setup_postgresql DB_NAME=snowshare DB_USER=snowshare DB_PASS="$(openssl rand -base64 18 | cut -c1-13)" -setup_postgresql $STD sudo -u postgres psql -c "CREATE ROLE $DB_USER WITH LOGIN PASSWORD '$DB_PASS';" $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCODING 'UTF8' TEMPLATE template0;" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" @@ -44,11 +36,9 @@ echo -e "Database Name: $DB_NAME" >>~/snowshare.creds msg_ok "Set up PostgreSQL Database" msg_info "Installing SnowShare (Patience)" - APP="snowshare" - +cd /opt/snowshare $STD npm ci - cat </opt/snowshare/.env DATABASE_URL="postgresql://$DB_USER:$DB_PASS@localhost:5432/$DB_NAME" NEXTAUTH_URL="http://localhost:3000" @@ -57,7 +47,6 @@ ALLOW_SIGNUP=true NODE_ENV=production EOF -cd /opt/snowshare $STD npx prisma generate $STD npx prisma migrate deploy $STD npm run build From d49a1704e5d02447f2600019d9d608c920814a44 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Sat, 1 Nov 2025 12:22:44 +0100 Subject: [PATCH 1636/1733] deb13 --- frontend/src/config/siteConfig.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/config/siteConfig.tsx b/frontend/src/config/siteConfig.tsx index fabc0ed20..6ee43d1b0 100644 --- a/frontend/src/config/siteConfig.tsx +++ b/frontend/src/config/siteConfig.tsx @@ -58,8 +58,8 @@ export const OperatingSystems: OperatingSystem[] = [ { name: "Debian", versions: [ - { name: "11", slug: "bullseye" }, { name: "12", slug: "bookworm" }, + { name: "13", slug: "trixie" }, ], }, { From a39be2339d18d2bf48a444be09f13b6e278f9956 Mon Sep 17 00:00:00 2001 From: Romain PINSOLLE Date: Sat, 1 Nov 2025 17:34:03 +0100 Subject: [PATCH 1637/1733] ok --- ct/snowshare.sh | 2 -- install/snowshare-install.sh | 21 ++++++++++----------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/ct/snowshare.sh b/ct/snowshare.sh index 093d265ed..f8a06fd96 100644 --- a/ct/snowshare.sh +++ b/ct/snowshare.sh @@ -13,9 +13,7 @@ var_disk="${var_disk:-5}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" - header_info "$APP" - variables color catch_errors diff --git a/install/snowshare-install.sh b/install/snowshare-install.sh index 0d8590f8f..b188c7775 100644 --- a/install/snowshare-install.sh +++ b/install/snowshare-install.sh @@ -12,12 +12,10 @@ setting_up_container network_check update_os -NODE_VERSION="22" setup_nodejs +setup_nodejs cd /opt -msg_info "Downloading" fetch_and_deploy_gh_release "snowshare" "TuroYT/snowshare" -msg_ok "Snowshare Downloaded" msg_info "Setting up PostgreSQL Database" setup_postgresql @@ -29,14 +27,15 @@ $STD sudo -u postgres psql -c "CREATE DATABASE $DB_NAME WITH OWNER $DB_USER ENCO $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" -echo "" >>~/snowshare.creds -echo -e "Database Username: $DB_USER" >>~/snowshare.creds -echo -e "Database Password: $DB_PASS" >>~/snowshare.creds -echo -e "Database Name: $DB_NAME" >>~/snowshare.creds +{ + echo "SnowShare-Database-Credentials" + echo "Database Username: $DB_USER" + echo "Database Password: $DB_PASS" + echo "Database Name: $DB_NAME" +} >>~/snowshare.creds msg_ok "Set up PostgreSQL Database" msg_info "Installing SnowShare (Patience)" -APP="snowshare" cd /opt/snowshare $STD npm ci cat </opt/snowshare/.env @@ -46,11 +45,9 @@ NEXTAUTH_SECRET="$(openssl rand -base64 32)" ALLOW_SIGNUP=true NODE_ENV=production EOF - $STD npx prisma generate $STD npx prisma migrate deploy $STD npm run build - cat </etc/systemd/system/snowshare.service [Unit] Description=SnowShare - Modern File Sharing Platform @@ -69,7 +66,7 @@ RestartSec=10 [Install] WantedBy=multi-user.target EOF -systemctl enable -q --now snowshare.service +systemctl enable -q --now snowshare msg_ok "Installed SnowShare" msg_info "Setting up Cleanup Cron Job" @@ -77,8 +74,10 @@ cat </etc/cron.d/snowshare-cleanup 0 2 * * * root cd /opt/snowshare && /usr/bin/npm run cleanup:expired >> /var/log/snowshare-cleanup.log 2>&1 EOF msg_ok "Set up Cleanup Cron Job" + motd_ssh customize + msg_info "Cleaning up" $STD apt -y autoremove $STD apt -y autoclean From 4171b1f9dc0a08c714dfd676ca2604b76c037e4c Mon Sep 17 00:00:00 2001 From: Frans Stofberg Date: Sat, 1 Nov 2025 22:50:54 +0200 Subject: [PATCH 1638/1733] Add Donetick app script --- ct/Donetick.sh | 74 ++++++++++++++++++++++++++++++ frontend/public/json/Donetick.json | 36 +++++++++++++++ install/Donetick-install.sh | 60 ++++++++++++++++++++++++ misc/build.func | 6 +-- misc/install.func | 2 +- 5 files changed, 174 insertions(+), 4 deletions(-) create mode 100644 ct/Donetick.sh create mode 100644 frontend/public/json/Donetick.json create mode 100644 install/Donetick-install.sh diff --git a/ct/Donetick.sh b/ct/Donetick.sh new file mode 100644 index 000000000..f009786c3 --- /dev/null +++ b/ct/Donetick.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/fstof/ProxmoxVED/refs/heads/donetick/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: fstof +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/donetick/donetick + +# App Default Values +APP="Donetick" +var_tags="${var_tags:-productivity;tasks}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-8}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + + # Check if installation is present | -f for file, -d for folder + if [[ ! -f /opt/donetick ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + # Crawling the new version and checking whether an update is required + RELEASE=$(curl -fsSL https://api.github.com/repos/donetick/donetick/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + if [[ "${RELEASE}" != "$(cat /opt/donetick/donetick_version.txt)" ]] || [[ ! -f /opt/donetick/donetick_version.txt ]]; then + # Stopping Services + msg_info "Stopping $APP" + systemctl stop donetick + msg_ok "Stopped $APP" + + # Execute Update + msg_info "Updating $APP to ${RELEASE}" + curl -fsSL "https://github.com/donetick/donetick/releases/download/${RELEASE}/donetick_Linux_x86_64.tar.gz" | tar -xz -C . + mv donetick "/opt/donetick/donetick" + msg_ok "Updated $APP to ${RELEASE}" + + # Starting Services + msg_info "Starting $APP" + systemctl start donetick + msg_ok "Started $APP" + + # Cleaning up + msg_info "Cleaning Up" + rm -rf config + msg_ok "Cleanup Completed" + + # Last Action + echo "${RELEASE}" > /opt/donetick/donetick_version.txt + msg_ok "Update Successful" + else + msg_ok "No update required. ${APP} is already at ${RELEASE}" + fi + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:2021${CL}" diff --git a/frontend/public/json/Donetick.json b/frontend/public/json/Donetick.json new file mode 100644 index 000000000..030fc2438 --- /dev/null +++ b/frontend/public/json/Donetick.json @@ -0,0 +1,36 @@ +{ + "name": "Donetick", + "slug": "donetick", + "categories": [ + 0, + 12 + ], + "date_created": "2025-11-01", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 2021, + "documentation": null, + "config_path": "", + "website": "https://donetick.com", + "logo": "https://donetick.com/assets/logo-inhNxF6J.svg", + "description": "The smart task manager that keeps individuals and families organized with intelligent scheduling and fair task distribution", + "install_methods": [ + { + "type": "default", + "script": "ct/Donetick.sh", + "resources": { + "cpu": 2, + "ram": 2048, + "hdd": 8, + "os": "Debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [] +} diff --git a/install/Donetick-install.sh b/install/Donetick-install.sh new file mode 100644 index 000000000..b8a9d8c39 --- /dev/null +++ b/install/Donetick-install.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: fstof +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://github.com/donetick/donetick + +# Import Functions und Setup +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +# Installing Dependencies +msg_info "Installing Dependencies" +$STD apt install -y \ + ca-certificates \ + libc6-compat +msg_ok "Installed Dependencies" + +msg_info "Setup Donetick" +RELEASE=$(curl -fsSL https://api.github.com/repos/donetick/donetick/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') + +mkdir -p /opt/donetick +cd /opt/donetick +curl -fsSL "https://github.com/donetick/donetick/releases/download/${RELEASE}/donetick_Linux_x86_64.tar.gz" | tar -xz -C . + +echo "${RELEASE}" > /opt/donetick/donetick_version.txt +msg_ok "Setup Donetick" + +# Creating Service (if needed) +msg_info "Creating Service" +cat </etc/systemd/system/donetick.service +[Unit] +Description=Donetick Service +After=network.target + +[Service] +Environment="DT_ENV=selfhosted" +WorkingDirectory=/opt/donetick +ExecStart=/opt/donetick/donetick +Restart=always + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now donetick +msg_ok "Created Service" + +motd_ssh +customize + +# Cleanup +msg_info "Cleaning up" +$STD apt -y autoremove +$STD apt -y autoclean +msg_ok "Cleaned" diff --git a/misc/build.func b/misc/build.func index eb2183872..797f2b405 100644 --- a/misc/build.func +++ b/misc/build.func @@ -48,7 +48,7 @@ variables() { # FUNC_DIR="/usr/local/community-scripts/core" # mkdir -p "$FUNC_DIR" -# BUILD_URL="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func" +# BUILD_URL="https://raw.githubusercontent.com/fstof/ProxmoxVED/refs/heads/donetick/misc/build.func" # BUILD_REV="$FUNC_DIR/build.rev" # DEVMODE="${DEVMODE:-no}" @@ -73,7 +73,7 @@ variables() { # update_func_file() { # local file="$1" -# local url="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/$file" +# local url="https://raw.githubusercontent.com/fstof/ProxmoxVED/refs/heads/donetick/misc/$file" # local local_path="$FUNC_DIR/$file" # echo "⬇️ Downloading $file ..." @@ -2488,7 +2488,7 @@ EOF' install_ssh_keys_into_ct # Run application installer - if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)"; then + if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/fstof/ProxmoxVED/refs/heads/donetick/install/${var_install}.sh)"; then exit $? fi } diff --git a/misc/install.func b/misc/install.func index f741b921d..f3b805a8d 100644 --- a/misc/install.func +++ b/misc/install.func @@ -32,7 +32,7 @@ verb_ip6() { # # This function handles errors # error_handler() { -# source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/api.func) +# source <(curl -fsSL https://raw.githubusercontent.com/fstof/ProxmoxVED/refs/heads/donetick/misc/api.func) # local exit_code="$1" # local line_number="$2" # local command="${3:-}" From 044bd6be71c83a82f9333193e99293465764d538 Mon Sep 17 00:00:00 2001 From: Frans Stofberg Date: Sat, 1 Nov 2025 23:57:31 +0200 Subject: [PATCH 1639/1733] replace config token --- ct/Donetick.sh | 10 ++++++++-- install/Donetick-install.sh | 9 ++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/ct/Donetick.sh b/ct/Donetick.sh index f009786c3..ca77b286e 100644 --- a/ct/Donetick.sh +++ b/ct/Donetick.sh @@ -41,8 +41,14 @@ function update_script() { # Execute Update msg_info "Updating $APP to ${RELEASE}" - curl -fsSL "https://github.com/donetick/donetick/releases/download/${RELEASE}/donetick_Linux_x86_64.tar.gz" | tar -xz -C . - mv donetick "/opt/donetick/donetick" + + cd /opt/donetick + + wget -q https://github.com/donetick/donetick/releases/download/${RELEASE}/donetick_Linux_x86_64.tar.gz + tar -xf donetick_Linux_x86_64.tar.gz + + rm -rf /opt/donetick/donetick_Linux_x86_64.tar.gz + msg_ok "Updated $APP to ${RELEASE}" # Starting Services diff --git a/install/Donetick-install.sh b/install/Donetick-install.sh index b8a9d8c39..ceb6ee638 100644 --- a/install/Donetick-install.sh +++ b/install/Donetick-install.sh @@ -26,7 +26,12 @@ RELEASE=$(curl -fsSL https://api.github.com/repos/donetick/donetick/releases/lat mkdir -p /opt/donetick cd /opt/donetick -curl -fsSL "https://github.com/donetick/donetick/releases/download/${RELEASE}/donetick_Linux_x86_64.tar.gz" | tar -xz -C . + +wget -q https://github.com/donetick/donetick/releases/download/${RELEASE}/donetick_Linux_x86_64.tar.gz +tar -xf donetick_Linux_x86_64.tar.gz + +TOKEN=$(openssl rand -hex 16) +sed -i -e "s/change_this_to_a_secure_random_string_32_characters_long/${TOKEN}/g" config/selfhosted.yaml echo "${RELEASE}" > /opt/donetick/donetick_version.txt msg_ok "Setup Donetick" @@ -55,6 +60,8 @@ customize # Cleanup msg_info "Cleaning up" +rm -rf /opt/donetick/donetick_Linux_x86_64.tar.gz $STD apt -y autoremove $STD apt -y autoclean msg_ok "Cleaned" + From 2edfd31eebeae436dc5c5789817217c39348b783 Mon Sep 17 00:00:00 2001 From: Frans Stofberg Date: Sun, 2 Nov 2025 00:03:56 +0200 Subject: [PATCH 1640/1733] rename files --- ct/{Donetick.sh => donetickx.sh} | 2 +- frontend/public/json/{Donetick.json => donetickx.json} | 2 +- install/{Donetick-install.sh => donetickx-install.sh} | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) rename ct/{Donetick.sh => donetickx.sh} (99%) rename frontend/public/json/{Donetick.json => donetickx.json} (95%) rename install/{Donetick-install.sh => donetickx-install.sh} (95%) diff --git a/ct/Donetick.sh b/ct/donetickx.sh similarity index 99% rename from ct/Donetick.sh rename to ct/donetickx.sh index ca77b286e..92067ecf3 100644 --- a/ct/Donetick.sh +++ b/ct/donetickx.sh @@ -6,7 +6,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/fstof/ProxmoxVED/refs/head # Source: https://github.com/donetick/donetick # App Default Values -APP="Donetick" +APP="donetick" var_tags="${var_tags:-productivity;tasks}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" diff --git a/frontend/public/json/Donetick.json b/frontend/public/json/donetickx.json similarity index 95% rename from frontend/public/json/Donetick.json rename to frontend/public/json/donetickx.json index 030fc2438..f0d2aab84 100644 --- a/frontend/public/json/Donetick.json +++ b/frontend/public/json/donetickx.json @@ -18,7 +18,7 @@ "install_methods": [ { "type": "default", - "script": "ct/Donetick.sh", + "script": "ct/donetick.sh", "resources": { "cpu": 2, "ram": 2048, diff --git a/install/Donetick-install.sh b/install/donetickx-install.sh similarity index 95% rename from install/Donetick-install.sh rename to install/donetickx-install.sh index ceb6ee638..5962c21ae 100644 --- a/install/Donetick-install.sh +++ b/install/donetickx-install.sh @@ -21,7 +21,7 @@ $STD apt install -y \ libc6-compat msg_ok "Installed Dependencies" -msg_info "Setup Donetick" +msg_info "Setup donetick" RELEASE=$(curl -fsSL https://api.github.com/repos/donetick/donetick/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') mkdir -p /opt/donetick @@ -34,13 +34,13 @@ TOKEN=$(openssl rand -hex 16) sed -i -e "s/change_this_to_a_secure_random_string_32_characters_long/${TOKEN}/g" config/selfhosted.yaml echo "${RELEASE}" > /opt/donetick/donetick_version.txt -msg_ok "Setup Donetick" +msg_ok "Setup donetick" # Creating Service (if needed) msg_info "Creating Service" cat </etc/systemd/system/donetick.service [Unit] -Description=Donetick Service +Description=donetick Service After=network.target [Service] From 739f25086cad089e95710a5c0e8af100754cd93a Mon Sep 17 00:00:00 2001 From: Frans Stofberg Date: Sun, 2 Nov 2025 00:04:30 +0200 Subject: [PATCH 1641/1733] rename files --- ct/{donetickx.sh => donetick.sh} | 0 frontend/public/json/{donetickx.json => donetick.json} | 0 install/{donetickx-install.sh => donetick-install.sh} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename ct/{donetickx.sh => donetick.sh} (100%) rename frontend/public/json/{donetickx.json => donetick.json} (100%) rename install/{donetickx-install.sh => donetick-install.sh} (100%) diff --git a/ct/donetickx.sh b/ct/donetick.sh similarity index 100% rename from ct/donetickx.sh rename to ct/donetick.sh diff --git a/frontend/public/json/donetickx.json b/frontend/public/json/donetick.json similarity index 100% rename from frontend/public/json/donetickx.json rename to frontend/public/json/donetick.json diff --git a/install/donetickx-install.sh b/install/donetick-install.sh similarity index 100% rename from install/donetickx-install.sh rename to install/donetick-install.sh From e1cef5e6cd43ea01b2e2bbce88e1a4d24b12be57 Mon Sep 17 00:00:00 2001 From: Frans Stofberg Date: Sun, 2 Nov 2025 00:09:01 +0200 Subject: [PATCH 1642/1733] remove apt dependencies --- install/donetick-install.sh | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/install/donetick-install.sh b/install/donetick-install.sh index 5962c21ae..628b944ff 100644 --- a/install/donetick-install.sh +++ b/install/donetick-install.sh @@ -14,14 +14,7 @@ setting_up_container network_check update_os -# Installing Dependencies -msg_info "Installing Dependencies" -$STD apt install -y \ - ca-certificates \ - libc6-compat -msg_ok "Installed Dependencies" - -msg_info "Setup donetick" +msg_info "Install donetick" RELEASE=$(curl -fsSL https://api.github.com/repos/donetick/donetick/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') mkdir -p /opt/donetick @@ -34,9 +27,8 @@ TOKEN=$(openssl rand -hex 16) sed -i -e "s/change_this_to_a_secure_random_string_32_characters_long/${TOKEN}/g" config/selfhosted.yaml echo "${RELEASE}" > /opt/donetick/donetick_version.txt -msg_ok "Setup donetick" +msg_ok "Install donetick" -# Creating Service (if needed) msg_info "Creating Service" cat </etc/systemd/system/donetick.service [Unit] @@ -61,7 +53,5 @@ customize # Cleanup msg_info "Cleaning up" rm -rf /opt/donetick/donetick_Linux_x86_64.tar.gz -$STD apt -y autoremove -$STD apt -y autoclean msg_ok "Cleaned" From f6c2697457e44c43c4f84672b4abf3b20165ffbc Mon Sep 17 00:00:00 2001 From: Frans Stofberg Date: Sun, 2 Nov 2025 00:18:44 +0200 Subject: [PATCH 1643/1733] use apt-get --- install/donetick-install.sh | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/install/donetick-install.sh b/install/donetick-install.sh index 628b944ff..41de9e3e6 100644 --- a/install/donetick-install.sh +++ b/install/donetick-install.sh @@ -14,7 +14,12 @@ setting_up_container network_check update_os -msg_info "Install donetick" +# Installing Dependencies +msg_info "Installing Dependencies" +$STD apt-get install -y ca-certificates libc6-compat +msg_ok "Installed Dependencies" + +msg_info "Setup donetick" RELEASE=$(curl -fsSL https://api.github.com/repos/donetick/donetick/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') mkdir -p /opt/donetick @@ -27,8 +32,9 @@ TOKEN=$(openssl rand -hex 16) sed -i -e "s/change_this_to_a_secure_random_string_32_characters_long/${TOKEN}/g" config/selfhosted.yaml echo "${RELEASE}" > /opt/donetick/donetick_version.txt -msg_ok "Install donetick" +msg_ok "Setup donetick" +# Creating Service (if needed) msg_info "Creating Service" cat </etc/systemd/system/donetick.service [Unit] @@ -53,5 +59,7 @@ customize # Cleanup msg_info "Cleaning up" rm -rf /opt/donetick/donetick_Linux_x86_64.tar.gz +$STD apt-get -y autoremove +$STD apt-get -y autoclean msg_ok "Cleaned" From 67927b2a891002e98832ec12fbd84bc7a487519e Mon Sep 17 00:00:00 2001 From: Frans Stofberg Date: Sun, 2 Nov 2025 00:23:04 +0200 Subject: [PATCH 1644/1733] remove broken package --- install/donetick-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/donetick-install.sh b/install/donetick-install.sh index 41de9e3e6..d9cd2dffc 100644 --- a/install/donetick-install.sh +++ b/install/donetick-install.sh @@ -16,7 +16,7 @@ update_os # Installing Dependencies msg_info "Installing Dependencies" -$STD apt-get install -y ca-certificates libc6-compat +$STD apt-get install -y ca-certificates msg_ok "Installed Dependencies" msg_info "Setup donetick" From 86028cc6405561072d0861acb157402bb7970eea Mon Sep 17 00:00:00 2001 From: Frans Stofberg Date: Sun, 2 Nov 2025 09:41:02 +0200 Subject: [PATCH 1645/1733] Remove comments --- ct/donetick.sh | 14 ++------------ install/donetick-install.sh | 4 ---- tools/addon/glances.sh | 4 ++-- tools/pve/update-apps.sh | 4 ++-- 4 files changed, 6 insertions(+), 20 deletions(-) diff --git a/ct/donetick.sh b/ct/donetick.sh index 92067ecf3..01643334a 100644 --- a/ct/donetick.sh +++ b/ct/donetick.sh @@ -5,7 +5,6 @@ source <(curl -fsSL https://raw.githubusercontent.com/fstof/ProxmoxVED/refs/head # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/donetick/donetick -# App Default Values APP="donetick" var_tags="${var_tags:-productivity;tasks}" var_cpu="${var_cpu:-2}" @@ -25,43 +24,34 @@ function update_script() { check_container_storage check_container_resources - # Check if installation is present | -f for file, -d for folder if [[ ! -f /opt/donetick ]]; then msg_error "No ${APP} Installation Found!" exit fi - # Crawling the new version and checking whether an update is required RELEASE=$(curl -fsSL https://api.github.com/repos/donetick/donetick/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') if [[ "${RELEASE}" != "$(cat /opt/donetick/donetick_version.txt)" ]] || [[ ! -f /opt/donetick/donetick_version.txt ]]; then - # Stopping Services msg_info "Stopping $APP" systemctl stop donetick msg_ok "Stopped $APP" - # Execute Update msg_info "Updating $APP to ${RELEASE}" - cd /opt/donetick - wget -q https://github.com/donetick/donetick/releases/download/${RELEASE}/donetick_Linux_x86_64.tar.gz tar -xf donetick_Linux_x86_64.tar.gz - - rm -rf /opt/donetick/donetick_Linux_x86_64.tar.gz + mv donetick /opt/donetick/donetick msg_ok "Updated $APP to ${RELEASE}" - # Starting Services msg_info "Starting $APP" systemctl start donetick msg_ok "Started $APP" - # Cleaning up msg_info "Cleaning Up" + rm -rf donetick_Linux_x86_64.tar.gz rm -rf config msg_ok "Cleanup Completed" - # Last Action echo "${RELEASE}" > /opt/donetick/donetick_version.txt msg_ok "Update Successful" else diff --git a/install/donetick-install.sh b/install/donetick-install.sh index d9cd2dffc..192120392 100644 --- a/install/donetick-install.sh +++ b/install/donetick-install.sh @@ -5,7 +5,6 @@ # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://github.com/donetick/donetick -# Import Functions und Setup source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" color verb_ip6 @@ -14,7 +13,6 @@ setting_up_container network_check update_os -# Installing Dependencies msg_info "Installing Dependencies" $STD apt-get install -y ca-certificates msg_ok "Installed Dependencies" @@ -34,7 +32,6 @@ sed -i -e "s/change_this_to_a_secure_random_string_32_characters_long/${TOKEN}/g echo "${RELEASE}" > /opt/donetick/donetick_version.txt msg_ok "Setup donetick" -# Creating Service (if needed) msg_info "Creating Service" cat </etc/systemd/system/donetick.service [Unit] @@ -56,7 +53,6 @@ msg_ok "Created Service" motd_ssh customize -# Cleanup msg_info "Cleaning up" rm -rf /opt/donetick/donetick_Linux_x86_64.tar.gz $STD apt-get -y autoremove diff --git a/tools/addon/glances.sh b/tools/addon/glances.sh index 0f1b76f11..3cf0d6ab3 100644 --- a/tools/addon/glances.sh +++ b/tools/addon/glances.sh @@ -48,7 +48,7 @@ install_glances_debian() { msg_ok "Installed dependencies" msg_info "Setting up Python + uv" - source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/tools.func) + source <(curl -fsSL https://raw.githubusercontent.com/fstof/ProxmoxVED/donetick/misc/tools.func) setup_uv PYTHON_VERSION="3.12" msg_ok "Setup Python + uv" @@ -118,7 +118,7 @@ install_glances_alpine() { msg_ok "Installed dependencies" msg_info "Setting up Python + uv" - source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/tools.func) + source <(curl -fsSL https://raw.githubusercontent.com/fstof/ProxmoxVED/donetick/misc/tools.func) setup_uv PYTHON_VERSION="3.12" msg_ok "Setup Python + uv" diff --git a/tools/pve/update-apps.sh b/tools/pve/update-apps.sh index f07e5c03d..c69af1e08 100644 --- a/tools/pve/update-apps.sh +++ b/tools/pve/update-apps.sh @@ -4,7 +4,7 @@ # Author: BvdBerg01 | Co-Author: remz1337 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/refs/heads/main/misc/core.func) +source <(curl -fsSL https://raw.githubusercontent.com/fstof/ProxmoxVED/refs/heads/donetick/misc/core.func) function header_info { clear @@ -168,7 +168,7 @@ for container in $CHOICE; do fi #2) Extract service build/update resource requirements from config/installation file - script=$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${service}.sh) + script=$(curl -fsSL https://raw.githubusercontent.com/fstof/ProxmoxVED/donetick/ct/${service}.sh) #2.1) Check if the script downloaded successfully if [ $? -ne 0 ]; then From 0cd72e9f390ad0ae58e999ad029033a083bab13f Mon Sep 17 00:00:00 2001 From: Frans Stofberg Date: Sun, 2 Nov 2025 09:51:36 +0200 Subject: [PATCH 1646/1733] Remove files not needed for PR --- ct/donetick.sh | 2 +- misc/build.func | 6 +- misc/install.func | 2 +- tools/addon/glances.sh | 202 +++++++++++++++++++++++++++++++++++++++ tools/pve/update-apps.sh | 8 +- 5 files changed, 211 insertions(+), 9 deletions(-) diff --git a/ct/donetick.sh b/ct/donetick.sh index 01643334a..111da67fb 100644 --- a/ct/donetick.sh +++ b/ct/donetick.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/fstof/ProxmoxVED/refs/heads/donetick/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: fstof # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE diff --git a/misc/build.func b/misc/build.func index 797f2b405..eb2183872 100644 --- a/misc/build.func +++ b/misc/build.func @@ -48,7 +48,7 @@ variables() { # FUNC_DIR="/usr/local/community-scripts/core" # mkdir -p "$FUNC_DIR" -# BUILD_URL="https://raw.githubusercontent.com/fstof/ProxmoxVED/refs/heads/donetick/misc/build.func" +# BUILD_URL="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func" # BUILD_REV="$FUNC_DIR/build.rev" # DEVMODE="${DEVMODE:-no}" @@ -73,7 +73,7 @@ variables() { # update_func_file() { # local file="$1" -# local url="https://raw.githubusercontent.com/fstof/ProxmoxVED/refs/heads/donetick/misc/$file" +# local url="https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/$file" # local local_path="$FUNC_DIR/$file" # echo "⬇️ Downloading $file ..." @@ -2488,7 +2488,7 @@ EOF' install_ssh_keys_into_ct # Run application installer - if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/fstof/ProxmoxVED/refs/heads/donetick/install/${var_install}.sh)"; then + if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)"; then exit $? fi } diff --git a/misc/install.func b/misc/install.func index f3b805a8d..f741b921d 100644 --- a/misc/install.func +++ b/misc/install.func @@ -32,7 +32,7 @@ verb_ip6() { # # This function handles errors # error_handler() { -# source <(curl -fsSL https://raw.githubusercontent.com/fstof/ProxmoxVED/refs/heads/donetick/misc/api.func) +# source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/api.func) # local exit_code="$1" # local line_number="$2" # local command="${3:-}" diff --git a/tools/addon/glances.sh b/tools/addon/glances.sh index 3cf0d6ab3..d3c7b03dd 100644 --- a/tools/addon/glances.sh +++ b/tools/addon/glances.sh @@ -4,6 +4,14 @@ # Author: tteck (tteckster) | MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +function header_info { + clear + cat <<"EOF"#!/usr/bin/env bash + +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) | MickLesk (CanbiZ) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE + function header_info { clear cat <<"EOF" @@ -41,6 +49,200 @@ get_local_ip() { } IP=$(get_local_ip) +install_glances_debian() { + msg_info "Installing dependencies" + apt-get update >/dev/null 2>&1 + apt-get install -y gcc lm-sensors wireless-tools >/dev/null 2>&1 + msg_ok "Installed dependencies" + + msg_info "Setting up Python + uv" + source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/tools.func) + setup_uv PYTHON_VERSION="3.12" + msg_ok "Setup Python + uv" + + msg_info "Installing $APP (with web UI)" + cd /opt + mkdir -p glances + cd glances + uv venv + source .venv/bin/activate >/dev/null 2>&1 + uv pip install --upgrade pip wheel setuptools >/dev/null 2>&1 + uv pip install "glances[web]" >/dev/null 2>&1 + deactivate + msg_ok "Installed $APP" + + msg_info "Creating systemd service" + cat </etc/systemd/system/glances.service +[Unit] +Description=Glances - An eye on your system +After=network.target + +[Service] +Type=simple +ExecStart=/opt/glances/.venv/bin/glances -w +Restart=on-failure +WorkingDirectory=/opt/glances + +[Install] +WantedBy=multi-user.target +EOF + systemctl enable -q --now glances + msg_ok "Created systemd service" + + echo -e "\n$APP is now running at: http://$IP:61208\n" +} + +# update on Debian/Ubuntu +update_glances_debian() { + if [[ ! -d /opt/glances/.venv ]]; then + msg_error "$APP is not installed" + exit 1 + fi + msg_info "Updating $APP" + cd /opt/glances + source .venv/bin/activate + uv pip install --upgrade "glances[web]" >/dev/null 2>&1 + deactivate + systemctl restart glances + msg_ok "Updated $APP" +} + +# uninstall on Debian/Ubuntu +uninstall_glances_debian() { + msg_info "Uninstalling $APP" + systemctl disable -q --now glances || true + rm -f /etc/systemd/system/glances.service + rm -rf /opt/glances + msg_ok "Removed $APP" +} + +# install on Alpine +install_glances_alpine() { + msg_info "Installing dependencies" + apk update >/dev/null 2>&1 + $STD apk add --no-cache \ + gcc musl-dev linux-headers python3-dev \ + python3 py3-pip py3-virtualenv lm-sensors wireless-tools >/dev/null 2>&1 + msg_ok "Installed dependencies" + + msg_info "Setting up Python + uv" + source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/tools.func) + setup_uv PYTHON_VERSION="3.12" + msg_ok "Setup Python + uv" + + msg_info "Installing $APP (with web UI)" + cd /opt + mkdir -p glances + cd glances + uv venv + source .venv/bin/activate + uv pip install --upgrade pip wheel setuptools >/dev/null 2>&1 + uv pip install "glances[web]" >/dev/null 2>&1 + deactivate + msg_ok "Installed $APP" + + msg_info "Creating OpenRC service" + cat <<'EOF' >/etc/init.d/glances +#!/sbin/openrc-run +command="/opt/glances/.venv/bin/glances" +command_args="-w" +command_background="yes" +pidfile="/run/glances.pid" +name="glances" +description="Glances monitoring tool" +EOF + chmod +x /etc/init.d/glances + rc-update add glances default + rc-service glances start + msg_ok "Created OpenRC service" + + echo -e "\n$APP is now running at: http://$IP:61208\n" +} + +# update on Alpine +update_glances_alpine() { + if [[ ! -d /opt/glances/.venv ]]; then + msg_error "$APP is not installed" + exit 1 + fi + msg_info "Updating $APP" + cd /opt/glances + source .venv/bin/activate + uv pip install --upgrade "glances[web]" >/dev/null 2>&1 + deactivate + rc-service glances restart + msg_ok "Updated $APP" +} + +# uninstall on Alpine +uninstall_glances_alpine() { + msg_info "Uninstalling $APP" + rc-service glances stop || true + rc-update del glances || true + rm -f /etc/init.d/glances + rm -rf /opt/glances + msg_ok "Removed $APP" +} + +# options menu +OPTIONS=(Install "Install $APP" + Update "Update $APP" + Uninstall "Uninstall $APP") + +CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "$APP" --menu "Select an option:" 12 58 3 \ + "${OPTIONS[@]}" 3>&1 1>&2 2>&3 || true) + +# OS detection +if grep -qi "alpine" /etc/os-release; then + case "$CHOICE" in + Install) install_glances_alpine ;; + Update) update_glances_alpine ;; + Uninstall) uninstall_glances_alpine ;; + *) exit 0 ;; + esac +else + case "$CHOICE" in + Install) install_glances_debian ;; + Update) update_glances_debian ;; + Uninstall) uninstall_glances_debian ;; + *) exit 0 ;; + esac +fi + + ________ + / ____/ /___ _____ ________ _____ + / / __/ / __ `/ __ \/ ___/ _ \/ ___/ +/ /_/ / / /_/ / / / / /__/ __(__ ) +\____/_/\__,_/_/ /_/\___/\___/____/ + +EOF +} + +APP="Glances" +YW=$(echo "\033[33m") +GN=$(echo "\033[1;92m") +RD=$(echo "\033[01;31m") +BL=$(echo "\033[36m") +CL=$(echo "\033[m") +CM="${GN}✔️${CL}" +CROSS="${RD}✖️${CL}" +INFO="${BL}ℹ️${CL}" + +function msg_info() { echo -e "${INFO} ${YW}$1...${CL}"; } +function msg_ok() { echo -e "${CM} ${GN}$1${CL}"; } +function msg_error() { echo -e "${CROSS} ${RD}$1${CL}"; } + +get_local_ip() { + if command -v hostname >/dev/null 2>&1 && hostname -I 2>/dev/null; then + hostname -I | awk '{print $1}' + elif command -v ip >/dev/null 2>&1; then + ip -4 addr show scope global | awk '/inet / {print $2}' | cut -d/ -f1 | head -n1 + else + echo "127.0.0.1" + fi +} +IP=$(get_local_ip) + install_glances_debian() { msg_info "Installing dependencies" apt-get update >/dev/null 2>&1 diff --git a/tools/pve/update-apps.sh b/tools/pve/update-apps.sh index c69af1e08..787fb7aac 100644 --- a/tools/pve/update-apps.sh +++ b/tools/pve/update-apps.sh @@ -4,7 +4,7 @@ # Author: BvdBerg01 | Co-Author: remz1337 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -source <(curl -fsSL https://raw.githubusercontent.com/fstof/ProxmoxVED/refs/heads/donetick/misc/core.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/refs/heads/main/misc/core.func) function header_info { clear @@ -64,7 +64,8 @@ END { } header_info -msg_info "Loading all possible LXC containers from Proxmox VE. This may take a few seconds..." +echo "Loading all possible LXC containers from Proxmox VE" +echo "This may take a few seconds..." whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC Container Update" --yesno "This will update LXC container. Proceed?" 10 58 || exit NODE=$(hostname) @@ -88,7 +89,6 @@ while read -r container; do menu_items+=("$container_id" "$formatted_line" "OFF") fi done <<<"$containers" -msg_ok "Loaded ${#menu_items[@]} containers" CHOICE=$(whiptail --title "LXC Container Update" \ --checklist "Select LXC containers to update:" 25 60 13 \ @@ -168,7 +168,7 @@ for container in $CHOICE; do fi #2) Extract service build/update resource requirements from config/installation file - script=$(curl -fsSL https://raw.githubusercontent.com/fstof/ProxmoxVED/donetick/ct/${service}.sh) + script=$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${service}.sh) #2.1) Check if the script downloaded successfully if [ $? -ne 0 ]; then From aebedc7fd8d0e5a10ed91b24d09378c7f689a313 Mon Sep 17 00:00:00 2001 From: Frans Stofberg Date: Sun, 2 Nov 2025 09:55:55 +0200 Subject: [PATCH 1647/1733] Revert more files --- tools/addon/glances.sh | 202 --------------------------------------- tools/pve/update-apps.sh | 4 +- 2 files changed, 2 insertions(+), 204 deletions(-) diff --git a/tools/addon/glances.sh b/tools/addon/glances.sh index d3c7b03dd..0f1b76f11 100644 --- a/tools/addon/glances.sh +++ b/tools/addon/glances.sh @@ -4,14 +4,6 @@ # Author: tteck (tteckster) | MickLesk (CanbiZ) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -function header_info { - clear - cat <<"EOF"#!/usr/bin/env bash - -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) | MickLesk (CanbiZ) -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE - function header_info { clear cat <<"EOF" @@ -184,200 +176,6 @@ uninstall_glances_alpine() { msg_ok "Removed $APP" } -# options menu -OPTIONS=(Install "Install $APP" - Update "Update $APP" - Uninstall "Uninstall $APP") - -CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "$APP" --menu "Select an option:" 12 58 3 \ - "${OPTIONS[@]}" 3>&1 1>&2 2>&3 || true) - -# OS detection -if grep -qi "alpine" /etc/os-release; then - case "$CHOICE" in - Install) install_glances_alpine ;; - Update) update_glances_alpine ;; - Uninstall) uninstall_glances_alpine ;; - *) exit 0 ;; - esac -else - case "$CHOICE" in - Install) install_glances_debian ;; - Update) update_glances_debian ;; - Uninstall) uninstall_glances_debian ;; - *) exit 0 ;; - esac -fi - - ________ - / ____/ /___ _____ ________ _____ - / / __/ / __ `/ __ \/ ___/ _ \/ ___/ -/ /_/ / / /_/ / / / / /__/ __(__ ) -\____/_/\__,_/_/ /_/\___/\___/____/ - -EOF -} - -APP="Glances" -YW=$(echo "\033[33m") -GN=$(echo "\033[1;92m") -RD=$(echo "\033[01;31m") -BL=$(echo "\033[36m") -CL=$(echo "\033[m") -CM="${GN}✔️${CL}" -CROSS="${RD}✖️${CL}" -INFO="${BL}ℹ️${CL}" - -function msg_info() { echo -e "${INFO} ${YW}$1...${CL}"; } -function msg_ok() { echo -e "${CM} ${GN}$1${CL}"; } -function msg_error() { echo -e "${CROSS} ${RD}$1${CL}"; } - -get_local_ip() { - if command -v hostname >/dev/null 2>&1 && hostname -I 2>/dev/null; then - hostname -I | awk '{print $1}' - elif command -v ip >/dev/null 2>&1; then - ip -4 addr show scope global | awk '/inet / {print $2}' | cut -d/ -f1 | head -n1 - else - echo "127.0.0.1" - fi -} -IP=$(get_local_ip) - -install_glances_debian() { - msg_info "Installing dependencies" - apt-get update >/dev/null 2>&1 - apt-get install -y gcc lm-sensors wireless-tools >/dev/null 2>&1 - msg_ok "Installed dependencies" - - msg_info "Setting up Python + uv" - source <(curl -fsSL https://raw.githubusercontent.com/fstof/ProxmoxVED/donetick/misc/tools.func) - setup_uv PYTHON_VERSION="3.12" - msg_ok "Setup Python + uv" - - msg_info "Installing $APP (with web UI)" - cd /opt - mkdir -p glances - cd glances - uv venv - source .venv/bin/activate >/dev/null 2>&1 - uv pip install --upgrade pip wheel setuptools >/dev/null 2>&1 - uv pip install "glances[web]" >/dev/null 2>&1 - deactivate - msg_ok "Installed $APP" - - msg_info "Creating systemd service" - cat </etc/systemd/system/glances.service -[Unit] -Description=Glances - An eye on your system -After=network.target - -[Service] -Type=simple -ExecStart=/opt/glances/.venv/bin/glances -w -Restart=on-failure -WorkingDirectory=/opt/glances - -[Install] -WantedBy=multi-user.target -EOF - systemctl enable -q --now glances - msg_ok "Created systemd service" - - echo -e "\n$APP is now running at: http://$IP:61208\n" -} - -# update on Debian/Ubuntu -update_glances_debian() { - if [[ ! -d /opt/glances/.venv ]]; then - msg_error "$APP is not installed" - exit 1 - fi - msg_info "Updating $APP" - cd /opt/glances - source .venv/bin/activate - uv pip install --upgrade "glances[web]" >/dev/null 2>&1 - deactivate - systemctl restart glances - msg_ok "Updated $APP" -} - -# uninstall on Debian/Ubuntu -uninstall_glances_debian() { - msg_info "Uninstalling $APP" - systemctl disable -q --now glances || true - rm -f /etc/systemd/system/glances.service - rm -rf /opt/glances - msg_ok "Removed $APP" -} - -# install on Alpine -install_glances_alpine() { - msg_info "Installing dependencies" - apk update >/dev/null 2>&1 - $STD apk add --no-cache \ - gcc musl-dev linux-headers python3-dev \ - python3 py3-pip py3-virtualenv lm-sensors wireless-tools >/dev/null 2>&1 - msg_ok "Installed dependencies" - - msg_info "Setting up Python + uv" - source <(curl -fsSL https://raw.githubusercontent.com/fstof/ProxmoxVED/donetick/misc/tools.func) - setup_uv PYTHON_VERSION="3.12" - msg_ok "Setup Python + uv" - - msg_info "Installing $APP (with web UI)" - cd /opt - mkdir -p glances - cd glances - uv venv - source .venv/bin/activate - uv pip install --upgrade pip wheel setuptools >/dev/null 2>&1 - uv pip install "glances[web]" >/dev/null 2>&1 - deactivate - msg_ok "Installed $APP" - - msg_info "Creating OpenRC service" - cat <<'EOF' >/etc/init.d/glances -#!/sbin/openrc-run -command="/opt/glances/.venv/bin/glances" -command_args="-w" -command_background="yes" -pidfile="/run/glances.pid" -name="glances" -description="Glances monitoring tool" -EOF - chmod +x /etc/init.d/glances - rc-update add glances default - rc-service glances start - msg_ok "Created OpenRC service" - - echo -e "\n$APP is now running at: http://$IP:61208\n" -} - -# update on Alpine -update_glances_alpine() { - if [[ ! -d /opt/glances/.venv ]]; then - msg_error "$APP is not installed" - exit 1 - fi - msg_info "Updating $APP" - cd /opt/glances - source .venv/bin/activate - uv pip install --upgrade "glances[web]" >/dev/null 2>&1 - deactivate - rc-service glances restart - msg_ok "Updated $APP" -} - -# uninstall on Alpine -uninstall_glances_alpine() { - msg_info "Uninstalling $APP" - rc-service glances stop || true - rc-update del glances || true - rm -f /etc/init.d/glances - rm -rf /opt/glances - msg_ok "Removed $APP" -} - # options menu OPTIONS=(Install "Install $APP" Update "Update $APP" diff --git a/tools/pve/update-apps.sh b/tools/pve/update-apps.sh index 787fb7aac..f07e5c03d 100644 --- a/tools/pve/update-apps.sh +++ b/tools/pve/update-apps.sh @@ -64,8 +64,7 @@ END { } header_info -echo "Loading all possible LXC containers from Proxmox VE" -echo "This may take a few seconds..." +msg_info "Loading all possible LXC containers from Proxmox VE. This may take a few seconds..." whiptail --backtitle "Proxmox VE Helper Scripts" --title "LXC Container Update" --yesno "This will update LXC container. Proceed?" 10 58 || exit NODE=$(hostname) @@ -89,6 +88,7 @@ while read -r container; do menu_items+=("$container_id" "$formatted_line" "OFF") fi done <<<"$containers" +msg_ok "Loaded ${#menu_items[@]} containers" CHOICE=$(whiptail --title "LXC Container Update" \ --checklist "Select LXC containers to update:" 25 60 13 \ From 4097ed96d2159b92351166691b2ddbb827a4ba48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Sun, 2 Nov 2025 09:35:08 +0100 Subject: [PATCH 1648/1733] Update donetick installation script for package manager --- install/donetick-install.sh | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/install/donetick-install.sh b/install/donetick-install.sh index 192120392..2ee9da715 100644 --- a/install/donetick-install.sh +++ b/install/donetick-install.sh @@ -14,22 +14,15 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt-get install -y ca-certificates +$STD apt install -y ca-certificates msg_ok "Installed Dependencies" +fetch_and_deploy_gh_release "donetick" "donetick/donetick" "prebuild" "latest" "/opt/donetick" "donetick_Linux_x86_64.tar.gz" + msg_info "Setup donetick" -RELEASE=$(curl -fsSL https://api.github.com/repos/donetick/donetick/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - -mkdir -p /opt/donetick cd /opt/donetick - -wget -q https://github.com/donetick/donetick/releases/download/${RELEASE}/donetick_Linux_x86_64.tar.gz -tar -xf donetick_Linux_x86_64.tar.gz - TOKEN=$(openssl rand -hex 16) sed -i -e "s/change_this_to_a_secure_random_string_32_characters_long/${TOKEN}/g" config/selfhosted.yaml - -echo "${RELEASE}" > /opt/donetick/donetick_version.txt msg_ok "Setup donetick" msg_info "Creating Service" @@ -54,8 +47,8 @@ motd_ssh customize msg_info "Cleaning up" -rm -rf /opt/donetick/donetick_Linux_x86_64.tar.gz -$STD apt-get -y autoremove -$STD apt-get -y autoclean +$STD apt -y autoremove +$STD apt -y autoclean +$STD apt -y clean msg_ok "Cleaned" From 58000ff630561eca295eaa0f805fa52070aca405 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Sun, 2 Nov 2025 09:38:44 +0100 Subject: [PATCH 1649/1733] Update Donetick JSON configuration Updated Donetick configuration with new categories, documentation link, config path, logo, and description. Adjusted resource requirements for installation. --- frontend/public/json/donetick.json | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/frontend/public/json/donetick.json b/frontend/public/json/donetick.json index f0d2aab84..6cb64737a 100644 --- a/frontend/public/json/donetick.json +++ b/frontend/public/json/donetick.json @@ -2,27 +2,26 @@ "name": "Donetick", "slug": "donetick", "categories": [ - 0, - 12 + 19 ], "date_created": "2025-11-01", "type": "ct", "updateable": true, "privileged": false, "interface_port": 2021, - "documentation": null, - "config_path": "", + "documentation": "https://docs.donetick.com/getting-started/", + "config_path": "/opt/donetick/selfhosted.yml", "website": "https://donetick.com", - "logo": "https://donetick.com/assets/logo-inhNxF6J.svg", - "description": "The smart task manager that keeps individuals and families organized with intelligent scheduling and fair task distribution", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/donetick.webp", + "description": "Donetick an open-source, user-friendly app for managing tasks and chores, featuring customizable options to help you and others stay organized", "install_methods": [ { "type": "default", "script": "ct/donetick.sh", "resources": { - "cpu": 2, - "ram": 2048, - "hdd": 8, + "cpu": 1, + "ram": 512, + "hdd": 2, "os": "Debian", "version": "13" } From e528d46babb61c0100633d0e10909db3c8d571c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Sun, 2 Nov 2025 10:11:16 +0100 Subject: [PATCH 1650/1733] Update donetick.sh --- ct/donetick.sh | 40 +++++++++++++--------------------------- 1 file changed, 13 insertions(+), 27 deletions(-) diff --git a/ct/donetick.sh b/ct/donetick.sh index 111da67fb..f5a993bc0 100644 --- a/ct/donetick.sh +++ b/ct/donetick.sh @@ -7,9 +7,9 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV APP="donetick" var_tags="${var_tags:-productivity;tasks}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-8}" +var_cpu="${var_cpu:-1}" +var_ram="${var_ram:-512}" +var_disk="${var_disk:-2}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" @@ -24,38 +24,24 @@ function update_script() { check_container_storage check_container_resources - if [[ ! -f /opt/donetick ]]; then + if [[ ! -d /opt/donetick ]]; then msg_error "No ${APP} Installation Found!" exit fi - RELEASE=$(curl -fsSL https://api.github.com/repos/donetick/donetick/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }') - if [[ "${RELEASE}" != "$(cat /opt/donetick/donetick_version.txt)" ]] || [[ ! -f /opt/donetick/donetick_version.txt ]]; then - msg_info "Stopping $APP" + if check_for_gh_release "donetick" "donetick/donetick"; then + msg_info "Stopping Service" systemctl stop donetick - msg_ok "Stopped $APP" + msg_ok "Stopped Service" - msg_info "Updating $APP to ${RELEASE}" + mv /opt/donetick/config/selfhosted.yml /opt + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "donetick" "donetick/donetick" "prebuild" "latest" "/opt/donetick" "donetick_Linux_x86_64.tar.gz" + mv /opt/selfhosted.yml /opt/donetick/config - wget -q https://github.com/donetick/donetick/releases/download/${RELEASE}/donetick_Linux_x86_64.tar.gz - tar -xf donetick_Linux_x86_64.tar.gz - mv donetick /opt/donetick/donetick - - msg_ok "Updated $APP to ${RELEASE}" - - msg_info "Starting $APP" + msg_info "Starting Service" systemctl start donetick - msg_ok "Started $APP" - - msg_info "Cleaning Up" - rm -rf donetick_Linux_x86_64.tar.gz - rm -rf config - msg_ok "Cleanup Completed" - - echo "${RELEASE}" > /opt/donetick/donetick_version.txt - msg_ok "Update Successful" - else - msg_ok "No update required. ${APP} is already at ${RELEASE}" + msg_ok "Started Service" + msg_ok "Updated Successfully!" fi exit } From 70df9a9159452a8030ea38955155a3417366e2b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Sun, 2 Nov 2025 10:11:59 +0100 Subject: [PATCH 1651/1733] Update source URL for build functions --- ct/donetick.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/donetick.sh b/ct/donetick.sh index f5a993bc0..0dbcfcc4f 100644 --- a/ct/donetick.sh +++ b/ct/donetick.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 community-scripts ORG # Author: fstof # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE From 4f17d0ef3779ce7fb4b764e294d1e5ba5e8d2665 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Slavi=C5=A1a=20Are=C5=BEina?= <58952836+tremor021@users.noreply.github.com> Date: Sun, 2 Nov 2025 10:14:28 +0100 Subject: [PATCH 1652/1733] Move selfhosted.yml and donetick.db to new locations --- ct/donetick.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ct/donetick.sh b/ct/donetick.sh index 0dbcfcc4f..dc59bc895 100644 --- a/ct/donetick.sh +++ b/ct/donetick.sh @@ -34,9 +34,10 @@ function update_script() { systemctl stop donetick msg_ok "Stopped Service" - mv /opt/donetick/config/selfhosted.yml /opt + mv /opt/donetick/config/selfhosted.yml /opt/donetick/donetick.db /opt CLEAN_INSTALL=1 fetch_and_deploy_gh_release "donetick" "donetick/donetick" "prebuild" "latest" "/opt/donetick" "donetick_Linux_x86_64.tar.gz" mv /opt/selfhosted.yml /opt/donetick/config + mv /opt/donetick.db /opt/donetick msg_info "Starting Service" systemctl start donetick From 6e81ce0930a340a2d3261282ea44d7742f152c06 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Sun, 2 Nov 2025 13:00:52 +0000 Subject: [PATCH 1653/1733] Update .app files --- ct/headers/donetick | 6 ++++++ ct/headers/miniflux | 6 ++++++ 2 files changed, 12 insertions(+) create mode 100644 ct/headers/donetick create mode 100644 ct/headers/miniflux diff --git a/ct/headers/donetick b/ct/headers/donetick new file mode 100644 index 000000000..7bcb7f3f3 --- /dev/null +++ b/ct/headers/donetick @@ -0,0 +1,6 @@ + __ __ _ __ + ____/ /___ ____ ___ / /_(_)____/ /__ + / __ / __ \/ __ \/ _ \/ __/ / ___/ //_/ +/ /_/ / /_/ / / / / __/ /_/ / /__/ ,< +\__,_/\____/_/ /_/\___/\__/_/\___/_/|_| + diff --git a/ct/headers/miniflux b/ct/headers/miniflux new file mode 100644 index 000000000..cb3195ae2 --- /dev/null +++ b/ct/headers/miniflux @@ -0,0 +1,6 @@ + __ ____ _ ______ + / |/ (_)___ (_) __/ /_ ___ __ + / /|_/ / / __ \/ / /_/ / / / / |/_/ + / / / / / / / / / __/ / /_/ /> < +/_/ /_/_/_/ /_/_/_/ /_/\__,_/_/|_| + From d45a0128b475c9536c7f4a223ec734bf895012ea Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sun, 2 Nov 2025 18:56:30 +0100 Subject: [PATCH 1654/1733] Test --- install/pangolin-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/pangolin-install.sh b/install/pangolin-install.sh index 2936394b7..206938ee0 100644 --- a/install/pangolin-install.sh +++ b/install/pangolin-install.sh @@ -75,8 +75,8 @@ flags: enable_integration_api: true enable_clients: true EOF -$STD npm run db:sqlite:generate -$STD npm run db:sqlite:push +#$STD npm run db:sqlite:generate +#$STD npm run db:sqlite:push msg_ok "Setup Pangolin" msg_info "Creating Pangolin Service" From f380816439a13ee64a675f0639269b6f68199633 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Sun, 2 Nov 2025 19:14:07 +0100 Subject: [PATCH 1655/1733] Pangolin: update json --- install/pangolin-install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install/pangolin-install.sh b/install/pangolin-install.sh index 206938ee0..0a82cd4fb 100644 --- a/install/pangolin-install.sh +++ b/install/pangolin-install.sh @@ -33,6 +33,7 @@ $STD npm ci echo "export * from \"./$DATABASE\";" > server/db/index.ts echo "export const build = \"$BUILD\" as any;" > server/build.ts cp tsconfig.oss.json tsconfig.json +rm -rf server/private mkdir -p dist $STD npm run next:build $STD node esbuild.mjs -e server/index.ts -o dist/server.mjs -b $BUILD From 8535e8b35711e616adc293c90c98cefd9a6e4bec Mon Sep 17 00:00:00 2001 From: Romain PINSOLLE <53913510+TuroYT@users.noreply.github.com> Date: Mon, 3 Nov 2025 08:09:59 +0100 Subject: [PATCH 1656/1733] Change environment variable to use .env file --- install/snowshare-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/snowshare-install.sh b/install/snowshare-install.sh index b188c7775..b038048ab 100644 --- a/install/snowshare-install.sh +++ b/install/snowshare-install.sh @@ -58,7 +58,7 @@ Requires=postgresql.service Type=simple User=root WorkingDirectory=/opt/snowshare -Environment=NODE_ENV=production +EnvironmentFile=/opt/snowshare/.env ExecStart=/usr/bin/npm start Restart=on-failure RestartSec=10 From 00bca3d91621aad02ce5126235d53481a348170b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 3 Nov 2025 09:07:13 +0100 Subject: [PATCH 1657/1733] Update donetick.sh --- ct/donetick.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ct/donetick.sh b/ct/donetick.sh index dc59bc895..c27d820c9 100644 --- a/ct/donetick.sh +++ b/ct/donetick.sh @@ -34,15 +34,21 @@ function update_script() { systemctl stop donetick msg_ok "Stopped Service" + msg_info "Backing Up Configurations" mv /opt/donetick/config/selfhosted.yml /opt/donetick/donetick.db /opt + msg_ok "Backed Up Configurations" + CLEAN_INSTALL=1 fetch_and_deploy_gh_release "donetick" "donetick/donetick" "prebuild" "latest" "/opt/donetick" "donetick_Linux_x86_64.tar.gz" + + msg_info "Restoring Configurations" mv /opt/selfhosted.yml /opt/donetick/config mv /opt/donetick.db /opt/donetick + msg_ok "Restored Configurations" msg_info "Starting Service" systemctl start donetick msg_ok "Started Service" - msg_ok "Updated Successfully!" + msg_ok "Updated successfully!" fi exit } From 958e42edee1cb6674b9e49ca633eda054bcceeb5 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 3 Nov 2025 11:09:34 +0100 Subject: [PATCH 1658/1733] test --- ct/asterisk.sh | 35 +++++++++++ ct/rybbit.sh | 24 ++++---- install/asterisk-install.sh | 113 ++++++++++++++++++++++++++++++++++++ install/rybbit-install.sh | 2 +- 4 files changed, 161 insertions(+), 13 deletions(-) create mode 100644 ct/asterisk.sh create mode 100644 install/asterisk-install.sh diff --git a/ct/asterisk.sh b/ct/asterisk.sh new file mode 100644 index 000000000..02f2506cb --- /dev/null +++ b/ct/asterisk.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 community-scripts ORG +# Author: michelroegl-brunner +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://asterisk.org/ + +APP="Asterisk" +var_tags="${var_tags:-telephone;pbx}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-12}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + msg_error "No Update function provided for ${APP} LXC" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" diff --git a/ct/rybbit.sh b/ct/rybbit.sh index e523bde2b..62447664c 100644 --- a/ct/rybbit.sh +++ b/ct/rybbit.sh @@ -11,7 +11,7 @@ var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" var_disk="${var_disk:-5}" var_os="${var_os:-debian}" -var_version="${var_version:-12}" +var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" header_info "$APP" @@ -20,18 +20,18 @@ color catch_errors function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -d /var ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - msg_info "Updating $APP LXC" - $STD apt-get update - $STD apt-get -y upgrade - msg_ok "Updated $APP LXC" + header_info + check_container_storage + check_container_resources + if [[ ! -d /var ]]; then + msg_error "No ${APP} Installation Found!" exit + fi + msg_info "Updating $APP LXC" + $STD apt-get update + $STD apt-get -y upgrade + msg_ok "Updated $APP LXC" + exit } start diff --git a/install/asterisk-install.sh b/install/asterisk-install.sh new file mode 100644 index 000000000..07557a072 --- /dev/null +++ b/install/asterisk-install.sh @@ -0,0 +1,113 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 community-scripts ORG +# Author: michelroegl-brunner +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://asterisk.org + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +ASTERISK_VERSIONS_URL="https://www.asterisk.org/downloads/asterisk/all-asterisk-versions/" +html=$(curl -fsSL "$ASTERISK_VERSIONS_URL") + +LTS_VERSION="" +for major in 20 22 24 26; do + block=$(echo "$html" | awk "/Asterisk $major - LTS/,/
    /") + ver=$(echo "$block" | grep -oE 'Download Latest - [0-9]+\.[0-9]+(\.[0-9]+)?' | head -n1 | sed -E 's/.* - //') + if [ -n "$ver" ]; then + LTS_VERSION="$LTS_VERSION $ver" + fi + unset ver block +done +LTS_VERSION=$(echo "$LTS_VERSION" | xargs | tr ' ' '\n' | sort -V | tail -n1) + +STD_VERSION="" +for major in 21 23 25 27; do + block=$(echo "$html" | awk "/Asterisk $major/") + ver=$(echo "$block" | grep -oE 'Download (Latest - )?[0-9]+\.[0-9]+(\.[0-9]+)?' | head -n1 | sed -E 's/.* - //;s/Download //') + if [ -n "$ver" ]; then + STD_VERSION="$STD_VERSION $ver" + fi + unset ver block +done +STD_VERSION=$(echo "$STD_VERSION" | xargs | tr ' ' '\n' | sort -V | tail -n1) + +cert_block=$(echo "$html" | awk '/Certified Asterisk/,/
      /') +CERT_VERSION=$(echo "$cert_block" | grep -oE 'Download Latest - [0-9]+\.[0-9]+-cert[0-9]+' | head -n1 | sed -E 's/.* - //') + +cat < Date: Mon, 3 Nov 2025 11:17:34 +0100 Subject: [PATCH 1659/1733] Update asterisk-install.sh --- install/asterisk-install.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/install/asterisk-install.sh b/install/asterisk-install.sh index 07557a072..20d5214ea 100644 --- a/install/asterisk-install.sh +++ b/install/asterisk-install.sh @@ -19,7 +19,7 @@ html=$(curl -fsSL "$ASTERISK_VERSIONS_URL") LTS_VERSION="" for major in 20 22 24 26; do block=$(echo "$html" | awk "/Asterisk $major - LTS/,/
        /") - ver=$(echo "$block" | grep -oE 'Download Latest - [0-9]+\.[0-9]+(\.[0-9]+)?' | head -n1 | sed -E 's/.* - //') + ver=$(echo "$block" | grep -oE 'Download Latest - [0-9]+\.[0-9]+(\.[0-9]+)?' | head -n1 | sed -E 's/.* - //' || true) if [ -n "$ver" ]; then LTS_VERSION="$LTS_VERSION $ver" fi @@ -29,8 +29,8 @@ LTS_VERSION=$(echo "$LTS_VERSION" | xargs | tr ' ' '\n' | sort -V | tail -n1) STD_VERSION="" for major in 21 23 25 27; do - block=$(echo "$html" | awk "/Asterisk $major/") - ver=$(echo "$block" | grep -oE 'Download (Latest - )?[0-9]+\.[0-9]+(\.[0-9]+)?' | head -n1 | sed -E 's/.* - //;s/Download //') + block=$(echo "$html" | grep -A 20 "Asterisk $major" | head -n 20) + ver=$(echo "$block" | grep -oE 'Download (Latest - )?'"$major"'\.[0-9]+\.[0-9]+' | head -n1 | sed -E 's/Download (Latest - )?//' || true) if [ -n "$ver" ]; then STD_VERSION="$STD_VERSION $ver" fi @@ -39,7 +39,7 @@ done STD_VERSION=$(echo "$STD_VERSION" | xargs | tr ' ' '\n' | sort -V | tail -n1) cert_block=$(echo "$html" | awk '/Certified Asterisk/,/
          /') -CERT_VERSION=$(echo "$cert_block" | grep -oE 'Download Latest - [0-9]+\.[0-9]+-cert[0-9]+' | head -n1 | sed -E 's/.* - //') +CERT_VERSION=$(echo "$cert_block" | grep -oE 'Download Latest - [0-9]+\.[0-9]+-cert[0-9]+' | head -n1 | sed -E 's/.* - //' || true) cat < Date: Mon, 3 Nov 2025 11:22:51 +0100 Subject: [PATCH 1660/1733] Update asterisk-install.sh --- install/asterisk-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/asterisk-install.sh b/install/asterisk-install.sh index 20d5214ea..d6307c288 100644 --- a/install/asterisk-install.sh +++ b/install/asterisk-install.sh @@ -18,7 +18,7 @@ html=$(curl -fsSL "$ASTERISK_VERSIONS_URL") LTS_VERSION="" for major in 20 22 24 26; do - block=$(echo "$html" | awk "/Asterisk $major - LTS/,/
            /") + block=$(echo "$html" | awk "/Asterisk $major - LTS/,/
              /" || true) ver=$(echo "$block" | grep -oE 'Download Latest - [0-9]+\.[0-9]+(\.[0-9]+)?' | head -n1 | sed -E 's/.* - //' || true) if [ -n "$ver" ]; then LTS_VERSION="$LTS_VERSION $ver" @@ -29,7 +29,7 @@ LTS_VERSION=$(echo "$LTS_VERSION" | xargs | tr ' ' '\n' | sort -V | tail -n1) STD_VERSION="" for major in 21 23 25 27; do - block=$(echo "$html" | grep -A 20 "Asterisk $major" | head -n 20) + block=$(echo "$html" | grep -A 20 "Asterisk $major" | head -n 20 || true) ver=$(echo "$block" | grep -oE 'Download (Latest - )?'"$major"'\.[0-9]+\.[0-9]+' | head -n1 | sed -E 's/Download (Latest - )?//' || true) if [ -n "$ver" ]; then STD_VERSION="$STD_VERSION $ver" From 394cf4c1a8c3e92a44d7da7d1372ce7691ae2d30 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Mon, 3 Nov 2025 11:27:32 +0100 Subject: [PATCH 1661/1733] Update asterisk-install.sh --- install/asterisk-install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install/asterisk-install.sh b/install/asterisk-install.sh index d6307c288..202c12e10 100644 --- a/install/asterisk-install.sh +++ b/install/asterisk-install.sh @@ -49,6 +49,7 @@ Choose Asterisk version to install: EOF read -rp "Enter choice [1-3]: " ASTERISK_CHOICE +CERTIFIED=0 case "$ASTERISK_CHOICE" in 2) ASTERISK_VERSION="$LTS_VERSION" From 21b7d27dbc2f891dd950f12119196afdb9c94733 Mon Sep 17 00:00:00 2001 From: Romain PINSOLLE <53913510+TuroYT@users.noreply.github.com> Date: Mon, 3 Nov 2025 13:01:08 +0100 Subject: [PATCH 1662/1733] Update logo URL in snowshare.json --- frontend/public/json/snowshare.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/public/json/snowshare.json b/frontend/public/json/snowshare.json index f952b4b18..40433298c 100644 --- a/frontend/public/json/snowshare.json +++ b/frontend/public/json/snowshare.json @@ -12,7 +12,7 @@ "documentation": "https://github.com/TuroYT/snowshare", "config_path": "/opt/snowshare/.env", "website": "https://github.com/TuroYT/snowshare", - "logo": "https://github.com/TuroYT/snowshare/raw/main/public/logo.svg", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/png/snowshare.png", "description": "A modern, secure file and link sharing platform built with Next.js, Prisma, and NextAuth. Share URLs, code snippets, and files with customizable expiration, privacy, and QR codes.", "install_methods": [ { @@ -32,4 +32,4 @@ "password": null }, "notes": [] -} \ No newline at end of file +} From 24832bc29d5e4cbb8ee803603f0975a6e1df91b2 Mon Sep 17 00:00:00 2001 From: Romain PINSOLLE Date: Mon, 3 Nov 2025 14:57:36 +0000 Subject: [PATCH 1663/1733] fixed --- install/snowshare-install.sh | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/install/snowshare-install.sh b/install/snowshare-install.sh index b038048ab..98e064a15 100644 --- a/install/snowshare-install.sh +++ b/install/snowshare-install.sh @@ -14,11 +14,10 @@ update_os setup_nodejs -cd /opt fetch_and_deploy_gh_release "snowshare" "TuroYT/snowshare" -msg_info "Setting up PostgreSQL Database" setup_postgresql +msg_info "Setting up PostgreSQL Database" DB_NAME=snowshare DB_USER=snowshare DB_PASS="$(openssl rand -base64 18 | cut -c1-13)" @@ -35,10 +34,10 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" } >>~/snowshare.creds msg_ok "Set up PostgreSQL Database" -msg_info "Installing SnowShare (Patience)" +msg_info "Installing SnowShare" cd /opt/snowshare $STD npm ci -cat </opt/snowshare/.env +cat <.env DATABASE_URL="postgresql://$DB_USER:$DB_PASS@localhost:5432/$DB_NAME" NEXTAUTH_URL="http://localhost:3000" NEXTAUTH_SECRET="$(openssl rand -base64 32)" @@ -56,9 +55,8 @@ Requires=postgresql.service [Service] Type=simple -User=root WorkingDirectory=/opt/snowshare -EnvironmentFile=/opt/snowshare/.env +EnvironmentFile=.env ExecStart=/usr/bin/npm start Restart=on-failure RestartSec=10 From a5a59fced4dc499a0afb054ccd496f5fd4cee239 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Mon, 3 Nov 2025 16:20:34 +0100 Subject: [PATCH 1664/1733] fix: env file location --- install/snowshare-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/snowshare-install.sh b/install/snowshare-install.sh index 98e064a15..d31695b64 100644 --- a/install/snowshare-install.sh +++ b/install/snowshare-install.sh @@ -37,7 +37,7 @@ msg_ok "Set up PostgreSQL Database" msg_info "Installing SnowShare" cd /opt/snowshare $STD npm ci -cat <.env +cat </opt/snowshare.env DATABASE_URL="postgresql://$DB_USER:$DB_PASS@localhost:5432/$DB_NAME" NEXTAUTH_URL="http://localhost:3000" NEXTAUTH_SECRET="$(openssl rand -base64 32)" @@ -56,7 +56,7 @@ Requires=postgresql.service [Service] Type=simple WorkingDirectory=/opt/snowshare -EnvironmentFile=.env +EnvironmentFile=/opt/snowshare.env ExecStart=/usr/bin/npm start Restart=on-failure RestartSec=10 From 300c2356837000700cf50f225a0153a70aef05b0 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Mon, 3 Nov 2025 16:21:07 +0100 Subject: [PATCH 1665/1733] blank link --- ct/snowshare.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ct/snowshare.sh b/ct/snowshare.sh index f8a06fd96..9241f44db 100644 --- a/ct/snowshare.sh +++ b/ct/snowshare.sh @@ -13,6 +13,7 @@ var_disk="${var_disk:-5}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" + header_info "$APP" variables color From 2c597f3a494d499ca466b11f338d535fb0c5059a Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 3 Nov 2025 15:28:02 +0000 Subject: [PATCH 1666/1733] Update .app files --- ct/headers/asterisk | 6 ++++++ ct/headers/snowshare | 6 ++++++ 2 files changed, 12 insertions(+) create mode 100644 ct/headers/asterisk create mode 100644 ct/headers/snowshare diff --git a/ct/headers/asterisk b/ct/headers/asterisk new file mode 100644 index 000000000..ed4356862 --- /dev/null +++ b/ct/headers/asterisk @@ -0,0 +1,6 @@ + ___ __ _ __ + / | _____/ /____ _____(_)____/ /__ + / /| | / ___/ __/ _ \/ ___/ / ___/ //_/ + / ___ |(__ ) /_/ __/ / / (__ ) ,< +/_/ |_/____/\__/\___/_/ /_/____/_/|_| + diff --git a/ct/headers/snowshare b/ct/headers/snowshare new file mode 100644 index 000000000..160614e0c --- /dev/null +++ b/ct/headers/snowshare @@ -0,0 +1,6 @@ + _____ _____ __ + / ___/____ ____ _ __/ ___// /_ ____ _________ + \__ \/ __ \/ __ \ | /| / /\__ \/ __ \/ __ `/ ___/ _ \ + ___/ / / / / /_/ / |/ |/ /___/ / / / / /_/ / / / __/ +/____/_/ /_/\____/|__/|__//____/_/ /_/\__,_/_/ \___/ + From 9b811aed38c2850fbc3bf7d9bc697d6776fa06a5 Mon Sep 17 00:00:00 2001 From: Romain PINSOLLE <53913510+TuroYT@users.noreply.github.com> Date: Mon, 3 Nov 2025 18:23:29 +0100 Subject: [PATCH 1667/1733] Add DATABASE_URL to snowshare-install.sh --- install/snowshare-install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install/snowshare-install.sh b/install/snowshare-install.sh index d31695b64..2f2865480 100644 --- a/install/snowshare-install.sh +++ b/install/snowshare-install.sh @@ -44,6 +44,7 @@ NEXTAUTH_SECRET="$(openssl rand -base64 32)" ALLOW_SIGNUP=true NODE_ENV=production EOF +DATABASE_URL="postgresql://$DB_USER:$DB_PASS@localhost:5432/$DB_NAME" $STD npx prisma generate $STD npx prisma migrate deploy $STD npm run build From 396f1380295c446afa8a72c5a5b646d54623dfac Mon Sep 17 00:00:00 2001 From: Romain PINSOLLE Date: Mon, 3 Nov 2025 18:47:58 +0100 Subject: [PATCH 1668/1733] fix env for build --- install/snowshare-install.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/install/snowshare-install.sh b/install/snowshare-install.sh index 2f2865480..31395dca9 100644 --- a/install/snowshare-install.sh +++ b/install/snowshare-install.sh @@ -44,7 +44,9 @@ NEXTAUTH_SECRET="$(openssl rand -base64 32)" ALLOW_SIGNUP=true NODE_ENV=production EOF -DATABASE_URL="postgresql://$DB_USER:$DB_PASS@localhost:5432/$DB_NAME" +set -a +source /opt/snowshare.env +set +a $STD npx prisma generate $STD npx prisma migrate deploy $STD npm run build From 0c5b2a7392dc9fb2ab540a7524bb48af0eea228b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 4 Nov 2025 10:56:28 +0100 Subject: [PATCH 1669/1733] Update snowshare-install.sh --- install/snowshare-install.sh | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/install/snowshare-install.sh b/install/snowshare-install.sh index 31395dca9..b953d1ad3 100644 --- a/install/snowshare-install.sh +++ b/install/snowshare-install.sh @@ -13,10 +13,9 @@ network_check update_os setup_nodejs - +setup_postgresql fetch_and_deploy_gh_release "snowshare" "TuroYT/snowshare" -setup_postgresql msg_info "Setting up PostgreSQL Database" DB_NAME=snowshare DB_USER=snowshare @@ -27,10 +26,10 @@ $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET client_encoding TO 'utf8' $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET default_transaction_isolation TO 'read committed';" $STD sudo -u postgres psql -c "ALTER ROLE $DB_USER SET timezone TO 'UTC';" { - echo "SnowShare-Database-Credentials" - echo "Database Username: $DB_USER" - echo "Database Password: $DB_PASS" - echo "Database Name: $DB_NAME" + echo "SnowShare-Database-Credentials" + echo "Database Username: $DB_USER" + echo "Database Password: $DB_PASS" + echo "Database Name: $DB_NAME" } >>~/snowshare.creds msg_ok "Set up PostgreSQL Database" From bb64b2f25a5c3f790fb4acbd1723ad92c16d0152 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 4 Nov 2025 10:58:22 +0100 Subject: [PATCH 1670/1733] Update librenms-install.sh --- install/librenms-install.sh | 68 ++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/install/librenms-install.sh b/install/librenms-install.sh index ad1cbb6fe..970ba9bed 100644 --- a/install/librenms-install.sh +++ b/install/librenms-install.sh @@ -14,30 +14,34 @@ network_check update_os msg_info "Installing Dependencies" -$STD apt-get install -y \ - lsb-release \ - ca-certificates \ - acl \ - fping \ - graphviz \ - imagemagick \ - mtr-tiny \ - nginx \ - nmap \ - rrdtool \ - snmp \ - snmpd +$STD apt install -y \ + acl \ + fping \ + graphviz \ + imagemagick \ + mtr-tiny \ + nginx \ + nmap \ + rrdtool \ + snmp \ + snmpd \ + whois msg_ok "Installed Dependencies" -PHP_VERSION="8.3" PHP_FPM="YES" PHP_MODULE="gmp,mysql,snmp" setup_php +PHP_VERSION="8.4" PHP_FPM="YES" PHP_MODULE="gmp,mysql,snmp" setup_php setup_mariadb setup_composer PYTHON_VERSION="3.13" setup_uv -msg_info "Installing Python" -$STD apt-get install -y \ - python3-{dotenv,pymysql,redis,setuptools,systemd,pip} -msg_ok "Installed Python" +msg_info "Installing Python Dependencies" +$STD apt install -y \ + python3-dotenv \ + python3-pymysql \ + python3-redis \ + python3-setuptools \ + python3-systemd \ + python3-pip +msg_ok "Installed Python Dependencies" msg_info "Configuring Database" DB_NAME=librenms @@ -47,10 +51,10 @@ $STD mariadb -u root -e "CREATE DATABASE $DB_NAME CHARACTER SET utf8mb4 COLLATE $STD mariadb -u root -e "CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';" $STD mariadb -u root -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUSH PRIVILEGES;" { - echo "LibreNMS-Credentials" - echo "LibreNMS Database User: $DB_USER" - echo "LibreNMS Database Password: $DB_PASS" - echo "LibreNMS Database Name: $DB_NAME" + echo "LibreNMS-Credentials" + echo "LibreNMS Database User: $DB_USER" + echo "LibreNMS Database Password: $DB_PASS" + echo "LibreNMS Database Name: $DB_NAME" } >>~/librenms.creds msg_ok "Configured Database" @@ -81,11 +85,11 @@ systemctl enable -q --now mariadb msg_ok "Configured MariaDB" msg_info "Configure PHP-FPM" -cp /etc/php/8.2/fpm/pool.d/www.conf /etc/php/8.2/fpm/pool.d/librenms.conf -sed -i "s/\[www\]/\[librenms\]/g" /etc/php/8.2/fpm/pool.d/librenms.conf -sed -i "s/user = www-data/user = librenms/g" /etc/php/8.2/fpm/pool.d/librenms.conf -sed -i "s/group = www-data/group = librenms/g" /etc/php/8.2/fpm/pool.d/librenms.conf -sed -i "s/listen = \/run\/php\/php8.2-fpm.sock/listen = \/run\/php-fpm-librenms.sock/g" /etc/php/8.2/fpm/pool.d/librenms.conf +cp /etc/php/8.4/fpm/pool.d/www.conf /etc/php/8.4/fpm/pool.d/librenms.conf +sed -i "s/\[www\]/\[librenms\]/g" /etc/php/8.4/fpm/pool.d/librenms.conf +sed -i "s/user = www-data/user = librenms/g" /etc/php/8.4/fpm/pool.d/librenms.conf +sed -i "s/group = www-data/group = librenms/g" /etc/php/8.4/fpm/pool.d/librenms.conf +sed -i "s/listen = \/run\/php\/php8.4-fpm.sock/listen = \/run\/php-fpm-librenms.sock/g" /etc/php/8.4/fpm/pool.d/librenms.conf msg_ok "Configured PHP-FPM" msg_info "Configure Nginx" @@ -115,14 +119,14 @@ server { EOF rm /etc/nginx/sites-enabled/default $STD systemctl reload nginx -systemctl restart php8.2-fpm +systemctl restart php8.4-fpm msg_ok "Configured Nginx" msg_info "Configure Services" COMPOSER_ALLOW_SUPERUSER=1 $STD composer install --no-dev -$STD php8.2 artisan migrate --force -$STD php8.2 artisan key:generate --force +$STD php8.4 artisan migrate --force +$STD php8.4 artisan key:generate --force $STD su librenms -s /bin/bash -c "lnms db:seed --force" $STD su librenms -s /bin/bash -c "lnms user:add -p admin -r admin admin" ln -s /opt/librenms/lnms /usr/bin/lnms @@ -148,6 +152,6 @@ motd_ssh customize msg_info "Cleaning up" -$STD apt-get -y autoremove -$STD apt-get -y autoclean +$STD apt -y autoremove +$STD apt -y autoclean msg_ok "Cleaned" From 07a5f145f62f8c1c6a14ff767692c450781ad5a2 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 4 Nov 2025 12:47:12 +0100 Subject: [PATCH 1671/1733] Update librenms-install.sh --- install/librenms-install.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/install/librenms-install.sh b/install/librenms-install.sh index 970ba9bed..8b60b65de 100644 --- a/install/librenms-install.sh +++ b/install/librenms-install.sh @@ -62,8 +62,7 @@ fetch_and_deploy_gh_release "LibreNMS" "librenms/librenms" msg_info "Configuring LibreNMS" $STD useradd librenms -d /opt/librenms -M -r -s "$(which bash)" -setfacl -d -m g::rwx /opt/librenms/rrd /opt/librenms/logs /opt/librenms/bootstrap/cache/ /opt/librenms/storage/ -setfacl -R -m g::rwx /opt/librenms/rrd /opt/librenms/logs /opt/librenms/bootstrap/cache/ /opt/librenms/storage/ +mkdir -p /opt/librenms/{rrd,logs,bootstrap/cache,storage,html} cd /opt/librenms $STD uv venv .venv $STD source .venv/bin/activate @@ -75,7 +74,6 @@ DB_PASSWORD=${DB_PASS} EOF chown -R librenms:librenms /opt/librenms chmod 771 /opt/librenms -setfacl -d -m g::rwx /opt/librenms/bootstrap/cache /opt/librenms/storage /opt/librenms/logs /opt/librenms/rrd chmod -R ug=rwX /opt/librenms/bootstrap/cache /opt/librenms/storage /opt/librenms/logs /opt/librenms/rrd msg_ok "Configured LibreNMS" From 5ae38e84c81bde6712c2f78d4debed7e6190ab84 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 4 Nov 2025 13:13:08 +0100 Subject: [PATCH 1672/1733] Fix LibreNMS release name and improve Node.js install logs Corrects the case of the release name in the LibreNMS installer to 'librenms' and updates Node.js installation commands to use the $STD variable for consistent output and logging. --- install/librenms-install.sh | 2 +- misc/tools.func | 5990 +++++++++++++++++------------------ 2 files changed, 2996 insertions(+), 2996 deletions(-) diff --git a/install/librenms-install.sh b/install/librenms-install.sh index 8b60b65de..2b9ff3e44 100644 --- a/install/librenms-install.sh +++ b/install/librenms-install.sh @@ -58,7 +58,7 @@ $STD mariadb -u root -e "GRANT ALL ON $DB_NAME.* TO '$DB_USER'@'localhost'; FLUS } >>~/librenms.creds msg_ok "Configured Database" -fetch_and_deploy_gh_release "LibreNMS" "librenms/librenms" +fetch_and_deploy_gh_release "librenms" "librenms/librenms" msg_info "Configuring LibreNMS" $STD useradd librenms -d /opt/librenms -M -r -s "$(which bash)" diff --git a/misc/tools.func b/misc/tools.func index fb96ed0ee..dc3b00b45 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -8,20 +8,20 @@ # Cache installed version to avoid repeated checks # ------------------------------------------------------------------------------ cache_installed_version() { - local app="$1" - local version="$2" - mkdir -p /var/cache/app-versions - echo "$version" >"/var/cache/app-versions/${app}_version.txt" + local app="$1" + local version="$2" + mkdir -p /var/cache/app-versions + echo "$version" >"/var/cache/app-versions/${app}_version.txt" } get_cached_version() { - local app="$1" - mkdir -p /var/cache/app-versions - if [[ -f "/var/cache/app-versions/${app}_version.txt" ]]; then - cat "/var/cache/app-versions/${app}_version.txt" + local app="$1" + mkdir -p /var/cache/app-versions + if [[ -f "/var/cache/app-versions/${app}_version.txt" ]]; then + cat "/var/cache/app-versions/${app}_version.txt" + return 0 + fi return 0 - fi - return 0 } # ------------------------------------------------------------------------------ @@ -30,74 +30,74 @@ get_cached_version() { # Usage: is_tool_installed "mariadb" "11.4" || echo "Not installed" # ------------------------------------------------------------------------------ is_tool_installed() { - local tool_name="$1" - local required_version="${2:-}" - local installed_version="" + local tool_name="$1" + local required_version="${2:-}" + local installed_version="" - case "$tool_name" in - mariadb) - if command -v mariadb >/dev/null 2>&1; then - installed_version=$(mariadb --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1) - fi - ;; - mysql) - if command -v mysql >/dev/null 2>&1; then - installed_version=$(mysql --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1) - fi - ;; - mongodb | mongod) - if command -v mongod >/dev/null 2>&1; then - installed_version=$(mongod --version 2>/dev/null | awk '/db version/{print $3}' | cut -d. -f1,2) - fi - ;; - node | nodejs) - if command -v node >/dev/null 2>&1; then - installed_version=$(node -v 2>/dev/null | grep -oP '^v\K[0-9]+') - fi - ;; - php) - if command -v php >/dev/null 2>&1; then - installed_version=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2) - fi - ;; - postgres | postgresql) - if command -v psql >/dev/null 2>&1; then - installed_version=$(psql --version 2>/dev/null | awk '{print $3}' | cut -d. -f1) - fi - ;; - ruby) - if command -v ruby >/dev/null 2>&1; then - installed_version=$(ruby --version 2>/dev/null | awk '{print $2}' | cut -d. -f1,2) - fi - ;; - rust | rustc) - if command -v rustc >/dev/null 2>&1; then - installed_version=$(rustc --version 2>/dev/null | awk '{print $2}') - fi - ;; - go | golang) - if command -v go >/dev/null 2>&1; then - installed_version=$(go version 2>/dev/null | awk '{print $3}' | sed 's/go//') - fi - ;; - clickhouse) - if command -v clickhouse >/dev/null 2>&1; then - installed_version=$(clickhouse --version 2>/dev/null | awk '{print $2}') - fi - ;; - esac + case "$tool_name" in + mariadb) + if command -v mariadb >/dev/null 2>&1; then + installed_version=$(mariadb --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1) + fi + ;; + mysql) + if command -v mysql >/dev/null 2>&1; then + installed_version=$(mysql --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1) + fi + ;; + mongodb | mongod) + if command -v mongod >/dev/null 2>&1; then + installed_version=$(mongod --version 2>/dev/null | awk '/db version/{print $3}' | cut -d. -f1,2) + fi + ;; + node | nodejs) + if command -v node >/dev/null 2>&1; then + installed_version=$(node -v 2>/dev/null | grep -oP '^v\K[0-9]+') + fi + ;; + php) + if command -v php >/dev/null 2>&1; then + installed_version=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2) + fi + ;; + postgres | postgresql) + if command -v psql >/dev/null 2>&1; then + installed_version=$(psql --version 2>/dev/null | awk '{print $3}' | cut -d. -f1) + fi + ;; + ruby) + if command -v ruby >/dev/null 2>&1; then + installed_version=$(ruby --version 2>/dev/null | awk '{print $2}' | cut -d. -f1,2) + fi + ;; + rust | rustc) + if command -v rustc >/dev/null 2>&1; then + installed_version=$(rustc --version 2>/dev/null | awk '{print $2}') + fi + ;; + go | golang) + if command -v go >/dev/null 2>&1; then + installed_version=$(go version 2>/dev/null | awk '{print $3}' | sed 's/go//') + fi + ;; + clickhouse) + if command -v clickhouse >/dev/null 2>&1; then + installed_version=$(clickhouse --version 2>/dev/null | awk '{print $2}') + fi + ;; + esac - if [[ -z "$installed_version" ]]; then - return 1 # Not installed - fi + if [[ -z "$installed_version" ]]; then + return 1 # Not installed + fi + + if [[ -n "$required_version" && "$installed_version" != "$required_version" ]]; then + echo "$installed_version" + return 1 # Version mismatch + fi - if [[ -n "$required_version" && "$installed_version" != "$required_version" ]]; then echo "$installed_version" - return 1 # Version mismatch - fi - - echo "$installed_version" - return 0 # Installed and version matches (if specified) + return 0 # Installed and version matches (if specified) } # ------------------------------------------------------------------------------ @@ -105,65 +105,65 @@ is_tool_installed() { # Usage: remove_old_tool_version "mariadb" "repository-name" # ------------------------------------------------------------------------------ remove_old_tool_version() { - local tool_name="$1" - local repo_name="${2:-$tool_name}" + local tool_name="$1" + local repo_name="${2:-$tool_name}" - case "$tool_name" in - mariadb) - $STD systemctl stop mariadb >/dev/null 2>&1 || true - $STD apt purge -y 'mariadb*' >/dev/null 2>&1 || true - ;; - mysql) - $STD systemctl stop mysql >/dev/null 2>&1 || true - $STD apt purge -y 'mysql*' >/dev/null 2>&1 || true - rm -rf /var/lib/mysql >/dev/null 2>&1 || true - ;; - mongodb) - $STD systemctl stop mongod >/dev/null 2>&1 || true - $STD apt purge -y 'mongodb*' >/dev/null 2>&1 || true - rm -rf /var/lib/mongodb >/dev/null 2>&1 || true - ;; - node | nodejs) - $STD apt purge -y nodejs npm >/dev/null 2>&1 || true - npm list -g 2>/dev/null | grep -oE '^ \S+' | awk '{print $1}' | while read -r module; do - npm uninstall -g "$module" >/dev/null 2>&1 || true - done - ;; - php) - # Disable PHP-FPM if running - $STD systemctl disable php*-fpm >/dev/null 2>&1 || true - $STD systemctl stop php*-fpm >/dev/null 2>&1 || true - $STD apt purge -y 'php*' >/dev/null 2>&1 || true - rm -rf /etc/php >/dev/null 2>&1 || true - ;; - postgresql) - $STD systemctl stop postgresql >/dev/null 2>&1 || true - $STD apt purge -y 'postgresql*' >/dev/null 2>&1 || true - rm -rf /var/lib/postgresql >/dev/null 2>&1 || true - ;; - ruby) - if [[ -d "$HOME/.rbenv" ]]; then - rm -rf "$HOME/.rbenv" - fi - $STD apt purge -y 'ruby*' >/dev/null 2>&1 || true - ;; - rust) - rm -rf "$HOME/.cargo" "$HOME/.rustup" >/dev/null 2>&1 || true - ;; - go | golang) - rm -rf /usr/local/go >/dev/null 2>&1 || true - ;; - clickhouse) - $STD systemctl stop clickhouse-server >/dev/null 2>&1 || true - $STD apt purge -y 'clickhouse*' >/dev/null 2>&1 || true - rm -rf /var/lib/clickhouse >/dev/null 2>&1 || true - ;; - esac + case "$tool_name" in + mariadb) + $STD systemctl stop mariadb >/dev/null 2>&1 || true + $STD apt purge -y 'mariadb*' >/dev/null 2>&1 || true + ;; + mysql) + $STD systemctl stop mysql >/dev/null 2>&1 || true + $STD apt purge -y 'mysql*' >/dev/null 2>&1 || true + rm -rf /var/lib/mysql >/dev/null 2>&1 || true + ;; + mongodb) + $STD systemctl stop mongod >/dev/null 2>&1 || true + $STD apt purge -y 'mongodb*' >/dev/null 2>&1 || true + rm -rf /var/lib/mongodb >/dev/null 2>&1 || true + ;; + node | nodejs) + $STD apt purge -y nodejs npm >/dev/null 2>&1 || true + npm list -g 2>/dev/null | grep -oE '^ \S+' | awk '{print $1}' | while read -r module; do + npm uninstall -g "$module" >/dev/null 2>&1 || true + done + ;; + php) + # Disable PHP-FPM if running + $STD systemctl disable php*-fpm >/dev/null 2>&1 || true + $STD systemctl stop php*-fpm >/dev/null 2>&1 || true + $STD apt purge -y 'php*' >/dev/null 2>&1 || true + rm -rf /etc/php >/dev/null 2>&1 || true + ;; + postgresql) + $STD systemctl stop postgresql >/dev/null 2>&1 || true + $STD apt purge -y 'postgresql*' >/dev/null 2>&1 || true + rm -rf /var/lib/postgresql >/dev/null 2>&1 || true + ;; + ruby) + if [[ -d "$HOME/.rbenv" ]]; then + rm -rf "$HOME/.rbenv" + fi + $STD apt purge -y 'ruby*' >/dev/null 2>&1 || true + ;; + rust) + rm -rf "$HOME/.cargo" "$HOME/.rustup" >/dev/null 2>&1 || true + ;; + go | golang) + rm -rf /usr/local/go >/dev/null 2>&1 || true + ;; + clickhouse) + $STD systemctl stop clickhouse-server >/dev/null 2>&1 || true + $STD apt purge -y 'clickhouse*' >/dev/null 2>&1 || true + rm -rf /var/lib/clickhouse >/dev/null 2>&1 || true + ;; + esac - # Clean up old repositories - cleanup_old_repo_files "$repo_name" + # Clean up old repositories + cleanup_old_repo_files "$repo_name" - return 0 + return 0 } # ------------------------------------------------------------------------------ @@ -172,19 +172,19 @@ remove_old_tool_version() { # Usage: if should_update_tool "mariadb" "11.4"; then ... fi # ------------------------------------------------------------------------------ should_update_tool() { - local tool_name="$1" - local target_version="$2" - local current_version="" + local tool_name="$1" + local target_version="$2" + local current_version="" - # Get currently installed version - current_version=$(is_tool_installed "$tool_name" 2>/dev/null) || return 0 # Not installed = needs install + # Get currently installed version + current_version=$(is_tool_installed "$tool_name" 2>/dev/null) || return 0 # Not installed = needs install - # If versions are identical, no update needed - if [[ "$current_version" == "$target_version" ]]; then - return 1 # No update needed - fi + # If versions are identical, no update needed + if [[ "$current_version" == "$target_version" ]]; then + return 1 # No update needed + fi - return 0 # Update needed + return 0 # Update needed } # ---------------------–---------------------------------------------------------- @@ -194,59 +194,59 @@ should_update_tool() { # Supports: mariadb, mongodb, nodejs, postgresql, php, mysql # ------------------------------------------------------------------------------ manage_tool_repository() { - local tool_name="$1" - local version="$2" - local repo_url="$3" - local gpg_key_url="${4:-}" - local distro_id repo_component suite + local tool_name="$1" + local version="$2" + local repo_url="$3" + local gpg_key_url="${4:-}" + local distro_id repo_component suite - distro_id=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + distro_id=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') - case "$tool_name" in - mariadb) - if [[ -z "$repo_url" || -z "$gpg_key_url" ]]; then - msg_error "MariaDB repository requires repo_url and gpg_key_url" - return 1 - fi + case "$tool_name" in + mariadb) + if [[ -z "$repo_url" || -z "$gpg_key_url" ]]; then + msg_error "MariaDB repository requires repo_url and gpg_key_url" + return 1 + fi - # Clean old repos first - cleanup_old_repo_files "mariadb" + # Clean old repos first + cleanup_old_repo_files "mariadb" - # Get suite for fallback handling - local distro_codename - distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - suite=$(get_fallback_suite "$distro_id" "$distro_codename" "$repo_url/$distro_id") + # Get suite for fallback handling + local distro_codename + distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + suite=$(get_fallback_suite "$distro_id" "$distro_codename" "$repo_url/$distro_id") - # Setup new repository using deb822 format - setup_deb822_repo "mariadb" "$gpg_key_url" "$repo_url/$distro_id" "$suite" "main" "amd64 arm64" || return 1 - return 0 - ;; + # Setup new repository using deb822 format + setup_deb822_repo "mariadb" "$gpg_key_url" "$repo_url/$distro_id" "$suite" "main" "amd64 arm64" || return 1 + return 0 + ;; - mongodb) - if [[ -z "$repo_url" || -z "$gpg_key_url" ]]; then - msg_error "MongoDB repository requires repo_url and gpg_key_url" - return 1 - fi + mongodb) + if [[ -z "$repo_url" || -z "$gpg_key_url" ]]; then + msg_error "MongoDB repository requires repo_url and gpg_key_url" + return 1 + fi - # Clean old repos first - cleanup_old_repo_files "mongodb" + # Clean old repos first + cleanup_old_repo_files "mongodb" - # Import GPG key - mkdir -p /etc/apt/keyrings - if ! curl -fsSL "$gpg_key_url" | gpg --dearmor --yes -o "/etc/apt/keyrings/mongodb-server-${version}.gpg" 2>/dev/null; then - msg_error "Failed to download MongoDB GPG key" - return 1 - fi + # Import GPG key + mkdir -p /etc/apt/keyrings + if ! curl -fsSL "$gpg_key_url" | gpg --dearmor --yes -o "/etc/apt/keyrings/mongodb-server-${version}.gpg" 2>/dev/null; then + msg_error "Failed to download MongoDB GPG key" + return 1 + fi - # Setup repository - local distro_codename - distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - suite=$(get_fallback_suite "$distro_id" "$distro_codename" "$repo_url") + # Setup repository + local distro_codename + distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + suite=$(get_fallback_suite "$distro_id" "$distro_codename" "$repo_url") - repo_component="main" - [[ "$distro_id" == "ubuntu" ]] && repo_component="multiverse" + repo_component="main" + [[ "$distro_id" == "ubuntu" ]] && repo_component="multiverse" - cat </etc/apt/sources.list.d/mongodb-org-${version}.sources + cat </etc/apt/sources.list.d/mongodb-org-${version}.sources Types: deb URIs: ${repo_url} Suites: ${suite}/mongodb-org/${version} @@ -254,31 +254,31 @@ Components: ${repo_component} Architectures: amd64 arm64 Signed-By: /etc/apt/keyrings/mongodb-server-${version}.gpg EOF - return 0 - ;; + return 0 + ;; - nodejs) - if [[ -z "$repo_url" || -z "$gpg_key_url" ]]; then - msg_error "Node.js repository requires repo_url and gpg_key_url" - return 1 - fi + nodejs) + if [[ -z "$repo_url" || -z "$gpg_key_url" ]]; then + msg_error "Node.js repository requires repo_url and gpg_key_url" + return 1 + fi - cleanup_old_repo_files "nodesource" + cleanup_old_repo_files "nodesource" - # NodeSource uses deb822 format with GPG from repo - local distro_codename - distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + # NodeSource uses deb822 format with GPG from repo + local distro_codename + distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - # Create keyring directory first - mkdir -p /etc/apt/keyrings + # Create keyring directory first + mkdir -p /etc/apt/keyrings - # Download GPG key from NodeSource - curl -fsSL "$gpg_key_url" | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg || { - msg_error "Failed to import NodeSource GPG key" - return 1 - } + # Download GPG key from NodeSource + curl -fsSL "$gpg_key_url" | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg || { + msg_error "Failed to import NodeSource GPG key" + return 1 + } - cat </etc/apt/sources.list.d/nodesource.sources + cat </etc/apt/sources.list.d/nodesource.sources Types: deb URIs: $repo_url Suites: nodistro @@ -286,33 +286,33 @@ Components: main Architectures: amd64 arm64 Signed-By: /etc/apt/keyrings/nodesource.gpg EOF - return 0 - ;; + return 0 + ;; - php) - if [[ -z "$gpg_key_url" ]]; then - msg_error "PHP repository requires gpg_key_url" - return 1 - fi + php) + if [[ -z "$gpg_key_url" ]]; then + msg_error "PHP repository requires gpg_key_url" + return 1 + fi - cleanup_old_repo_files "php" + cleanup_old_repo_files "php" - # Download and install keyring - curl -fsSLo /tmp/debsuryorg-archive-keyring.deb "$gpg_key_url" || { - msg_error "Failed to download PHP keyring" - return 1 - } - dpkg -i /tmp/debsuryorg-archive-keyring.deb >/dev/null 2>&1 || { - msg_error "Failed to install PHP keyring" - rm -f /tmp/debsuryorg-archive-keyring.deb - return 1 - } - rm -f /tmp/debsuryorg-archive-keyring.deb + # Download and install keyring + curl -fsSLo /tmp/debsuryorg-archive-keyring.deb "$gpg_key_url" || { + msg_error "Failed to download PHP keyring" + return 1 + } + dpkg -i /tmp/debsuryorg-archive-keyring.deb >/dev/null 2>&1 || { + msg_error "Failed to install PHP keyring" + rm -f /tmp/debsuryorg-archive-keyring.deb + return 1 + } + rm -f /tmp/debsuryorg-archive-keyring.deb - # Setup repository - local distro_codename - distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - cat </etc/apt/sources.list.d/php.sources + # Setup repository + local distro_codename + distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + cat </etc/apt/sources.list.d/php.sources Types: deb URIs: https://packages.sury.org/php Suites: $distro_codename @@ -320,30 +320,30 @@ Components: main Architectures: amd64 arm64 Signed-By: /usr/share/keyrings/deb.sury.org-php.gpg EOF - return 0 - ;; + return 0 + ;; - postgresql) - if [[ -z "$gpg_key_url" ]]; then - msg_error "PostgreSQL repository requires gpg_key_url" - return 1 - fi + postgresql) + if [[ -z "$gpg_key_url" ]]; then + msg_error "PostgreSQL repository requires gpg_key_url" + return 1 + fi - cleanup_old_repo_files "postgresql" + cleanup_old_repo_files "postgresql" - # Create keyring directory first - mkdir -p /etc/apt/keyrings + # Create keyring directory first + mkdir -p /etc/apt/keyrings - # Import PostgreSQL key - curl -fsSL "$gpg_key_url" | gpg --dearmor -o /etc/apt/keyrings/postgresql.gpg || { - msg_error "Failed to import PostgreSQL GPG key" - return 1 - } + # Import PostgreSQL key + curl -fsSL "$gpg_key_url" | gpg --dearmor -o /etc/apt/keyrings/postgresql.gpg || { + msg_error "Failed to import PostgreSQL GPG key" + return 1 + } - # Setup repository - local distro_codename - distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - cat </etc/apt/sources.list.d/postgresql.sources + # Setup repository + local distro_codename + distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + cat </etc/apt/sources.list.d/postgresql.sources Types: deb URIs: http://apt.postgresql.org/pub/repos/apt Suites: $distro_codename-pgdg @@ -351,532 +351,532 @@ Components: main Architectures: amd64 arm64 Signed-By: /etc/apt/keyrings/postgresql.gpg EOF + return 0 + ;; + + *) + msg_error "Unknown tool repository: $tool_name" + return 1 + ;; + esac + return 0 - ;; - - *) - msg_error "Unknown tool repository: $tool_name" - return 1 - ;; - esac - - return 0 } # ------–---------------------------------------------------------------------- # Unified package upgrade function (with apt update caching) # ------------------------------------------------------------------------------ upgrade_package() { - local package="$1" + local package="$1" - # Use same caching logic as ensure_dependencies - local apt_cache_file="/var/cache/apt-update-timestamp" - local current_time=$(date +%s) - local last_update=0 + # Use same caching logic as ensure_dependencies + local apt_cache_file="/var/cache/apt-update-timestamp" + local current_time=$(date +%s) + local last_update=0 - if [[ -f "$apt_cache_file" ]]; then - last_update=$(cat "$apt_cache_file" 2>/dev/null || echo 0) - fi + if [[ -f "$apt_cache_file" ]]; then + last_update=$(cat "$apt_cache_file" 2>/dev/null || echo 0) + fi - if ((current_time - last_update > 300)); then - $STD apt update || { - msg_warn "APT update failed in upgrade_package - continuing with cached packages" + if ((current_time - last_update > 300)); then + $STD apt update || { + msg_warn "APT update failed in upgrade_package - continuing with cached packages" + } + echo "$current_time" >"$apt_cache_file" + fi + + $STD apt install --only-upgrade -y "$package" || { + msg_warn "Failed to upgrade $package" + return 1 } - echo "$current_time" >"$apt_cache_file" - fi - - $STD apt install --only-upgrade -y "$package" || { - msg_warn "Failed to upgrade $package" - return 1 - } } # ------------------------------------------------------------------------------ # Repository availability check # ------------------------------------------------------------------------------ verify_repo_available() { - local repo_url="$1" - local suite="$2" + local repo_url="$1" + local suite="$2" - if curl -fsSL --max-time 10 "${repo_url}/dists/${suite}/Release" &>/dev/null; then - return 0 - fi - return 1 + if curl -fsSL --max-time 10 "${repo_url}/dists/${suite}/Release" &>/dev/null; then + return 0 + fi + return 1 } # ------------------------------------------------------------------------------ # Ensure dependencies are installed (with apt update caching) # ------------------------------------------------------------------------------ ensure_dependencies() { - local deps=("$@") - local missing=() + local deps=("$@") + local missing=() - for dep in "${deps[@]}"; do - if ! command -v "$dep" &>/dev/null && ! is_package_installed "$dep"; then - missing+=("$dep") + for dep in "${deps[@]}"; do + if ! command -v "$dep" &>/dev/null && ! is_package_installed "$dep"; then + missing+=("$dep") + fi + done + + if [[ ${#missing[@]} -gt 0 ]]; then + # Only run apt update if not done recently (within last 5 minutes) + local apt_cache_file="/var/cache/apt-update-timestamp" + local current_time=$(date +%s) + local last_update=0 + + if [[ -f "$apt_cache_file" ]]; then + last_update=$(cat "$apt_cache_file" 2>/dev/null || echo 0) + fi + + if ((current_time - last_update > 300)); then + # Ensure orphaned sources are cleaned before updating + cleanup_orphaned_sources 2>/dev/null || true + + if ! $STD apt update; then + ensure_apt_working || return 1 + fi + echo "$current_time" >"$apt_cache_file" + fi + + $STD apt install -y "${missing[@]}" || { + msg_error "Failed to install dependencies: ${missing[*]}" + return 1 + } fi - done - - if [[ ${#missing[@]} -gt 0 ]]; then - # Only run apt update if not done recently (within last 5 minutes) - local apt_cache_file="/var/cache/apt-update-timestamp" - local current_time=$(date +%s) - local last_update=0 - - if [[ -f "$apt_cache_file" ]]; then - last_update=$(cat "$apt_cache_file" 2>/dev/null || echo 0) - fi - - if ((current_time - last_update > 300)); then - # Ensure orphaned sources are cleaned before updating - cleanup_orphaned_sources 2>/dev/null || true - - if ! $STD apt update; then - ensure_apt_working || return 1 - fi - echo "$current_time" >"$apt_cache_file" - fi - - $STD apt install -y "${missing[@]}" || { - msg_error "Failed to install dependencies: ${missing[*]}" - return 1 - } - fi } # ------------------------------------------------------------------------------ # Smart version comparison # ------------------------------------------------------------------------------ version_gt() { - test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1" + test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1" } # ------------------------------------------------------------------------------ # Get system architecture (normalized) # ------------------------------------------------------------------------------ get_system_arch() { - local arch_type="${1:-dpkg}" # dpkg, uname, or both - local arch + local arch_type="${1:-dpkg}" # dpkg, uname, or both + local arch - case "$arch_type" in - dpkg) - arch=$(dpkg --print-architecture 2>/dev/null) - ;; - uname) - arch=$(uname -m) - [[ "$arch" == "x86_64" ]] && arch="amd64" - [[ "$arch" == "aarch64" ]] && arch="arm64" - ;; - both | *) - arch=$(dpkg --print-architecture 2>/dev/null || uname -m) - [[ "$arch" == "x86_64" ]] && arch="amd64" - [[ "$arch" == "aarch64" ]] && arch="arm64" - ;; - esac + case "$arch_type" in + dpkg) + arch=$(dpkg --print-architecture 2>/dev/null) + ;; + uname) + arch=$(uname -m) + [[ "$arch" == "x86_64" ]] && arch="amd64" + [[ "$arch" == "aarch64" ]] && arch="arm64" + ;; + both | *) + arch=$(dpkg --print-architecture 2>/dev/null || uname -m) + [[ "$arch" == "x86_64" ]] && arch="amd64" + [[ "$arch" == "aarch64" ]] && arch="arm64" + ;; + esac - echo "$arch" + echo "$arch" } # ------------------------------------------------------------------------------ # Create temporary directory with automatic cleanup # ------------------------------------------------------------------------------ create_temp_dir() { - local tmp_dir=$(mktemp -d) - # Set trap to cleanup on EXIT, ERR, INT, TERM - trap "rm -rf '$tmp_dir'" EXIT ERR INT TERM - echo "$tmp_dir" + local tmp_dir=$(mktemp -d) + # Set trap to cleanup on EXIT, ERR, INT, TERM + trap "rm -rf '$tmp_dir'" EXIT ERR INT TERM + echo "$tmp_dir" } # ------------------------------------------------------------------------------ # Check if package is installed (faster than dpkg -l | grep) # ------------------------------------------------------------------------------ is_package_installed() { - local package="$1" - dpkg-query -W -f='${Status}' "$package" 2>/dev/null | grep -q "^install ok installed$" + local package="$1" + dpkg-query -W -f='${Status}' "$package" 2>/dev/null | grep -q "^install ok installed$" } # ------------------------------------------------------------------------------ # GitHub API call with authentication and rate limit handling # ------------------------------------------------------------------------------ github_api_call() { - local url="$1" - local output_file="${2:-/dev/stdout}" - local max_retries=3 - local retry_delay=2 + local url="$1" + local output_file="${2:-/dev/stdout}" + local max_retries=3 + local retry_delay=2 - local header_args=() - [[ -n "${GITHUB_TOKEN:-}" ]] && header_args=(-H "Authorization: Bearer $GITHUB_TOKEN") + local header_args=() + [[ -n "${GITHUB_TOKEN:-}" ]] && header_args=(-H "Authorization: Bearer $GITHUB_TOKEN") - for attempt in $(seq 1 $max_retries); do - local http_code - http_code=$(curl -fsSL -w "%{http_code}" -o "$output_file" \ - -H "Accept: application/vnd.github+json" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ - "${header_args[@]}" \ - "$url" 2>/dev/null || echo "000") + for attempt in $(seq 1 $max_retries); do + local http_code + http_code=$(curl -fsSL -w "%{http_code}" -o "$output_file" \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "${header_args[@]}" \ + "$url" 2>/dev/null || echo "000") - case "$http_code" in - 200) - return 0 - ;; - 403) - # Rate limit - check if we can retry - if [[ $attempt -lt $max_retries ]]; then - msg_warn "GitHub API rate limit, waiting ${retry_delay}s... (attempt $attempt/$max_retries)" - sleep "$retry_delay" - retry_delay=$((retry_delay * 2)) - continue - fi - msg_error "GitHub API rate limit exceeded. Set GITHUB_TOKEN to increase limits." - return 1 - ;; - 404) - msg_error "GitHub API endpoint not found: $url" - return 1 - ;; - *) - if [[ $attempt -lt $max_retries ]]; then - sleep "$retry_delay" - continue - fi - msg_error "GitHub API call failed with HTTP $http_code" - return 1 - ;; - esac - done + case "$http_code" in + 200) + return 0 + ;; + 403) + # Rate limit - check if we can retry + if [[ $attempt -lt $max_retries ]]; then + msg_warn "GitHub API rate limit, waiting ${retry_delay}s... (attempt $attempt/$max_retries)" + sleep "$retry_delay" + retry_delay=$((retry_delay * 2)) + continue + fi + msg_error "GitHub API rate limit exceeded. Set GITHUB_TOKEN to increase limits." + return 1 + ;; + 404) + msg_error "GitHub API endpoint not found: $url" + return 1 + ;; + *) + if [[ $attempt -lt $max_retries ]]; then + sleep "$retry_delay" + continue + fi + msg_error "GitHub API call failed with HTTP $http_code" + return 1 + ;; + esac + done - return 1 + return 1 } should_upgrade() { - local current="$1" - local target="$2" + local current="$1" + local target="$2" - [[ -z "$current" ]] && return 0 - version_gt "$target" "$current" && return 0 - return 1 + [[ -z "$current" ]] && return 0 + version_gt "$target" "$current" && return 0 + return 1 } # ------------------------------------------------------------------------------ # Get OS information (cached for performance) # ------------------------------------------------------------------------------ get_os_info() { - local field="${1:-all}" # id, codename, version, version_id, all + local field="${1:-all}" # id, codename, version, version_id, all - # Cache OS info to avoid repeated file reads - if [[ -z "${_OS_ID:-}" ]]; then - export _OS_ID=$(awk -F= '/^ID=/{gsub(/"/,"",$2); print $2}' /etc/os-release) - export _OS_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{gsub(/"/,"",$2); print $2}' /etc/os-release) - export _OS_VERSION=$(awk -F= '/^VERSION_ID=/{gsub(/"/,"",$2); print $2}' /etc/os-release) - export _OS_VERSION_FULL=$(awk -F= '/^VERSION=/{gsub(/"/,"",$2); print $2}' /etc/os-release) - fi + # Cache OS info to avoid repeated file reads + if [[ -z "${_OS_ID:-}" ]]; then + export _OS_ID=$(awk -F= '/^ID=/{gsub(/"/,"",$2); print $2}' /etc/os-release) + export _OS_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{gsub(/"/,"",$2); print $2}' /etc/os-release) + export _OS_VERSION=$(awk -F= '/^VERSION_ID=/{gsub(/"/,"",$2); print $2}' /etc/os-release) + export _OS_VERSION_FULL=$(awk -F= '/^VERSION=/{gsub(/"/,"",$2); print $2}' /etc/os-release) + fi - case "$field" in - id) echo "$_OS_ID" ;; - codename) echo "$_OS_CODENAME" ;; - version) echo "$_OS_VERSION" ;; - version_id) echo "$_OS_VERSION" ;; - version_full) echo "$_OS_VERSION_FULL" ;; - all) echo "ID=$_OS_ID CODENAME=$_OS_CODENAME VERSION=$_OS_VERSION" ;; - *) echo "$_OS_ID" ;; - esac + case "$field" in + id) echo "$_OS_ID" ;; + codename) echo "$_OS_CODENAME" ;; + version) echo "$_OS_VERSION" ;; + version_id) echo "$_OS_VERSION" ;; + version_full) echo "$_OS_VERSION_FULL" ;; + all) echo "ID=$_OS_ID CODENAME=$_OS_CODENAME VERSION=$_OS_VERSION" ;; + *) echo "$_OS_ID" ;; + esac } # ------------------------------------------------------------------------------ # Check if running on specific OS # ------------------------------------------------------------------------------ is_debian() { - [[ "$(get_os_info id)" == "debian" ]] + [[ "$(get_os_info id)" == "debian" ]] } is_ubuntu() { - [[ "$(get_os_info id)" == "ubuntu" ]] + [[ "$(get_os_info id)" == "ubuntu" ]] } is_alpine() { - [[ "$(get_os_info id)" == "alpine" ]] + [[ "$(get_os_info id)" == "alpine" ]] } # ------------------------------------------------------------------------------ # Get Debian/Ubuntu major version # ------------------------------------------------------------------------------ get_os_version_major() { - local version=$(get_os_info version) - echo "${version%%.*}" + local version=$(get_os_info version) + echo "${version%%.*}" } # ------------------------------------------------------------------------------ # Download file with retry logic and progress # ------------------------------------------------------------------------------ download_file() { - local url="$1" - local output="$2" - local max_retries="${3:-3}" - local show_progress="${4:-false}" + local url="$1" + local output="$2" + local max_retries="${3:-3}" + local show_progress="${4:-false}" - local curl_opts=(-fsSL) - [[ "$show_progress" == "true" ]] && curl_opts=(-fL#) + local curl_opts=(-fsSL) + [[ "$show_progress" == "true" ]] && curl_opts=(-fL#) - for attempt in $(seq 1 $max_retries); do - if curl "${curl_opts[@]}" -o "$output" "$url"; then - return 0 - fi + for attempt in $(seq 1 $max_retries); do + if curl "${curl_opts[@]}" -o "$output" "$url"; then + return 0 + fi - if [[ $attempt -lt $max_retries ]]; then - msg_warn "Download failed, retrying... (attempt $attempt/$max_retries)" - sleep 2 - fi - done + if [[ $attempt -lt $max_retries ]]; then + msg_warn "Download failed, retrying... (attempt $attempt/$max_retries)" + sleep 2 + fi + done - msg_error "Failed to download: $url" - return 1 + msg_error "Failed to download: $url" + return 1 } # ------------------------------------------------------------------------------ # Get fallback suite for repository (comprehensive mapping) # ------------------------------------------------------------------------------ get_fallback_suite() { - local distro_id="$1" - local distro_codename="$2" - local repo_base_url="$3" + local distro_id="$1" + local distro_codename="$2" + local repo_base_url="$3" - # Check if current codename works - if verify_repo_available "$repo_base_url" "$distro_codename"; then - echo "$distro_codename" - return 0 - fi + # Check if current codename works + if verify_repo_available "$repo_base_url" "$distro_codename"; then + echo "$distro_codename" + return 0 + fi - # Comprehensive fallback mappings - case "$distro_id" in - debian) - case "$distro_codename" in - # Debian 13 (Trixie) → Debian 12 (Bookworm) - trixie | forky | sid) - echo "bookworm" - ;; - # Debian 12 (Bookworm) stays - bookworm) - echo "bookworm" - ;; - # Debian 11 (Bullseye) stays - bullseye) - echo "bullseye" - ;; - # Unknown → latest stable + # Comprehensive fallback mappings + case "$distro_id" in + debian) + case "$distro_codename" in + # Debian 13 (Trixie) → Debian 12 (Bookworm) + trixie | forky | sid) + echo "bookworm" + ;; + # Debian 12 (Bookworm) stays + bookworm) + echo "bookworm" + ;; + # Debian 11 (Bullseye) stays + bullseye) + echo "bullseye" + ;; + # Unknown → latest stable + *) + echo "bookworm" + ;; + esac + ;; + ubuntu) + case "$distro_codename" in + # Ubuntu 24.10 (Oracular) → 24.04 LTS (Noble) + oracular | plucky) + echo "noble" + ;; + # Ubuntu 24.04 LTS (Noble) stays + noble) + echo "noble" + ;; + # Ubuntu 23.10 (Mantic) → 22.04 LTS (Jammy) + mantic | lunar) + echo "jammy" + ;; + # Ubuntu 22.04 LTS (Jammy) stays + jammy) + echo "jammy" + ;; + # Ubuntu 20.04 LTS (Focal) stays + focal) + echo "focal" + ;; + # Unknown → latest LTS + *) + echo "jammy" + ;; + esac + ;; *) - echo "bookworm" - ;; + echo "$distro_codename" + ;; esac - ;; - ubuntu) - case "$distro_codename" in - # Ubuntu 24.10 (Oracular) → 24.04 LTS (Noble) - oracular | plucky) - echo "noble" - ;; - # Ubuntu 24.04 LTS (Noble) stays - noble) - echo "noble" - ;; - # Ubuntu 23.10 (Mantic) → 22.04 LTS (Jammy) - mantic | lunar) - echo "jammy" - ;; - # Ubuntu 22.04 LTS (Jammy) stays - jammy) - echo "jammy" - ;; - # Ubuntu 20.04 LTS (Focal) stays - focal) - echo "focal" - ;; - # Unknown → latest LTS - *) - echo "jammy" - ;; - esac - ;; - *) - echo "$distro_codename" - ;; - esac } # ------------------------------------------------------------------------------ # Verify package source and version # ------------------------------------------------------------------------------ verify_package_source() { - local package="$1" - local expected_version="$2" + local package="$1" + local expected_version="$2" - if apt-cache policy "$package" 2>/dev/null | grep -q "$expected_version"; then - return 0 - fi - return 1 + if apt-cache policy "$package" 2>/dev/null | grep -q "$expected_version"; then + return 0 + fi + return 1 } # ------------------------------------------------------------------------------ # Check if running on LTS version # ------------------------------------------------------------------------------ is_lts_version() { - local os_id=$(get_os_info id) - local codename=$(get_os_info codename) + local os_id=$(get_os_info id) + local codename=$(get_os_info codename) - if [[ "$os_id" == "ubuntu" ]]; then - case "$codename" in - focal | jammy | noble) return 0 ;; # 20.04, 22.04, 24.04 - *) return 1 ;; - esac - elif [[ "$os_id" == "debian" ]]; then - # Debian releases are all "stable" - case "$codename" in - bullseye | bookworm | trixie) return 0 ;; - *) return 1 ;; - esac - fi + if [[ "$os_id" == "ubuntu" ]]; then + case "$codename" in + focal | jammy | noble) return 0 ;; # 20.04, 22.04, 24.04 + *) return 1 ;; + esac + elif [[ "$os_id" == "debian" ]]; then + # Debian releases are all "stable" + case "$codename" in + bullseye | bookworm | trixie) return 0 ;; + *) return 1 ;; + esac + fi - return 1 + return 1 } # ------------------------------------------------------------------------------ # Get optimal number of parallel jobs (cached) # ------------------------------------------------------------------------------ get_parallel_jobs() { - if [[ -z "${_PARALLEL_JOBS:-}" ]]; then - local cpu_count=$(nproc 2>/dev/null || echo 1) - local mem_gb=$(free -g | awk '/^Mem:/{print $2}') + if [[ -z "${_PARALLEL_JOBS:-}" ]]; then + local cpu_count=$(nproc 2>/dev/null || echo 1) + local mem_gb=$(free -g | awk '/^Mem:/{print $2}') - # Limit by available memory (assume 1GB per job for compilation) - local max_by_mem=$((mem_gb > 0 ? mem_gb : 1)) - local max_jobs=$((cpu_count < max_by_mem ? cpu_count : max_by_mem)) + # Limit by available memory (assume 1GB per job for compilation) + local max_by_mem=$((mem_gb > 0 ? mem_gb : 1)) + local max_jobs=$((cpu_count < max_by_mem ? cpu_count : max_by_mem)) - # At least 1, at most cpu_count - export _PARALLEL_JOBS=$((max_jobs > 0 ? max_jobs : 1)) - fi - echo "$_PARALLEL_JOBS" + # At least 1, at most cpu_count + export _PARALLEL_JOBS=$((max_jobs > 0 ? max_jobs : 1)) + fi + echo "$_PARALLEL_JOBS" } # ------------------------------------------------------------------------------ # Get default PHP version for OS # ------------------------------------------------------------------------------ get_default_php_version() { - local os_id=$(get_os_info id) - local os_version=$(get_os_version_major) + local os_id=$(get_os_info id) + local os_version=$(get_os_version_major) - case "$os_id" in - debian) - case "$os_version" in - 13) echo "8.3" ;; # Debian 13 (Trixie) - 12) echo "8.2" ;; # Debian 12 (Bookworm) - 11) echo "7.4" ;; # Debian 11 (Bullseye) - *) echo "8.2" ;; + case "$os_id" in + debian) + case "$os_version" in + 13) echo "8.3" ;; # Debian 13 (Trixie) + 12) echo "8.2" ;; # Debian 12 (Bookworm) + 11) echo "7.4" ;; # Debian 11 (Bullseye) + *) echo "8.2" ;; + esac + ;; + ubuntu) + case "$os_version" in + 24) echo "8.3" ;; # Ubuntu 24.04 LTS (Noble) + 22) echo "8.1" ;; # Ubuntu 22.04 LTS (Jammy) + 20) echo "7.4" ;; # Ubuntu 20.04 LTS (Focal) + *) echo "8.1" ;; + esac + ;; + *) + echo "8.2" + ;; esac - ;; - ubuntu) - case "$os_version" in - 24) echo "8.3" ;; # Ubuntu 24.04 LTS (Noble) - 22) echo "8.1" ;; # Ubuntu 22.04 LTS (Jammy) - 20) echo "7.4" ;; # Ubuntu 20.04 LTS (Focal) - *) echo "8.1" ;; - esac - ;; - *) - echo "8.2" - ;; - esac } # ------------------------------------------------------------------------------ # Get default Python version for OS # ------------------------------------------------------------------------------ get_default_python_version() { - local os_id=$(get_os_info id) - local os_version=$(get_os_version_major) + local os_id=$(get_os_info id) + local os_version=$(get_os_version_major) - case "$os_id" in - debian) - case "$os_version" in - 13) echo "3.12" ;; # Debian 13 (Trixie) - 12) echo "3.11" ;; # Debian 12 (Bookworm) - 11) echo "3.9" ;; # Debian 11 (Bullseye) - *) echo "3.11" ;; + case "$os_id" in + debian) + case "$os_version" in + 13) echo "3.12" ;; # Debian 13 (Trixie) + 12) echo "3.11" ;; # Debian 12 (Bookworm) + 11) echo "3.9" ;; # Debian 11 (Bullseye) + *) echo "3.11" ;; + esac + ;; + ubuntu) + case "$os_version" in + 24) echo "3.12" ;; # Ubuntu 24.04 LTS + 22) echo "3.10" ;; # Ubuntu 22.04 LTS + 20) echo "3.8" ;; # Ubuntu 20.04 LTS + *) echo "3.10" ;; + esac + ;; + *) + echo "3.11" + ;; esac - ;; - ubuntu) - case "$os_version" in - 24) echo "3.12" ;; # Ubuntu 24.04 LTS - 22) echo "3.10" ;; # Ubuntu 22.04 LTS - 20) echo "3.8" ;; # Ubuntu 20.04 LTS - *) echo "3.10" ;; - esac - ;; - *) - echo "3.11" - ;; - esac } # ------------------------------------------------------------------------------ # Get default Node.js LTS version # ------------------------------------------------------------------------------ get_default_nodejs_version() { - # Always return current LTS (as of 2025) - echo "22" + # Always return current LTS (as of 2025) + echo "22" } # ------------------------------------------------------------------------------ # Check if package manager is locked # ------------------------------------------------------------------------------ is_apt_locked() { - if fuser /var/lib/dpkg/lock-frontend &>/dev/null || - fuser /var/lib/apt/lists/lock &>/dev/null || - fuser /var/cache/apt/archives/lock &>/dev/null; then - return 0 - fi - return 1 + if fuser /var/lib/dpkg/lock-frontend &>/dev/null || + fuser /var/lib/apt/lists/lock &>/dev/null || + fuser /var/cache/apt/archives/lock &>/dev/null; then + return 0 + fi + return 1 } # ------------------------------------------------------------------------------ # Wait for apt to be available # ------------------------------------------------------------------------------ wait_for_apt() { - local max_wait="${1:-300}" # 5 minutes default - local waited=0 + local max_wait="${1:-300}" # 5 minutes default + local waited=0 - while is_apt_locked; do - if [[ $waited -ge $max_wait ]]; then - msg_error "Timeout waiting for apt to be available" - return 1 - fi + while is_apt_locked; do + if [[ $waited -ge $max_wait ]]; then + msg_error "Timeout waiting for apt to be available" + return 1 + fi - sleep 5 - waited=$((waited + 5)) - done + sleep 5 + waited=$((waited + 5)) + done - return 0 + return 0 } # ------------------------------------------------------------------------------ # Cleanup old repository files (migration helper) # ------------------------------------------------------------------------------ cleanup_old_repo_files() { - local app="$1" + local app="$1" - # Remove old-style .list files (including backups) - rm -f /etc/apt/sources.list.d/"${app}"*.list - rm -f /etc/apt/sources.list.d/"${app}"*.list.save - rm -f /etc/apt/sources.list.d/"${app}"*.list.distUpgrade - rm -f /etc/apt/sources.list.d/"${app}"*.list.dpkg-* + # Remove old-style .list files (including backups) + rm -f /etc/apt/sources.list.d/"${app}"*.list + rm -f /etc/apt/sources.list.d/"${app}"*.list.save + rm -f /etc/apt/sources.list.d/"${app}"*.list.distUpgrade + rm -f /etc/apt/sources.list.d/"${app}"*.list.dpkg-* - # Remove old GPG keys from trusted.gpg.d - rm -f /etc/apt/trusted.gpg.d/"${app}"*.gpg + # Remove old GPG keys from trusted.gpg.d + rm -f /etc/apt/trusted.gpg.d/"${app}"*.gpg - # Remove keyrings from /etc/apt/keyrings - rm -f /etc/apt/keyrings/"${app}"*.gpg + # Remove keyrings from /etc/apt/keyrings + rm -f /etc/apt/keyrings/"${app}"*.gpg - # Remove ALL .sources files for this app (including the main one) - # This ensures no orphaned .sources files reference deleted keyrings - rm -f /etc/apt/sources.list.d/"${app}"*.sources + # Remove ALL .sources files for this app (including the main one) + # This ensures no orphaned .sources files reference deleted keyrings + rm -f /etc/apt/sources.list.d/"${app}"*.sources } # ------------------------------------------------------------------------------ @@ -885,34 +885,34 @@ cleanup_old_repo_files() { # Call this at the start of any setup function to ensure APT is in a clean state # ------------------------------------------------------------------------------ cleanup_orphaned_sources() { - local sources_dir="/etc/apt/sources.list.d" - local keyrings_dir="/etc/apt/keyrings" + local sources_dir="/etc/apt/sources.list.d" + local keyrings_dir="/etc/apt/keyrings" - [[ ! -d "$sources_dir" ]] && return 0 + [[ ! -d "$sources_dir" ]] && return 0 - while IFS= read -r -d '' sources_file; do - local basename_file - basename_file=$(basename "$sources_file") + while IFS= read -r -d '' sources_file; do + local basename_file + basename_file=$(basename "$sources_file") - # NEVER remove debian.sources - this is the standard Debian repository - if [[ "$basename_file" == "debian.sources" ]]; then - continue + # NEVER remove debian.sources - this is the standard Debian repository + if [[ "$basename_file" == "debian.sources" ]]; then + continue + fi + + # Extract Signed-By path from .sources file + local keyring_path + keyring_path=$(grep -E '^Signed-By:' "$sources_file" 2>/dev/null | awk '{print $2}') + + # If keyring doesn't exist, remove the .sources file + if [[ -n "$keyring_path" ]] && [[ ! -f "$keyring_path" ]]; then + rm -f "$sources_file" + fi + done < <(find "$sources_dir" -name "*.sources" -print0 2>/dev/null) + + # Also check for broken symlinks in keyrings directory + if [[ -d "$keyrings_dir" ]]; then + find "$keyrings_dir" -type l ! -exec test -e {} \; -delete 2>/dev/null || true fi - - # Extract Signed-By path from .sources file - local keyring_path - keyring_path=$(grep -E '^Signed-By:' "$sources_file" 2>/dev/null | awk '{print $2}') - - # If keyring doesn't exist, remove the .sources file - if [[ -n "$keyring_path" ]] && [[ ! -f "$keyring_path" ]]; then - rm -f "$sources_file" - fi - done < <(find "$sources_dir" -name "*.sources" -print0 2>/dev/null) - - # Also check for broken symlinks in keyrings directory - if [[ -d "$keyrings_dir" ]]; then - find "$keyrings_dir" -type l ! -exec test -e {} \; -delete 2>/dev/null || true - fi } # ------------------------------------------------------------------------------ @@ -920,23 +920,23 @@ cleanup_orphaned_sources() { # This should be called at the start of any setup function # ------------------------------------------------------------------------------ ensure_apt_working() { - # Clean up orphaned sources first - cleanup_orphaned_sources - - # Try to update package lists - if ! apt update -qq 2>/dev/null; then - # More aggressive cleanup - rm -f /etc/apt/sources.list.d/*.sources 2>/dev/null || true + # Clean up orphaned sources first cleanup_orphaned_sources - # Try again + # Try to update package lists if ! apt update -qq 2>/dev/null; then - msg_error "Cannot update package lists - APT is critically broken" - return 1 - fi - fi + # More aggressive cleanup + rm -f /etc/apt/sources.list.d/*.sources 2>/dev/null || true + cleanup_orphaned_sources - return 0 + # Try again + if ! apt update -qq 2>/dev/null; then + msg_error "Cannot update package lists - APT is critically broken" + return 1 + fi + fi + + return 0 } # ------------------------------------------------------------------------------ @@ -944,39 +944,39 @@ ensure_apt_working() { # Validates all parameters and fails safely if any are empty # ------------------------------------------------------------------------------ setup_deb822_repo() { - local name="$1" - local gpg_url="$2" - local repo_url="$3" - local suite="$4" - local component="${5:-main}" - local architectures="${6:-amd64 arm64}" + local name="$1" + local gpg_url="$2" + local repo_url="$3" + local suite="$4" + local component="${5:-main}" + local architectures="${6:-amd64 arm64}" - # Validate required parameters - if [[ -z "$name" || -z "$gpg_url" || -z "$repo_url" || -z "$suite" ]]; then - msg_error "setup_deb822_repo: missing required parameters (name=$name, gpg=$gpg_url, repo=$repo_url, suite=$suite)" - return 1 - fi + # Validate required parameters + if [[ -z "$name" || -z "$gpg_url" || -z "$repo_url" || -z "$suite" ]]; then + msg_error "setup_deb822_repo: missing required parameters (name=$name, gpg=$gpg_url, repo=$repo_url, suite=$suite)" + return 1 + fi - # Cleanup old configs for this app - cleanup_old_repo_files "$name" + # Cleanup old configs for this app + cleanup_old_repo_files "$name" - # Cleanup any orphaned .sources files from other apps - cleanup_orphaned_sources + # Cleanup any orphaned .sources files from other apps + cleanup_orphaned_sources - # Ensure keyring directory exists - mkdir -p /etc/apt/keyrings || { - msg_error "Failed to create /etc/apt/keyrings directory" - return 1 - } + # Ensure keyring directory exists + mkdir -p /etc/apt/keyrings || { + msg_error "Failed to create /etc/apt/keyrings directory" + return 1 + } - # Download GPG key (with --yes to avoid interactive prompts) - curl -fsSL "$gpg_url" | gpg --dearmor --yes -o "/etc/apt/keyrings/${name}.gpg" 2>/dev/null || { - msg_error "Failed to download or import GPG key for ${name} from $gpg_url" - return 1 - } + # Download GPG key (with --yes to avoid interactive prompts) + curl -fsSL "$gpg_url" | gpg --dearmor --yes -o "/etc/apt/keyrings/${name}.gpg" 2>/dev/null || { + msg_error "Failed to download or import GPG key for ${name} from $gpg_url" + return 1 + } - # Create deb822 sources file - cat </etc/apt/sources.list.d/${name}.sources + # Create deb822 sources file + cat </etc/apt/sources.list.d/${name}.sources Types: deb URIs: $repo_url Suites: $suite @@ -985,175 +985,175 @@ Architectures: $architectures Signed-By: /etc/apt/keyrings/${name}.gpg EOF - # Use cached apt update - local apt_cache_file="/var/cache/apt-update-timestamp" - local current_time=$(date +%s) - local last_update=0 + # Use cached apt update + local apt_cache_file="/var/cache/apt-update-timestamp" + local current_time=$(date +%s) + local last_update=0 - if [[ -f "$apt_cache_file" ]]; then - last_update=$(cat "$apt_cache_file" 2>/dev/null || echo 0) - fi + if [[ -f "$apt_cache_file" ]]; then + last_update=$(cat "$apt_cache_file" 2>/dev/null || echo 0) + fi - # For repo changes, always update but respect short-term cache (30s) - if ((current_time - last_update > 30)); then - $STD apt update - echo "$current_time" >"$apt_cache_file" - fi + # For repo changes, always update but respect short-term cache (30s) + if ((current_time - last_update > 30)); then + $STD apt update + echo "$current_time" >"$apt_cache_file" + fi } # ------------------------------------------------------------------------------ # Package version hold/unhold helpers # ------------------------------------------------------------------------------ hold_package_version() { - local package="$1" - $STD apt-mark hold "$package" + local package="$1" + $STD apt-mark hold "$package" } unhold_package_version() { - local package="$1" - $STD apt-mark unhold "$package" + local package="$1" + $STD apt-mark unhold "$package" } # ------------------------------------------------------------------------------ # Safe service restart with verification # ------------------------------------------------------------------------------ safe_service_restart() { - local service="$1" + local service="$1" - if systemctl is-active --quiet "$service"; then - $STD systemctl restart "$service" - else - $STD systemctl start "$service" - fi + if systemctl is-active --quiet "$service"; then + $STD systemctl restart "$service" + else + $STD systemctl start "$service" + fi - if ! systemctl is-active --quiet "$service"; then - msg_error "Failed to start $service" - systemctl status "$service" --no-pager - return 1 - fi - return 0 + if ! systemctl is-active --quiet "$service"; then + msg_error "Failed to start $service" + systemctl status "$service" --no-pager + return 1 + fi + return 0 } # ------------------------------------------------------------------------------ # Enable and start service (with error handling) # ------------------------------------------------------------------------------ enable_and_start_service() { - local service="$1" + local service="$1" - if ! systemctl enable "$service" &>/dev/null; then - return 1 - fi + if ! systemctl enable "$service" &>/dev/null; then + return 1 + fi - if ! systemctl start "$service" &>/dev/null; then - msg_error "Failed to start $service" - systemctl status "$service" --no-pager - return 1 - fi + if ! systemctl start "$service" &>/dev/null; then + msg_error "Failed to start $service" + systemctl status "$service" --no-pager + return 1 + fi - return 0 + return 0 } # ------------------------------------------------------------------------------ # Check if service is enabled # ------------------------------------------------------------------------------ is_service_enabled() { - local service="$1" - systemctl is-enabled --quiet "$service" 2>/dev/null + local service="$1" + systemctl is-enabled --quiet "$service" 2>/dev/null } # ------------------------------------------------------------------------------ # Check if service is running # ------------------------------------------------------------------------------ is_service_running() { - local service="$1" - systemctl is-active --quiet "$service" 2>/dev/null + local service="$1" + systemctl is-active --quiet "$service" 2>/dev/null } # ------------------------------------------------------------------------------ # Extract version from JSON (GitHub releases) # ------------------------------------------------------------------------------ extract_version_from_json() { - local json="$1" - local field="${2:-tag_name}" - local strip_v="${3:-true}" + local json="$1" + local field="${2:-tag_name}" + local strip_v="${3:-true}" - ensure_dependencies jq + ensure_dependencies jq - local version - version=$(echo "$json" | jq -r ".${field} // empty") + local version + version=$(echo "$json" | jq -r ".${field} // empty") - if [[ -z "$version" ]]; then - return 1 - fi + if [[ -z "$version" ]]; then + return 1 + fi - if [[ "$strip_v" == "true" ]]; then - echo "${version#v}" - else - echo "$version" - fi + if [[ "$strip_v" == "true" ]]; then + echo "${version#v}" + else + echo "$version" + fi } # ------------------------------------------------------------------------------ # Get latest GitHub release version # ------------------------------------------------------------------------------ get_latest_github_release() { - local repo="$1" - local strip_v="${2:-true}" - local temp_file=$(mktemp) + local repo="$1" + local strip_v="${2:-true}" + local temp_file=$(mktemp) - if ! github_api_call "https://api.github.com/repos/${repo}/releases/latest" "$temp_file"; then + if ! github_api_call "https://api.github.com/repos/${repo}/releases/latest" "$temp_file"; then + rm -f "$temp_file" + return 1 + fi + + local version + version=$(extract_version_from_json "$(cat "$temp_file")" "tag_name" "$strip_v") rm -f "$temp_file" - return 1 - fi - local version - version=$(extract_version_from_json "$(cat "$temp_file")" "tag_name" "$strip_v") - rm -f "$temp_file" + if [[ -z "$version" ]]; then + return 1 + fi - if [[ -z "$version" ]]; then - return 1 - fi - - echo "$version" + echo "$version" } # ------------------------------------------------------------------------------ # Debug logging (only if DEBUG=1) # ------------------------------------------------------------------------------ debug_log() { - [[ "${DEBUG:-0}" == "1" ]] && echo "[DEBUG] $*" >&2 + [[ "${DEBUG:-0}" == "1" ]] && echo "[DEBUG] $*" >&2 } # ------------------------------------------------------------------------------ # Performance timing helper # ------------------------------------------------------------------------------ start_timer() { - echo $(date +%s) + echo $(date +%s) } end_timer() { - local start_time="$1" - local label="${2:-Operation}" - local end_time=$(date +%s) - local duration=$((end_time - start_time)) + local start_time="$1" + local label="${2:-Operation}" + local end_time=$(date +%s) + local duration=$((end_time - start_time)) } # ------------------------------------------------------------------------------ # GPG key fingerprint verification # ------------------------------------------------------------------------------ verify_gpg_fingerprint() { - local key_file="$1" - local expected_fingerprint="$2" + local key_file="$1" + local expected_fingerprint="$2" - local actual_fingerprint - actual_fingerprint=$(gpg --show-keys --with-fingerprint --with-colons "$key_file" 2>&1 | grep -m1 '^fpr:' | cut -d: -f10) + local actual_fingerprint + actual_fingerprint=$(gpg --show-keys --with-fingerprint --with-colons "$key_file" 2>&1 | grep -m1 '^fpr:' | cut -d: -f10) - if [[ "$actual_fingerprint" == "$expected_fingerprint" ]]; then - return 0 - fi + if [[ "$actual_fingerprint" == "$expected_fingerprint" ]]; then + return 0 + fi - msg_error "GPG fingerprint mismatch! Expected: $expected_fingerprint, Got: $actual_fingerprint" - return 1 + msg_error "GPG fingerprint mismatch! Expected: $expected_fingerprint, Got: $actual_fingerprint" + return 1 } # ============================================================================== @@ -1181,97 +1181,97 @@ verify_gpg_fingerprint() { # - Does not support pre-releases # ------------------------------------------------------------------------------ check_for_gh_release() { - local app="$1" - local source="$2" - local pinned_version_in="${3:-}" # optional - local app_lc="${app,,}" - local current_file="$HOME/.${app_lc}" + local app="$1" + local source="$2" + local pinned_version_in="${3:-}" # optional + local app_lc="${app,,}" + local current_file="$HOME/.${app_lc}" - msg_info "Checking for update: ${app}" + msg_info "Checking for update: ${app}" - # DNS check - if ! getent hosts api.github.com >/dev/null 2>&1; then - msg_error "Network error: cannot resolve api.github.com" - return 1 - fi - - ensure_dependencies jq - - # Fetch releases and exclude drafts/prereleases - local releases_json - releases_json=$(curl -fsSL --max-time 20 \ - -H 'Accept: application/vnd.github+json' \ - -H 'X-GitHub-Api-Version: 2022-11-28' \ - "https://api.github.com/repos/${source}/releases") || { - msg_error "Unable to fetch releases for ${app}" - return 1 - } - - mapfile -t raw_tags < <(jq -r '.[] | select(.draft==false and .prerelease==false) | .tag_name' <<<"$releases_json") - if ((${#raw_tags[@]} == 0)); then - msg_error "No stable releases found for ${app}" - return 1 - fi - - local clean_tags=() - for t in "${raw_tags[@]}"; do - clean_tags+=("${t#v}") - done - - local latest_raw="${raw_tags[0]}" - local latest_clean="${clean_tags[0]}" - - # current installed (stored without v) - local current="" - if [[ -f "$current_file" ]]; then - current="$(<"$current_file")" - else - # Migration: search for any /opt/*_version.txt - local legacy_files - mapfile -t legacy_files < <(find /opt -maxdepth 1 -type f -name "*_version.txt" 2>/dev/null) - if ((${#legacy_files[@]} == 1)); then - current="$(<"${legacy_files[0]}")" - echo "${current#v}" >"$current_file" - rm -f "${legacy_files[0]}" + # DNS check + if ! getent hosts api.github.com >/dev/null 2>&1; then + msg_error "Network error: cannot resolve api.github.com" + return 1 fi - fi - current="${current#v}" - # Pinned version handling - if [[ -n "$pinned_version_in" ]]; then - local pin_clean="${pinned_version_in#v}" - local match_raw="" - for i in "${!clean_tags[@]}"; do - if [[ "${clean_tags[$i]}" == "$pin_clean" ]]; then - match_raw="${raw_tags[$i]}" - break - fi + ensure_dependencies jq + + # Fetch releases and exclude drafts/prereleases + local releases_json + releases_json=$(curl -fsSL --max-time 20 \ + -H 'Accept: application/vnd.github+json' \ + -H 'X-GitHub-Api-Version: 2022-11-28' \ + "https://api.github.com/repos/${source}/releases") || { + msg_error "Unable to fetch releases for ${app}" + return 1 + } + + mapfile -t raw_tags < <(jq -r '.[] | select(.draft==false and .prerelease==false) | .tag_name' <<<"$releases_json") + if ((${#raw_tags[@]} == 0)); then + msg_error "No stable releases found for ${app}" + return 1 + fi + + local clean_tags=() + for t in "${raw_tags[@]}"; do + clean_tags+=("${t#v}") done - if [[ -z "$match_raw" ]]; then - msg_error "Pinned version ${pinned_version_in} not found upstream" - return 1 + local latest_raw="${raw_tags[0]}" + local latest_clean="${clean_tags[0]}" + + # current installed (stored without v) + local current="" + if [[ -f "$current_file" ]]; then + current="$(<"$current_file")" + else + # Migration: search for any /opt/*_version.txt + local legacy_files + mapfile -t legacy_files < <(find /opt -maxdepth 1 -type f -name "*_version.txt" 2>/dev/null) + if ((${#legacy_files[@]} == 1)); then + current="$(<"${legacy_files[0]}")" + echo "${current#v}" >"$current_file" + rm -f "${legacy_files[0]}" + fi + fi + current="${current#v}" + + # Pinned version handling + if [[ -n "$pinned_version_in" ]]; then + local pin_clean="${pinned_version_in#v}" + local match_raw="" + for i in "${!clean_tags[@]}"; do + if [[ "${clean_tags[$i]}" == "$pin_clean" ]]; then + match_raw="${raw_tags[$i]}" + break + fi + done + + if [[ -z "$match_raw" ]]; then + msg_error "Pinned version ${pinned_version_in} not found upstream" + return 1 + fi + + if [[ "$current" != "$pin_clean" ]]; then + CHECK_UPDATE_RELEASE="$match_raw" + msg_ok "Update available: ${app} ${current:-not installed} → ${pin_clean}" + return 0 + fi + + msg_error "No update available: ${app} is not installed!" + return 1 fi - if [[ "$current" != "$pin_clean" ]]; then - CHECK_UPDATE_RELEASE="$match_raw" - msg_ok "Update available: ${app} ${current:-not installed} → ${pin_clean}" - return 0 + # No pinning → use latest + if [[ -z "$current" || "$current" != "$latest_clean" ]]; then + CHECK_UPDATE_RELEASE="$latest_raw" + msg_ok "Update available: ${app} ${current:-not installed} → ${latest_clean}" + return 0 fi - msg_error "No update available: ${app} is not installed!" + msg_ok "No update available: ${app} (${latest_clean})" return 1 - fi - - # No pinning → use latest - if [[ -z "$current" || "$current" != "$latest_clean" ]]; then - CHECK_UPDATE_RELEASE="$latest_raw" - msg_ok "Update available: ${app} ${current:-not installed} → ${latest_clean}" - return 0 - fi - - msg_ok "No update available: ${app} (${latest_clean})" - return 1 } # ------------------------------------------------------------------------------ @@ -1284,35 +1284,35 @@ check_for_gh_release() { # APP - Application name (default: $APPLICATION variable) # ------------------------------------------------------------------------------ create_self_signed_cert() { - local APP_NAME="${1:-${APPLICATION}}" - local CERT_DIR="/etc/ssl/${APP_NAME}" - local CERT_KEY="${CERT_DIR}/${APP_NAME}.key" - local CERT_CRT="${CERT_DIR}/${APP_NAME}.crt" + local APP_NAME="${1:-${APPLICATION}}" + local CERT_DIR="/etc/ssl/${APP_NAME}" + local CERT_KEY="${CERT_DIR}/${APP_NAME}.key" + local CERT_CRT="${CERT_DIR}/${APP_NAME}.crt" - if [[ -f "$CERT_CRT" && -f "$CERT_KEY" ]]; then - return 0 - fi + if [[ -f "$CERT_CRT" && -f "$CERT_KEY" ]]; then + return 0 + fi - $STD apt update || { - msg_error "Failed to update package list" - return 1 - } - $STD apt install -y openssl || { - msg_error "Failed to install OpenSSL" - return 1 - } + $STD apt update || { + msg_error "Failed to update package list" + return 1 + } + $STD apt install -y openssl || { + msg_error "Failed to install OpenSSL" + return 1 + } - mkdir -p "$CERT_DIR" - $STD openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 \ - -subj "/C=US/ST=State/L=City/O=Organization/CN=${APP_NAME}" \ - -keyout "$CERT_KEY" \ - -out "$CERT_CRT" || { - msg_error "Failed to create self-signed certificate" - return 1 - } + mkdir -p "$CERT_DIR" + $STD openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 \ + -subj "/C=US/ST=State/L=City/O=Organization/CN=${APP_NAME}" \ + -keyout "$CERT_KEY" \ + -out "$CERT_CRT" || { + msg_error "Failed to create self-signed certificate" + return 1 + } - chmod 600 "$CERT_KEY" - chmod 644 "$CERT_CRT" + chmod 600 "$CERT_KEY" + chmod 644 "$CERT_CRT" } # ------------------------------------------------------------------------------ @@ -1324,28 +1324,28 @@ create_self_signed_cert() { # ------------------------------------------------------------------------------ function download_with_progress() { - local url="$1" - local output="$2" - if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi + local url="$1" + local output="$2" + if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi - ensure_dependencies pv - set -o pipefail + ensure_dependencies pv + set -o pipefail - # Content-Length aus HTTP-Header holen - local content_length - content_length=$(curl -fsSLI "$url" | awk '/Content-Length/ {print $2}' | tr -d '\r' || true) + # Content-Length aus HTTP-Header holen + local content_length + content_length=$(curl -fsSLI "$url" | awk '/Content-Length/ {print $2}' | tr -d '\r' || true) - if [[ -z "$content_length" ]]; then - if ! curl -fL# -o "$output" "$url"; then - msg_error "Download failed" - return 1 + if [[ -z "$content_length" ]]; then + if ! curl -fL# -o "$output" "$url"; then + msg_error "Download failed" + return 1 + fi + else + if ! curl -fsSL "$url" | pv -s "$content_length" >"$output"; then + msg_error "Download failed" + return 1 + fi fi - else - if ! curl -fsSL "$url" | pv -s "$content_length" >"$output"; then - msg_error "Download failed" - return 1 - fi - fi } # ------------------------------------------------------------------------------ @@ -1356,12 +1356,12 @@ function download_with_progress() { # ------------------------------------------------------------------------------ function ensure_usr_local_bin_persist() { - local PROFILE_FILE="/etc/profile.d/custom_path.sh" + local PROFILE_FILE="/etc/profile.d/custom_path.sh" - if [[ ! -f "$PROFILE_FILE" ]] && ! command -v pveversion &>/dev/null; then - echo 'export PATH="/usr/local/bin:$PATH"' >"$PROFILE_FILE" - chmod +x "$PROFILE_FILE" - fi + if [[ ! -f "$PROFILE_FILE" ]] && ! command -v pveversion &>/dev/null; then + echo 'export PATH="/usr/local/bin:$PATH"' >"$PROFILE_FILE" + chmod +x "$PROFILE_FILE" + fi } # ------------------------------------------------------------------------------ @@ -1409,315 +1409,315 @@ function ensure_usr_local_bin_persist() { # ------------------------------------------------------------------------------ function fetch_and_deploy_gh_release() { - local app="$1" - local repo="$2" - local mode="${3:-tarball}" # tarball | binary | prebuild | singlefile - local version="${4:-latest}" - local target="${5:-/opt/$app}" - local asset_pattern="${6:-}" + local app="$1" + local repo="$2" + local mode="${3:-tarball}" # tarball | binary | prebuild | singlefile + local version="${4:-latest}" + local target="${5:-/opt/$app}" + local asset_pattern="${6:-}" - local app_lc=$(echo "${app,,}" | tr -d ' ') - local version_file="$HOME/.${app_lc}" + local app_lc=$(echo "${app,,}" | tr -d ' ') + local version_file="$HOME/.${app_lc}" - local api_timeout="--connect-timeout 10 --max-time 60" - local download_timeout="--connect-timeout 15 --max-time 900" + local api_timeout="--connect-timeout 10 --max-time 60" + local download_timeout="--connect-timeout 15 --max-time 900" - local current_version="" - [[ -f "$version_file" ]] && current_version=$(<"$version_file") + local current_version="" + [[ -f "$version_file" ]] && current_version=$(<"$version_file") - ensure_dependencies jq + ensure_dependencies jq - local api_url="https://api.github.com/repos/$repo/releases" - [[ "$version" != "latest" ]] && api_url="$api_url/tags/$version" || api_url="$api_url/latest" - local header=() - [[ -n "${GITHUB_TOKEN:-}" ]] && header=(-H "Authorization: token $GITHUB_TOKEN") + local api_url="https://api.github.com/repos/$repo/releases" + [[ "$version" != "latest" ]] && api_url="$api_url/tags/$version" || api_url="$api_url/latest" + local header=() + [[ -n "${GITHUB_TOKEN:-}" ]] && header=(-H "Authorization: token $GITHUB_TOKEN") - # dns pre check - local gh_host - gh_host=$(awk -F/ '{print $3}' <<<"$api_url") - if ! getent hosts "$gh_host" &>/dev/null; then - msg_error "DNS resolution failed for $gh_host – check /etc/resolv.conf or networking" - return 1 - fi - - local max_retries=3 retry_delay=2 attempt=1 success=false resp http_code - - while ((attempt <= max_retries)); do - resp=$(curl $api_timeout -fsSL -w "%{http_code}" -o /tmp/gh_rel.json "${header[@]}" "$api_url") && success=true && break - sleep "$retry_delay" - ((attempt++)) - done - - if ! $success; then - msg_error "Failed to fetch release metadata from $api_url after $max_retries attempts" - return 1 - fi - - http_code="${resp:(-3)}" - [[ "$http_code" != "200" ]] && { - msg_error "GitHub API returned HTTP $http_code" - return 1 - } - - local json tag_name - json=$(/dev/null; then + msg_error "DNS resolution failed for $gh_host – check /etc/resolv.conf or networking" + return 1 fi - tar --no-same-owner -xzf "$tmpdir/$filename" -C "$tmpdir" || { - msg_error "Failed to extract tarball" - rm -rf "$tmpdir" - return 1 - } - local unpack_dir - unpack_dir=$(find "$tmpdir" -mindepth 1 -maxdepth 1 -type d | head -n1) + local max_retries=3 retry_delay=2 attempt=1 success=false resp http_code - shopt -s dotglob nullglob - cp -r "$unpack_dir"/* "$target/" - shopt -u dotglob nullglob + while ((attempt <= max_retries)); do + resp=$(curl $api_timeout -fsSL -w "%{http_code}" -o /tmp/gh_rel.json "${header[@]}" "$api_url") && success=true && break + sleep "$retry_delay" + ((attempt++)) + done - ### Binary Mode ### - elif [[ "$mode" == "binary" ]]; then - local arch - arch=$(dpkg --print-architecture 2>/dev/null || uname -m) - [[ "$arch" == "x86_64" ]] && arch="amd64" - [[ "$arch" == "aarch64" ]] && arch="arm64" - - local assets url_match="" - assets=$(echo "$json" | jq -r '.assets[].browser_download_url') - - # If explicit filename pattern is provided (param $6), match that first - if [[ -n "$asset_pattern" ]]; then - for u in $assets; do - case "${u##*/}" in - $asset_pattern) - url_match="$u" - break - ;; - esac - done + if ! $success; then + msg_error "Failed to fetch release metadata from $api_url after $max_retries attempts" + return 1 fi - # If no match via explicit pattern, fall back to architecture heuristic - if [[ -z "$url_match" ]]; then - for u in $assets; do - if [[ "$u" =~ ($arch|amd64|x86_64|aarch64|arm64).*\.deb$ ]]; then - url_match="$u" - break + http_code="${resp:(-3)}" + [[ "$http_code" != "200" ]] && { + msg_error "GitHub API returned HTTP $http_code" + return 1 + } + + local json tag_name + json=$(/dev/null || uname -m) + [[ "$arch" == "x86_64" ]] && arch="amd64" + [[ "$arch" == "aarch64" ]] && arch="arm64" - chmod 644 "$tmpdir/$filename" - $STD apt install -y "$tmpdir/$filename" || { - $STD dpkg -i "$tmpdir/$filename" || { - msg_error "Both apt and dpkg installation failed" + local assets url_match="" + assets=$(echo "$json" | jq -r '.assets[].browser_download_url') + + # If explicit filename pattern is provided (param $6), match that first + if [[ -n "$asset_pattern" ]]; then + for u in $assets; do + case "${u##*/}" in + $asset_pattern) + url_match="$u" + break + ;; + esac + done + fi + + # If no match via explicit pattern, fall back to architecture heuristic + if [[ -z "$url_match" ]]; then + for u in $assets; do + if [[ "$u" =~ ($arch|amd64|x86_64|aarch64|arm64).*\.deb$ ]]; then + url_match="$u" + break + fi + done + fi + + # Fallback: any .deb file + if [[ -z "$url_match" ]]; then + for u in $assets; do + [[ "$u" =~ \.deb$ ]] && url_match="$u" && break + done + fi + + if [[ -z "$url_match" ]]; then + msg_error "No suitable .deb asset found for $app" + rm -rf "$tmpdir" + return 1 + fi + + filename="${url_match##*/}" + curl $download_timeout -fsSL -o "$tmpdir/$filename" "$url_match" || { + msg_error "Download failed: $url_match" + rm -rf "$tmpdir" + return 1 + } + + chmod 644 "$tmpdir/$filename" + $STD apt install -y "$tmpdir/$filename" || { + $STD dpkg -i "$tmpdir/$filename" || { + msg_error "Both apt and dpkg installation failed" + rm -rf "$tmpdir" + return 1 + } + } + + ### Prebuild Mode ### + elif [[ "$mode" == "prebuild" ]]; then + local pattern="${6%\"}" + pattern="${pattern#\"}" + [[ -z "$pattern" ]] && { + msg_error "Mode 'prebuild' requires 6th parameter (asset filename pattern)" + rm -rf "$tmpdir" + return 1 + } + + local asset_url="" + for u in $(echo "$json" | jq -r '.assets[].browser_download_url'); do + filename_candidate="${u##*/}" + case "$filename_candidate" in + $pattern) + asset_url="$u" + break + ;; + esac + done + + [[ -z "$asset_url" ]] && { + msg_error "No asset matching '$pattern' found" + rm -rf "$tmpdir" + return 1 + } + + filename="${asset_url##*/}" + curl $download_timeout -fsSL -o "$tmpdir/$filename" "$asset_url" || { + msg_error "Download failed: $asset_url" + rm -rf "$tmpdir" + return 1 + } + + local unpack_tmp + unpack_tmp=$(mktemp -d) + mkdir -p "$target" + if [[ "${CLEAN_INSTALL:-0}" == "1" ]]; then + rm -rf "${target:?}/"* + fi + + if [[ "$filename" == *.zip ]]; then + ensure_dependencies unzip + unzip -q "$tmpdir/$filename" -d "$unpack_tmp" || { + msg_error "Failed to extract ZIP archive" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + } + elif [[ "$filename" == *.tar.* || "$filename" == *.tgz ]]; then + tar --no-same-owner -xf "$tmpdir/$filename" -C "$unpack_tmp" || { + msg_error "Failed to extract TAR archive" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + } + else + msg_error "Unsupported archive format: $filename" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + fi + + local top_dirs + top_dirs=$(find "$unpack_tmp" -mindepth 1 -maxdepth 1 -type d | wc -l) + local top_entries inner_dir + top_entries=$(find "$unpack_tmp" -mindepth 1 -maxdepth 1) + if [[ "$(echo "$top_entries" | wc -l)" -eq 1 && -d "$top_entries" ]]; then + # Strip leading folder + inner_dir="$top_entries" + shopt -s dotglob nullglob + if compgen -G "$inner_dir/*" >/dev/null; then + cp -r "$inner_dir"/* "$target/" || { + msg_error "Failed to copy contents from $inner_dir to $target" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + } + else + msg_error "Inner directory is empty: $inner_dir" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + fi + shopt -u dotglob nullglob + else + # Copy all contents + shopt -s dotglob nullglob + if compgen -G "$unpack_tmp/*" >/dev/null; then + cp -r "$unpack_tmp"/* "$target/" || { + msg_error "Failed to copy contents to $target" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + } + else + msg_error "Unpacked archive is empty" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + fi + shopt -u dotglob nullglob + fi + + ### Singlefile Mode ### + elif [[ "$mode" == "singlefile" ]]; then + local pattern="${6%\"}" + pattern="${pattern#\"}" + [[ -z "$pattern" ]] && { + msg_error "Mode 'singlefile' requires 6th parameter (asset filename pattern)" + rm -rf "$tmpdir" + return 1 + } + + local asset_url="" + for u in $(echo "$json" | jq -r '.assets[].browser_download_url'); do + filename_candidate="${u##*/}" + case "$filename_candidate" in + $pattern) + asset_url="$u" + break + ;; + esac + done + + [[ -z "$asset_url" ]] && { + msg_error "No asset matching '$pattern' found" + rm -rf "$tmpdir" + return 1 + } + + filename="${asset_url##*/}" + mkdir -p "$target" + + local use_filename="${USE_ORIGINAL_FILENAME:-false}" + local target_file="$app" + [[ "$use_filename" == "true" ]] && target_file="$filename" + + curl $download_timeout -fsSL -o "$target/$target_file" "$asset_url" || { + msg_error "Download failed: $asset_url" + rm -rf "$tmpdir" + return 1 + } + + if [[ "$target_file" != *.jar && -f "$target/$target_file" ]]; then + chmod +x "$target/$target_file" + fi + + else + msg_error "Unknown mode: $mode" rm -rf "$tmpdir" return 1 - } - } - - ### Prebuild Mode ### - elif [[ "$mode" == "prebuild" ]]; then - local pattern="${6%\"}" - pattern="${pattern#\"}" - [[ -z "$pattern" ]] && { - msg_error "Mode 'prebuild' requires 6th parameter (asset filename pattern)" - rm -rf "$tmpdir" - return 1 - } - - local asset_url="" - for u in $(echo "$json" | jq -r '.assets[].browser_download_url'); do - filename_candidate="${u##*/}" - case "$filename_candidate" in - $pattern) - asset_url="$u" - break - ;; - esac - done - - [[ -z "$asset_url" ]] && { - msg_error "No asset matching '$pattern' found" - rm -rf "$tmpdir" - return 1 - } - - filename="${asset_url##*/}" - curl $download_timeout -fsSL -o "$tmpdir/$filename" "$asset_url" || { - msg_error "Download failed: $asset_url" - rm -rf "$tmpdir" - return 1 - } - - local unpack_tmp - unpack_tmp=$(mktemp -d) - mkdir -p "$target" - if [[ "${CLEAN_INSTALL:-0}" == "1" ]]; then - rm -rf "${target:?}/"* fi - if [[ "$filename" == *.zip ]]; then - ensure_dependencies unzip - unzip -q "$tmpdir/$filename" -d "$unpack_tmp" || { - msg_error "Failed to extract ZIP archive" - rm -rf "$tmpdir" "$unpack_tmp" - return 1 - } - elif [[ "$filename" == *.tar.* || "$filename" == *.tgz ]]; then - tar --no-same-owner -xf "$tmpdir/$filename" -C "$unpack_tmp" || { - msg_error "Failed to extract TAR archive" - rm -rf "$tmpdir" "$unpack_tmp" - return 1 - } - else - msg_error "Unsupported archive format: $filename" - rm -rf "$tmpdir" "$unpack_tmp" - return 1 - fi - - local top_dirs - top_dirs=$(find "$unpack_tmp" -mindepth 1 -maxdepth 1 -type d | wc -l) - local top_entries inner_dir - top_entries=$(find "$unpack_tmp" -mindepth 1 -maxdepth 1) - if [[ "$(echo "$top_entries" | wc -l)" -eq 1 && -d "$top_entries" ]]; then - # Strip leading folder - inner_dir="$top_entries" - shopt -s dotglob nullglob - if compgen -G "$inner_dir/*" >/dev/null; then - cp -r "$inner_dir"/* "$target/" || { - msg_error "Failed to copy contents from $inner_dir to $target" - rm -rf "$tmpdir" "$unpack_tmp" - return 1 - } - else - msg_error "Inner directory is empty: $inner_dir" - rm -rf "$tmpdir" "$unpack_tmp" - return 1 - fi - shopt -u dotglob nullglob - else - # Copy all contents - shopt -s dotglob nullglob - if compgen -G "$unpack_tmp/*" >/dev/null; then - cp -r "$unpack_tmp"/* "$target/" || { - msg_error "Failed to copy contents to $target" - rm -rf "$tmpdir" "$unpack_tmp" - return 1 - } - else - msg_error "Unpacked archive is empty" - rm -rf "$tmpdir" "$unpack_tmp" - return 1 - fi - shopt -u dotglob nullglob - fi - - ### Singlefile Mode ### - elif [[ "$mode" == "singlefile" ]]; then - local pattern="${6%\"}" - pattern="${pattern#\"}" - [[ -z "$pattern" ]] && { - msg_error "Mode 'singlefile' requires 6th parameter (asset filename pattern)" - rm -rf "$tmpdir" - return 1 - } - - local asset_url="" - for u in $(echo "$json" | jq -r '.assets[].browser_download_url'); do - filename_candidate="${u##*/}" - case "$filename_candidate" in - $pattern) - asset_url="$u" - break - ;; - esac - done - - [[ -z "$asset_url" ]] && { - msg_error "No asset matching '$pattern' found" - rm -rf "$tmpdir" - return 1 - } - - filename="${asset_url##*/}" - mkdir -p "$target" - - local use_filename="${USE_ORIGINAL_FILENAME:-false}" - local target_file="$app" - [[ "$use_filename" == "true" ]] && target_file="$filename" - - curl $download_timeout -fsSL -o "$target/$target_file" "$asset_url" || { - msg_error "Download failed: $asset_url" - rm -rf "$tmpdir" - return 1 - } - - if [[ "$target_file" != *.jar && -f "$target/$target_file" ]]; then - chmod +x "$target/$target_file" - fi - - else - msg_error "Unknown mode: $mode" + echo "$version" >"$version_file" + msg_ok "Deployed: $app ($version)" rm -rf "$tmpdir" - return 1 - fi - - echo "$version" >"$version_file" - msg_ok "Deployed: $app ($version)" - rm -rf "$tmpdir" } # ------------------------------------------------------------------------------ @@ -1728,40 +1728,40 @@ function fetch_and_deploy_gh_release() { # ------------------------------------------------------------------------------ function import_local_ip() { - local IP_FILE="/run/local-ip.env" - if [[ -f "$IP_FILE" ]]; then - # shellcheck disable=SC1090 - source "$IP_FILE" - fi - - if [[ -z "${LOCAL_IP:-}" ]]; then - get_current_ip() { - local targets=("8.8.8.8" "1.1.1.1" "192.168.1.1" "10.0.0.1" "172.16.0.1" "default") - local ip - - for target in "${targets[@]}"; do - if [[ "$target" == "default" ]]; then - ip=$(ip route get 1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') - else - ip=$(ip route get "$target" 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') - fi - if [[ -n "$ip" ]]; then - echo "$ip" - return 0 - fi - done - - return 1 - } - - LOCAL_IP="$(get_current_ip || true)" - if [[ -z "$LOCAL_IP" ]]; then - msg_error "Could not determine LOCAL_IP" - return 1 + local IP_FILE="/run/local-ip.env" + if [[ -f "$IP_FILE" ]]; then + # shellcheck disable=SC1090 + source "$IP_FILE" fi - fi - export LOCAL_IP + if [[ -z "${LOCAL_IP:-}" ]]; then + get_current_ip() { + local targets=("8.8.8.8" "1.1.1.1" "192.168.1.1" "10.0.0.1" "172.16.0.1" "default") + local ip + + for target in "${targets[@]}"; do + if [[ "$target" == "default" ]]; then + ip=$(ip route get 1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') + else + ip=$(ip route get "$target" 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') + fi + if [[ -n "$ip" ]]; then + echo "$ip" + return 0 + fi + done + + return 1 + } + + LOCAL_IP="$(get_current_ip || true)" + if [[ -z "$LOCAL_IP" ]]; then + msg_error "Could not determine LOCAL_IP" + return 1 + fi + fi + + export LOCAL_IP } # ------------------------------------------------------------------------------ @@ -1773,32 +1773,32 @@ function import_local_ip() { # ------------------------------------------------------------------------------ function setup_adminer() { - if grep -qi alpine /etc/os-release; then - msg_info "Setup Adminer (Alpine)" - mkdir -p /var/www/localhost/htdocs/adminer - curl -fsSL https://github.com/vrana/adminer/releases/latest/download/adminer.php \ - -o /var/www/localhost/htdocs/adminer/index.php || { - msg_error "Failed to download Adminer" - return 1 - } - cache_installed_version "adminer" "latest-alpine" - msg_ok "Setup Adminer (Alpine)" - else - msg_info "Setup Adminer (Debian/Ubuntu)" - ensure_dependencies adminer - $STD a2enconf adminer || { - msg_error "Failed to enable Adminer Apache config" - return 1 - } - $STD systemctl reload apache2 || { - msg_error "Failed to reload Apache" - return 1 - } - local VERSION - VERSION=$(dpkg -s adminer 2>/dev/null | grep '^Version:' | awk '{print $2}') - cache_installed_version "adminer" "${VERSION:-unknown}" - msg_ok "Setup Adminer (Debian/Ubuntu)" - fi + if grep -qi alpine /etc/os-release; then + msg_info "Setup Adminer (Alpine)" + mkdir -p /var/www/localhost/htdocs/adminer + curl -fsSL https://github.com/vrana/adminer/releases/latest/download/adminer.php \ + -o /var/www/localhost/htdocs/adminer/index.php || { + msg_error "Failed to download Adminer" + return 1 + } + cache_installed_version "adminer" "latest-alpine" + msg_ok "Setup Adminer (Alpine)" + else + msg_info "Setup Adminer (Debian/Ubuntu)" + ensure_dependencies adminer + $STD a2enconf adminer || { + msg_error "Failed to enable Adminer Apache config" + return 1 + } + $STD systemctl reload apache2 || { + msg_error "Failed to reload Apache" + return 1 + } + local VERSION + VERSION=$(dpkg -s adminer 2>/dev/null | grep '^Version:' | awk '{print $2}') + cache_installed_version "adminer" "${VERSION:-unknown}" + msg_ok "Setup Adminer (Debian/Ubuntu)" + fi } # ------------------------------------------------------------------------------ @@ -1811,60 +1811,60 @@ function setup_adminer() { # ------------------------------------------------------------------------------ function setup_composer() { - local COMPOSER_BIN="/usr/local/bin/composer" - export COMPOSER_ALLOW_SUPERUSER=1 + local COMPOSER_BIN="/usr/local/bin/composer" + export COMPOSER_ALLOW_SUPERUSER=1 - # Get currently installed version - local INSTALLED_VERSION="" - if [[ -x "$COMPOSER_BIN" ]]; then - INSTALLED_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}') - fi + # Get currently installed version + local INSTALLED_VERSION="" + if [[ -x "$COMPOSER_BIN" ]]; then + INSTALLED_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}') + fi - # Scenario 1: Already installed - just self-update - if [[ -n "$INSTALLED_VERSION" ]]; then - msg_info "Update Composer $INSTALLED_VERSION" - $STD "$COMPOSER_BIN" self-update --no-interaction || true - local UPDATED_VERSION - UPDATED_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}') - cache_installed_version "composer" "$UPDATED_VERSION" - msg_ok "Update Composer $UPDATED_VERSION" - return 0 - fi + # Scenario 1: Already installed - just self-update + if [[ -n "$INSTALLED_VERSION" ]]; then + msg_info "Update Composer $INSTALLED_VERSION" + $STD "$COMPOSER_BIN" self-update --no-interaction || true + local UPDATED_VERSION + UPDATED_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}') + cache_installed_version "composer" "$UPDATED_VERSION" + msg_ok "Update Composer $UPDATED_VERSION" + return 0 + fi - # Scenario 2: Fresh install - msg_info "Setup Composer" + # Scenario 2: Fresh install + msg_info "Setup Composer" - for old in /usr/bin/composer /bin/composer /root/.composer/vendor/bin/composer; do - [[ -e "$old" && "$old" != "$COMPOSER_BIN" ]] && rm -f "$old" - done + for old in /usr/bin/composer /bin/composer /root/.composer/vendor/bin/composer; do + [[ -e "$old" && "$old" != "$COMPOSER_BIN" ]] && rm -f "$old" + done - ensure_usr_local_bin_persist - export PATH="/usr/local/bin:$PATH" + ensure_usr_local_bin_persist + export PATH="/usr/local/bin:$PATH" - curl -fsSL https://getcomposer.org/installer -o /tmp/composer-setup.php || { - msg_error "Failed to download Composer installer" - return 1 - } + curl -fsSL https://getcomposer.org/installer -o /tmp/composer-setup.php || { + msg_error "Failed to download Composer installer" + return 1 + } - $STD php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer || { - msg_error "Failed to install Composer" + $STD php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer || { + msg_error "Failed to install Composer" + rm -f /tmp/composer-setup.php + return 1 + } rm -f /tmp/composer-setup.php - return 1 - } - rm -f /tmp/composer-setup.php - if [[ ! -x "$COMPOSER_BIN" ]]; then - msg_error "Composer installation failed" - return 1 - fi + if [[ ! -x "$COMPOSER_BIN" ]]; then + msg_error "Composer installation failed" + return 1 + fi - chmod +x "$COMPOSER_BIN" - $STD "$COMPOSER_BIN" self-update --no-interaction || true + chmod +x "$COMPOSER_BIN" + $STD "$COMPOSER_BIN" self-update --no-interaction || true - local FINAL_VERSION - FINAL_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}') - cache_installed_version "composer" "$FINAL_VERSION" - msg_ok "Setup Composer" + local FINAL_VERSION + FINAL_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}') + cache_installed_version "composer" "$FINAL_VERSION" + msg_ok "Setup Composer" } # ------------------------------------------------------------------------------ @@ -1886,201 +1886,201 @@ function setup_composer() { # ------------------------------------------------------------------------------ function setup_ffmpeg() { - local TMP_DIR=$(mktemp -d) - local GITHUB_REPO="FFmpeg/FFmpeg" - local VERSION="${FFMPEG_VERSION:-latest}" - local TYPE="${FFMPEG_TYPE:-full}" - local BIN_PATH="/usr/local/bin/ffmpeg" + local TMP_DIR=$(mktemp -d) + local GITHUB_REPO="FFmpeg/FFmpeg" + local VERSION="${FFMPEG_VERSION:-latest}" + local TYPE="${FFMPEG_TYPE:-full}" + local BIN_PATH="/usr/local/bin/ffmpeg" - # Get currently installed version - local INSTALLED_VERSION="" - if command -v ffmpeg &>/dev/null; then - INSTALLED_VERSION=$(ffmpeg -version 2>/dev/null | head -n1 | awk '{print $3}') - fi + # Get currently installed version + local INSTALLED_VERSION="" + if command -v ffmpeg &>/dev/null; then + INSTALLED_VERSION=$(ffmpeg -version 2>/dev/null | head -n1 | awk '{print $3}') + fi - msg_info "Setup FFmpeg ${VERSION} ($TYPE)" + msg_info "Setup FFmpeg ${VERSION} ($TYPE)" - # Binary fallback mode - if [[ "$TYPE" == "binary" ]]; then - curl -fsSL https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz -o "$TMP_DIR/ffmpeg.tar.xz" || { - msg_error "Failed to download FFmpeg binary" - rm -rf "$TMP_DIR" - return 1 + # Binary fallback mode + if [[ "$TYPE" == "binary" ]]; then + curl -fsSL https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz -o "$TMP_DIR/ffmpeg.tar.xz" || { + msg_error "Failed to download FFmpeg binary" + rm -rf "$TMP_DIR" + return 1 + } + tar -xf "$TMP_DIR/ffmpeg.tar.xz" -C "$TMP_DIR" || { + msg_error "Failed to extract FFmpeg binary" + rm -rf "$TMP_DIR" + return 1 + } + local EXTRACTED_DIR + EXTRACTED_DIR=$(find "$TMP_DIR" -maxdepth 1 -type d -name "ffmpeg-*") + cp "$EXTRACTED_DIR/ffmpeg" "$BIN_PATH" + cp "$EXTRACTED_DIR/ffprobe" /usr/local/bin/ffprobe + chmod +x "$BIN_PATH" /usr/local/bin/ffprobe + local FINAL_VERSION=$($BIN_PATH -version 2>/dev/null | head -n1 | awk '{print $3}') + rm -rf "$TMP_DIR" + cache_installed_version "ffmpeg" "$FINAL_VERSION" + ensure_usr_local_bin_persist + [[ -n "$INSTALLED_VERSION" ]] && msg_ok "Upgrade FFmpeg $INSTALLED_VERSION → $FINAL_VERSION" || msg_ok "Setup FFmpeg $FINAL_VERSION" + return 0 + fi + + ensure_dependencies jq + + # Auto-detect latest stable version if none specified + if [[ "$VERSION" == "latest" || -z "$VERSION" ]]; then + local ffmpeg_tags + ffmpeg_tags=$(curl -fsSL --max-time 15 "https://api.github.com/repos/${GITHUB_REPO}/tags" 2>/dev/null || echo "") + + if [[ -z "$ffmpeg_tags" ]]; then + msg_warn "Could not fetch FFmpeg versions from GitHub, trying binary fallback" + VERSION="" # Will trigger binary fallback below + else + VERSION=$(echo "$ffmpeg_tags" | jq -r '.[].name' 2>/dev/null | + grep -E '^n[0-9]+\.[0-9]+\.[0-9]+$' | + sort -V | tail -n1 || echo "") + fi + fi + + if [[ -z "$VERSION" ]]; then + msg_info "Could not determine FFmpeg source version, using pre-built binary" + VERSION="" # Will use binary fallback + fi + + # Dependency selection + local DEPS=(build-essential yasm nasm pkg-config) + case "$TYPE" in + minimal) + DEPS+=(libx264-dev libvpx-dev libmp3lame-dev) + ;; + medium) + DEPS+=(libx264-dev libvpx-dev libmp3lame-dev libfreetype6-dev libass-dev libopus-dev libvorbis-dev) + ;; + full) + DEPS+=( + libx264-dev libx265-dev libvpx-dev libmp3lame-dev + libfreetype6-dev libass-dev libopus-dev libvorbis-dev + libdav1d-dev libsvtav1-dev zlib1g-dev libnuma-dev + libva-dev libdrm-dev + ) + ;; + *) + msg_error "Invalid FFMPEG_TYPE: $TYPE" + rm -rf "$TMP_DIR" + return 1 + ;; + esac + + ensure_dependencies "${DEPS[@]}" + + # Try to download source if VERSION is set + if [[ -n "$VERSION" ]]; then + curl -fsSL "https://github.com/${GITHUB_REPO}/archive/refs/tags/${VERSION}.tar.gz" -o "$TMP_DIR/ffmpeg.tar.gz" || { + msg_warn "Failed to download FFmpeg source ${VERSION}, falling back to pre-built binary" + VERSION="" + } + fi + + # If no source download (either VERSION empty or download failed), use binary + if [[ -z "$VERSION" ]]; then + msg_info "Setup FFmpeg from pre-built binary" + curl -fsSL https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz -o "$TMP_DIR/ffmpeg.tar.xz" || { + msg_error "Failed to download FFmpeg pre-built binary" + rm -rf "$TMP_DIR" + return 1 + } + + tar -xJf "$TMP_DIR/ffmpeg.tar.xz" -C "$TMP_DIR" || { + msg_error "Failed to extract FFmpeg binary archive" + rm -rf "$TMP_DIR" + return 1 + } + + if ! cp "$TMP_DIR/ffmpeg-"*/ffmpeg /usr/local/bin/ffmpeg 2>/dev/null; then + msg_error "Failed to install FFmpeg binary" + rm -rf "$TMP_DIR" + return 1 + fi + + cache_installed_version "ffmpeg" "static" + rm -rf "$TMP_DIR" + msg_ok "Setup FFmpeg from pre-built binary" + return 0 + fi + + tar -xzf "$TMP_DIR/ffmpeg.tar.gz" -C "$TMP_DIR" || { + msg_error "Failed to extract FFmpeg source" + rm -rf "$TMP_DIR" + return 1 } - tar -xf "$TMP_DIR/ffmpeg.tar.xz" -C "$TMP_DIR" || { - msg_error "Failed to extract FFmpeg binary" - rm -rf "$TMP_DIR" - return 1 + + cd "$TMP_DIR/FFmpeg-"* || { + msg_error "Source extraction failed" + rm -rf "$TMP_DIR" + return 1 } - local EXTRACTED_DIR - EXTRACTED_DIR=$(find "$TMP_DIR" -maxdepth 1 -type d -name "ffmpeg-*") - cp "$EXTRACTED_DIR/ffmpeg" "$BIN_PATH" - cp "$EXTRACTED_DIR/ffprobe" /usr/local/bin/ffprobe - chmod +x "$BIN_PATH" /usr/local/bin/ffprobe - local FINAL_VERSION=$($BIN_PATH -version 2>/dev/null | head -n1 | awk '{print $3}') + + local args=( + --enable-gpl + --enable-shared + --enable-nonfree + --disable-static + --enable-libx264 + --enable-libvpx + --enable-libmp3lame + ) + + if [[ "$TYPE" != "minimal" ]]; then + args+=(--enable-libfreetype --enable-libass --enable-libopus --enable-libvorbis) + fi + + if [[ "$TYPE" == "full" ]]; then + args+=(--enable-libx265 --enable-libdav1d --enable-zlib) + args+=(--enable-vaapi --enable-libdrm) + fi + + if [[ ${#args[@]} -eq 0 ]]; then + msg_error "FFmpeg configure args array is empty" + rm -rf "$TMP_DIR" + return 1 + fi + + $STD ./configure "${args[@]}" || { + msg_error "FFmpeg configure failed" + rm -rf "$TMP_DIR" + return 1 + } + $STD make -j"$(nproc)" || { + msg_error "FFmpeg compilation failed" + rm -rf "$TMP_DIR" + return 1 + } + $STD make install || { + msg_error "FFmpeg installation failed" + rm -rf "$TMP_DIR" + return 1 + } + echo "/usr/local/lib" >/etc/ld.so.conf.d/ffmpeg.conf + $STD ldconfig + + ldconfig -p 2>/dev/null | grep libavdevice >/dev/null || { + msg_error "libavdevice not registered with dynamic linker" + rm -rf "$TMP_DIR" + return 1 + } + + if ! command -v ffmpeg &>/dev/null; then + msg_error "FFmpeg installation failed" + rm -rf "$TMP_DIR" + return 1 + fi + + local FINAL_VERSION + FINAL_VERSION=$(ffmpeg -version 2>/dev/null | head -n1 | awk '{print $3}') rm -rf "$TMP_DIR" cache_installed_version "ffmpeg" "$FINAL_VERSION" ensure_usr_local_bin_persist [[ -n "$INSTALLED_VERSION" ]] && msg_ok "Upgrade FFmpeg $INSTALLED_VERSION → $FINAL_VERSION" || msg_ok "Setup FFmpeg $FINAL_VERSION" - return 0 - fi - - ensure_dependencies jq - - # Auto-detect latest stable version if none specified - if [[ "$VERSION" == "latest" || -z "$VERSION" ]]; then - local ffmpeg_tags - ffmpeg_tags=$(curl -fsSL --max-time 15 "https://api.github.com/repos/${GITHUB_REPO}/tags" 2>/dev/null || echo "") - - if [[ -z "$ffmpeg_tags" ]]; then - msg_warn "Could not fetch FFmpeg versions from GitHub, trying binary fallback" - VERSION="" # Will trigger binary fallback below - else - VERSION=$(echo "$ffmpeg_tags" | jq -r '.[].name' 2>/dev/null | - grep -E '^n[0-9]+\.[0-9]+\.[0-9]+$' | - sort -V | tail -n1 || echo "") - fi - fi - - if [[ -z "$VERSION" ]]; then - msg_info "Could not determine FFmpeg source version, using pre-built binary" - VERSION="" # Will use binary fallback - fi - - # Dependency selection - local DEPS=(build-essential yasm nasm pkg-config) - case "$TYPE" in - minimal) - DEPS+=(libx264-dev libvpx-dev libmp3lame-dev) - ;; - medium) - DEPS+=(libx264-dev libvpx-dev libmp3lame-dev libfreetype6-dev libass-dev libopus-dev libvorbis-dev) - ;; - full) - DEPS+=( - libx264-dev libx265-dev libvpx-dev libmp3lame-dev - libfreetype6-dev libass-dev libopus-dev libvorbis-dev - libdav1d-dev libsvtav1-dev zlib1g-dev libnuma-dev - libva-dev libdrm-dev - ) - ;; - *) - msg_error "Invalid FFMPEG_TYPE: $TYPE" - rm -rf "$TMP_DIR" - return 1 - ;; - esac - - ensure_dependencies "${DEPS[@]}" - - # Try to download source if VERSION is set - if [[ -n "$VERSION" ]]; then - curl -fsSL "https://github.com/${GITHUB_REPO}/archive/refs/tags/${VERSION}.tar.gz" -o "$TMP_DIR/ffmpeg.tar.gz" || { - msg_warn "Failed to download FFmpeg source ${VERSION}, falling back to pre-built binary" - VERSION="" - } - fi - - # If no source download (either VERSION empty or download failed), use binary - if [[ -z "$VERSION" ]]; then - msg_info "Setup FFmpeg from pre-built binary" - curl -fsSL https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz -o "$TMP_DIR/ffmpeg.tar.xz" || { - msg_error "Failed to download FFmpeg pre-built binary" - rm -rf "$TMP_DIR" - return 1 - } - - tar -xJf "$TMP_DIR/ffmpeg.tar.xz" -C "$TMP_DIR" || { - msg_error "Failed to extract FFmpeg binary archive" - rm -rf "$TMP_DIR" - return 1 - } - - if ! cp "$TMP_DIR/ffmpeg-"*/ffmpeg /usr/local/bin/ffmpeg 2>/dev/null; then - msg_error "Failed to install FFmpeg binary" - rm -rf "$TMP_DIR" - return 1 - fi - - cache_installed_version "ffmpeg" "static" - rm -rf "$TMP_DIR" - msg_ok "Setup FFmpeg from pre-built binary" - return 0 - fi - - tar -xzf "$TMP_DIR/ffmpeg.tar.gz" -C "$TMP_DIR" || { - msg_error "Failed to extract FFmpeg source" - rm -rf "$TMP_DIR" - return 1 - } - - cd "$TMP_DIR/FFmpeg-"* || { - msg_error "Source extraction failed" - rm -rf "$TMP_DIR" - return 1 - } - - local args=( - --enable-gpl - --enable-shared - --enable-nonfree - --disable-static - --enable-libx264 - --enable-libvpx - --enable-libmp3lame - ) - - if [[ "$TYPE" != "minimal" ]]; then - args+=(--enable-libfreetype --enable-libass --enable-libopus --enable-libvorbis) - fi - - if [[ "$TYPE" == "full" ]]; then - args+=(--enable-libx265 --enable-libdav1d --enable-zlib) - args+=(--enable-vaapi --enable-libdrm) - fi - - if [[ ${#args[@]} -eq 0 ]]; then - msg_error "FFmpeg configure args array is empty" - rm -rf "$TMP_DIR" - return 1 - fi - - $STD ./configure "${args[@]}" || { - msg_error "FFmpeg configure failed" - rm -rf "$TMP_DIR" - return 1 - } - $STD make -j"$(nproc)" || { - msg_error "FFmpeg compilation failed" - rm -rf "$TMP_DIR" - return 1 - } - $STD make install || { - msg_error "FFmpeg installation failed" - rm -rf "$TMP_DIR" - return 1 - } - echo "/usr/local/lib" >/etc/ld.so.conf.d/ffmpeg.conf - $STD ldconfig - - ldconfig -p 2>/dev/null | grep libavdevice >/dev/null || { - msg_error "libavdevice not registered with dynamic linker" - rm -rf "$TMP_DIR" - return 1 - } - - if ! command -v ffmpeg &>/dev/null; then - msg_error "FFmpeg installation failed" - rm -rf "$TMP_DIR" - return 1 - fi - - local FINAL_VERSION - FINAL_VERSION=$(ffmpeg -version 2>/dev/null | head -n1 | awk '{print $3}') - rm -rf "$TMP_DIR" - cache_installed_version "ffmpeg" "$FINAL_VERSION" - ensure_usr_local_bin_persist - [[ -n "$INSTALLED_VERSION" ]] && msg_ok "Upgrade FFmpeg $INSTALLED_VERSION → $FINAL_VERSION" || msg_ok "Setup FFmpeg $FINAL_VERSION" } # ------------------------------------------------------------------------------ @@ -2095,75 +2095,75 @@ function setup_ffmpeg() { # ------------------------------------------------------------------------------ function setup_go() { - local ARCH - case "$(uname -m)" in - x86_64) ARCH="amd64" ;; - aarch64) ARCH="arm64" ;; - *) - msg_error "Unsupported architecture: $(uname -m)" - return 1 - ;; - esac + local ARCH + case "$(uname -m)" in + x86_64) ARCH="amd64" ;; + aarch64) ARCH="arm64" ;; + *) + msg_error "Unsupported architecture: $(uname -m)" + return 1 + ;; + esac - # Resolve "latest" version - local GO_VERSION="${GO_VERSION:-latest}" - if [[ "$GO_VERSION" == "latest" ]]; then - GO_VERSION=$(curl -fsSL https://go.dev/VERSION?m=text 2>/dev/null | head -n1 | sed 's/^go//') || { - msg_error "Could not determine latest Go version" - return 1 + # Resolve "latest" version + local GO_VERSION="${GO_VERSION:-latest}" + if [[ "$GO_VERSION" == "latest" ]]; then + GO_VERSION=$(curl -fsSL https://go.dev/VERSION?m=text 2>/dev/null | head -n1 | sed 's/^go//') || { + msg_error "Could not determine latest Go version" + return 1 + } + [[ -z "$GO_VERSION" ]] && { + msg_error "Latest Go version is empty" + return 1 + } + fi + + local GO_BIN="/usr/local/bin/go" + local GO_INSTALL_DIR="/usr/local/go" + + # Get currently installed version + local CURRENT_VERSION="" + if [[ -x "$GO_BIN" ]]; then + CURRENT_VERSION=$("$GO_BIN" version 2>/dev/null | awk '{print $3}' | sed 's/go//') + fi + + # Scenario 1: Already at target version + if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$GO_VERSION" ]]; then + cache_installed_version "go" "$GO_VERSION" + return 0 + fi + + # Scenario 2: Different version or not installed + if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" != "$GO_VERSION" ]]; then + msg_info "Upgrade Go from $CURRENT_VERSION to $GO_VERSION" + remove_old_tool_version "go" + else + msg_info "Setup Go $GO_VERSION" + fi + + local TARBALL="go${GO_VERSION}.linux-${ARCH}.tar.gz" + local URL="https://go.dev/dl/${TARBALL}" + local TMP_TAR=$(mktemp) + + curl -fsSL "$URL" -o "$TMP_TAR" || { + msg_error "Failed to download Go $GO_VERSION" + rm -f "$TMP_TAR" + return 1 } - [[ -z "$GO_VERSION" ]] && { - msg_error "Latest Go version is empty" - return 1 + + $STD tar -C /usr/local -xzf "$TMP_TAR" || { + msg_error "Failed to extract Go tarball" + rm -f "$TMP_TAR" + return 1 } - fi - local GO_BIN="/usr/local/bin/go" - local GO_INSTALL_DIR="/usr/local/go" + ln -sf /usr/local/go/bin/go /usr/local/bin/go + ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt + rm -f "$TMP_TAR" - # Get currently installed version - local CURRENT_VERSION="" - if [[ -x "$GO_BIN" ]]; then - CURRENT_VERSION=$("$GO_BIN" version 2>/dev/null | awk '{print $3}' | sed 's/go//') - fi - - # Scenario 1: Already at target version - if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$GO_VERSION" ]]; then cache_installed_version "go" "$GO_VERSION" - return 0 - fi - - # Scenario 2: Different version or not installed - if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" != "$GO_VERSION" ]]; then - msg_info "Upgrade Go from $CURRENT_VERSION to $GO_VERSION" - remove_old_tool_version "go" - else - msg_info "Setup Go $GO_VERSION" - fi - - local TARBALL="go${GO_VERSION}.linux-${ARCH}.tar.gz" - local URL="https://go.dev/dl/${TARBALL}" - local TMP_TAR=$(mktemp) - - curl -fsSL "$URL" -o "$TMP_TAR" || { - msg_error "Failed to download Go $GO_VERSION" - rm -f "$TMP_TAR" - return 1 - } - - $STD tar -C /usr/local -xzf "$TMP_TAR" || { - msg_error "Failed to extract Go tarball" - rm -f "$TMP_TAR" - return 1 - } - - ln -sf /usr/local/go/bin/go /usr/local/bin/go - ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt - rm -f "$TMP_TAR" - - cache_installed_version "go" "$GO_VERSION" - ensure_usr_local_bin_persist - msg_ok "Setup Go $GO_VERSION" + ensure_usr_local_bin_persist + msg_ok "Setup Go $GO_VERSION" } # ------------------------------------------------------------------------------ @@ -2175,110 +2175,110 @@ function setup_go() { # ------------------------------------------------------------------------------ function setup_gs() { - local TMP_DIR=$(mktemp -d) - local CURRENT_VERSION=$(gs --version 2>/dev/null || echo "0") + local TMP_DIR=$(mktemp -d) + local CURRENT_VERSION=$(gs --version 2>/dev/null || echo "0") - ensure_dependencies jq + ensure_dependencies jq - local RELEASE_JSON - RELEASE_JSON=$(curl -fsSL --max-time 15 https://api.github.com/repos/ArtifexSoftware/ghostpdl-downloads/releases/latest 2>/dev/null || echo "") + local RELEASE_JSON + RELEASE_JSON=$(curl -fsSL --max-time 15 https://api.github.com/repos/ArtifexSoftware/ghostpdl-downloads/releases/latest 2>/dev/null || echo "") - if [[ -z "$RELEASE_JSON" ]]; then - msg_warn "Cannot fetch latest Ghostscript version from GitHub API" - # Try to get from current version - if command -v gs &>/dev/null; then - gs --version | head -n1 - cache_installed_version "ghostscript" "$CURRENT_VERSION" - return 0 + if [[ -z "$RELEASE_JSON" ]]; then + msg_warn "Cannot fetch latest Ghostscript version from GitHub API" + # Try to get from current version + if command -v gs &>/dev/null; then + gs --version | head -n1 + cache_installed_version "ghostscript" "$CURRENT_VERSION" + return 0 + fi + msg_error "Cannot determine Ghostscript version and no existing installation found" + return 1 fi - msg_error "Cannot determine Ghostscript version and no existing installation found" - return 1 - fi - local LATEST_VERSION - LATEST_VERSION=$(echo "$RELEASE_JSON" | jq -r '.tag_name' | sed 's/^gs//') - local LATEST_VERSION_DOTTED - LATEST_VERSION_DOTTED=$(echo "$RELEASE_JSON" | jq -r '.name' | grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+') + local LATEST_VERSION + LATEST_VERSION=$(echo "$RELEASE_JSON" | jq -r '.tag_name' | sed 's/^gs//') + local LATEST_VERSION_DOTTED + LATEST_VERSION_DOTTED=$(echo "$RELEASE_JSON" | jq -r '.name' | grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+') - if [[ -z "$LATEST_VERSION" || -z "$LATEST_VERSION_DOTTED" ]]; then - msg_warn "Could not determine latest Ghostscript version from GitHub - checking system" - # Fallback: try to use system version or return error - if [[ "$CURRENT_VERSION" == "0" ]]; then - msg_error "Ghostscript not installed and cannot determine latest version" - rm -rf "$TMP_DIR" - return 1 + if [[ -z "$LATEST_VERSION" || -z "$LATEST_VERSION_DOTTED" ]]; then + msg_warn "Could not determine latest Ghostscript version from GitHub - checking system" + # Fallback: try to use system version or return error + if [[ "$CURRENT_VERSION" == "0" ]]; then + msg_error "Ghostscript not installed and cannot determine latest version" + rm -rf "$TMP_DIR" + return 1 + fi + rm -rf "$TMP_DIR" + return 0 fi + + # Scenario 1: Already at latest version + if [[ -n "$LATEST_VERSION_DOTTED" ]] && dpkg --compare-versions "$CURRENT_VERSION" ge "$LATEST_VERSION_DOTTED" 2>/dev/null; then + cache_installed_version "ghostscript" "$LATEST_VERSION_DOTTED" + rm -rf "$TMP_DIR" + return 0 + fi + + # Scenario 2: New install or upgrade + if [[ "$CURRENT_VERSION" != "0" && "$CURRENT_VERSION" != "$LATEST_VERSION_DOTTED" ]]; then + msg_info "Upgrade Ghostscript from $CURRENT_VERSION to $LATEST_VERSION_DOTTED" + else + msg_info "Setup Ghostscript $LATEST_VERSION_DOTTED" + fi + + curl -fsSL "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs${LATEST_VERSION}/ghostscript-${LATEST_VERSION_DOTTED}.tar.gz" -o "$TMP_DIR/ghostscript.tar.gz" || { + msg_error "Failed to download Ghostscript" + rm -rf "$TMP_DIR" + return 1 + } + + if ! tar -xzf "$TMP_DIR/ghostscript.tar.gz" -C "$TMP_DIR"; then + msg_error "Failed to extract Ghostscript archive" + rm -rf "$TMP_DIR" + return 1 + fi + + # Verify directory exists before cd + if [[ ! -d "$TMP_DIR/ghostscript-${LATEST_VERSION_DOTTED}" ]]; then + msg_error "Ghostscript source directory not found: $TMP_DIR/ghostscript-${LATEST_VERSION_DOTTED}" + rm -rf "$TMP_DIR" + return 1 + fi + + cd "$TMP_DIR/ghostscript-${LATEST_VERSION_DOTTED}" || { + msg_error "Failed to enter Ghostscript source directory" + rm -rf "$TMP_DIR" + return 1 + } + + ensure_dependencies build-essential libpng-dev zlib1g-dev + + $STD ./configure || { + msg_error "Ghostscript configure failed" + rm -rf "$TMP_DIR" + return 1 + } + $STD make -j"$(nproc)" || { + msg_error "Ghostscript compilation failed" + rm -rf "$TMP_DIR" + return 1 + } + $STD make install || { + msg_error "Ghostscript installation failed" + rm -rf "$TMP_DIR" + return 1 + } + + hash -r + if [[ ! -x "$(command -v gs)" ]]; then + if [[ -x /usr/local/bin/gs ]]; then + ln -sf /usr/local/bin/gs /usr/bin/gs + fi + fi + rm -rf "$TMP_DIR" - return 0 - fi - - # Scenario 1: Already at latest version - if [[ -n "$LATEST_VERSION_DOTTED" ]] && dpkg --compare-versions "$CURRENT_VERSION" ge "$LATEST_VERSION_DOTTED" 2>/dev/null; then cache_installed_version "ghostscript" "$LATEST_VERSION_DOTTED" - rm -rf "$TMP_DIR" - return 0 - fi - - # Scenario 2: New install or upgrade - if [[ "$CURRENT_VERSION" != "0" && "$CURRENT_VERSION" != "$LATEST_VERSION_DOTTED" ]]; then - msg_info "Upgrade Ghostscript from $CURRENT_VERSION to $LATEST_VERSION_DOTTED" - else - msg_info "Setup Ghostscript $LATEST_VERSION_DOTTED" - fi - - curl -fsSL "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs${LATEST_VERSION}/ghostscript-${LATEST_VERSION_DOTTED}.tar.gz" -o "$TMP_DIR/ghostscript.tar.gz" || { - msg_error "Failed to download Ghostscript" - rm -rf "$TMP_DIR" - return 1 - } - - if ! tar -xzf "$TMP_DIR/ghostscript.tar.gz" -C "$TMP_DIR"; then - msg_error "Failed to extract Ghostscript archive" - rm -rf "$TMP_DIR" - return 1 - fi - - # Verify directory exists before cd - if [[ ! -d "$TMP_DIR/ghostscript-${LATEST_VERSION_DOTTED}" ]]; then - msg_error "Ghostscript source directory not found: $TMP_DIR/ghostscript-${LATEST_VERSION_DOTTED}" - rm -rf "$TMP_DIR" - return 1 - fi - - cd "$TMP_DIR/ghostscript-${LATEST_VERSION_DOTTED}" || { - msg_error "Failed to enter Ghostscript source directory" - rm -rf "$TMP_DIR" - return 1 - } - - ensure_dependencies build-essential libpng-dev zlib1g-dev - - $STD ./configure || { - msg_error "Ghostscript configure failed" - rm -rf "$TMP_DIR" - return 1 - } - $STD make -j"$(nproc)" || { - msg_error "Ghostscript compilation failed" - rm -rf "$TMP_DIR" - return 1 - } - $STD make install || { - msg_error "Ghostscript installation failed" - rm -rf "$TMP_DIR" - return 1 - } - - hash -r - if [[ ! -x "$(command -v gs)" ]]; then - if [[ -x /usr/local/bin/gs ]]; then - ln -sf /usr/local/bin/gs /usr/bin/gs - fi - fi - - rm -rf "$TMP_DIR" - cache_installed_version "ghostscript" "$LATEST_VERSION_DOTTED" - ensure_usr_local_bin_persist - msg_ok "Setup Ghostscript $LATEST_VERSION_DOTTED" + ensure_usr_local_bin_persist + msg_ok "Setup Ghostscript $LATEST_VERSION_DOTTED" } # ------------------------------------------------------------------------------ @@ -2293,111 +2293,111 @@ function setup_gs() { # - Some things are fetched from intel repositories due to not being in debian repositories. # ------------------------------------------------------------------------------ function setup_hwaccel() { - msg_info "Setup Hardware Acceleration" + msg_info "Setup Hardware Acceleration" - if ! command -v lspci &>/dev/null; then - $STD apt -y update || { - msg_error "Failed to update package list" - return 1 - } - $STD apt -y install pciutils || { - msg_error "Failed to install pciutils" - return 1 - } - fi + if ! command -v lspci &>/dev/null; then + $STD apt -y update || { + msg_error "Failed to update package list" + return 1 + } + $STD apt -y install pciutils || { + msg_error "Failed to install pciutils" + return 1 + } + fi - # Detect GPU vendor (Intel, AMD, NVIDIA) - local gpu_vendor - gpu_vendor=$(lspci 2>/dev/null | grep -Ei 'vga|3d|display' | grep -Eo 'Intel|AMD|NVIDIA' | head -n1 || echo "") + # Detect GPU vendor (Intel, AMD, NVIDIA) + local gpu_vendor + gpu_vendor=$(lspci 2>/dev/null | grep -Ei 'vga|3d|display' | grep -Eo 'Intel|AMD|NVIDIA' | head -n1 || echo "") - # Detect CPU vendor (relevant for AMD APUs) - local cpu_vendor - cpu_vendor=$(lscpu 2>/dev/null | grep -i 'Vendor ID' | awk '{print $3}' || echo "") + # Detect CPU vendor (relevant for AMD APUs) + local cpu_vendor + cpu_vendor=$(lscpu 2>/dev/null | grep -i 'Vendor ID' | awk '{print $3}' || echo "") - if [[ -z "$gpu_vendor" && -z "$cpu_vendor" ]]; then - msg_error "No GPU or CPU vendor detected (missing lspci/lscpu output)" - return 1 - fi - - # Detect OS with fallbacks - local os_id os_codename - os_id=$(grep -oP '(?<=^ID=).+' /etc/os-release 2>/dev/null | tr -d '"' || grep '^ID=' /etc/os-release 2>/dev/null | cut -d'=' -f2 | tr -d '"' || echo "debian") - os_codename=$(grep -oP '(?<=^VERSION_CODENAME=).+' /etc/os-release 2>/dev/null | tr -d '"' || grep '^VERSION_CODENAME=' /etc/os-release 2>/dev/null | cut -d'=' -f2 | tr -d '"' || echo "unknown") - - # Validate os_id - if [[ -z "$os_id" ]]; then - os_id="debian" - fi - - # Determine if we are on a VM or LXC - local in_ct="${CTTYPE:-0}" - - case "$gpu_vendor" in - Intel) - if [[ "$os_id" == "ubuntu" ]]; then - $STD apt -y install intel-opencl-icd || { - msg_error "Failed to install intel-opencl-icd" + if [[ -z "$gpu_vendor" && -z "$cpu_vendor" ]]; then + msg_error "No GPU or CPU vendor detected (missing lspci/lscpu output)" return 1 - } - else - # For Debian: fetch Intel GPU drivers from GitHub - fetch_and_deploy_gh_release "" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-core-2_*_amd64.deb" || { - msg_warn "Failed to deploy Intel IGC core 2" - } - fetch_and_deploy_gh_release "" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-opencl-2_*_amd64.deb" || { - msg_warn "Failed to deploy Intel IGC OpenCL 2" - } - fetch_and_deploy_gh_release "" "intel/compute-runtime" "binary" "latest" "" "libigdgmm12_*_amd64.deb" || { - msg_warn "Failed to deploy Intel GDGMM12" - } - fetch_and_deploy_gh_release "" "intel/compute-runtime" "binary" "latest" "" "intel-opencl-icd_*_amd64.deb" || { - msg_warn "Failed to deploy Intel OpenCL ICD" - } fi - $STD apt -y install va-driver-all ocl-icd-libopencl1 vainfo intel-gpu-tools || { - msg_error "Failed to install Intel GPU dependencies" - return 1 - } - ;; - AMD) - $STD apt -y install mesa-va-drivers mesa-vdpau-drivers mesa-opencl-icd vainfo clinfo || { - msg_error "Failed to install AMD GPU dependencies" - return 1 - } + # Detect OS with fallbacks + local os_id os_codename + os_id=$(grep -oP '(?<=^ID=).+' /etc/os-release 2>/dev/null | tr -d '"' || grep '^ID=' /etc/os-release 2>/dev/null | cut -d'=' -f2 | tr -d '"' || echo "debian") + os_codename=$(grep -oP '(?<=^VERSION_CODENAME=).+' /etc/os-release 2>/dev/null | tr -d '"' || grep '^VERSION_CODENAME=' /etc/os-release 2>/dev/null | cut -d'=' -f2 | tr -d '"' || echo "unknown") - # For AMD CPUs without discrete GPU (APUs) - if [[ "$cpu_vendor" == "AuthenticAMD" && -n "$gpu_vendor" ]]; then - $STD apt -y install libdrm-amdgpu1 firmware-amd-graphics || true + # Validate os_id + if [[ -z "$os_id" ]]; then + os_id="debian" fi - ;; - NVIDIA) - # NVIDIA needs manual driver setup - skip for now - msg_info "NVIDIA GPU detected - manual driver setup required" - ;; - *) - # If no discrete GPU, but AMD CPU (e.g., Ryzen APU) - if [[ "$cpu_vendor" == "AuthenticAMD" ]]; then - $STD apt -y install mesa-opencl-icd ocl-icd-libopencl1 clinfo || { - msg_error "Failed to install Mesa OpenCL stack" - return 1 - } - else - msg_warn "No supported GPU vendor detected - skipping GPU acceleration" + + # Determine if we are on a VM or LXC + local in_ct="${CTTYPE:-0}" + + case "$gpu_vendor" in + Intel) + if [[ "$os_id" == "ubuntu" ]]; then + $STD apt -y install intel-opencl-icd || { + msg_error "Failed to install intel-opencl-icd" + return 1 + } + else + # For Debian: fetch Intel GPU drivers from GitHub + fetch_and_deploy_gh_release "" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-core-2_*_amd64.deb" || { + msg_warn "Failed to deploy Intel IGC core 2" + } + fetch_and_deploy_gh_release "" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-opencl-2_*_amd64.deb" || { + msg_warn "Failed to deploy Intel IGC OpenCL 2" + } + fetch_and_deploy_gh_release "" "intel/compute-runtime" "binary" "latest" "" "libigdgmm12_*_amd64.deb" || { + msg_warn "Failed to deploy Intel GDGMM12" + } + fetch_and_deploy_gh_release "" "intel/compute-runtime" "binary" "latest" "" "intel-opencl-icd_*_amd64.deb" || { + msg_warn "Failed to deploy Intel OpenCL ICD" + } + fi + + $STD apt -y install va-driver-all ocl-icd-libopencl1 vainfo intel-gpu-tools || { + msg_error "Failed to install Intel GPU dependencies" + return 1 + } + ;; + AMD) + $STD apt -y install mesa-va-drivers mesa-vdpau-drivers mesa-opencl-icd vainfo clinfo || { + msg_error "Failed to install AMD GPU dependencies" + return 1 + } + + # For AMD CPUs without discrete GPU (APUs) + if [[ "$cpu_vendor" == "AuthenticAMD" && -n "$gpu_vendor" ]]; then + $STD apt -y install libdrm-amdgpu1 firmware-amd-graphics || true + fi + ;; + NVIDIA) + # NVIDIA needs manual driver setup - skip for now + msg_info "NVIDIA GPU detected - manual driver setup required" + ;; + *) + # If no discrete GPU, but AMD CPU (e.g., Ryzen APU) + if [[ "$cpu_vendor" == "AuthenticAMD" ]]; then + $STD apt -y install mesa-opencl-icd ocl-icd-libopencl1 clinfo || { + msg_error "Failed to install Mesa OpenCL stack" + return 1 + } + else + msg_warn "No supported GPU vendor detected - skipping GPU acceleration" + fi + ;; + esac + + if [[ "$in_ct" == "0" ]]; then + chgrp video /dev/dri 2>/dev/null || true + chmod 755 /dev/dri 2>/dev/null || true + chmod 660 /dev/dri/* 2>/dev/null || true + $STD adduser "$(id -u -n)" video + $STD adduser "$(id -u -n)" render fi - ;; - esac - if [[ "$in_ct" == "0" ]]; then - chgrp video /dev/dri 2>/dev/null || true - chmod 755 /dev/dri 2>/dev/null || true - chmod 660 /dev/dri/* 2>/dev/null || true - $STD adduser "$(id -u -n)" video - $STD adduser "$(id -u -n)" render - fi - - cache_installed_version "hwaccel" "1.0" - msg_ok "Setup Hardware Acceleration" + cache_installed_version "hwaccel" "1.0" + msg_ok "Setup Hardware Acceleration" } # ------------------------------------------------------------------------------ @@ -2412,89 +2412,89 @@ function setup_hwaccel() { # - Requires: build-essential, libtool, libjpeg-dev, libpng-dev, etc. # ------------------------------------------------------------------------------ function setup_imagemagick() { - local TMP_DIR=$(mktemp -d) - local BINARY_PATH="/usr/local/bin/magick" + local TMP_DIR=$(mktemp -d) + local BINARY_PATH="/usr/local/bin/magick" - # Get currently installed version - local INSTALLED_VERSION="" - if command -v magick &>/dev/null; then - INSTALLED_VERSION=$(magick -version | awk '/^Version/ {print $3}') - fi + # Get currently installed version + local INSTALLED_VERSION="" + if command -v magick &>/dev/null; then + INSTALLED_VERSION=$(magick -version | awk '/^Version/ {print $3}') + fi - msg_info "Setup ImageMagick" + msg_info "Setup ImageMagick" - ensure_dependencies \ - build-essential \ - libtool \ - libjpeg-dev \ - libpng-dev \ - libtiff-dev \ - libwebp-dev \ - libheif-dev \ - libde265-dev \ - libopenjp2-7-dev \ - libxml2-dev \ - liblcms2-dev \ - libfreetype6-dev \ - libraw-dev \ - libfftw3-dev \ - liblqr-1-0-dev \ - libgsl-dev \ - pkg-config \ - ghostscript + ensure_dependencies \ + build-essential \ + libtool \ + libjpeg-dev \ + libpng-dev \ + libtiff-dev \ + libwebp-dev \ + libheif-dev \ + libde265-dev \ + libopenjp2-7-dev \ + libxml2-dev \ + liblcms2-dev \ + libfreetype6-dev \ + libraw-dev \ + libfftw3-dev \ + liblqr-1-0-dev \ + libgsl-dev \ + pkg-config \ + ghostscript - curl -fsSL https://imagemagick.org/archive/ImageMagick.tar.gz -o "$TMP_DIR/ImageMagick.tar.gz" || { - msg_error "Failed to download ImageMagick" + curl -fsSL https://imagemagick.org/archive/ImageMagick.tar.gz -o "$TMP_DIR/ImageMagick.tar.gz" || { + msg_error "Failed to download ImageMagick" + rm -rf "$TMP_DIR" + return 1 + } + + tar -xzf "$TMP_DIR/ImageMagick.tar.gz" -C "$TMP_DIR" || { + msg_error "Failed to extract ImageMagick" + rm -rf "$TMP_DIR" + return 1 + } + + cd "$TMP_DIR"/ImageMagick-* || { + msg_error "Source extraction failed" + rm -rf "$TMP_DIR" + return 1 + } + + $STD ./configure --disable-static || { + msg_error "ImageMagick configure failed" + rm -rf "$TMP_DIR" + return 1 + } + $STD make -j"$(nproc)" || { + msg_error "ImageMagick compilation failed" + rm -rf "$TMP_DIR" + return 1 + } + $STD make install || { + msg_error "ImageMagick installation failed" + rm -rf "$TMP_DIR" + return 1 + } + $STD ldconfig /usr/local/lib + + if [[ ! -x "$BINARY_PATH" ]]; then + msg_error "ImageMagick installation failed" + rm -rf "$TMP_DIR" + return 1 + fi + + local FINAL_VERSION + FINAL_VERSION=$("$BINARY_PATH" -version | awk '/^Version/ {print $3}') rm -rf "$TMP_DIR" - return 1 - } + cache_installed_version "imagemagick" "$FINAL_VERSION" + ensure_usr_local_bin_persist - tar -xzf "$TMP_DIR/ImageMagick.tar.gz" -C "$TMP_DIR" || { - msg_error "Failed to extract ImageMagick" - rm -rf "$TMP_DIR" - return 1 - } - - cd "$TMP_DIR"/ImageMagick-* || { - msg_error "Source extraction failed" - rm -rf "$TMP_DIR" - return 1 - } - - $STD ./configure --disable-static || { - msg_error "ImageMagick configure failed" - rm -rf "$TMP_DIR" - return 1 - } - $STD make -j"$(nproc)" || { - msg_error "ImageMagick compilation failed" - rm -rf "$TMP_DIR" - return 1 - } - $STD make install || { - msg_error "ImageMagick installation failed" - rm -rf "$TMP_DIR" - return 1 - } - $STD ldconfig /usr/local/lib - - if [[ ! -x "$BINARY_PATH" ]]; then - msg_error "ImageMagick installation failed" - rm -rf "$TMP_DIR" - return 1 - fi - - local FINAL_VERSION - FINAL_VERSION=$("$BINARY_PATH" -version | awk '/^Version/ {print $3}') - rm -rf "$TMP_DIR" - cache_installed_version "imagemagick" "$FINAL_VERSION" - ensure_usr_local_bin_persist - - if [[ -n "$INSTALLED_VERSION" ]]; then - msg_ok "Upgrade ImageMagick $INSTALLED_VERSION → $FINAL_VERSION" - else - msg_ok "Setup ImageMagick $FINAL_VERSION" - fi + if [[ -n "$INSTALLED_VERSION" ]]; then + msg_ok "Upgrade ImageMagick $INSTALLED_VERSION → $FINAL_VERSION" + else + msg_ok "Setup ImageMagick $FINAL_VERSION" + fi } # ------------------------------------------------------------------------------ @@ -2509,74 +2509,74 @@ function setup_imagemagick() { # ------------------------------------------------------------------------------ function setup_java() { - local JAVA_VERSION="${JAVA_VERSION:-21}" - local DISTRO_ID DISTRO_CODENAME - DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') - DISTRO_CODENAME=$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release) - local DESIRED_PACKAGE="temurin-${JAVA_VERSION}-jdk" + local JAVA_VERSION="${JAVA_VERSION:-21}" + local DISTRO_ID DISTRO_CODENAME + DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + DISTRO_CODENAME=$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release) + local DESIRED_PACKAGE="temurin-${JAVA_VERSION}-jdk" - # Add repo if needed - if [[ ! -f /etc/apt/sources.list.d/adoptium.sources ]]; then - cleanup_old_repo_files "adoptium" - local SUITE - SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://packages.adoptium.net/artifactory/deb") - setup_deb822_repo \ - "adoptium" \ - "https://packages.adoptium.net/artifactory/api/gpg/key/public" \ - "https://packages.adoptium.net/artifactory/deb" \ - "$SUITE" \ - "main" \ - "amd64 arm64" - fi + # Add repo if needed + if [[ ! -f /etc/apt/sources.list.d/adoptium.sources ]]; then + cleanup_old_repo_files "adoptium" + local SUITE + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://packages.adoptium.net/artifactory/deb") + setup_deb822_repo \ + "adoptium" \ + "https://packages.adoptium.net/artifactory/api/gpg/key/public" \ + "https://packages.adoptium.net/artifactory/deb" \ + "$SUITE" \ + "main" \ + "amd64 arm64" + fi - # Get currently installed version - local INSTALLED_VERSION="" - if dpkg -l | grep -q "temurin-.*-jdk" 2>/dev/null; then - INSTALLED_VERSION=$(dpkg -l 2>/dev/null | awk '/temurin-.*-jdk/{print $2}' | grep -oP 'temurin-\K[0-9]+' | head -n1 || echo "") - fi + # Get currently installed version + local INSTALLED_VERSION="" + if dpkg -l | grep -q "temurin-.*-jdk" 2>/dev/null; then + INSTALLED_VERSION=$(dpkg -l 2>/dev/null | awk '/temurin-.*-jdk/{print $2}' | grep -oP 'temurin-\K[0-9]+' | head -n1 || echo "") + fi - # Validate INSTALLED_VERSION is not empty if matched - local JDK_COUNT=$(dpkg -l 2>/dev/null | grep -c "temurin-.*-jdk" || echo "0") - if [[ -z "$INSTALLED_VERSION" && "$JDK_COUNT" -gt 0 ]]; then - msg_warn "Found Temurin JDK but cannot determine version" - INSTALLED_VERSION="0" - fi + # Validate INSTALLED_VERSION is not empty if matched + local JDK_COUNT=$(dpkg -l 2>/dev/null | grep -c "temurin-.*-jdk" || echo "0") + if [[ -z "$INSTALLED_VERSION" && "$JDK_COUNT" -gt 0 ]]; then + msg_warn "Found Temurin JDK but cannot determine version" + INSTALLED_VERSION="0" + fi + + # Scenario 1: Already at correct version + if [[ "$INSTALLED_VERSION" == "$JAVA_VERSION" ]]; then + msg_info "Update Temurin JDK $JAVA_VERSION" + $STD apt update || { + msg_error "APT update failed" + return 1 + } + $STD apt install --only-upgrade -y "$DESIRED_PACKAGE" || { + msg_error "Failed to update Temurin JDK" + return 1 + } + cache_installed_version "temurin-jdk" "$JAVA_VERSION" + msg_ok "Update Temurin JDK $JAVA_VERSION" + return 0 + fi + + # Scenario 2: Different version - remove old and install new + if [[ -n "$INSTALLED_VERSION" ]]; then + msg_info "Upgrade Temurin JDK from $INSTALLED_VERSION to $JAVA_VERSION" + $STD apt purge -y "temurin-${INSTALLED_VERSION}-jdk" || true + else + msg_info "Setup Temurin JDK $JAVA_VERSION" + fi - # Scenario 1: Already at correct version - if [[ "$INSTALLED_VERSION" == "$JAVA_VERSION" ]]; then - msg_info "Update Temurin JDK $JAVA_VERSION" $STD apt update || { - msg_error "APT update failed" - return 1 + msg_error "APT update failed" + return 1 } - $STD apt install --only-upgrade -y "$DESIRED_PACKAGE" || { - msg_error "Failed to update Temurin JDK" - return 1 + $STD apt install -y "$DESIRED_PACKAGE" || { + msg_error "Failed to install Temurin JDK $JAVA_VERSION" + return 1 } + cache_installed_version "temurin-jdk" "$JAVA_VERSION" - msg_ok "Update Temurin JDK $JAVA_VERSION" - return 0 - fi - - # Scenario 2: Different version - remove old and install new - if [[ -n "$INSTALLED_VERSION" ]]; then - msg_info "Upgrade Temurin JDK from $INSTALLED_VERSION to $JAVA_VERSION" - $STD apt purge -y "temurin-${INSTALLED_VERSION}-jdk" || true - else - msg_info "Setup Temurin JDK $JAVA_VERSION" - fi - - $STD apt update || { - msg_error "APT update failed" - return 1 - } - $STD apt install -y "$DESIRED_PACKAGE" || { - msg_error "Failed to install Temurin JDK $JAVA_VERSION" - return 1 - } - - cache_installed_version "temurin-jdk" "$JAVA_VERSION" - msg_ok "Setup Temurin JDK $JAVA_VERSION" + msg_ok "Setup Temurin JDK $JAVA_VERSION" } # ------------------------------------------------------------------------------ @@ -2588,36 +2588,36 @@ function setup_java() { # ------------------------------------------------------------------------------ function setup_local_ip_helper() { - local BASE_DIR="/usr/local/community-scripts/ip-management" - local SCRIPT_PATH="$BASE_DIR/update_local_ip.sh" - local IP_FILE="/run/local-ip.env" - local DISPATCHER_SCRIPT="/etc/networkd-dispatcher/routable.d/10-update-local-ip.sh" + local BASE_DIR="/usr/local/community-scripts/ip-management" + local SCRIPT_PATH="$BASE_DIR/update_local_ip.sh" + local IP_FILE="/run/local-ip.env" + local DISPATCHER_SCRIPT="/etc/networkd-dispatcher/routable.d/10-update-local-ip.sh" - # Check if already set up - if [[ -f "$SCRIPT_PATH" && -f "$DISPATCHER_SCRIPT" ]]; then - msg_info "Update Local IP Helper" - cache_installed_version "local-ip-helper" "1.0" - msg_ok "Update Local IP Helper" - else - msg_info "Setup Local IP Helper" - fi + # Check if already set up + if [[ -f "$SCRIPT_PATH" && -f "$DISPATCHER_SCRIPT" ]]; then + msg_info "Update Local IP Helper" + cache_installed_version "local-ip-helper" "1.0" + msg_ok "Update Local IP Helper" + else + msg_info "Setup Local IP Helper" + fi - mkdir -p "$BASE_DIR" + mkdir -p "$BASE_DIR" - # Install networkd-dispatcher if not present - if ! dpkg -s networkd-dispatcher >/dev/null 2>&1; then - $STD apt update || { - msg_error "Failed to update package list" - return 1 - } - $STD apt install -y networkd-dispatcher || { - msg_error "Failed to install networkd-dispatcher" - return 1 - } - fi + # Install networkd-dispatcher if not present + if ! dpkg -s networkd-dispatcher >/dev/null 2>&1; then + $STD apt update || { + msg_error "Failed to update package list" + return 1 + } + $STD apt install -y networkd-dispatcher || { + msg_error "Failed to install networkd-dispatcher" + return 1 + } + fi - # Write update_local_ip.sh - cat <<'EOF' >"$SCRIPT_PATH" + # Write update_local_ip.sh + cat <<'EOF' >"$SCRIPT_PATH" #!/bin/bash set -euo pipefail @@ -2659,22 +2659,22 @@ echo "LOCAL_IP=$current_ip" > "$IP_FILE" echo "[INFO] LOCAL_IP updated to $current_ip" EOF - chmod +x "$SCRIPT_PATH" + chmod +x "$SCRIPT_PATH" - # Install dispatcher hook - mkdir -p "$(dirname "$DISPATCHER_SCRIPT")" - cat <"$DISPATCHER_SCRIPT" + # Install dispatcher hook + mkdir -p "$(dirname "$DISPATCHER_SCRIPT")" + cat <"$DISPATCHER_SCRIPT" #!/bin/bash $SCRIPT_PATH EOF - chmod +x "$DISPATCHER_SCRIPT" - systemctl enable -q --now networkd-dispatcher.service || { - msg_warn "Failed to enable networkd-dispatcher service" - } + chmod +x "$DISPATCHER_SCRIPT" + systemctl enable -q --now networkd-dispatcher.service || { + msg_warn "Failed to enable networkd-dispatcher service" + } - cache_installed_version "local-ip-helper" "1.0" - msg_ok "Setup Local IP Helper" + cache_installed_version "local-ip-helper" "1.0" + msg_ok "Setup Local IP Helper" } # ------------------------------------------------------------------------------ @@ -2690,122 +2690,122 @@ EOF # ------------------------------------------------------------------------------ setup_mariadb() { - local MARIADB_VERSION="${MARIADB_VERSION:-latest}" + local MARIADB_VERSION="${MARIADB_VERSION:-latest}" - # Resolve "latest" to actual version - if [[ "$MARIADB_VERSION" == "latest" ]]; then - if ! curl -fsI --max-time 10 http://mirror.mariadb.org/repo/ >/dev/null 2>&1; then - msg_warn "MariaDB mirror not reachable - trying cached package list fallback" - # Fallback: try to use a known stable version - MARIADB_VERSION="12.0" - else - MARIADB_VERSION=$(curl -fsSL --max-time 15 http://mirror.mariadb.org/repo/ 2>/dev/null | - grep -Eo '[0-9]+\.[0-9]+\.[0-9]+/' | - grep -vE 'rc/|rolling/' | - sed 's|/||' | - sort -Vr | - head -n1 || echo "") + # Resolve "latest" to actual version + if [[ "$MARIADB_VERSION" == "latest" ]]; then + if ! curl -fsI --max-time 10 http://mirror.mariadb.org/repo/ >/dev/null 2>&1; then + msg_warn "MariaDB mirror not reachable - trying cached package list fallback" + # Fallback: try to use a known stable version + MARIADB_VERSION="12.0" + else + MARIADB_VERSION=$(curl -fsSL --max-time 15 http://mirror.mariadb.org/repo/ 2>/dev/null | + grep -Eo '[0-9]+\.[0-9]+\.[0-9]+/' | + grep -vE 'rc/|rolling/' | + sed 's|/||' | + sort -Vr | + head -n1 || echo "") - if [[ -z "$MARIADB_VERSION" ]]; then - msg_warn "Could not parse latest GA MariaDB version from mirror - using fallback" - MARIADB_VERSION="12.0" - fi + if [[ -z "$MARIADB_VERSION" ]]; then + msg_warn "Could not parse latest GA MariaDB version from mirror - using fallback" + MARIADB_VERSION="12.0" + fi + fi fi - fi - # Get currently installed version - local CURRENT_VERSION="" - CURRENT_VERSION=$(is_tool_installed "mariadb" 2>/dev/null) || true + # Get currently installed version + local CURRENT_VERSION="" + CURRENT_VERSION=$(is_tool_installed "mariadb" 2>/dev/null) || true - # Scenario 1: Already installed at target version - just update packages - if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$MARIADB_VERSION" ]]; then - msg_info "Update MariaDB $MARIADB_VERSION" + # Scenario 1: Already installed at target version - just update packages + if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$MARIADB_VERSION" ]]; then + msg_info "Update MariaDB $MARIADB_VERSION" - # Ensure APT is working + # Ensure APT is working + ensure_apt_working || return 1 + + # Check if repository needs to be refreshed + if [[ -f /etc/apt/sources.list.d/mariadb.sources ]]; then + local REPO_VERSION="" + REPO_VERSION=$(grep -oP 'repo/\K[0-9]+\.[0-9]+' /etc/apt/sources.list.d/mariadb.sources 2>/dev/null || echo "") + if [[ -n "$REPO_VERSION" && "$REPO_VERSION" != "${MARIADB_VERSION%.*}" ]]; then + msg_warn "Repository version mismatch, updating..." + manage_tool_repository "mariadb" "$MARIADB_VERSION" "http://mirror.mariadb.org/repo/$MARIADB_VERSION" \ + "https://mariadb.org/mariadb_release_signing_key.asc" || { + msg_error "Failed to update MariaDB repository" + return 1 + } + fi + fi + + # Perform upgrade + $STD apt update || { + msg_error "Failed to update package list" + return 1 + } + $STD apt install --only-upgrade -y mariadb-server mariadb-client || { + msg_error "Failed to upgrade MariaDB packages" + return 1 + } + cache_installed_version "mariadb" "$MARIADB_VERSION" + msg_ok "Update MariaDB $MARIADB_VERSION" + return 0 + fi + + # Scenario 2: Different version installed - clean upgrade + if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" != "$MARIADB_VERSION" ]]; then + msg_info "Upgrade MariaDB from $CURRENT_VERSION to $MARIADB_VERSION" + remove_old_tool_version "mariadb" + fi + + # Scenario 3: Fresh install or version change + msg_info "Setup MariaDB $MARIADB_VERSION" + + # Ensure APT is working before proceeding ensure_apt_working || return 1 - # Check if repository needs to be refreshed - if [[ -f /etc/apt/sources.list.d/mariadb.sources ]]; then - local REPO_VERSION="" - REPO_VERSION=$(grep -oP 'repo/\K[0-9]+\.[0-9]+' /etc/apt/sources.list.d/mariadb.sources 2>/dev/null || echo "") - if [[ -n "$REPO_VERSION" && "$REPO_VERSION" != "${MARIADB_VERSION%.*}" ]]; then - msg_warn "Repository version mismatch, updating..." - manage_tool_repository "mariadb" "$MARIADB_VERSION" "http://mirror.mariadb.org/repo/$MARIADB_VERSION" \ - "https://mariadb.org/mariadb_release_signing_key.asc" || { - msg_error "Failed to update MariaDB repository" - return 1 - } - fi + # Install required dependencies first + local mariadb_deps=() + for dep in gawk rsync socat libdbi-perl pv; do + if apt-cache search "^${dep}$" 2>/dev/null | grep -q .; then + mariadb_deps+=("$dep") + fi + done + + if [[ ${#mariadb_deps[@]} -gt 0 ]]; then + $STD apt install -y "${mariadb_deps[@]}" 2>/dev/null || true fi - # Perform upgrade - $STD apt update || { - msg_error "Failed to update package list" - return 1 + # Setup repository + manage_tool_repository "mariadb" "$MARIADB_VERSION" "http://mirror.mariadb.org/repo/$MARIADB_VERSION" \ + "https://mariadb.org/mariadb_release_signing_key.asc" || { + msg_error "Failed to setup MariaDB repository" + return 1 } - $STD apt install --only-upgrade -y mariadb-server mariadb-client || { - msg_error "Failed to upgrade MariaDB packages" - return 1 - } - cache_installed_version "mariadb" "$MARIADB_VERSION" - msg_ok "Update MariaDB $MARIADB_VERSION" - return 0 - fi - # Scenario 2: Different version installed - clean upgrade - if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" != "$MARIADB_VERSION" ]]; then - msg_info "Upgrade MariaDB from $CURRENT_VERSION to $MARIADB_VERSION" - remove_old_tool_version "mariadb" - fi - - # Scenario 3: Fresh install or version change - msg_info "Setup MariaDB $MARIADB_VERSION" - - # Ensure APT is working before proceeding - ensure_apt_working || return 1 - - # Install required dependencies first - local mariadb_deps=() - for dep in gawk rsync socat libdbi-perl pv; do - if apt-cache search "^${dep}$" 2>/dev/null | grep -q .; then - mariadb_deps+=("$dep") + # Set debconf selections for all potential versions + local MARIADB_MAJOR_MINOR + MARIADB_MAJOR_MINOR=$(echo "$MARIADB_VERSION" | awk -F. '{print $1"."$2}') + if [[ -n "$MARIADB_MAJOR_MINOR" ]]; then + echo "mariadb-server-$MARIADB_MAJOR_MINOR mariadb-server/feedback boolean false" | debconf-set-selections fi - done - if [[ ${#mariadb_deps[@]} -gt 0 ]]; then - $STD apt install -y "${mariadb_deps[@]}" 2>/dev/null || true - fi - - # Setup repository - manage_tool_repository "mariadb" "$MARIADB_VERSION" "http://mirror.mariadb.org/repo/$MARIADB_VERSION" \ - "https://mariadb.org/mariadb_release_signing_key.asc" || { - msg_error "Failed to setup MariaDB repository" - return 1 - } - - # Set debconf selections for all potential versions - local MARIADB_MAJOR_MINOR - MARIADB_MAJOR_MINOR=$(echo "$MARIADB_VERSION" | awk -F. '{print $1"."$2}') - if [[ -n "$MARIADB_MAJOR_MINOR" ]]; then - echo "mariadb-server-$MARIADB_MAJOR_MINOR mariadb-server/feedback boolean false" | debconf-set-selections - fi - - # Install packages - DEBIAN_FRONTEND=noninteractive $STD apt install -y mariadb-server mariadb-client || { - # Fallback: try without specific version - msg_warn "Failed to install MariaDB packages from upstream repo, trying distro fallback..." - cleanup_old_repo_files "mariadb" - $STD apt update || { - msg_warn "APT update also failed, continuing with cache" - } + # Install packages DEBIAN_FRONTEND=noninteractive $STD apt install -y mariadb-server mariadb-client || { - msg_error "Failed to install MariaDB packages (both upstream and distro)" - return 1 + # Fallback: try without specific version + msg_warn "Failed to install MariaDB packages from upstream repo, trying distro fallback..." + cleanup_old_repo_files "mariadb" + $STD apt update || { + msg_warn "APT update also failed, continuing with cache" + } + DEBIAN_FRONTEND=noninteractive $STD apt install -y mariadb-server mariadb-client || { + msg_error "Failed to install MariaDB packages (both upstream and distro)" + return 1 + } } - } - cache_installed_version "mariadb" "$MARIADB_VERSION" - msg_ok "Setup MariaDB $MARIADB_VERSION" + cache_installed_version "mariadb" "$MARIADB_VERSION" + msg_ok "Setup MariaDB $MARIADB_VERSION" } # ------------------------------------------------------------------------------ @@ -2820,92 +2820,92 @@ setup_mariadb() { # ------------------------------------------------------------------------------ function setup_mongodb() { - local MONGO_VERSION="${MONGO_VERSION:-8.0}" - local DISTRO_ID DISTRO_CODENAME - DISTRO_ID=$(get_os_info id) - DISTRO_CODENAME=$(get_os_info codename) + local MONGO_VERSION="${MONGO_VERSION:-8.0}" + local DISTRO_ID DISTRO_CODENAME + DISTRO_ID=$(get_os_info id) + DISTRO_CODENAME=$(get_os_info codename) - # Check AVX support - if ! grep -qm1 'avx[^ ]*' /proc/cpuinfo; then - local major="${MONGO_VERSION%%.*}" - if ((major > 5)); then - msg_error "MongoDB ${MONGO_VERSION} requires AVX support, which is not available on this system." - return 1 + # Check AVX support + if ! grep -qm1 'avx[^ ]*' /proc/cpuinfo; then + local major="${MONGO_VERSION%%.*}" + if ((major > 5)); then + msg_error "MongoDB ${MONGO_VERSION} requires AVX support, which is not available on this system." + return 1 + fi fi - fi - case "$DISTRO_ID" in - ubuntu) - MONGO_BASE_URL="https://repo.mongodb.org/apt/ubuntu" - ;; - debian) - MONGO_BASE_URL="https://repo.mongodb.org/apt/debian" - ;; - *) - msg_error "Unsupported distribution: $DISTRO_ID" - return 1 - ;; - esac + case "$DISTRO_ID" in + ubuntu) + MONGO_BASE_URL="https://repo.mongodb.org/apt/ubuntu" + ;; + debian) + MONGO_BASE_URL="https://repo.mongodb.org/apt/debian" + ;; + *) + msg_error "Unsupported distribution: $DISTRO_ID" + return 1 + ;; + esac - # Get currently installed version - local INSTALLED_VERSION="" - INSTALLED_VERSION=$(is_tool_installed "mongodb" 2>/dev/null) || true + # Get currently installed version + local INSTALLED_VERSION="" + INSTALLED_VERSION=$(is_tool_installed "mongodb" 2>/dev/null) || true - # Scenario 1: Already at target version - just update packages - if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" == "$MONGO_VERSION" ]]; then - msg_info "Update MongoDB $MONGO_VERSION" + # Scenario 1: Already at target version - just update packages + if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" == "$MONGO_VERSION" ]]; then + msg_info "Update MongoDB $MONGO_VERSION" - ensure_apt_working || return 1 + ensure_apt_working || return 1 - # Perform upgrade - $STD apt install --only-upgrade -y mongodb-org || { - msg_error "Failed to upgrade MongoDB" - return 1 + # Perform upgrade + $STD apt install --only-upgrade -y mongodb-org || { + msg_error "Failed to upgrade MongoDB" + return 1 + } + cache_installed_version "mongodb" "$MONGO_VERSION" + msg_ok "Update MongoDB $MONGO_VERSION" + return 0 + fi + + # Scenario 2: Different version installed - clean upgrade + if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" != "$MONGO_VERSION" ]]; then + msg_info "Upgrade MongoDB from $INSTALLED_VERSION to $MONGO_VERSION" + remove_old_tool_version "mongodb" + else + msg_info "Setup MongoDB $MONGO_VERSION" + fi + + cleanup_orphaned_sources + + # Setup repository + manage_tool_repository "mongodb" "$MONGO_VERSION" "$MONGO_BASE_URL" \ + "https://www.mongodb.org/static/pgp/server-${MONGO_VERSION}.asc" || { + msg_error "Failed to setup MongoDB repository" + return 1 } + + # Wait for repo to settle + $STD apt update || { + msg_error "APT update failed — invalid MongoDB repo for ${DISTRO_ID}-${DISTRO_CODENAME}?" + return 1 + } + + # Install MongoDB + $STD apt install -y mongodb-org || { + msg_error "Failed to install MongoDB packages" + return 1 + } + + mkdir -p /var/lib/mongodb + chown -R mongodb:mongodb /var/lib/mongodb + + $STD systemctl enable mongod || { + msg_warn "Failed to enable mongod service" + } + safe_service_restart mongod cache_installed_version "mongodb" "$MONGO_VERSION" - msg_ok "Update MongoDB $MONGO_VERSION" - return 0 - fi - # Scenario 2: Different version installed - clean upgrade - if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" != "$MONGO_VERSION" ]]; then - msg_info "Upgrade MongoDB from $INSTALLED_VERSION to $MONGO_VERSION" - remove_old_tool_version "mongodb" - else - msg_info "Setup MongoDB $MONGO_VERSION" - fi - - cleanup_orphaned_sources - - # Setup repository - manage_tool_repository "mongodb" "$MONGO_VERSION" "$MONGO_BASE_URL" \ - "https://www.mongodb.org/static/pgp/server-${MONGO_VERSION}.asc" || { - msg_error "Failed to setup MongoDB repository" - return 1 - } - - # Wait for repo to settle - $STD apt update || { - msg_error "APT update failed — invalid MongoDB repo for ${DISTRO_ID}-${DISTRO_CODENAME}?" - return 1 - } - - # Install MongoDB - $STD apt install -y mongodb-org || { - msg_error "Failed to install MongoDB packages" - return 1 - } - - mkdir -p /var/lib/mongodb - chown -R mongodb:mongodb /var/lib/mongodb - - $STD systemctl enable mongod || { - msg_warn "Failed to enable mongod service" - } - safe_service_restart mongod - cache_installed_version "mongodb" "$MONGO_VERSION" - - msg_ok "Setup MongoDB $MONGO_VERSION" + msg_ok "Setup MongoDB $MONGO_VERSION" } # ------------------------------------------------------------------------------ @@ -2922,48 +2922,48 @@ function setup_mongodb() { # ------------------------------------------------------------------------------ function setup_mysql() { - local MYSQL_VERSION="${MYSQL_VERSION:-8.0}" - local DISTRO_ID DISTRO_CODENAME - DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') - DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + local MYSQL_VERSION="${MYSQL_VERSION:-8.0}" + local DISTRO_ID DISTRO_CODENAME + DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - # Get currently installed version - local CURRENT_VERSION="" - CURRENT_VERSION=$(is_tool_installed "mysql" 2>/dev/null) || true + # Get currently installed version + local CURRENT_VERSION="" + CURRENT_VERSION=$(is_tool_installed "mysql" 2>/dev/null) || true - # Scenario 1: Already at target version - just update packages - if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$MYSQL_VERSION" ]]; then - msg_info "Update MySQL $MYSQL_VERSION" + # Scenario 1: Already at target version - just update packages + if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$MYSQL_VERSION" ]]; then + msg_info "Update MySQL $MYSQL_VERSION" - ensure_apt_working || return 1 + ensure_apt_working || return 1 - $STD apt install --only-upgrade -y mysql-server mysql-client || true + $STD apt install --only-upgrade -y mysql-server mysql-client || true - cache_installed_version "mysql" "$MYSQL_VERSION" - msg_ok "Update MySQL $MYSQL_VERSION" - return 0 - fi - - # Scenario 2: Different version installed - clean upgrade - if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" != "$MYSQL_VERSION" ]]; then - msg_info "Upgrade MySQL from $CURRENT_VERSION to $MYSQL_VERSION" - remove_old_tool_version "mysql" - else - msg_info "Setup MySQL $MYSQL_VERSION" - fi - - # Debian 13+ Fix: MySQL 8.0 incompatible with libaio1t64, use 8.4 LTS - if [[ "$DISTRO_ID" == "debian" && "$DISTRO_CODENAME" =~ ^(trixie|forky|sid)$ ]]; then - msg_info "Debian ${DISTRO_CODENAME} detected → using MySQL 8.4 LTS (libaio1t64 compatible)" - - cleanup_old_repo_files "mysql" - - if ! curl -fsSL https://repo.mysql.com/RPM-GPG-KEY-mysql-2023 | gpg --dearmor -o /etc/apt/keyrings/mysql.gpg 2>/dev/null; then - msg_error "Failed to import MySQL GPG key" - return 1 + cache_installed_version "mysql" "$MYSQL_VERSION" + msg_ok "Update MySQL $MYSQL_VERSION" + return 0 fi - cat >/etc/apt/sources.list.d/mysql.sources <<'EOF' + # Scenario 2: Different version installed - clean upgrade + if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" != "$MYSQL_VERSION" ]]; then + msg_info "Upgrade MySQL from $CURRENT_VERSION to $MYSQL_VERSION" + remove_old_tool_version "mysql" + else + msg_info "Setup MySQL $MYSQL_VERSION" + fi + + # Debian 13+ Fix: MySQL 8.0 incompatible with libaio1t64, use 8.4 LTS + if [[ "$DISTRO_ID" == "debian" && "$DISTRO_CODENAME" =~ ^(trixie|forky|sid)$ ]]; then + msg_info "Debian ${DISTRO_CODENAME} detected → using MySQL 8.4 LTS (libaio1t64 compatible)" + + cleanup_old_repo_files "mysql" + + if ! curl -fsSL https://repo.mysql.com/RPM-GPG-KEY-mysql-2023 | gpg --dearmor -o /etc/apt/keyrings/mysql.gpg 2>/dev/null; then + msg_error "Failed to import MySQL GPG key" + return 1 + fi + + cat >/etc/apt/sources.list.d/mysql.sources <<'EOF' Types: deb URIs: https://repo.mysql.com/apt/debian/ Suites: bookworm @@ -2972,79 +2972,79 @@ Architectures: amd64 arm64 Signed-By: /etc/apt/keyrings/mysql.gpg EOF - $STD apt update || { - msg_error "Failed to update APT for MySQL 8.4 LTS" - return 1 + $STD apt update || { + msg_error "Failed to update APT for MySQL 8.4 LTS" + return 1 + } + + if ! $STD apt install -y mysql-community-server mysql-community-client; then + msg_warn "MySQL 8.4 LTS installation failed – falling back to MariaDB" + cleanup_old_repo_files "mysql" + $STD apt update + $STD apt install -y mariadb-server mariadb-client || { + msg_error "Failed to install database engine (MySQL/MariaDB fallback)" + return 1 + } + msg_ok "Setup Database Engine (MariaDB fallback on Debian ${DISTRO_CODENAME})" + return 0 + fi + + cache_installed_version "mysql" "8.4" + msg_ok "Setup MySQL 8.4 LTS (Debian ${DISTRO_CODENAME})" + return 0 + fi + + # Standard setup for other distributions + local SUITE + if [[ "$DISTRO_ID" == "debian" ]]; then + case "$DISTRO_CODENAME" in + bookworm | bullseye) SUITE="$DISTRO_CODENAME" ;; + *) SUITE="bookworm" ;; + esac + else + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://repo.mysql.com/apt/${DISTRO_ID}") + fi + + # Setup repository + manage_tool_repository "mysql" "$MYSQL_VERSION" "https://repo.mysql.com/apt/${DISTRO_ID}" \ + "https://repo.mysql.com/RPM-GPG-KEY-mysql-2023" || { + msg_error "Failed to setup MySQL repository" + return 1 } - if ! $STD apt install -y mysql-community-server mysql-community-client; then - msg_warn "MySQL 8.4 LTS installation failed – falling back to MariaDB" - cleanup_old_repo_files "mysql" - $STD apt update - $STD apt install -y mariadb-server mariadb-client || { - msg_error "Failed to install database engine (MySQL/MariaDB fallback)" + ensure_apt_working || return 1 + + # Try multiple package names (mysql-server, mysql-community-server, mysql) + export DEBIAN_FRONTEND=noninteractive + local mysql_install_success=false + + if apt-cache search "^mysql-server$" 2>/dev/null | grep -q . && + $STD apt install -y mysql-server mysql-client 2>/dev/null; then + mysql_install_success=true + elif apt-cache search "^mysql-community-server$" 2>/dev/null | grep -q . && + $STD apt install -y mysql-community-server mysql-community-client 2>/dev/null; then + mysql_install_success=true + elif apt-cache search "^mysql$" 2>/dev/null | grep -q . && + $STD apt install -y mysql 2>/dev/null; then + mysql_install_success=true + fi + + if [[ "$mysql_install_success" == false ]]; then + msg_error "MySQL ${MYSQL_VERSION} package not available for suite ${SUITE}" return 1 - } - msg_ok "Setup Database Engine (MariaDB fallback on Debian ${DISTRO_CODENAME})" - return 0 fi - cache_installed_version "mysql" "8.4" - msg_ok "Setup MySQL 8.4 LTS (Debian ${DISTRO_CODENAME})" - return 0 - fi - - # Standard setup for other distributions - local SUITE - if [[ "$DISTRO_ID" == "debian" ]]; then - case "$DISTRO_CODENAME" in - bookworm | bullseye) SUITE="$DISTRO_CODENAME" ;; - *) SUITE="bookworm" ;; - esac - else - SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://repo.mysql.com/apt/${DISTRO_ID}") - fi - - # Setup repository - manage_tool_repository "mysql" "$MYSQL_VERSION" "https://repo.mysql.com/apt/${DISTRO_ID}" \ - "https://repo.mysql.com/RPM-GPG-KEY-mysql-2023" || { - msg_error "Failed to setup MySQL repository" - return 1 - } - - ensure_apt_working || return 1 - - # Try multiple package names (mysql-server, mysql-community-server, mysql) - export DEBIAN_FRONTEND=noninteractive - local mysql_install_success=false - - if apt-cache search "^mysql-server$" 2>/dev/null | grep -q . && - $STD apt install -y mysql-server mysql-client 2>/dev/null; then - mysql_install_success=true - elif apt-cache search "^mysql-community-server$" 2>/dev/null | grep -q . && - $STD apt install -y mysql-community-server mysql-community-client 2>/dev/null; then - mysql_install_success=true - elif apt-cache search "^mysql$" 2>/dev/null | grep -q . && - $STD apt install -y mysql 2>/dev/null; then - mysql_install_success=true - fi - - if [[ "$mysql_install_success" == false ]]; then - msg_error "MySQL ${MYSQL_VERSION} package not available for suite ${SUITE}" - return 1 - fi - - # Verify mysql command is accessible - if ! command -v mysql >/dev/null 2>&1; then - hash -r + # Verify mysql command is accessible if ! command -v mysql >/dev/null 2>&1; then - msg_error "MySQL installed but mysql command still not found" - return 1 + hash -r + if ! command -v mysql >/dev/null 2>&1; then + msg_error "MySQL installed but mysql command still not found" + return 1 + fi fi - fi - cache_installed_version "mysql" "$MYSQL_VERSION" - msg_ok "Setup MySQL $MYSQL_VERSION" + cache_installed_version "mysql" "$MYSQL_VERSION" + msg_ok "Setup MySQL $MYSQL_VERSION" } # ------------------------------------------------------------------------------ @@ -3060,142 +3060,142 @@ EOF # ------------------------------------------------------------------------------ function setup_nodejs() { - local NODE_VERSION="${NODE_VERSION:-22}" - local NODE_MODULE="${NODE_MODULE:-}" + local NODE_VERSION="${NODE_VERSION:-22}" + local NODE_MODULE="${NODE_MODULE:-}" - # Get currently installed version - local CURRENT_NODE_VERSION="" - CURRENT_NODE_VERSION=$(is_tool_installed "nodejs" 2>/dev/null) || true + # Get currently installed version + local CURRENT_NODE_VERSION="" + CURRENT_NODE_VERSION=$(is_tool_installed "nodejs" 2>/dev/null) || true - # Ensure jq is available for JSON parsing - if ! command -v jq &>/dev/null; then - $STD apt update - $STD apt install -y jq || { - msg_error "Failed to install jq" - return 1 - } - fi - - # Scenario 1: Already installed at target version - just update packages/modules - if [[ -n "$CURRENT_NODE_VERSION" && "$CURRENT_NODE_VERSION" == "$NODE_VERSION" ]]; then - msg_info "Update Node.js $NODE_VERSION" - - ensure_apt_working || return 1 - - # Just update npm to latest - $STD npm install -g npm@latest 2>/dev/null || true - - cache_installed_version "nodejs" "$NODE_VERSION" - msg_ok "Update Node.js $NODE_VERSION" - else - # Scenario 2: Different version installed - clean upgrade - if [[ -n "$CURRENT_NODE_VERSION" && "$CURRENT_NODE_VERSION" != "$NODE_VERSION" ]]; then - msg_info "Upgrade Node.js from $CURRENT_NODE_VERSION to $NODE_VERSION" - remove_old_tool_version "nodejs" - else - msg_info "Setup Node.js $NODE_VERSION" + # Ensure jq is available for JSON parsing + if ! command -v jq &>/dev/null; then + $STD apt update + $STD apt install -y jq || { + msg_error "Failed to install jq" + return 1 + } fi - ensure_dependencies curl ca-certificates gnupg + # Scenario 1: Already installed at target version - just update packages/modules + if [[ -n "$CURRENT_NODE_VERSION" && "$CURRENT_NODE_VERSION" == "$NODE_VERSION" ]]; then + msg_info "Update Node.js $NODE_VERSION" - # Setup repository - manage_tool_repository "nodejs" "$NODE_VERSION" "https://deb.nodesource.com/node_${NODE_VERSION}.x" "https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key" || { - msg_error "Failed to setup Node.js repository" - return 1 - } + ensure_apt_working || return 1 - # Wait for repo to settle - sleep 2 + # Just update npm to latest + $STD npm install -g npm@latest 2>/dev/null || true - # Install Node.js - if ! apt update >/dev/null 2>&1; then - msg_warn "APT update failed – retrying in 5s" - sleep 5 - if ! apt update >/dev/null 2>&1; then - msg_error "Failed to update APT repositories after adding NodeSource" + cache_installed_version "nodejs" "$NODE_VERSION" + msg_ok "Update Node.js $NODE_VERSION" + else + # Scenario 2: Different version installed - clean upgrade + if [[ -n "$CURRENT_NODE_VERSION" && "$CURRENT_NODE_VERSION" != "$NODE_VERSION" ]]; then + msg_info "Upgrade Node.js from $CURRENT_NODE_VERSION to $NODE_VERSION" + remove_old_tool_version "nodejs" + else + msg_info "Setup Node.js $NODE_VERSION" + fi + + ensure_dependencies curl ca-certificates gnupg + + # Setup repository + manage_tool_repository "nodejs" "$NODE_VERSION" "https://deb.nodesource.com/node_${NODE_VERSION}.x" "https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key" || { + msg_error "Failed to setup Node.js repository" + return 1 + } + + # Wait for repo to settle + sleep 2 + + # Install Node.js + if ! $STD apt update; then + msg_warn "APT update failed – retrying in 5s" + sleep 5 + if ! $STD apt update; then + msg_error "Failed to update APT repositories after adding NodeSource" + return 1 + fi + fi + + if ! $STD apt install -y nodejs; then + msg_error "Failed to install Node.js ${NODE_VERSION} from NodeSource" + return 1 + fi + + # Update to latest npm + $STD npm install -g npm@latest || { + msg_error "Failed to update npm to latest version" + return 1 + } + + cache_installed_version "nodejs" "$NODE_VERSION" + msg_ok "Setup Node.js $NODE_VERSION" + fi + + export NODE_OPTIONS="--max-old-space-size=4096" + + # Ensure valid working directory for npm (avoids uv_cwd error) + if [[ ! -d /opt ]]; then + mkdir -p /opt + fi + cd /opt || { + msg_error "Failed to set safe working directory before npm install" return 1 - fi - fi - - if ! apt install -y nodejs >/dev/null 2>&1; then - msg_error "Failed to install Node.js ${NODE_VERSION} from NodeSource" - return 1 - fi - - # Update to latest npm - $STD npm install -g npm@latest || { - msg_error "Failed to update npm to latest version" - return 1 } - cache_installed_version "nodejs" "$NODE_VERSION" - msg_ok "Setup Node.js $NODE_VERSION" - fi + # Install global Node modules + if [[ -n "$NODE_MODULE" ]]; then + IFS=',' read -ra MODULES <<<"$NODE_MODULE" + local failed_modules=0 + for mod in "${MODULES[@]}"; do + local MODULE_NAME MODULE_REQ_VERSION MODULE_INSTALLED_VERSION + if [[ "$mod" == @*/*@* ]]; then + # Scoped package with version, e.g. @vue/cli-service@latest + MODULE_NAME="${mod%@*}" + MODULE_REQ_VERSION="${mod##*@}" + elif [[ "$mod" == *"@"* ]]; then + # Unscoped package with version, e.g. yarn@latest + MODULE_NAME="${mod%@*}" + MODULE_REQ_VERSION="${mod##*@}" + else + # No version specified + MODULE_NAME="$mod" + MODULE_REQ_VERSION="latest" + fi - export NODE_OPTIONS="--max-old-space-size=4096" - - # Ensure valid working directory for npm (avoids uv_cwd error) - if [[ ! -d /opt ]]; then - mkdir -p /opt - fi - cd /opt || { - msg_error "Failed to set safe working directory before npm install" - return 1 - } - - # Install global Node modules - if [[ -n "$NODE_MODULE" ]]; then - IFS=',' read -ra MODULES <<<"$NODE_MODULE" - local failed_modules=0 - for mod in "${MODULES[@]}"; do - local MODULE_NAME MODULE_REQ_VERSION MODULE_INSTALLED_VERSION - if [[ "$mod" == @*/*@* ]]; then - # Scoped package with version, e.g. @vue/cli-service@latest - MODULE_NAME="${mod%@*}" - MODULE_REQ_VERSION="${mod##*@}" - elif [[ "$mod" == *"@"* ]]; then - # Unscoped package with version, e.g. yarn@latest - MODULE_NAME="${mod%@*}" - MODULE_REQ_VERSION="${mod##*@}" - else - # No version specified - MODULE_NAME="$mod" - MODULE_REQ_VERSION="latest" - fi - - # Check if the module is already installed - if npm list -g --depth=0 "$MODULE_NAME" >/dev/null 2>&1; then - MODULE_INSTALLED_VERSION="$(npm list -g --depth=0 "$MODULE_NAME" | grep "$MODULE_NAME@" | awk -F@ '{print $2}' | tr -d '[:space:]')" - if [[ "$MODULE_REQ_VERSION" != "latest" && "$MODULE_REQ_VERSION" != "$MODULE_INSTALLED_VERSION" ]]; then - msg_info "Updating $MODULE_NAME from v$MODULE_INSTALLED_VERSION to v$MODULE_REQ_VERSION" - if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}" 2>/dev/null; then - msg_warn "Failed to update $MODULE_NAME to version $MODULE_REQ_VERSION" - ((failed_modules++)) - continue - fi - elif [[ "$MODULE_REQ_VERSION" == "latest" ]]; then - msg_info "Updating $MODULE_NAME to latest version" - if ! $STD npm install -g "${MODULE_NAME}@latest" 2>/dev/null; then - msg_warn "Failed to update $MODULE_NAME to latest version" - ((failed_modules++)) - continue - fi + # Check if the module is already installed + if npm list -g --depth=0 "$MODULE_NAME" >/dev/null 2>&1; then + MODULE_INSTALLED_VERSION="$(npm list -g --depth=0 "$MODULE_NAME" | grep "$MODULE_NAME@" | awk -F@ '{print $2}' | tr -d '[:space:]')" + if [[ "$MODULE_REQ_VERSION" != "latest" && "$MODULE_REQ_VERSION" != "$MODULE_INSTALLED_VERSION" ]]; then + msg_info "Updating $MODULE_NAME from v$MODULE_INSTALLED_VERSION to v$MODULE_REQ_VERSION" + if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}" 2>/dev/null; then + msg_warn "Failed to update $MODULE_NAME to version $MODULE_REQ_VERSION" + ((failed_modules++)) + continue + fi + elif [[ "$MODULE_REQ_VERSION" == "latest" ]]; then + msg_info "Updating $MODULE_NAME to latest version" + if ! $STD npm install -g "${MODULE_NAME}@latest" 2>/dev/null; then + msg_warn "Failed to update $MODULE_NAME to latest version" + ((failed_modules++)) + continue + fi + fi + else + msg_info "Installing $MODULE_NAME@$MODULE_REQ_VERSION" + if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}" 2>/dev/null; then + msg_warn "Failed to install $MODULE_NAME@$MODULE_REQ_VERSION" + ((failed_modules++)) + continue + fi + fi + done + if [[ $failed_modules -eq 0 ]]; then + msg_ok "Installed Node.js modules: $NODE_MODULE" + else + msg_warn "Installed Node.js modules with $failed_modules failure(s): $NODE_MODULE" fi - else - msg_info "Installing $MODULE_NAME@$MODULE_REQ_VERSION" - if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}" 2>/dev/null; then - msg_warn "Failed to install $MODULE_NAME@$MODULE_REQ_VERSION" - ((failed_modules++)) - continue - fi - fi - done - if [[ $failed_modules -eq 0 ]]; then - msg_ok "Installed Node.js modules: $NODE_MODULE" - else - msg_warn "Installed Node.js modules with $failed_modules failure(s): $NODE_MODULE" fi - fi } # ------------------------------------------------------------------------------ @@ -3218,139 +3218,139 @@ function setup_nodejs() { # ------------------------------------------------------------------------------ function setup_php() { - local PHP_VERSION="${PHP_VERSION:-8.4}" - local PHP_MODULE="${PHP_MODULE:-}" - local PHP_APACHE="${PHP_APACHE:-NO}" - local PHP_FPM="${PHP_FPM:-NO}" - local DISTRO_ID DISTRO_CODENAME - DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') - DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + local PHP_VERSION="${PHP_VERSION:-8.4}" + local PHP_MODULE="${PHP_MODULE:-}" + local PHP_APACHE="${PHP_APACHE:-NO}" + local PHP_FPM="${PHP_FPM:-NO}" + local DISTRO_ID DISTRO_CODENAME + DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - local DEFAULT_MODULES="bcmath,cli,curl,gd,intl,mbstring,opcache,readline,xml,zip" - local COMBINED_MODULES + local DEFAULT_MODULES="bcmath,cli,curl,gd,intl,mbstring,opcache,readline,xml,zip" + local COMBINED_MODULES - local PHP_MEMORY_LIMIT="${PHP_MEMORY_LIMIT:-512M}" - local PHP_UPLOAD_MAX_FILESIZE="${PHP_UPLOAD_MAX_FILESIZE:-128M}" - local PHP_POST_MAX_SIZE="${PHP_POST_MAX_SIZE:-128M}" - local PHP_MAX_EXECUTION_TIME="${PHP_MAX_EXECUTION_TIME:-300}" + local PHP_MEMORY_LIMIT="${PHP_MEMORY_LIMIT:-512M}" + local PHP_UPLOAD_MAX_FILESIZE="${PHP_UPLOAD_MAX_FILESIZE:-128M}" + local PHP_POST_MAX_SIZE="${PHP_POST_MAX_SIZE:-128M}" + local PHP_MAX_EXECUTION_TIME="${PHP_MAX_EXECUTION_TIME:-300}" - # Merge default + user-defined modules - if [[ -n "$PHP_MODULE" ]]; then - COMBINED_MODULES="${DEFAULT_MODULES},${PHP_MODULE}" - else - COMBINED_MODULES="${DEFAULT_MODULES}" - fi - - # Deduplicate - COMBINED_MODULES=$(echo "$COMBINED_MODULES" | tr ',' '\n' | awk '!seen[$0]++' | paste -sd, -) - - # Get current PHP-CLI version - local CURRENT_PHP="" - CURRENT_PHP=$(is_tool_installed "php" 2>/dev/null) || true - - # Scenario 1: Already at target version - just update packages - if [[ -n "$CURRENT_PHP" && "$CURRENT_PHP" == "$PHP_VERSION" ]]; then - msg_info "Update PHP $PHP_VERSION" - - # Ensure Sury repo is available - if [[ ! -f /etc/apt/sources.list.d/php.sources ]]; then - manage_tool_repository "php" "$PHP_VERSION" "" "https://packages.sury.org/debsuryorg-archive-keyring.deb" || { - msg_error "Failed to setup PHP repository" - return 1 - } - fi - - ensure_apt_working || return 1 - - # Just update PHP packages - $STD apt install --only-upgrade -y "php${PHP_VERSION}" || true - - cache_installed_version "php" "$PHP_VERSION" - msg_ok "Update PHP $PHP_VERSION" - else - # Scenario 2: Different version installed - clean upgrade - if [[ -n "$CURRENT_PHP" && "$CURRENT_PHP" != "$PHP_VERSION" ]]; then - msg_info "Upgrade PHP from $CURRENT_PHP to $PHP_VERSION" - # Stop old PHP-FPM if running - $STD systemctl stop "php${CURRENT_PHP}-fpm" >/dev/null 2>&1 || true - $STD systemctl disable "php${CURRENT_PHP}-fpm" >/dev/null 2>&1 || true - remove_old_tool_version "php" + # Merge default + user-defined modules + if [[ -n "$PHP_MODULE" ]]; then + COMBINED_MODULES="${DEFAULT_MODULES},${PHP_MODULE}" else - msg_info "Setup PHP $PHP_VERSION" + COMBINED_MODULES="${DEFAULT_MODULES}" fi - # Setup Sury repository - manage_tool_repository "php" "$PHP_VERSION" "" "https://packages.sury.org/debsuryorg-archive-keyring.deb" || { - msg_error "Failed to setup PHP repository" - return 1 - } + # Deduplicate + COMBINED_MODULES=$(echo "$COMBINED_MODULES" | tr ',' '\n' | awk '!seen[$0]++' | paste -sd, -) - ensure_apt_working || return 1 - fi + # Get current PHP-CLI version + local CURRENT_PHP="" + CURRENT_PHP=$(is_tool_installed "php" 2>/dev/null) || true - # Build module list - local MODULE_LIST="php${PHP_VERSION}" - IFS=',' read -ra MODULES <<<"$COMBINED_MODULES" - for mod in "${MODULES[@]}"; do - if apt-cache show "php${PHP_VERSION}-${mod}" >/dev/null 2>&1; then - MODULE_LIST+=" php${PHP_VERSION}-${mod}" + # Scenario 1: Already at target version - just update packages + if [[ -n "$CURRENT_PHP" && "$CURRENT_PHP" == "$PHP_VERSION" ]]; then + msg_info "Update PHP $PHP_VERSION" + + # Ensure Sury repo is available + if [[ ! -f /etc/apt/sources.list.d/php.sources ]]; then + manage_tool_repository "php" "$PHP_VERSION" "" "https://packages.sury.org/debsuryorg-archive-keyring.deb" || { + msg_error "Failed to setup PHP repository" + return 1 + } + fi + + ensure_apt_working || return 1 + + # Just update PHP packages + $STD apt install --only-upgrade -y "php${PHP_VERSION}" || true + + cache_installed_version "php" "$PHP_VERSION" + msg_ok "Update PHP $PHP_VERSION" + else + # Scenario 2: Different version installed - clean upgrade + if [[ -n "$CURRENT_PHP" && "$CURRENT_PHP" != "$PHP_VERSION" ]]; then + msg_info "Upgrade PHP from $CURRENT_PHP to $PHP_VERSION" + # Stop old PHP-FPM if running + $STD systemctl stop "php${CURRENT_PHP}-fpm" >/dev/null 2>&1 || true + $STD systemctl disable "php${CURRENT_PHP}-fpm" >/dev/null 2>&1 || true + remove_old_tool_version "php" + else + msg_info "Setup PHP $PHP_VERSION" + fi + + # Setup Sury repository + manage_tool_repository "php" "$PHP_VERSION" "" "https://packages.sury.org/debsuryorg-archive-keyring.deb" || { + msg_error "Failed to setup PHP repository" + return 1 + } + + ensure_apt_working || return 1 fi - done - if [[ "$PHP_FPM" == "YES" ]]; then - MODULE_LIST+=" php${PHP_VERSION}-fpm" - fi - # install apache2 with PHP support if requested - if [[ "$PHP_APACHE" == "YES" ]]; then - if ! dpkg -l 2>/dev/null | grep -q "libapache2-mod-php${PHP_VERSION}"; then - $STD apt install -y apache2 libapache2-mod-php${PHP_VERSION} || { - msg_error "Failed to install Apache with PHP module" - return 1 - } - fi - fi - - # Install PHP packages - $STD apt install -y $MODULE_LIST || { - msg_error "Failed to install PHP packages" - return 1 - } - cache_installed_version "php" "$PHP_VERSION" - - # Patch all relevant php.ini files - local PHP_INI_PATHS=("/etc/php/${PHP_VERSION}/cli/php.ini") - [[ "$PHP_FPM" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/fpm/php.ini") - [[ "$PHP_APACHE" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/apache2/php.ini") - for ini in "${PHP_INI_PATHS[@]}"; do - if [[ -f "$ini" ]]; then - $STD sed -i "s|^memory_limit = .*|memory_limit = ${PHP_MEMORY_LIMIT}|" "$ini" - $STD sed -i "s|^upload_max_filesize = .*|upload_max_filesize = ${PHP_UPLOAD_MAX_FILESIZE}|" "$ini" - $STD sed -i "s|^post_max_size = .*|post_max_size = ${PHP_POST_MAX_SIZE}|" "$ini" - $STD sed -i "s|^max_execution_time = .*|max_execution_time = ${PHP_MAX_EXECUTION_TIME}|" "$ini" - fi - done - - # Patch Apache configuration if needed - if [[ "$PHP_APACHE" == "YES" ]]; then - for mod in $(ls /etc/apache2/mods-enabled/ 2>/dev/null | grep -E '^php[0-9]\.[0-9]\.conf$' | sed 's/\.conf//'); do - if [[ "$mod" != "php${PHP_VERSION}" ]]; then - $STD a2dismod "$mod" || true - fi + # Build module list + local MODULE_LIST="php${PHP_VERSION}" + IFS=',' read -ra MODULES <<<"$COMBINED_MODULES" + for mod in "${MODULES[@]}"; do + if apt-cache show "php${PHP_VERSION}-${mod}" >/dev/null 2>&1; then + MODULE_LIST+=" php${PHP_VERSION}-${mod}" + fi done - $STD a2enmod mpm_prefork - $STD a2enmod "php${PHP_VERSION}" - safe_service_restart apache2 || true - fi - - # Enable and restart PHP-FPM if requested - if [[ "$PHP_FPM" == "YES" ]]; then - if systemctl list-unit-files | grep -q "php${PHP_VERSION}-fpm.service"; then - $STD systemctl enable php${PHP_VERSION}-fpm - safe_service_restart php${PHP_VERSION}-fpm + if [[ "$PHP_FPM" == "YES" ]]; then + MODULE_LIST+=" php${PHP_VERSION}-fpm" fi - fi - msg_ok "Setup PHP $PHP_VERSION" + # install apache2 with PHP support if requested + if [[ "$PHP_APACHE" == "YES" ]]; then + if ! dpkg -l 2>/dev/null | grep -q "libapache2-mod-php${PHP_VERSION}"; then + $STD apt install -y apache2 libapache2-mod-php${PHP_VERSION} || { + msg_error "Failed to install Apache with PHP module" + return 1 + } + fi + fi + + # Install PHP packages + $STD apt install -y $MODULE_LIST || { + msg_error "Failed to install PHP packages" + return 1 + } + cache_installed_version "php" "$PHP_VERSION" + + # Patch all relevant php.ini files + local PHP_INI_PATHS=("/etc/php/${PHP_VERSION}/cli/php.ini") + [[ "$PHP_FPM" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/fpm/php.ini") + [[ "$PHP_APACHE" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/apache2/php.ini") + for ini in "${PHP_INI_PATHS[@]}"; do + if [[ -f "$ini" ]]; then + $STD sed -i "s|^memory_limit = .*|memory_limit = ${PHP_MEMORY_LIMIT}|" "$ini" + $STD sed -i "s|^upload_max_filesize = .*|upload_max_filesize = ${PHP_UPLOAD_MAX_FILESIZE}|" "$ini" + $STD sed -i "s|^post_max_size = .*|post_max_size = ${PHP_POST_MAX_SIZE}|" "$ini" + $STD sed -i "s|^max_execution_time = .*|max_execution_time = ${PHP_MAX_EXECUTION_TIME}|" "$ini" + fi + done + + # Patch Apache configuration if needed + if [[ "$PHP_APACHE" == "YES" ]]; then + for mod in $(ls /etc/apache2/mods-enabled/ 2>/dev/null | grep -E '^php[0-9]\.[0-9]\.conf$' | sed 's/\.conf//'); do + if [[ "$mod" != "php${PHP_VERSION}" ]]; then + $STD a2dismod "$mod" || true + fi + done + $STD a2enmod mpm_prefork + $STD a2enmod "php${PHP_VERSION}" + safe_service_restart apache2 || true + fi + + # Enable and restart PHP-FPM if requested + if [[ "$PHP_FPM" == "YES" ]]; then + if systemctl list-unit-files | grep -q "php${PHP_VERSION}-fpm.service"; then + $STD systemctl enable php${PHP_VERSION}-fpm + safe_service_restart php${PHP_VERSION}-fpm + fi + fi + + msg_ok "Setup PHP $PHP_VERSION" } # ------------------------------------------------------------------------------ @@ -3366,141 +3366,141 @@ function setup_php() { # Variables: # PG_VERSION - Major PostgreSQL version (e.g. 15, 16) (default: 16) function setup_postgresql() { - local PG_VERSION="${PG_VERSION:-16}" - local PG_MODULES="${PG_MODULES:-}" - local DISTRO_ID DISTRO_CODENAME - DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') - DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + local PG_VERSION="${PG_VERSION:-16}" + local PG_MODULES="${PG_MODULES:-}" + local DISTRO_ID DISTRO_CODENAME + DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - # Get currently installed version - local CURRENT_PG_VERSION="" - if command -v psql >/dev/null; then - CURRENT_PG_VERSION="$(psql -V 2>/dev/null | awk '{print $3}' | cut -d. -f1)" - fi - - # Scenario 1: Already at correct version - if [[ "$CURRENT_PG_VERSION" == "$PG_VERSION" ]]; then - msg_info "Update PostgreSQL $PG_VERSION" - $STD apt update - $STD apt install --only-upgrade -y "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}" 2>/dev/null || true - cache_installed_version "postgresql" "$PG_VERSION" - msg_ok "Update PostgreSQL $PG_VERSION" - - # Still install modules if specified - if [[ -n "$PG_MODULES" ]]; then - IFS=',' read -ra MODULES <<<"$PG_MODULES" - for module in "${MODULES[@]}"; do - $STD apt install -y "postgresql-${PG_VERSION}-${module}" 2>/dev/null || true - done + # Get currently installed version + local CURRENT_PG_VERSION="" + if command -v psql >/dev/null; then + CURRENT_PG_VERSION="$(psql -V 2>/dev/null | awk '{print $3}' | cut -d. -f1)" fi - return 0 - fi - # Scenario 2: Different version - backup, remove old, install new - if [[ -n "$CURRENT_PG_VERSION" ]]; then - msg_info "Upgrade PostgreSQL from $CURRENT_PG_VERSION to $PG_VERSION" - msg_info "Creating backup of PostgreSQL $CURRENT_PG_VERSION databases..." - $STD runuser -u postgres -- pg_dumpall >/var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql || { - msg_error "Failed to backup PostgreSQL databases" - return 1 - } - $STD systemctl stop postgresql || true - $STD apt purge -y "postgresql-${CURRENT_PG_VERSION}" "postgresql-client-${CURRENT_PG_VERSION}" 2>/dev/null || true - else - msg_info "Setup PostgreSQL $PG_VERSION" - fi + # Scenario 1: Already at correct version + if [[ "$CURRENT_PG_VERSION" == "$PG_VERSION" ]]; then + msg_info "Update PostgreSQL $PG_VERSION" + $STD apt update + $STD apt install --only-upgrade -y "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}" 2>/dev/null || true + cache_installed_version "postgresql" "$PG_VERSION" + msg_ok "Update PostgreSQL $PG_VERSION" - # Scenario 3: Fresh install or after removal - setup repo and install - cleanup_old_repo_files "pgdg" + # Still install modules if specified + if [[ -n "$PG_MODULES" ]]; then + IFS=',' read -ra MODULES <<<"$PG_MODULES" + for module in "${MODULES[@]}"; do + $STD apt install -y "postgresql-${PG_VERSION}-${module}" 2>/dev/null || true + done + fi + return 0 + fi - local SUITE - case "$DISTRO_CODENAME" in - trixie | forky | sid) - if verify_repo_available "https://apt.postgresql.org/pub/repos/apt" "trixie-pgdg"; then - SUITE="trixie-pgdg" + # Scenario 2: Different version - backup, remove old, install new + if [[ -n "$CURRENT_PG_VERSION" ]]; then + msg_info "Upgrade PostgreSQL from $CURRENT_PG_VERSION to $PG_VERSION" + msg_info "Creating backup of PostgreSQL $CURRENT_PG_VERSION databases..." + $STD runuser -u postgres -- pg_dumpall >/var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql || { + msg_error "Failed to backup PostgreSQL databases" + return 1 + } + $STD systemctl stop postgresql || true + $STD apt purge -y "postgresql-${CURRENT_PG_VERSION}" "postgresql-client-${CURRENT_PG_VERSION}" 2>/dev/null || true else - SUITE="bookworm-pgdg" + msg_info "Setup PostgreSQL $PG_VERSION" fi - ;; - *) - SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://apt.postgresql.org/pub/repos/apt") - SUITE="${SUITE}-pgdg" - ;; - esac - setup_deb822_repo \ - "pgdg" \ - "https://www.postgresql.org/media/keys/ACCC4CF8.asc" \ - "https://apt.postgresql.org/pub/repos/apt" \ - "$SUITE" \ - "main" \ - "amd64 arm64" + # Scenario 3: Fresh install or after removal - setup repo and install + cleanup_old_repo_files "pgdg" - if ! $STD apt update; then - msg_error "APT update failed for PostgreSQL repository" - return 1 - fi + local SUITE + case "$DISTRO_CODENAME" in + trixie | forky | sid) + if verify_repo_available "https://apt.postgresql.org/pub/repos/apt" "trixie-pgdg"; then + SUITE="trixie-pgdg" + else + SUITE="bookworm-pgdg" + fi + ;; + *) + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://apt.postgresql.org/pub/repos/apt") + SUITE="${SUITE}-pgdg" + ;; + esac - # Install ssl-cert dependency if available - if apt-cache search "^ssl-cert$" 2>/dev/null | grep -q .; then - $STD apt install -y ssl-cert 2>/dev/null || true - fi + setup_deb822_repo \ + "pgdg" \ + "https://www.postgresql.org/media/keys/ACCC4CF8.asc" \ + "https://apt.postgresql.org/pub/repos/apt" \ + "$SUITE" \ + "main" \ + "amd64 arm64" - # Try multiple PostgreSQL package patterns - local pg_install_success=false + if ! $STD apt update; then + msg_error "APT update failed for PostgreSQL repository" + return 1 + fi - if apt-cache search "^postgresql-${PG_VERSION}$" 2>/dev/null | grep -q . && - $STD apt install -y "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}" 2>/dev/null; then - pg_install_success=true - fi + # Install ssl-cert dependency if available + if apt-cache search "^ssl-cert$" 2>/dev/null | grep -q .; then + $STD apt install -y ssl-cert 2>/dev/null || true + fi - if [[ "$pg_install_success" == false ]] && - apt-cache search "^postgresql-server-${PG_VERSION}$" 2>/dev/null | grep -q . && - $STD apt install -y "postgresql-server-${PG_VERSION}" "postgresql-client-${PG_VERSION}" 2>/dev/null; then - pg_install_success=true - fi + # Try multiple PostgreSQL package patterns + local pg_install_success=false - if [[ "$pg_install_success" == false ]] && - apt-cache search "^postgresql$" 2>/dev/null | grep -q . && - $STD apt install -y postgresql postgresql-client 2>/dev/null; then - pg_install_success=true - fi + if apt-cache search "^postgresql-${PG_VERSION}$" 2>/dev/null | grep -q . && + $STD apt install -y "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}" 2>/dev/null; then + pg_install_success=true + fi - if [[ "$pg_install_success" == false ]]; then - msg_error "PostgreSQL package not available for suite ${SUITE}" - return 1 - fi + if [[ "$pg_install_success" == false ]] && + apt-cache search "^postgresql-server-${PG_VERSION}$" 2>/dev/null | grep -q . && + $STD apt install -y "postgresql-server-${PG_VERSION}" "postgresql-client-${PG_VERSION}" 2>/dev/null; then + pg_install_success=true + fi - if ! command -v psql >/dev/null 2>&1; then - msg_error "PostgreSQL installed but psql command not found" - return 1 - fi + if [[ "$pg_install_success" == false ]] && + apt-cache search "^postgresql$" 2>/dev/null | grep -q . && + $STD apt install -y postgresql postgresql-client 2>/dev/null; then + pg_install_success=true + fi - # Restore database backup if we upgraded from previous version - if [[ -n "$CURRENT_PG_VERSION" ]]; then - msg_info "Restoring PostgreSQL databases from backup..." - $STD runuser -u postgres -- psql /dev/null || { - msg_warn "Failed to restore database backup - this may be expected for major version upgrades" - } - fi + if [[ "$pg_install_success" == false ]]; then + msg_error "PostgreSQL package not available for suite ${SUITE}" + return 1 + fi - $STD systemctl enable --now postgresql 2>/dev/null || true + if ! command -v psql >/dev/null 2>&1; then + msg_error "PostgreSQL installed but psql command not found" + return 1 + fi - # Add PostgreSQL binaries to PATH - if ! grep -q '/usr/lib/postgresql' /etc/environment 2>/dev/null; then - echo 'PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/postgresql/'"${PG_VERSION}"'/bin"' >/etc/environment - fi + # Restore database backup if we upgraded from previous version + if [[ -n "$CURRENT_PG_VERSION" ]]; then + msg_info "Restoring PostgreSQL databases from backup..." + $STD runuser -u postgres -- psql /dev/null || { + msg_warn "Failed to restore database backup - this may be expected for major version upgrades" + } + fi - cache_installed_version "postgresql" "$PG_VERSION" - msg_ok "Setup PostgreSQL $PG_VERSION" + $STD systemctl enable --now postgresql 2>/dev/null || true - # Install optional modules - if [[ -n "$PG_MODULES" ]]; then - IFS=',' read -ra MODULES <<<"$PG_MODULES" - for module in "${MODULES[@]}"; do - $STD apt install -y "postgresql-${PG_VERSION}-${module}" 2>/dev/null || true - done - fi + # Add PostgreSQL binaries to PATH + if ! grep -q '/usr/lib/postgresql' /etc/environment 2>/dev/null; then + echo 'PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/postgresql/'"${PG_VERSION}"'/bin"' >/etc/environment + fi + + cache_installed_version "postgresql" "$PG_VERSION" + msg_ok "Setup PostgreSQL $PG_VERSION" + + # Install optional modules + if [[ -n "$PG_MODULES" ]]; then + IFS=',' read -ra MODULES <<<"$PG_MODULES" + for module in "${MODULES[@]}"; do + $STD apt install -y "postgresql-${PG_VERSION}-${module}" 2>/dev/null || true + done + fi } # ------------------------------------------------------------------------------ @@ -3517,192 +3517,192 @@ function setup_postgresql() { # ------------------------------------------------------------------------------ function setup_ruby() { - local RUBY_VERSION="${RUBY_VERSION:-3.4.4}" - local RUBY_INSTALL_RAILS="${RUBY_INSTALL_RAILS:-true}" - local RBENV_DIR="$HOME/.rbenv" - local RBENV_BIN="$RBENV_DIR/bin/rbenv" - local PROFILE_FILE="$HOME/.profile" - local TMP_DIR=$(mktemp -d) + local RUBY_VERSION="${RUBY_VERSION:-3.4.4}" + local RUBY_INSTALL_RAILS="${RUBY_INSTALL_RAILS:-true}" + local RBENV_DIR="$HOME/.rbenv" + local RBENV_BIN="$RBENV_DIR/bin/rbenv" + local PROFILE_FILE="$HOME/.profile" + local TMP_DIR=$(mktemp -d) - # Get currently installed Ruby version - local CURRENT_RUBY_VERSION="" - if [[ -x "$RBENV_BIN" ]]; then - CURRENT_RUBY_VERSION=$("$RBENV_BIN" global 2>/dev/null || echo "") - fi + # Get currently installed Ruby version + local CURRENT_RUBY_VERSION="" + if [[ -x "$RBENV_BIN" ]]; then + CURRENT_RUBY_VERSION=$("$RBENV_BIN" global 2>/dev/null || echo "") + fi - # Scenario 1: Already at correct Ruby version - if [[ "$CURRENT_RUBY_VERSION" == "$RUBY_VERSION" ]]; then - msg_info "Update Ruby $RUBY_VERSION" - cache_installed_version "ruby" "$RUBY_VERSION" - msg_ok "Update Ruby $RUBY_VERSION" - return 0 - fi + # Scenario 1: Already at correct Ruby version + if [[ "$CURRENT_RUBY_VERSION" == "$RUBY_VERSION" ]]; then + msg_info "Update Ruby $RUBY_VERSION" + cache_installed_version "ruby" "$RUBY_VERSION" + msg_ok "Update Ruby $RUBY_VERSION" + return 0 + fi - # Scenario 2: Different version - reinstall - if [[ -n "$CURRENT_RUBY_VERSION" ]]; then - msg_info "Upgrade Ruby from $CURRENT_RUBY_VERSION to $RUBY_VERSION" - else - msg_info "Setup Ruby $RUBY_VERSION" - fi - - ensure_apt_working || return 1 - - # Install build dependencies with fallbacks - local ruby_deps=() - local dep_variations=( - "jq" - "autoconf" - "patch" - "build-essential" - "libssl-dev" - "libyaml-dev" - "libreadline-dev|libreadline6-dev" - "zlib1g-dev" - "libgmp-dev" - "libncurses-dev|libncurses5-dev" - "libffi-dev" - "libgdbm-dev" - "libdb-dev" - "uuid-dev" - ) - - for dep_pattern in "${dep_variations[@]}"; do - if [[ "$dep_pattern" == *"|"* ]]; then - IFS='|' read -ra variations <<<"$dep_pattern" - for var in "${variations[@]}"; do - if apt-cache search "^${var}$" 2>/dev/null | grep -q .; then - ruby_deps+=("$var") - break - fi - done + # Scenario 2: Different version - reinstall + if [[ -n "$CURRENT_RUBY_VERSION" ]]; then + msg_info "Upgrade Ruby from $CURRENT_RUBY_VERSION to $RUBY_VERSION" else - if apt-cache search "^${dep_pattern}$" 2>/dev/null | grep -q .; then - ruby_deps+=("$dep_pattern") - fi + msg_info "Setup Ruby $RUBY_VERSION" + fi + + ensure_apt_working || return 1 + + # Install build dependencies with fallbacks + local ruby_deps=() + local dep_variations=( + "jq" + "autoconf" + "patch" + "build-essential" + "libssl-dev" + "libyaml-dev" + "libreadline-dev|libreadline6-dev" + "zlib1g-dev" + "libgmp-dev" + "libncurses-dev|libncurses5-dev" + "libffi-dev" + "libgdbm-dev" + "libdb-dev" + "uuid-dev" + ) + + for dep_pattern in "${dep_variations[@]}"; do + if [[ "$dep_pattern" == *"|"* ]]; then + IFS='|' read -ra variations <<<"$dep_pattern" + for var in "${variations[@]}"; do + if apt-cache search "^${var}$" 2>/dev/null | grep -q .; then + ruby_deps+=("$var") + break + fi + done + else + if apt-cache search "^${dep_pattern}$" 2>/dev/null | grep -q .; then + ruby_deps+=("$dep_pattern") + fi + fi + done + + if [[ ${#ruby_deps[@]} -gt 0 ]]; then + $STD apt install -y "${ruby_deps[@]}" 2>/dev/null || true + else + msg_error "No Ruby build dependencies available" + rm -rf "$TMP_DIR" + return 1 + fi + + # Download and build rbenv if needed + if [[ ! -x "$RBENV_BIN" ]]; then + local RBENV_RELEASE + local rbenv_json + rbenv_json=$(curl -fsSL --max-time 15 https://api.github.com/repos/rbenv/rbenv/releases/latest 2>/dev/null || echo "") + + if [[ -z "$rbenv_json" ]]; then + msg_error "Failed to fetch latest rbenv version from GitHub" + rm -rf "$TMP_DIR" + return 1 + fi + + RBENV_RELEASE=$(echo "$rbenv_json" | jq -r '.tag_name' 2>/dev/null | sed 's/^v//' || echo "") + + if [[ -z "$RBENV_RELEASE" ]]; then + msg_error "Could not parse rbenv version from GitHub response" + rm -rf "$TMP_DIR" + return 1 + fi + + curl -fsSL "https://github.com/rbenv/rbenv/archive/refs/tags/v${RBENV_RELEASE}.tar.gz" -o "$TMP_DIR/rbenv.tar.gz" || { + msg_error "Failed to download rbenv" + rm -rf "$TMP_DIR" + return 1 + } + + tar -xzf "$TMP_DIR/rbenv.tar.gz" -C "$TMP_DIR" || { + msg_error "Failed to extract rbenv" + rm -rf "$TMP_DIR" + return 1 + } + + mkdir -p "$RBENV_DIR" + cp -r "$TMP_DIR/rbenv-${RBENV_RELEASE}/." "$RBENV_DIR/" + (cd "$RBENV_DIR" && src/configure && $STD make -C src) || { + msg_error "Failed to build rbenv" + rm -rf "$TMP_DIR" + return 1 + } + + # Setup profile + if ! grep -q 'rbenv init' "$PROFILE_FILE" 2>/dev/null; then + echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >>"$PROFILE_FILE" + echo 'eval "$(rbenv init -)"' >>"$PROFILE_FILE" + fi + fi + + # Install ruby-build plugin + if [[ ! -d "$RBENV_DIR/plugins/ruby-build" ]]; then + local RUBY_BUILD_RELEASE + local ruby_build_json + ruby_build_json=$(curl -fsSL --max-time 15 https://api.github.com/repos/rbenv/ruby-build/releases/latest 2>/dev/null || echo "") + + if [[ -z "$ruby_build_json" ]]; then + msg_error "Failed to fetch latest ruby-build version from GitHub" + rm -rf "$TMP_DIR" + return 1 + fi + + RUBY_BUILD_RELEASE=$(echo "$ruby_build_json" | jq -r '.tag_name' 2>/dev/null | sed 's/^v//' || echo "") + + if [[ -z "$RUBY_BUILD_RELEASE" ]]; then + msg_error "Could not parse ruby-build version from GitHub response" + rm -rf "$TMP_DIR" + return 1 + fi + + curl -fsSL "https://github.com/rbenv/ruby-build/archive/refs/tags/v${RUBY_BUILD_RELEASE}.tar.gz" -o "$TMP_DIR/ruby-build.tar.gz" || { + msg_error "Failed to download ruby-build" + rm -rf "$TMP_DIR" + return 1 + } + + tar -xzf "$TMP_DIR/ruby-build.tar.gz" -C "$TMP_DIR" || { + msg_error "Failed to extract ruby-build" + rm -rf "$TMP_DIR" + return 1 + } + + mkdir -p "$RBENV_DIR/plugins/ruby-build" + cp -r "$TMP_DIR/ruby-build-${RUBY_BUILD_RELEASE}/." "$RBENV_DIR/plugins/ruby-build/" + fi + + # Setup PATH and install Ruby version + export PATH="$RBENV_DIR/bin:$PATH" + eval "$("$RBENV_BIN" init - bash)" 2>/dev/null || true + + if ! "$RBENV_BIN" versions --bare 2>/dev/null | grep -qx "$RUBY_VERSION"; then + $STD "$RBENV_BIN" install "$RUBY_VERSION" || { + msg_error "Failed to install Ruby $RUBY_VERSION" + rm -rf "$TMP_DIR" + return 1 + } + fi + + "$RBENV_BIN" global "$RUBY_VERSION" || { + msg_error "Failed to set Ruby $RUBY_VERSION as global version" + rm -rf "$TMP_DIR" + return 1 + } + + hash -r + + # Install Rails if requested + if [[ "$RUBY_INSTALL_RAILS" == "true" ]]; then + $STD gem install rails || { + msg_warn "Failed to install Rails - Ruby installation successful" + } fi - done - if [[ ${#ruby_deps[@]} -gt 0 ]]; then - $STD apt install -y "${ruby_deps[@]}" 2>/dev/null || true - else - msg_error "No Ruby build dependencies available" rm -rf "$TMP_DIR" - return 1 - fi - - # Download and build rbenv if needed - if [[ ! -x "$RBENV_BIN" ]]; then - local RBENV_RELEASE - local rbenv_json - rbenv_json=$(curl -fsSL --max-time 15 https://api.github.com/repos/rbenv/rbenv/releases/latest 2>/dev/null || echo "") - - if [[ -z "$rbenv_json" ]]; then - msg_error "Failed to fetch latest rbenv version from GitHub" - rm -rf "$TMP_DIR" - return 1 - fi - - RBENV_RELEASE=$(echo "$rbenv_json" | jq -r '.tag_name' 2>/dev/null | sed 's/^v//' || echo "") - - if [[ -z "$RBENV_RELEASE" ]]; then - msg_error "Could not parse rbenv version from GitHub response" - rm -rf "$TMP_DIR" - return 1 - fi - - curl -fsSL "https://github.com/rbenv/rbenv/archive/refs/tags/v${RBENV_RELEASE}.tar.gz" -o "$TMP_DIR/rbenv.tar.gz" || { - msg_error "Failed to download rbenv" - rm -rf "$TMP_DIR" - return 1 - } - - tar -xzf "$TMP_DIR/rbenv.tar.gz" -C "$TMP_DIR" || { - msg_error "Failed to extract rbenv" - rm -rf "$TMP_DIR" - return 1 - } - - mkdir -p "$RBENV_DIR" - cp -r "$TMP_DIR/rbenv-${RBENV_RELEASE}/." "$RBENV_DIR/" - (cd "$RBENV_DIR" && src/configure && $STD make -C src) || { - msg_error "Failed to build rbenv" - rm -rf "$TMP_DIR" - return 1 - } - - # Setup profile - if ! grep -q 'rbenv init' "$PROFILE_FILE" 2>/dev/null; then - echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >>"$PROFILE_FILE" - echo 'eval "$(rbenv init -)"' >>"$PROFILE_FILE" - fi - fi - - # Install ruby-build plugin - if [[ ! -d "$RBENV_DIR/plugins/ruby-build" ]]; then - local RUBY_BUILD_RELEASE - local ruby_build_json - ruby_build_json=$(curl -fsSL --max-time 15 https://api.github.com/repos/rbenv/ruby-build/releases/latest 2>/dev/null || echo "") - - if [[ -z "$ruby_build_json" ]]; then - msg_error "Failed to fetch latest ruby-build version from GitHub" - rm -rf "$TMP_DIR" - return 1 - fi - - RUBY_BUILD_RELEASE=$(echo "$ruby_build_json" | jq -r '.tag_name' 2>/dev/null | sed 's/^v//' || echo "") - - if [[ -z "$RUBY_BUILD_RELEASE" ]]; then - msg_error "Could not parse ruby-build version from GitHub response" - rm -rf "$TMP_DIR" - return 1 - fi - - curl -fsSL "https://github.com/rbenv/ruby-build/archive/refs/tags/v${RUBY_BUILD_RELEASE}.tar.gz" -o "$TMP_DIR/ruby-build.tar.gz" || { - msg_error "Failed to download ruby-build" - rm -rf "$TMP_DIR" - return 1 - } - - tar -xzf "$TMP_DIR/ruby-build.tar.gz" -C "$TMP_DIR" || { - msg_error "Failed to extract ruby-build" - rm -rf "$TMP_DIR" - return 1 - } - - mkdir -p "$RBENV_DIR/plugins/ruby-build" - cp -r "$TMP_DIR/ruby-build-${RUBY_BUILD_RELEASE}/." "$RBENV_DIR/plugins/ruby-build/" - fi - - # Setup PATH and install Ruby version - export PATH="$RBENV_DIR/bin:$PATH" - eval "$("$RBENV_BIN" init - bash)" 2>/dev/null || true - - if ! "$RBENV_BIN" versions --bare 2>/dev/null | grep -qx "$RUBY_VERSION"; then - $STD "$RBENV_BIN" install "$RUBY_VERSION" || { - msg_error "Failed to install Ruby $RUBY_VERSION" - rm -rf "$TMP_DIR" - return 1 - } - fi - - "$RBENV_BIN" global "$RUBY_VERSION" || { - msg_error "Failed to set Ruby $RUBY_VERSION as global version" - rm -rf "$TMP_DIR" - return 1 - } - - hash -r - - # Install Rails if requested - if [[ "$RUBY_INSTALL_RAILS" == "true" ]]; then - $STD gem install rails || { - msg_warn "Failed to install Rails - Ruby installation successful" - } - fi - - rm -rf "$TMP_DIR" - cache_installed_version "ruby" "$RUBY_VERSION" - msg_ok "Setup Ruby $RUBY_VERSION" + cache_installed_version "ruby" "$RUBY_VERSION" + msg_ok "Setup Ruby $RUBY_VERSION" } # ------------------------------------------------------------------------------ @@ -3719,97 +3719,97 @@ function setup_ruby() { # ------------------------------------------------------------------------------ function setup_clickhouse() { - local CLICKHOUSE_VERSION="${CLICKHOUSE_VERSION:-latest}" - local DISTRO_ID DISTRO_CODENAME - DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') - DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + local CLICKHOUSE_VERSION="${CLICKHOUSE_VERSION:-latest}" + local DISTRO_ID DISTRO_CODENAME + DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - # Resolve "latest" version - if [[ "$CLICKHOUSE_VERSION" == "latest" ]]; then - CLICKHOUSE_VERSION=$(curl -fsSL --max-time 15 https://packages.clickhouse.com/tgz/stable/ 2>/dev/null | - grep -oP 'clickhouse-common-static-\K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | - sort -V | tail -n1 || echo "") + # Resolve "latest" version + if [[ "$CLICKHOUSE_VERSION" == "latest" ]]; then + CLICKHOUSE_VERSION=$(curl -fsSL --max-time 15 https://packages.clickhouse.com/tgz/stable/ 2>/dev/null | + grep -oP 'clickhouse-common-static-\K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | + sort -V | tail -n1 || echo "") - # Fallback to GitHub API if package server failed - if [[ -z "$CLICKHOUSE_VERSION" ]]; then - CLICKHOUSE_VERSION=$(curl -fsSL --max-time 15 https://api.github.com/repos/ClickHouse/ClickHouse/releases/latest 2>/dev/null | - grep -oP '"tag_name":\s*"v\K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -n1 || echo "") + # Fallback to GitHub API if package server failed + if [[ -z "$CLICKHOUSE_VERSION" ]]; then + CLICKHOUSE_VERSION=$(curl -fsSL --max-time 15 https://api.github.com/repos/ClickHouse/ClickHouse/releases/latest 2>/dev/null | + grep -oP '"tag_name":\s*"v\K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -n1 || echo "") + fi + + [[ -z "$CLICKHOUSE_VERSION" ]] && { + msg_error "Could not determine latest ClickHouse version from any source" + return 1 + } fi - [[ -z "$CLICKHOUSE_VERSION" ]] && { - msg_error "Could not determine latest ClickHouse version from any source" - return 1 + # Get currently installed version + local CURRENT_VERSION="" + if command -v clickhouse-server >/dev/null 2>&1; then + CURRENT_VERSION=$(clickhouse-server --version 2>/dev/null | grep -oP 'version \K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -n1) + fi + + # Scenario 1: Already at target version - just update packages + if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$CLICKHOUSE_VERSION" ]]; then + msg_info "Update ClickHouse $CLICKHOUSE_VERSION" + ensure_apt_working || return 1 + $STD apt install --only-upgrade -y clickhouse-server clickhouse-client || true + cache_installed_version "clickhouse" "$CLICKHOUSE_VERSION" + msg_ok "Update ClickHouse $CLICKHOUSE_VERSION" + return 0 + fi + + # Scenario 2: Different version - clean upgrade + if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" != "$CLICKHOUSE_VERSION" ]]; then + msg_info "Upgrade ClickHouse from $CURRENT_VERSION to $CLICKHOUSE_VERSION" + $STD systemctl stop clickhouse-server >/dev/null 2>&1 || true + remove_old_tool_version "clickhouse" + else + msg_info "Setup ClickHouse $CLICKHOUSE_VERSION" + fi + + ensure_dependencies apt-transport-https ca-certificates dirmngr gnupg + + # Setup repository (ClickHouse uses 'stable' suite) + setup_deb822_repo \ + "clickhouse" \ + "https://packages.clickhouse.com/rpm/lts/repodata/repomd.xml.key" \ + "https://packages.clickhouse.com/deb" \ + "stable" \ + "main" \ + "amd64 arm64" + + # Install packages + export DEBIAN_FRONTEND=noninteractive + $STD apt update || { + msg_error "APT update failed for ClickHouse repository" + return 1 } - fi - # Get currently installed version - local CURRENT_VERSION="" - if command -v clickhouse-server >/dev/null 2>&1; then - CURRENT_VERSION=$(clickhouse-server --version 2>/dev/null | grep -oP 'version \K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -n1) - fi + $STD apt install -y clickhouse-server clickhouse-client || { + msg_error "Failed to install ClickHouse packages" + return 1 + } + + # Verify installation + if ! command -v clickhouse-server >/dev/null 2>&1; then + msg_error "ClickHouse installation completed but clickhouse-server command not found" + return 1 + fi + + # Setup data directory + mkdir -p /var/lib/clickhouse + if id clickhouse >/dev/null 2>&1; then + chown -R clickhouse:clickhouse /var/lib/clickhouse + fi + + # Enable and start service + $STD systemctl enable clickhouse-server || { + msg_warn "Failed to enable clickhouse-server service" + } + safe_service_restart clickhouse-server || true - # Scenario 1: Already at target version - just update packages - if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$CLICKHOUSE_VERSION" ]]; then - msg_info "Update ClickHouse $CLICKHOUSE_VERSION" - ensure_apt_working || return 1 - $STD apt install --only-upgrade -y clickhouse-server clickhouse-client || true cache_installed_version "clickhouse" "$CLICKHOUSE_VERSION" - msg_ok "Update ClickHouse $CLICKHOUSE_VERSION" - return 0 - fi - - # Scenario 2: Different version - clean upgrade - if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" != "$CLICKHOUSE_VERSION" ]]; then - msg_info "Upgrade ClickHouse from $CURRENT_VERSION to $CLICKHOUSE_VERSION" - $STD systemctl stop clickhouse-server >/dev/null 2>&1 || true - remove_old_tool_version "clickhouse" - else - msg_info "Setup ClickHouse $CLICKHOUSE_VERSION" - fi - - ensure_dependencies apt-transport-https ca-certificates dirmngr gnupg - - # Setup repository (ClickHouse uses 'stable' suite) - setup_deb822_repo \ - "clickhouse" \ - "https://packages.clickhouse.com/rpm/lts/repodata/repomd.xml.key" \ - "https://packages.clickhouse.com/deb" \ - "stable" \ - "main" \ - "amd64 arm64" - - # Install packages - export DEBIAN_FRONTEND=noninteractive - $STD apt update || { - msg_error "APT update failed for ClickHouse repository" - return 1 - } - - $STD apt install -y clickhouse-server clickhouse-client || { - msg_error "Failed to install ClickHouse packages" - return 1 - } - - # Verify installation - if ! command -v clickhouse-server >/dev/null 2>&1; then - msg_error "ClickHouse installation completed but clickhouse-server command not found" - return 1 - fi - - # Setup data directory - mkdir -p /var/lib/clickhouse - if id clickhouse >/dev/null 2>&1; then - chown -R clickhouse:clickhouse /var/lib/clickhouse - fi - - # Enable and start service - $STD systemctl enable clickhouse-server || { - msg_warn "Failed to enable clickhouse-server service" - } - safe_service_restart clickhouse-server || true - - cache_installed_version "clickhouse" "$CLICKHOUSE_VERSION" - msg_ok "Setup ClickHouse $CLICKHOUSE_VERSION" + msg_ok "Setup ClickHouse $CLICKHOUSE_VERSION" } # ------------------------------------------------------------------------------ @@ -3830,71 +3830,71 @@ function setup_clickhouse() { # ------------------------------------------------------------------------------ function setup_rust() { - local RUST_TOOLCHAIN="${RUST_TOOLCHAIN:-stable}" - local RUST_CRATES="${RUST_CRATES:-}" - local CARGO_BIN="${HOME}/.cargo/bin" + local RUST_TOOLCHAIN="${RUST_TOOLCHAIN:-stable}" + local RUST_CRATES="${RUST_CRATES:-}" + local CARGO_BIN="${HOME}/.cargo/bin" - # Get currently installed version - local CURRENT_VERSION="" - if command -v rustc &>/dev/null; then - CURRENT_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}') - fi + # Get currently installed version + local CURRENT_VERSION="" + if command -v rustc &>/dev/null; then + CURRENT_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}') + fi - # Scenario 1: Rustup not installed - fresh install - if ! command -v rustup &>/dev/null; then - msg_info "Setup Rust ($RUST_TOOLCHAIN)" - curl -fsSL https://sh.rustup.rs | $STD sh -s -- -y --default-toolchain "$RUST_TOOLCHAIN" || { - msg_error "Failed to install Rust" - return 1 - } - export PATH="$CARGO_BIN:$PATH" - echo 'export PATH="$HOME/.cargo/bin:$PATH"' >>"$HOME/.profile" - local RUST_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}') - cache_installed_version "rust" "$RUST_VERSION" - msg_ok "Setup Rust $RUST_VERSION" - else - # Scenario 2: Rustup already installed - update/maintain - msg_info "Update Rust ($RUST_TOOLCHAIN)" - $STD rustup install "$RUST_TOOLCHAIN" || { - msg_error "Failed to install Rust toolchain $RUST_TOOLCHAIN" - return 1 - } - $STD rustup default "$RUST_TOOLCHAIN" || { - msg_error "Failed to set default Rust toolchain" - return 1 - } - $STD rustup update "$RUST_TOOLCHAIN" || true - local RUST_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}') - cache_installed_version "rust" "$RUST_VERSION" - msg_ok "Update Rust $RUST_VERSION" - fi + # Scenario 1: Rustup not installed - fresh install + if ! command -v rustup &>/dev/null; then + msg_info "Setup Rust ($RUST_TOOLCHAIN)" + curl -fsSL https://sh.rustup.rs | $STD sh -s -- -y --default-toolchain "$RUST_TOOLCHAIN" || { + msg_error "Failed to install Rust" + return 1 + } + export PATH="$CARGO_BIN:$PATH" + echo 'export PATH="$HOME/.cargo/bin:$PATH"' >>"$HOME/.profile" + local RUST_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}') + cache_installed_version "rust" "$RUST_VERSION" + msg_ok "Setup Rust $RUST_VERSION" + else + # Scenario 2: Rustup already installed - update/maintain + msg_info "Update Rust ($RUST_TOOLCHAIN)" + $STD rustup install "$RUST_TOOLCHAIN" || { + msg_error "Failed to install Rust toolchain $RUST_TOOLCHAIN" + return 1 + } + $STD rustup default "$RUST_TOOLCHAIN" || { + msg_error "Failed to set default Rust toolchain" + return 1 + } + $STD rustup update "$RUST_TOOLCHAIN" || true + local RUST_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}') + cache_installed_version "rust" "$RUST_VERSION" + msg_ok "Update Rust $RUST_VERSION" + fi - # Install global crates - if [[ -n "$RUST_CRATES" ]]; then - IFS=',' read -ra CRATES <<<"$RUST_CRATES" - for crate in "${CRATES[@]}"; do - local NAME VER INSTALLED_VER - if [[ "$crate" == *"@"* ]]; then - NAME="${crate%@*}" - VER="${crate##*@}" - else - NAME="$crate" - VER="" - fi + # Install global crates + if [[ -n "$RUST_CRATES" ]]; then + IFS=',' read -ra CRATES <<<"$RUST_CRATES" + for crate in "${CRATES[@]}"; do + local NAME VER INSTALLED_VER + if [[ "$crate" == *"@"* ]]; then + NAME="${crate%@*}" + VER="${crate##*@}" + else + NAME="$crate" + VER="" + fi - INSTALLED_VER=$(cargo install --list 2>/dev/null | awk "/^$NAME v[0-9]/ {print \$2}" | tr -d 'v') + INSTALLED_VER=$(cargo install --list 2>/dev/null | awk "/^$NAME v[0-9]/ {print \$2}" | tr -d 'v') - if [[ -n "$INSTALLED_VER" ]]; then - if [[ -n "$VER" && "$VER" != "$INSTALLED_VER" ]]; then - $STD cargo install "$NAME" --version "$VER" --force - elif [[ -z "$VER" ]]; then - $STD cargo install "$NAME" --force - fi - else - $STD cargo install "$NAME" ${VER:+--version "$VER"} - fi - done - fi + if [[ -n "$INSTALLED_VER" ]]; then + if [[ -n "$VER" && "$VER" != "$INSTALLED_VER" ]]; then + $STD cargo install "$NAME" --version "$VER" --force + elif [[ -z "$VER" ]]; then + $STD cargo install "$NAME" --force + fi + else + $STD cargo install "$NAME" ${VER:+--version "$VER"} + fi + done + fi } # ------------------------------------------------------------------------------ @@ -3906,122 +3906,122 @@ function setup_rust() { # ------------------------------------------------------------------------------ function setup_uv() { - local UV_BIN="/usr/local/bin/uv" - local TMP_DIR=$(mktemp -d) - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "uv") + local UV_BIN="/usr/local/bin/uv" + local TMP_DIR=$(mktemp -d) + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "uv") - local ARCH=$(uname -m) - local UV_TAR + local ARCH=$(uname -m) + local UV_TAR - case "$ARCH" in - x86_64) - if grep -qi "alpine" /etc/os-release; then - UV_TAR="uv-x86_64-unknown-linux-musl.tar.gz" - else - UV_TAR="uv-x86_64-unknown-linux-gnu.tar.gz" - fi - ;; - aarch64) - if grep -qi "alpine" /etc/os-release; then - UV_TAR="uv-aarch64-unknown-linux-musl.tar.gz" - else - UV_TAR="uv-aarch64-unknown-linux-gnu.tar.gz" - fi - ;; - *) - msg_error "Unsupported architecture: $ARCH" - rm -rf "$TMP_DIR" - return 1 - ;; - esac - - ensure_dependencies jq - - local LATEST_VERSION - local releases_json - releases_json=$(curl -fsSL --max-time 15 https://api.github.com/repos/astral-sh/uv/releases/latest 2>/dev/null || echo "") - - if [[ -z "$releases_json" ]]; then - msg_error "Could not fetch latest uv version from GitHub API" - rm -rf "$TMP_DIR" - return 1 - fi - - LATEST_VERSION=$(echo "$releases_json" | jq -r '.tag_name' 2>/dev/null | sed 's/^v//' || echo "") - - if [[ -z "$LATEST_VERSION" ]]; then - msg_error "Could not parse uv version from GitHub API response" - rm -rf "$TMP_DIR" - return 1 - fi - - # Get currently installed version - local INSTALLED_VERSION="" - if [[ -x "$UV_BIN" ]]; then - INSTALLED_VERSION=$($UV_BIN -V 2>/dev/null | awk '{print $2}') - fi - - # Scenario 1: Already at latest version - if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" == "$LATEST_VERSION" ]]; then - cache_installed_version "uv" "$LATEST_VERSION" - rm -rf "$TMP_DIR" - return 0 - fi - - # Scenario 2: New install or upgrade - if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" != "$LATEST_VERSION" ]]; then - msg_info "Upgrade uv from $INSTALLED_VERSION to $LATEST_VERSION" - else - msg_info "Setup uv $LATEST_VERSION" - fi - - local UV_URL="https://github.com/astral-sh/uv/releases/latest/download/${UV_TAR}" - curl -fsSL "$UV_URL" -o "$TMP_DIR/uv.tar.gz" || { - msg_error "Failed to download uv" - rm -rf "$TMP_DIR" - return 1 - } - - tar -xzf "$TMP_DIR/uv.tar.gz" -C "$TMP_DIR" || { - msg_error "Failed to extract uv" - rm -rf "$TMP_DIR" - return 1 - } - - install -m 755 "$TMP_DIR"/*/uv "$UV_BIN" || { - msg_error "Failed to install uv binary" - rm -rf "$TMP_DIR" - return 1 - } - - rm -rf "$TMP_DIR" - ensure_usr_local_bin_persist - export PATH="/usr/local/bin:$PATH" - - $STD uv python update-shell || true - cache_installed_version "uv" "$LATEST_VERSION" - msg_ok "Setup uv $LATEST_VERSION" - - # Optional: Install specific Python version - if [[ -n "${PYTHON_VERSION:-}" ]]; then - local VERSION_MATCH - VERSION_MATCH=$(uv python list --only-downloads 2>/dev/null | - grep -E "^cpython-${PYTHON_VERSION//./\\.}\.[0-9]+-linux" | - cut -d'-' -f2 | sort -V | tail -n1) - - if [[ -z "$VERSION_MATCH" ]]; then - msg_error "No matching Python $PYTHON_VERSION.x version found" - return 1 - fi - - if ! uv python list 2>/dev/null | grep -q "cpython-${VERSION_MATCH}-linux.*uv/python"; then - $STD uv python install "$VERSION_MATCH" || { - msg_error "Failed to install Python $VERSION_MATCH" + case "$ARCH" in + x86_64) + if grep -qi "alpine" /etc/os-release; then + UV_TAR="uv-x86_64-unknown-linux-musl.tar.gz" + else + UV_TAR="uv-x86_64-unknown-linux-gnu.tar.gz" + fi + ;; + aarch64) + if grep -qi "alpine" /etc/os-release; then + UV_TAR="uv-aarch64-unknown-linux-musl.tar.gz" + else + UV_TAR="uv-aarch64-unknown-linux-gnu.tar.gz" + fi + ;; + *) + msg_error "Unsupported architecture: $ARCH" + rm -rf "$TMP_DIR" + return 1 + ;; + esac + + ensure_dependencies jq + + local LATEST_VERSION + local releases_json + releases_json=$(curl -fsSL --max-time 15 https://api.github.com/repos/astral-sh/uv/releases/latest 2>/dev/null || echo "") + + if [[ -z "$releases_json" ]]; then + msg_error "Could not fetch latest uv version from GitHub API" + rm -rf "$TMP_DIR" return 1 - } fi - fi + + LATEST_VERSION=$(echo "$releases_json" | jq -r '.tag_name' 2>/dev/null | sed 's/^v//' || echo "") + + if [[ -z "$LATEST_VERSION" ]]; then + msg_error "Could not parse uv version from GitHub API response" + rm -rf "$TMP_DIR" + return 1 + fi + + # Get currently installed version + local INSTALLED_VERSION="" + if [[ -x "$UV_BIN" ]]; then + INSTALLED_VERSION=$($UV_BIN -V 2>/dev/null | awk '{print $2}') + fi + + # Scenario 1: Already at latest version + if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" == "$LATEST_VERSION" ]]; then + cache_installed_version "uv" "$LATEST_VERSION" + rm -rf "$TMP_DIR" + return 0 + fi + + # Scenario 2: New install or upgrade + if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" != "$LATEST_VERSION" ]]; then + msg_info "Upgrade uv from $INSTALLED_VERSION to $LATEST_VERSION" + else + msg_info "Setup uv $LATEST_VERSION" + fi + + local UV_URL="https://github.com/astral-sh/uv/releases/latest/download/${UV_TAR}" + curl -fsSL "$UV_URL" -o "$TMP_DIR/uv.tar.gz" || { + msg_error "Failed to download uv" + rm -rf "$TMP_DIR" + return 1 + } + + tar -xzf "$TMP_DIR/uv.tar.gz" -C "$TMP_DIR" || { + msg_error "Failed to extract uv" + rm -rf "$TMP_DIR" + return 1 + } + + install -m 755 "$TMP_DIR"/*/uv "$UV_BIN" || { + msg_error "Failed to install uv binary" + rm -rf "$TMP_DIR" + return 1 + } + + rm -rf "$TMP_DIR" + ensure_usr_local_bin_persist + export PATH="/usr/local/bin:$PATH" + + $STD uv python update-shell || true + cache_installed_version "uv" "$LATEST_VERSION" + msg_ok "Setup uv $LATEST_VERSION" + + # Optional: Install specific Python version + if [[ -n "${PYTHON_VERSION:-}" ]]; then + local VERSION_MATCH + VERSION_MATCH=$(uv python list --only-downloads 2>/dev/null | + grep -E "^cpython-${PYTHON_VERSION//./\\.}\.[0-9]+-linux" | + cut -d'-' -f2 | sort -V | tail -n1) + + if [[ -z "$VERSION_MATCH" ]]; then + msg_error "No matching Python $PYTHON_VERSION.x version found" + return 1 + fi + + if ! uv python list 2>/dev/null | grep -q "cpython-${VERSION_MATCH}-linux.*uv/python"; then + $STD uv python install "$VERSION_MATCH" || { + msg_error "Failed to install Python $VERSION_MATCH" + return 1 + } + fi + fi } # ------------------------------------------------------------------------------ @@ -4034,76 +4034,76 @@ function setup_uv() { # ------------------------------------------------------------------------------ function setup_yq() { - local TMP_DIR=$(mktemp -d) - local BINARY_PATH="/usr/local/bin/yq" - local GITHUB_REPO="mikefarah/yq" + local TMP_DIR=$(mktemp -d) + local BINARY_PATH="/usr/local/bin/yq" + local GITHUB_REPO="mikefarah/yq" - ensure_dependencies jq - ensure_usr_local_bin_persist + ensure_dependencies jq + ensure_usr_local_bin_persist - # Remove non-mikefarah implementations - if command -v yq &>/dev/null; then - if ! yq --version 2>&1 | grep -q 'mikefarah'; then - rm -f "$(command -v yq)" + # Remove non-mikefarah implementations + if command -v yq &>/dev/null; then + if ! yq --version 2>&1 | grep -q 'mikefarah'; then + rm -f "$(command -v yq)" + fi fi - fi - local LATEST_VERSION - local releases_json - releases_json=$(curl -fsSL --max-time 15 "https://api.github.com/repos/${GITHUB_REPO}/releases/latest" 2>/dev/null || echo "") + local LATEST_VERSION + local releases_json + releases_json=$(curl -fsSL --max-time 15 "https://api.github.com/repos/${GITHUB_REPO}/releases/latest" 2>/dev/null || echo "") + + if [[ -z "$releases_json" ]]; then + msg_error "Could not fetch latest yq version from GitHub API" + rm -rf "$TMP_DIR" + return 1 + fi + + LATEST_VERSION=$(echo "$releases_json" | jq -r '.tag_name' 2>/dev/null | sed 's/^v//' || echo "") + + if [[ -z "$LATEST_VERSION" ]]; then + msg_error "Could not parse yq version from GitHub API response" + rm -rf "$TMP_DIR" + return 1 + fi + + # Get currently installed version + local INSTALLED_VERSION="" + if command -v yq &>/dev/null && yq --version 2>&1 | grep -q 'mikefarah'; then + INSTALLED_VERSION=$(yq --version 2>/dev/null | awk '{print $NF}' | sed 's/^v//') + fi + + # Scenario 1: Already at latest version + if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" == "$LATEST_VERSION" ]]; then + cache_installed_version "yq" "$LATEST_VERSION" + rm -rf "$TMP_DIR" + return 0 + fi + + # Scenario 2: New install or upgrade + if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" != "$LATEST_VERSION" ]]; then + msg_info "Upgrade yq from $INSTALLED_VERSION to $LATEST_VERSION" + else + msg_info "Setup yq $LATEST_VERSION" + fi + + curl -fsSL "https://github.com/${GITHUB_REPO}/releases/download/v${LATEST_VERSION}/yq_linux_amd64" -o "$TMP_DIR/yq" || { + msg_error "Failed to download yq" + rm -rf "$TMP_DIR" + return 1 + } + + chmod +x "$TMP_DIR/yq" + mv "$TMP_DIR/yq" "$BINARY_PATH" || { + msg_error "Failed to install yq" + rm -rf "$TMP_DIR" + return 1 + } - if [[ -z "$releases_json" ]]; then - msg_error "Could not fetch latest yq version from GitHub API" rm -rf "$TMP_DIR" - return 1 - fi + hash -r - LATEST_VERSION=$(echo "$releases_json" | jq -r '.tag_name' 2>/dev/null | sed 's/^v//' || echo "") - - if [[ -z "$LATEST_VERSION" ]]; then - msg_error "Could not parse yq version from GitHub API response" - rm -rf "$TMP_DIR" - return 1 - fi - - # Get currently installed version - local INSTALLED_VERSION="" - if command -v yq &>/dev/null && yq --version 2>&1 | grep -q 'mikefarah'; then - INSTALLED_VERSION=$(yq --version 2>/dev/null | awk '{print $NF}' | sed 's/^v//') - fi - - # Scenario 1: Already at latest version - if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" == "$LATEST_VERSION" ]]; then - cache_installed_version "yq" "$LATEST_VERSION" - rm -rf "$TMP_DIR" - return 0 - fi - - # Scenario 2: New install or upgrade - if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" != "$LATEST_VERSION" ]]; then - msg_info "Upgrade yq from $INSTALLED_VERSION to $LATEST_VERSION" - else - msg_info "Setup yq $LATEST_VERSION" - fi - - curl -fsSL "https://github.com/${GITHUB_REPO}/releases/download/v${LATEST_VERSION}/yq_linux_amd64" -o "$TMP_DIR/yq" || { - msg_error "Failed to download yq" - rm -rf "$TMP_DIR" - return 1 - } - - chmod +x "$TMP_DIR/yq" - mv "$TMP_DIR/yq" "$BINARY_PATH" || { - msg_error "Failed to install yq" - rm -rf "$TMP_DIR" - return 1 - } - - rm -rf "$TMP_DIR" - hash -r - - local FINAL_VERSION - FINAL_VERSION=$("$BINARY_PATH" --version 2>/dev/null | awk '{print $NF}' | sed 's/^v//') - cache_installed_version "yq" "$FINAL_VERSION" - msg_ok "Setup yq $FINAL_VERSION" + local FINAL_VERSION + FINAL_VERSION=$("$BINARY_PATH" --version 2>/dev/null | awk '{print $NF}' | sed 's/^v//') + cache_installed_version "yq" "$FINAL_VERSION" + msg_ok "Setup yq $FINAL_VERSION" } From f55fa4f60ed35ac445b9d75bb3549c46b40ef1c7 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 4 Nov 2025 13:18:36 +0100 Subject: [PATCH 1673/1733] Reformat misc/tools.func with consistent indentation Updated the entire misc/tools.func script to use consistent 2-space indentation for improved readability and maintainability. No functional changes were made. --- misc/tools.func | 6015 ++++++++++++++++++++++++----------------------- 1 file changed, 3022 insertions(+), 2993 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index dc3b00b45..e4a62e13b 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -8,20 +8,20 @@ # Cache installed version to avoid repeated checks # ------------------------------------------------------------------------------ cache_installed_version() { - local app="$1" - local version="$2" - mkdir -p /var/cache/app-versions - echo "$version" >"/var/cache/app-versions/${app}_version.txt" + local app="$1" + local version="$2" + mkdir -p /var/cache/app-versions + echo "$version" >"/var/cache/app-versions/${app}_version.txt" } get_cached_version() { - local app="$1" - mkdir -p /var/cache/app-versions - if [[ -f "/var/cache/app-versions/${app}_version.txt" ]]; then - cat "/var/cache/app-versions/${app}_version.txt" - return 0 - fi + local app="$1" + mkdir -p /var/cache/app-versions + if [[ -f "/var/cache/app-versions/${app}_version.txt" ]]; then + cat "/var/cache/app-versions/${app}_version.txt" return 0 + fi + return 0 } # ------------------------------------------------------------------------------ @@ -30,74 +30,74 @@ get_cached_version() { # Usage: is_tool_installed "mariadb" "11.4" || echo "Not installed" # ------------------------------------------------------------------------------ is_tool_installed() { - local tool_name="$1" - local required_version="${2:-}" - local installed_version="" + local tool_name="$1" + local required_version="${2:-}" + local installed_version="" - case "$tool_name" in - mariadb) - if command -v mariadb >/dev/null 2>&1; then - installed_version=$(mariadb --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1) - fi - ;; - mysql) - if command -v mysql >/dev/null 2>&1; then - installed_version=$(mysql --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1) - fi - ;; - mongodb | mongod) - if command -v mongod >/dev/null 2>&1; then - installed_version=$(mongod --version 2>/dev/null | awk '/db version/{print $3}' | cut -d. -f1,2) - fi - ;; - node | nodejs) - if command -v node >/dev/null 2>&1; then - installed_version=$(node -v 2>/dev/null | grep -oP '^v\K[0-9]+') - fi - ;; - php) - if command -v php >/dev/null 2>&1; then - installed_version=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2) - fi - ;; - postgres | postgresql) - if command -v psql >/dev/null 2>&1; then - installed_version=$(psql --version 2>/dev/null | awk '{print $3}' | cut -d. -f1) - fi - ;; - ruby) - if command -v ruby >/dev/null 2>&1; then - installed_version=$(ruby --version 2>/dev/null | awk '{print $2}' | cut -d. -f1,2) - fi - ;; - rust | rustc) - if command -v rustc >/dev/null 2>&1; then - installed_version=$(rustc --version 2>/dev/null | awk '{print $2}') - fi - ;; - go | golang) - if command -v go >/dev/null 2>&1; then - installed_version=$(go version 2>/dev/null | awk '{print $3}' | sed 's/go//') - fi - ;; - clickhouse) - if command -v clickhouse >/dev/null 2>&1; then - installed_version=$(clickhouse --version 2>/dev/null | awk '{print $2}') - fi - ;; - esac - - if [[ -z "$installed_version" ]]; then - return 1 # Not installed + case "$tool_name" in + mariadb) + if command -v mariadb >/dev/null 2>&1; then + installed_version=$(mariadb --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1) fi - - if [[ -n "$required_version" && "$installed_version" != "$required_version" ]]; then - echo "$installed_version" - return 1 # Version mismatch + ;; + mysql) + if command -v mysql >/dev/null 2>&1; then + installed_version=$(mysql --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1) fi + ;; + mongodb | mongod) + if command -v mongod >/dev/null 2>&1; then + installed_version=$(mongod --version 2>/dev/null | awk '/db version/{print $3}' | cut -d. -f1,2) + fi + ;; + node | nodejs) + if command -v node >/dev/null 2>&1; then + installed_version=$(node -v 2>/dev/null | grep -oP '^v\K[0-9]+') + fi + ;; + php) + if command -v php >/dev/null 2>&1; then + installed_version=$(php -v 2>/dev/null | awk '/^PHP/{print $2}' | cut -d. -f1,2) + fi + ;; + postgres | postgresql) + if command -v psql >/dev/null 2>&1; then + installed_version=$(psql --version 2>/dev/null | awk '{print $3}' | cut -d. -f1) + fi + ;; + ruby) + if command -v ruby >/dev/null 2>&1; then + installed_version=$(ruby --version 2>/dev/null | awk '{print $2}' | cut -d. -f1,2) + fi + ;; + rust | rustc) + if command -v rustc >/dev/null 2>&1; then + installed_version=$(rustc --version 2>/dev/null | awk '{print $2}') + fi + ;; + go | golang) + if command -v go >/dev/null 2>&1; then + installed_version=$(go version 2>/dev/null | awk '{print $3}' | sed 's/go//') + fi + ;; + clickhouse) + if command -v clickhouse >/dev/null 2>&1; then + installed_version=$(clickhouse --version 2>/dev/null | awk '{print $2}') + fi + ;; + esac + if [[ -z "$installed_version" ]]; then + return 1 # Not installed + fi + + if [[ -n "$required_version" && "$installed_version" != "$required_version" ]]; then echo "$installed_version" - return 0 # Installed and version matches (if specified) + return 1 # Version mismatch + fi + + echo "$installed_version" + return 0 # Installed and version matches (if specified) } # ------------------------------------------------------------------------------ @@ -105,65 +105,65 @@ is_tool_installed() { # Usage: remove_old_tool_version "mariadb" "repository-name" # ------------------------------------------------------------------------------ remove_old_tool_version() { - local tool_name="$1" - local repo_name="${2:-$tool_name}" + local tool_name="$1" + local repo_name="${2:-$tool_name}" - case "$tool_name" in - mariadb) - $STD systemctl stop mariadb >/dev/null 2>&1 || true - $STD apt purge -y 'mariadb*' >/dev/null 2>&1 || true - ;; - mysql) - $STD systemctl stop mysql >/dev/null 2>&1 || true - $STD apt purge -y 'mysql*' >/dev/null 2>&1 || true - rm -rf /var/lib/mysql >/dev/null 2>&1 || true - ;; - mongodb) - $STD systemctl stop mongod >/dev/null 2>&1 || true - $STD apt purge -y 'mongodb*' >/dev/null 2>&1 || true - rm -rf /var/lib/mongodb >/dev/null 2>&1 || true - ;; - node | nodejs) - $STD apt purge -y nodejs npm >/dev/null 2>&1 || true - npm list -g 2>/dev/null | grep -oE '^ \S+' | awk '{print $1}' | while read -r module; do - npm uninstall -g "$module" >/dev/null 2>&1 || true - done - ;; - php) - # Disable PHP-FPM if running - $STD systemctl disable php*-fpm >/dev/null 2>&1 || true - $STD systemctl stop php*-fpm >/dev/null 2>&1 || true - $STD apt purge -y 'php*' >/dev/null 2>&1 || true - rm -rf /etc/php >/dev/null 2>&1 || true - ;; - postgresql) - $STD systemctl stop postgresql >/dev/null 2>&1 || true - $STD apt purge -y 'postgresql*' >/dev/null 2>&1 || true - rm -rf /var/lib/postgresql >/dev/null 2>&1 || true - ;; - ruby) - if [[ -d "$HOME/.rbenv" ]]; then - rm -rf "$HOME/.rbenv" - fi - $STD apt purge -y 'ruby*' >/dev/null 2>&1 || true - ;; - rust) - rm -rf "$HOME/.cargo" "$HOME/.rustup" >/dev/null 2>&1 || true - ;; - go | golang) - rm -rf /usr/local/go >/dev/null 2>&1 || true - ;; - clickhouse) - $STD systemctl stop clickhouse-server >/dev/null 2>&1 || true - $STD apt purge -y 'clickhouse*' >/dev/null 2>&1 || true - rm -rf /var/lib/clickhouse >/dev/null 2>&1 || true - ;; - esac + case "$tool_name" in + mariadb) + $STD systemctl stop mariadb >/dev/null 2>&1 || true + $STD apt purge -y 'mariadb*' >/dev/null 2>&1 || true + ;; + mysql) + $STD systemctl stop mysql >/dev/null 2>&1 || true + $STD apt purge -y 'mysql*' >/dev/null 2>&1 || true + rm -rf /var/lib/mysql >/dev/null 2>&1 || true + ;; + mongodb) + $STD systemctl stop mongod >/dev/null 2>&1 || true + $STD apt purge -y 'mongodb*' >/dev/null 2>&1 || true + rm -rf /var/lib/mongodb >/dev/null 2>&1 || true + ;; + node | nodejs) + $STD apt purge -y nodejs npm >/dev/null 2>&1 || true + npm list -g 2>/dev/null | grep -oE '^ \S+' | awk '{print $1}' | while read -r module; do + npm uninstall -g "$module" >/dev/null 2>&1 || true + done + ;; + php) + # Disable PHP-FPM if running + $STD systemctl disable php*-fpm >/dev/null 2>&1 || true + $STD systemctl stop php*-fpm >/dev/null 2>&1 || true + $STD apt purge -y 'php*' >/dev/null 2>&1 || true + rm -rf /etc/php >/dev/null 2>&1 || true + ;; + postgresql) + $STD systemctl stop postgresql >/dev/null 2>&1 || true + $STD apt purge -y 'postgresql*' >/dev/null 2>&1 || true + rm -rf /var/lib/postgresql >/dev/null 2>&1 || true + ;; + ruby) + if [[ -d "$HOME/.rbenv" ]]; then + rm -rf "$HOME/.rbenv" + fi + $STD apt purge -y 'ruby*' >/dev/null 2>&1 || true + ;; + rust) + rm -rf "$HOME/.cargo" "$HOME/.rustup" >/dev/null 2>&1 || true + ;; + go | golang) + rm -rf /usr/local/go >/dev/null 2>&1 || true + ;; + clickhouse) + $STD systemctl stop clickhouse-server >/dev/null 2>&1 || true + $STD apt purge -y 'clickhouse*' >/dev/null 2>&1 || true + rm -rf /var/lib/clickhouse >/dev/null 2>&1 || true + ;; + esac - # Clean up old repositories - cleanup_old_repo_files "$repo_name" + # Clean up old repositories + cleanup_old_repo_files "$repo_name" - return 0 + return 0 } # ------------------------------------------------------------------------------ @@ -172,19 +172,19 @@ remove_old_tool_version() { # Usage: if should_update_tool "mariadb" "11.4"; then ... fi # ------------------------------------------------------------------------------ should_update_tool() { - local tool_name="$1" - local target_version="$2" - local current_version="" + local tool_name="$1" + local target_version="$2" + local current_version="" - # Get currently installed version - current_version=$(is_tool_installed "$tool_name" 2>/dev/null) || return 0 # Not installed = needs install + # Get currently installed version + current_version=$(is_tool_installed "$tool_name" 2>/dev/null) || return 0 # Not installed = needs install - # If versions are identical, no update needed - if [[ "$current_version" == "$target_version" ]]; then - return 1 # No update needed - fi + # If versions are identical, no update needed + if [[ "$current_version" == "$target_version" ]]; then + return 1 # No update needed + fi - return 0 # Update needed + return 0 # Update needed } # ---------------------–---------------------------------------------------------- @@ -194,59 +194,59 @@ should_update_tool() { # Supports: mariadb, mongodb, nodejs, postgresql, php, mysql # ------------------------------------------------------------------------------ manage_tool_repository() { - local tool_name="$1" - local version="$2" - local repo_url="$3" - local gpg_key_url="${4:-}" - local distro_id repo_component suite + local tool_name="$1" + local version="$2" + local repo_url="$3" + local gpg_key_url="${4:-}" + local distro_id repo_component suite - distro_id=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + distro_id=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') - case "$tool_name" in - mariadb) - if [[ -z "$repo_url" || -z "$gpg_key_url" ]]; then - msg_error "MariaDB repository requires repo_url and gpg_key_url" - return 1 - fi + case "$tool_name" in + mariadb) + if [[ -z "$repo_url" || -z "$gpg_key_url" ]]; then + msg_error "MariaDB repository requires repo_url and gpg_key_url" + return 1 + fi - # Clean old repos first - cleanup_old_repo_files "mariadb" + # Clean old repos first + cleanup_old_repo_files "mariadb" - # Get suite for fallback handling - local distro_codename - distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - suite=$(get_fallback_suite "$distro_id" "$distro_codename" "$repo_url/$distro_id") + # Get suite for fallback handling + local distro_codename + distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + suite=$(get_fallback_suite "$distro_id" "$distro_codename" "$repo_url/$distro_id") - # Setup new repository using deb822 format - setup_deb822_repo "mariadb" "$gpg_key_url" "$repo_url/$distro_id" "$suite" "main" "amd64 arm64" || return 1 - return 0 - ;; + # Setup new repository using deb822 format + setup_deb822_repo "mariadb" "$gpg_key_url" "$repo_url/$distro_id" "$suite" "main" "amd64 arm64" || return 1 + return 0 + ;; - mongodb) - if [[ -z "$repo_url" || -z "$gpg_key_url" ]]; then - msg_error "MongoDB repository requires repo_url and gpg_key_url" - return 1 - fi + mongodb) + if [[ -z "$repo_url" || -z "$gpg_key_url" ]]; then + msg_error "MongoDB repository requires repo_url and gpg_key_url" + return 1 + fi - # Clean old repos first - cleanup_old_repo_files "mongodb" + # Clean old repos first + cleanup_old_repo_files "mongodb" - # Import GPG key - mkdir -p /etc/apt/keyrings - if ! curl -fsSL "$gpg_key_url" | gpg --dearmor --yes -o "/etc/apt/keyrings/mongodb-server-${version}.gpg" 2>/dev/null; then - msg_error "Failed to download MongoDB GPG key" - return 1 - fi + # Import GPG key + mkdir -p /etc/apt/keyrings + if ! curl -fsSL "$gpg_key_url" | gpg --dearmor --yes -o "/etc/apt/keyrings/mongodb-server-${version}.gpg" 2>/dev/null; then + msg_error "Failed to download MongoDB GPG key" + return 1 + fi - # Setup repository - local distro_codename - distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - suite=$(get_fallback_suite "$distro_id" "$distro_codename" "$repo_url") + # Setup repository + local distro_codename + distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + suite=$(get_fallback_suite "$distro_id" "$distro_codename" "$repo_url") - repo_component="main" - [[ "$distro_id" == "ubuntu" ]] && repo_component="multiverse" + repo_component="main" + [[ "$distro_id" == "ubuntu" ]] && repo_component="multiverse" - cat </etc/apt/sources.list.d/mongodb-org-${version}.sources + cat </etc/apt/sources.list.d/mongodb-org-${version}.sources Types: deb URIs: ${repo_url} Suites: ${suite}/mongodb-org/${version} @@ -254,31 +254,31 @@ Components: ${repo_component} Architectures: amd64 arm64 Signed-By: /etc/apt/keyrings/mongodb-server-${version}.gpg EOF - return 0 - ;; + return 0 + ;; - nodejs) - if [[ -z "$repo_url" || -z "$gpg_key_url" ]]; then - msg_error "Node.js repository requires repo_url and gpg_key_url" - return 1 - fi + nodejs) + if [[ -z "$repo_url" || -z "$gpg_key_url" ]]; then + msg_error "Node.js repository requires repo_url and gpg_key_url" + return 1 + fi - cleanup_old_repo_files "nodesource" + cleanup_old_repo_files "nodesource" - # NodeSource uses deb822 format with GPG from repo - local distro_codename - distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + # NodeSource uses deb822 format with GPG from repo + local distro_codename + distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - # Create keyring directory first - mkdir -p /etc/apt/keyrings + # Create keyring directory first + mkdir -p /etc/apt/keyrings - # Download GPG key from NodeSource - curl -fsSL "$gpg_key_url" | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg || { - msg_error "Failed to import NodeSource GPG key" - return 1 - } + # Download GPG key from NodeSource + curl -fsSL "$gpg_key_url" | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg || { + msg_error "Failed to import NodeSource GPG key" + return 1 + } - cat </etc/apt/sources.list.d/nodesource.sources + cat </etc/apt/sources.list.d/nodesource.sources Types: deb URIs: $repo_url Suites: nodistro @@ -286,33 +286,33 @@ Components: main Architectures: amd64 arm64 Signed-By: /etc/apt/keyrings/nodesource.gpg EOF - return 0 - ;; + return 0 + ;; - php) - if [[ -z "$gpg_key_url" ]]; then - msg_error "PHP repository requires gpg_key_url" - return 1 - fi + php) + if [[ -z "$gpg_key_url" ]]; then + msg_error "PHP repository requires gpg_key_url" + return 1 + fi - cleanup_old_repo_files "php" + cleanup_old_repo_files "php" - # Download and install keyring - curl -fsSLo /tmp/debsuryorg-archive-keyring.deb "$gpg_key_url" || { - msg_error "Failed to download PHP keyring" - return 1 - } - dpkg -i /tmp/debsuryorg-archive-keyring.deb >/dev/null 2>&1 || { - msg_error "Failed to install PHP keyring" - rm -f /tmp/debsuryorg-archive-keyring.deb - return 1 - } - rm -f /tmp/debsuryorg-archive-keyring.deb + # Download and install keyring + curl -fsSLo /tmp/debsuryorg-archive-keyring.deb "$gpg_key_url" || { + msg_error "Failed to download PHP keyring" + return 1 + } + dpkg -i /tmp/debsuryorg-archive-keyring.deb >/dev/null 2>&1 || { + msg_error "Failed to install PHP keyring" + rm -f /tmp/debsuryorg-archive-keyring.deb + return 1 + } + rm -f /tmp/debsuryorg-archive-keyring.deb - # Setup repository - local distro_codename - distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - cat </etc/apt/sources.list.d/php.sources + # Setup repository + local distro_codename + distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + cat </etc/apt/sources.list.d/php.sources Types: deb URIs: https://packages.sury.org/php Suites: $distro_codename @@ -320,30 +320,30 @@ Components: main Architectures: amd64 arm64 Signed-By: /usr/share/keyrings/deb.sury.org-php.gpg EOF - return 0 - ;; + return 0 + ;; - postgresql) - if [[ -z "$gpg_key_url" ]]; then - msg_error "PostgreSQL repository requires gpg_key_url" - return 1 - fi + postgresql) + if [[ -z "$gpg_key_url" ]]; then + msg_error "PostgreSQL repository requires gpg_key_url" + return 1 + fi - cleanup_old_repo_files "postgresql" + cleanup_old_repo_files "postgresql" - # Create keyring directory first - mkdir -p /etc/apt/keyrings + # Create keyring directory first + mkdir -p /etc/apt/keyrings - # Import PostgreSQL key - curl -fsSL "$gpg_key_url" | gpg --dearmor -o /etc/apt/keyrings/postgresql.gpg || { - msg_error "Failed to import PostgreSQL GPG key" - return 1 - } + # Import PostgreSQL key + curl -fsSL "$gpg_key_url" | gpg --dearmor -o /etc/apt/keyrings/postgresql.gpg || { + msg_error "Failed to import PostgreSQL GPG key" + return 1 + } - # Setup repository - local distro_codename - distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - cat </etc/apt/sources.list.d/postgresql.sources + # Setup repository + local distro_codename + distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + cat </etc/apt/sources.list.d/postgresql.sources Types: deb URIs: http://apt.postgresql.org/pub/repos/apt Suites: $distro_codename-pgdg @@ -351,532 +351,532 @@ Components: main Architectures: amd64 arm64 Signed-By: /etc/apt/keyrings/postgresql.gpg EOF - return 0 - ;; - - *) - msg_error "Unknown tool repository: $tool_name" - return 1 - ;; - esac - return 0 + ;; + + *) + msg_error "Unknown tool repository: $tool_name" + return 1 + ;; + esac + + return 0 } # ------–---------------------------------------------------------------------- # Unified package upgrade function (with apt update caching) # ------------------------------------------------------------------------------ upgrade_package() { - local package="$1" + local package="$1" - # Use same caching logic as ensure_dependencies - local apt_cache_file="/var/cache/apt-update-timestamp" - local current_time=$(date +%s) - local last_update=0 + # Use same caching logic as ensure_dependencies + local apt_cache_file="/var/cache/apt-update-timestamp" + local current_time=$(date +%s) + local last_update=0 - if [[ -f "$apt_cache_file" ]]; then - last_update=$(cat "$apt_cache_file" 2>/dev/null || echo 0) - fi + if [[ -f "$apt_cache_file" ]]; then + last_update=$(cat "$apt_cache_file" 2>/dev/null || echo 0) + fi - if ((current_time - last_update > 300)); then - $STD apt update || { - msg_warn "APT update failed in upgrade_package - continuing with cached packages" - } - echo "$current_time" >"$apt_cache_file" - fi - - $STD apt install --only-upgrade -y "$package" || { - msg_warn "Failed to upgrade $package" - return 1 + if ((current_time - last_update > 300)); then + $STD apt update || { + msg_warn "APT update failed in upgrade_package - continuing with cached packages" } + echo "$current_time" >"$apt_cache_file" + fi + + $STD apt install --only-upgrade -y "$package" || { + msg_warn "Failed to upgrade $package" + return 1 + } } # ------------------------------------------------------------------------------ # Repository availability check # ------------------------------------------------------------------------------ verify_repo_available() { - local repo_url="$1" - local suite="$2" + local repo_url="$1" + local suite="$2" - if curl -fsSL --max-time 10 "${repo_url}/dists/${suite}/Release" &>/dev/null; then - return 0 - fi - return 1 + if curl -fsSL --max-time 10 "${repo_url}/dists/${suite}/Release" &>/dev/null; then + return 0 + fi + return 1 } # ------------------------------------------------------------------------------ # Ensure dependencies are installed (with apt update caching) # ------------------------------------------------------------------------------ ensure_dependencies() { - local deps=("$@") - local missing=() + local deps=("$@") + local missing=() - for dep in "${deps[@]}"; do - if ! command -v "$dep" &>/dev/null && ! is_package_installed "$dep"; then - missing+=("$dep") - fi - done - - if [[ ${#missing[@]} -gt 0 ]]; then - # Only run apt update if not done recently (within last 5 minutes) - local apt_cache_file="/var/cache/apt-update-timestamp" - local current_time=$(date +%s) - local last_update=0 - - if [[ -f "$apt_cache_file" ]]; then - last_update=$(cat "$apt_cache_file" 2>/dev/null || echo 0) - fi - - if ((current_time - last_update > 300)); then - # Ensure orphaned sources are cleaned before updating - cleanup_orphaned_sources 2>/dev/null || true - - if ! $STD apt update; then - ensure_apt_working || return 1 - fi - echo "$current_time" >"$apt_cache_file" - fi - - $STD apt install -y "${missing[@]}" || { - msg_error "Failed to install dependencies: ${missing[*]}" - return 1 - } + for dep in "${deps[@]}"; do + if ! command -v "$dep" &>/dev/null && ! is_package_installed "$dep"; then + missing+=("$dep") fi + done + + if [[ ${#missing[@]} -gt 0 ]]; then + # Only run apt update if not done recently (within last 5 minutes) + local apt_cache_file="/var/cache/apt-update-timestamp" + local current_time=$(date +%s) + local last_update=0 + + if [[ -f "$apt_cache_file" ]]; then + last_update=$(cat "$apt_cache_file" 2>/dev/null || echo 0) + fi + + if ((current_time - last_update > 300)); then + # Ensure orphaned sources are cleaned before updating + cleanup_orphaned_sources 2>/dev/null || true + + if ! $STD apt update; then + ensure_apt_working || return 1 + fi + echo "$current_time" >"$apt_cache_file" + fi + + $STD apt install -y "${missing[@]}" || { + msg_error "Failed to install dependencies: ${missing[*]}" + return 1 + } + fi } # ------------------------------------------------------------------------------ # Smart version comparison # ------------------------------------------------------------------------------ version_gt() { - test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1" + test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1" } # ------------------------------------------------------------------------------ # Get system architecture (normalized) # ------------------------------------------------------------------------------ get_system_arch() { - local arch_type="${1:-dpkg}" # dpkg, uname, or both - local arch + local arch_type="${1:-dpkg}" # dpkg, uname, or both + local arch - case "$arch_type" in - dpkg) - arch=$(dpkg --print-architecture 2>/dev/null) - ;; - uname) - arch=$(uname -m) - [[ "$arch" == "x86_64" ]] && arch="amd64" - [[ "$arch" == "aarch64" ]] && arch="arm64" - ;; - both | *) - arch=$(dpkg --print-architecture 2>/dev/null || uname -m) - [[ "$arch" == "x86_64" ]] && arch="amd64" - [[ "$arch" == "aarch64" ]] && arch="arm64" - ;; - esac + case "$arch_type" in + dpkg) + arch=$(dpkg --print-architecture 2>/dev/null) + ;; + uname) + arch=$(uname -m) + [[ "$arch" == "x86_64" ]] && arch="amd64" + [[ "$arch" == "aarch64" ]] && arch="arm64" + ;; + both | *) + arch=$(dpkg --print-architecture 2>/dev/null || uname -m) + [[ "$arch" == "x86_64" ]] && arch="amd64" + [[ "$arch" == "aarch64" ]] && arch="arm64" + ;; + esac - echo "$arch" + echo "$arch" } # ------------------------------------------------------------------------------ # Create temporary directory with automatic cleanup # ------------------------------------------------------------------------------ create_temp_dir() { - local tmp_dir=$(mktemp -d) - # Set trap to cleanup on EXIT, ERR, INT, TERM - trap "rm -rf '$tmp_dir'" EXIT ERR INT TERM - echo "$tmp_dir" + local tmp_dir=$(mktemp -d) + # Set trap to cleanup on EXIT, ERR, INT, TERM + trap "rm -rf '$tmp_dir'" EXIT ERR INT TERM + echo "$tmp_dir" } # ------------------------------------------------------------------------------ # Check if package is installed (faster than dpkg -l | grep) # ------------------------------------------------------------------------------ is_package_installed() { - local package="$1" - dpkg-query -W -f='${Status}' "$package" 2>/dev/null | grep -q "^install ok installed$" + local package="$1" + dpkg-query -W -f='${Status}' "$package" 2>/dev/null | grep -q "^install ok installed$" } # ------------------------------------------------------------------------------ # GitHub API call with authentication and rate limit handling # ------------------------------------------------------------------------------ github_api_call() { - local url="$1" - local output_file="${2:-/dev/stdout}" - local max_retries=3 - local retry_delay=2 + local url="$1" + local output_file="${2:-/dev/stdout}" + local max_retries=3 + local retry_delay=2 - local header_args=() - [[ -n "${GITHUB_TOKEN:-}" ]] && header_args=(-H "Authorization: Bearer $GITHUB_TOKEN") + local header_args=() + [[ -n "${GITHUB_TOKEN:-}" ]] && header_args=(-H "Authorization: Bearer $GITHUB_TOKEN") - for attempt in $(seq 1 $max_retries); do - local http_code - http_code=$(curl -fsSL -w "%{http_code}" -o "$output_file" \ - -H "Accept: application/vnd.github+json" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ - "${header_args[@]}" \ - "$url" 2>/dev/null || echo "000") + for attempt in $(seq 1 $max_retries); do + local http_code + http_code=$(curl -fsSL -w "%{http_code}" -o "$output_file" \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "${header_args[@]}" \ + "$url" 2>/dev/null || echo "000") - case "$http_code" in - 200) - return 0 - ;; - 403) - # Rate limit - check if we can retry - if [[ $attempt -lt $max_retries ]]; then - msg_warn "GitHub API rate limit, waiting ${retry_delay}s... (attempt $attempt/$max_retries)" - sleep "$retry_delay" - retry_delay=$((retry_delay * 2)) - continue - fi - msg_error "GitHub API rate limit exceeded. Set GITHUB_TOKEN to increase limits." - return 1 - ;; - 404) - msg_error "GitHub API endpoint not found: $url" - return 1 - ;; - *) - if [[ $attempt -lt $max_retries ]]; then - sleep "$retry_delay" - continue - fi - msg_error "GitHub API call failed with HTTP $http_code" - return 1 - ;; - esac - done + case "$http_code" in + 200) + return 0 + ;; + 403) + # Rate limit - check if we can retry + if [[ $attempt -lt $max_retries ]]; then + msg_warn "GitHub API rate limit, waiting ${retry_delay}s... (attempt $attempt/$max_retries)" + sleep "$retry_delay" + retry_delay=$((retry_delay * 2)) + continue + fi + msg_error "GitHub API rate limit exceeded. Set GITHUB_TOKEN to increase limits." + return 1 + ;; + 404) + msg_error "GitHub API endpoint not found: $url" + return 1 + ;; + *) + if [[ $attempt -lt $max_retries ]]; then + sleep "$retry_delay" + continue + fi + msg_error "GitHub API call failed with HTTP $http_code" + return 1 + ;; + esac + done - return 1 + return 1 } should_upgrade() { - local current="$1" - local target="$2" + local current="$1" + local target="$2" - [[ -z "$current" ]] && return 0 - version_gt "$target" "$current" && return 0 - return 1 + [[ -z "$current" ]] && return 0 + version_gt "$target" "$current" && return 0 + return 1 } # ------------------------------------------------------------------------------ # Get OS information (cached for performance) # ------------------------------------------------------------------------------ get_os_info() { - local field="${1:-all}" # id, codename, version, version_id, all + local field="${1:-all}" # id, codename, version, version_id, all - # Cache OS info to avoid repeated file reads - if [[ -z "${_OS_ID:-}" ]]; then - export _OS_ID=$(awk -F= '/^ID=/{gsub(/"/,"",$2); print $2}' /etc/os-release) - export _OS_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{gsub(/"/,"",$2); print $2}' /etc/os-release) - export _OS_VERSION=$(awk -F= '/^VERSION_ID=/{gsub(/"/,"",$2); print $2}' /etc/os-release) - export _OS_VERSION_FULL=$(awk -F= '/^VERSION=/{gsub(/"/,"",$2); print $2}' /etc/os-release) - fi + # Cache OS info to avoid repeated file reads + if [[ -z "${_OS_ID:-}" ]]; then + export _OS_ID=$(awk -F= '/^ID=/{gsub(/"/,"",$2); print $2}' /etc/os-release) + export _OS_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{gsub(/"/,"",$2); print $2}' /etc/os-release) + export _OS_VERSION=$(awk -F= '/^VERSION_ID=/{gsub(/"/,"",$2); print $2}' /etc/os-release) + export _OS_VERSION_FULL=$(awk -F= '/^VERSION=/{gsub(/"/,"",$2); print $2}' /etc/os-release) + fi - case "$field" in - id) echo "$_OS_ID" ;; - codename) echo "$_OS_CODENAME" ;; - version) echo "$_OS_VERSION" ;; - version_id) echo "$_OS_VERSION" ;; - version_full) echo "$_OS_VERSION_FULL" ;; - all) echo "ID=$_OS_ID CODENAME=$_OS_CODENAME VERSION=$_OS_VERSION" ;; - *) echo "$_OS_ID" ;; - esac + case "$field" in + id) echo "$_OS_ID" ;; + codename) echo "$_OS_CODENAME" ;; + version) echo "$_OS_VERSION" ;; + version_id) echo "$_OS_VERSION" ;; + version_full) echo "$_OS_VERSION_FULL" ;; + all) echo "ID=$_OS_ID CODENAME=$_OS_CODENAME VERSION=$_OS_VERSION" ;; + *) echo "$_OS_ID" ;; + esac } # ------------------------------------------------------------------------------ # Check if running on specific OS # ------------------------------------------------------------------------------ is_debian() { - [[ "$(get_os_info id)" == "debian" ]] + [[ "$(get_os_info id)" == "debian" ]] } is_ubuntu() { - [[ "$(get_os_info id)" == "ubuntu" ]] + [[ "$(get_os_info id)" == "ubuntu" ]] } is_alpine() { - [[ "$(get_os_info id)" == "alpine" ]] + [[ "$(get_os_info id)" == "alpine" ]] } # ------------------------------------------------------------------------------ # Get Debian/Ubuntu major version # ------------------------------------------------------------------------------ get_os_version_major() { - local version=$(get_os_info version) - echo "${version%%.*}" + local version=$(get_os_info version) + echo "${version%%.*}" } # ------------------------------------------------------------------------------ # Download file with retry logic and progress # ------------------------------------------------------------------------------ download_file() { - local url="$1" - local output="$2" - local max_retries="${3:-3}" - local show_progress="${4:-false}" + local url="$1" + local output="$2" + local max_retries="${3:-3}" + local show_progress="${4:-false}" - local curl_opts=(-fsSL) - [[ "$show_progress" == "true" ]] && curl_opts=(-fL#) + local curl_opts=(-fsSL) + [[ "$show_progress" == "true" ]] && curl_opts=(-fL#) - for attempt in $(seq 1 $max_retries); do - if curl "${curl_opts[@]}" -o "$output" "$url"; then - return 0 - fi + for attempt in $(seq 1 $max_retries); do + if curl "${curl_opts[@]}" -o "$output" "$url"; then + return 0 + fi - if [[ $attempt -lt $max_retries ]]; then - msg_warn "Download failed, retrying... (attempt $attempt/$max_retries)" - sleep 2 - fi - done + if [[ $attempt -lt $max_retries ]]; then + msg_warn "Download failed, retrying... (attempt $attempt/$max_retries)" + sleep 2 + fi + done - msg_error "Failed to download: $url" - return 1 + msg_error "Failed to download: $url" + return 1 } # ------------------------------------------------------------------------------ # Get fallback suite for repository (comprehensive mapping) # ------------------------------------------------------------------------------ get_fallback_suite() { - local distro_id="$1" - local distro_codename="$2" - local repo_base_url="$3" + local distro_id="$1" + local distro_codename="$2" + local repo_base_url="$3" - # Check if current codename works - if verify_repo_available "$repo_base_url" "$distro_codename"; then - echo "$distro_codename" - return 0 - fi + # Check if current codename works + if verify_repo_available "$repo_base_url" "$distro_codename"; then + echo "$distro_codename" + return 0 + fi - # Comprehensive fallback mappings - case "$distro_id" in - debian) - case "$distro_codename" in - # Debian 13 (Trixie) → Debian 12 (Bookworm) - trixie | forky | sid) - echo "bookworm" - ;; - # Debian 12 (Bookworm) stays - bookworm) - echo "bookworm" - ;; - # Debian 11 (Bullseye) stays - bullseye) - echo "bullseye" - ;; - # Unknown → latest stable - *) - echo "bookworm" - ;; - esac - ;; - ubuntu) - case "$distro_codename" in - # Ubuntu 24.10 (Oracular) → 24.04 LTS (Noble) - oracular | plucky) - echo "noble" - ;; - # Ubuntu 24.04 LTS (Noble) stays - noble) - echo "noble" - ;; - # Ubuntu 23.10 (Mantic) → 22.04 LTS (Jammy) - mantic | lunar) - echo "jammy" - ;; - # Ubuntu 22.04 LTS (Jammy) stays - jammy) - echo "jammy" - ;; - # Ubuntu 20.04 LTS (Focal) stays - focal) - echo "focal" - ;; - # Unknown → latest LTS - *) - echo "jammy" - ;; - esac - ;; + # Comprehensive fallback mappings + case "$distro_id" in + debian) + case "$distro_codename" in + # Debian 13 (Trixie) → Debian 12 (Bookworm) + trixie | forky | sid) + echo "bookworm" + ;; + # Debian 12 (Bookworm) stays + bookworm) + echo "bookworm" + ;; + # Debian 11 (Bullseye) stays + bullseye) + echo "bullseye" + ;; + # Unknown → latest stable *) - echo "$distro_codename" - ;; + echo "bookworm" + ;; esac + ;; + ubuntu) + case "$distro_codename" in + # Ubuntu 24.10 (Oracular) → 24.04 LTS (Noble) + oracular | plucky) + echo "noble" + ;; + # Ubuntu 24.04 LTS (Noble) stays + noble) + echo "noble" + ;; + # Ubuntu 23.10 (Mantic) → 22.04 LTS (Jammy) + mantic | lunar) + echo "jammy" + ;; + # Ubuntu 22.04 LTS (Jammy) stays + jammy) + echo "jammy" + ;; + # Ubuntu 20.04 LTS (Focal) stays + focal) + echo "focal" + ;; + # Unknown → latest LTS + *) + echo "jammy" + ;; + esac + ;; + *) + echo "$distro_codename" + ;; + esac } # ------------------------------------------------------------------------------ # Verify package source and version # ------------------------------------------------------------------------------ verify_package_source() { - local package="$1" - local expected_version="$2" + local package="$1" + local expected_version="$2" - if apt-cache policy "$package" 2>/dev/null | grep -q "$expected_version"; then - return 0 - fi - return 1 + if apt-cache policy "$package" 2>/dev/null | grep -q "$expected_version"; then + return 0 + fi + return 1 } # ------------------------------------------------------------------------------ # Check if running on LTS version # ------------------------------------------------------------------------------ is_lts_version() { - local os_id=$(get_os_info id) - local codename=$(get_os_info codename) + local os_id=$(get_os_info id) + local codename=$(get_os_info codename) - if [[ "$os_id" == "ubuntu" ]]; then - case "$codename" in - focal | jammy | noble) return 0 ;; # 20.04, 22.04, 24.04 - *) return 1 ;; - esac - elif [[ "$os_id" == "debian" ]]; then - # Debian releases are all "stable" - case "$codename" in - bullseye | bookworm | trixie) return 0 ;; - *) return 1 ;; - esac - fi + if [[ "$os_id" == "ubuntu" ]]; then + case "$codename" in + focal | jammy | noble) return 0 ;; # 20.04, 22.04, 24.04 + *) return 1 ;; + esac + elif [[ "$os_id" == "debian" ]]; then + # Debian releases are all "stable" + case "$codename" in + bullseye | bookworm | trixie) return 0 ;; + *) return 1 ;; + esac + fi - return 1 + return 1 } # ------------------------------------------------------------------------------ # Get optimal number of parallel jobs (cached) # ------------------------------------------------------------------------------ get_parallel_jobs() { - if [[ -z "${_PARALLEL_JOBS:-}" ]]; then - local cpu_count=$(nproc 2>/dev/null || echo 1) - local mem_gb=$(free -g | awk '/^Mem:/{print $2}') + if [[ -z "${_PARALLEL_JOBS:-}" ]]; then + local cpu_count=$(nproc 2>/dev/null || echo 1) + local mem_gb=$(free -g | awk '/^Mem:/{print $2}') - # Limit by available memory (assume 1GB per job for compilation) - local max_by_mem=$((mem_gb > 0 ? mem_gb : 1)) - local max_jobs=$((cpu_count < max_by_mem ? cpu_count : max_by_mem)) + # Limit by available memory (assume 1GB per job for compilation) + local max_by_mem=$((mem_gb > 0 ? mem_gb : 1)) + local max_jobs=$((cpu_count < max_by_mem ? cpu_count : max_by_mem)) - # At least 1, at most cpu_count - export _PARALLEL_JOBS=$((max_jobs > 0 ? max_jobs : 1)) - fi - echo "$_PARALLEL_JOBS" + # At least 1, at most cpu_count + export _PARALLEL_JOBS=$((max_jobs > 0 ? max_jobs : 1)) + fi + echo "$_PARALLEL_JOBS" } # ------------------------------------------------------------------------------ # Get default PHP version for OS # ------------------------------------------------------------------------------ get_default_php_version() { - local os_id=$(get_os_info id) - local os_version=$(get_os_version_major) + local os_id=$(get_os_info id) + local os_version=$(get_os_version_major) - case "$os_id" in - debian) - case "$os_version" in - 13) echo "8.3" ;; # Debian 13 (Trixie) - 12) echo "8.2" ;; # Debian 12 (Bookworm) - 11) echo "7.4" ;; # Debian 11 (Bullseye) - *) echo "8.2" ;; - esac - ;; - ubuntu) - case "$os_version" in - 24) echo "8.3" ;; # Ubuntu 24.04 LTS (Noble) - 22) echo "8.1" ;; # Ubuntu 22.04 LTS (Jammy) - 20) echo "7.4" ;; # Ubuntu 20.04 LTS (Focal) - *) echo "8.1" ;; - esac - ;; - *) - echo "8.2" - ;; + case "$os_id" in + debian) + case "$os_version" in + 13) echo "8.3" ;; # Debian 13 (Trixie) + 12) echo "8.2" ;; # Debian 12 (Bookworm) + 11) echo "7.4" ;; # Debian 11 (Bullseye) + *) echo "8.2" ;; esac + ;; + ubuntu) + case "$os_version" in + 24) echo "8.3" ;; # Ubuntu 24.04 LTS (Noble) + 22) echo "8.1" ;; # Ubuntu 22.04 LTS (Jammy) + 20) echo "7.4" ;; # Ubuntu 20.04 LTS (Focal) + *) echo "8.1" ;; + esac + ;; + *) + echo "8.2" + ;; + esac } # ------------------------------------------------------------------------------ # Get default Python version for OS # ------------------------------------------------------------------------------ get_default_python_version() { - local os_id=$(get_os_info id) - local os_version=$(get_os_version_major) + local os_id=$(get_os_info id) + local os_version=$(get_os_version_major) - case "$os_id" in - debian) - case "$os_version" in - 13) echo "3.12" ;; # Debian 13 (Trixie) - 12) echo "3.11" ;; # Debian 12 (Bookworm) - 11) echo "3.9" ;; # Debian 11 (Bullseye) - *) echo "3.11" ;; - esac - ;; - ubuntu) - case "$os_version" in - 24) echo "3.12" ;; # Ubuntu 24.04 LTS - 22) echo "3.10" ;; # Ubuntu 22.04 LTS - 20) echo "3.8" ;; # Ubuntu 20.04 LTS - *) echo "3.10" ;; - esac - ;; - *) - echo "3.11" - ;; + case "$os_id" in + debian) + case "$os_version" in + 13) echo "3.12" ;; # Debian 13 (Trixie) + 12) echo "3.11" ;; # Debian 12 (Bookworm) + 11) echo "3.9" ;; # Debian 11 (Bullseye) + *) echo "3.11" ;; esac + ;; + ubuntu) + case "$os_version" in + 24) echo "3.12" ;; # Ubuntu 24.04 LTS + 22) echo "3.10" ;; # Ubuntu 22.04 LTS + 20) echo "3.8" ;; # Ubuntu 20.04 LTS + *) echo "3.10" ;; + esac + ;; + *) + echo "3.11" + ;; + esac } # ------------------------------------------------------------------------------ # Get default Node.js LTS version # ------------------------------------------------------------------------------ get_default_nodejs_version() { - # Always return current LTS (as of 2025) - echo "22" + # Always return current LTS (as of 2025) + echo "22" } # ------------------------------------------------------------------------------ # Check if package manager is locked # ------------------------------------------------------------------------------ is_apt_locked() { - if fuser /var/lib/dpkg/lock-frontend &>/dev/null || - fuser /var/lib/apt/lists/lock &>/dev/null || - fuser /var/cache/apt/archives/lock &>/dev/null; then - return 0 - fi - return 1 + if fuser /var/lib/dpkg/lock-frontend &>/dev/null || + fuser /var/lib/apt/lists/lock &>/dev/null || + fuser /var/cache/apt/archives/lock &>/dev/null; then + return 0 + fi + return 1 } # ------------------------------------------------------------------------------ # Wait for apt to be available # ------------------------------------------------------------------------------ wait_for_apt() { - local max_wait="${1:-300}" # 5 minutes default - local waited=0 + local max_wait="${1:-300}" # 5 minutes default + local waited=0 - while is_apt_locked; do - if [[ $waited -ge $max_wait ]]; then - msg_error "Timeout waiting for apt to be available" - return 1 - fi + while is_apt_locked; do + if [[ $waited -ge $max_wait ]]; then + msg_error "Timeout waiting for apt to be available" + return 1 + fi - sleep 5 - waited=$((waited + 5)) - done + sleep 5 + waited=$((waited + 5)) + done - return 0 + return 0 } # ------------------------------------------------------------------------------ # Cleanup old repository files (migration helper) # ------------------------------------------------------------------------------ cleanup_old_repo_files() { - local app="$1" + local app="$1" - # Remove old-style .list files (including backups) - rm -f /etc/apt/sources.list.d/"${app}"*.list - rm -f /etc/apt/sources.list.d/"${app}"*.list.save - rm -f /etc/apt/sources.list.d/"${app}"*.list.distUpgrade - rm -f /etc/apt/sources.list.d/"${app}"*.list.dpkg-* + # Remove old-style .list files (including backups) + rm -f /etc/apt/sources.list.d/"${app}"*.list + rm -f /etc/apt/sources.list.d/"${app}"*.list.save + rm -f /etc/apt/sources.list.d/"${app}"*.list.distUpgrade + rm -f /etc/apt/sources.list.d/"${app}"*.list.dpkg-* - # Remove old GPG keys from trusted.gpg.d - rm -f /etc/apt/trusted.gpg.d/"${app}"*.gpg + # Remove old GPG keys from trusted.gpg.d + rm -f /etc/apt/trusted.gpg.d/"${app}"*.gpg - # Remove keyrings from /etc/apt/keyrings - rm -f /etc/apt/keyrings/"${app}"*.gpg + # Remove keyrings from /etc/apt/keyrings + rm -f /etc/apt/keyrings/"${app}"*.gpg - # Remove ALL .sources files for this app (including the main one) - # This ensures no orphaned .sources files reference deleted keyrings - rm -f /etc/apt/sources.list.d/"${app}"*.sources + # Remove ALL .sources files for this app (including the main one) + # This ensures no orphaned .sources files reference deleted keyrings + rm -f /etc/apt/sources.list.d/"${app}"*.sources } # ------------------------------------------------------------------------------ @@ -885,34 +885,34 @@ cleanup_old_repo_files() { # Call this at the start of any setup function to ensure APT is in a clean state # ------------------------------------------------------------------------------ cleanup_orphaned_sources() { - local sources_dir="/etc/apt/sources.list.d" - local keyrings_dir="/etc/apt/keyrings" + local sources_dir="/etc/apt/sources.list.d" + local keyrings_dir="/etc/apt/keyrings" - [[ ! -d "$sources_dir" ]] && return 0 + [[ ! -d "$sources_dir" ]] && return 0 - while IFS= read -r -d '' sources_file; do - local basename_file - basename_file=$(basename "$sources_file") + while IFS= read -r -d '' sources_file; do + local basename_file + basename_file=$(basename "$sources_file") - # NEVER remove debian.sources - this is the standard Debian repository - if [[ "$basename_file" == "debian.sources" ]]; then - continue - fi - - # Extract Signed-By path from .sources file - local keyring_path - keyring_path=$(grep -E '^Signed-By:' "$sources_file" 2>/dev/null | awk '{print $2}') - - # If keyring doesn't exist, remove the .sources file - if [[ -n "$keyring_path" ]] && [[ ! -f "$keyring_path" ]]; then - rm -f "$sources_file" - fi - done < <(find "$sources_dir" -name "*.sources" -print0 2>/dev/null) - - # Also check for broken symlinks in keyrings directory - if [[ -d "$keyrings_dir" ]]; then - find "$keyrings_dir" -type l ! -exec test -e {} \; -delete 2>/dev/null || true + # NEVER remove debian.sources - this is the standard Debian repository + if [[ "$basename_file" == "debian.sources" ]]; then + continue fi + + # Extract Signed-By path from .sources file + local keyring_path + keyring_path=$(grep -E '^Signed-By:' "$sources_file" 2>/dev/null | awk '{print $2}') + + # If keyring doesn't exist, remove the .sources file + if [[ -n "$keyring_path" ]] && [[ ! -f "$keyring_path" ]]; then + rm -f "$sources_file" + fi + done < <(find "$sources_dir" -name "*.sources" -print0 2>/dev/null) + + # Also check for broken symlinks in keyrings directory + if [[ -d "$keyrings_dir" ]]; then + find "$keyrings_dir" -type l ! -exec test -e {} \; -delete 2>/dev/null || true + fi } # ------------------------------------------------------------------------------ @@ -920,23 +920,23 @@ cleanup_orphaned_sources() { # This should be called at the start of any setup function # ------------------------------------------------------------------------------ ensure_apt_working() { - # Clean up orphaned sources first + # Clean up orphaned sources first + cleanup_orphaned_sources + + # Try to update package lists + if ! apt update -qq 2>/dev/null; then + # More aggressive cleanup + rm -f /etc/apt/sources.list.d/*.sources 2>/dev/null || true cleanup_orphaned_sources - # Try to update package lists + # Try again if ! apt update -qq 2>/dev/null; then - # More aggressive cleanup - rm -f /etc/apt/sources.list.d/*.sources 2>/dev/null || true - cleanup_orphaned_sources - - # Try again - if ! apt update -qq 2>/dev/null; then - msg_error "Cannot update package lists - APT is critically broken" - return 1 - fi + msg_error "Cannot update package lists - APT is critically broken" + return 1 fi + fi - return 0 + return 0 } # ------------------------------------------------------------------------------ @@ -944,39 +944,39 @@ ensure_apt_working() { # Validates all parameters and fails safely if any are empty # ------------------------------------------------------------------------------ setup_deb822_repo() { - local name="$1" - local gpg_url="$2" - local repo_url="$3" - local suite="$4" - local component="${5:-main}" - local architectures="${6:-amd64 arm64}" + local name="$1" + local gpg_url="$2" + local repo_url="$3" + local suite="$4" + local component="${5:-main}" + local architectures="${6:-amd64 arm64}" - # Validate required parameters - if [[ -z "$name" || -z "$gpg_url" || -z "$repo_url" || -z "$suite" ]]; then - msg_error "setup_deb822_repo: missing required parameters (name=$name, gpg=$gpg_url, repo=$repo_url, suite=$suite)" - return 1 - fi + # Validate required parameters + if [[ -z "$name" || -z "$gpg_url" || -z "$repo_url" || -z "$suite" ]]; then + msg_error "setup_deb822_repo: missing required parameters (name=$name, gpg=$gpg_url, repo=$repo_url, suite=$suite)" + return 1 + fi - # Cleanup old configs for this app - cleanup_old_repo_files "$name" + # Cleanup old configs for this app + cleanup_old_repo_files "$name" - # Cleanup any orphaned .sources files from other apps - cleanup_orphaned_sources + # Cleanup any orphaned .sources files from other apps + cleanup_orphaned_sources - # Ensure keyring directory exists - mkdir -p /etc/apt/keyrings || { - msg_error "Failed to create /etc/apt/keyrings directory" - return 1 - } + # Ensure keyring directory exists + mkdir -p /etc/apt/keyrings || { + msg_error "Failed to create /etc/apt/keyrings directory" + return 1 + } - # Download GPG key (with --yes to avoid interactive prompts) - curl -fsSL "$gpg_url" | gpg --dearmor --yes -o "/etc/apt/keyrings/${name}.gpg" 2>/dev/null || { - msg_error "Failed to download or import GPG key for ${name} from $gpg_url" - return 1 - } + # Download GPG key (with --yes to avoid interactive prompts) + curl -fsSL "$gpg_url" | gpg --dearmor --yes -o "/etc/apt/keyrings/${name}.gpg" 2>/dev/null || { + msg_error "Failed to download or import GPG key for ${name} from $gpg_url" + return 1 + } - # Create deb822 sources file - cat </etc/apt/sources.list.d/${name}.sources + # Create deb822 sources file + cat </etc/apt/sources.list.d/${name}.sources Types: deb URIs: $repo_url Suites: $suite @@ -985,175 +985,175 @@ Architectures: $architectures Signed-By: /etc/apt/keyrings/${name}.gpg EOF - # Use cached apt update - local apt_cache_file="/var/cache/apt-update-timestamp" - local current_time=$(date +%s) - local last_update=0 + # Use cached apt update + local apt_cache_file="/var/cache/apt-update-timestamp" + local current_time=$(date +%s) + local last_update=0 - if [[ -f "$apt_cache_file" ]]; then - last_update=$(cat "$apt_cache_file" 2>/dev/null || echo 0) - fi + if [[ -f "$apt_cache_file" ]]; then + last_update=$(cat "$apt_cache_file" 2>/dev/null || echo 0) + fi - # For repo changes, always update but respect short-term cache (30s) - if ((current_time - last_update > 30)); then - $STD apt update - echo "$current_time" >"$apt_cache_file" - fi + # For repo changes, always update but respect short-term cache (30s) + if ((current_time - last_update > 30)); then + $STD apt update + echo "$current_time" >"$apt_cache_file" + fi } # ------------------------------------------------------------------------------ # Package version hold/unhold helpers # ------------------------------------------------------------------------------ hold_package_version() { - local package="$1" - $STD apt-mark hold "$package" + local package="$1" + $STD apt-mark hold "$package" } unhold_package_version() { - local package="$1" - $STD apt-mark unhold "$package" + local package="$1" + $STD apt-mark unhold "$package" } # ------------------------------------------------------------------------------ # Safe service restart with verification # ------------------------------------------------------------------------------ safe_service_restart() { - local service="$1" + local service="$1" - if systemctl is-active --quiet "$service"; then - $STD systemctl restart "$service" - else - $STD systemctl start "$service" - fi + if systemctl is-active --quiet "$service"; then + $STD systemctl restart "$service" + else + $STD systemctl start "$service" + fi - if ! systemctl is-active --quiet "$service"; then - msg_error "Failed to start $service" - systemctl status "$service" --no-pager - return 1 - fi - return 0 + if ! systemctl is-active --quiet "$service"; then + msg_error "Failed to start $service" + systemctl status "$service" --no-pager + return 1 + fi + return 0 } # ------------------------------------------------------------------------------ # Enable and start service (with error handling) # ------------------------------------------------------------------------------ enable_and_start_service() { - local service="$1" + local service="$1" - if ! systemctl enable "$service" &>/dev/null; then - return 1 - fi + if ! systemctl enable "$service" &>/dev/null; then + return 1 + fi - if ! systemctl start "$service" &>/dev/null; then - msg_error "Failed to start $service" - systemctl status "$service" --no-pager - return 1 - fi + if ! systemctl start "$service" &>/dev/null; then + msg_error "Failed to start $service" + systemctl status "$service" --no-pager + return 1 + fi - return 0 + return 0 } # ------------------------------------------------------------------------------ # Check if service is enabled # ------------------------------------------------------------------------------ is_service_enabled() { - local service="$1" - systemctl is-enabled --quiet "$service" 2>/dev/null + local service="$1" + systemctl is-enabled --quiet "$service" 2>/dev/null } # ------------------------------------------------------------------------------ # Check if service is running # ------------------------------------------------------------------------------ is_service_running() { - local service="$1" - systemctl is-active --quiet "$service" 2>/dev/null + local service="$1" + systemctl is-active --quiet "$service" 2>/dev/null } # ------------------------------------------------------------------------------ # Extract version from JSON (GitHub releases) # ------------------------------------------------------------------------------ extract_version_from_json() { - local json="$1" - local field="${2:-tag_name}" - local strip_v="${3:-true}" + local json="$1" + local field="${2:-tag_name}" + local strip_v="${3:-true}" - ensure_dependencies jq + ensure_dependencies jq - local version - version=$(echo "$json" | jq -r ".${field} // empty") + local version + version=$(echo "$json" | jq -r ".${field} // empty") - if [[ -z "$version" ]]; then - return 1 - fi + if [[ -z "$version" ]]; then + return 1 + fi - if [[ "$strip_v" == "true" ]]; then - echo "${version#v}" - else - echo "$version" - fi + if [[ "$strip_v" == "true" ]]; then + echo "${version#v}" + else + echo "$version" + fi } # ------------------------------------------------------------------------------ # Get latest GitHub release version # ------------------------------------------------------------------------------ get_latest_github_release() { - local repo="$1" - local strip_v="${2:-true}" - local temp_file=$(mktemp) + local repo="$1" + local strip_v="${2:-true}" + local temp_file=$(mktemp) - if ! github_api_call "https://api.github.com/repos/${repo}/releases/latest" "$temp_file"; then - rm -f "$temp_file" - return 1 - fi - - local version - version=$(extract_version_from_json "$(cat "$temp_file")" "tag_name" "$strip_v") + if ! github_api_call "https://api.github.com/repos/${repo}/releases/latest" "$temp_file"; then rm -f "$temp_file" + return 1 + fi - if [[ -z "$version" ]]; then - return 1 - fi + local version + version=$(extract_version_from_json "$(cat "$temp_file")" "tag_name" "$strip_v") + rm -f "$temp_file" - echo "$version" + if [[ -z "$version" ]]; then + return 1 + fi + + echo "$version" } # ------------------------------------------------------------------------------ # Debug logging (only if DEBUG=1) # ------------------------------------------------------------------------------ debug_log() { - [[ "${DEBUG:-0}" == "1" ]] && echo "[DEBUG] $*" >&2 + [[ "${DEBUG:-0}" == "1" ]] && echo "[DEBUG] $*" >&2 } # ------------------------------------------------------------------------------ # Performance timing helper # ------------------------------------------------------------------------------ start_timer() { - echo $(date +%s) + echo $(date +%s) } end_timer() { - local start_time="$1" - local label="${2:-Operation}" - local end_time=$(date +%s) - local duration=$((end_time - start_time)) + local start_time="$1" + local label="${2:-Operation}" + local end_time=$(date +%s) + local duration=$((end_time - start_time)) } # ------------------------------------------------------------------------------ # GPG key fingerprint verification # ------------------------------------------------------------------------------ verify_gpg_fingerprint() { - local key_file="$1" - local expected_fingerprint="$2" + local key_file="$1" + local expected_fingerprint="$2" - local actual_fingerprint - actual_fingerprint=$(gpg --show-keys --with-fingerprint --with-colons "$key_file" 2>&1 | grep -m1 '^fpr:' | cut -d: -f10) + local actual_fingerprint + actual_fingerprint=$(gpg --show-keys --with-fingerprint --with-colons "$key_file" 2>&1 | grep -m1 '^fpr:' | cut -d: -f10) - if [[ "$actual_fingerprint" == "$expected_fingerprint" ]]; then - return 0 - fi + if [[ "$actual_fingerprint" == "$expected_fingerprint" ]]; then + return 0 + fi - msg_error "GPG fingerprint mismatch! Expected: $expected_fingerprint, Got: $actual_fingerprint" - return 1 + msg_error "GPG fingerprint mismatch! Expected: $expected_fingerprint, Got: $actual_fingerprint" + return 1 } # ============================================================================== @@ -1181,97 +1181,97 @@ verify_gpg_fingerprint() { # - Does not support pre-releases # ------------------------------------------------------------------------------ check_for_gh_release() { - local app="$1" - local source="$2" - local pinned_version_in="${3:-}" # optional - local app_lc="${app,,}" - local current_file="$HOME/.${app_lc}" + local app="$1" + local source="$2" + local pinned_version_in="${3:-}" # optional + local app_lc="${app,,}" + local current_file="$HOME/.${app_lc}" - msg_info "Checking for update: ${app}" + msg_info "Checking for update: ${app}" - # DNS check - if ! getent hosts api.github.com >/dev/null 2>&1; then - msg_error "Network error: cannot resolve api.github.com" - return 1 + # DNS check + if ! getent hosts api.github.com >/dev/null 2>&1; then + msg_error "Network error: cannot resolve api.github.com" + return 1 + fi + + ensure_dependencies jq + + # Fetch releases and exclude drafts/prereleases + local releases_json + releases_json=$(curl -fsSL --max-time 20 \ + -H 'Accept: application/vnd.github+json' \ + -H 'X-GitHub-Api-Version: 2022-11-28' \ + "https://api.github.com/repos/${source}/releases") || { + msg_error "Unable to fetch releases for ${app}" + return 1 + } + + mapfile -t raw_tags < <(jq -r '.[] | select(.draft==false and .prerelease==false) | .tag_name' <<<"$releases_json") + if ((${#raw_tags[@]} == 0)); then + msg_error "No stable releases found for ${app}" + return 1 + fi + + local clean_tags=() + for t in "${raw_tags[@]}"; do + clean_tags+=("${t#v}") + done + + local latest_raw="${raw_tags[0]}" + local latest_clean="${clean_tags[0]}" + + # current installed (stored without v) + local current="" + if [[ -f "$current_file" ]]; then + current="$(<"$current_file")" + else + # Migration: search for any /opt/*_version.txt + local legacy_files + mapfile -t legacy_files < <(find /opt -maxdepth 1 -type f -name "*_version.txt" 2>/dev/null) + if ((${#legacy_files[@]} == 1)); then + current="$(<"${legacy_files[0]}")" + echo "${current#v}" >"$current_file" + rm -f "${legacy_files[0]}" fi + fi + current="${current#v}" - ensure_dependencies jq - - # Fetch releases and exclude drafts/prereleases - local releases_json - releases_json=$(curl -fsSL --max-time 20 \ - -H 'Accept: application/vnd.github+json' \ - -H 'X-GitHub-Api-Version: 2022-11-28' \ - "https://api.github.com/repos/${source}/releases") || { - msg_error "Unable to fetch releases for ${app}" - return 1 - } - - mapfile -t raw_tags < <(jq -r '.[] | select(.draft==false and .prerelease==false) | .tag_name' <<<"$releases_json") - if ((${#raw_tags[@]} == 0)); then - msg_error "No stable releases found for ${app}" - return 1 - fi - - local clean_tags=() - for t in "${raw_tags[@]}"; do - clean_tags+=("${t#v}") + # Pinned version handling + if [[ -n "$pinned_version_in" ]]; then + local pin_clean="${pinned_version_in#v}" + local match_raw="" + for i in "${!clean_tags[@]}"; do + if [[ "${clean_tags[$i]}" == "$pin_clean" ]]; then + match_raw="${raw_tags[$i]}" + break + fi done - local latest_raw="${raw_tags[0]}" - local latest_clean="${clean_tags[0]}" - - # current installed (stored without v) - local current="" - if [[ -f "$current_file" ]]; then - current="$(<"$current_file")" - else - # Migration: search for any /opt/*_version.txt - local legacy_files - mapfile -t legacy_files < <(find /opt -maxdepth 1 -type f -name "*_version.txt" 2>/dev/null) - if ((${#legacy_files[@]} == 1)); then - current="$(<"${legacy_files[0]}")" - echo "${current#v}" >"$current_file" - rm -f "${legacy_files[0]}" - fi - fi - current="${current#v}" - - # Pinned version handling - if [[ -n "$pinned_version_in" ]]; then - local pin_clean="${pinned_version_in#v}" - local match_raw="" - for i in "${!clean_tags[@]}"; do - if [[ "${clean_tags[$i]}" == "$pin_clean" ]]; then - match_raw="${raw_tags[$i]}" - break - fi - done - - if [[ -z "$match_raw" ]]; then - msg_error "Pinned version ${pinned_version_in} not found upstream" - return 1 - fi - - if [[ "$current" != "$pin_clean" ]]; then - CHECK_UPDATE_RELEASE="$match_raw" - msg_ok "Update available: ${app} ${current:-not installed} → ${pin_clean}" - return 0 - fi - - msg_error "No update available: ${app} is not installed!" - return 1 + if [[ -z "$match_raw" ]]; then + msg_error "Pinned version ${pinned_version_in} not found upstream" + return 1 fi - # No pinning → use latest - if [[ -z "$current" || "$current" != "$latest_clean" ]]; then - CHECK_UPDATE_RELEASE="$latest_raw" - msg_ok "Update available: ${app} ${current:-not installed} → ${latest_clean}" - return 0 + if [[ "$current" != "$pin_clean" ]]; then + CHECK_UPDATE_RELEASE="$match_raw" + msg_ok "Update available: ${app} ${current:-not installed} → ${pin_clean}" + return 0 fi - msg_ok "No update available: ${app} (${latest_clean})" + msg_error "No update available: ${app} is not installed!" return 1 + fi + + # No pinning → use latest + if [[ -z "$current" || "$current" != "$latest_clean" ]]; then + CHECK_UPDATE_RELEASE="$latest_raw" + msg_ok "Update available: ${app} ${current:-not installed} → ${latest_clean}" + return 0 + fi + + msg_ok "No update available: ${app} (${latest_clean})" + return 1 } # ------------------------------------------------------------------------------ @@ -1284,35 +1284,35 @@ check_for_gh_release() { # APP - Application name (default: $APPLICATION variable) # ------------------------------------------------------------------------------ create_self_signed_cert() { - local APP_NAME="${1:-${APPLICATION}}" - local CERT_DIR="/etc/ssl/${APP_NAME}" - local CERT_KEY="${CERT_DIR}/${APP_NAME}.key" - local CERT_CRT="${CERT_DIR}/${APP_NAME}.crt" + local APP_NAME="${1:-${APPLICATION}}" + local CERT_DIR="/etc/ssl/${APP_NAME}" + local CERT_KEY="${CERT_DIR}/${APP_NAME}.key" + local CERT_CRT="${CERT_DIR}/${APP_NAME}.crt" - if [[ -f "$CERT_CRT" && -f "$CERT_KEY" ]]; then - return 0 - fi + if [[ -f "$CERT_CRT" && -f "$CERT_KEY" ]]; then + return 0 + fi - $STD apt update || { - msg_error "Failed to update package list" - return 1 - } - $STD apt install -y openssl || { - msg_error "Failed to install OpenSSL" - return 1 - } + $STD apt update || { + msg_error "Failed to update package list" + return 1 + } + $STD apt install -y openssl || { + msg_error "Failed to install OpenSSL" + return 1 + } - mkdir -p "$CERT_DIR" - $STD openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 \ - -subj "/C=US/ST=State/L=City/O=Organization/CN=${APP_NAME}" \ - -keyout "$CERT_KEY" \ - -out "$CERT_CRT" || { - msg_error "Failed to create self-signed certificate" - return 1 - } + mkdir -p "$CERT_DIR" + $STD openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 \ + -subj "/C=US/ST=State/L=City/O=Organization/CN=${APP_NAME}" \ + -keyout "$CERT_KEY" \ + -out "$CERT_CRT" || { + msg_error "Failed to create self-signed certificate" + return 1 + } - chmod 600 "$CERT_KEY" - chmod 644 "$CERT_CRT" + chmod 600 "$CERT_KEY" + chmod 644 "$CERT_CRT" } # ------------------------------------------------------------------------------ @@ -1324,28 +1324,28 @@ create_self_signed_cert() { # ------------------------------------------------------------------------------ function download_with_progress() { - local url="$1" - local output="$2" - if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi + local url="$1" + local output="$2" + if [ -n "$SPINNER_PID" ] && ps -p "$SPINNER_PID" >/dev/null; then kill "$SPINNER_PID" >/dev/null; fi - ensure_dependencies pv - set -o pipefail + ensure_dependencies pv + set -o pipefail - # Content-Length aus HTTP-Header holen - local content_length - content_length=$(curl -fsSLI "$url" | awk '/Content-Length/ {print $2}' | tr -d '\r' || true) + # Content-Length aus HTTP-Header holen + local content_length + content_length=$(curl -fsSLI "$url" | awk '/Content-Length/ {print $2}' | tr -d '\r' || true) - if [[ -z "$content_length" ]]; then - if ! curl -fL# -o "$output" "$url"; then - msg_error "Download failed" - return 1 - fi - else - if ! curl -fsSL "$url" | pv -s "$content_length" >"$output"; then - msg_error "Download failed" - return 1 - fi + if [[ -z "$content_length" ]]; then + if ! curl -fL# -o "$output" "$url"; then + msg_error "Download failed" + return 1 fi + else + if ! curl -fsSL "$url" | pv -s "$content_length" >"$output"; then + msg_error "Download failed" + return 1 + fi + fi } # ------------------------------------------------------------------------------ @@ -1356,12 +1356,12 @@ function download_with_progress() { # ------------------------------------------------------------------------------ function ensure_usr_local_bin_persist() { - local PROFILE_FILE="/etc/profile.d/custom_path.sh" + local PROFILE_FILE="/etc/profile.d/custom_path.sh" - if [[ ! -f "$PROFILE_FILE" ]] && ! command -v pveversion &>/dev/null; then - echo 'export PATH="/usr/local/bin:$PATH"' >"$PROFILE_FILE" - chmod +x "$PROFILE_FILE" - fi + if [[ ! -f "$PROFILE_FILE" ]] && ! command -v pveversion &>/dev/null; then + echo 'export PATH="/usr/local/bin:$PATH"' >"$PROFILE_FILE" + chmod +x "$PROFILE_FILE" + fi } # ------------------------------------------------------------------------------ @@ -1409,315 +1409,315 @@ function ensure_usr_local_bin_persist() { # ------------------------------------------------------------------------------ function fetch_and_deploy_gh_release() { - local app="$1" - local repo="$2" - local mode="${3:-tarball}" # tarball | binary | prebuild | singlefile - local version="${4:-latest}" - local target="${5:-/opt/$app}" - local asset_pattern="${6:-}" + local app="$1" + local repo="$2" + local mode="${3:-tarball}" # tarball | binary | prebuild | singlefile + local version="${4:-latest}" + local target="${5:-/opt/$app}" + local asset_pattern="${6:-}" - local app_lc=$(echo "${app,,}" | tr -d ' ') - local version_file="$HOME/.${app_lc}" + local app_lc=$(echo "${app,,}" | tr -d ' ') + local version_file="$HOME/.${app_lc}" - local api_timeout="--connect-timeout 10 --max-time 60" - local download_timeout="--connect-timeout 15 --max-time 900" + local api_timeout="--connect-timeout 10 --max-time 60" + local download_timeout="--connect-timeout 15 --max-time 900" - local current_version="" - [[ -f "$version_file" ]] && current_version=$(<"$version_file") + local current_version="" + [[ -f "$version_file" ]] && current_version=$(<"$version_file") - ensure_dependencies jq + ensure_dependencies jq - local api_url="https://api.github.com/repos/$repo/releases" - [[ "$version" != "latest" ]] && api_url="$api_url/tags/$version" || api_url="$api_url/latest" - local header=() - [[ -n "${GITHUB_TOKEN:-}" ]] && header=(-H "Authorization: token $GITHUB_TOKEN") + local api_url="https://api.github.com/repos/$repo/releases" + [[ "$version" != "latest" ]] && api_url="$api_url/tags/$version" || api_url="$api_url/latest" + local header=() + [[ -n "${GITHUB_TOKEN:-}" ]] && header=(-H "Authorization: token $GITHUB_TOKEN") - # dns pre check - local gh_host - gh_host=$(awk -F/ '{print $3}' <<<"$api_url") - if ! getent hosts "$gh_host" &>/dev/null; then - msg_error "DNS resolution failed for $gh_host – check /etc/resolv.conf or networking" - return 1 - fi + # dns pre check + local gh_host + gh_host=$(awk -F/ '{print $3}' <<<"$api_url") + if ! getent hosts "$gh_host" &>/dev/null; then + msg_error "DNS resolution failed for $gh_host – check /etc/resolv.conf or networking" + return 1 + fi - local max_retries=3 retry_delay=2 attempt=1 success=false resp http_code + local max_retries=3 retry_delay=2 attempt=1 success=false resp http_code - while ((attempt <= max_retries)); do - resp=$(curl $api_timeout -fsSL -w "%{http_code}" -o /tmp/gh_rel.json "${header[@]}" "$api_url") && success=true && break - sleep "$retry_delay" - ((attempt++)) - done + while ((attempt <= max_retries)); do + resp=$(curl $api_timeout -fsSL -w "%{http_code}" -o /tmp/gh_rel.json "${header[@]}" "$api_url") && success=true && break + sleep "$retry_delay" + ((attempt++)) + done - if ! $success; then - msg_error "Failed to fetch release metadata from $api_url after $max_retries attempts" - return 1 - fi + if ! $success; then + msg_error "Failed to fetch release metadata from $api_url after $max_retries attempts" + return 1 + fi - http_code="${resp:(-3)}" - [[ "$http_code" != "200" ]] && { - msg_error "GitHub API returned HTTP $http_code" - return 1 + http_code="${resp:(-3)}" + [[ "$http_code" != "200" ]] && { + msg_error "GitHub API returned HTTP $http_code" + return 1 + } + + local json tag_name + json=$(/dev/null || uname -m) + [[ "$arch" == "x86_64" ]] && arch="amd64" + [[ "$arch" == "aarch64" ]] && arch="arm64" - ### Tarball Mode ### - if [[ "$mode" == "tarball" || "$mode" == "source" ]]; then - url=$(echo "$json" | jq -r '.tarball_url // empty') - [[ -z "$url" ]] && url="https://github.com/$repo/archive/refs/tags/v$version.tar.gz" - filename="${app_lc}-${version}.tar.gz" + local assets url_match="" + assets=$(echo "$json" | jq -r '.assets[].browser_download_url') - curl $download_timeout -fsSL -o "$tmpdir/$filename" "$url" || { - msg_error "Download failed: $url" - rm -rf "$tmpdir" - return 1 - } + # If explicit filename pattern is provided (param $6), match that first + if [[ -n "$asset_pattern" ]]; then + for u in $assets; do + case "${u##*/}" in + $asset_pattern) + url_match="$u" + break + ;; + esac + done + fi - mkdir -p "$target" - if [[ "${CLEAN_INSTALL:-0}" == "1" ]]; then - rm -rf "${target:?}/"* + # If no match via explicit pattern, fall back to architecture heuristic + if [[ -z "$url_match" ]]; then + for u in $assets; do + if [[ "$u" =~ ($arch|amd64|x86_64|aarch64|arm64).*\.deb$ ]]; then + url_match="$u" + break fi + done + fi - tar --no-same-owner -xzf "$tmpdir/$filename" -C "$tmpdir" || { - msg_error "Failed to extract tarball" - rm -rf "$tmpdir" - return 1 - } - local unpack_dir - unpack_dir=$(find "$tmpdir" -mindepth 1 -maxdepth 1 -type d | head -n1) + # Fallback: any .deb file + if [[ -z "$url_match" ]]; then + for u in $assets; do + [[ "$u" =~ \.deb$ ]] && url_match="$u" && break + done + fi - shopt -s dotglob nullglob - cp -r "$unpack_dir"/* "$target/" - shopt -u dotglob nullglob + if [[ -z "$url_match" ]]; then + msg_error "No suitable .deb asset found for $app" + rm -rf "$tmpdir" + return 1 + fi - ### Binary Mode ### - elif [[ "$mode" == "binary" ]]; then - local arch - arch=$(dpkg --print-architecture 2>/dev/null || uname -m) - [[ "$arch" == "x86_64" ]] && arch="amd64" - [[ "$arch" == "aarch64" ]] && arch="arm64" + filename="${url_match##*/}" + curl $download_timeout -fsSL -o "$tmpdir/$filename" "$url_match" || { + msg_error "Download failed: $url_match" + rm -rf "$tmpdir" + return 1 + } - local assets url_match="" - assets=$(echo "$json" | jq -r '.assets[].browser_download_url') - - # If explicit filename pattern is provided (param $6), match that first - if [[ -n "$asset_pattern" ]]; then - for u in $assets; do - case "${u##*/}" in - $asset_pattern) - url_match="$u" - break - ;; - esac - done - fi - - # If no match via explicit pattern, fall back to architecture heuristic - if [[ -z "$url_match" ]]; then - for u in $assets; do - if [[ "$u" =~ ($arch|amd64|x86_64|aarch64|arm64).*\.deb$ ]]; then - url_match="$u" - break - fi - done - fi - - # Fallback: any .deb file - if [[ -z "$url_match" ]]; then - for u in $assets; do - [[ "$u" =~ \.deb$ ]] && url_match="$u" && break - done - fi - - if [[ -z "$url_match" ]]; then - msg_error "No suitable .deb asset found for $app" - rm -rf "$tmpdir" - return 1 - fi - - filename="${url_match##*/}" - curl $download_timeout -fsSL -o "$tmpdir/$filename" "$url_match" || { - msg_error "Download failed: $url_match" - rm -rf "$tmpdir" - return 1 - } - - chmod 644 "$tmpdir/$filename" - $STD apt install -y "$tmpdir/$filename" || { - $STD dpkg -i "$tmpdir/$filename" || { - msg_error "Both apt and dpkg installation failed" - rm -rf "$tmpdir" - return 1 - } - } - - ### Prebuild Mode ### - elif [[ "$mode" == "prebuild" ]]; then - local pattern="${6%\"}" - pattern="${pattern#\"}" - [[ -z "$pattern" ]] && { - msg_error "Mode 'prebuild' requires 6th parameter (asset filename pattern)" - rm -rf "$tmpdir" - return 1 - } - - local asset_url="" - for u in $(echo "$json" | jq -r '.assets[].browser_download_url'); do - filename_candidate="${u##*/}" - case "$filename_candidate" in - $pattern) - asset_url="$u" - break - ;; - esac - done - - [[ -z "$asset_url" ]] && { - msg_error "No asset matching '$pattern' found" - rm -rf "$tmpdir" - return 1 - } - - filename="${asset_url##*/}" - curl $download_timeout -fsSL -o "$tmpdir/$filename" "$asset_url" || { - msg_error "Download failed: $asset_url" - rm -rf "$tmpdir" - return 1 - } - - local unpack_tmp - unpack_tmp=$(mktemp -d) - mkdir -p "$target" - if [[ "${CLEAN_INSTALL:-0}" == "1" ]]; then - rm -rf "${target:?}/"* - fi - - if [[ "$filename" == *.zip ]]; then - ensure_dependencies unzip - unzip -q "$tmpdir/$filename" -d "$unpack_tmp" || { - msg_error "Failed to extract ZIP archive" - rm -rf "$tmpdir" "$unpack_tmp" - return 1 - } - elif [[ "$filename" == *.tar.* || "$filename" == *.tgz ]]; then - tar --no-same-owner -xf "$tmpdir/$filename" -C "$unpack_tmp" || { - msg_error "Failed to extract TAR archive" - rm -rf "$tmpdir" "$unpack_tmp" - return 1 - } - else - msg_error "Unsupported archive format: $filename" - rm -rf "$tmpdir" "$unpack_tmp" - return 1 - fi - - local top_dirs - top_dirs=$(find "$unpack_tmp" -mindepth 1 -maxdepth 1 -type d | wc -l) - local top_entries inner_dir - top_entries=$(find "$unpack_tmp" -mindepth 1 -maxdepth 1) - if [[ "$(echo "$top_entries" | wc -l)" -eq 1 && -d "$top_entries" ]]; then - # Strip leading folder - inner_dir="$top_entries" - shopt -s dotglob nullglob - if compgen -G "$inner_dir/*" >/dev/null; then - cp -r "$inner_dir"/* "$target/" || { - msg_error "Failed to copy contents from $inner_dir to $target" - rm -rf "$tmpdir" "$unpack_tmp" - return 1 - } - else - msg_error "Inner directory is empty: $inner_dir" - rm -rf "$tmpdir" "$unpack_tmp" - return 1 - fi - shopt -u dotglob nullglob - else - # Copy all contents - shopt -s dotglob nullglob - if compgen -G "$unpack_tmp/*" >/dev/null; then - cp -r "$unpack_tmp"/* "$target/" || { - msg_error "Failed to copy contents to $target" - rm -rf "$tmpdir" "$unpack_tmp" - return 1 - } - else - msg_error "Unpacked archive is empty" - rm -rf "$tmpdir" "$unpack_tmp" - return 1 - fi - shopt -u dotglob nullglob - fi - - ### Singlefile Mode ### - elif [[ "$mode" == "singlefile" ]]; then - local pattern="${6%\"}" - pattern="${pattern#\"}" - [[ -z "$pattern" ]] && { - msg_error "Mode 'singlefile' requires 6th parameter (asset filename pattern)" - rm -rf "$tmpdir" - return 1 - } - - local asset_url="" - for u in $(echo "$json" | jq -r '.assets[].browser_download_url'); do - filename_candidate="${u##*/}" - case "$filename_candidate" in - $pattern) - asset_url="$u" - break - ;; - esac - done - - [[ -z "$asset_url" ]] && { - msg_error "No asset matching '$pattern' found" - rm -rf "$tmpdir" - return 1 - } - - filename="${asset_url##*/}" - mkdir -p "$target" - - local use_filename="${USE_ORIGINAL_FILENAME:-false}" - local target_file="$app" - [[ "$use_filename" == "true" ]] && target_file="$filename" - - curl $download_timeout -fsSL -o "$target/$target_file" "$asset_url" || { - msg_error "Download failed: $asset_url" - rm -rf "$tmpdir" - return 1 - } - - if [[ "$target_file" != *.jar && -f "$target/$target_file" ]]; then - chmod +x "$target/$target_file" - fi - - else - msg_error "Unknown mode: $mode" + chmod 644 "$tmpdir/$filename" + $STD apt install -y "$tmpdir/$filename" || { + $STD dpkg -i "$tmpdir/$filename" || { + msg_error "Both apt and dpkg installation failed" rm -rf "$tmpdir" return 1 + } + } + + ### Prebuild Mode ### + elif [[ "$mode" == "prebuild" ]]; then + local pattern="${6%\"}" + pattern="${pattern#\"}" + [[ -z "$pattern" ]] && { + msg_error "Mode 'prebuild' requires 6th parameter (asset filename pattern)" + rm -rf "$tmpdir" + return 1 + } + + local asset_url="" + for u in $(echo "$json" | jq -r '.assets[].browser_download_url'); do + filename_candidate="${u##*/}" + case "$filename_candidate" in + $pattern) + asset_url="$u" + break + ;; + esac + done + + [[ -z "$asset_url" ]] && { + msg_error "No asset matching '$pattern' found" + rm -rf "$tmpdir" + return 1 + } + + filename="${asset_url##*/}" + curl $download_timeout -fsSL -o "$tmpdir/$filename" "$asset_url" || { + msg_error "Download failed: $asset_url" + rm -rf "$tmpdir" + return 1 + } + + local unpack_tmp + unpack_tmp=$(mktemp -d) + mkdir -p "$target" + if [[ "${CLEAN_INSTALL:-0}" == "1" ]]; then + rm -rf "${target:?}/"* fi - echo "$version" >"$version_file" - msg_ok "Deployed: $app ($version)" + if [[ "$filename" == *.zip ]]; then + ensure_dependencies unzip + unzip -q "$tmpdir/$filename" -d "$unpack_tmp" || { + msg_error "Failed to extract ZIP archive" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + } + elif [[ "$filename" == *.tar.* || "$filename" == *.tgz ]]; then + tar --no-same-owner -xf "$tmpdir/$filename" -C "$unpack_tmp" || { + msg_error "Failed to extract TAR archive" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + } + else + msg_error "Unsupported archive format: $filename" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + fi + + local top_dirs + top_dirs=$(find "$unpack_tmp" -mindepth 1 -maxdepth 1 -type d | wc -l) + local top_entries inner_dir + top_entries=$(find "$unpack_tmp" -mindepth 1 -maxdepth 1) + if [[ "$(echo "$top_entries" | wc -l)" -eq 1 && -d "$top_entries" ]]; then + # Strip leading folder + inner_dir="$top_entries" + shopt -s dotglob nullglob + if compgen -G "$inner_dir/*" >/dev/null; then + cp -r "$inner_dir"/* "$target/" || { + msg_error "Failed to copy contents from $inner_dir to $target" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + } + else + msg_error "Inner directory is empty: $inner_dir" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + fi + shopt -u dotglob nullglob + else + # Copy all contents + shopt -s dotglob nullglob + if compgen -G "$unpack_tmp/*" >/dev/null; then + cp -r "$unpack_tmp"/* "$target/" || { + msg_error "Failed to copy contents to $target" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + } + else + msg_error "Unpacked archive is empty" + rm -rf "$tmpdir" "$unpack_tmp" + return 1 + fi + shopt -u dotglob nullglob + fi + + ### Singlefile Mode ### + elif [[ "$mode" == "singlefile" ]]; then + local pattern="${6%\"}" + pattern="${pattern#\"}" + [[ -z "$pattern" ]] && { + msg_error "Mode 'singlefile' requires 6th parameter (asset filename pattern)" + rm -rf "$tmpdir" + return 1 + } + + local asset_url="" + for u in $(echo "$json" | jq -r '.assets[].browser_download_url'); do + filename_candidate="${u##*/}" + case "$filename_candidate" in + $pattern) + asset_url="$u" + break + ;; + esac + done + + [[ -z "$asset_url" ]] && { + msg_error "No asset matching '$pattern' found" + rm -rf "$tmpdir" + return 1 + } + + filename="${asset_url##*/}" + mkdir -p "$target" + + local use_filename="${USE_ORIGINAL_FILENAME:-false}" + local target_file="$app" + [[ "$use_filename" == "true" ]] && target_file="$filename" + + curl $download_timeout -fsSL -o "$target/$target_file" "$asset_url" || { + msg_error "Download failed: $asset_url" + rm -rf "$tmpdir" + return 1 + } + + if [[ "$target_file" != *.jar && -f "$target/$target_file" ]]; then + chmod +x "$target/$target_file" + fi + + else + msg_error "Unknown mode: $mode" rm -rf "$tmpdir" + return 1 + fi + + echo "$version" >"$version_file" + msg_ok "Deployed: $app ($version)" + rm -rf "$tmpdir" } # ------------------------------------------------------------------------------ @@ -1728,40 +1728,40 @@ function fetch_and_deploy_gh_release() { # ------------------------------------------------------------------------------ function import_local_ip() { - local IP_FILE="/run/local-ip.env" - if [[ -f "$IP_FILE" ]]; then - # shellcheck disable=SC1090 - source "$IP_FILE" - fi + local IP_FILE="/run/local-ip.env" + if [[ -f "$IP_FILE" ]]; then + # shellcheck disable=SC1090 + source "$IP_FILE" + fi - if [[ -z "${LOCAL_IP:-}" ]]; then - get_current_ip() { - local targets=("8.8.8.8" "1.1.1.1" "192.168.1.1" "10.0.0.1" "172.16.0.1" "default") - local ip + if [[ -z "${LOCAL_IP:-}" ]]; then + get_current_ip() { + local targets=("8.8.8.8" "1.1.1.1" "192.168.1.1" "10.0.0.1" "172.16.0.1" "default") + local ip - for target in "${targets[@]}"; do - if [[ "$target" == "default" ]]; then - ip=$(ip route get 1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') - else - ip=$(ip route get "$target" 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') - fi - if [[ -n "$ip" ]]; then - echo "$ip" - return 0 - fi - done - - return 1 - } - - LOCAL_IP="$(get_current_ip || true)" - if [[ -z "$LOCAL_IP" ]]; then - msg_error "Could not determine LOCAL_IP" - return 1 + for target in "${targets[@]}"; do + if [[ "$target" == "default" ]]; then + ip=$(ip route get 1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') + else + ip=$(ip route get "$target" 2>/dev/null | awk '{for(i=1;i<=NF;i++) if ($i=="src") print $(i+1)}') fi - fi + if [[ -n "$ip" ]]; then + echo "$ip" + return 0 + fi + done - export LOCAL_IP + return 1 + } + + LOCAL_IP="$(get_current_ip || true)" + if [[ -z "$LOCAL_IP" ]]; then + msg_error "Could not determine LOCAL_IP" + return 1 + fi + fi + + export LOCAL_IP } # ------------------------------------------------------------------------------ @@ -1773,32 +1773,32 @@ function import_local_ip() { # ------------------------------------------------------------------------------ function setup_adminer() { - if grep -qi alpine /etc/os-release; then - msg_info "Setup Adminer (Alpine)" - mkdir -p /var/www/localhost/htdocs/adminer - curl -fsSL https://github.com/vrana/adminer/releases/latest/download/adminer.php \ - -o /var/www/localhost/htdocs/adminer/index.php || { - msg_error "Failed to download Adminer" - return 1 - } - cache_installed_version "adminer" "latest-alpine" - msg_ok "Setup Adminer (Alpine)" - else - msg_info "Setup Adminer (Debian/Ubuntu)" - ensure_dependencies adminer - $STD a2enconf adminer || { - msg_error "Failed to enable Adminer Apache config" - return 1 - } - $STD systemctl reload apache2 || { - msg_error "Failed to reload Apache" - return 1 - } - local VERSION - VERSION=$(dpkg -s adminer 2>/dev/null | grep '^Version:' | awk '{print $2}') - cache_installed_version "adminer" "${VERSION:-unknown}" - msg_ok "Setup Adminer (Debian/Ubuntu)" - fi + if grep -qi alpine /etc/os-release; then + msg_info "Setup Adminer (Alpine)" + mkdir -p /var/www/localhost/htdocs/adminer + curl -fsSL https://github.com/vrana/adminer/releases/latest/download/adminer.php \ + -o /var/www/localhost/htdocs/adminer/index.php || { + msg_error "Failed to download Adminer" + return 1 + } + cache_installed_version "adminer" "latest-alpine" + msg_ok "Setup Adminer (Alpine)" + else + msg_info "Setup Adminer (Debian/Ubuntu)" + ensure_dependencies adminer + $STD a2enconf adminer || { + msg_error "Failed to enable Adminer Apache config" + return 1 + } + $STD systemctl reload apache2 || { + msg_error "Failed to reload Apache" + return 1 + } + local VERSION + VERSION=$(dpkg -s adminer 2>/dev/null | grep '^Version:' | awk '{print $2}') + cache_installed_version "adminer" "${VERSION:-unknown}" + msg_ok "Setup Adminer (Debian/Ubuntu)" + fi } # ------------------------------------------------------------------------------ @@ -1811,60 +1811,60 @@ function setup_adminer() { # ------------------------------------------------------------------------------ function setup_composer() { - local COMPOSER_BIN="/usr/local/bin/composer" - export COMPOSER_ALLOW_SUPERUSER=1 + local COMPOSER_BIN="/usr/local/bin/composer" + export COMPOSER_ALLOW_SUPERUSER=1 - # Get currently installed version - local INSTALLED_VERSION="" - if [[ -x "$COMPOSER_BIN" ]]; then - INSTALLED_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}') - fi + # Get currently installed version + local INSTALLED_VERSION="" + if [[ -x "$COMPOSER_BIN" ]]; then + INSTALLED_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}') + fi - # Scenario 1: Already installed - just self-update - if [[ -n "$INSTALLED_VERSION" ]]; then - msg_info "Update Composer $INSTALLED_VERSION" - $STD "$COMPOSER_BIN" self-update --no-interaction || true - local UPDATED_VERSION - UPDATED_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}') - cache_installed_version "composer" "$UPDATED_VERSION" - msg_ok "Update Composer $UPDATED_VERSION" - return 0 - fi - - # Scenario 2: Fresh install - msg_info "Setup Composer" - - for old in /usr/bin/composer /bin/composer /root/.composer/vendor/bin/composer; do - [[ -e "$old" && "$old" != "$COMPOSER_BIN" ]] && rm -f "$old" - done - - ensure_usr_local_bin_persist - export PATH="/usr/local/bin:$PATH" - - curl -fsSL https://getcomposer.org/installer -o /tmp/composer-setup.php || { - msg_error "Failed to download Composer installer" - return 1 - } - - $STD php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer || { - msg_error "Failed to install Composer" - rm -f /tmp/composer-setup.php - return 1 - } - rm -f /tmp/composer-setup.php - - if [[ ! -x "$COMPOSER_BIN" ]]; then - msg_error "Composer installation failed" - return 1 - fi - - chmod +x "$COMPOSER_BIN" + # Scenario 1: Already installed - just self-update + if [[ -n "$INSTALLED_VERSION" ]]; then + msg_info "Update Composer $INSTALLED_VERSION" $STD "$COMPOSER_BIN" self-update --no-interaction || true + local UPDATED_VERSION + UPDATED_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}') + cache_installed_version "composer" "$UPDATED_VERSION" + msg_ok "Update Composer $UPDATED_VERSION" + return 0 + fi - local FINAL_VERSION - FINAL_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}') - cache_installed_version "composer" "$FINAL_VERSION" - msg_ok "Setup Composer" + # Scenario 2: Fresh install + msg_info "Setup Composer" + + for old in /usr/bin/composer /bin/composer /root/.composer/vendor/bin/composer; do + [[ -e "$old" && "$old" != "$COMPOSER_BIN" ]] && rm -f "$old" + done + + ensure_usr_local_bin_persist + export PATH="/usr/local/bin:$PATH" + + curl -fsSL https://getcomposer.org/installer -o /tmp/composer-setup.php || { + msg_error "Failed to download Composer installer" + return 1 + } + + $STD php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer || { + msg_error "Failed to install Composer" + rm -f /tmp/composer-setup.php + return 1 + } + rm -f /tmp/composer-setup.php + + if [[ ! -x "$COMPOSER_BIN" ]]; then + msg_error "Composer installation failed" + return 1 + fi + + chmod +x "$COMPOSER_BIN" + $STD "$COMPOSER_BIN" self-update --no-interaction || true + + local FINAL_VERSION + FINAL_VERSION=$("$COMPOSER_BIN" --version 2>/dev/null | awk '{print $3}') + cache_installed_version "composer" "$FINAL_VERSION" + msg_ok "Setup Composer" } # ------------------------------------------------------------------------------ @@ -1886,201 +1886,201 @@ function setup_composer() { # ------------------------------------------------------------------------------ function setup_ffmpeg() { - local TMP_DIR=$(mktemp -d) - local GITHUB_REPO="FFmpeg/FFmpeg" - local VERSION="${FFMPEG_VERSION:-latest}" - local TYPE="${FFMPEG_TYPE:-full}" - local BIN_PATH="/usr/local/bin/ffmpeg" + local TMP_DIR=$(mktemp -d) + local GITHUB_REPO="FFmpeg/FFmpeg" + local VERSION="${FFMPEG_VERSION:-latest}" + local TYPE="${FFMPEG_TYPE:-full}" + local BIN_PATH="/usr/local/bin/ffmpeg" - # Get currently installed version - local INSTALLED_VERSION="" - if command -v ffmpeg &>/dev/null; then - INSTALLED_VERSION=$(ffmpeg -version 2>/dev/null | head -n1 | awk '{print $3}') - fi + # Get currently installed version + local INSTALLED_VERSION="" + if command -v ffmpeg &>/dev/null; then + INSTALLED_VERSION=$(ffmpeg -version 2>/dev/null | head -n1 | awk '{print $3}') + fi - msg_info "Setup FFmpeg ${VERSION} ($TYPE)" + msg_info "Setup FFmpeg ${VERSION} ($TYPE)" - # Binary fallback mode - if [[ "$TYPE" == "binary" ]]; then - curl -fsSL https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz -o "$TMP_DIR/ffmpeg.tar.xz" || { - msg_error "Failed to download FFmpeg binary" - rm -rf "$TMP_DIR" - return 1 - } - tar -xf "$TMP_DIR/ffmpeg.tar.xz" -C "$TMP_DIR" || { - msg_error "Failed to extract FFmpeg binary" - rm -rf "$TMP_DIR" - return 1 - } - local EXTRACTED_DIR - EXTRACTED_DIR=$(find "$TMP_DIR" -maxdepth 1 -type d -name "ffmpeg-*") - cp "$EXTRACTED_DIR/ffmpeg" "$BIN_PATH" - cp "$EXTRACTED_DIR/ffprobe" /usr/local/bin/ffprobe - chmod +x "$BIN_PATH" /usr/local/bin/ffprobe - local FINAL_VERSION=$($BIN_PATH -version 2>/dev/null | head -n1 | awk '{print $3}') - rm -rf "$TMP_DIR" - cache_installed_version "ffmpeg" "$FINAL_VERSION" - ensure_usr_local_bin_persist - [[ -n "$INSTALLED_VERSION" ]] && msg_ok "Upgrade FFmpeg $INSTALLED_VERSION → $FINAL_VERSION" || msg_ok "Setup FFmpeg $FINAL_VERSION" - return 0 - fi - - ensure_dependencies jq - - # Auto-detect latest stable version if none specified - if [[ "$VERSION" == "latest" || -z "$VERSION" ]]; then - local ffmpeg_tags - ffmpeg_tags=$(curl -fsSL --max-time 15 "https://api.github.com/repos/${GITHUB_REPO}/tags" 2>/dev/null || echo "") - - if [[ -z "$ffmpeg_tags" ]]; then - msg_warn "Could not fetch FFmpeg versions from GitHub, trying binary fallback" - VERSION="" # Will trigger binary fallback below - else - VERSION=$(echo "$ffmpeg_tags" | jq -r '.[].name' 2>/dev/null | - grep -E '^n[0-9]+\.[0-9]+\.[0-9]+$' | - sort -V | tail -n1 || echo "") - fi - fi - - if [[ -z "$VERSION" ]]; then - msg_info "Could not determine FFmpeg source version, using pre-built binary" - VERSION="" # Will use binary fallback - fi - - # Dependency selection - local DEPS=(build-essential yasm nasm pkg-config) - case "$TYPE" in - minimal) - DEPS+=(libx264-dev libvpx-dev libmp3lame-dev) - ;; - medium) - DEPS+=(libx264-dev libvpx-dev libmp3lame-dev libfreetype6-dev libass-dev libopus-dev libvorbis-dev) - ;; - full) - DEPS+=( - libx264-dev libx265-dev libvpx-dev libmp3lame-dev - libfreetype6-dev libass-dev libopus-dev libvorbis-dev - libdav1d-dev libsvtav1-dev zlib1g-dev libnuma-dev - libva-dev libdrm-dev - ) - ;; - *) - msg_error "Invalid FFMPEG_TYPE: $TYPE" - rm -rf "$TMP_DIR" - return 1 - ;; - esac - - ensure_dependencies "${DEPS[@]}" - - # Try to download source if VERSION is set - if [[ -n "$VERSION" ]]; then - curl -fsSL "https://github.com/${GITHUB_REPO}/archive/refs/tags/${VERSION}.tar.gz" -o "$TMP_DIR/ffmpeg.tar.gz" || { - msg_warn "Failed to download FFmpeg source ${VERSION}, falling back to pre-built binary" - VERSION="" - } - fi - - # If no source download (either VERSION empty or download failed), use binary - if [[ -z "$VERSION" ]]; then - msg_info "Setup FFmpeg from pre-built binary" - curl -fsSL https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz -o "$TMP_DIR/ffmpeg.tar.xz" || { - msg_error "Failed to download FFmpeg pre-built binary" - rm -rf "$TMP_DIR" - return 1 - } - - tar -xJf "$TMP_DIR/ffmpeg.tar.xz" -C "$TMP_DIR" || { - msg_error "Failed to extract FFmpeg binary archive" - rm -rf "$TMP_DIR" - return 1 - } - - if ! cp "$TMP_DIR/ffmpeg-"*/ffmpeg /usr/local/bin/ffmpeg 2>/dev/null; then - msg_error "Failed to install FFmpeg binary" - rm -rf "$TMP_DIR" - return 1 - fi - - cache_installed_version "ffmpeg" "static" - rm -rf "$TMP_DIR" - msg_ok "Setup FFmpeg from pre-built binary" - return 0 - fi - - tar -xzf "$TMP_DIR/ffmpeg.tar.gz" -C "$TMP_DIR" || { - msg_error "Failed to extract FFmpeg source" - rm -rf "$TMP_DIR" - return 1 + # Binary fallback mode + if [[ "$TYPE" == "binary" ]]; then + curl -fsSL https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz -o "$TMP_DIR/ffmpeg.tar.xz" || { + msg_error "Failed to download FFmpeg binary" + rm -rf "$TMP_DIR" + return 1 } - - cd "$TMP_DIR/FFmpeg-"* || { - msg_error "Source extraction failed" - rm -rf "$TMP_DIR" - return 1 + tar -xf "$TMP_DIR/ffmpeg.tar.xz" -C "$TMP_DIR" || { + msg_error "Failed to extract FFmpeg binary" + rm -rf "$TMP_DIR" + return 1 } - - local args=( - --enable-gpl - --enable-shared - --enable-nonfree - --disable-static - --enable-libx264 - --enable-libvpx - --enable-libmp3lame - ) - - if [[ "$TYPE" != "minimal" ]]; then - args+=(--enable-libfreetype --enable-libass --enable-libopus --enable-libvorbis) - fi - - if [[ "$TYPE" == "full" ]]; then - args+=(--enable-libx265 --enable-libdav1d --enable-zlib) - args+=(--enable-vaapi --enable-libdrm) - fi - - if [[ ${#args[@]} -eq 0 ]]; then - msg_error "FFmpeg configure args array is empty" - rm -rf "$TMP_DIR" - return 1 - fi - - $STD ./configure "${args[@]}" || { - msg_error "FFmpeg configure failed" - rm -rf "$TMP_DIR" - return 1 - } - $STD make -j"$(nproc)" || { - msg_error "FFmpeg compilation failed" - rm -rf "$TMP_DIR" - return 1 - } - $STD make install || { - msg_error "FFmpeg installation failed" - rm -rf "$TMP_DIR" - return 1 - } - echo "/usr/local/lib" >/etc/ld.so.conf.d/ffmpeg.conf - $STD ldconfig - - ldconfig -p 2>/dev/null | grep libavdevice >/dev/null || { - msg_error "libavdevice not registered with dynamic linker" - rm -rf "$TMP_DIR" - return 1 - } - - if ! command -v ffmpeg &>/dev/null; then - msg_error "FFmpeg installation failed" - rm -rf "$TMP_DIR" - return 1 - fi - - local FINAL_VERSION - FINAL_VERSION=$(ffmpeg -version 2>/dev/null | head -n1 | awk '{print $3}') + local EXTRACTED_DIR + EXTRACTED_DIR=$(find "$TMP_DIR" -maxdepth 1 -type d -name "ffmpeg-*") + cp "$EXTRACTED_DIR/ffmpeg" "$BIN_PATH" + cp "$EXTRACTED_DIR/ffprobe" /usr/local/bin/ffprobe + chmod +x "$BIN_PATH" /usr/local/bin/ffprobe + local FINAL_VERSION=$($BIN_PATH -version 2>/dev/null | head -n1 | awk '{print $3}') rm -rf "$TMP_DIR" cache_installed_version "ffmpeg" "$FINAL_VERSION" ensure_usr_local_bin_persist [[ -n "$INSTALLED_VERSION" ]] && msg_ok "Upgrade FFmpeg $INSTALLED_VERSION → $FINAL_VERSION" || msg_ok "Setup FFmpeg $FINAL_VERSION" + return 0 + fi + + ensure_dependencies jq + + # Auto-detect latest stable version if none specified + if [[ "$VERSION" == "latest" || -z "$VERSION" ]]; then + local ffmpeg_tags + ffmpeg_tags=$(curl -fsSL --max-time 15 "https://api.github.com/repos/${GITHUB_REPO}/tags" 2>/dev/null || echo "") + + if [[ -z "$ffmpeg_tags" ]]; then + msg_warn "Could not fetch FFmpeg versions from GitHub, trying binary fallback" + VERSION="" # Will trigger binary fallback below + else + VERSION=$(echo "$ffmpeg_tags" | jq -r '.[].name' 2>/dev/null | + grep -E '^n[0-9]+\.[0-9]+\.[0-9]+$' | + sort -V | tail -n1 || echo "") + fi + fi + + if [[ -z "$VERSION" ]]; then + msg_info "Could not determine FFmpeg source version, using pre-built binary" + VERSION="" # Will use binary fallback + fi + + # Dependency selection + local DEPS=(build-essential yasm nasm pkg-config) + case "$TYPE" in + minimal) + DEPS+=(libx264-dev libvpx-dev libmp3lame-dev) + ;; + medium) + DEPS+=(libx264-dev libvpx-dev libmp3lame-dev libfreetype6-dev libass-dev libopus-dev libvorbis-dev) + ;; + full) + DEPS+=( + libx264-dev libx265-dev libvpx-dev libmp3lame-dev + libfreetype6-dev libass-dev libopus-dev libvorbis-dev + libdav1d-dev libsvtav1-dev zlib1g-dev libnuma-dev + libva-dev libdrm-dev + ) + ;; + *) + msg_error "Invalid FFMPEG_TYPE: $TYPE" + rm -rf "$TMP_DIR" + return 1 + ;; + esac + + ensure_dependencies "${DEPS[@]}" + + # Try to download source if VERSION is set + if [[ -n "$VERSION" ]]; then + curl -fsSL "https://github.com/${GITHUB_REPO}/archive/refs/tags/${VERSION}.tar.gz" -o "$TMP_DIR/ffmpeg.tar.gz" || { + msg_warn "Failed to download FFmpeg source ${VERSION}, falling back to pre-built binary" + VERSION="" + } + fi + + # If no source download (either VERSION empty or download failed), use binary + if [[ -z "$VERSION" ]]; then + msg_info "Setup FFmpeg from pre-built binary" + curl -fsSL https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz -o "$TMP_DIR/ffmpeg.tar.xz" || { + msg_error "Failed to download FFmpeg pre-built binary" + rm -rf "$TMP_DIR" + return 1 + } + + tar -xJf "$TMP_DIR/ffmpeg.tar.xz" -C "$TMP_DIR" || { + msg_error "Failed to extract FFmpeg binary archive" + rm -rf "$TMP_DIR" + return 1 + } + + if ! cp "$TMP_DIR/ffmpeg-"*/ffmpeg /usr/local/bin/ffmpeg 2>/dev/null; then + msg_error "Failed to install FFmpeg binary" + rm -rf "$TMP_DIR" + return 1 + fi + + cache_installed_version "ffmpeg" "static" + rm -rf "$TMP_DIR" + msg_ok "Setup FFmpeg from pre-built binary" + return 0 + fi + + tar -xzf "$TMP_DIR/ffmpeg.tar.gz" -C "$TMP_DIR" || { + msg_error "Failed to extract FFmpeg source" + rm -rf "$TMP_DIR" + return 1 + } + + cd "$TMP_DIR/FFmpeg-"* || { + msg_error "Source extraction failed" + rm -rf "$TMP_DIR" + return 1 + } + + local args=( + --enable-gpl + --enable-shared + --enable-nonfree + --disable-static + --enable-libx264 + --enable-libvpx + --enable-libmp3lame + ) + + if [[ "$TYPE" != "minimal" ]]; then + args+=(--enable-libfreetype --enable-libass --enable-libopus --enable-libvorbis) + fi + + if [[ "$TYPE" == "full" ]]; then + args+=(--enable-libx265 --enable-libdav1d --enable-zlib) + args+=(--enable-vaapi --enable-libdrm) + fi + + if [[ ${#args[@]} -eq 0 ]]; then + msg_error "FFmpeg configure args array is empty" + rm -rf "$TMP_DIR" + return 1 + fi + + $STD ./configure "${args[@]}" || { + msg_error "FFmpeg configure failed" + rm -rf "$TMP_DIR" + return 1 + } + $STD make -j"$(nproc)" || { + msg_error "FFmpeg compilation failed" + rm -rf "$TMP_DIR" + return 1 + } + $STD make install || { + msg_error "FFmpeg installation failed" + rm -rf "$TMP_DIR" + return 1 + } + echo "/usr/local/lib" >/etc/ld.so.conf.d/ffmpeg.conf + $STD ldconfig + + ldconfig -p 2>/dev/null | grep libavdevice >/dev/null || { + msg_error "libavdevice not registered with dynamic linker" + rm -rf "$TMP_DIR" + return 1 + } + + if ! command -v ffmpeg &>/dev/null; then + msg_error "FFmpeg installation failed" + rm -rf "$TMP_DIR" + return 1 + fi + + local FINAL_VERSION + FINAL_VERSION=$(ffmpeg -version 2>/dev/null | head -n1 | awk '{print $3}') + rm -rf "$TMP_DIR" + cache_installed_version "ffmpeg" "$FINAL_VERSION" + ensure_usr_local_bin_persist + [[ -n "$INSTALLED_VERSION" ]] && msg_ok "Upgrade FFmpeg $INSTALLED_VERSION → $FINAL_VERSION" || msg_ok "Setup FFmpeg $FINAL_VERSION" } # ------------------------------------------------------------------------------ @@ -2095,75 +2095,75 @@ function setup_ffmpeg() { # ------------------------------------------------------------------------------ function setup_go() { - local ARCH - case "$(uname -m)" in - x86_64) ARCH="amd64" ;; - aarch64) ARCH="arm64" ;; - *) - msg_error "Unsupported architecture: $(uname -m)" - return 1 - ;; - esac + local ARCH + case "$(uname -m)" in + x86_64) ARCH="amd64" ;; + aarch64) ARCH="arm64" ;; + *) + msg_error "Unsupported architecture: $(uname -m)" + return 1 + ;; + esac - # Resolve "latest" version - local GO_VERSION="${GO_VERSION:-latest}" - if [[ "$GO_VERSION" == "latest" ]]; then - GO_VERSION=$(curl -fsSL https://go.dev/VERSION?m=text 2>/dev/null | head -n1 | sed 's/^go//') || { - msg_error "Could not determine latest Go version" - return 1 - } - [[ -z "$GO_VERSION" ]] && { - msg_error "Latest Go version is empty" - return 1 - } - fi - - local GO_BIN="/usr/local/bin/go" - local GO_INSTALL_DIR="/usr/local/go" - - # Get currently installed version - local CURRENT_VERSION="" - if [[ -x "$GO_BIN" ]]; then - CURRENT_VERSION=$("$GO_BIN" version 2>/dev/null | awk '{print $3}' | sed 's/go//') - fi - - # Scenario 1: Already at target version - if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$GO_VERSION" ]]; then - cache_installed_version "go" "$GO_VERSION" - return 0 - fi - - # Scenario 2: Different version or not installed - if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" != "$GO_VERSION" ]]; then - msg_info "Upgrade Go from $CURRENT_VERSION to $GO_VERSION" - remove_old_tool_version "go" - else - msg_info "Setup Go $GO_VERSION" - fi - - local TARBALL="go${GO_VERSION}.linux-${ARCH}.tar.gz" - local URL="https://go.dev/dl/${TARBALL}" - local TMP_TAR=$(mktemp) - - curl -fsSL "$URL" -o "$TMP_TAR" || { - msg_error "Failed to download Go $GO_VERSION" - rm -f "$TMP_TAR" - return 1 + # Resolve "latest" version + local GO_VERSION="${GO_VERSION:-latest}" + if [[ "$GO_VERSION" == "latest" ]]; then + GO_VERSION=$(curl -fsSL https://go.dev/VERSION?m=text 2>/dev/null | head -n1 | sed 's/^go//') || { + msg_error "Could not determine latest Go version" + return 1 } - - $STD tar -C /usr/local -xzf "$TMP_TAR" || { - msg_error "Failed to extract Go tarball" - rm -f "$TMP_TAR" - return 1 + [[ -z "$GO_VERSION" ]] && { + msg_error "Latest Go version is empty" + return 1 } + fi - ln -sf /usr/local/go/bin/go /usr/local/bin/go - ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt - rm -f "$TMP_TAR" + local GO_BIN="/usr/local/bin/go" + local GO_INSTALL_DIR="/usr/local/go" + # Get currently installed version + local CURRENT_VERSION="" + if [[ -x "$GO_BIN" ]]; then + CURRENT_VERSION=$("$GO_BIN" version 2>/dev/null | awk '{print $3}' | sed 's/go//') + fi + + # Scenario 1: Already at target version + if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$GO_VERSION" ]]; then cache_installed_version "go" "$GO_VERSION" - ensure_usr_local_bin_persist - msg_ok "Setup Go $GO_VERSION" + return 0 + fi + + # Scenario 2: Different version or not installed + if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" != "$GO_VERSION" ]]; then + msg_info "Upgrade Go from $CURRENT_VERSION to $GO_VERSION" + remove_old_tool_version "go" + else + msg_info "Setup Go $GO_VERSION" + fi + + local TARBALL="go${GO_VERSION}.linux-${ARCH}.tar.gz" + local URL="https://go.dev/dl/${TARBALL}" + local TMP_TAR=$(mktemp) + + curl -fsSL "$URL" -o "$TMP_TAR" || { + msg_error "Failed to download Go $GO_VERSION" + rm -f "$TMP_TAR" + return 1 + } + + $STD tar -C /usr/local -xzf "$TMP_TAR" || { + msg_error "Failed to extract Go tarball" + rm -f "$TMP_TAR" + return 1 + } + + ln -sf /usr/local/go/bin/go /usr/local/bin/go + ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt + rm -f "$TMP_TAR" + + cache_installed_version "go" "$GO_VERSION" + ensure_usr_local_bin_persist + msg_ok "Setup Go $GO_VERSION" } # ------------------------------------------------------------------------------ @@ -2175,110 +2175,110 @@ function setup_go() { # ------------------------------------------------------------------------------ function setup_gs() { - local TMP_DIR=$(mktemp -d) - local CURRENT_VERSION=$(gs --version 2>/dev/null || echo "0") + local TMP_DIR=$(mktemp -d) + local CURRENT_VERSION=$(gs --version 2>/dev/null || echo "0") - ensure_dependencies jq + ensure_dependencies jq - local RELEASE_JSON - RELEASE_JSON=$(curl -fsSL --max-time 15 https://api.github.com/repos/ArtifexSoftware/ghostpdl-downloads/releases/latest 2>/dev/null || echo "") + local RELEASE_JSON + RELEASE_JSON=$(curl -fsSL --max-time 15 https://api.github.com/repos/ArtifexSoftware/ghostpdl-downloads/releases/latest 2>/dev/null || echo "") - if [[ -z "$RELEASE_JSON" ]]; then - msg_warn "Cannot fetch latest Ghostscript version from GitHub API" - # Try to get from current version - if command -v gs &>/dev/null; then - gs --version | head -n1 - cache_installed_version "ghostscript" "$CURRENT_VERSION" - return 0 - fi - msg_error "Cannot determine Ghostscript version and no existing installation found" - return 1 + if [[ -z "$RELEASE_JSON" ]]; then + msg_warn "Cannot fetch latest Ghostscript version from GitHub API" + # Try to get from current version + if command -v gs &>/dev/null; then + gs --version | head -n1 + cache_installed_version "ghostscript" "$CURRENT_VERSION" + return 0 fi - local LATEST_VERSION - LATEST_VERSION=$(echo "$RELEASE_JSON" | jq -r '.tag_name' | sed 's/^gs//') - local LATEST_VERSION_DOTTED - LATEST_VERSION_DOTTED=$(echo "$RELEASE_JSON" | jq -r '.name' | grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+') + msg_error "Cannot determine Ghostscript version and no existing installation found" + return 1 + fi + local LATEST_VERSION + LATEST_VERSION=$(echo "$RELEASE_JSON" | jq -r '.tag_name' | sed 's/^gs//') + local LATEST_VERSION_DOTTED + LATEST_VERSION_DOTTED=$(echo "$RELEASE_JSON" | jq -r '.name' | grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+') - if [[ -z "$LATEST_VERSION" || -z "$LATEST_VERSION_DOTTED" ]]; then - msg_warn "Could not determine latest Ghostscript version from GitHub - checking system" - # Fallback: try to use system version or return error - if [[ "$CURRENT_VERSION" == "0" ]]; then - msg_error "Ghostscript not installed and cannot determine latest version" - rm -rf "$TMP_DIR" - return 1 - fi - rm -rf "$TMP_DIR" - return 0 + if [[ -z "$LATEST_VERSION" || -z "$LATEST_VERSION_DOTTED" ]]; then + msg_warn "Could not determine latest Ghostscript version from GitHub - checking system" + # Fallback: try to use system version or return error + if [[ "$CURRENT_VERSION" == "0" ]]; then + msg_error "Ghostscript not installed and cannot determine latest version" + rm -rf "$TMP_DIR" + return 1 fi - - # Scenario 1: Already at latest version - if [[ -n "$LATEST_VERSION_DOTTED" ]] && dpkg --compare-versions "$CURRENT_VERSION" ge "$LATEST_VERSION_DOTTED" 2>/dev/null; then - cache_installed_version "ghostscript" "$LATEST_VERSION_DOTTED" - rm -rf "$TMP_DIR" - return 0 - fi - - # Scenario 2: New install or upgrade - if [[ "$CURRENT_VERSION" != "0" && "$CURRENT_VERSION" != "$LATEST_VERSION_DOTTED" ]]; then - msg_info "Upgrade Ghostscript from $CURRENT_VERSION to $LATEST_VERSION_DOTTED" - else - msg_info "Setup Ghostscript $LATEST_VERSION_DOTTED" - fi - - curl -fsSL "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs${LATEST_VERSION}/ghostscript-${LATEST_VERSION_DOTTED}.tar.gz" -o "$TMP_DIR/ghostscript.tar.gz" || { - msg_error "Failed to download Ghostscript" - rm -rf "$TMP_DIR" - return 1 - } - - if ! tar -xzf "$TMP_DIR/ghostscript.tar.gz" -C "$TMP_DIR"; then - msg_error "Failed to extract Ghostscript archive" - rm -rf "$TMP_DIR" - return 1 - fi - - # Verify directory exists before cd - if [[ ! -d "$TMP_DIR/ghostscript-${LATEST_VERSION_DOTTED}" ]]; then - msg_error "Ghostscript source directory not found: $TMP_DIR/ghostscript-${LATEST_VERSION_DOTTED}" - rm -rf "$TMP_DIR" - return 1 - fi - - cd "$TMP_DIR/ghostscript-${LATEST_VERSION_DOTTED}" || { - msg_error "Failed to enter Ghostscript source directory" - rm -rf "$TMP_DIR" - return 1 - } - - ensure_dependencies build-essential libpng-dev zlib1g-dev - - $STD ./configure || { - msg_error "Ghostscript configure failed" - rm -rf "$TMP_DIR" - return 1 - } - $STD make -j"$(nproc)" || { - msg_error "Ghostscript compilation failed" - rm -rf "$TMP_DIR" - return 1 - } - $STD make install || { - msg_error "Ghostscript installation failed" - rm -rf "$TMP_DIR" - return 1 - } - - hash -r - if [[ ! -x "$(command -v gs)" ]]; then - if [[ -x /usr/local/bin/gs ]]; then - ln -sf /usr/local/bin/gs /usr/bin/gs - fi - fi - rm -rf "$TMP_DIR" + return 0 + fi + + # Scenario 1: Already at latest version + if [[ -n "$LATEST_VERSION_DOTTED" ]] && dpkg --compare-versions "$CURRENT_VERSION" ge "$LATEST_VERSION_DOTTED" 2>/dev/null; then cache_installed_version "ghostscript" "$LATEST_VERSION_DOTTED" - ensure_usr_local_bin_persist - msg_ok "Setup Ghostscript $LATEST_VERSION_DOTTED" + rm -rf "$TMP_DIR" + return 0 + fi + + # Scenario 2: New install or upgrade + if [[ "$CURRENT_VERSION" != "0" && "$CURRENT_VERSION" != "$LATEST_VERSION_DOTTED" ]]; then + msg_info "Upgrade Ghostscript from $CURRENT_VERSION to $LATEST_VERSION_DOTTED" + else + msg_info "Setup Ghostscript $LATEST_VERSION_DOTTED" + fi + + curl -fsSL "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs${LATEST_VERSION}/ghostscript-${LATEST_VERSION_DOTTED}.tar.gz" -o "$TMP_DIR/ghostscript.tar.gz" || { + msg_error "Failed to download Ghostscript" + rm -rf "$TMP_DIR" + return 1 + } + + if ! tar -xzf "$TMP_DIR/ghostscript.tar.gz" -C "$TMP_DIR"; then + msg_error "Failed to extract Ghostscript archive" + rm -rf "$TMP_DIR" + return 1 + fi + + # Verify directory exists before cd + if [[ ! -d "$TMP_DIR/ghostscript-${LATEST_VERSION_DOTTED}" ]]; then + msg_error "Ghostscript source directory not found: $TMP_DIR/ghostscript-${LATEST_VERSION_DOTTED}" + rm -rf "$TMP_DIR" + return 1 + fi + + cd "$TMP_DIR/ghostscript-${LATEST_VERSION_DOTTED}" || { + msg_error "Failed to enter Ghostscript source directory" + rm -rf "$TMP_DIR" + return 1 + } + + ensure_dependencies build-essential libpng-dev zlib1g-dev + + $STD ./configure || { + msg_error "Ghostscript configure failed" + rm -rf "$TMP_DIR" + return 1 + } + $STD make -j"$(nproc)" || { + msg_error "Ghostscript compilation failed" + rm -rf "$TMP_DIR" + return 1 + } + $STD make install || { + msg_error "Ghostscript installation failed" + rm -rf "$TMP_DIR" + return 1 + } + + hash -r + if [[ ! -x "$(command -v gs)" ]]; then + if [[ -x /usr/local/bin/gs ]]; then + ln -sf /usr/local/bin/gs /usr/bin/gs + fi + fi + + rm -rf "$TMP_DIR" + cache_installed_version "ghostscript" "$LATEST_VERSION_DOTTED" + ensure_usr_local_bin_persist + msg_ok "Setup Ghostscript $LATEST_VERSION_DOTTED" } # ------------------------------------------------------------------------------ @@ -2293,111 +2293,111 @@ function setup_gs() { # - Some things are fetched from intel repositories due to not being in debian repositories. # ------------------------------------------------------------------------------ function setup_hwaccel() { - msg_info "Setup Hardware Acceleration" + msg_info "Setup Hardware Acceleration" - if ! command -v lspci &>/dev/null; then - $STD apt -y update || { - msg_error "Failed to update package list" - return 1 - } - $STD apt -y install pciutils || { - msg_error "Failed to install pciutils" - return 1 - } - fi + if ! command -v lspci &>/dev/null; then + $STD apt -y update || { + msg_error "Failed to update package list" + return 1 + } + $STD apt -y install pciutils || { + msg_error "Failed to install pciutils" + return 1 + } + fi - # Detect GPU vendor (Intel, AMD, NVIDIA) - local gpu_vendor - gpu_vendor=$(lspci 2>/dev/null | grep -Ei 'vga|3d|display' | grep -Eo 'Intel|AMD|NVIDIA' | head -n1 || echo "") + # Detect GPU vendor (Intel, AMD, NVIDIA) + local gpu_vendor + gpu_vendor=$(lspci 2>/dev/null | grep -Ei 'vga|3d|display' | grep -Eo 'Intel|AMD|NVIDIA' | head -n1 || echo "") - # Detect CPU vendor (relevant for AMD APUs) - local cpu_vendor - cpu_vendor=$(lscpu 2>/dev/null | grep -i 'Vendor ID' | awk '{print $3}' || echo "") + # Detect CPU vendor (relevant for AMD APUs) + local cpu_vendor + cpu_vendor=$(lscpu 2>/dev/null | grep -i 'Vendor ID' | awk '{print $3}' || echo "") - if [[ -z "$gpu_vendor" && -z "$cpu_vendor" ]]; then - msg_error "No GPU or CPU vendor detected (missing lspci/lscpu output)" + if [[ -z "$gpu_vendor" && -z "$cpu_vendor" ]]; then + msg_error "No GPU or CPU vendor detected (missing lspci/lscpu output)" + return 1 + fi + + # Detect OS with fallbacks + local os_id os_codename + os_id=$(grep -oP '(?<=^ID=).+' /etc/os-release 2>/dev/null | tr -d '"' || grep '^ID=' /etc/os-release 2>/dev/null | cut -d'=' -f2 | tr -d '"' || echo "debian") + os_codename=$(grep -oP '(?<=^VERSION_CODENAME=).+' /etc/os-release 2>/dev/null | tr -d '"' || grep '^VERSION_CODENAME=' /etc/os-release 2>/dev/null | cut -d'=' -f2 | tr -d '"' || echo "unknown") + + # Validate os_id + if [[ -z "$os_id" ]]; then + os_id="debian" + fi + + # Determine if we are on a VM or LXC + local in_ct="${CTTYPE:-0}" + + case "$gpu_vendor" in + Intel) + if [[ "$os_id" == "ubuntu" ]]; then + $STD apt -y install intel-opencl-icd || { + msg_error "Failed to install intel-opencl-icd" return 1 + } + else + # For Debian: fetch Intel GPU drivers from GitHub + fetch_and_deploy_gh_release "" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-core-2_*_amd64.deb" || { + msg_warn "Failed to deploy Intel IGC core 2" + } + fetch_and_deploy_gh_release "" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-opencl-2_*_amd64.deb" || { + msg_warn "Failed to deploy Intel IGC OpenCL 2" + } + fetch_and_deploy_gh_release "" "intel/compute-runtime" "binary" "latest" "" "libigdgmm12_*_amd64.deb" || { + msg_warn "Failed to deploy Intel GDGMM12" + } + fetch_and_deploy_gh_release "" "intel/compute-runtime" "binary" "latest" "" "intel-opencl-icd_*_amd64.deb" || { + msg_warn "Failed to deploy Intel OpenCL ICD" + } fi - # Detect OS with fallbacks - local os_id os_codename - os_id=$(grep -oP '(?<=^ID=).+' /etc/os-release 2>/dev/null | tr -d '"' || grep '^ID=' /etc/os-release 2>/dev/null | cut -d'=' -f2 | tr -d '"' || echo "debian") - os_codename=$(grep -oP '(?<=^VERSION_CODENAME=).+' /etc/os-release 2>/dev/null | tr -d '"' || grep '^VERSION_CODENAME=' /etc/os-release 2>/dev/null | cut -d'=' -f2 | tr -d '"' || echo "unknown") + $STD apt -y install va-driver-all ocl-icd-libopencl1 vainfo intel-gpu-tools || { + msg_error "Failed to install Intel GPU dependencies" + return 1 + } + ;; + AMD) + $STD apt -y install mesa-va-drivers mesa-vdpau-drivers mesa-opencl-icd vainfo clinfo || { + msg_error "Failed to install AMD GPU dependencies" + return 1 + } - # Validate os_id - if [[ -z "$os_id" ]]; then - os_id="debian" + # For AMD CPUs without discrete GPU (APUs) + if [[ "$cpu_vendor" == "AuthenticAMD" && -n "$gpu_vendor" ]]; then + $STD apt -y install libdrm-amdgpu1 firmware-amd-graphics || true fi - - # Determine if we are on a VM or LXC - local in_ct="${CTTYPE:-0}" - - case "$gpu_vendor" in - Intel) - if [[ "$os_id" == "ubuntu" ]]; then - $STD apt -y install intel-opencl-icd || { - msg_error "Failed to install intel-opencl-icd" - return 1 - } - else - # For Debian: fetch Intel GPU drivers from GitHub - fetch_and_deploy_gh_release "" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-core-2_*_amd64.deb" || { - msg_warn "Failed to deploy Intel IGC core 2" - } - fetch_and_deploy_gh_release "" "intel/intel-graphics-compiler" "binary" "latest" "" "intel-igc-opencl-2_*_amd64.deb" || { - msg_warn "Failed to deploy Intel IGC OpenCL 2" - } - fetch_and_deploy_gh_release "" "intel/compute-runtime" "binary" "latest" "" "libigdgmm12_*_amd64.deb" || { - msg_warn "Failed to deploy Intel GDGMM12" - } - fetch_and_deploy_gh_release "" "intel/compute-runtime" "binary" "latest" "" "intel-opencl-icd_*_amd64.deb" || { - msg_warn "Failed to deploy Intel OpenCL ICD" - } - fi - - $STD apt -y install va-driver-all ocl-icd-libopencl1 vainfo intel-gpu-tools || { - msg_error "Failed to install Intel GPU dependencies" - return 1 - } - ;; - AMD) - $STD apt -y install mesa-va-drivers mesa-vdpau-drivers mesa-opencl-icd vainfo clinfo || { - msg_error "Failed to install AMD GPU dependencies" - return 1 - } - - # For AMD CPUs without discrete GPU (APUs) - if [[ "$cpu_vendor" == "AuthenticAMD" && -n "$gpu_vendor" ]]; then - $STD apt -y install libdrm-amdgpu1 firmware-amd-graphics || true - fi - ;; - NVIDIA) - # NVIDIA needs manual driver setup - skip for now - msg_info "NVIDIA GPU detected - manual driver setup required" - ;; - *) - # If no discrete GPU, but AMD CPU (e.g., Ryzen APU) - if [[ "$cpu_vendor" == "AuthenticAMD" ]]; then - $STD apt -y install mesa-opencl-icd ocl-icd-libopencl1 clinfo || { - msg_error "Failed to install Mesa OpenCL stack" - return 1 - } - else - msg_warn "No supported GPU vendor detected - skipping GPU acceleration" - fi - ;; - esac - - if [[ "$in_ct" == "0" ]]; then - chgrp video /dev/dri 2>/dev/null || true - chmod 755 /dev/dri 2>/dev/null || true - chmod 660 /dev/dri/* 2>/dev/null || true - $STD adduser "$(id -u -n)" video - $STD adduser "$(id -u -n)" render + ;; + NVIDIA) + # NVIDIA needs manual driver setup - skip for now + msg_info "NVIDIA GPU detected - manual driver setup required" + ;; + *) + # If no discrete GPU, but AMD CPU (e.g., Ryzen APU) + if [[ "$cpu_vendor" == "AuthenticAMD" ]]; then + $STD apt -y install mesa-opencl-icd ocl-icd-libopencl1 clinfo || { + msg_error "Failed to install Mesa OpenCL stack" + return 1 + } + else + msg_warn "No supported GPU vendor detected - skipping GPU acceleration" fi + ;; + esac - cache_installed_version "hwaccel" "1.0" - msg_ok "Setup Hardware Acceleration" + if [[ "$in_ct" == "0" ]]; then + chgrp video /dev/dri 2>/dev/null || true + chmod 755 /dev/dri 2>/dev/null || true + chmod 660 /dev/dri/* 2>/dev/null || true + $STD adduser "$(id -u -n)" video + $STD adduser "$(id -u -n)" render + fi + + cache_installed_version "hwaccel" "1.0" + msg_ok "Setup Hardware Acceleration" } # ------------------------------------------------------------------------------ @@ -2412,89 +2412,89 @@ function setup_hwaccel() { # - Requires: build-essential, libtool, libjpeg-dev, libpng-dev, etc. # ------------------------------------------------------------------------------ function setup_imagemagick() { - local TMP_DIR=$(mktemp -d) - local BINARY_PATH="/usr/local/bin/magick" + local TMP_DIR=$(mktemp -d) + local BINARY_PATH="/usr/local/bin/magick" - # Get currently installed version - local INSTALLED_VERSION="" - if command -v magick &>/dev/null; then - INSTALLED_VERSION=$(magick -version | awk '/^Version/ {print $3}') - fi + # Get currently installed version + local INSTALLED_VERSION="" + if command -v magick &>/dev/null; then + INSTALLED_VERSION=$(magick -version | awk '/^Version/ {print $3}') + fi - msg_info "Setup ImageMagick" + msg_info "Setup ImageMagick" - ensure_dependencies \ - build-essential \ - libtool \ - libjpeg-dev \ - libpng-dev \ - libtiff-dev \ - libwebp-dev \ - libheif-dev \ - libde265-dev \ - libopenjp2-7-dev \ - libxml2-dev \ - liblcms2-dev \ - libfreetype6-dev \ - libraw-dev \ - libfftw3-dev \ - liblqr-1-0-dev \ - libgsl-dev \ - pkg-config \ - ghostscript + ensure_dependencies \ + build-essential \ + libtool \ + libjpeg-dev \ + libpng-dev \ + libtiff-dev \ + libwebp-dev \ + libheif-dev \ + libde265-dev \ + libopenjp2-7-dev \ + libxml2-dev \ + liblcms2-dev \ + libfreetype6-dev \ + libraw-dev \ + libfftw3-dev \ + liblqr-1-0-dev \ + libgsl-dev \ + pkg-config \ + ghostscript - curl -fsSL https://imagemagick.org/archive/ImageMagick.tar.gz -o "$TMP_DIR/ImageMagick.tar.gz" || { - msg_error "Failed to download ImageMagick" - rm -rf "$TMP_DIR" - return 1 - } - - tar -xzf "$TMP_DIR/ImageMagick.tar.gz" -C "$TMP_DIR" || { - msg_error "Failed to extract ImageMagick" - rm -rf "$TMP_DIR" - return 1 - } - - cd "$TMP_DIR"/ImageMagick-* || { - msg_error "Source extraction failed" - rm -rf "$TMP_DIR" - return 1 - } - - $STD ./configure --disable-static || { - msg_error "ImageMagick configure failed" - rm -rf "$TMP_DIR" - return 1 - } - $STD make -j"$(nproc)" || { - msg_error "ImageMagick compilation failed" - rm -rf "$TMP_DIR" - return 1 - } - $STD make install || { - msg_error "ImageMagick installation failed" - rm -rf "$TMP_DIR" - return 1 - } - $STD ldconfig /usr/local/lib - - if [[ ! -x "$BINARY_PATH" ]]; then - msg_error "ImageMagick installation failed" - rm -rf "$TMP_DIR" - return 1 - fi - - local FINAL_VERSION - FINAL_VERSION=$("$BINARY_PATH" -version | awk '/^Version/ {print $3}') + curl -fsSL https://imagemagick.org/archive/ImageMagick.tar.gz -o "$TMP_DIR/ImageMagick.tar.gz" || { + msg_error "Failed to download ImageMagick" rm -rf "$TMP_DIR" - cache_installed_version "imagemagick" "$FINAL_VERSION" - ensure_usr_local_bin_persist + return 1 + } - if [[ -n "$INSTALLED_VERSION" ]]; then - msg_ok "Upgrade ImageMagick $INSTALLED_VERSION → $FINAL_VERSION" - else - msg_ok "Setup ImageMagick $FINAL_VERSION" - fi + tar -xzf "$TMP_DIR/ImageMagick.tar.gz" -C "$TMP_DIR" || { + msg_error "Failed to extract ImageMagick" + rm -rf "$TMP_DIR" + return 1 + } + + cd "$TMP_DIR"/ImageMagick-* || { + msg_error "Source extraction failed" + rm -rf "$TMP_DIR" + return 1 + } + + $STD ./configure --disable-static || { + msg_error "ImageMagick configure failed" + rm -rf "$TMP_DIR" + return 1 + } + $STD make -j"$(nproc)" || { + msg_error "ImageMagick compilation failed" + rm -rf "$TMP_DIR" + return 1 + } + $STD make install || { + msg_error "ImageMagick installation failed" + rm -rf "$TMP_DIR" + return 1 + } + $STD ldconfig /usr/local/lib + + if [[ ! -x "$BINARY_PATH" ]]; then + msg_error "ImageMagick installation failed" + rm -rf "$TMP_DIR" + return 1 + fi + + local FINAL_VERSION + FINAL_VERSION=$("$BINARY_PATH" -version | awk '/^Version/ {print $3}') + rm -rf "$TMP_DIR" + cache_installed_version "imagemagick" "$FINAL_VERSION" + ensure_usr_local_bin_persist + + if [[ -n "$INSTALLED_VERSION" ]]; then + msg_ok "Upgrade ImageMagick $INSTALLED_VERSION → $FINAL_VERSION" + else + msg_ok "Setup ImageMagick $FINAL_VERSION" + fi } # ------------------------------------------------------------------------------ @@ -2509,74 +2509,74 @@ function setup_imagemagick() { # ------------------------------------------------------------------------------ function setup_java() { - local JAVA_VERSION="${JAVA_VERSION:-21}" - local DISTRO_ID DISTRO_CODENAME - DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') - DISTRO_CODENAME=$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release) - local DESIRED_PACKAGE="temurin-${JAVA_VERSION}-jdk" + local JAVA_VERSION="${JAVA_VERSION:-21}" + local DISTRO_ID DISTRO_CODENAME + DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + DISTRO_CODENAME=$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release) + local DESIRED_PACKAGE="temurin-${JAVA_VERSION}-jdk" - # Add repo if needed - if [[ ! -f /etc/apt/sources.list.d/adoptium.sources ]]; then - cleanup_old_repo_files "adoptium" - local SUITE - SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://packages.adoptium.net/artifactory/deb") - setup_deb822_repo \ - "adoptium" \ - "https://packages.adoptium.net/artifactory/api/gpg/key/public" \ - "https://packages.adoptium.net/artifactory/deb" \ - "$SUITE" \ - "main" \ - "amd64 arm64" - fi + # Add repo if needed + if [[ ! -f /etc/apt/sources.list.d/adoptium.sources ]]; then + cleanup_old_repo_files "adoptium" + local SUITE + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://packages.adoptium.net/artifactory/deb") + setup_deb822_repo \ + "adoptium" \ + "https://packages.adoptium.net/artifactory/api/gpg/key/public" \ + "https://packages.adoptium.net/artifactory/deb" \ + "$SUITE" \ + "main" \ + "amd64 arm64" + fi - # Get currently installed version - local INSTALLED_VERSION="" - if dpkg -l | grep -q "temurin-.*-jdk" 2>/dev/null; then - INSTALLED_VERSION=$(dpkg -l 2>/dev/null | awk '/temurin-.*-jdk/{print $2}' | grep -oP 'temurin-\K[0-9]+' | head -n1 || echo "") - fi + # Get currently installed version + local INSTALLED_VERSION="" + if dpkg -l | grep -q "temurin-.*-jdk" 2>/dev/null; then + INSTALLED_VERSION=$(dpkg -l 2>/dev/null | awk '/temurin-.*-jdk/{print $2}' | grep -oP 'temurin-\K[0-9]+' | head -n1 || echo "") + fi - # Validate INSTALLED_VERSION is not empty if matched - local JDK_COUNT=$(dpkg -l 2>/dev/null | grep -c "temurin-.*-jdk" || echo "0") - if [[ -z "$INSTALLED_VERSION" && "$JDK_COUNT" -gt 0 ]]; then - msg_warn "Found Temurin JDK but cannot determine version" - INSTALLED_VERSION="0" - fi - - # Scenario 1: Already at correct version - if [[ "$INSTALLED_VERSION" == "$JAVA_VERSION" ]]; then - msg_info "Update Temurin JDK $JAVA_VERSION" - $STD apt update || { - msg_error "APT update failed" - return 1 - } - $STD apt install --only-upgrade -y "$DESIRED_PACKAGE" || { - msg_error "Failed to update Temurin JDK" - return 1 - } - cache_installed_version "temurin-jdk" "$JAVA_VERSION" - msg_ok "Update Temurin JDK $JAVA_VERSION" - return 0 - fi - - # Scenario 2: Different version - remove old and install new - if [[ -n "$INSTALLED_VERSION" ]]; then - msg_info "Upgrade Temurin JDK from $INSTALLED_VERSION to $JAVA_VERSION" - $STD apt purge -y "temurin-${INSTALLED_VERSION}-jdk" || true - else - msg_info "Setup Temurin JDK $JAVA_VERSION" - fi + # Validate INSTALLED_VERSION is not empty if matched + local JDK_COUNT=$(dpkg -l 2>/dev/null | grep -c "temurin-.*-jdk" || echo "0") + if [[ -z "$INSTALLED_VERSION" && "$JDK_COUNT" -gt 0 ]]; then + msg_warn "Found Temurin JDK but cannot determine version" + INSTALLED_VERSION="0" + fi + # Scenario 1: Already at correct version + if [[ "$INSTALLED_VERSION" == "$JAVA_VERSION" ]]; then + msg_info "Update Temurin JDK $JAVA_VERSION" $STD apt update || { - msg_error "APT update failed" - return 1 + msg_error "APT update failed" + return 1 } - $STD apt install -y "$DESIRED_PACKAGE" || { - msg_error "Failed to install Temurin JDK $JAVA_VERSION" - return 1 + $STD apt install --only-upgrade -y "$DESIRED_PACKAGE" || { + msg_error "Failed to update Temurin JDK" + return 1 } - cache_installed_version "temurin-jdk" "$JAVA_VERSION" - msg_ok "Setup Temurin JDK $JAVA_VERSION" + msg_ok "Update Temurin JDK $JAVA_VERSION" + return 0 + fi + + # Scenario 2: Different version - remove old and install new + if [[ -n "$INSTALLED_VERSION" ]]; then + msg_info "Upgrade Temurin JDK from $INSTALLED_VERSION to $JAVA_VERSION" + $STD apt purge -y "temurin-${INSTALLED_VERSION}-jdk" || true + else + msg_info "Setup Temurin JDK $JAVA_VERSION" + fi + + $STD apt update || { + msg_error "APT update failed" + return 1 + } + $STD apt install -y "$DESIRED_PACKAGE" || { + msg_error "Failed to install Temurin JDK $JAVA_VERSION" + return 1 + } + + cache_installed_version "temurin-jdk" "$JAVA_VERSION" + msg_ok "Setup Temurin JDK $JAVA_VERSION" } # ------------------------------------------------------------------------------ @@ -2588,36 +2588,36 @@ function setup_java() { # ------------------------------------------------------------------------------ function setup_local_ip_helper() { - local BASE_DIR="/usr/local/community-scripts/ip-management" - local SCRIPT_PATH="$BASE_DIR/update_local_ip.sh" - local IP_FILE="/run/local-ip.env" - local DISPATCHER_SCRIPT="/etc/networkd-dispatcher/routable.d/10-update-local-ip.sh" + local BASE_DIR="/usr/local/community-scripts/ip-management" + local SCRIPT_PATH="$BASE_DIR/update_local_ip.sh" + local IP_FILE="/run/local-ip.env" + local DISPATCHER_SCRIPT="/etc/networkd-dispatcher/routable.d/10-update-local-ip.sh" - # Check if already set up - if [[ -f "$SCRIPT_PATH" && -f "$DISPATCHER_SCRIPT" ]]; then - msg_info "Update Local IP Helper" - cache_installed_version "local-ip-helper" "1.0" - msg_ok "Update Local IP Helper" - else - msg_info "Setup Local IP Helper" - fi + # Check if already set up + if [[ -f "$SCRIPT_PATH" && -f "$DISPATCHER_SCRIPT" ]]; then + msg_info "Update Local IP Helper" + cache_installed_version "local-ip-helper" "1.0" + msg_ok "Update Local IP Helper" + else + msg_info "Setup Local IP Helper" + fi - mkdir -p "$BASE_DIR" + mkdir -p "$BASE_DIR" - # Install networkd-dispatcher if not present - if ! dpkg -s networkd-dispatcher >/dev/null 2>&1; then - $STD apt update || { - msg_error "Failed to update package list" - return 1 - } - $STD apt install -y networkd-dispatcher || { - msg_error "Failed to install networkd-dispatcher" - return 1 - } - fi + # Install networkd-dispatcher if not present + if ! dpkg -s networkd-dispatcher >/dev/null 2>&1; then + $STD apt update || { + msg_error "Failed to update package list" + return 1 + } + $STD apt install -y networkd-dispatcher || { + msg_error "Failed to install networkd-dispatcher" + return 1 + } + fi - # Write update_local_ip.sh - cat <<'EOF' >"$SCRIPT_PATH" + # Write update_local_ip.sh + cat <<'EOF' >"$SCRIPT_PATH" #!/bin/bash set -euo pipefail @@ -2659,22 +2659,22 @@ echo "LOCAL_IP=$current_ip" > "$IP_FILE" echo "[INFO] LOCAL_IP updated to $current_ip" EOF - chmod +x "$SCRIPT_PATH" + chmod +x "$SCRIPT_PATH" - # Install dispatcher hook - mkdir -p "$(dirname "$DISPATCHER_SCRIPT")" - cat <"$DISPATCHER_SCRIPT" + # Install dispatcher hook + mkdir -p "$(dirname "$DISPATCHER_SCRIPT")" + cat <"$DISPATCHER_SCRIPT" #!/bin/bash $SCRIPT_PATH EOF - chmod +x "$DISPATCHER_SCRIPT" - systemctl enable -q --now networkd-dispatcher.service || { - msg_warn "Failed to enable networkd-dispatcher service" - } + chmod +x "$DISPATCHER_SCRIPT" + systemctl enable -q --now networkd-dispatcher.service || { + msg_warn "Failed to enable networkd-dispatcher service" + } - cache_installed_version "local-ip-helper" "1.0" - msg_ok "Setup Local IP Helper" + cache_installed_version "local-ip-helper" "1.0" + msg_ok "Setup Local IP Helper" } # ------------------------------------------------------------------------------ @@ -2690,122 +2690,122 @@ EOF # ------------------------------------------------------------------------------ setup_mariadb() { - local MARIADB_VERSION="${MARIADB_VERSION:-latest}" + local MARIADB_VERSION="${MARIADB_VERSION:-latest}" - # Resolve "latest" to actual version - if [[ "$MARIADB_VERSION" == "latest" ]]; then - if ! curl -fsI --max-time 10 http://mirror.mariadb.org/repo/ >/dev/null 2>&1; then - msg_warn "MariaDB mirror not reachable - trying cached package list fallback" - # Fallback: try to use a known stable version - MARIADB_VERSION="12.0" - else - MARIADB_VERSION=$(curl -fsSL --max-time 15 http://mirror.mariadb.org/repo/ 2>/dev/null | - grep -Eo '[0-9]+\.[0-9]+\.[0-9]+/' | - grep -vE 'rc/|rolling/' | - sed 's|/||' | - sort -Vr | - head -n1 || echo "") + # Resolve "latest" to actual version + if [[ "$MARIADB_VERSION" == "latest" ]]; then + if ! curl -fsI --max-time 10 http://mirror.mariadb.org/repo/ >/dev/null 2>&1; then + msg_warn "MariaDB mirror not reachable - trying cached package list fallback" + # Fallback: try to use a known stable version + MARIADB_VERSION="12.0" + else + MARIADB_VERSION=$(curl -fsSL --max-time 15 http://mirror.mariadb.org/repo/ 2>/dev/null | + grep -Eo '[0-9]+\.[0-9]+\.[0-9]+/' | + grep -vE 'rc/|rolling/' | + sed 's|/||' | + sort -Vr | + head -n1 || echo "") - if [[ -z "$MARIADB_VERSION" ]]; then - msg_warn "Could not parse latest GA MariaDB version from mirror - using fallback" - MARIADB_VERSION="12.0" - fi - fi + if [[ -z "$MARIADB_VERSION" ]]; then + msg_warn "Could not parse latest GA MariaDB version from mirror - using fallback" + MARIADB_VERSION="12.0" + fi fi + fi - # Get currently installed version - local CURRENT_VERSION="" - CURRENT_VERSION=$(is_tool_installed "mariadb" 2>/dev/null) || true + # Get currently installed version + local CURRENT_VERSION="" + CURRENT_VERSION=$(is_tool_installed "mariadb" 2>/dev/null) || true - # Scenario 1: Already installed at target version - just update packages - if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$MARIADB_VERSION" ]]; then - msg_info "Update MariaDB $MARIADB_VERSION" + # Scenario 1: Already installed at target version - just update packages + if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$MARIADB_VERSION" ]]; then + msg_info "Update MariaDB $MARIADB_VERSION" - # Ensure APT is working - ensure_apt_working || return 1 - - # Check if repository needs to be refreshed - if [[ -f /etc/apt/sources.list.d/mariadb.sources ]]; then - local REPO_VERSION="" - REPO_VERSION=$(grep -oP 'repo/\K[0-9]+\.[0-9]+' /etc/apt/sources.list.d/mariadb.sources 2>/dev/null || echo "") - if [[ -n "$REPO_VERSION" && "$REPO_VERSION" != "${MARIADB_VERSION%.*}" ]]; then - msg_warn "Repository version mismatch, updating..." - manage_tool_repository "mariadb" "$MARIADB_VERSION" "http://mirror.mariadb.org/repo/$MARIADB_VERSION" \ - "https://mariadb.org/mariadb_release_signing_key.asc" || { - msg_error "Failed to update MariaDB repository" - return 1 - } - fi - fi - - # Perform upgrade - $STD apt update || { - msg_error "Failed to update package list" - return 1 - } - $STD apt install --only-upgrade -y mariadb-server mariadb-client || { - msg_error "Failed to upgrade MariaDB packages" - return 1 - } - cache_installed_version "mariadb" "$MARIADB_VERSION" - msg_ok "Update MariaDB $MARIADB_VERSION" - return 0 - fi - - # Scenario 2: Different version installed - clean upgrade - if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" != "$MARIADB_VERSION" ]]; then - msg_info "Upgrade MariaDB from $CURRENT_VERSION to $MARIADB_VERSION" - remove_old_tool_version "mariadb" - fi - - # Scenario 3: Fresh install or version change - msg_info "Setup MariaDB $MARIADB_VERSION" - - # Ensure APT is working before proceeding + # Ensure APT is working ensure_apt_working || return 1 - # Install required dependencies first - local mariadb_deps=() - for dep in gawk rsync socat libdbi-perl pv; do - if apt-cache search "^${dep}$" 2>/dev/null | grep -q .; then - mariadb_deps+=("$dep") - fi - done - - if [[ ${#mariadb_deps[@]} -gt 0 ]]; then - $STD apt install -y "${mariadb_deps[@]}" 2>/dev/null || true + # Check if repository needs to be refreshed + if [[ -f /etc/apt/sources.list.d/mariadb.sources ]]; then + local REPO_VERSION="" + REPO_VERSION=$(grep -oP 'repo/\K[0-9]+\.[0-9]+' /etc/apt/sources.list.d/mariadb.sources 2>/dev/null || echo "") + if [[ -n "$REPO_VERSION" && "$REPO_VERSION" != "${MARIADB_VERSION%.*}" ]]; then + msg_warn "Repository version mismatch, updating..." + manage_tool_repository "mariadb" "$MARIADB_VERSION" "http://mirror.mariadb.org/repo/$MARIADB_VERSION" \ + "https://mariadb.org/mariadb_release_signing_key.asc" || { + msg_error "Failed to update MariaDB repository" + return 1 + } + fi fi - # Setup repository - manage_tool_repository "mariadb" "$MARIADB_VERSION" "http://mirror.mariadb.org/repo/$MARIADB_VERSION" \ - "https://mariadb.org/mariadb_release_signing_key.asc" || { - msg_error "Failed to setup MariaDB repository" - return 1 + # Perform upgrade + $STD apt update || { + msg_error "Failed to update package list" + return 1 } - - # Set debconf selections for all potential versions - local MARIADB_MAJOR_MINOR - MARIADB_MAJOR_MINOR=$(echo "$MARIADB_VERSION" | awk -F. '{print $1"."$2}') - if [[ -n "$MARIADB_MAJOR_MINOR" ]]; then - echo "mariadb-server-$MARIADB_MAJOR_MINOR mariadb-server/feedback boolean false" | debconf-set-selections - fi - - # Install packages - DEBIAN_FRONTEND=noninteractive $STD apt install -y mariadb-server mariadb-client || { - # Fallback: try without specific version - msg_warn "Failed to install MariaDB packages from upstream repo, trying distro fallback..." - cleanup_old_repo_files "mariadb" - $STD apt update || { - msg_warn "APT update also failed, continuing with cache" - } - DEBIAN_FRONTEND=noninteractive $STD apt install -y mariadb-server mariadb-client || { - msg_error "Failed to install MariaDB packages (both upstream and distro)" - return 1 - } + $STD apt install --only-upgrade -y mariadb-server mariadb-client || { + msg_error "Failed to upgrade MariaDB packages" + return 1 } - cache_installed_version "mariadb" "$MARIADB_VERSION" - msg_ok "Setup MariaDB $MARIADB_VERSION" + msg_ok "Update MariaDB $MARIADB_VERSION" + return 0 + fi + + # Scenario 2: Different version installed - clean upgrade + if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" != "$MARIADB_VERSION" ]]; then + msg_info "Upgrade MariaDB from $CURRENT_VERSION to $MARIADB_VERSION" + remove_old_tool_version "mariadb" + fi + + # Scenario 3: Fresh install or version change + msg_info "Setup MariaDB $MARIADB_VERSION" + + # Ensure APT is working before proceeding + ensure_apt_working || return 1 + + # Install required dependencies first + local mariadb_deps=() + for dep in gawk rsync socat libdbi-perl pv; do + if apt-cache search "^${dep}$" 2>/dev/null | grep -q .; then + mariadb_deps+=("$dep") + fi + done + + if [[ ${#mariadb_deps[@]} -gt 0 ]]; then + $STD apt install -y "${mariadb_deps[@]}" 2>/dev/null || true + fi + + # Setup repository + manage_tool_repository "mariadb" "$MARIADB_VERSION" "http://mirror.mariadb.org/repo/$MARIADB_VERSION" \ + "https://mariadb.org/mariadb_release_signing_key.asc" || { + msg_error "Failed to setup MariaDB repository" + return 1 + } + + # Set debconf selections for all potential versions + local MARIADB_MAJOR_MINOR + MARIADB_MAJOR_MINOR=$(echo "$MARIADB_VERSION" | awk -F. '{print $1"."$2}') + if [[ -n "$MARIADB_MAJOR_MINOR" ]]; then + echo "mariadb-server-$MARIADB_MAJOR_MINOR mariadb-server/feedback boolean false" | debconf-set-selections + fi + + # Install packages + DEBIAN_FRONTEND=noninteractive $STD apt install -y mariadb-server mariadb-client || { + # Fallback: try without specific version + msg_warn "Failed to install MariaDB packages from upstream repo, trying distro fallback..." + cleanup_old_repo_files "mariadb" + $STD apt update || { + msg_warn "APT update also failed, continuing with cache" + } + DEBIAN_FRONTEND=noninteractive $STD apt install -y mariadb-server mariadb-client || { + msg_error "Failed to install MariaDB packages (both upstream and distro)" + return 1 + } + } + + cache_installed_version "mariadb" "$MARIADB_VERSION" + msg_ok "Setup MariaDB $MARIADB_VERSION" } # ------------------------------------------------------------------------------ @@ -2820,92 +2820,92 @@ setup_mariadb() { # ------------------------------------------------------------------------------ function setup_mongodb() { - local MONGO_VERSION="${MONGO_VERSION:-8.0}" - local DISTRO_ID DISTRO_CODENAME - DISTRO_ID=$(get_os_info id) - DISTRO_CODENAME=$(get_os_info codename) + local MONGO_VERSION="${MONGO_VERSION:-8.0}" + local DISTRO_ID DISTRO_CODENAME + DISTRO_ID=$(get_os_info id) + DISTRO_CODENAME=$(get_os_info codename) - # Check AVX support - if ! grep -qm1 'avx[^ ]*' /proc/cpuinfo; then - local major="${MONGO_VERSION%%.*}" - if ((major > 5)); then - msg_error "MongoDB ${MONGO_VERSION} requires AVX support, which is not available on this system." - return 1 - fi + # Check AVX support + if ! grep -qm1 'avx[^ ]*' /proc/cpuinfo; then + local major="${MONGO_VERSION%%.*}" + if ((major > 5)); then + msg_error "MongoDB ${MONGO_VERSION} requires AVX support, which is not available on this system." + return 1 fi + fi - case "$DISTRO_ID" in - ubuntu) - MONGO_BASE_URL="https://repo.mongodb.org/apt/ubuntu" - ;; - debian) - MONGO_BASE_URL="https://repo.mongodb.org/apt/debian" - ;; - *) - msg_error "Unsupported distribution: $DISTRO_ID" - return 1 - ;; - esac + case "$DISTRO_ID" in + ubuntu) + MONGO_BASE_URL="https://repo.mongodb.org/apt/ubuntu" + ;; + debian) + MONGO_BASE_URL="https://repo.mongodb.org/apt/debian" + ;; + *) + msg_error "Unsupported distribution: $DISTRO_ID" + return 1 + ;; + esac - # Get currently installed version - local INSTALLED_VERSION="" - INSTALLED_VERSION=$(is_tool_installed "mongodb" 2>/dev/null) || true + # Get currently installed version + local INSTALLED_VERSION="" + INSTALLED_VERSION=$(is_tool_installed "mongodb" 2>/dev/null) || true - # Scenario 1: Already at target version - just update packages - if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" == "$MONGO_VERSION" ]]; then - msg_info "Update MongoDB $MONGO_VERSION" + # Scenario 1: Already at target version - just update packages + if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" == "$MONGO_VERSION" ]]; then + msg_info "Update MongoDB $MONGO_VERSION" - ensure_apt_working || return 1 + ensure_apt_working || return 1 - # Perform upgrade - $STD apt install --only-upgrade -y mongodb-org || { - msg_error "Failed to upgrade MongoDB" - return 1 - } - cache_installed_version "mongodb" "$MONGO_VERSION" - msg_ok "Update MongoDB $MONGO_VERSION" - return 0 - fi - - # Scenario 2: Different version installed - clean upgrade - if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" != "$MONGO_VERSION" ]]; then - msg_info "Upgrade MongoDB from $INSTALLED_VERSION to $MONGO_VERSION" - remove_old_tool_version "mongodb" - else - msg_info "Setup MongoDB $MONGO_VERSION" - fi - - cleanup_orphaned_sources - - # Setup repository - manage_tool_repository "mongodb" "$MONGO_VERSION" "$MONGO_BASE_URL" \ - "https://www.mongodb.org/static/pgp/server-${MONGO_VERSION}.asc" || { - msg_error "Failed to setup MongoDB repository" - return 1 + # Perform upgrade + $STD apt install --only-upgrade -y mongodb-org || { + msg_error "Failed to upgrade MongoDB" + return 1 } - - # Wait for repo to settle - $STD apt update || { - msg_error "APT update failed — invalid MongoDB repo for ${DISTRO_ID}-${DISTRO_CODENAME}?" - return 1 - } - - # Install MongoDB - $STD apt install -y mongodb-org || { - msg_error "Failed to install MongoDB packages" - return 1 - } - - mkdir -p /var/lib/mongodb - chown -R mongodb:mongodb /var/lib/mongodb - - $STD systemctl enable mongod || { - msg_warn "Failed to enable mongod service" - } - safe_service_restart mongod cache_installed_version "mongodb" "$MONGO_VERSION" + msg_ok "Update MongoDB $MONGO_VERSION" + return 0 + fi - msg_ok "Setup MongoDB $MONGO_VERSION" + # Scenario 2: Different version installed - clean upgrade + if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" != "$MONGO_VERSION" ]]; then + msg_info "Upgrade MongoDB from $INSTALLED_VERSION to $MONGO_VERSION" + remove_old_tool_version "mongodb" + else + msg_info "Setup MongoDB $MONGO_VERSION" + fi + + cleanup_orphaned_sources + + # Setup repository + manage_tool_repository "mongodb" "$MONGO_VERSION" "$MONGO_BASE_URL" \ + "https://www.mongodb.org/static/pgp/server-${MONGO_VERSION}.asc" || { + msg_error "Failed to setup MongoDB repository" + return 1 + } + + # Wait for repo to settle + $STD apt update || { + msg_error "APT update failed — invalid MongoDB repo for ${DISTRO_ID}-${DISTRO_CODENAME}?" + return 1 + } + + # Install MongoDB + $STD apt install -y mongodb-org || { + msg_error "Failed to install MongoDB packages" + return 1 + } + + mkdir -p /var/lib/mongodb + chown -R mongodb:mongodb /var/lib/mongodb + + $STD systemctl enable mongod || { + msg_warn "Failed to enable mongod service" + } + safe_service_restart mongod + cache_installed_version "mongodb" "$MONGO_VERSION" + + msg_ok "Setup MongoDB $MONGO_VERSION" } # ------------------------------------------------------------------------------ @@ -2922,48 +2922,48 @@ function setup_mongodb() { # ------------------------------------------------------------------------------ function setup_mysql() { - local MYSQL_VERSION="${MYSQL_VERSION:-8.0}" - local DISTRO_ID DISTRO_CODENAME - DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') - DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + local MYSQL_VERSION="${MYSQL_VERSION:-8.0}" + local DISTRO_ID DISTRO_CODENAME + DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - # Get currently installed version - local CURRENT_VERSION="" - CURRENT_VERSION=$(is_tool_installed "mysql" 2>/dev/null) || true + # Get currently installed version + local CURRENT_VERSION="" + CURRENT_VERSION=$(is_tool_installed "mysql" 2>/dev/null) || true - # Scenario 1: Already at target version - just update packages - if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$MYSQL_VERSION" ]]; then - msg_info "Update MySQL $MYSQL_VERSION" + # Scenario 1: Already at target version - just update packages + if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$MYSQL_VERSION" ]]; then + msg_info "Update MySQL $MYSQL_VERSION" - ensure_apt_working || return 1 + ensure_apt_working || return 1 - $STD apt install --only-upgrade -y mysql-server mysql-client || true + $STD apt install --only-upgrade -y mysql-server mysql-client || true - cache_installed_version "mysql" "$MYSQL_VERSION" - msg_ok "Update MySQL $MYSQL_VERSION" - return 0 + cache_installed_version "mysql" "$MYSQL_VERSION" + msg_ok "Update MySQL $MYSQL_VERSION" + return 0 + fi + + # Scenario 2: Different version installed - clean upgrade + if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" != "$MYSQL_VERSION" ]]; then + msg_info "Upgrade MySQL from $CURRENT_VERSION to $MYSQL_VERSION" + remove_old_tool_version "mysql" + else + msg_info "Setup MySQL $MYSQL_VERSION" + fi + + # Debian 13+ Fix: MySQL 8.0 incompatible with libaio1t64, use 8.4 LTS + if [[ "$DISTRO_ID" == "debian" && "$DISTRO_CODENAME" =~ ^(trixie|forky|sid)$ ]]; then + msg_info "Debian ${DISTRO_CODENAME} detected → using MySQL 8.4 LTS (libaio1t64 compatible)" + + cleanup_old_repo_files "mysql" + + if ! curl -fsSL https://repo.mysql.com/RPM-GPG-KEY-mysql-2023 | gpg --dearmor -o /etc/apt/keyrings/mysql.gpg 2>/dev/null; then + msg_error "Failed to import MySQL GPG key" + return 1 fi - # Scenario 2: Different version installed - clean upgrade - if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" != "$MYSQL_VERSION" ]]; then - msg_info "Upgrade MySQL from $CURRENT_VERSION to $MYSQL_VERSION" - remove_old_tool_version "mysql" - else - msg_info "Setup MySQL $MYSQL_VERSION" - fi - - # Debian 13+ Fix: MySQL 8.0 incompatible with libaio1t64, use 8.4 LTS - if [[ "$DISTRO_ID" == "debian" && "$DISTRO_CODENAME" =~ ^(trixie|forky|sid)$ ]]; then - msg_info "Debian ${DISTRO_CODENAME} detected → using MySQL 8.4 LTS (libaio1t64 compatible)" - - cleanup_old_repo_files "mysql" - - if ! curl -fsSL https://repo.mysql.com/RPM-GPG-KEY-mysql-2023 | gpg --dearmor -o /etc/apt/keyrings/mysql.gpg 2>/dev/null; then - msg_error "Failed to import MySQL GPG key" - return 1 - fi - - cat >/etc/apt/sources.list.d/mysql.sources <<'EOF' + cat >/etc/apt/sources.list.d/mysql.sources <<'EOF' Types: deb URIs: https://repo.mysql.com/apt/debian/ Suites: bookworm @@ -2972,79 +2972,79 @@ Architectures: amd64 arm64 Signed-By: /etc/apt/keyrings/mysql.gpg EOF - $STD apt update || { - msg_error "Failed to update APT for MySQL 8.4 LTS" - return 1 - } - - if ! $STD apt install -y mysql-community-server mysql-community-client; then - msg_warn "MySQL 8.4 LTS installation failed – falling back to MariaDB" - cleanup_old_repo_files "mysql" - $STD apt update - $STD apt install -y mariadb-server mariadb-client || { - msg_error "Failed to install database engine (MySQL/MariaDB fallback)" - return 1 - } - msg_ok "Setup Database Engine (MariaDB fallback on Debian ${DISTRO_CODENAME})" - return 0 - fi - - cache_installed_version "mysql" "8.4" - msg_ok "Setup MySQL 8.4 LTS (Debian ${DISTRO_CODENAME})" - return 0 - fi - - # Standard setup for other distributions - local SUITE - if [[ "$DISTRO_ID" == "debian" ]]; then - case "$DISTRO_CODENAME" in - bookworm | bullseye) SUITE="$DISTRO_CODENAME" ;; - *) SUITE="bookworm" ;; - esac - else - SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://repo.mysql.com/apt/${DISTRO_ID}") - fi - - # Setup repository - manage_tool_repository "mysql" "$MYSQL_VERSION" "https://repo.mysql.com/apt/${DISTRO_ID}" \ - "https://repo.mysql.com/RPM-GPG-KEY-mysql-2023" || { - msg_error "Failed to setup MySQL repository" - return 1 + $STD apt update || { + msg_error "Failed to update APT for MySQL 8.4 LTS" + return 1 } - ensure_apt_working || return 1 - - # Try multiple package names (mysql-server, mysql-community-server, mysql) - export DEBIAN_FRONTEND=noninteractive - local mysql_install_success=false - - if apt-cache search "^mysql-server$" 2>/dev/null | grep -q . && - $STD apt install -y mysql-server mysql-client 2>/dev/null; then - mysql_install_success=true - elif apt-cache search "^mysql-community-server$" 2>/dev/null | grep -q . && - $STD apt install -y mysql-community-server mysql-community-client 2>/dev/null; then - mysql_install_success=true - elif apt-cache search "^mysql$" 2>/dev/null | grep -q . && - $STD apt install -y mysql 2>/dev/null; then - mysql_install_success=true - fi - - if [[ "$mysql_install_success" == false ]]; then - msg_error "MySQL ${MYSQL_VERSION} package not available for suite ${SUITE}" + if ! $STD apt install -y mysql-community-server mysql-community-client; then + msg_warn "MySQL 8.4 LTS installation failed – falling back to MariaDB" + cleanup_old_repo_files "mysql" + $STD apt update + $STD apt install -y mariadb-server mariadb-client || { + msg_error "Failed to install database engine (MySQL/MariaDB fallback)" return 1 + } + msg_ok "Setup Database Engine (MariaDB fallback on Debian ${DISTRO_CODENAME})" + return 0 fi - # Verify mysql command is accessible + cache_installed_version "mysql" "8.4" + msg_ok "Setup MySQL 8.4 LTS (Debian ${DISTRO_CODENAME})" + return 0 + fi + + # Standard setup for other distributions + local SUITE + if [[ "$DISTRO_ID" == "debian" ]]; then + case "$DISTRO_CODENAME" in + bookworm | bullseye) SUITE="$DISTRO_CODENAME" ;; + *) SUITE="bookworm" ;; + esac + else + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://repo.mysql.com/apt/${DISTRO_ID}") + fi + + # Setup repository + manage_tool_repository "mysql" "$MYSQL_VERSION" "https://repo.mysql.com/apt/${DISTRO_ID}" \ + "https://repo.mysql.com/RPM-GPG-KEY-mysql-2023" || { + msg_error "Failed to setup MySQL repository" + return 1 + } + + ensure_apt_working || return 1 + + # Try multiple package names (mysql-server, mysql-community-server, mysql) + export DEBIAN_FRONTEND=noninteractive + local mysql_install_success=false + + if apt-cache search "^mysql-server$" 2>/dev/null | grep -q . && + $STD apt install -y mysql-server mysql-client 2>/dev/null; then + mysql_install_success=true + elif apt-cache search "^mysql-community-server$" 2>/dev/null | grep -q . && + $STD apt install -y mysql-community-server mysql-community-client 2>/dev/null; then + mysql_install_success=true + elif apt-cache search "^mysql$" 2>/dev/null | grep -q . && + $STD apt install -y mysql 2>/dev/null; then + mysql_install_success=true + fi + + if [[ "$mysql_install_success" == false ]]; then + msg_error "MySQL ${MYSQL_VERSION} package not available for suite ${SUITE}" + return 1 + fi + + # Verify mysql command is accessible + if ! command -v mysql >/dev/null 2>&1; then + hash -r if ! command -v mysql >/dev/null 2>&1; then - hash -r - if ! command -v mysql >/dev/null 2>&1; then - msg_error "MySQL installed but mysql command still not found" - return 1 - fi + msg_error "MySQL installed but mysql command still not found" + return 1 fi + fi - cache_installed_version "mysql" "$MYSQL_VERSION" - msg_ok "Setup MySQL $MYSQL_VERSION" + cache_installed_version "mysql" "$MYSQL_VERSION" + msg_ok "Setup MySQL $MYSQL_VERSION" } # ------------------------------------------------------------------------------ @@ -3060,142 +3060,171 @@ EOF # ------------------------------------------------------------------------------ function setup_nodejs() { - local NODE_VERSION="${NODE_VERSION:-22}" - local NODE_MODULE="${NODE_MODULE:-}" + local NODE_VERSION="${NODE_VERSION:-22}" + local NODE_MODULE="${NODE_MODULE:-}" - # Get currently installed version - local CURRENT_NODE_VERSION="" - CURRENT_NODE_VERSION=$(is_tool_installed "nodejs" 2>/dev/null) || true + # Get currently installed version + local CURRENT_NODE_VERSION="" + CURRENT_NODE_VERSION=$(is_tool_installed "nodejs" 2>/dev/null) || true - # Ensure jq is available for JSON parsing - if ! command -v jq &>/dev/null; then - $STD apt update - $STD apt install -y jq || { - msg_error "Failed to install jq" - return 1 - } - fi + # Ensure jq is available for JSON parsing + if ! command -v jq &>/dev/null; then + $STD apt update + $STD apt install -y jq || { + msg_error "Failed to install jq" + return 1 + } + fi - # Scenario 1: Already installed at target version - just update packages/modules - if [[ -n "$CURRENT_NODE_VERSION" && "$CURRENT_NODE_VERSION" == "$NODE_VERSION" ]]; then - msg_info "Update Node.js $NODE_VERSION" + # Scenario 1: Already installed at target version - just update packages/modules + if [[ -n "$CURRENT_NODE_VERSION" && "$CURRENT_NODE_VERSION" == "$NODE_VERSION" ]]; then + msg_info "Update Node.js $NODE_VERSION" - ensure_apt_working || return 1 + ensure_apt_working || return 1 - # Just update npm to latest - $STD npm install -g npm@latest 2>/dev/null || true + # Just update npm to latest + $STD npm install -g npm@latest 2>/dev/null || true - cache_installed_version "nodejs" "$NODE_VERSION" - msg_ok "Update Node.js $NODE_VERSION" + cache_installed_version "nodejs" "$NODE_VERSION" + msg_ok "Update Node.js $NODE_VERSION" + else + # Scenario 2: Different version installed - clean upgrade + if [[ -n "$CURRENT_NODE_VERSION" && "$CURRENT_NODE_VERSION" != "$NODE_VERSION" ]]; then + msg_info "Upgrade Node.js from $CURRENT_NODE_VERSION to $NODE_VERSION" + remove_old_tool_version "nodejs" else - # Scenario 2: Different version installed - clean upgrade - if [[ -n "$CURRENT_NODE_VERSION" && "$CURRENT_NODE_VERSION" != "$NODE_VERSION" ]]; then - msg_info "Upgrade Node.js from $CURRENT_NODE_VERSION to $NODE_VERSION" - remove_old_tool_version "nodejs" - else - msg_info "Setup Node.js $NODE_VERSION" - fi - - ensure_dependencies curl ca-certificates gnupg - - # Setup repository - manage_tool_repository "nodejs" "$NODE_VERSION" "https://deb.nodesource.com/node_${NODE_VERSION}.x" "https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key" || { - msg_error "Failed to setup Node.js repository" - return 1 - } - - # Wait for repo to settle - sleep 2 - - # Install Node.js - if ! $STD apt update; then - msg_warn "APT update failed – retrying in 5s" - sleep 5 - if ! $STD apt update; then - msg_error "Failed to update APT repositories after adding NodeSource" - return 1 - fi - fi - - if ! $STD apt install -y nodejs; then - msg_error "Failed to install Node.js ${NODE_VERSION} from NodeSource" - return 1 - fi - - # Update to latest npm - $STD npm install -g npm@latest || { - msg_error "Failed to update npm to latest version" - return 1 - } - - cache_installed_version "nodejs" "$NODE_VERSION" - msg_ok "Setup Node.js $NODE_VERSION" + msg_info "Setup Node.js $NODE_VERSION" fi - export NODE_OPTIONS="--max-old-space-size=4096" - - # Ensure valid working directory for npm (avoids uv_cwd error) - if [[ ! -d /opt ]]; then - mkdir -p /opt + # Clean up any legacy nvm installations + if [[ -d "$HOME/.nvm" ]]; then + msg_info "Removing legacy nvm installation" + rm -rf "$HOME/.nvm" "$HOME/.npm" "$HOME/.bower" "$HOME/.config/yarn" 2>/dev/null || true + sed -i '/NVM_DIR/d' "$HOME/.bashrc" "$HOME/.profile" 2>/dev/null || true fi - cd /opt || { - msg_error "Failed to set safe working directory before npm install" - return 1 + + ensure_dependencies curl ca-certificates gnupg + + # Clean up ALL old NodeSource repository configurations to avoid conflicts + rm -f /etc/apt/sources.list.d/nodesource.list \ + /etc/apt/sources.list.d/nodesource.sources \ + /usr/share/keyrings/nodesource.gpg \ + /etc/apt/keyrings/nodesource.gpg 2>/dev/null || true + + # Setup repository + manage_tool_repository "nodejs" "$NODE_VERSION" "https://deb.nodesource.com/node_${NODE_VERSION}.x" "https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key" || { + msg_error "Failed to setup Node.js repository" + return 1 } - # Install global Node modules - if [[ -n "$NODE_MODULE" ]]; then - IFS=',' read -ra MODULES <<<"$NODE_MODULE" - local failed_modules=0 - for mod in "${MODULES[@]}"; do - local MODULE_NAME MODULE_REQ_VERSION MODULE_INSTALLED_VERSION - if [[ "$mod" == @*/*@* ]]; then - # Scoped package with version, e.g. @vue/cli-service@latest - MODULE_NAME="${mod%@*}" - MODULE_REQ_VERSION="${mod##*@}" - elif [[ "$mod" == *"@"* ]]; then - # Unscoped package with version, e.g. yarn@latest - MODULE_NAME="${mod%@*}" - MODULE_REQ_VERSION="${mod##*@}" - else - # No version specified - MODULE_NAME="$mod" - MODULE_REQ_VERSION="latest" - fi + # Wait for repo to settle + sleep 2 - # Check if the module is already installed - if npm list -g --depth=0 "$MODULE_NAME" >/dev/null 2>&1; then - MODULE_INSTALLED_VERSION="$(npm list -g --depth=0 "$MODULE_NAME" | grep "$MODULE_NAME@" | awk -F@ '{print $2}' | tr -d '[:space:]')" - if [[ "$MODULE_REQ_VERSION" != "latest" && "$MODULE_REQ_VERSION" != "$MODULE_INSTALLED_VERSION" ]]; then - msg_info "Updating $MODULE_NAME from v$MODULE_INSTALLED_VERSION to v$MODULE_REQ_VERSION" - if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}" 2>/dev/null; then - msg_warn "Failed to update $MODULE_NAME to version $MODULE_REQ_VERSION" - ((failed_modules++)) - continue - fi - elif [[ "$MODULE_REQ_VERSION" == "latest" ]]; then - msg_info "Updating $MODULE_NAME to latest version" - if ! $STD npm install -g "${MODULE_NAME}@latest" 2>/dev/null; then - msg_warn "Failed to update $MODULE_NAME to latest version" - ((failed_modules++)) - continue - fi - fi - else - msg_info "Installing $MODULE_NAME@$MODULE_REQ_VERSION" - if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}" 2>/dev/null; then - msg_warn "Failed to install $MODULE_NAME@$MODULE_REQ_VERSION" - ((failed_modules++)) - continue - fi - fi - done - if [[ $failed_modules -eq 0 ]]; then - msg_ok "Installed Node.js modules: $NODE_MODULE" - else - msg_warn "Installed Node.js modules with $failed_modules failure(s): $NODE_MODULE" - fi + # Install Node.js + if ! $STD apt update; then + msg_warn "APT update failed – retrying in 5s" + sleep 5 + if ! $STD apt update; then + msg_error "Failed to update APT repositories after adding NodeSource" + return 1 + fi fi + + if ! $STD apt install -y nodejs; then + msg_error "Failed to install Node.js ${NODE_VERSION} from NodeSource" + return 1 + fi + + # Verify Node.js was installed correctly + if ! command -v node >/dev/null 2>&1; then + msg_error "Node.js binary not found after installation" + return 1 + fi + + local INSTALLED_NODE_VERSION + INSTALLED_NODE_VERSION=$(node -v 2>/dev/null | grep -oP '^v\K[0-9]+' || echo "0") + if [[ "$INSTALLED_NODE_VERSION" != "$NODE_VERSION" ]]; then + msg_error "Node.js version mismatch: expected $NODE_VERSION, got $INSTALLED_NODE_VERSION" + return 1 + fi + + # Update to latest npm (with version check to avoid incompatibility) + local NPM_VERSION + NPM_VERSION=$(npm -v 2>/dev/null || echo "0") + if [[ "$NPM_VERSION" != "0" ]]; then + $STD npm install -g npm@latest 2>/dev/null || { + msg_warn "Failed to update npm to latest version (continuing with bundled npm $NPM_VERSION)" + } + fi + + cache_installed_version "nodejs" "$NODE_VERSION" + msg_ok "Setup Node.js $NODE_VERSION" + fi + + export NODE_OPTIONS="--max-old-space-size=4096" + + # Ensure valid working directory for npm (avoids uv_cwd error) + if [[ ! -d /opt ]]; then + mkdir -p /opt + fi + cd /opt || { + msg_error "Failed to set safe working directory before npm install" + return 1 + } + + # Install global Node modules + if [[ -n "$NODE_MODULE" ]]; then + IFS=',' read -ra MODULES <<<"$NODE_MODULE" + local failed_modules=0 + for mod in "${MODULES[@]}"; do + local MODULE_NAME MODULE_REQ_VERSION MODULE_INSTALLED_VERSION + if [[ "$mod" == @*/*@* ]]; then + # Scoped package with version, e.g. @vue/cli-service@latest + MODULE_NAME="${mod%@*}" + MODULE_REQ_VERSION="${mod##*@}" + elif [[ "$mod" == *"@"* ]]; then + # Unscoped package with version, e.g. yarn@latest + MODULE_NAME="${mod%@*}" + MODULE_REQ_VERSION="${mod##*@}" + else + # No version specified + MODULE_NAME="$mod" + MODULE_REQ_VERSION="latest" + fi + + # Check if the module is already installed + if npm list -g --depth=0 "$MODULE_NAME" >/dev/null 2>&1; then + MODULE_INSTALLED_VERSION="$(npm list -g --depth=0 "$MODULE_NAME" | grep "$MODULE_NAME@" | awk -F@ '{print $2}' | tr -d '[:space:]')" + if [[ "$MODULE_REQ_VERSION" != "latest" && "$MODULE_REQ_VERSION" != "$MODULE_INSTALLED_VERSION" ]]; then + msg_info "Updating $MODULE_NAME from v$MODULE_INSTALLED_VERSION to v$MODULE_REQ_VERSION" + if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}" 2>/dev/null; then + msg_warn "Failed to update $MODULE_NAME to version $MODULE_REQ_VERSION" + ((failed_modules++)) + continue + fi + elif [[ "$MODULE_REQ_VERSION" == "latest" ]]; then + msg_info "Updating $MODULE_NAME to latest version" + if ! $STD npm install -g "${MODULE_NAME}@latest" 2>/dev/null; then + msg_warn "Failed to update $MODULE_NAME to latest version" + ((failed_modules++)) + continue + fi + fi + else + msg_info "Installing $MODULE_NAME@$MODULE_REQ_VERSION" + if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}" 2>/dev/null; then + msg_warn "Failed to install $MODULE_NAME@$MODULE_REQ_VERSION" + ((failed_modules++)) + continue + fi + fi + done + if [[ $failed_modules -eq 0 ]]; then + msg_ok "Installed Node.js modules: $NODE_MODULE" + else + msg_warn "Installed Node.js modules with $failed_modules failure(s): $NODE_MODULE" + fi + fi } # ------------------------------------------------------------------------------ @@ -3218,139 +3247,139 @@ function setup_nodejs() { # ------------------------------------------------------------------------------ function setup_php() { - local PHP_VERSION="${PHP_VERSION:-8.4}" - local PHP_MODULE="${PHP_MODULE:-}" - local PHP_APACHE="${PHP_APACHE:-NO}" - local PHP_FPM="${PHP_FPM:-NO}" - local DISTRO_ID DISTRO_CODENAME - DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') - DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + local PHP_VERSION="${PHP_VERSION:-8.4}" + local PHP_MODULE="${PHP_MODULE:-}" + local PHP_APACHE="${PHP_APACHE:-NO}" + local PHP_FPM="${PHP_FPM:-NO}" + local DISTRO_ID DISTRO_CODENAME + DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - local DEFAULT_MODULES="bcmath,cli,curl,gd,intl,mbstring,opcache,readline,xml,zip" - local COMBINED_MODULES + local DEFAULT_MODULES="bcmath,cli,curl,gd,intl,mbstring,opcache,readline,xml,zip" + local COMBINED_MODULES - local PHP_MEMORY_LIMIT="${PHP_MEMORY_LIMIT:-512M}" - local PHP_UPLOAD_MAX_FILESIZE="${PHP_UPLOAD_MAX_FILESIZE:-128M}" - local PHP_POST_MAX_SIZE="${PHP_POST_MAX_SIZE:-128M}" - local PHP_MAX_EXECUTION_TIME="${PHP_MAX_EXECUTION_TIME:-300}" + local PHP_MEMORY_LIMIT="${PHP_MEMORY_LIMIT:-512M}" + local PHP_UPLOAD_MAX_FILESIZE="${PHP_UPLOAD_MAX_FILESIZE:-128M}" + local PHP_POST_MAX_SIZE="${PHP_POST_MAX_SIZE:-128M}" + local PHP_MAX_EXECUTION_TIME="${PHP_MAX_EXECUTION_TIME:-300}" - # Merge default + user-defined modules - if [[ -n "$PHP_MODULE" ]]; then - COMBINED_MODULES="${DEFAULT_MODULES},${PHP_MODULE}" - else - COMBINED_MODULES="${DEFAULT_MODULES}" - fi + # Merge default + user-defined modules + if [[ -n "$PHP_MODULE" ]]; then + COMBINED_MODULES="${DEFAULT_MODULES},${PHP_MODULE}" + else + COMBINED_MODULES="${DEFAULT_MODULES}" + fi - # Deduplicate - COMBINED_MODULES=$(echo "$COMBINED_MODULES" | tr ',' '\n' | awk '!seen[$0]++' | paste -sd, -) + # Deduplicate + COMBINED_MODULES=$(echo "$COMBINED_MODULES" | tr ',' '\n' | awk '!seen[$0]++' | paste -sd, -) - # Get current PHP-CLI version - local CURRENT_PHP="" - CURRENT_PHP=$(is_tool_installed "php" 2>/dev/null) || true + # Get current PHP-CLI version + local CURRENT_PHP="" + CURRENT_PHP=$(is_tool_installed "php" 2>/dev/null) || true - # Scenario 1: Already at target version - just update packages - if [[ -n "$CURRENT_PHP" && "$CURRENT_PHP" == "$PHP_VERSION" ]]; then - msg_info "Update PHP $PHP_VERSION" + # Scenario 1: Already at target version - just update packages + if [[ -n "$CURRENT_PHP" && "$CURRENT_PHP" == "$PHP_VERSION" ]]; then + msg_info "Update PHP $PHP_VERSION" - # Ensure Sury repo is available - if [[ ! -f /etc/apt/sources.list.d/php.sources ]]; then - manage_tool_repository "php" "$PHP_VERSION" "" "https://packages.sury.org/debsuryorg-archive-keyring.deb" || { - msg_error "Failed to setup PHP repository" - return 1 - } - fi - - ensure_apt_working || return 1 - - # Just update PHP packages - $STD apt install --only-upgrade -y "php${PHP_VERSION}" || true - - cache_installed_version "php" "$PHP_VERSION" - msg_ok "Update PHP $PHP_VERSION" - else - # Scenario 2: Different version installed - clean upgrade - if [[ -n "$CURRENT_PHP" && "$CURRENT_PHP" != "$PHP_VERSION" ]]; then - msg_info "Upgrade PHP from $CURRENT_PHP to $PHP_VERSION" - # Stop old PHP-FPM if running - $STD systemctl stop "php${CURRENT_PHP}-fpm" >/dev/null 2>&1 || true - $STD systemctl disable "php${CURRENT_PHP}-fpm" >/dev/null 2>&1 || true - remove_old_tool_version "php" - else - msg_info "Setup PHP $PHP_VERSION" - fi - - # Setup Sury repository - manage_tool_repository "php" "$PHP_VERSION" "" "https://packages.sury.org/debsuryorg-archive-keyring.deb" || { - msg_error "Failed to setup PHP repository" - return 1 - } - - ensure_apt_working || return 1 - fi - - # Build module list - local MODULE_LIST="php${PHP_VERSION}" - IFS=',' read -ra MODULES <<<"$COMBINED_MODULES" - for mod in "${MODULES[@]}"; do - if apt-cache show "php${PHP_VERSION}-${mod}" >/dev/null 2>&1; then - MODULE_LIST+=" php${PHP_VERSION}-${mod}" - fi - done - if [[ "$PHP_FPM" == "YES" ]]; then - MODULE_LIST+=" php${PHP_VERSION}-fpm" - fi - - # install apache2 with PHP support if requested - if [[ "$PHP_APACHE" == "YES" ]]; then - if ! dpkg -l 2>/dev/null | grep -q "libapache2-mod-php${PHP_VERSION}"; then - $STD apt install -y apache2 libapache2-mod-php${PHP_VERSION} || { - msg_error "Failed to install Apache with PHP module" - return 1 - } - fi - fi - - # Install PHP packages - $STD apt install -y $MODULE_LIST || { - msg_error "Failed to install PHP packages" + # Ensure Sury repo is available + if [[ ! -f /etc/apt/sources.list.d/php.sources ]]; then + manage_tool_repository "php" "$PHP_VERSION" "" "https://packages.sury.org/debsuryorg-archive-keyring.deb" || { + msg_error "Failed to setup PHP repository" return 1 - } + } + fi + + ensure_apt_working || return 1 + + # Just update PHP packages + $STD apt install --only-upgrade -y "php${PHP_VERSION}" || true + cache_installed_version "php" "$PHP_VERSION" + msg_ok "Update PHP $PHP_VERSION" + else + # Scenario 2: Different version installed - clean upgrade + if [[ -n "$CURRENT_PHP" && "$CURRENT_PHP" != "$PHP_VERSION" ]]; then + msg_info "Upgrade PHP from $CURRENT_PHP to $PHP_VERSION" + # Stop old PHP-FPM if running + $STD systemctl stop "php${CURRENT_PHP}-fpm" >/dev/null 2>&1 || true + $STD systemctl disable "php${CURRENT_PHP}-fpm" >/dev/null 2>&1 || true + remove_old_tool_version "php" + else + msg_info "Setup PHP $PHP_VERSION" + fi - # Patch all relevant php.ini files - local PHP_INI_PATHS=("/etc/php/${PHP_VERSION}/cli/php.ini") - [[ "$PHP_FPM" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/fpm/php.ini") - [[ "$PHP_APACHE" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/apache2/php.ini") - for ini in "${PHP_INI_PATHS[@]}"; do - if [[ -f "$ini" ]]; then - $STD sed -i "s|^memory_limit = .*|memory_limit = ${PHP_MEMORY_LIMIT}|" "$ini" - $STD sed -i "s|^upload_max_filesize = .*|upload_max_filesize = ${PHP_UPLOAD_MAX_FILESIZE}|" "$ini" - $STD sed -i "s|^post_max_size = .*|post_max_size = ${PHP_POST_MAX_SIZE}|" "$ini" - $STD sed -i "s|^max_execution_time = .*|max_execution_time = ${PHP_MAX_EXECUTION_TIME}|" "$ini" - fi + # Setup Sury repository + manage_tool_repository "php" "$PHP_VERSION" "" "https://packages.sury.org/debsuryorg-archive-keyring.deb" || { + msg_error "Failed to setup PHP repository" + return 1 + } + + ensure_apt_working || return 1 + fi + + # Build module list + local MODULE_LIST="php${PHP_VERSION}" + IFS=',' read -ra MODULES <<<"$COMBINED_MODULES" + for mod in "${MODULES[@]}"; do + if apt-cache show "php${PHP_VERSION}-${mod}" >/dev/null 2>&1; then + MODULE_LIST+=" php${PHP_VERSION}-${mod}" + fi + done + if [[ "$PHP_FPM" == "YES" ]]; then + MODULE_LIST+=" php${PHP_VERSION}-fpm" + fi + + # install apache2 with PHP support if requested + if [[ "$PHP_APACHE" == "YES" ]]; then + if ! dpkg -l 2>/dev/null | grep -q "libapache2-mod-php${PHP_VERSION}"; then + $STD apt install -y apache2 libapache2-mod-php${PHP_VERSION} || { + msg_error "Failed to install Apache with PHP module" + return 1 + } + fi + fi + + # Install PHP packages + $STD apt install -y $MODULE_LIST || { + msg_error "Failed to install PHP packages" + return 1 + } + cache_installed_version "php" "$PHP_VERSION" + + # Patch all relevant php.ini files + local PHP_INI_PATHS=("/etc/php/${PHP_VERSION}/cli/php.ini") + [[ "$PHP_FPM" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/fpm/php.ini") + [[ "$PHP_APACHE" == "YES" ]] && PHP_INI_PATHS+=("/etc/php/${PHP_VERSION}/apache2/php.ini") + for ini in "${PHP_INI_PATHS[@]}"; do + if [[ -f "$ini" ]]; then + $STD sed -i "s|^memory_limit = .*|memory_limit = ${PHP_MEMORY_LIMIT}|" "$ini" + $STD sed -i "s|^upload_max_filesize = .*|upload_max_filesize = ${PHP_UPLOAD_MAX_FILESIZE}|" "$ini" + $STD sed -i "s|^post_max_size = .*|post_max_size = ${PHP_POST_MAX_SIZE}|" "$ini" + $STD sed -i "s|^max_execution_time = .*|max_execution_time = ${PHP_MAX_EXECUTION_TIME}|" "$ini" + fi + done + + # Patch Apache configuration if needed + if [[ "$PHP_APACHE" == "YES" ]]; then + for mod in $(ls /etc/apache2/mods-enabled/ 2>/dev/null | grep -E '^php[0-9]\.[0-9]\.conf$' | sed 's/\.conf//'); do + if [[ "$mod" != "php${PHP_VERSION}" ]]; then + $STD a2dismod "$mod" || true + fi done + $STD a2enmod mpm_prefork + $STD a2enmod "php${PHP_VERSION}" + safe_service_restart apache2 || true + fi - # Patch Apache configuration if needed - if [[ "$PHP_APACHE" == "YES" ]]; then - for mod in $(ls /etc/apache2/mods-enabled/ 2>/dev/null | grep -E '^php[0-9]\.[0-9]\.conf$' | sed 's/\.conf//'); do - if [[ "$mod" != "php${PHP_VERSION}" ]]; then - $STD a2dismod "$mod" || true - fi - done - $STD a2enmod mpm_prefork - $STD a2enmod "php${PHP_VERSION}" - safe_service_restart apache2 || true + # Enable and restart PHP-FPM if requested + if [[ "$PHP_FPM" == "YES" ]]; then + if systemctl list-unit-files | grep -q "php${PHP_VERSION}-fpm.service"; then + $STD systemctl enable php${PHP_VERSION}-fpm + safe_service_restart php${PHP_VERSION}-fpm fi + fi - # Enable and restart PHP-FPM if requested - if [[ "$PHP_FPM" == "YES" ]]; then - if systemctl list-unit-files | grep -q "php${PHP_VERSION}-fpm.service"; then - $STD systemctl enable php${PHP_VERSION}-fpm - safe_service_restart php${PHP_VERSION}-fpm - fi - fi - - msg_ok "Setup PHP $PHP_VERSION" + msg_ok "Setup PHP $PHP_VERSION" } # ------------------------------------------------------------------------------ @@ -3366,141 +3395,141 @@ function setup_php() { # Variables: # PG_VERSION - Major PostgreSQL version (e.g. 15, 16) (default: 16) function setup_postgresql() { - local PG_VERSION="${PG_VERSION:-16}" - local PG_MODULES="${PG_MODULES:-}" - local DISTRO_ID DISTRO_CODENAME - DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') - DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + local PG_VERSION="${PG_VERSION:-16}" + local PG_MODULES="${PG_MODULES:-}" + local DISTRO_ID DISTRO_CODENAME + DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - # Get currently installed version - local CURRENT_PG_VERSION="" - if command -v psql >/dev/null; then - CURRENT_PG_VERSION="$(psql -V 2>/dev/null | awk '{print $3}' | cut -d. -f1)" - fi - - # Scenario 1: Already at correct version - if [[ "$CURRENT_PG_VERSION" == "$PG_VERSION" ]]; then - msg_info "Update PostgreSQL $PG_VERSION" - $STD apt update - $STD apt install --only-upgrade -y "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}" 2>/dev/null || true - cache_installed_version "postgresql" "$PG_VERSION" - msg_ok "Update PostgreSQL $PG_VERSION" - - # Still install modules if specified - if [[ -n "$PG_MODULES" ]]; then - IFS=',' read -ra MODULES <<<"$PG_MODULES" - for module in "${MODULES[@]}"; do - $STD apt install -y "postgresql-${PG_VERSION}-${module}" 2>/dev/null || true - done - fi - return 0 - fi - - # Scenario 2: Different version - backup, remove old, install new - if [[ -n "$CURRENT_PG_VERSION" ]]; then - msg_info "Upgrade PostgreSQL from $CURRENT_PG_VERSION to $PG_VERSION" - msg_info "Creating backup of PostgreSQL $CURRENT_PG_VERSION databases..." - $STD runuser -u postgres -- pg_dumpall >/var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql || { - msg_error "Failed to backup PostgreSQL databases" - return 1 - } - $STD systemctl stop postgresql || true - $STD apt purge -y "postgresql-${CURRENT_PG_VERSION}" "postgresql-client-${CURRENT_PG_VERSION}" 2>/dev/null || true - else - msg_info "Setup PostgreSQL $PG_VERSION" - fi - - # Scenario 3: Fresh install or after removal - setup repo and install - cleanup_old_repo_files "pgdg" - - local SUITE - case "$DISTRO_CODENAME" in - trixie | forky | sid) - if verify_repo_available "https://apt.postgresql.org/pub/repos/apt" "trixie-pgdg"; then - SUITE="trixie-pgdg" - else - SUITE="bookworm-pgdg" - fi - ;; - *) - SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://apt.postgresql.org/pub/repos/apt") - SUITE="${SUITE}-pgdg" - ;; - esac - - setup_deb822_repo \ - "pgdg" \ - "https://www.postgresql.org/media/keys/ACCC4CF8.asc" \ - "https://apt.postgresql.org/pub/repos/apt" \ - "$SUITE" \ - "main" \ - "amd64 arm64" - - if ! $STD apt update; then - msg_error "APT update failed for PostgreSQL repository" - return 1 - fi - - # Install ssl-cert dependency if available - if apt-cache search "^ssl-cert$" 2>/dev/null | grep -q .; then - $STD apt install -y ssl-cert 2>/dev/null || true - fi - - # Try multiple PostgreSQL package patterns - local pg_install_success=false - - if apt-cache search "^postgresql-${PG_VERSION}$" 2>/dev/null | grep -q . && - $STD apt install -y "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}" 2>/dev/null; then - pg_install_success=true - fi - - if [[ "$pg_install_success" == false ]] && - apt-cache search "^postgresql-server-${PG_VERSION}$" 2>/dev/null | grep -q . && - $STD apt install -y "postgresql-server-${PG_VERSION}" "postgresql-client-${PG_VERSION}" 2>/dev/null; then - pg_install_success=true - fi - - if [[ "$pg_install_success" == false ]] && - apt-cache search "^postgresql$" 2>/dev/null | grep -q . && - $STD apt install -y postgresql postgresql-client 2>/dev/null; then - pg_install_success=true - fi - - if [[ "$pg_install_success" == false ]]; then - msg_error "PostgreSQL package not available for suite ${SUITE}" - return 1 - fi - - if ! command -v psql >/dev/null 2>&1; then - msg_error "PostgreSQL installed but psql command not found" - return 1 - fi - - # Restore database backup if we upgraded from previous version - if [[ -n "$CURRENT_PG_VERSION" ]]; then - msg_info "Restoring PostgreSQL databases from backup..." - $STD runuser -u postgres -- psql /dev/null || { - msg_warn "Failed to restore database backup - this may be expected for major version upgrades" - } - fi - - $STD systemctl enable --now postgresql 2>/dev/null || true - - # Add PostgreSQL binaries to PATH - if ! grep -q '/usr/lib/postgresql' /etc/environment 2>/dev/null; then - echo 'PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/postgresql/'"${PG_VERSION}"'/bin"' >/etc/environment - fi + # Get currently installed version + local CURRENT_PG_VERSION="" + if command -v psql >/dev/null; then + CURRENT_PG_VERSION="$(psql -V 2>/dev/null | awk '{print $3}' | cut -d. -f1)" + fi + # Scenario 1: Already at correct version + if [[ "$CURRENT_PG_VERSION" == "$PG_VERSION" ]]; then + msg_info "Update PostgreSQL $PG_VERSION" + $STD apt update + $STD apt install --only-upgrade -y "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}" 2>/dev/null || true cache_installed_version "postgresql" "$PG_VERSION" - msg_ok "Setup PostgreSQL $PG_VERSION" + msg_ok "Update PostgreSQL $PG_VERSION" - # Install optional modules + # Still install modules if specified if [[ -n "$PG_MODULES" ]]; then - IFS=',' read -ra MODULES <<<"$PG_MODULES" - for module in "${MODULES[@]}"; do - $STD apt install -y "postgresql-${PG_VERSION}-${module}" 2>/dev/null || true - done + IFS=',' read -ra MODULES <<<"$PG_MODULES" + for module in "${MODULES[@]}"; do + $STD apt install -y "postgresql-${PG_VERSION}-${module}" 2>/dev/null || true + done fi + return 0 + fi + + # Scenario 2: Different version - backup, remove old, install new + if [[ -n "$CURRENT_PG_VERSION" ]]; then + msg_info "Upgrade PostgreSQL from $CURRENT_PG_VERSION to $PG_VERSION" + msg_info "Creating backup of PostgreSQL $CURRENT_PG_VERSION databases..." + $STD runuser -u postgres -- pg_dumpall >/var/lib/postgresql/backup_$(date +%F)_v${CURRENT_PG_VERSION}.sql || { + msg_error "Failed to backup PostgreSQL databases" + return 1 + } + $STD systemctl stop postgresql || true + $STD apt purge -y "postgresql-${CURRENT_PG_VERSION}" "postgresql-client-${CURRENT_PG_VERSION}" 2>/dev/null || true + else + msg_info "Setup PostgreSQL $PG_VERSION" + fi + + # Scenario 3: Fresh install or after removal - setup repo and install + cleanup_old_repo_files "pgdg" + + local SUITE + case "$DISTRO_CODENAME" in + trixie | forky | sid) + if verify_repo_available "https://apt.postgresql.org/pub/repos/apt" "trixie-pgdg"; then + SUITE="trixie-pgdg" + else + SUITE="bookworm-pgdg" + fi + ;; + *) + SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://apt.postgresql.org/pub/repos/apt") + SUITE="${SUITE}-pgdg" + ;; + esac + + setup_deb822_repo \ + "pgdg" \ + "https://www.postgresql.org/media/keys/ACCC4CF8.asc" \ + "https://apt.postgresql.org/pub/repos/apt" \ + "$SUITE" \ + "main" \ + "amd64 arm64" + + if ! $STD apt update; then + msg_error "APT update failed for PostgreSQL repository" + return 1 + fi + + # Install ssl-cert dependency if available + if apt-cache search "^ssl-cert$" 2>/dev/null | grep -q .; then + $STD apt install -y ssl-cert 2>/dev/null || true + fi + + # Try multiple PostgreSQL package patterns + local pg_install_success=false + + if apt-cache search "^postgresql-${PG_VERSION}$" 2>/dev/null | grep -q . && + $STD apt install -y "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}" 2>/dev/null; then + pg_install_success=true + fi + + if [[ "$pg_install_success" == false ]] && + apt-cache search "^postgresql-server-${PG_VERSION}$" 2>/dev/null | grep -q . && + $STD apt install -y "postgresql-server-${PG_VERSION}" "postgresql-client-${PG_VERSION}" 2>/dev/null; then + pg_install_success=true + fi + + if [[ "$pg_install_success" == false ]] && + apt-cache search "^postgresql$" 2>/dev/null | grep -q . && + $STD apt install -y postgresql postgresql-client 2>/dev/null; then + pg_install_success=true + fi + + if [[ "$pg_install_success" == false ]]; then + msg_error "PostgreSQL package not available for suite ${SUITE}" + return 1 + fi + + if ! command -v psql >/dev/null 2>&1; then + msg_error "PostgreSQL installed but psql command not found" + return 1 + fi + + # Restore database backup if we upgraded from previous version + if [[ -n "$CURRENT_PG_VERSION" ]]; then + msg_info "Restoring PostgreSQL databases from backup..." + $STD runuser -u postgres -- psql /dev/null || { + msg_warn "Failed to restore database backup - this may be expected for major version upgrades" + } + fi + + $STD systemctl enable --now postgresql 2>/dev/null || true + + # Add PostgreSQL binaries to PATH + if ! grep -q '/usr/lib/postgresql' /etc/environment 2>/dev/null; then + echo 'PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/postgresql/'"${PG_VERSION}"'/bin"' >/etc/environment + fi + + cache_installed_version "postgresql" "$PG_VERSION" + msg_ok "Setup PostgreSQL $PG_VERSION" + + # Install optional modules + if [[ -n "$PG_MODULES" ]]; then + IFS=',' read -ra MODULES <<<"$PG_MODULES" + for module in "${MODULES[@]}"; do + $STD apt install -y "postgresql-${PG_VERSION}-${module}" 2>/dev/null || true + done + fi } # ------------------------------------------------------------------------------ @@ -3517,192 +3546,192 @@ function setup_postgresql() { # ------------------------------------------------------------------------------ function setup_ruby() { - local RUBY_VERSION="${RUBY_VERSION:-3.4.4}" - local RUBY_INSTALL_RAILS="${RUBY_INSTALL_RAILS:-true}" - local RBENV_DIR="$HOME/.rbenv" - local RBENV_BIN="$RBENV_DIR/bin/rbenv" - local PROFILE_FILE="$HOME/.profile" - local TMP_DIR=$(mktemp -d) + local RUBY_VERSION="${RUBY_VERSION:-3.4.4}" + local RUBY_INSTALL_RAILS="${RUBY_INSTALL_RAILS:-true}" + local RBENV_DIR="$HOME/.rbenv" + local RBENV_BIN="$RBENV_DIR/bin/rbenv" + local PROFILE_FILE="$HOME/.profile" + local TMP_DIR=$(mktemp -d) - # Get currently installed Ruby version - local CURRENT_RUBY_VERSION="" - if [[ -x "$RBENV_BIN" ]]; then - CURRENT_RUBY_VERSION=$("$RBENV_BIN" global 2>/dev/null || echo "") - fi + # Get currently installed Ruby version + local CURRENT_RUBY_VERSION="" + if [[ -x "$RBENV_BIN" ]]; then + CURRENT_RUBY_VERSION=$("$RBENV_BIN" global 2>/dev/null || echo "") + fi - # Scenario 1: Already at correct Ruby version - if [[ "$CURRENT_RUBY_VERSION" == "$RUBY_VERSION" ]]; then - msg_info "Update Ruby $RUBY_VERSION" - cache_installed_version "ruby" "$RUBY_VERSION" - msg_ok "Update Ruby $RUBY_VERSION" - return 0 - fi + # Scenario 1: Already at correct Ruby version + if [[ "$CURRENT_RUBY_VERSION" == "$RUBY_VERSION" ]]; then + msg_info "Update Ruby $RUBY_VERSION" + cache_installed_version "ruby" "$RUBY_VERSION" + msg_ok "Update Ruby $RUBY_VERSION" + return 0 + fi - # Scenario 2: Different version - reinstall - if [[ -n "$CURRENT_RUBY_VERSION" ]]; then - msg_info "Upgrade Ruby from $CURRENT_RUBY_VERSION to $RUBY_VERSION" + # Scenario 2: Different version - reinstall + if [[ -n "$CURRENT_RUBY_VERSION" ]]; then + msg_info "Upgrade Ruby from $CURRENT_RUBY_VERSION to $RUBY_VERSION" + else + msg_info "Setup Ruby $RUBY_VERSION" + fi + + ensure_apt_working || return 1 + + # Install build dependencies with fallbacks + local ruby_deps=() + local dep_variations=( + "jq" + "autoconf" + "patch" + "build-essential" + "libssl-dev" + "libyaml-dev" + "libreadline-dev|libreadline6-dev" + "zlib1g-dev" + "libgmp-dev" + "libncurses-dev|libncurses5-dev" + "libffi-dev" + "libgdbm-dev" + "libdb-dev" + "uuid-dev" + ) + + for dep_pattern in "${dep_variations[@]}"; do + if [[ "$dep_pattern" == *"|"* ]]; then + IFS='|' read -ra variations <<<"$dep_pattern" + for var in "${variations[@]}"; do + if apt-cache search "^${var}$" 2>/dev/null | grep -q .; then + ruby_deps+=("$var") + break + fi + done else - msg_info "Setup Ruby $RUBY_VERSION" + if apt-cache search "^${dep_pattern}$" 2>/dev/null | grep -q .; then + ruby_deps+=("$dep_pattern") + fi + fi + done + + if [[ ${#ruby_deps[@]} -gt 0 ]]; then + $STD apt install -y "${ruby_deps[@]}" 2>/dev/null || true + else + msg_error "No Ruby build dependencies available" + rm -rf "$TMP_DIR" + return 1 + fi + + # Download and build rbenv if needed + if [[ ! -x "$RBENV_BIN" ]]; then + local RBENV_RELEASE + local rbenv_json + rbenv_json=$(curl -fsSL --max-time 15 https://api.github.com/repos/rbenv/rbenv/releases/latest 2>/dev/null || echo "") + + if [[ -z "$rbenv_json" ]]; then + msg_error "Failed to fetch latest rbenv version from GitHub" + rm -rf "$TMP_DIR" + return 1 fi - ensure_apt_working || return 1 + RBENV_RELEASE=$(echo "$rbenv_json" | jq -r '.tag_name' 2>/dev/null | sed 's/^v//' || echo "") - # Install build dependencies with fallbacks - local ruby_deps=() - local dep_variations=( - "jq" - "autoconf" - "patch" - "build-essential" - "libssl-dev" - "libyaml-dev" - "libreadline-dev|libreadline6-dev" - "zlib1g-dev" - "libgmp-dev" - "libncurses-dev|libncurses5-dev" - "libffi-dev" - "libgdbm-dev" - "libdb-dev" - "uuid-dev" - ) - - for dep_pattern in "${dep_variations[@]}"; do - if [[ "$dep_pattern" == *"|"* ]]; then - IFS='|' read -ra variations <<<"$dep_pattern" - for var in "${variations[@]}"; do - if apt-cache search "^${var}$" 2>/dev/null | grep -q .; then - ruby_deps+=("$var") - break - fi - done - else - if apt-cache search "^${dep_pattern}$" 2>/dev/null | grep -q .; then - ruby_deps+=("$dep_pattern") - fi - fi - done - - if [[ ${#ruby_deps[@]} -gt 0 ]]; then - $STD apt install -y "${ruby_deps[@]}" 2>/dev/null || true - else - msg_error "No Ruby build dependencies available" - rm -rf "$TMP_DIR" - return 1 + if [[ -z "$RBENV_RELEASE" ]]; then + msg_error "Could not parse rbenv version from GitHub response" + rm -rf "$TMP_DIR" + return 1 fi - # Download and build rbenv if needed - if [[ ! -x "$RBENV_BIN" ]]; then - local RBENV_RELEASE - local rbenv_json - rbenv_json=$(curl -fsSL --max-time 15 https://api.github.com/repos/rbenv/rbenv/releases/latest 2>/dev/null || echo "") - - if [[ -z "$rbenv_json" ]]; then - msg_error "Failed to fetch latest rbenv version from GitHub" - rm -rf "$TMP_DIR" - return 1 - fi - - RBENV_RELEASE=$(echo "$rbenv_json" | jq -r '.tag_name' 2>/dev/null | sed 's/^v//' || echo "") - - if [[ -z "$RBENV_RELEASE" ]]; then - msg_error "Could not parse rbenv version from GitHub response" - rm -rf "$TMP_DIR" - return 1 - fi - - curl -fsSL "https://github.com/rbenv/rbenv/archive/refs/tags/v${RBENV_RELEASE}.tar.gz" -o "$TMP_DIR/rbenv.tar.gz" || { - msg_error "Failed to download rbenv" - rm -rf "$TMP_DIR" - return 1 - } - - tar -xzf "$TMP_DIR/rbenv.tar.gz" -C "$TMP_DIR" || { - msg_error "Failed to extract rbenv" - rm -rf "$TMP_DIR" - return 1 - } - - mkdir -p "$RBENV_DIR" - cp -r "$TMP_DIR/rbenv-${RBENV_RELEASE}/." "$RBENV_DIR/" - (cd "$RBENV_DIR" && src/configure && $STD make -C src) || { - msg_error "Failed to build rbenv" - rm -rf "$TMP_DIR" - return 1 - } - - # Setup profile - if ! grep -q 'rbenv init' "$PROFILE_FILE" 2>/dev/null; then - echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >>"$PROFILE_FILE" - echo 'eval "$(rbenv init -)"' >>"$PROFILE_FILE" - fi - fi - - # Install ruby-build plugin - if [[ ! -d "$RBENV_DIR/plugins/ruby-build" ]]; then - local RUBY_BUILD_RELEASE - local ruby_build_json - ruby_build_json=$(curl -fsSL --max-time 15 https://api.github.com/repos/rbenv/ruby-build/releases/latest 2>/dev/null || echo "") - - if [[ -z "$ruby_build_json" ]]; then - msg_error "Failed to fetch latest ruby-build version from GitHub" - rm -rf "$TMP_DIR" - return 1 - fi - - RUBY_BUILD_RELEASE=$(echo "$ruby_build_json" | jq -r '.tag_name' 2>/dev/null | sed 's/^v//' || echo "") - - if [[ -z "$RUBY_BUILD_RELEASE" ]]; then - msg_error "Could not parse ruby-build version from GitHub response" - rm -rf "$TMP_DIR" - return 1 - fi - - curl -fsSL "https://github.com/rbenv/ruby-build/archive/refs/tags/v${RUBY_BUILD_RELEASE}.tar.gz" -o "$TMP_DIR/ruby-build.tar.gz" || { - msg_error "Failed to download ruby-build" - rm -rf "$TMP_DIR" - return 1 - } - - tar -xzf "$TMP_DIR/ruby-build.tar.gz" -C "$TMP_DIR" || { - msg_error "Failed to extract ruby-build" - rm -rf "$TMP_DIR" - return 1 - } - - mkdir -p "$RBENV_DIR/plugins/ruby-build" - cp -r "$TMP_DIR/ruby-build-${RUBY_BUILD_RELEASE}/." "$RBENV_DIR/plugins/ruby-build/" - fi - - # Setup PATH and install Ruby version - export PATH="$RBENV_DIR/bin:$PATH" - eval "$("$RBENV_BIN" init - bash)" 2>/dev/null || true - - if ! "$RBENV_BIN" versions --bare 2>/dev/null | grep -qx "$RUBY_VERSION"; then - $STD "$RBENV_BIN" install "$RUBY_VERSION" || { - msg_error "Failed to install Ruby $RUBY_VERSION" - rm -rf "$TMP_DIR" - return 1 - } - fi - - "$RBENV_BIN" global "$RUBY_VERSION" || { - msg_error "Failed to set Ruby $RUBY_VERSION as global version" - rm -rf "$TMP_DIR" - return 1 + curl -fsSL "https://github.com/rbenv/rbenv/archive/refs/tags/v${RBENV_RELEASE}.tar.gz" -o "$TMP_DIR/rbenv.tar.gz" || { + msg_error "Failed to download rbenv" + rm -rf "$TMP_DIR" + return 1 } - hash -r + tar -xzf "$TMP_DIR/rbenv.tar.gz" -C "$TMP_DIR" || { + msg_error "Failed to extract rbenv" + rm -rf "$TMP_DIR" + return 1 + } - # Install Rails if requested - if [[ "$RUBY_INSTALL_RAILS" == "true" ]]; then - $STD gem install rails || { - msg_warn "Failed to install Rails - Ruby installation successful" - } + mkdir -p "$RBENV_DIR" + cp -r "$TMP_DIR/rbenv-${RBENV_RELEASE}/." "$RBENV_DIR/" + (cd "$RBENV_DIR" && src/configure && $STD make -C src) || { + msg_error "Failed to build rbenv" + rm -rf "$TMP_DIR" + return 1 + } + + # Setup profile + if ! grep -q 'rbenv init' "$PROFILE_FILE" 2>/dev/null; then + echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >>"$PROFILE_FILE" + echo 'eval "$(rbenv init -)"' >>"$PROFILE_FILE" + fi + fi + + # Install ruby-build plugin + if [[ ! -d "$RBENV_DIR/plugins/ruby-build" ]]; then + local RUBY_BUILD_RELEASE + local ruby_build_json + ruby_build_json=$(curl -fsSL --max-time 15 https://api.github.com/repos/rbenv/ruby-build/releases/latest 2>/dev/null || echo "") + + if [[ -z "$ruby_build_json" ]]; then + msg_error "Failed to fetch latest ruby-build version from GitHub" + rm -rf "$TMP_DIR" + return 1 fi + RUBY_BUILD_RELEASE=$(echo "$ruby_build_json" | jq -r '.tag_name' 2>/dev/null | sed 's/^v//' || echo "") + + if [[ -z "$RUBY_BUILD_RELEASE" ]]; then + msg_error "Could not parse ruby-build version from GitHub response" + rm -rf "$TMP_DIR" + return 1 + fi + + curl -fsSL "https://github.com/rbenv/ruby-build/archive/refs/tags/v${RUBY_BUILD_RELEASE}.tar.gz" -o "$TMP_DIR/ruby-build.tar.gz" || { + msg_error "Failed to download ruby-build" + rm -rf "$TMP_DIR" + return 1 + } + + tar -xzf "$TMP_DIR/ruby-build.tar.gz" -C "$TMP_DIR" || { + msg_error "Failed to extract ruby-build" + rm -rf "$TMP_DIR" + return 1 + } + + mkdir -p "$RBENV_DIR/plugins/ruby-build" + cp -r "$TMP_DIR/ruby-build-${RUBY_BUILD_RELEASE}/." "$RBENV_DIR/plugins/ruby-build/" + fi + + # Setup PATH and install Ruby version + export PATH="$RBENV_DIR/bin:$PATH" + eval "$("$RBENV_BIN" init - bash)" 2>/dev/null || true + + if ! "$RBENV_BIN" versions --bare 2>/dev/null | grep -qx "$RUBY_VERSION"; then + $STD "$RBENV_BIN" install "$RUBY_VERSION" || { + msg_error "Failed to install Ruby $RUBY_VERSION" + rm -rf "$TMP_DIR" + return 1 + } + fi + + "$RBENV_BIN" global "$RUBY_VERSION" || { + msg_error "Failed to set Ruby $RUBY_VERSION as global version" rm -rf "$TMP_DIR" - cache_installed_version "ruby" "$RUBY_VERSION" - msg_ok "Setup Ruby $RUBY_VERSION" + return 1 + } + + hash -r + + # Install Rails if requested + if [[ "$RUBY_INSTALL_RAILS" == "true" ]]; then + $STD gem install rails || { + msg_warn "Failed to install Rails - Ruby installation successful" + } + fi + + rm -rf "$TMP_DIR" + cache_installed_version "ruby" "$RUBY_VERSION" + msg_ok "Setup Ruby $RUBY_VERSION" } # ------------------------------------------------------------------------------ @@ -3719,97 +3748,97 @@ function setup_ruby() { # ------------------------------------------------------------------------------ function setup_clickhouse() { - local CLICKHOUSE_VERSION="${CLICKHOUSE_VERSION:-latest}" - local DISTRO_ID DISTRO_CODENAME - DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') - DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) + local CLICKHOUSE_VERSION="${CLICKHOUSE_VERSION:-latest}" + local DISTRO_ID DISTRO_CODENAME + DISTRO_ID=$(awk -F= '/^ID=/{print $2}' /etc/os-release | tr -d '"') + DISTRO_CODENAME=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - # Resolve "latest" version - if [[ "$CLICKHOUSE_VERSION" == "latest" ]]; then - CLICKHOUSE_VERSION=$(curl -fsSL --max-time 15 https://packages.clickhouse.com/tgz/stable/ 2>/dev/null | - grep -oP 'clickhouse-common-static-\K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | - sort -V | tail -n1 || echo "") + # Resolve "latest" version + if [[ "$CLICKHOUSE_VERSION" == "latest" ]]; then + CLICKHOUSE_VERSION=$(curl -fsSL --max-time 15 https://packages.clickhouse.com/tgz/stable/ 2>/dev/null | + grep -oP 'clickhouse-common-static-\K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | + sort -V | tail -n1 || echo "") - # Fallback to GitHub API if package server failed - if [[ -z "$CLICKHOUSE_VERSION" ]]; then - CLICKHOUSE_VERSION=$(curl -fsSL --max-time 15 https://api.github.com/repos/ClickHouse/ClickHouse/releases/latest 2>/dev/null | - grep -oP '"tag_name":\s*"v\K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -n1 || echo "") - fi - - [[ -z "$CLICKHOUSE_VERSION" ]] && { - msg_error "Could not determine latest ClickHouse version from any source" - return 1 - } + # Fallback to GitHub API if package server failed + if [[ -z "$CLICKHOUSE_VERSION" ]]; then + CLICKHOUSE_VERSION=$(curl -fsSL --max-time 15 https://api.github.com/repos/ClickHouse/ClickHouse/releases/latest 2>/dev/null | + grep -oP '"tag_name":\s*"v\K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -n1 || echo "") fi - # Get currently installed version - local CURRENT_VERSION="" - if command -v clickhouse-server >/dev/null 2>&1; then - CURRENT_VERSION=$(clickhouse-server --version 2>/dev/null | grep -oP 'version \K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -n1) - fi - - # Scenario 1: Already at target version - just update packages - if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$CLICKHOUSE_VERSION" ]]; then - msg_info "Update ClickHouse $CLICKHOUSE_VERSION" - ensure_apt_working || return 1 - $STD apt install --only-upgrade -y clickhouse-server clickhouse-client || true - cache_installed_version "clickhouse" "$CLICKHOUSE_VERSION" - msg_ok "Update ClickHouse $CLICKHOUSE_VERSION" - return 0 - fi - - # Scenario 2: Different version - clean upgrade - if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" != "$CLICKHOUSE_VERSION" ]]; then - msg_info "Upgrade ClickHouse from $CURRENT_VERSION to $CLICKHOUSE_VERSION" - $STD systemctl stop clickhouse-server >/dev/null 2>&1 || true - remove_old_tool_version "clickhouse" - else - msg_info "Setup ClickHouse $CLICKHOUSE_VERSION" - fi - - ensure_dependencies apt-transport-https ca-certificates dirmngr gnupg - - # Setup repository (ClickHouse uses 'stable' suite) - setup_deb822_repo \ - "clickhouse" \ - "https://packages.clickhouse.com/rpm/lts/repodata/repomd.xml.key" \ - "https://packages.clickhouse.com/deb" \ - "stable" \ - "main" \ - "amd64 arm64" - - # Install packages - export DEBIAN_FRONTEND=noninteractive - $STD apt update || { - msg_error "APT update failed for ClickHouse repository" - return 1 + [[ -z "$CLICKHOUSE_VERSION" ]] && { + msg_error "Could not determine latest ClickHouse version from any source" + return 1 } + fi - $STD apt install -y clickhouse-server clickhouse-client || { - msg_error "Failed to install ClickHouse packages" - return 1 - } - - # Verify installation - if ! command -v clickhouse-server >/dev/null 2>&1; then - msg_error "ClickHouse installation completed but clickhouse-server command not found" - return 1 - fi - - # Setup data directory - mkdir -p /var/lib/clickhouse - if id clickhouse >/dev/null 2>&1; then - chown -R clickhouse:clickhouse /var/lib/clickhouse - fi - - # Enable and start service - $STD systemctl enable clickhouse-server || { - msg_warn "Failed to enable clickhouse-server service" - } - safe_service_restart clickhouse-server || true + # Get currently installed version + local CURRENT_VERSION="" + if command -v clickhouse-server >/dev/null 2>&1; then + CURRENT_VERSION=$(clickhouse-server --version 2>/dev/null | grep -oP 'version \K[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -n1) + fi + # Scenario 1: Already at target version - just update packages + if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$CLICKHOUSE_VERSION" ]]; then + msg_info "Update ClickHouse $CLICKHOUSE_VERSION" + ensure_apt_working || return 1 + $STD apt install --only-upgrade -y clickhouse-server clickhouse-client || true cache_installed_version "clickhouse" "$CLICKHOUSE_VERSION" - msg_ok "Setup ClickHouse $CLICKHOUSE_VERSION" + msg_ok "Update ClickHouse $CLICKHOUSE_VERSION" + return 0 + fi + + # Scenario 2: Different version - clean upgrade + if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" != "$CLICKHOUSE_VERSION" ]]; then + msg_info "Upgrade ClickHouse from $CURRENT_VERSION to $CLICKHOUSE_VERSION" + $STD systemctl stop clickhouse-server >/dev/null 2>&1 || true + remove_old_tool_version "clickhouse" + else + msg_info "Setup ClickHouse $CLICKHOUSE_VERSION" + fi + + ensure_dependencies apt-transport-https ca-certificates dirmngr gnupg + + # Setup repository (ClickHouse uses 'stable' suite) + setup_deb822_repo \ + "clickhouse" \ + "https://packages.clickhouse.com/rpm/lts/repodata/repomd.xml.key" \ + "https://packages.clickhouse.com/deb" \ + "stable" \ + "main" \ + "amd64 arm64" + + # Install packages + export DEBIAN_FRONTEND=noninteractive + $STD apt update || { + msg_error "APT update failed for ClickHouse repository" + return 1 + } + + $STD apt install -y clickhouse-server clickhouse-client || { + msg_error "Failed to install ClickHouse packages" + return 1 + } + + # Verify installation + if ! command -v clickhouse-server >/dev/null 2>&1; then + msg_error "ClickHouse installation completed but clickhouse-server command not found" + return 1 + fi + + # Setup data directory + mkdir -p /var/lib/clickhouse + if id clickhouse >/dev/null 2>&1; then + chown -R clickhouse:clickhouse /var/lib/clickhouse + fi + + # Enable and start service + $STD systemctl enable clickhouse-server || { + msg_warn "Failed to enable clickhouse-server service" + } + safe_service_restart clickhouse-server || true + + cache_installed_version "clickhouse" "$CLICKHOUSE_VERSION" + msg_ok "Setup ClickHouse $CLICKHOUSE_VERSION" } # ------------------------------------------------------------------------------ @@ -3830,71 +3859,71 @@ function setup_clickhouse() { # ------------------------------------------------------------------------------ function setup_rust() { - local RUST_TOOLCHAIN="${RUST_TOOLCHAIN:-stable}" - local RUST_CRATES="${RUST_CRATES:-}" - local CARGO_BIN="${HOME}/.cargo/bin" + local RUST_TOOLCHAIN="${RUST_TOOLCHAIN:-stable}" + local RUST_CRATES="${RUST_CRATES:-}" + local CARGO_BIN="${HOME}/.cargo/bin" - # Get currently installed version - local CURRENT_VERSION="" - if command -v rustc &>/dev/null; then - CURRENT_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}') - fi + # Get currently installed version + local CURRENT_VERSION="" + if command -v rustc &>/dev/null; then + CURRENT_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}') + fi - # Scenario 1: Rustup not installed - fresh install - if ! command -v rustup &>/dev/null; then - msg_info "Setup Rust ($RUST_TOOLCHAIN)" - curl -fsSL https://sh.rustup.rs | $STD sh -s -- -y --default-toolchain "$RUST_TOOLCHAIN" || { - msg_error "Failed to install Rust" - return 1 - } - export PATH="$CARGO_BIN:$PATH" - echo 'export PATH="$HOME/.cargo/bin:$PATH"' >>"$HOME/.profile" - local RUST_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}') - cache_installed_version "rust" "$RUST_VERSION" - msg_ok "Setup Rust $RUST_VERSION" - else - # Scenario 2: Rustup already installed - update/maintain - msg_info "Update Rust ($RUST_TOOLCHAIN)" - $STD rustup install "$RUST_TOOLCHAIN" || { - msg_error "Failed to install Rust toolchain $RUST_TOOLCHAIN" - return 1 - } - $STD rustup default "$RUST_TOOLCHAIN" || { - msg_error "Failed to set default Rust toolchain" - return 1 - } - $STD rustup update "$RUST_TOOLCHAIN" || true - local RUST_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}') - cache_installed_version "rust" "$RUST_VERSION" - msg_ok "Update Rust $RUST_VERSION" - fi + # Scenario 1: Rustup not installed - fresh install + if ! command -v rustup &>/dev/null; then + msg_info "Setup Rust ($RUST_TOOLCHAIN)" + curl -fsSL https://sh.rustup.rs | $STD sh -s -- -y --default-toolchain "$RUST_TOOLCHAIN" || { + msg_error "Failed to install Rust" + return 1 + } + export PATH="$CARGO_BIN:$PATH" + echo 'export PATH="$HOME/.cargo/bin:$PATH"' >>"$HOME/.profile" + local RUST_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}') + cache_installed_version "rust" "$RUST_VERSION" + msg_ok "Setup Rust $RUST_VERSION" + else + # Scenario 2: Rustup already installed - update/maintain + msg_info "Update Rust ($RUST_TOOLCHAIN)" + $STD rustup install "$RUST_TOOLCHAIN" || { + msg_error "Failed to install Rust toolchain $RUST_TOOLCHAIN" + return 1 + } + $STD rustup default "$RUST_TOOLCHAIN" || { + msg_error "Failed to set default Rust toolchain" + return 1 + } + $STD rustup update "$RUST_TOOLCHAIN" || true + local RUST_VERSION=$(rustc --version 2>/dev/null | awk '{print $2}') + cache_installed_version "rust" "$RUST_VERSION" + msg_ok "Update Rust $RUST_VERSION" + fi - # Install global crates - if [[ -n "$RUST_CRATES" ]]; then - IFS=',' read -ra CRATES <<<"$RUST_CRATES" - for crate in "${CRATES[@]}"; do - local NAME VER INSTALLED_VER - if [[ "$crate" == *"@"* ]]; then - NAME="${crate%@*}" - VER="${crate##*@}" - else - NAME="$crate" - VER="" - fi + # Install global crates + if [[ -n "$RUST_CRATES" ]]; then + IFS=',' read -ra CRATES <<<"$RUST_CRATES" + for crate in "${CRATES[@]}"; do + local NAME VER INSTALLED_VER + if [[ "$crate" == *"@"* ]]; then + NAME="${crate%@*}" + VER="${crate##*@}" + else + NAME="$crate" + VER="" + fi - INSTALLED_VER=$(cargo install --list 2>/dev/null | awk "/^$NAME v[0-9]/ {print \$2}" | tr -d 'v') + INSTALLED_VER=$(cargo install --list 2>/dev/null | awk "/^$NAME v[0-9]/ {print \$2}" | tr -d 'v') - if [[ -n "$INSTALLED_VER" ]]; then - if [[ -n "$VER" && "$VER" != "$INSTALLED_VER" ]]; then - $STD cargo install "$NAME" --version "$VER" --force - elif [[ -z "$VER" ]]; then - $STD cargo install "$NAME" --force - fi - else - $STD cargo install "$NAME" ${VER:+--version "$VER"} - fi - done - fi + if [[ -n "$INSTALLED_VER" ]]; then + if [[ -n "$VER" && "$VER" != "$INSTALLED_VER" ]]; then + $STD cargo install "$NAME" --version "$VER" --force + elif [[ -z "$VER" ]]; then + $STD cargo install "$NAME" --force + fi + else + $STD cargo install "$NAME" ${VER:+--version "$VER"} + fi + done + fi } # ------------------------------------------------------------------------------ @@ -3906,122 +3935,122 @@ function setup_rust() { # ------------------------------------------------------------------------------ function setup_uv() { - local UV_BIN="/usr/local/bin/uv" - local TMP_DIR=$(mktemp -d) - local CACHED_VERSION - CACHED_VERSION=$(get_cached_version "uv") + local UV_BIN="/usr/local/bin/uv" + local TMP_DIR=$(mktemp -d) + local CACHED_VERSION + CACHED_VERSION=$(get_cached_version "uv") - local ARCH=$(uname -m) - local UV_TAR + local ARCH=$(uname -m) + local UV_TAR - case "$ARCH" in - x86_64) - if grep -qi "alpine" /etc/os-release; then - UV_TAR="uv-x86_64-unknown-linux-musl.tar.gz" - else - UV_TAR="uv-x86_64-unknown-linux-gnu.tar.gz" - fi - ;; - aarch64) - if grep -qi "alpine" /etc/os-release; then - UV_TAR="uv-aarch64-unknown-linux-musl.tar.gz" - else - UV_TAR="uv-aarch64-unknown-linux-gnu.tar.gz" - fi - ;; - *) - msg_error "Unsupported architecture: $ARCH" - rm -rf "$TMP_DIR" - return 1 - ;; - esac - - ensure_dependencies jq - - local LATEST_VERSION - local releases_json - releases_json=$(curl -fsSL --max-time 15 https://api.github.com/repos/astral-sh/uv/releases/latest 2>/dev/null || echo "") - - if [[ -z "$releases_json" ]]; then - msg_error "Could not fetch latest uv version from GitHub API" - rm -rf "$TMP_DIR" - return 1 - fi - - LATEST_VERSION=$(echo "$releases_json" | jq -r '.tag_name' 2>/dev/null | sed 's/^v//' || echo "") - - if [[ -z "$LATEST_VERSION" ]]; then - msg_error "Could not parse uv version from GitHub API response" - rm -rf "$TMP_DIR" - return 1 - fi - - # Get currently installed version - local INSTALLED_VERSION="" - if [[ -x "$UV_BIN" ]]; then - INSTALLED_VERSION=$($UV_BIN -V 2>/dev/null | awk '{print $2}') - fi - - # Scenario 1: Already at latest version - if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" == "$LATEST_VERSION" ]]; then - cache_installed_version "uv" "$LATEST_VERSION" - rm -rf "$TMP_DIR" - return 0 - fi - - # Scenario 2: New install or upgrade - if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" != "$LATEST_VERSION" ]]; then - msg_info "Upgrade uv from $INSTALLED_VERSION to $LATEST_VERSION" + case "$ARCH" in + x86_64) + if grep -qi "alpine" /etc/os-release; then + UV_TAR="uv-x86_64-unknown-linux-musl.tar.gz" else - msg_info "Setup uv $LATEST_VERSION" + UV_TAR="uv-x86_64-unknown-linux-gnu.tar.gz" fi - - local UV_URL="https://github.com/astral-sh/uv/releases/latest/download/${UV_TAR}" - curl -fsSL "$UV_URL" -o "$TMP_DIR/uv.tar.gz" || { - msg_error "Failed to download uv" - rm -rf "$TMP_DIR" - return 1 - } - - tar -xzf "$TMP_DIR/uv.tar.gz" -C "$TMP_DIR" || { - msg_error "Failed to extract uv" - rm -rf "$TMP_DIR" - return 1 - } - - install -m 755 "$TMP_DIR"/*/uv "$UV_BIN" || { - msg_error "Failed to install uv binary" - rm -rf "$TMP_DIR" - return 1 - } - + ;; + aarch64) + if grep -qi "alpine" /etc/os-release; then + UV_TAR="uv-aarch64-unknown-linux-musl.tar.gz" + else + UV_TAR="uv-aarch64-unknown-linux-gnu.tar.gz" + fi + ;; + *) + msg_error "Unsupported architecture: $ARCH" rm -rf "$TMP_DIR" - ensure_usr_local_bin_persist - export PATH="/usr/local/bin:$PATH" + return 1 + ;; + esac - $STD uv python update-shell || true + ensure_dependencies jq + + local LATEST_VERSION + local releases_json + releases_json=$(curl -fsSL --max-time 15 https://api.github.com/repos/astral-sh/uv/releases/latest 2>/dev/null || echo "") + + if [[ -z "$releases_json" ]]; then + msg_error "Could not fetch latest uv version from GitHub API" + rm -rf "$TMP_DIR" + return 1 + fi + + LATEST_VERSION=$(echo "$releases_json" | jq -r '.tag_name' 2>/dev/null | sed 's/^v//' || echo "") + + if [[ -z "$LATEST_VERSION" ]]; then + msg_error "Could not parse uv version from GitHub API response" + rm -rf "$TMP_DIR" + return 1 + fi + + # Get currently installed version + local INSTALLED_VERSION="" + if [[ -x "$UV_BIN" ]]; then + INSTALLED_VERSION=$($UV_BIN -V 2>/dev/null | awk '{print $2}') + fi + + # Scenario 1: Already at latest version + if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" == "$LATEST_VERSION" ]]; then cache_installed_version "uv" "$LATEST_VERSION" - msg_ok "Setup uv $LATEST_VERSION" + rm -rf "$TMP_DIR" + return 0 + fi - # Optional: Install specific Python version - if [[ -n "${PYTHON_VERSION:-}" ]]; then - local VERSION_MATCH - VERSION_MATCH=$(uv python list --only-downloads 2>/dev/null | - grep -E "^cpython-${PYTHON_VERSION//./\\.}\.[0-9]+-linux" | - cut -d'-' -f2 | sort -V | tail -n1) + # Scenario 2: New install or upgrade + if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" != "$LATEST_VERSION" ]]; then + msg_info "Upgrade uv from $INSTALLED_VERSION to $LATEST_VERSION" + else + msg_info "Setup uv $LATEST_VERSION" + fi - if [[ -z "$VERSION_MATCH" ]]; then - msg_error "No matching Python $PYTHON_VERSION.x version found" - return 1 - fi + local UV_URL="https://github.com/astral-sh/uv/releases/latest/download/${UV_TAR}" + curl -fsSL "$UV_URL" -o "$TMP_DIR/uv.tar.gz" || { + msg_error "Failed to download uv" + rm -rf "$TMP_DIR" + return 1 + } - if ! uv python list 2>/dev/null | grep -q "cpython-${VERSION_MATCH}-linux.*uv/python"; then - $STD uv python install "$VERSION_MATCH" || { - msg_error "Failed to install Python $VERSION_MATCH" - return 1 - } - fi + tar -xzf "$TMP_DIR/uv.tar.gz" -C "$TMP_DIR" || { + msg_error "Failed to extract uv" + rm -rf "$TMP_DIR" + return 1 + } + + install -m 755 "$TMP_DIR"/*/uv "$UV_BIN" || { + msg_error "Failed to install uv binary" + rm -rf "$TMP_DIR" + return 1 + } + + rm -rf "$TMP_DIR" + ensure_usr_local_bin_persist + export PATH="/usr/local/bin:$PATH" + + $STD uv python update-shell || true + cache_installed_version "uv" "$LATEST_VERSION" + msg_ok "Setup uv $LATEST_VERSION" + + # Optional: Install specific Python version + if [[ -n "${PYTHON_VERSION:-}" ]]; then + local VERSION_MATCH + VERSION_MATCH=$(uv python list --only-downloads 2>/dev/null | + grep -E "^cpython-${PYTHON_VERSION//./\\.}\.[0-9]+-linux" | + cut -d'-' -f2 | sort -V | tail -n1) + + if [[ -z "$VERSION_MATCH" ]]; then + msg_error "No matching Python $PYTHON_VERSION.x version found" + return 1 fi + + if ! uv python list 2>/dev/null | grep -q "cpython-${VERSION_MATCH}-linux.*uv/python"; then + $STD uv python install "$VERSION_MATCH" || { + msg_error "Failed to install Python $VERSION_MATCH" + return 1 + } + fi + fi } # ------------------------------------------------------------------------------ @@ -4034,76 +4063,76 @@ function setup_uv() { # ------------------------------------------------------------------------------ function setup_yq() { - local TMP_DIR=$(mktemp -d) - local BINARY_PATH="/usr/local/bin/yq" - local GITHUB_REPO="mikefarah/yq" + local TMP_DIR=$(mktemp -d) + local BINARY_PATH="/usr/local/bin/yq" + local GITHUB_REPO="mikefarah/yq" - ensure_dependencies jq - ensure_usr_local_bin_persist + ensure_dependencies jq + ensure_usr_local_bin_persist - # Remove non-mikefarah implementations - if command -v yq &>/dev/null; then - if ! yq --version 2>&1 | grep -q 'mikefarah'; then - rm -f "$(command -v yq)" - fi + # Remove non-mikefarah implementations + if command -v yq &>/dev/null; then + if ! yq --version 2>&1 | grep -q 'mikefarah'; then + rm -f "$(command -v yq)" fi + fi - local LATEST_VERSION - local releases_json - releases_json=$(curl -fsSL --max-time 15 "https://api.github.com/repos/${GITHUB_REPO}/releases/latest" 2>/dev/null || echo "") - - if [[ -z "$releases_json" ]]; then - msg_error "Could not fetch latest yq version from GitHub API" - rm -rf "$TMP_DIR" - return 1 - fi - - LATEST_VERSION=$(echo "$releases_json" | jq -r '.tag_name' 2>/dev/null | sed 's/^v//' || echo "") - - if [[ -z "$LATEST_VERSION" ]]; then - msg_error "Could not parse yq version from GitHub API response" - rm -rf "$TMP_DIR" - return 1 - fi - - # Get currently installed version - local INSTALLED_VERSION="" - if command -v yq &>/dev/null && yq --version 2>&1 | grep -q 'mikefarah'; then - INSTALLED_VERSION=$(yq --version 2>/dev/null | awk '{print $NF}' | sed 's/^v//') - fi - - # Scenario 1: Already at latest version - if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" == "$LATEST_VERSION" ]]; then - cache_installed_version "yq" "$LATEST_VERSION" - rm -rf "$TMP_DIR" - return 0 - fi - - # Scenario 2: New install or upgrade - if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" != "$LATEST_VERSION" ]]; then - msg_info "Upgrade yq from $INSTALLED_VERSION to $LATEST_VERSION" - else - msg_info "Setup yq $LATEST_VERSION" - fi - - curl -fsSL "https://github.com/${GITHUB_REPO}/releases/download/v${LATEST_VERSION}/yq_linux_amd64" -o "$TMP_DIR/yq" || { - msg_error "Failed to download yq" - rm -rf "$TMP_DIR" - return 1 - } - - chmod +x "$TMP_DIR/yq" - mv "$TMP_DIR/yq" "$BINARY_PATH" || { - msg_error "Failed to install yq" - rm -rf "$TMP_DIR" - return 1 - } + local LATEST_VERSION + local releases_json + releases_json=$(curl -fsSL --max-time 15 "https://api.github.com/repos/${GITHUB_REPO}/releases/latest" 2>/dev/null || echo "") + if [[ -z "$releases_json" ]]; then + msg_error "Could not fetch latest yq version from GitHub API" rm -rf "$TMP_DIR" - hash -r + return 1 + fi - local FINAL_VERSION - FINAL_VERSION=$("$BINARY_PATH" --version 2>/dev/null | awk '{print $NF}' | sed 's/^v//') - cache_installed_version "yq" "$FINAL_VERSION" - msg_ok "Setup yq $FINAL_VERSION" + LATEST_VERSION=$(echo "$releases_json" | jq -r '.tag_name' 2>/dev/null | sed 's/^v//' || echo "") + + if [[ -z "$LATEST_VERSION" ]]; then + msg_error "Could not parse yq version from GitHub API response" + rm -rf "$TMP_DIR" + return 1 + fi + + # Get currently installed version + local INSTALLED_VERSION="" + if command -v yq &>/dev/null && yq --version 2>&1 | grep -q 'mikefarah'; then + INSTALLED_VERSION=$(yq --version 2>/dev/null | awk '{print $NF}' | sed 's/^v//') + fi + + # Scenario 1: Already at latest version + if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" == "$LATEST_VERSION" ]]; then + cache_installed_version "yq" "$LATEST_VERSION" + rm -rf "$TMP_DIR" + return 0 + fi + + # Scenario 2: New install or upgrade + if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" != "$LATEST_VERSION" ]]; then + msg_info "Upgrade yq from $INSTALLED_VERSION to $LATEST_VERSION" + else + msg_info "Setup yq $LATEST_VERSION" + fi + + curl -fsSL "https://github.com/${GITHUB_REPO}/releases/download/v${LATEST_VERSION}/yq_linux_amd64" -o "$TMP_DIR/yq" || { + msg_error "Failed to download yq" + rm -rf "$TMP_DIR" + return 1 + } + + chmod +x "$TMP_DIR/yq" + mv "$TMP_DIR/yq" "$BINARY_PATH" || { + msg_error "Failed to install yq" + rm -rf "$TMP_DIR" + return 1 + } + + rm -rf "$TMP_DIR" + hash -r + + local FINAL_VERSION + FINAL_VERSION=$("$BINARY_PATH" --version 2>/dev/null | awk '{print $NF}' | sed 's/^v//') + cache_installed_version "yq" "$FINAL_VERSION" + msg_ok "Setup yq $FINAL_VERSION" } From 03bf6dadf157d5777740eb83aa9c278c6d20f5d5 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 4 Nov 2025 13:24:44 +0100 Subject: [PATCH 1674/1733] Enhance cleanup of keyrings and repo configs for tools Expanded the removal of GPG keyrings and repository configuration files for MariaDB, MySQL, MongoDB, Node.js, PHP, PostgreSQL, Java (Adoptium), and ClickHouse in both removal and setup functions. This ensures all possible keyring locations are cleaned before new installations, reducing risk of conflicts and improving idempotency. Also improved PHP-FPM service cleanup and added version verification for MongoDB setup. --- misc/tools.func | 152 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 128 insertions(+), 24 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index e4a62e13b..252320170 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -112,55 +112,101 @@ remove_old_tool_version() { mariadb) $STD systemctl stop mariadb >/dev/null 2>&1 || true $STD apt purge -y 'mariadb*' >/dev/null 2>&1 || true + # Clean up ALL keyring locations + rm -f /usr/share/keyrings/mariadb*.gpg \ + /etc/apt/keyrings/mariadb*.gpg \ + /etc/apt/trusted.gpg.d/mariadb*.gpg 2>/dev/null || true ;; mysql) $STD systemctl stop mysql >/dev/null 2>&1 || true $STD apt purge -y 'mysql*' >/dev/null 2>&1 || true - rm -rf /var/lib/mysql >/dev/null 2>&1 || true + rm -rf /var/lib/mysql 2>/dev/null || true + # Clean up ALL keyring locations + rm -f /usr/share/keyrings/mysql*.gpg \ + /etc/apt/keyrings/mysql*.gpg \ + /etc/apt/trusted.gpg.d/mysql*.gpg 2>/dev/null || true ;; mongodb) $STD systemctl stop mongod >/dev/null 2>&1 || true $STD apt purge -y 'mongodb*' >/dev/null 2>&1 || true - rm -rf /var/lib/mongodb >/dev/null 2>&1 || true + rm -rf /var/lib/mongodb 2>/dev/null || true + # Clean up ALL keyring locations + rm -f /usr/share/keyrings/mongodb*.gpg \ + /etc/apt/keyrings/mongodb*.gpg \ + /etc/apt/trusted.gpg.d/mongodb*.gpg 2>/dev/null || true ;; node | nodejs) $STD apt purge -y nodejs npm >/dev/null 2>&1 || true - npm list -g 2>/dev/null | grep -oE '^ \S+' | awk '{print $1}' | while read -r module; do - npm uninstall -g "$module" >/dev/null 2>&1 || true - done + # Clean up npm global modules + if command -v npm >/dev/null 2>&1; then + npm list -g 2>/dev/null | grep -oE '^ \S+' | awk '{print $1}' | while read -r module; do + npm uninstall -g "$module" >/dev/null 2>&1 || true + done + fi + # Clean up nvm installations and npm caches + rm -rf "$HOME/.nvm" "$HOME/.npm" "$HOME/.bower" "$HOME/.config/yarn" 2>/dev/null || true + sed -i '/NVM_DIR/d' "$HOME/.bashrc" "$HOME/.profile" 2>/dev/null || true + # Clean up ALL keyring locations + rm -f /usr/share/keyrings/nodesource*.gpg \ + /etc/apt/keyrings/nodesource*.gpg \ + /etc/apt/trusted.gpg.d/nodesource*.gpg 2>/dev/null || true ;; php) - # Disable PHP-FPM if running - $STD systemctl disable php*-fpm >/dev/null 2>&1 || true - $STD systemctl stop php*-fpm >/dev/null 2>&1 || true + # Stop and disable ALL PHP-FPM versions + for fpm_service in $(systemctl list-units --type=service --all | grep -oE 'php[0-9]+\.[0-9]+-fpm' | sort -u); do + $STD systemctl stop "$fpm_service" >/dev/null 2>&1 || true + $STD systemctl disable "$fpm_service" >/dev/null 2>&1 || true + done $STD apt purge -y 'php*' >/dev/null 2>&1 || true - rm -rf /etc/php >/dev/null 2>&1 || true + rm -rf /etc/php 2>/dev/null || true + # Clean up ALL keyring locations (Sury PHP) + rm -f /usr/share/keyrings/deb.sury.org-php.gpg \ + /usr/share/keyrings/php*.gpg \ + /etc/apt/keyrings/php*.gpg \ + /etc/apt/trusted.gpg.d/php*.gpg 2>/dev/null || true ;; postgresql) $STD systemctl stop postgresql >/dev/null 2>&1 || true $STD apt purge -y 'postgresql*' >/dev/null 2>&1 || true - rm -rf /var/lib/postgresql >/dev/null 2>&1 || true + # Keep data directory for safety (can be removed manually if needed) + # rm -rf /var/lib/postgresql 2>/dev/null || true + # Clean up ALL keyring locations + rm -f /usr/share/keyrings/postgresql*.gpg \ + /usr/share/keyrings/pgdg*.gpg \ + /etc/apt/keyrings/postgresql*.gpg \ + /etc/apt/keyrings/pgdg*.gpg \ + /etc/apt/trusted.gpg.d/postgresql*.gpg \ + /etc/apt/trusted.gpg.d/pgdg*.gpg 2>/dev/null || true + ;; + java) + $STD apt purge -y 'temurin*' 'adoptium*' 'openjdk*' >/dev/null 2>&1 || true + # Clean up ALL keyring locations (Adoptium) + rm -f /usr/share/keyrings/adoptium*.gpg \ + /etc/apt/keyrings/adoptium*.gpg \ + /etc/apt/trusted.gpg.d/adoptium*.gpg 2>/dev/null || true ;; ruby) - if [[ -d "$HOME/.rbenv" ]]; then - rm -rf "$HOME/.rbenv" - fi + rm -rf "$HOME/.rbenv" 2>/dev/null || true $STD apt purge -y 'ruby*' >/dev/null 2>&1 || true ;; rust) - rm -rf "$HOME/.cargo" "$HOME/.rustup" >/dev/null 2>&1 || true + rm -rf "$HOME/.cargo" "$HOME/.rustup" 2>/dev/null || true ;; go | golang) - rm -rf /usr/local/go >/dev/null 2>&1 || true + rm -rf /usr/local/go 2>/dev/null || true ;; clickhouse) $STD systemctl stop clickhouse-server >/dev/null 2>&1 || true $STD apt purge -y 'clickhouse*' >/dev/null 2>&1 || true - rm -rf /var/lib/clickhouse >/dev/null 2>&1 || true + rm -rf /var/lib/clickhouse 2>/dev/null || true + # Clean up ALL keyring locations + rm -f /usr/share/keyrings/clickhouse*.gpg \ + /etc/apt/keyrings/clickhouse*.gpg \ + /etc/apt/trusted.gpg.d/clickhouse*.gpg 2>/dev/null || true ;; esac - # Clean up old repositories + # Clean up old repository files (both .list and .sources) cleanup_old_repo_files "$repo_name" return 0 @@ -2515,9 +2561,14 @@ function setup_java() { DISTRO_CODENAME=$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release) local DESIRED_PACKAGE="temurin-${JAVA_VERSION}-jdk" + # Clean up ALL old Adoptium repo configs and keyrings before setup + cleanup_old_repo_files "adoptium" + rm -f /usr/share/keyrings/adoptium*.gpg \ + /etc/apt/keyrings/adoptium*.gpg \ + /etc/apt/trusted.gpg.d/adoptium*.gpg 2>/dev/null || true + # Add repo if needed if [[ ! -f /etc/apt/sources.list.d/adoptium.sources ]]; then - cleanup_old_repo_files "adoptium" local SUITE SUITE=$(get_fallback_suite "$DISTRO_ID" "$DISTRO_CODENAME" "https://packages.adoptium.net/artifactory/deb") setup_deb822_repo \ @@ -2761,6 +2812,12 @@ setup_mariadb() { # Scenario 3: Fresh install or version change msg_info "Setup MariaDB $MARIADB_VERSION" + # Clean up ALL old MariaDB repo configs and keyrings before setup + cleanup_old_repo_files "mariadb" + rm -f /usr/share/keyrings/mariadb*.gpg \ + /etc/apt/keyrings/mariadb*.gpg \ + /etc/apt/trusted.gpg.d/mariadb*.gpg 2>/dev/null || true + # Ensure APT is working before proceeding ensure_apt_working || return 1 @@ -2877,6 +2934,12 @@ function setup_mongodb() { cleanup_orphaned_sources + # Clean up ALL old MongoDB repo configs and keyrings before setup + cleanup_old_repo_files "mongodb" + rm -f /usr/share/keyrings/mongodb*.gpg \ + /etc/apt/keyrings/mongodb*.gpg \ + /etc/apt/trusted.gpg.d/mongodb*.gpg 2>/dev/null || true + # Setup repository manage_tool_repository "mongodb" "$MONGO_VERSION" "$MONGO_BASE_URL" \ "https://www.mongodb.org/static/pgp/server-${MONGO_VERSION}.asc" || { @@ -2896,6 +2959,12 @@ function setup_mongodb() { return 1 } + # Verify MongoDB was installed correctly + if ! command -v mongod >/dev/null 2>&1; then + msg_error "MongoDB binary not found after installation" + return 1 + fi + mkdir -p /var/lib/mongodb chown -R mongodb:mongodb /var/lib/mongodb @@ -2903,8 +2972,15 @@ function setup_mongodb() { msg_warn "Failed to enable mongod service" } safe_service_restart mongod - cache_installed_version "mongodb" "$MONGO_VERSION" + # Verify MongoDB version + local INSTALLED_VERSION + INSTALLED_VERSION=$(mongod --version 2>/dev/null | grep -oP 'db version v\K[0-9]+\.[0-9]+' | head -n1 || echo "0.0") + if [[ "${INSTALLED_VERSION%%.*}" != "${MONGO_VERSION%%.*}" ]]; then + msg_warn "MongoDB version mismatch: expected $MONGO_VERSION, got $INSTALLED_VERSION" + fi + + cache_installed_version "mongodb" "$MONGO_VERSION" msg_ok "Setup MongoDB $MONGO_VERSION" } @@ -2952,12 +3028,16 @@ function setup_mysql() { msg_info "Setup MySQL $MYSQL_VERSION" fi + # Clean up ALL old MySQL repo configs and keyrings before setup + cleanup_old_repo_files "mysql" + rm -f /usr/share/keyrings/mysql*.gpg \ + /etc/apt/keyrings/mysql*.gpg \ + /etc/apt/trusted.gpg.d/mysql*.gpg 2>/dev/null || true + # Debian 13+ Fix: MySQL 8.0 incompatible with libaio1t64, use 8.4 LTS if [[ "$DISTRO_ID" == "debian" && "$DISTRO_CODENAME" =~ ^(trixie|forky|sid)$ ]]; then msg_info "Debian ${DISTRO_CODENAME} detected → using MySQL 8.4 LTS (libaio1t64 compatible)" - cleanup_old_repo_files "mysql" - if ! curl -fsSL https://repo.mysql.com/RPM-GPG-KEY-mysql-2023 | gpg --dearmor -o /etc/apt/keyrings/mysql.gpg 2>/dev/null; then msg_error "Failed to import MySQL GPG key" return 1 @@ -3300,14 +3380,23 @@ function setup_php() { # Scenario 2: Different version installed - clean upgrade if [[ -n "$CURRENT_PHP" && "$CURRENT_PHP" != "$PHP_VERSION" ]]; then msg_info "Upgrade PHP from $CURRENT_PHP to $PHP_VERSION" - # Stop old PHP-FPM if running - $STD systemctl stop "php${CURRENT_PHP}-fpm" >/dev/null 2>&1 || true - $STD systemctl disable "php${CURRENT_PHP}-fpm" >/dev/null 2>&1 || true + # Stop and disable ALL PHP-FPM versions (not just current one) + for fpm_service in $(systemctl list-units --type=service --all 2>/dev/null | grep -oE 'php[0-9]+\.[0-9]+-fpm' | sort -u); do + $STD systemctl stop "$fpm_service" >/dev/null 2>&1 || true + $STD systemctl disable "$fpm_service" >/dev/null 2>&1 || true + done remove_old_tool_version "php" else msg_info "Setup PHP $PHP_VERSION" fi + # Clean up ALL old PHP repo configs and keyrings before setup + cleanup_old_repo_files "php" + rm -f /usr/share/keyrings/deb.sury.org-php.gpg \ + /usr/share/keyrings/php*.gpg \ + /etc/apt/keyrings/php*.gpg \ + /etc/apt/trusted.gpg.d/php*.gpg 2>/dev/null || true + # Setup Sury repository manage_tool_repository "php" "$PHP_VERSION" "" "https://packages.sury.org/debsuryorg-archive-keyring.deb" || { msg_error "Failed to setup PHP repository" @@ -3441,6 +3530,15 @@ function setup_postgresql() { # Scenario 3: Fresh install or after removal - setup repo and install cleanup_old_repo_files "pgdg" + cleanup_old_repo_files "postgresql" + + # Clean up ALL old PostgreSQL repo configs and keyrings before setup + rm -f /usr/share/keyrings/postgresql*.gpg \ + /usr/share/keyrings/pgdg*.gpg \ + /etc/apt/keyrings/postgresql*.gpg \ + /etc/apt/keyrings/pgdg*.gpg \ + /etc/apt/trusted.gpg.d/postgresql*.gpg \ + /etc/apt/trusted.gpg.d/pgdg*.gpg 2>/dev/null || true local SUITE case "$DISTRO_CODENAME" in @@ -3798,6 +3896,12 @@ function setup_clickhouse() { ensure_dependencies apt-transport-https ca-certificates dirmngr gnupg + # Clean up ALL old ClickHouse repo configs and keyrings before setup + cleanup_old_repo_files "clickhouse" + rm -f /usr/share/keyrings/clickhouse*.gpg \ + /etc/apt/keyrings/clickhouse*.gpg \ + /etc/apt/trusted.gpg.d/clickhouse*.gpg 2>/dev/null || true + # Setup repository (ClickHouse uses 'stable' suite) setup_deb822_repo \ "clickhouse" \ From 08724c6b555a0c13d3aa3a606e6ded5deb72a52b Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Tue, 4 Nov 2025 13:43:13 +0100 Subject: [PATCH 1675/1733] add: nginxproxymanager --- install/nginxproxymanager-install.sh | 193 +++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 install/nginxproxymanager-install.sh diff --git a/install/nginxproxymanager-install.sh b/install/nginxproxymanager-install.sh new file mode 100644 index 000000000..cf06b6c85 --- /dev/null +++ b/install/nginxproxymanager-install.sh @@ -0,0 +1,193 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://nginxproxymanager.com/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt update +$STD apt -y install \ + ca-certificates \ + apache2-utils \ + logrotate \ + build-essential \ + git +msg_ok "Installed Dependencies" + +msg_info "Installing Python Dependencies" +$STD apt install -y \ + python3 \ + python3-dev \ + python3-pip \ + python3-venv \ + python3-cffi +msg_ok "Installed Python Dependencies" + +msg_info "Setting up Certbot" +$STD python3 -m venv /opt/certbot +$STD /opt/certbot/bin/pip install --upgrade pip setuptools wheel +$STD /opt/certbot/bin/pip install certbot certbot-dns-cloudflare +ln -sf /opt/certbot/bin/certbot /usr/local/bin/certbot +msg_ok "Set up Certbot" + +VERSION="$(awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release)" + +msg_info "Installing Openresty" +curl -fsSL "https://openresty.org/package/pubkey.gpg" | gpg --dearmor -o /etc/apt/trusted.gpg.d/openresty-archive-keyring.gpg +case "$VERSION" in +trixie) + echo -e "deb http://openresty.org/package/debian bookworm openresty" >/etc/apt/sources.list.d/openresty.list + ;; +*) + echo -e "deb http://openresty.org/package/debian $VERSION openresty" >/etc/apt/sources.list.d/openresty.list + ;; +esac +$STD apt update +$STD apt -y install openresty +msg_ok "Installed Openresty" + +NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs + +RELEASE=$(curl -fsSL https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/releases/latest | + grep "tag_name" | + awk '{print substr($2, 3, length($2)-4) }') + +################## +RELEASE="2.13" +################## + + +msg_info "Downloading Nginx Proxy Manager v${RELEASE}" +curl -fsSL "https://codeload.github.com/NginxProxyManager/nginx-proxy-manager/tar.gz/v${RELEASE}" | tar -xz +cd ./nginx-proxy-manager-"${RELEASE}" +msg_ok "Downloaded Nginx Proxy Manager v${RELEASE}" + +msg_info "Setting up Environment" +ln -sf /usr/bin/python3 /usr/bin/python +ln -sf /usr/local/openresty/nginx/sbin/nginx /usr/sbin/nginx +ln -sf /usr/local/openresty/nginx/ /etc/nginx +sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" backend/package.json +sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" frontend/package.json +sed -i 's+^daemon+#daemon+g' docker/rootfs/etc/nginx/nginx.conf +NGINX_CONFS=$(find "$(pwd)" -type f -name "*.conf") +for NGINX_CONF in $NGINX_CONFS; do + sed -i 's+include conf.d+include /etc/nginx/conf.d+g' "$NGINX_CONF" +done + +mkdir -p /var/www/html /etc/nginx/logs +cp -r docker/rootfs/var/www/html/* /var/www/html/ +cp -r docker/rootfs/etc/nginx/* /etc/nginx/ +cp docker/rootfs/etc/letsencrypt.ini /etc/letsencrypt.ini +cp docker/rootfs/etc/logrotate.d/nginx-proxy-manager /etc/logrotate.d/nginx-proxy-manager +ln -sf /etc/nginx/nginx.conf /etc/nginx/conf/nginx.conf +rm -f /etc/nginx/conf.d/dev.conf + +mkdir -p /tmp/nginx/body \ + /run/nginx \ + /data/nginx \ + /data/custom_ssl \ + /data/logs \ + /data/access \ + /data/nginx/default_host \ + /data/nginx/default_www \ + /data/nginx/proxy_host \ + /data/nginx/redirection_host \ + /data/nginx/stream \ + /data/nginx/dead_host \ + /data/nginx/temp \ + /var/lib/nginx/cache/public \ + /var/lib/nginx/cache/private \ + /var/cache/nginx/proxy_temp + +chmod -R 777 /var/cache/nginx +chown root /tmp/nginx + +echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" {print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf);" >/etc/nginx/conf.d/include/resolvers.conf + +if [ ! -f /data/nginx/dummycert.pem ] || [ ! -f /data/nginx/dummykey.pem ]; then + openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 -subj "/O=Nginx Proxy Manager/OU=Dummy Certificate/CN=localhost" -keyout /data/nginx/dummykey.pem -out /data/nginx/dummycert.pem &>/dev/null +fi + +mkdir -p /app/global /app/frontend/images +cp -r backend/* /app +cp -r global/* /app/global +msg_ok "Set up Environment" + +msg_info "Building Frontend" +cd ./frontend +# Replace node-sass with sass in package.json before installation +sed -i 's/"node-sass".*$/"sass": "^1.92.1",/g' package.json +$STD yarn install --network-timeout 600000 +$STD yarn build +cp -r dist/* /app/frontend +cp -r app-images/* /app/frontend/images +msg_ok "Built Frontend" + +msg_info "Initializing Backend" +rm -rf /app/config/default.json +if [ ! -f /app/config/production.json ]; then + cat <<'EOF' >/app/config/production.json +{ + "database": { + "engine": "knex-native", + "knex": { + "client": "sqlite3", + "connection": { + "filename": "/data/database.sqlite" + } + } + } +} +EOF +fi +cd /app +# export NODE_OPTIONS="--openssl-legacy-provider" +$STD yarn install --network-timeout 600000 +msg_ok "Initialized Backend" + +msg_info "Creating Service" +cat <<'EOF' >/lib/systemd/system/npm.service +[Unit] +Description=Nginx Proxy Manager +After=network.target +Wants=openresty.service + +[Service] +Type=simple +Environment=NODE_ENV=production +ExecStartPre=-mkdir -p /tmp/nginx/body /data/letsencrypt-acme-challenge +ExecStart=/usr/bin/node index.js --abort_on_uncaught_exception --max_old_space_size=250 +WorkingDirectory=/app +Restart=on-failure + +[Install] +WantedBy=multi-user.target +EOF +msg_ok "Created Service" + +motd_ssh +customize + +msg_info "Starting Services" +sed -i 's/user npm/user root/g; s/^pid/#pid/g' /usr/local/openresty/nginx/conf/nginx.conf +sed -r -i 's/^([[:space:]]*)su npm npm/\1#su npm npm/g;' /etc/logrotate.d/nginx-proxy-manager +systemctl enable -q --now openresty +systemctl enable -q --now npm +msg_ok "Started Services" + +msg_info "Cleaning up" +rm -rf ../nginx-proxy-manager-* +systemctl restart openresty +$STD apt -y autoremove +$STD apt -y autoclean +$STD apt -y clean +msg_ok "Cleaned" From 18b4d92c06d476ec124b697bcaaadb16a94532ee Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Tue, 4 Nov 2025 13:45:33 +0100 Subject: [PATCH 1676/1733] Create nginxproxymanager.sh --- ct/nginxproxymanager.sh | 184 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 ct/nginxproxymanager.sh diff --git a/ct/nginxproxymanager.sh b/ct/nginxproxymanager.sh new file mode 100644 index 000000000..1dbc9e91f --- /dev/null +++ b/ct/nginxproxymanager.sh @@ -0,0 +1,184 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://nginxproxymanager.com/ + +APP="Nginx Proxy Manager" +var_tags="${var_tags:-proxy}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-1024}" +var_disk="${var_disk:-4}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -f /lib/systemd/system/npm.service ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if command -v node &>/dev/null; then + CURRENT_NODE_VERSION=$(node --version | cut -d'v' -f2 | cut -d'.' -f1) + if [[ "$CURRENT_NODE_VERSION" != "22" ]]; then + systemctl stop openresty + apt-get purge -y nodejs npm + apt-get autoremove -y + rm -rf /usr/local/bin/node /usr/local/bin/npm + rm -rf /usr/local/lib/node_modules + rm -rf ~/.npm + rm -rf /root/.npm + fi + fi + + NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs + # export NODE_OPTIONS="--openssl-legacy-provider" + + RELEASE=$(curl -fsSL https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/releases/latest | + grep "tag_name" | + awk '{print substr($2, 3, length($2)-4) }') + + ################ + RELEASE="2.13" + ################ + + + msg_info "Downloading NPM v${RELEASE}" + curl -fsSL "https://codeload.github.com/NginxProxyManager/nginx-proxy-manager/tar.gz/v${RELEASE}" | tar -xz + cd nginx-proxy-manager-"${RELEASE}" || exit + msg_ok "Downloaded NPM v${RELEASE}" + + msg_info "Building Frontend" + ( + sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" backend/package.json + sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" frontend/package.json + cd ./frontend || exit + # Replace node-sass with sass in package.json before installation + sed -i 's/"node-sass".*$/"sass": "^1.92.1",/g' package.json + $STD yarn install --network-timeout 600000 + $STD yarn build + ) + msg_ok "Built Frontend" + + msg_info "Stopping Services" + systemctl stop openresty + systemctl stop npm + msg_ok "Stopped Services" + + msg_info "Cleaning Old Files" + rm -rf /app \ + /var/www/html \ + /etc/nginx \ + /var/log/nginx \ + /var/lib/nginx \ + "$STD" /var/cache/nginx + msg_ok "Cleaned Old Files" + + msg_info "Setting up Environment" + ln -sf /usr/bin/python3 /usr/bin/python + ln -sf /opt/certbot/bin/certbot /usr/local/bin/certbot + ln -sf /usr/local/openresty/nginx/sbin/nginx /usr/sbin/nginx + ln -sf /usr/local/openresty/nginx/ /etc/nginx + sed -i 's+^daemon+#daemon+g' docker/rootfs/etc/nginx/nginx.conf + NGINX_CONFS=$(find "$(pwd)" -type f -name "*.conf") + for NGINX_CONF in $NGINX_CONFS; do + sed -i 's+include conf.d+include /etc/nginx/conf.d+g' "$NGINX_CONF" + done + mkdir -p /var/www/html /etc/nginx/logs + cp -r docker/rootfs/var/www/html/* /var/www/html/ + cp -r docker/rootfs/etc/nginx/* /etc/nginx/ + cp docker/rootfs/etc/letsencrypt.ini /etc/letsencrypt.ini + cp docker/rootfs/etc/logrotate.d/nginx-proxy-manager /etc/logrotate.d/nginx-proxy-manager + ln -sf /etc/nginx/nginx.conf /etc/nginx/conf/nginx.conf + rm -f /etc/nginx/conf.d/dev.conf + mkdir -p /tmp/nginx/body \ + /run/nginx \ + /data/nginx \ + /data/custom_ssl \ + /data/logs \ + /data/access \ + /data/nginx/default_host \ + /data/nginx/default_www \ + /data/nginx/proxy_host \ + /data/nginx/redirection_host \ + /data/nginx/stream \ + /data/nginx/dead_host \ + /data/nginx/temp \ + /var/lib/nginx/cache/public \ + /var/lib/nginx/cache/private \ + /var/cache/nginx/proxy_temp + chmod -R 777 /var/cache/nginx + chown root /tmp/nginx + echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" {print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf);" >/etc/nginx/conf.d/include/resolvers.conf + if [ ! -f /data/nginx/dummycert.pem ] || [ ! -f /data/nginx/dummykey.pem ]; then + $STD openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 -subj "/O=Nginx Proxy Manager/OU=Dummy Certificate/CN=localhost" -keyout /data/nginx/dummykey.pem -out /data/nginx/dummycert.pem + fi + mkdir -p /app/global /app/frontend/images + cp -r frontend/dist/* /app/frontend + cp -r frontend/app-images/* /app/frontend/images + cp -r backend/* /app + cp -r global/* /app/global + + # Update Certbot and plugins in virtual environment + if [ -d /opt/certbot ]; then + $STD /opt/certbot/bin/pip install --upgrade pip setuptools wheel + $STD /opt/certbot/bin/pip install --upgrade certbot certbot-dns-cloudflare + fi + msg_ok "Setup Environment" + + msg_info "Initializing Backend" + $STD rm -rf /app/config/default.json + if [ ! -f /app/config/production.json ]; then + cat <<'EOF' >/app/config/production.json +{ + "database": { + "engine": "knex-native", + "knex": { + "client": "sqlite3", + "connection": { + "filename": "/data/database.sqlite" + } + } + } +} +EOF + fi + cd /app || exit + export NODE_OPTIONS="--openssl-legacy-provider" + $STD yarn install --network-timeout 600000 + msg_ok "Initialized Backend" + + msg_info "Starting Services" + sed -i 's/user npm/user root/g; s/^pid/#pid/g' /usr/local/openresty/nginx/conf/nginx.conf + sed -i 's/su npm npm/su root root/g' /etc/logrotate.d/nginx-proxy-manager + sed -i 's/include-system-site-packages = false/include-system-site-packages = true/g' /opt/certbot/pyvenv.cfg + systemctl enable -q --now openresty + systemctl enable -q --now npm + msg_ok "Started Services" + + msg_info "Cleaning up" + rm -rf ~/nginx-proxy-manager-* + msg_ok "Cleaned" + + msg_ok "Updated successfully!" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:81${CL}" From 3f7f39abe29a529e828f57dcb31a7c397119b5c5 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 4 Nov 2025 13:49:51 +0100 Subject: [PATCH 1677/1733] Refactor tool setup: unify cleanup, retries, and validation Introduces unified helper functions for cleaning up keyrings, stopping services, verifying tool versions, and cleaning legacy installs. Adds a retry mechanism for package installation and a repository preparation function to streamline setup and error handling. Refactors all tool setup and removal logic to use these helpers, reducing code duplication and improving maintainability. --- misc/tools.func | 373 +++++++++++++++++++++++++++++------------------- 1 file changed, 225 insertions(+), 148 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 252320170..f50975178 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -24,6 +24,147 @@ get_cached_version() { return 0 } +# ------------------------------------------------------------------------------ +# Clean up ALL keyring locations for a tool (unified helper) +# Usage: cleanup_tool_keyrings "mariadb" "mysql" "postgresql" +# ------------------------------------------------------------------------------ +cleanup_tool_keyrings() { + local tool_patterns=("$@") + + for pattern in "${tool_patterns[@]}"; do + rm -f /usr/share/keyrings/${pattern}*.gpg \ + /etc/apt/keyrings/${pattern}*.gpg \ + /etc/apt/trusted.gpg.d/${pattern}*.gpg 2>/dev/null || true + done +} + +# ------------------------------------------------------------------------------ +# Stop and disable all service instances matching a pattern +# Usage: stop_all_services "php*-fpm" "mysql" "mariadb" +# ------------------------------------------------------------------------------ +stop_all_services() { + local service_patterns=("$@") + + for pattern in "${service_patterns[@]}"; do + # Find all matching services + systemctl list-units --type=service --all 2>/dev/null | + grep -oE "${pattern}[^ ]*\.service" | + sort -u | + while read -r service; do + $STD systemctl stop "$service" 2>/dev/null || true + $STD systemctl disable "$service" 2>/dev/null || true + done + done +} + +# ------------------------------------------------------------------------------ +# Verify installed tool version matches expected version +# Returns: 0 if match, 1 if mismatch (with warning) +# Usage: verify_tool_version "nodejs" "22" "$(node -v | grep -oP '^v\K[0-9]+')" +# ------------------------------------------------------------------------------ +verify_tool_version() { + local tool_name="$1" + local expected_version="$2" + local installed_version="$3" + + # Extract major version for comparison + local expected_major="${expected_version%%.*}" + local installed_major="${installed_version%%.*}" + + if [[ "$installed_major" != "$expected_major" ]]; then + msg_warn "$tool_name version mismatch: expected $expected_version, got $installed_version" + return 1 + fi + + return 0 +} + +# ------------------------------------------------------------------------------ +# Clean up legacy installation methods (nvm, rbenv, rustup, etc.) +# Usage: cleanup_legacy_install "nodejs" -> removes nvm +# ------------------------------------------------------------------------------ +cleanup_legacy_install() { + local tool_name="$1" + + case "$tool_name" in + nodejs | node) + if [[ -d "$HOME/.nvm" ]]; then + msg_info "Removing legacy nvm installation" + rm -rf "$HOME/.nvm" "$HOME/.npm" "$HOME/.bower" "$HOME/.config/yarn" 2>/dev/null || true + sed -i '/NVM_DIR/d' "$HOME/.bashrc" "$HOME/.profile" 2>/dev/null || true + fi + ;; + ruby) + if [[ -d "$HOME/.rbenv" ]]; then + msg_info "Removing legacy rbenv installation" + rm -rf "$HOME/.rbenv" 2>/dev/null || true + sed -i '/rbenv/d' "$HOME/.bashrc" "$HOME/.profile" 2>/dev/null || true + fi + ;; + rust) + if [[ -d "$HOME/.cargo" ]] || [[ -d "$HOME/.rustup" ]]; then + msg_info "Removing legacy rustup installation" + rm -rf "$HOME/.cargo" "$HOME/.rustup" 2>/dev/null || true + sed -i '/cargo/d' "$HOME/.bashrc" "$HOME/.profile" 2>/dev/null || true + fi + ;; + go | golang) + if [[ -d "$HOME/go" ]]; then + msg_info "Removing legacy Go workspace" + # Keep user code, just remove GOPATH env + sed -i '/GOPATH/d' "$HOME/.bashrc" "$HOME/.profile" 2>/dev/null || true + fi + ;; + esac +} + +# ------------------------------------------------------------------------------ +# Unified repository preparation before setup +# Cleans up old repos, keyrings, and ensures APT is working +# Usage: prepare_repository_setup "mariadb" "mysql" +# ------------------------------------------------------------------------------ +prepare_repository_setup() { + local repo_names=("$@") + + # Clean up all old repository files + for repo in "${repo_names[@]}"; do + cleanup_old_repo_files "$repo" + done + + # Clean up all keyrings + cleanup_tool_keyrings "${repo_names[@]}" + + # Ensure APT is in working state + ensure_apt_working || return 1 + + return 0 +} + +# ------------------------------------------------------------------------------ +# Install packages with retry logic +# Usage: install_packages_with_retry "mysql-server" "mysql-client" +# ------------------------------------------------------------------------------ +install_packages_with_retry() { + local packages=("$@") + local max_retries=2 + local retry=0 + + while [[ $retry -le $max_retries ]]; do + if $STD apt install -y "${packages[@]}" 2>/dev/null; then + return 0 + fi + + retry=$((retry + 1)) + if [[ $retry -le $max_retries ]]; then + msg_warn "Package installation failed, retrying ($retry/$max_retries)..." + sleep 2 + $STD apt update 2>/dev/null || true + fi + done + + return 1 +} + # ------------------------------------------------------------------------------ # Check if tool is already installed and optionally verify exact version # Returns: 0 if installed (with optional version match), 1 if not installed @@ -110,30 +251,21 @@ remove_old_tool_version() { case "$tool_name" in mariadb) - $STD systemctl stop mariadb >/dev/null 2>&1 || true + stop_all_services "mariadb" $STD apt purge -y 'mariadb*' >/dev/null 2>&1 || true - # Clean up ALL keyring locations - rm -f /usr/share/keyrings/mariadb*.gpg \ - /etc/apt/keyrings/mariadb*.gpg \ - /etc/apt/trusted.gpg.d/mariadb*.gpg 2>/dev/null || true + cleanup_tool_keyrings "mariadb" ;; mysql) - $STD systemctl stop mysql >/dev/null 2>&1 || true + stop_all_services "mysql" $STD apt purge -y 'mysql*' >/dev/null 2>&1 || true rm -rf /var/lib/mysql 2>/dev/null || true - # Clean up ALL keyring locations - rm -f /usr/share/keyrings/mysql*.gpg \ - /etc/apt/keyrings/mysql*.gpg \ - /etc/apt/trusted.gpg.d/mysql*.gpg 2>/dev/null || true + cleanup_tool_keyrings "mysql" ;; mongodb) - $STD systemctl stop mongod >/dev/null 2>&1 || true + stop_all_services "mongod" $STD apt purge -y 'mongodb*' >/dev/null 2>&1 || true rm -rf /var/lib/mongodb 2>/dev/null || true - # Clean up ALL keyring locations - rm -f /usr/share/keyrings/mongodb*.gpg \ - /etc/apt/keyrings/mongodb*.gpg \ - /etc/apt/trusted.gpg.d/mongodb*.gpg 2>/dev/null || true + cleanup_tool_keyrings "mongodb" ;; node | nodejs) $STD apt purge -y nodejs npm >/dev/null 2>&1 || true @@ -143,66 +275,42 @@ remove_old_tool_version() { npm uninstall -g "$module" >/dev/null 2>&1 || true done fi - # Clean up nvm installations and npm caches - rm -rf "$HOME/.nvm" "$HOME/.npm" "$HOME/.bower" "$HOME/.config/yarn" 2>/dev/null || true - sed -i '/NVM_DIR/d' "$HOME/.bashrc" "$HOME/.profile" 2>/dev/null || true - # Clean up ALL keyring locations - rm -f /usr/share/keyrings/nodesource*.gpg \ - /etc/apt/keyrings/nodesource*.gpg \ - /etc/apt/trusted.gpg.d/nodesource*.gpg 2>/dev/null || true + cleanup_legacy_install "nodejs" + cleanup_tool_keyrings "nodesource" ;; php) - # Stop and disable ALL PHP-FPM versions - for fpm_service in $(systemctl list-units --type=service --all | grep -oE 'php[0-9]+\.[0-9]+-fpm' | sort -u); do - $STD systemctl stop "$fpm_service" >/dev/null 2>&1 || true - $STD systemctl disable "$fpm_service" >/dev/null 2>&1 || true - done + stop_all_services "php.*-fpm" $STD apt purge -y 'php*' >/dev/null 2>&1 || true rm -rf /etc/php 2>/dev/null || true - # Clean up ALL keyring locations (Sury PHP) - rm -f /usr/share/keyrings/deb.sury.org-php.gpg \ - /usr/share/keyrings/php*.gpg \ - /etc/apt/keyrings/php*.gpg \ - /etc/apt/trusted.gpg.d/php*.gpg 2>/dev/null || true + cleanup_tool_keyrings "deb.sury.org-php" "php" ;; postgresql) - $STD systemctl stop postgresql >/dev/null 2>&1 || true + stop_all_services "postgresql" $STD apt purge -y 'postgresql*' >/dev/null 2>&1 || true # Keep data directory for safety (can be removed manually if needed) # rm -rf /var/lib/postgresql 2>/dev/null || true - # Clean up ALL keyring locations - rm -f /usr/share/keyrings/postgresql*.gpg \ - /usr/share/keyrings/pgdg*.gpg \ - /etc/apt/keyrings/postgresql*.gpg \ - /etc/apt/keyrings/pgdg*.gpg \ - /etc/apt/trusted.gpg.d/postgresql*.gpg \ - /etc/apt/trusted.gpg.d/pgdg*.gpg 2>/dev/null || true + cleanup_tool_keyrings "postgresql" "pgdg" ;; java) $STD apt purge -y 'temurin*' 'adoptium*' 'openjdk*' >/dev/null 2>&1 || true - # Clean up ALL keyring locations (Adoptium) - rm -f /usr/share/keyrings/adoptium*.gpg \ - /etc/apt/keyrings/adoptium*.gpg \ - /etc/apt/trusted.gpg.d/adoptium*.gpg 2>/dev/null || true + cleanup_tool_keyrings "adoptium" ;; ruby) - rm -rf "$HOME/.rbenv" 2>/dev/null || true + cleanup_legacy_install "ruby" $STD apt purge -y 'ruby*' >/dev/null 2>&1 || true ;; rust) - rm -rf "$HOME/.cargo" "$HOME/.rustup" 2>/dev/null || true + cleanup_legacy_install "rust" ;; go | golang) rm -rf /usr/local/go 2>/dev/null || true + cleanup_legacy_install "golang" ;; clickhouse) - $STD systemctl stop clickhouse-server >/dev/null 2>&1 || true + stop_all_services "clickhouse-server" $STD apt purge -y 'clickhouse*' >/dev/null 2>&1 || true rm -rf /var/lib/clickhouse 2>/dev/null || true - # Clean up ALL keyring locations - rm -f /usr/share/keyrings/clickhouse*.gpg \ - /etc/apt/keyrings/clickhouse*.gpg \ - /etc/apt/trusted.gpg.d/clickhouse*.gpg 2>/dev/null || true + cleanup_tool_keyrings "clickhouse" ;; esac @@ -2563,9 +2671,7 @@ function setup_java() { # Clean up ALL old Adoptium repo configs and keyrings before setup cleanup_old_repo_files "adoptium" - rm -f /usr/share/keyrings/adoptium*.gpg \ - /etc/apt/keyrings/adoptium*.gpg \ - /etc/apt/trusted.gpg.d/adoptium*.gpg 2>/dev/null || true + cleanup_tool_keyrings "adoptium" # Add repo if needed if [[ ! -f /etc/apt/sources.list.d/adoptium.sources ]]; then @@ -2812,14 +2918,11 @@ setup_mariadb() { # Scenario 3: Fresh install or version change msg_info "Setup MariaDB $MARIADB_VERSION" - # Clean up ALL old MariaDB repo configs and keyrings before setup - cleanup_old_repo_files "mariadb" - rm -f /usr/share/keyrings/mariadb*.gpg \ - /etc/apt/keyrings/mariadb*.gpg \ - /etc/apt/trusted.gpg.d/mariadb*.gpg 2>/dev/null || true - - # Ensure APT is working before proceeding - ensure_apt_working || return 1 + # Prepare repository (cleanup + validation) + prepare_repository_setup "mariadb" || { + msg_error "Failed to prepare MariaDB repository" + return 1 + } # Install required dependencies first local mariadb_deps=() @@ -2847,19 +2950,20 @@ setup_mariadb() { echo "mariadb-server-$MARIADB_MAJOR_MINOR mariadb-server/feedback boolean false" | debconf-set-selections fi - # Install packages - DEBIAN_FRONTEND=noninteractive $STD apt install -y mariadb-server mariadb-client || { + # Install packages with retry logic + export DEBIAN_FRONTEND=noninteractive + if ! install_packages_with_retry "mariadb-server" "mariadb-client"; then # Fallback: try without specific version msg_warn "Failed to install MariaDB packages from upstream repo, trying distro fallback..." cleanup_old_repo_files "mariadb" $STD apt update || { msg_warn "APT update also failed, continuing with cache" } - DEBIAN_FRONTEND=noninteractive $STD apt install -y mariadb-server mariadb-client || { + install_packages_with_retry "mariadb-server" "mariadb-client" || { msg_error "Failed to install MariaDB packages (both upstream and distro)" return 1 } - } + fi cache_installed_version "mariadb" "$MARIADB_VERSION" msg_ok "Setup MariaDB $MARIADB_VERSION" @@ -2934,11 +3038,11 @@ function setup_mongodb() { cleanup_orphaned_sources - # Clean up ALL old MongoDB repo configs and keyrings before setup - cleanup_old_repo_files "mongodb" - rm -f /usr/share/keyrings/mongodb*.gpg \ - /etc/apt/keyrings/mongodb*.gpg \ - /etc/apt/trusted.gpg.d/mongodb*.gpg 2>/dev/null || true + # Prepare repository (cleanup + validation) + prepare_repository_setup "mongodb" || { + msg_error "Failed to prepare MongoDB repository" + return 1 + } # Setup repository manage_tool_repository "mongodb" "$MONGO_VERSION" "$MONGO_BASE_URL" \ @@ -2953,8 +3057,8 @@ function setup_mongodb() { return 1 } - # Install MongoDB - $STD apt install -y mongodb-org || { + # Install MongoDB with retry logic + install_packages_with_retry "mongodb-org" || { msg_error "Failed to install MongoDB packages" return 1 } @@ -2976,9 +3080,7 @@ function setup_mongodb() { # Verify MongoDB version local INSTALLED_VERSION INSTALLED_VERSION=$(mongod --version 2>/dev/null | grep -oP 'db version v\K[0-9]+\.[0-9]+' | head -n1 || echo "0.0") - if [[ "${INSTALLED_VERSION%%.*}" != "${MONGO_VERSION%%.*}" ]]; then - msg_warn "MongoDB version mismatch: expected $MONGO_VERSION, got $INSTALLED_VERSION" - fi + verify_tool_version "MongoDB" "$MONGO_VERSION" "$INSTALLED_VERSION" || true cache_installed_version "mongodb" "$MONGO_VERSION" msg_ok "Setup MongoDB $MONGO_VERSION" @@ -3028,11 +3130,11 @@ function setup_mysql() { msg_info "Setup MySQL $MYSQL_VERSION" fi - # Clean up ALL old MySQL repo configs and keyrings before setup - cleanup_old_repo_files "mysql" - rm -f /usr/share/keyrings/mysql*.gpg \ - /etc/apt/keyrings/mysql*.gpg \ - /etc/apt/trusted.gpg.d/mysql*.gpg 2>/dev/null || true + # Prepare repository (cleanup + validation) + prepare_repository_setup "mysql" || { + msg_error "Failed to prepare MySQL repository" + return 1 + } # Debian 13+ Fix: MySQL 8.0 incompatible with libaio1t64, use 8.4 LTS if [[ "$DISTRO_ID" == "debian" && "$DISTRO_CODENAME" =~ ^(trixie|forky|sid)$ ]]; then @@ -3057,11 +3159,12 @@ EOF return 1 } - if ! $STD apt install -y mysql-community-server mysql-community-client; then + # Install with retry logic + if ! install_packages_with_retry "mysql-community-server" "mysql-community-client"; then msg_warn "MySQL 8.4 LTS installation failed – falling back to MariaDB" cleanup_old_repo_files "mysql" $STD apt update - $STD apt install -y mariadb-server mariadb-client || { + install_packages_with_retry "mariadb-server" "mariadb-client" || { msg_error "Failed to install database engine (MySQL/MariaDB fallback)" return 1 } @@ -3094,18 +3197,18 @@ EOF ensure_apt_working || return 1 - # Try multiple package names (mysql-server, mysql-community-server, mysql) + # Try multiple package names with retry logic export DEBIAN_FRONTEND=noninteractive local mysql_install_success=false if apt-cache search "^mysql-server$" 2>/dev/null | grep -q . && - $STD apt install -y mysql-server mysql-client 2>/dev/null; then + install_packages_with_retry "mysql-server" "mysql-client"; then mysql_install_success=true elif apt-cache search "^mysql-community-server$" 2>/dev/null | grep -q . && - $STD apt install -y mysql-community-server mysql-community-client 2>/dev/null; then + install_packages_with_retry "mysql-community-server" "mysql-community-client"; then mysql_install_success=true elif apt-cache search "^mysql$" 2>/dev/null | grep -q . && - $STD apt install -y mysql 2>/dev/null; then + install_packages_with_retry "mysql"; then mysql_install_success=true fi @@ -3176,20 +3279,16 @@ function setup_nodejs() { msg_info "Setup Node.js $NODE_VERSION" fi - # Clean up any legacy nvm installations - if [[ -d "$HOME/.nvm" ]]; then - msg_info "Removing legacy nvm installation" - rm -rf "$HOME/.nvm" "$HOME/.npm" "$HOME/.bower" "$HOME/.config/yarn" 2>/dev/null || true - sed -i '/NVM_DIR/d' "$HOME/.bashrc" "$HOME/.profile" 2>/dev/null || true - fi + # Clean up legacy installations (nvm, etc.) + cleanup_legacy_install "nodejs" ensure_dependencies curl ca-certificates gnupg - # Clean up ALL old NodeSource repository configurations to avoid conflicts - rm -f /etc/apt/sources.list.d/nodesource.list \ - /etc/apt/sources.list.d/nodesource.sources \ - /usr/share/keyrings/nodesource.gpg \ - /etc/apt/keyrings/nodesource.gpg 2>/dev/null || true + # Prepare repository (cleanup + validation) + prepare_repository_setup "nodesource" || { + msg_error "Failed to prepare Node.js repository" + return 1 + } # Setup repository manage_tool_repository "nodejs" "$NODE_VERSION" "https://deb.nodesource.com/node_${NODE_VERSION}.x" "https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key" || { @@ -3200,20 +3299,11 @@ function setup_nodejs() { # Wait for repo to settle sleep 2 - # Install Node.js - if ! $STD apt update; then - msg_warn "APT update failed – retrying in 5s" - sleep 5 - if ! $STD apt update; then - msg_error "Failed to update APT repositories after adding NodeSource" - return 1 - fi - fi - - if ! $STD apt install -y nodejs; then + # Install Node.js with retry logic + install_packages_with_retry "nodejs" || { msg_error "Failed to install Node.js ${NODE_VERSION} from NodeSource" return 1 - fi + } # Verify Node.js was installed correctly if ! command -v node >/dev/null 2>&1; then @@ -3223,10 +3313,7 @@ function setup_nodejs() { local INSTALLED_NODE_VERSION INSTALLED_NODE_VERSION=$(node -v 2>/dev/null | grep -oP '^v\K[0-9]+' || echo "0") - if [[ "$INSTALLED_NODE_VERSION" != "$NODE_VERSION" ]]; then - msg_error "Node.js version mismatch: expected $NODE_VERSION, got $INSTALLED_NODE_VERSION" - return 1 - fi + verify_tool_version "Node.js" "$NODE_VERSION" "$INSTALLED_NODE_VERSION" || true # Update to latest npm (with version check to avoid incompatibility) local NPM_VERSION @@ -3380,22 +3467,18 @@ function setup_php() { # Scenario 2: Different version installed - clean upgrade if [[ -n "$CURRENT_PHP" && "$CURRENT_PHP" != "$PHP_VERSION" ]]; then msg_info "Upgrade PHP from $CURRENT_PHP to $PHP_VERSION" - # Stop and disable ALL PHP-FPM versions (not just current one) - for fpm_service in $(systemctl list-units --type=service --all 2>/dev/null | grep -oE 'php[0-9]+\.[0-9]+-fpm' | sort -u); do - $STD systemctl stop "$fpm_service" >/dev/null 2>&1 || true - $STD systemctl disable "$fpm_service" >/dev/null 2>&1 || true - done + # Stop and disable ALL PHP-FPM versions + stop_all_services "php.*-fpm" remove_old_tool_version "php" else msg_info "Setup PHP $PHP_VERSION" fi - # Clean up ALL old PHP repo configs and keyrings before setup - cleanup_old_repo_files "php" - rm -f /usr/share/keyrings/deb.sury.org-php.gpg \ - /usr/share/keyrings/php*.gpg \ - /etc/apt/keyrings/php*.gpg \ - /etc/apt/trusted.gpg.d/php*.gpg 2>/dev/null || true + # Prepare repository (cleanup + validation) + prepare_repository_setup "php" "deb.sury.org-php" || { + msg_error "Failed to prepare PHP repository" + return 1 + } # Setup Sury repository manage_tool_repository "php" "$PHP_VERSION" "" "https://packages.sury.org/debsuryorg-archive-keyring.deb" || { @@ -3421,15 +3504,15 @@ function setup_php() { # install apache2 with PHP support if requested if [[ "$PHP_APACHE" == "YES" ]]; then if ! dpkg -l 2>/dev/null | grep -q "libapache2-mod-php${PHP_VERSION}"; then - $STD apt install -y apache2 libapache2-mod-php${PHP_VERSION} || { + install_packages_with_retry "apache2" "libapache2-mod-php${PHP_VERSION}" || { msg_error "Failed to install Apache with PHP module" return 1 } fi fi - # Install PHP packages - $STD apt install -y $MODULE_LIST || { + # Install PHP packages with retry logic + install_packages_with_retry $MODULE_LIST || { msg_error "Failed to install PHP packages" return 1 } @@ -3529,16 +3612,10 @@ function setup_postgresql() { fi # Scenario 3: Fresh install or after removal - setup repo and install - cleanup_old_repo_files "pgdg" - cleanup_old_repo_files "postgresql" - - # Clean up ALL old PostgreSQL repo configs and keyrings before setup - rm -f /usr/share/keyrings/postgresql*.gpg \ - /usr/share/keyrings/pgdg*.gpg \ - /etc/apt/keyrings/postgresql*.gpg \ - /etc/apt/keyrings/pgdg*.gpg \ - /etc/apt/trusted.gpg.d/postgresql*.gpg \ - /etc/apt/trusted.gpg.d/pgdg*.gpg 2>/dev/null || true + prepare_repository_setup "pgdg" "postgresql" || { + msg_error "Failed to prepare PostgreSQL repository" + return 1 + } local SUITE case "$DISTRO_CODENAME" in @@ -3573,11 +3650,11 @@ function setup_postgresql() { $STD apt install -y ssl-cert 2>/dev/null || true fi - # Try multiple PostgreSQL package patterns + # Try multiple PostgreSQL package patterns with retry logic local pg_install_success=false if apt-cache search "^postgresql-${PG_VERSION}$" 2>/dev/null | grep -q . && - $STD apt install -y "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}" 2>/dev/null; then + install_packages_with_retry "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}"; then pg_install_success=true fi @@ -3888,7 +3965,7 @@ function setup_clickhouse() { # Scenario 2: Different version - clean upgrade if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" != "$CLICKHOUSE_VERSION" ]]; then msg_info "Upgrade ClickHouse from $CURRENT_VERSION to $CLICKHOUSE_VERSION" - $STD systemctl stop clickhouse-server >/dev/null 2>&1 || true + stop_all_services "clickhouse-server" remove_old_tool_version "clickhouse" else msg_info "Setup ClickHouse $CLICKHOUSE_VERSION" @@ -3896,11 +3973,11 @@ function setup_clickhouse() { ensure_dependencies apt-transport-https ca-certificates dirmngr gnupg - # Clean up ALL old ClickHouse repo configs and keyrings before setup - cleanup_old_repo_files "clickhouse" - rm -f /usr/share/keyrings/clickhouse*.gpg \ - /etc/apt/keyrings/clickhouse*.gpg \ - /etc/apt/trusted.gpg.d/clickhouse*.gpg 2>/dev/null || true + # Prepare repository (cleanup + validation) + prepare_repository_setup "clickhouse" || { + msg_error "Failed to prepare ClickHouse repository" + return 1 + } # Setup repository (ClickHouse uses 'stable' suite) setup_deb822_repo \ @@ -3911,14 +3988,14 @@ function setup_clickhouse() { "main" \ "amd64 arm64" - # Install packages + # Install packages with retry logic export DEBIAN_FRONTEND=noninteractive $STD apt update || { msg_error "APT update failed for ClickHouse repository" return 1 } - $STD apt install -y clickhouse-server clickhouse-client || { + install_packages_with_retry "clickhouse-server" "clickhouse-client" || { msg_error "Failed to install ClickHouse packages" return 1 } From 175df9e847fa69e19ad5528e5559b0aa1a0e6468 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 4 Nov 2025 13:52:58 +0100 Subject: [PATCH 1678/1733] Add retry logic for package upgrades and refactor installs Introduced upgrade_packages_with_retry to handle package upgrades with retry logic, similar to existing install_packages_with_retry. Refactored Java, MariaDB, and other install/upgrade flows to use the new retry functions and ensure_dependencies for more robust package management. Improved error handling and repository preparation steps. --- misc/tools.func | 71 +++++++++++++++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 29 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index f50975178..33cb0bbe6 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -165,6 +165,31 @@ install_packages_with_retry() { return 1 } +# ------------------------------------------------------------------------------ +# Upgrade specific packages with retry logic +# Usage: upgrade_packages_with_retry "mariadb-server" "mariadb-client" +# ------------------------------------------------------------------------------ +upgrade_packages_with_retry() { + local packages=("$@") + local max_retries=2 + local retry=0 + + while [[ $retry -le $max_retries ]]; do + if $STD apt install --only-upgrade -y "${packages[@]}" 2>/dev/null; then + return 0 + fi + + retry=$((retry + 1)) + if [[ $retry -le $max_retries ]]; then + msg_warn "Package upgrade failed, retrying ($retry/$max_retries)..." + sleep 2 + $STD apt update 2>/dev/null || true + fi + done + + return 1 +} + # ------------------------------------------------------------------------------ # Check if tool is already installed and optionally verify exact version # Returns: 0 if installed (with optional version match), 1 if not installed @@ -1447,11 +1472,8 @@ create_self_signed_cert() { return 0 fi - $STD apt update || { - msg_error "Failed to update package list" - return 1 - } - $STD apt install -y openssl || { + # Use ensure_dependencies for cleaner handling + ensure_dependencies openssl || { msg_error "Failed to install OpenSSL" return 1 } @@ -2669,9 +2691,11 @@ function setup_java() { DISTRO_CODENAME=$(awk -F= '/VERSION_CODENAME/ { print $2 }' /etc/os-release) local DESIRED_PACKAGE="temurin-${JAVA_VERSION}-jdk" - # Clean up ALL old Adoptium repo configs and keyrings before setup - cleanup_old_repo_files "adoptium" - cleanup_tool_keyrings "adoptium" + # Prepare repository (cleanup + validation) + prepare_repository_setup "adoptium" || { + msg_error "Failed to prepare Adoptium repository" + return 1 + } # Add repo if needed if [[ ! -f /etc/apt/sources.list.d/adoptium.sources ]]; then @@ -2702,11 +2726,8 @@ function setup_java() { # Scenario 1: Already at correct version if [[ "$INSTALLED_VERSION" == "$JAVA_VERSION" ]]; then msg_info "Update Temurin JDK $JAVA_VERSION" - $STD apt update || { - msg_error "APT update failed" - return 1 - } - $STD apt install --only-upgrade -y "$DESIRED_PACKAGE" || { + ensure_apt_working || return 1 + upgrade_packages_with_retry "$DESIRED_PACKAGE" || { msg_error "Failed to update Temurin JDK" return 1 } @@ -2723,11 +2744,10 @@ function setup_java() { msg_info "Setup Temurin JDK $JAVA_VERSION" fi - $STD apt update || { - msg_error "APT update failed" - return 1 - } - $STD apt install -y "$DESIRED_PACKAGE" || { + ensure_apt_working || return 1 + + # Install with retry logic + install_packages_with_retry "$DESIRED_PACKAGE" || { msg_error "Failed to install Temurin JDK $JAVA_VERSION" return 1 } @@ -2763,11 +2783,7 @@ function setup_local_ip_helper() { # Install networkd-dispatcher if not present if ! dpkg -s networkd-dispatcher >/dev/null 2>&1; then - $STD apt update || { - msg_error "Failed to update package list" - return 1 - } - $STD apt install -y networkd-dispatcher || { + ensure_dependencies networkd-dispatcher || { msg_error "Failed to install networkd-dispatcher" return 1 } @@ -2895,12 +2911,9 @@ setup_mariadb() { fi fi - # Perform upgrade - $STD apt update || { - msg_error "Failed to update package list" - return 1 - } - $STD apt install --only-upgrade -y mariadb-server mariadb-client || { + # Perform upgrade with retry logic + ensure_apt_working || return 1 + upgrade_packages_with_retry "mariadb-server" "mariadb-client" || { msg_error "Failed to upgrade MariaDB packages" return 1 } From 03fb8e2da3c75d905e2e978cae8f96898c1da382 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 4 Nov 2025 13:54:43 +0100 Subject: [PATCH 1679/1733] Add usage and feature documentation to tools.func Added detailed comments at the top of misc/tools.func describing its purpose, key features, and usage examples. This improves clarity for maintainers and users of the helper functions. --- misc/tools.func | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/misc/tools.func b/misc/tools.func index 33cb0bbe6..6dfedefd5 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -3,6 +3,32 @@ # ============================================================================== # HELPER FUNCTIONS FOR PACKAGE MANAGEMENT # ============================================================================== +# +# This file provides unified helper functions for robust package installation +# and repository management across Debian/Ubuntu OS upgrades. +# +# Key Features: +# - Automatic retry logic for transient APT/network failures +# - Unified keyring cleanup from all 3 locations +# - Legacy installation cleanup (nvm, rbenv, rustup) +# - OS-upgrade-safe repository preparation +# - Service pattern matching for multi-version tools +# +# Usage in install scripts: +# source /dev/stdin <<< "$FUNCTIONS" # Load from build.func +# prepare_repository_setup "mysql" +# install_packages_with_retry "mysql-server" "mysql-client" +# +# Quick Reference (Core Helpers): +# cleanup_tool_keyrings() - Remove keyrings from all 3 locations +# stop_all_services() - Stop services by pattern (e.g. "php*-fpm") +# verify_tool_version() - Validate installed version matches expected +# cleanup_legacy_install() - Remove nvm, rbenv, rustup, etc. +# prepare_repository_setup() - Cleanup repos + keyrings + validate APT +# install_packages_with_retry() - Install with 3 retries and APT refresh +# upgrade_packages_with_retry() - Upgrade with 3 retries and APT refresh +# +# ============================================================================== # ------------------------------------------------------------------------------ # Cache installed version to avoid repeated checks From 98599c51079288a8ecc49d260566c9e1fc4684c9 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Tue, 4 Nov 2025 14:33:41 +0100 Subject: [PATCH 1680/1733] improve install --- install/nginxproxymanager-install.sh | 55 +++++++++++----------------- 1 file changed, 21 insertions(+), 34 deletions(-) diff --git a/install/nginxproxymanager-install.sh b/install/nginxproxymanager-install.sh index cf06b6c85..8f14060e8 100644 --- a/install/nginxproxymanager-install.sh +++ b/install/nginxproxymanager-install.sh @@ -39,18 +39,15 @@ $STD /opt/certbot/bin/pip install certbot certbot-dns-cloudflare ln -sf /opt/certbot/bin/certbot /usr/local/bin/certbot msg_ok "Set up Certbot" -VERSION="$(awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release)" - msg_info "Installing Openresty" curl -fsSL "https://openresty.org/package/pubkey.gpg" | gpg --dearmor -o /etc/apt/trusted.gpg.d/openresty-archive-keyring.gpg -case "$VERSION" in -trixie) - echo -e "deb http://openresty.org/package/debian bookworm openresty" >/etc/apt/sources.list.d/openresty.list - ;; -*) - echo -e "deb http://openresty.org/package/debian $VERSION openresty" >/etc/apt/sources.list.d/openresty.list - ;; -esac +cat <<'EOF' >/etc/apt/sources.list.d/openresty.sources +Types: deb +URIs: http://openresty.org/package/debian/ +Suites: bookworm +Components: openresty +Signed-By: /etc/apt/trusted.gpg.d/openresty.gpg +EOF $STD apt update $STD apt -y install openresty msg_ok "Installed Openresty" @@ -61,33 +58,25 @@ RELEASE=$(curl -fsSL https://api.github.com/repos/NginxProxyManager/nginx-proxy- grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') -################## -RELEASE="2.13" -################## - - -msg_info "Downloading Nginx Proxy Manager v${RELEASE}" -curl -fsSL "https://codeload.github.com/NginxProxyManager/nginx-proxy-manager/tar.gz/v${RELEASE}" | tar -xz -cd ./nginx-proxy-manager-"${RELEASE}" -msg_ok "Downloaded Nginx Proxy Manager v${RELEASE}" +fetch_and_deploy_gh_release "nginxproxymanager" "NginxProxyManager/nginx-proxy-manager" msg_info "Setting up Environment" ln -sf /usr/bin/python3 /usr/bin/python ln -sf /usr/local/openresty/nginx/sbin/nginx /usr/sbin/nginx ln -sf /usr/local/openresty/nginx/ /etc/nginx -sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" backend/package.json -sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" frontend/package.json -sed -i 's+^daemon+#daemon+g' docker/rootfs/etc/nginx/nginx.conf -NGINX_CONFS=$(find "$(pwd)" -type f -name "*.conf") +sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" /opt/nginxproxymanager/backend/package.json +sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" /opt/nginxproxymanager/frontend/package.json +sed -i 's+^daemon+#daemon+g' /opt/nginxproxymanager/docker/rootfs/etc/nginx/nginx.conf +NGINX_CONFS=$(find /opt/nginxproxymanager -type f -name "*.conf") for NGINX_CONF in $NGINX_CONFS; do sed -i 's+include conf.d+include /etc/nginx/conf.d+g' "$NGINX_CONF" done mkdir -p /var/www/html /etc/nginx/logs -cp -r docker/rootfs/var/www/html/* /var/www/html/ -cp -r docker/rootfs/etc/nginx/* /etc/nginx/ -cp docker/rootfs/etc/letsencrypt.ini /etc/letsencrypt.ini -cp docker/rootfs/etc/logrotate.d/nginx-proxy-manager /etc/logrotate.d/nginx-proxy-manager +cp -r /opt/nginxproxymanager/docker/rootfs/var/www/html/* /var/www/html/ +cp -r /opt/nginxproxymanager/docker/rootfs/etc/nginx/* /etc/nginx/ +cp /opt/nginxproxymanager/docker/rootfs/etc/letsencrypt.ini /etc/letsencrypt.ini +cp /opt/nginxproxymanager/docker/rootfs/etc/logrotate.d/nginx-proxy-manager /etc/logrotate.d/nginx-proxy-manager ln -sf /etc/nginx/nginx.conf /etc/nginx/conf/nginx.conf rm -f /etc/nginx/conf.d/dev.conf @@ -118,18 +107,18 @@ if [ ! -f /data/nginx/dummycert.pem ] || [ ! -f /data/nginx/dummykey.pem ]; then fi mkdir -p /app/global /app/frontend/images -cp -r backend/* /app -cp -r global/* /app/global +cp -r /opt/nginxproxymanager/backend/* /app +cp -r /opt/nginxproxymanager/global/* /app/global msg_ok "Set up Environment" msg_info "Building Frontend" -cd ./frontend +cd /opt/nginxproxymanager/frontend # Replace node-sass with sass in package.json before installation sed -i 's/"node-sass".*$/"sass": "^1.92.1",/g' package.json $STD yarn install --network-timeout 600000 $STD yarn build -cp -r dist/* /app/frontend -cp -r app-images/* /app/frontend/images +cp -r /opt/nginxproxymanager/frontend/dist/* /app/frontend +cp -r /opt/nginxproxymanager/frontend/app-images/* /app/frontend/images msg_ok "Built Frontend" msg_info "Initializing Backend" @@ -150,7 +139,6 @@ if [ ! -f /app/config/production.json ]; then EOF fi cd /app -# export NODE_OPTIONS="--openssl-legacy-provider" $STD yarn install --network-timeout 600000 msg_ok "Initialized Backend" @@ -185,7 +173,6 @@ systemctl enable -q --now npm msg_ok "Started Services" msg_info "Cleaning up" -rm -rf ../nginx-proxy-manager-* systemctl restart openresty $STD apt -y autoremove $STD apt -y autoclean From e13aecdbdeb55dde5def52df5ca508ee7a3f0aa5 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Tue, 4 Nov 2025 14:47:06 +0100 Subject: [PATCH 1681/1733] fix: openresty keyring --- install/nginxproxymanager-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/nginxproxymanager-install.sh b/install/nginxproxymanager-install.sh index 8f14060e8..734b7e60a 100644 --- a/install/nginxproxymanager-install.sh +++ b/install/nginxproxymanager-install.sh @@ -40,7 +40,7 @@ ln -sf /opt/certbot/bin/certbot /usr/local/bin/certbot msg_ok "Set up Certbot" msg_info "Installing Openresty" -curl -fsSL "https://openresty.org/package/pubkey.gpg" | gpg --dearmor -o /etc/apt/trusted.gpg.d/openresty-archive-keyring.gpg +curl -fsSL "https://openresty.org/package/pubkey.gpg" | gpg --dearmor -o /etc/apt/trusted.gpg.d/openresty.gpg cat <<'EOF' >/etc/apt/sources.list.d/openresty.sources Types: deb URIs: http://openresty.org/package/debian/ From 000492671c66d029333f75e8d498be334b4211e0 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 4 Nov 2025 15:23:01 +0100 Subject: [PATCH 1682/1733] Improve Node.js setup for Debian systems Enhances the setup_nodejs function to remove Debian-packaged Node.js if present, set APT preferences to prioritize NodeSource packages, and verify npm availability after installation. These changes help avoid conflicts between Debian and NodeSource Node.js versions and ensure npm is properly installed. --- misc/tools.func | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/misc/tools.func b/misc/tools.func index 6dfedefd5..2fea2d886 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -3321,6 +3321,13 @@ function setup_nodejs() { # Clean up legacy installations (nvm, etc.) cleanup_legacy_install "nodejs" + # Remove Debian's nodejs if present (conflicts with NodeSource) + if dpkg -l | grep -q "^ii.*nodejs.*dfsg"; then + $STD msg_info "Removing Debian-packaged Node.js (conflicts with NodeSource)" + $STD apt purge -y nodejs libnode* 2>/dev/null || true + $STD apt autoremove -y 2>/dev/null || true + fi + ensure_dependencies curl ca-certificates gnupg # Prepare repository (cleanup + validation) @@ -3335,10 +3342,21 @@ function setup_nodejs() { return 1 } + # Set APT priority to prefer NodeSource over Debian repos + cat >/etc/apt/preferences.d/nodesource <<'EOF' +Package: nodejs +Pin: origin deb.nodesource.com +Pin-Priority: 600 + +Package: * +Pin: origin deb.nodesource.com +Pin-Priority: 600 +EOF + # Wait for repo to settle sleep 2 - # Install Node.js with retry logic + # Install Node.js with retry logic (explicit version to avoid Debian repo) install_packages_with_retry "nodejs" || { msg_error "Failed to install Node.js ${NODE_VERSION} from NodeSource" return 1 @@ -3354,6 +3372,12 @@ function setup_nodejs() { INSTALLED_NODE_VERSION=$(node -v 2>/dev/null | grep -oP '^v\K[0-9]+' || echo "0") verify_tool_version "Node.js" "$NODE_VERSION" "$INSTALLED_NODE_VERSION" || true + # Verify npm is available (should come with NodeSource nodejs) + if ! command -v npm >/dev/null 2>&1; then + msg_error "npm not found after Node.js installation - repository issue?" + return 1 + fi + # Update to latest npm (with version check to avoid incompatibility) local NPM_VERSION NPM_VERSION=$(npm -v 2>/dev/null || echo "0") From 5ebd30abfdb3d6624f5c5c0ece55bbd9b0353c11 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 4 Nov 2025 15:31:02 +0100 Subject: [PATCH 1683/1733] Update tools.func --- misc/tools.func | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 2fea2d886..48264e356 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -3057,8 +3057,8 @@ function setup_mongodb() { ensure_apt_working || return 1 - # Perform upgrade - $STD apt install --only-upgrade -y mongodb-org || { + # Perform upgrade with retry logic + upgrade_packages_with_retry "mongodb-org" || { msg_error "Failed to upgrade MongoDB" return 1 } @@ -3154,7 +3154,8 @@ function setup_mysql() { ensure_apt_working || return 1 - $STD apt install --only-upgrade -y mysql-server mysql-client || true + # Perform upgrade with retry logic (non-fatal if fails) + upgrade_packages_with_retry "mysql-server" "mysql-client" || true cache_installed_version "mysql" "$MYSQL_VERSION" msg_ok "Update MySQL $MYSQL_VERSION" @@ -3521,8 +3522,8 @@ function setup_php() { ensure_apt_working || return 1 - # Just update PHP packages - $STD apt install --only-upgrade -y "php${PHP_VERSION}" || true + # Perform upgrade with retry logic (non-fatal if fails) + upgrade_packages_with_retry "php${PHP_VERSION}" || true cache_installed_version "php" "$PHP_VERSION" msg_ok "Update PHP $PHP_VERSION" @@ -3645,8 +3646,10 @@ function setup_postgresql() { # Scenario 1: Already at correct version if [[ "$CURRENT_PG_VERSION" == "$PG_VERSION" ]]; then msg_info "Update PostgreSQL $PG_VERSION" - $STD apt update - $STD apt install --only-upgrade -y "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}" 2>/dev/null || true + ensure_apt_working || return 1 + + # Perform upgrade with retry logic (non-fatal if fails) + upgrade_packages_with_retry "postgresql-${PG_VERSION}" "postgresql-client-${PG_VERSION}" 2>/dev/null || true cache_installed_version "postgresql" "$PG_VERSION" msg_ok "Update PostgreSQL $PG_VERSION" @@ -4019,7 +4022,9 @@ function setup_clickhouse() { if [[ -n "$CURRENT_VERSION" && "$CURRENT_VERSION" == "$CLICKHOUSE_VERSION" ]]; then msg_info "Update ClickHouse $CLICKHOUSE_VERSION" ensure_apt_working || return 1 - $STD apt install --only-upgrade -y clickhouse-server clickhouse-client || true + + # Perform upgrade with retry logic (non-fatal if fails) + upgrade_packages_with_retry "clickhouse-server" "clickhouse-client" || true cache_installed_version "clickhouse" "$CLICKHOUSE_VERSION" msg_ok "Update ClickHouse $CLICKHOUSE_VERSION" return 0 From c8a299e401c587daf52b395b85bbc6392c3a65e0 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 4 Nov 2025 15:36:01 +0100 Subject: [PATCH 1684/1733] Improve Node.js setup to prevent Debian package conflicts Moves APT pinning for NodeSource to occur before removing existing Debian nodejs packages, ensuring Debian's nodejs is not reinstalled. Cleans up logic for removing conflicting packages and clarifies the order of operations in setup_nodejs. --- misc/tools.func | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 48264e356..0faed120a 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -3322,13 +3322,25 @@ function setup_nodejs() { # Clean up legacy installations (nvm, etc.) cleanup_legacy_install "nodejs" - # Remove Debian's nodejs if present (conflicts with NodeSource) - if dpkg -l | grep -q "^ii.*nodejs.*dfsg"; then - $STD msg_info "Removing Debian-packaged Node.js (conflicts with NodeSource)" - $STD apt purge -y nodejs libnode* 2>/dev/null || true + # Set APT priority FIRST to prevent Debian nodejs from being installed + cat >/etc/apt/preferences.d/nodesource <<'EOF' +Package: nodejs +Pin: origin deb.nodesource.com +Pin-Priority: 600 + +Package: * +Pin: origin deb.nodesource.com +Pin-Priority: 600 +EOF + + # Remove any existing Debian nodejs BEFORE adding NodeSource repo + if dpkg -l 2>/dev/null | grep -q "^ii.*nodejs"; then + msg_info "Removing Debian-packaged Node.js (conflicts with NodeSource)" + $STD apt purge -y nodejs nodejs-doc libnode* node-* 2>/dev/null || true $STD apt autoremove -y 2>/dev/null || true fi + # Install dependencies (now protected by APT pinning) ensure_dependencies curl ca-certificates gnupg # Prepare repository (cleanup + validation) @@ -3343,17 +3355,6 @@ function setup_nodejs() { return 1 } - # Set APT priority to prefer NodeSource over Debian repos - cat >/etc/apt/preferences.d/nodesource <<'EOF' -Package: nodejs -Pin: origin deb.nodesource.com -Pin-Priority: 600 - -Package: * -Pin: origin deb.nodesource.com -Pin-Priority: 600 -EOF - # Wait for repo to settle sleep 2 From 51dd3abeaadbdf18dc735507437ac084359a20ec Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 4 Nov 2025 15:39:45 +0100 Subject: [PATCH 1685/1733] Backup revision-bump.yml workflow file --- .github/workflows/{revision-bump.yml => revision-bump.yml.bak} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{revision-bump.yml => revision-bump.yml.bak} (100%) diff --git a/.github/workflows/revision-bump.yml b/.github/workflows/revision-bump.yml.bak similarity index 100% rename from .github/workflows/revision-bump.yml rename to .github/workflows/revision-bump.yml.bak From b159d519b295ded9bcaaf7435f5c41399840678c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 4 Nov 2025 15:49:26 +0100 Subject: [PATCH 1686/1733] Update tools.func --- misc/tools.func | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 0faed120a..a7e045e48 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -3322,26 +3322,17 @@ function setup_nodejs() { # Clean up legacy installations (nvm, etc.) cleanup_legacy_install "nodejs" - # Set APT priority FIRST to prevent Debian nodejs from being installed - cat >/etc/apt/preferences.d/nodesource <<'EOF' -Package: nodejs -Pin: origin deb.nodesource.com -Pin-Priority: 600 - -Package: * -Pin: origin deb.nodesource.com -Pin-Priority: 600 -EOF - - # Remove any existing Debian nodejs BEFORE adding NodeSource repo - if dpkg -l 2>/dev/null | grep -q "^ii.*nodejs"; then - msg_info "Removing Debian-packaged Node.js (conflicts with NodeSource)" + # CRITICAL: Remove ALL Debian nodejs packages BEFORE adding NodeSource repo + # This prevents conflicts during dependency resolution + if dpkg -l 2>/dev/null | grep -qE "^ii.*(nodejs|libnode|node-cjs|node-acorn|node-balanced|node-brace|node-minimatch|node-undici|node-xtend|node-corepack)"; then + msg_info "Removing Debian-packaged Node.js and dependencies" $STD apt purge -y nodejs nodejs-doc libnode* node-* 2>/dev/null || true $STD apt autoremove -y 2>/dev/null || true + $STD apt clean 2>/dev/null || true fi - # Install dependencies (now protected by APT pinning) - ensure_dependencies curl ca-certificates gnupg + # Remove any APT pinning (not needed if Debian nodejs is purged) + rm -f /etc/apt/preferences.d/nodesource 2>/dev/null || true # Prepare repository (cleanup + validation) prepare_repository_setup "nodesource" || { @@ -3349,12 +3340,15 @@ EOF return 1 } - # Setup repository + # Setup NodeSource repository BEFORE installing dependencies manage_tool_repository "nodejs" "$NODE_VERSION" "https://deb.nodesource.com/node_${NODE_VERSION}.x" "https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key" || { msg_error "Failed to setup Node.js repository" return 1 } + # Now install dependencies (NodeSource repo is already active, no Debian nodejs available) + ensure_dependencies curl ca-certificates gnupg + # Wait for repo to settle sleep 2 From 1925c1cd5fc3f840f4365d80d1e9ef19a375374b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 4 Nov 2025 16:01:24 +0100 Subject: [PATCH 1687/1733] Update tools.func --- misc/tools.func | 57 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index a7e045e48..16902fad0 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -3319,45 +3319,96 @@ function setup_nodejs() { msg_info "Setup Node.js $NODE_VERSION" fi + # DEBUG: Initial state + echo "🔍 DEBUG: Initial state check..." + echo " - Requested version: $NODE_VERSION" + echo " - Current version: ${CURRENT_NODE_VERSION:-none}" + echo " - Installed packages:" + dpkg -l | grep -E "(nodejs|libnode|node-)" | awk '{print " * " $2 " " $3}' || echo " * none" + echo "" + # Clean up legacy installations (nvm, etc.) + echo "🔍 DEBUG: Running cleanup_legacy_install..." cleanup_legacy_install "nodejs" # CRITICAL: Remove ALL Debian nodejs packages BEFORE adding NodeSource repo # This prevents conflicts during dependency resolution + echo "🔍 DEBUG: Checking for Debian nodejs packages..." if dpkg -l 2>/dev/null | grep -qE "^ii.*(nodejs|libnode|node-cjs|node-acorn|node-balanced|node-brace|node-minimatch|node-undici|node-xtend|node-corepack)"; then + echo " ⚠️ Found Debian nodejs packages - purging now..." msg_info "Removing Debian-packaged Node.js and dependencies" - $STD apt purge -y nodejs nodejs-doc libnode* node-* 2>/dev/null || true - $STD apt autoremove -y 2>/dev/null || true - $STD apt clean 2>/dev/null || true + apt purge -y nodejs nodejs-doc libnode* node-* 2>&1 | tee -a /tmp/silent.$$.log || true + apt autoremove -y 2>&1 | tee -a /tmp/silent.$$.log || true + apt clean 2>&1 | tee -a /tmp/silent.$$.log || true + echo " ✅ Purge completed" + else + echo " ✅ No Debian nodejs packages found" fi + echo "" # Remove any APT pinning (not needed if Debian nodejs is purged) + echo "🔍 DEBUG: Removing old APT pinning..." rm -f /etc/apt/preferences.d/nodesource 2>/dev/null || true # Prepare repository (cleanup + validation) + echo "🔍 DEBUG: Running prepare_repository_setup..." prepare_repository_setup "nodesource" || { msg_error "Failed to prepare Node.js repository" return 1 } # Setup NodeSource repository BEFORE installing dependencies + echo "🔍 DEBUG: Setting up NodeSource repository..." + echo " - URL: https://deb.nodesource.com/node_${NODE_VERSION}.x" manage_tool_repository "nodejs" "$NODE_VERSION" "https://deb.nodesource.com/node_${NODE_VERSION}.x" "https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key" || { msg_error "Failed to setup Node.js repository" return 1 } + # Verify repository is active + echo "🔍 DEBUG: Verifying NodeSource repository..." + if [[ -f /etc/apt/sources.list.d/nodesource.sources ]]; then + echo " ✅ Repository file exists:" + cat /etc/apt/sources.list.d/nodesource.sources | sed 's/^/ /' + else + echo " ❌ Repository file NOT FOUND!" + fi + echo "" + + # Force APT cache refresh to see NodeSource packages + echo "🔍 DEBUG: Forcing APT update to refresh NodeSource cache..." + apt update 2>&1 | grep -i "node\|nodesource" || echo " (no nodejs-related output)" + echo "" + + # Check what version APT will install + echo "🔍 DEBUG: Checking APT policy for nodejs package..." + apt-cache policy nodejs | head -n 20 + echo "" + # Now install dependencies (NodeSource repo is already active, no Debian nodejs available) + echo "🔍 DEBUG: Installing dependencies (curl, ca-certificates, gnupg)..." ensure_dependencies curl ca-certificates gnupg + # Check again after dependencies + echo "🔍 DEBUG: Checking installed packages after dependencies..." + dpkg -l | grep -E "(nodejs|libnode|node-)" | awk '{print " * " $2 " " $3}' || echo " * none" + echo "" + # Wait for repo to settle sleep 2 # Install Node.js with retry logic (explicit version to avoid Debian repo) + echo "🔍 DEBUG: Installing Node.js from NodeSource..." install_packages_with_retry "nodejs" || { + echo "❌ DEBUG: Installation failed!" + echo "Final APT policy:" + apt-cache policy nodejs msg_error "Failed to install Node.js ${NODE_VERSION} from NodeSource" return 1 } + echo "🔍 DEBUG: Installation completed, verifying..." + # Verify Node.js was installed correctly if ! command -v node >/dev/null 2>&1; then msg_error "Node.js binary not found after installation" From cf885419eaa967c17f981d2c6c1d3e11d7fbf424 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 4 Nov 2025 16:09:56 +0100 Subject: [PATCH 1688/1733] Update tools.func --- misc/tools.func | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 16902fad0..4202307fe 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -3382,16 +3382,34 @@ function setup_nodejs() { # Check what version APT will install echo "🔍 DEBUG: Checking APT policy for nodejs package..." - apt-cache policy nodejs | head -n 20 + apt-cache policy nodejs 2>&1 | head -n 20 || true echo "" # Now install dependencies (NodeSource repo is already active, no Debian nodejs available) echo "🔍 DEBUG: Installing dependencies (curl, ca-certificates, gnupg)..." + echo " ⚠️ CRITICAL: This is where Debian nodejs might sneak back in!" + + # Check available nodejs packages BEFORE ensure_dependencies + echo "🔍 DEBUG: Available nodejs packages BEFORE ensure_dependencies:" + apt-cache search "^nodejs$" 2>&1 || true + apt-cache madison nodejs 2>&1 | head -n 5 || true + ensure_dependencies curl ca-certificates gnupg - # Check again after dependencies - echo "🔍 DEBUG: Checking installed packages after dependencies..." - dpkg -l | grep -E "(nodejs|libnode|node-)" | awk '{print " * " $2 " " $3}' || echo " * none" + echo "🔍 DEBUG: ensure_dependencies completed" + + # Check again after dependencies - THIS IS THE SMOKING GUN + echo "🔍 DEBUG: Checking installed packages AFTER ensure_dependencies..." + if dpkg -l 2>/dev/null | grep -qE "^ii.*nodejs"; then + echo " ❌❌❌ DEBIAN NODEJS WAS INSTALLED BY ensure_dependencies! ❌❌❌" + dpkg -l | grep -E "(nodejs|libnode|node-)" | awk '{print " * " $2 " " $3}' + echo "" + echo " 🔧 Purging it again before final install..." + apt purge -y nodejs nodejs-doc libnode* node-* 2>&1 | grep -i "remove\|purge" || true + apt autoremove -y 2>&1 >/dev/null || true + else + echo " ✅ No nodejs packages installed (good!)" + fi echo "" # Wait for repo to settle From be47e960286e4518b2f21ac7e4a174788bc0db44 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 4 Nov 2025 16:16:38 +0100 Subject: [PATCH 1689/1733] remove debug --- misc/tools.func | 91 ++++++------------------------------------------- 1 file changed, 11 insertions(+), 80 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 4202307fe..d0e6f2c95 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -3319,114 +3319,45 @@ function setup_nodejs() { msg_info "Setup Node.js $NODE_VERSION" fi - # DEBUG: Initial state - echo "🔍 DEBUG: Initial state check..." - echo " - Requested version: $NODE_VERSION" - echo " - Current version: ${CURRENT_NODE_VERSION:-none}" - echo " - Installed packages:" - dpkg -l | grep -E "(nodejs|libnode|node-)" | awk '{print " * " $2 " " $3}' || echo " * none" - echo "" - # Clean up legacy installations (nvm, etc.) - echo "🔍 DEBUG: Running cleanup_legacy_install..." cleanup_legacy_install "nodejs" - # CRITICAL: Remove ALL Debian nodejs packages BEFORE adding NodeSource repo - # This prevents conflicts during dependency resolution - echo "🔍 DEBUG: Checking for Debian nodejs packages..." + # Remove ALL Debian nodejs packages BEFORE adding NodeSource repo if dpkg -l 2>/dev/null | grep -qE "^ii.*(nodejs|libnode|node-cjs|node-acorn|node-balanced|node-brace|node-minimatch|node-undici|node-xtend|node-corepack)"; then - echo " ⚠️ Found Debian nodejs packages - purging now..." msg_info "Removing Debian-packaged Node.js and dependencies" - apt purge -y nodejs nodejs-doc libnode* node-* 2>&1 | tee -a /tmp/silent.$$.log || true - apt autoremove -y 2>&1 | tee -a /tmp/silent.$$.log || true - apt clean 2>&1 | tee -a /tmp/silent.$$.log || true - echo " ✅ Purge completed" - else - echo " ✅ No Debian nodejs packages found" + $STD apt purge -y nodejs nodejs-doc libnode* node-* 2>/dev/null || true + $STD apt autoremove -y 2>/dev/null || true + $STD apt clean 2>/dev/null || true fi - echo "" - # Remove any APT pinning (not needed if Debian nodejs is purged) - echo "🔍 DEBUG: Removing old APT pinning..." + # Remove any APT pinning (not needed) rm -f /etc/apt/preferences.d/nodesource 2>/dev/null || true # Prepare repository (cleanup + validation) - echo "🔍 DEBUG: Running prepare_repository_setup..." prepare_repository_setup "nodesource" || { msg_error "Failed to prepare Node.js repository" return 1 } - # Setup NodeSource repository BEFORE installing dependencies - echo "🔍 DEBUG: Setting up NodeSource repository..." - echo " - URL: https://deb.nodesource.com/node_${NODE_VERSION}.x" + # Setup NodeSource repository manage_tool_repository "nodejs" "$NODE_VERSION" "https://deb.nodesource.com/node_${NODE_VERSION}.x" "https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key" || { msg_error "Failed to setup Node.js repository" return 1 } - # Verify repository is active - echo "🔍 DEBUG: Verifying NodeSource repository..." - if [[ -f /etc/apt/sources.list.d/nodesource.sources ]]; then - echo " ✅ Repository file exists:" - cat /etc/apt/sources.list.d/nodesource.sources | sed 's/^/ /' - else - echo " ❌ Repository file NOT FOUND!" - fi - echo "" - - # Force APT cache refresh to see NodeSource packages - echo "🔍 DEBUG: Forcing APT update to refresh NodeSource cache..." - apt update 2>&1 | grep -i "node\|nodesource" || echo " (no nodejs-related output)" - echo "" - - # Check what version APT will install - echo "🔍 DEBUG: Checking APT policy for nodejs package..." - apt-cache policy nodejs 2>&1 | head -n 20 || true - echo "" - - # Now install dependencies (NodeSource repo is already active, no Debian nodejs available) - echo "🔍 DEBUG: Installing dependencies (curl, ca-certificates, gnupg)..." - echo " ⚠️ CRITICAL: This is where Debian nodejs might sneak back in!" - - # Check available nodejs packages BEFORE ensure_dependencies - echo "🔍 DEBUG: Available nodejs packages BEFORE ensure_dependencies:" - apt-cache search "^nodejs$" 2>&1 || true - apt-cache madison nodejs 2>&1 | head -n 5 || true + # CRITICAL: Force APT cache refresh AFTER repository setup + # This ensures NodeSource is the only nodejs source in APT cache + $STD apt update + # Install dependencies (NodeSource is now the only nodejs source) ensure_dependencies curl ca-certificates gnupg - echo "🔍 DEBUG: ensure_dependencies completed" - - # Check again after dependencies - THIS IS THE SMOKING GUN - echo "🔍 DEBUG: Checking installed packages AFTER ensure_dependencies..." - if dpkg -l 2>/dev/null | grep -qE "^ii.*nodejs"; then - echo " ❌❌❌ DEBIAN NODEJS WAS INSTALLED BY ensure_dependencies! ❌❌❌" - dpkg -l | grep -E "(nodejs|libnode|node-)" | awk '{print " * " $2 " " $3}' - echo "" - echo " 🔧 Purging it again before final install..." - apt purge -y nodejs nodejs-doc libnode* node-* 2>&1 | grep -i "remove\|purge" || true - apt autoremove -y 2>&1 >/dev/null || true - else - echo " ✅ No nodejs packages installed (good!)" - fi - echo "" - - # Wait for repo to settle - sleep 2 - - # Install Node.js with retry logic (explicit version to avoid Debian repo) - echo "🔍 DEBUG: Installing Node.js from NodeSource..." + # Install Node.js from NodeSource install_packages_with_retry "nodejs" || { - echo "❌ DEBUG: Installation failed!" - echo "Final APT policy:" - apt-cache policy nodejs msg_error "Failed to install Node.js ${NODE_VERSION} from NodeSource" return 1 } - echo "🔍 DEBUG: Installation completed, verifying..." - # Verify Node.js was installed correctly if ! command -v node >/dev/null 2>&1; then msg_error "Node.js binary not found after installation" From 4577271e7dd7e703a6b506769133c780e157a8a7 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 4 Nov 2025 16:24:37 +0100 Subject: [PATCH 1690/1733] Prioritize app resource defaults and silence storage messages App-declared CPU, RAM, and disk defaults now take precedence over default.vars only if they are higher, ensuring resource allocations favor app requirements. Additionally, informational output for storage configuration and default.vars creation has been silenced for less verbose operation. --- misc/build.func | 54 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/misc/build.func b/misc/build.func index eb2183872..11222d75f 100644 --- a/misc/build.func +++ b/misc/build.func @@ -36,6 +36,19 @@ variables() { PVEVERSION="N/A" fi KERNEL_VERSION=$(uname -r) + + # Capture app-declared defaults (for precedence logic) + # These values are set by the app script BEFORE default.vars is loaded + # If app declares higher values than default.vars, app values take precedence + if [[ -n "${var_cpu:-}" && "${var_cpu}" =~ ^[0-9]+$ ]]; then + export APP_DEFAULT_CPU="${var_cpu}" + fi + if [[ -n "${var_ram:-}" && "${var_ram}" =~ ^[0-9]+$ ]]; then + export APP_DEFAULT_RAM="${var_ram}" + fi + if [[ -n "${var_disk:-}" && "${var_disk}" =~ ^[0-9]+$ ]]; then + export APP_DEFAULT_DISK="${var_disk}" + fi } # ----------------------------------------------------------------------------- @@ -288,13 +301,40 @@ install_ssh_keys_into_ct() { # - Defines all base/default variables for container creation # - Reads from environment variables (var_*) # - Provides fallback defaults for OS type/version +# - App-specific values take precedence when they are HIGHER (for CPU, RAM, DISK) # ------------------------------------------------------------------------------ base_settings() { # Default Settings CT_TYPE=${var_unprivileged:-"1"} - DISK_SIZE=${var_disk:-"4"} - CORE_COUNT=${var_cpu:-"1"} - RAM_SIZE=${var_ram:-"1024"} + + # Resource allocation: App defaults take precedence if HIGHER + # Compare app-declared values (saved in APP_DEFAULT_*) with current var_* values + local final_disk="${var_disk:-4}" + local final_cpu="${var_cpu:-1}" + local final_ram="${var_ram:-1024}" + + # If app declared higher values, use those instead + if [[ -n "${APP_DEFAULT_DISK:-}" && "${APP_DEFAULT_DISK}" =~ ^[0-9]+$ ]]; then + if [[ "${APP_DEFAULT_DISK}" -gt "${final_disk}" ]]; then + final_disk="${APP_DEFAULT_DISK}" + fi + fi + + if [[ -n "${APP_DEFAULT_CPU:-}" && "${APP_DEFAULT_CPU}" =~ ^[0-9]+$ ]]; then + if [[ "${APP_DEFAULT_CPU}" -gt "${final_cpu}" ]]; then + final_cpu="${APP_DEFAULT_CPU}" + fi + fi + + if [[ -n "${APP_DEFAULT_RAM:-}" && "${APP_DEFAULT_RAM}" =~ ^[0-9]+$ ]]; then + if [[ "${APP_DEFAULT_RAM}" -gt "${final_ram}" ]]; then + final_ram="${APP_DEFAULT_RAM}" + fi + fi + + DISK_SIZE="${final_disk}" + CORE_COUNT="${final_cpu}" + RAM_SIZE="${final_ram}" VERBOSE=${var_verbose:-"${1:-no}"} PW=${var_pw:-""} CT_ID=${var_ctid:-$NEXTID} @@ -1033,7 +1073,7 @@ default_var_settings() { _find_default_vars >/dev/null 2>&1 && return 0 local canonical="/usr/local/community-scripts/default.vars" - msg_info "No default.vars found. Creating ${canonical}" + # Silent creation - no msg_info output mkdir -p /usr/local/community-scripts # Pick storages before writing the file (always ask unless only one) @@ -1087,7 +1127,7 @@ EOF choose_and_set_storage_for_file "$canonical" container chmod 0644 "$canonical" - msg_ok "Created ${canonical}" + # Silent creation - no output message } # Whitelist check @@ -1492,7 +1532,7 @@ ensure_storage_selection_for_vars_file() { choose_and_set_storage_for_file "$vf" template choose_and_set_storage_for_file "$vf" container - msg_ok "Storage configuration saved to $(basename "$vf")" + # Silent operation - no output message } diagnostics_menu() { @@ -1757,7 +1797,7 @@ choose_and_set_storage_for_file() { export TEMPLATE_STORAGE="$STORAGE_RESULT" fi - msg_ok "Updated ${key} → ${STORAGE_RESULT}" + # Silent operation - no output message } # ------------------------------------------------------------------------------ From 2ff12b1f01fd76913790147a7a3e7aad449d968b Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 4 Nov 2025 16:30:23 +0100 Subject: [PATCH 1691/1733] Update build.func --- misc/build.func | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/misc/build.func b/misc/build.func index 11222d75f..ac1dee32c 100644 --- a/misc/build.func +++ b/misc/build.func @@ -1108,7 +1108,7 @@ var_ipv6_method=none var_ssh=no # var_ssh_authorized_key= -# APT cacher (optional) +# APT cacher (optional - with example) # var_apt_cacher=yes # var_apt_cacher_ip=192.168.1.10 @@ -2453,21 +2453,8 @@ EOF exit 1 fi - # Try to reach gateway - gw_ok=0 - for i in {1..10}; do - if pct exec "$CTID" -- ping -c1 -W1 "${GATEWAY:-8.8.8.8}" >/dev/null 2>&1; then - gw_ok=1 - break - fi - sleep 1 - done - - if [ "$gw_ok" -eq 1 ]; then - msg_ok "Network in LXC is reachable (IP $ip_in_lxc)" - else - msg_warn "Network reachable but gateway check failed" - fi + # Simple connectivity check - just verify IP is assigned + msg_ok "Network configured (IP: $ip_in_lxc)" fi # Function to get correct GID inside container get_container_gid() { @@ -2611,7 +2598,8 @@ fix_gpu_gids() { return 0 fi - msg_info "Detecting and setting correct GPU group IDs" + # Silent operation to avoid spinner conflicts + echo -e "\n 🔧 Detecting and setting correct GPU group IDs" # Ermittle die tatsächlichen GIDs aus dem Container local video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") @@ -2632,7 +2620,7 @@ fix_gpu_gids() { [[ -z "$render_gid" ]] && render_gid="104" # Ultimate fallback fi - msg_info "Container GIDs detected - video:${video_gid}, render:${render_gid}" + echo " ℹ️ Container GIDs detected - video:${video_gid}, render:${render_gid}" # Prüfe ob die GIDs von den Defaults abweichen local need_update=0 @@ -2641,7 +2629,7 @@ fix_gpu_gids() { fi if [[ $need_update -eq 1 ]]; then - msg_info "Updating device GIDs in container config" + echo " 🔄 Updating device GIDs in container config" # Stoppe Container für Config-Update pct stop "$CTID" >/dev/null 2>&1 @@ -2679,9 +2667,9 @@ fix_gpu_gids() { pct start "$CTID" >/dev/null 2>&1 sleep 3 - msg_ok "Device GIDs updated successfully" + echo -e " ✔️ Device GIDs updated successfully\n" else - msg_ok "Device GIDs are already correct" + echo -e " ✔️ Device GIDs are already correct\n" fi if [[ "$CT_TYPE" == "0" ]]; then pct exec "$CTID" -- bash -c " @@ -3004,6 +2992,8 @@ create_lxc_container() { ) pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)." + + msg_ok "Template search completed" #echo "[DEBUG] pveam available output (first 5 lines with .tar files):" #pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | head -5 | sed 's/^/ /' From 8ccd06b596744601c5cbdb3c6c2b59930f1e1032 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 4 Nov 2025 16:38:46 +0100 Subject: [PATCH 1692/1733] Refactor message output and improve SSH check logic Replaces many msg_info calls with msg_custom for more consistent and expressive status messages in build.func. Refines SSH client detection in core.func to better distinguish local, subnet, and external connections, and adds additional warnings for external SSH usage. --- misc/build.func | 50 ++++++++++++++++++++++++------------------------- misc/core.func | 15 ++++++++++++++- 2 files changed, 39 insertions(+), 26 deletions(-) diff --git a/misc/build.func b/misc/build.func index ac1dee32c..ba33674ba 100644 --- a/misc/build.func +++ b/misc/build.func @@ -220,7 +220,7 @@ maxkeys_check() { exit 1 fi - echo -e "${CM}${GN} All kernel key limits are within safe thresholds.${CL}" + # Silent success - only show errors if they exist } # ------------------------------------------------------------------------------ @@ -351,7 +351,7 @@ base_settings() { if [[ -n "$APT_CACHER_IP" && "$APT_CACHER" == "yes" ]]; then if ! curl -s --connect-timeout 2 "http://${APT_CACHER_IP}:3142" >/dev/null 2>&1; then msg_warn "APT Cacher configured but not reachable at ${APT_CACHER_IP}:3142" - msg_info "Disabling APT Cacher for this installation" + msg_custom "⚠️" "${YW}" "Disabling APT Cacher for this installation" APT_CACHER="" APT_CACHER_IP="" else @@ -1497,7 +1497,7 @@ maybe_offer_save_app_defaults() { break ;; "Keep Current") - msg_info "Keeping current app defaults: ${app_vars_path}" + msg_custom "ℹ️" "${BL}" "Keeping current app defaults: ${app_vars_path}" break ;; "View Diff") @@ -1506,7 +1506,7 @@ maybe_offer_save_app_defaults() { --scrolltext --textbox "$diff_tmp" 25 100 ;; "Cancel" | *) - msg_info "Canceled. No changes to app defaults." + msg_custom "🚫" "${YW}" "Canceled. No changes to app defaults." break ;; esac @@ -2212,7 +2212,7 @@ build_container() { # Check for Intel GPU - look for Intel vendor ID [8086] if echo "$pci_vga_info" | grep -q "\[8086:"; then - msg_info "Detected Intel GPU" + msg_custom "🎮" "${BL}" "Detected Intel GPU" if [[ -d /dev/dri ]]; then for d in /dev/dri/renderD* /dev/dri/card*; do [[ -e "$d" ]] && INTEL_DEVICES+=("$d") @@ -2222,7 +2222,7 @@ build_container() { # Check for AMD GPU - look for AMD vendor IDs [1002] (AMD/ATI) or [1022] (AMD) if echo "$pci_vga_info" | grep -qE "\[1002:|\[1022:"; then - msg_info "Detected AMD GPU" + msg_custom "🎮" "${RD}" "Detected AMD GPU" if [[ -d /dev/dri ]]; then # Only add if not already claimed by Intel if [[ ${#INTEL_DEVICES[@]} -eq 0 ]]; then @@ -2235,7 +2235,7 @@ build_container() { # Check for NVIDIA GPU - look for NVIDIA vendor ID [10de] if echo "$pci_vga_info" | grep -q "\[10de:"; then - msg_info "Detected NVIDIA GPU" + msg_custom "🎮" "${GN}" "Detected NVIDIA GPU" # Simple passthrough - just bind /dev/nvidia* devices if they exist for d in /dev/nvidia* /dev/nvidiactl /dev/nvidia-modeset /dev/nvidia-uvm /dev/nvidia-uvm-tools; do @@ -2243,10 +2243,10 @@ build_container() { done if [[ ${#NVIDIA_DEVICES[@]} -gt 0 ]]; then - msg_info "Found ${#NVIDIA_DEVICES[@]} NVIDIA device(s) for passthrough" + msg_custom "🎮" "${GN}" "Found ${#NVIDIA_DEVICES[@]} NVIDIA device(s) for passthrough" else msg_warn "NVIDIA GPU detected via PCI but no /dev/nvidia* devices found" - msg_info "Skipping NVIDIA passthrough (host drivers may not be loaded)" + msg_custom "ℹ️" "${YW}" "Skipping NVIDIA passthrough (host drivers may not be loaded)" fi fi @@ -2307,7 +2307,7 @@ EOF fi if [[ $gpu_count -eq 0 ]]; then - msg_info "No GPU devices found for passthrough" + msg_custom "ℹ️" "${YW}" "No GPU devices found for passthrough" return 0 fi @@ -2316,7 +2316,7 @@ EOF if [[ $gpu_count -eq 1 ]]; then # Automatic selection for single GPU selected_gpu="${available_gpus[0]}" - msg_info "Automatically configuring ${selected_gpu} GPU passthrough" + msg_custom "⚙️" "${GN}" "Automatically configuring ${selected_gpu} GPU passthrough" else # Multiple GPUs - ask user echo -e "\n${INFO} Multiple GPU types detected:" @@ -2407,7 +2407,7 @@ EOF # Coral TPU passthrough if [[ -e /dev/apex_0 ]]; then - msg_info "Detected Coral TPU - configuring passthrough" + msg_custom "🔌" "${BL}" "Detected Coral TPU - configuring passthrough" echo "lxc.mount.entry: /dev/apex_0 dev/apex_0 none bind,optional,create=file" >>"$LXC_CONFIG" fi } @@ -2546,7 +2546,7 @@ destroy_lxc() { fi ;; "" | n | no) - msg_info "Container was not removed." + msg_custom "ℹ️" "${BL}" "Container was not removed." ;; *) msg_warn "Invalid response. Container was not removed." @@ -2599,7 +2599,7 @@ fix_gpu_gids() { fi # Silent operation to avoid spinner conflicts - echo -e "\n 🔧 Detecting and setting correct GPU group IDs" + msg_custom "🔧" "${BL}" "Detecting and setting correct GPU group IDs" # Ermittle die tatsächlichen GIDs aus dem Container local video_gid=$(pct exec "$CTID" -- sh -c "getent group video 2>/dev/null | cut -d: -f3") @@ -2620,7 +2620,7 @@ fix_gpu_gids() { [[ -z "$render_gid" ]] && render_gid="104" # Ultimate fallback fi - echo " ℹ️ Container GIDs detected - video:${video_gid}, render:${render_gid}" + msg_custom "ℹ️" "${DGN}" "Container GIDs detected - video:${video_gid}, render:${render_gid}" # Prüfe ob die GIDs von den Defaults abweichen local need_update=0 @@ -2629,7 +2629,7 @@ fix_gpu_gids() { fi if [[ $need_update -eq 1 ]]; then - echo " 🔄 Updating device GIDs in container config" + msg_custom "🔄" "${YW}" "Updating device GIDs in container config" # Stoppe Container für Config-Update pct stop "$CTID" >/dev/null 2>&1 @@ -2667,9 +2667,9 @@ fix_gpu_gids() { pct start "$CTID" >/dev/null 2>&1 sleep 3 - echo -e " ✔️ Device GIDs updated successfully\n" + msg_ok "Device GIDs updated successfully" else - echo -e " ✔️ Device GIDs are already correct\n" + msg_ok "Device GIDs are already correct" fi if [[ "$CT_TYPE" == "0" ]]; then pct exec "$CTID" -- bash -c " @@ -2992,7 +2992,7 @@ create_lxc_container() { ) pveam update >/dev/null 2>&1 || msg_warn "Could not update template catalog (pveam update failed)." - + msg_ok "Template search completed" #echo "[DEBUG] pveam available output (first 5 lines with .tar files):" @@ -3081,7 +3081,7 @@ create_lxc_container() { exit 225 fi else - msg_info "Installation cancelled" + msg_custom "🚫" "${YW}" "Installation cancelled" exit 0 fi else @@ -3174,7 +3174,7 @@ create_lxc_container() { exit 220 } else - msg_info "Installation cancelled" + msg_custom "🚫" "${YW}" "Installation cancelled" exit 1 fi else @@ -3187,9 +3187,9 @@ create_lxc_container() { # Validate that we found a template if [[ -z "$TEMPLATE" ]]; then msg_error "No template found for ${PCT_OSTYPE} ${PCT_OSVERSION}" - msg_info "Please check:" - msg_info " - Is pveam catalog available? (run: pveam available -section system)" - msg_info " - Does the template exist for your OS version?" + msg_custom "ℹ️" "${YW}" "Please check:" + msg_custom " •" "${YW}" "Is pveam catalog available? (run: pveam available -section system)" + msg_custom " •" "${YW}" "Does the template exist for your OS version?" exit 225 fi @@ -3227,7 +3227,7 @@ create_lxc_container() { TEMPLATE="$ONLINE_TEMPLATE" NEED_DOWNLOAD=1 else - msg_info "Continuing with local template $TEMPLATE" + msg_custom "ℹ️" "${BL}" "Continuing with local template $TEMPLATE" fi fi diff --git a/misc/core.func b/misc/core.func index d4e288483..699ac1168 100644 --- a/misc/core.func +++ b/misc/core.func @@ -231,11 +231,24 @@ ssh_check() { local client_ip=$(awk '{print $1}' <<<"$SSH_CLIENT") local host_ip=$(hostname -I | awk '{print $1}') - if [[ "$client_ip" == "127.0.0.1" || "$client_ip" == "$host_ip" ]]; then + # Check if connection is local (Proxmox WebUI or same machine) + # - localhost (127.0.0.1, ::1) + # - same IP as host + # - local network range (10.x, 172.16-31.x, 192.168.x) + if [[ "$client_ip" == "127.0.0.1" || "$client_ip" == "::1" || "$client_ip" == "$host_ip" ]]; then return fi + # Check if client is in same local network (optional, safer approach) + local host_subnet=$(echo "$host_ip" | cut -d. -f1-3) + local client_subnet=$(echo "$client_ip" | cut -d. -f1-3) + if [[ "$host_subnet" == "$client_subnet" ]]; then + return + fi + + # Only warn for truly external connections msg_warn "Running via external SSH (client: $client_ip)." + msg_warn "For better stability, consider using the Proxmox Shell (Console) instead." fi } From 60ddf3af72fcd28b8208b484754d49495fb7ba28 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 4 Nov 2025 16:44:21 +0100 Subject: [PATCH 1693/1733] Refactor and enhance setup_uv function Improves architecture and OS detection, adds support for i686, and streamlines version fetching and installation logic. Introduces uvx wrapper installation when requested, generates shell completions, and optionally installs a specific Python version via uv. Cleans up temporary directory handling and error management for robustness. --- misc/tools.func | 126 ++++++++++++++++++++++++++++++------------------ 1 file changed, 79 insertions(+), 47 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index d0e6f2c95..ee94e3d22 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -4180,65 +4180,79 @@ function setup_rust() { function setup_uv() { local UV_BIN="/usr/local/bin/uv" + local UVX_BIN="/usr/local/bin/uvx" local TMP_DIR=$(mktemp -d) local CACHED_VERSION + + # Trap für TMP Cleanup + trap "rm -rf '$TMP_DIR'" EXIT + CACHED_VERSION=$(get_cached_version "uv") + # Architektur-Detection local ARCH=$(uname -m) - local UV_TAR + local OS_TYPE="" + local UV_TAR="" + + if grep -qi "alpine" /etc/os-release; then + OS_TYPE="musl" + else + OS_TYPE="gnu" + fi case "$ARCH" in x86_64) - if grep -qi "alpine" /etc/os-release; then - UV_TAR="uv-x86_64-unknown-linux-musl.tar.gz" - else - UV_TAR="uv-x86_64-unknown-linux-gnu.tar.gz" - fi + UV_TAR="uv-x86_64-unknown-linux-${OS_TYPE}.tar.gz" ;; aarch64) - if grep -qi "alpine" /etc/os-release; then - UV_TAR="uv-aarch64-unknown-linux-musl.tar.gz" - else - UV_TAR="uv-aarch64-unknown-linux-gnu.tar.gz" - fi + UV_TAR="uv-aarch64-unknown-linux-${OS_TYPE}.tar.gz" + ;; + i686) + UV_TAR="uv-i686-unknown-linux-${OS_TYPE}.tar.gz" ;; *) - msg_error "Unsupported architecture: $ARCH" - rm -rf "$TMP_DIR" + msg_error "Unsupported architecture: $ARCH (supported: x86_64, aarch64, i686)" return 1 ;; esac ensure_dependencies jq - local LATEST_VERSION + # Fetch latest version local releases_json - releases_json=$(curl -fsSL --max-time 15 https://api.github.com/repos/astral-sh/uv/releases/latest 2>/dev/null || echo "") + releases_json=$(curl -fsSL --max-time 15 \ + "https://api.github.com/repos/astral-sh/uv/releases/latest" 2>/dev/null || echo "") if [[ -z "$releases_json" ]]; then msg_error "Could not fetch latest uv version from GitHub API" - rm -rf "$TMP_DIR" return 1 fi - LATEST_VERSION=$(echo "$releases_json" | jq -r '.tag_name' 2>/dev/null | sed 's/^v//' || echo "") + local LATEST_VERSION + LATEST_VERSION=$(echo "$releases_json" | jq -r '.tag_name' 2>/dev/null | sed 's/^v//') if [[ -z "$LATEST_VERSION" ]]; then msg_error "Could not parse uv version from GitHub API response" - rm -rf "$TMP_DIR" return 1 fi # Get currently installed version local INSTALLED_VERSION="" if [[ -x "$UV_BIN" ]]; then - INSTALLED_VERSION=$($UV_BIN -V 2>/dev/null | awk '{print $2}') + INSTALLED_VERSION=$("$UV_BIN" --version 2>/dev/null | awk '{print $2}') fi # Scenario 1: Already at latest version if [[ -n "$INSTALLED_VERSION" && "$INSTALLED_VERSION" == "$LATEST_VERSION" ]]; then cache_installed_version "uv" "$LATEST_VERSION" - rm -rf "$TMP_DIR" + + # Check if uvx is needed and missing + if [[ "${USE_UVX:-NO}" == "YES" ]] && [[ ! -x "$UVX_BIN" ]]; then + msg_info "Installing uvx wrapper" + _install_uvx_wrapper || return 1 + msg_ok "uvx wrapper installed" + fi + return 0 fi @@ -4249,52 +4263,70 @@ function setup_uv() { msg_info "Setup uv $LATEST_VERSION" fi - local UV_URL="https://github.com/astral-sh/uv/releases/latest/download/${UV_TAR}" - curl -fsSL "$UV_URL" -o "$TMP_DIR/uv.tar.gz" || { - msg_error "Failed to download uv" - rm -rf "$TMP_DIR" + # Download + local UV_URL="https://github.com/astral-sh/uv/releases/download/v${LATEST_VERSION}/${UV_TAR}" + + $STD curl -fsSL "$UV_URL" -o "$TMP_DIR/uv.tar.gz" || { + msg_error "Failed to download uv from $UV_URL" return 1 } - tar -xzf "$TMP_DIR/uv.tar.gz" -C "$TMP_DIR" || { + # Extract + $STD tar -xzf "$TMP_DIR/uv.tar.gz" -C "$TMP_DIR" || { msg_error "Failed to extract uv" - rm -rf "$TMP_DIR" return 1 } - install -m 755 "$TMP_DIR"/*/uv "$UV_BIN" || { + # Install uv binary + $STD install -m 755 "$TMP_DIR/uv/uv" "$UV_BIN" || { msg_error "Failed to install uv binary" - rm -rf "$TMP_DIR" return 1 } - rm -rf "$TMP_DIR" ensure_usr_local_bin_persist export PATH="/usr/local/bin:$PATH" - $STD uv python update-shell || true + # Optional: Install uvx wrapper + if [[ "${USE_UVX:-NO}" == "YES" ]]; then + msg_info "Installing uvx wrapper" + _install_uvx_wrapper || { + msg_error "Failed to install uvx wrapper" + return 1 + } + msg_ok "uvx wrapper installed" + fi + + # Optional: Generate shell completions + $STD uv generate-shell-completion bash >/etc/bash_completion.d/uv 2>/dev/null || true + $STD uv generate-shell-completion zsh >/usr/share/zsh/site-functions/_uv 2>/dev/null || true + + # Optional: Install specific Python version if requested + if [[ -n "${PYTHON_VERSION:-}" ]]; then + msg_info "Installing Python $PYTHON_VERSION via uv" + $STD uv python install "$PYTHON_VERSION" || { + msg_error "Failed to install Python $PYTHON_VERSION" + return 1 + } + msg_ok "Python $PYTHON_VERSION installed" + fi + cache_installed_version "uv" "$LATEST_VERSION" msg_ok "Setup uv $LATEST_VERSION" +} - # Optional: Install specific Python version - if [[ -n "${PYTHON_VERSION:-}" ]]; then - local VERSION_MATCH - VERSION_MATCH=$(uv python list --only-downloads 2>/dev/null | - grep -E "^cpython-${PYTHON_VERSION//./\\.}\.[0-9]+-linux" | - cut -d'-' -f2 | sort -V | tail -n1) +# Helper function to install uvx wrapper +_install_uvx_wrapper() { + local UVX_BIN="/usr/local/bin/uvx" - if [[ -z "$VERSION_MATCH" ]]; then - msg_error "No matching Python $PYTHON_VERSION.x version found" - return 1 - fi + cat >"$UVX_BIN" <<'EOF' +#!/bin/bash +# uvx - Run Python applications from PyPI as command-line tools +# Wrapper for: uv tool run +exec /usr/local/bin/uv tool run "$@" +EOF - if ! uv python list 2>/dev/null | grep -q "cpython-${VERSION_MATCH}-linux.*uv/python"; then - $STD uv python install "$VERSION_MATCH" || { - msg_error "Failed to install Python $VERSION_MATCH" - return 1 - } - fi - fi + chmod +x "$UVX_BIN" + return 0 } # ------------------------------------------------------------------------------ From a6cdb474a12395a437a30973b90fecbb28692bff Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 4 Nov 2025 16:52:12 +0100 Subject: [PATCH 1694/1733] Update build.func --- misc/build.func | 6 ------ 1 file changed, 6 deletions(-) diff --git a/misc/build.func b/misc/build.func index ba33674ba..0dea49bd3 100644 --- a/misc/build.func +++ b/misc/build.func @@ -3002,12 +3002,6 @@ create_lxc_container() { mapfile -t ONLINE_TEMPLATES < <(pveam available -section system 2>/dev/null | grep -E '\.(tar\.zst|tar\.xz|tar\.gz)$' | awk '{print $2}' | grep -E "${SEARCH_PATTERN}.*${TEMPLATE_PATTERN}" | sort -t - -k 2 -V 2>/dev/null || true) #echo "[DEBUG] After filtering: ${#ONLINE_TEMPLATES[@]} online templates found" set -u - if [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]]; then - #echo "[DEBUG] Online templates:" - for tmpl in "${ONLINE_TEMPLATES[@]}"; do - echo " - $tmpl" - done - fi ONLINE_TEMPLATE="" [[ ${#ONLINE_TEMPLATES[@]} -gt 0 ]] && ONLINE_TEMPLATE="${ONLINE_TEMPLATES[-1]}" From b55e8f5f34c3e6a4381f9c21554cb1edb6591d53 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 4 Nov 2025 16:59:02 +0100 Subject: [PATCH 1695/1733] Improve logging and error handling with session IDs Introduces a SESSION_ID variable for log file naming and tracking, updates log file paths to include timestamps and session IDs, and enhances error handling output to use custom message functions when available. Also improves log file management and user guidance for viewing logs, and refactors error handler to better support containerized environments. --- misc/build.func | 4 +- misc/core.func | 8 +- misc/error_handler.func | 237 +++++++++++++++++++++++----------------- 3 files changed, 144 insertions(+), 105 deletions(-) diff --git a/misc/build.func b/misc/build.func index 0dea49bd3..a2270e845 100644 --- a/misc/build.func +++ b/misc/build.func @@ -27,7 +27,8 @@ variables() { DIAGNOSTICS="yes" # sets the DIAGNOSTICS variable to "yes", used for the API call. METHOD="default" # sets the METHOD variable to "default", used for the API call. RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)" # generates a random UUID and sets it to the RANDOM_UUID variable. - CTTYPE="${CTTYPE:-${CT_TYPE:-1}}" + SESSION_ID="${RANDOM_UUID:0:8}" # Short session ID (first 8 chars of UUID) for log files + CTTYPE="${CTTYPE:-${CT_TYPE:-1}}"} # Get Proxmox VE version and kernel version if command -v pveversion >/dev/null 2>&1; then @@ -2144,6 +2145,7 @@ build_container() { fi export DIAGNOSTICS="$DIAGNOSTICS" export RANDOM_UUID="$RANDOM_UUID" + export SESSION_ID="$SESSION_ID" export CACHER="$APT_CACHER" export CACHER_IP="$APT_CACHER_IP" export tz="$timezone" diff --git a/misc/core.func b/misc/core.func index 699ac1168..dfa6b2e7e 100644 --- a/misc/core.func +++ b/misc/core.func @@ -108,7 +108,7 @@ set_std_mode() { fi } -SILENT_LOGFILE="/tmp/silent.$$.log" +SILENT_LOGFILE="/tmp/install-$(date +%Y%m%d_%H%M%S)_${SESSION_ID:-$(date +%s)}.log" silent() { local cmd="$*" @@ -133,8 +133,8 @@ silent() { explanation="$(explain_exit_code "$rc")" printf "\e[?25h" - echo -e "\n${RD}[ERROR]${CL} in line ${RD}${caller_line}${CL}: exit code ${RD}${rc}${CL} (${explanation})" - echo -e "${RD}Command:${CL} ${YWB}${cmd}${CL}\n" + msg_error "in line ${caller_line}: exit code ${rc} (${explanation})" + msg_custom "→" "${YWB}" "${cmd}" if [[ -s "$SILENT_LOGFILE" ]]; then local log_lines=$(wc -l <"$SILENT_LOGFILE") @@ -144,7 +144,7 @@ silent() { # Show how to view full log if there are more lines if [[ $log_lines -gt 10 ]]; then - echo -e "${YW}View full log (${log_lines} lines):${CL} cat $SILENT_LOGFILE" + msg_custom "📋" "${YW}" "View full log (${log_lines} lines): /tmp/install-*_${SESSION_ID:-*}.log" fi fi diff --git a/misc/error_handler.func b/misc/error_handler.func index d2f21d087..2599d59b0 100644 --- a/misc/error_handler.func +++ b/misc/error_handler.func @@ -8,141 +8,178 @@ # ------------------------------------------------------------------------------ explain_exit_code() { - local code="$1" - case "$code" in - # --- Generic / Shell --- - 1) echo "General error / Operation not permitted" ;; - 2) echo "Misuse of shell builtins (e.g. syntax error)" ;; - 126) echo "Command invoked cannot execute (permission problem?)" ;; - 127) echo "Command not found" ;; - 128) echo "Invalid argument to exit" ;; - 130) echo "Terminated by Ctrl+C (SIGINT)" ;; - 137) echo "Killed (SIGKILL / Out of memory?)" ;; - 139) echo "Segmentation fault (core dumped)" ;; - 143) echo "Terminated (SIGTERM)" ;; + local code="$1" + case "$code" in + # --- Generic / Shell --- + 1) echo "General error / Operation not permitted" ;; + 2) echo "Misuse of shell builtins (e.g. syntax error)" ;; + 126) echo "Command invoked cannot execute (permission problem?)" ;; + 127) echo "Command not found" ;; + 128) echo "Invalid argument to exit" ;; + 130) echo "Terminated by Ctrl+C (SIGINT)" ;; + 137) echo "Killed (SIGKILL / Out of memory?)" ;; + 139) echo "Segmentation fault (core dumped)" ;; + 143) echo "Terminated (SIGTERM)" ;; - # --- Package manager / APT / DPKG --- - 100) echo "APT: Package manager error (broken packages / dependency problems)" ;; - 101) echo "APT: Configuration error (bad sources.list, malformed config)" ;; - 255) echo "DPKG: Fatal internal error" ;; + # --- Package manager / APT / DPKG --- + 100) echo "APT: Package manager error (broken packages / dependency problems)" ;; + 101) echo "APT: Configuration error (bad sources.list, malformed config)" ;; + 255) echo "DPKG: Fatal internal error" ;; - # --- Node.js / npm / pnpm / yarn --- - 243) echo "Node.js: Out of memory (JavaScript heap out of memory)" ;; - 245) echo "Node.js: Invalid command-line option" ;; - 246) echo "Node.js: Internal JavaScript Parse Error" ;; - 247) echo "Node.js: Fatal internal error" ;; - 248) echo "Node.js: Invalid C++ addon / N-API failure" ;; - 249) echo "Node.js: Inspector error" ;; - 254) echo "npm/pnpm/yarn: Unknown fatal error" ;; + # --- Node.js / npm / pnpm / yarn --- + 243) echo "Node.js: Out of memory (JavaScript heap out of memory)" ;; + 245) echo "Node.js: Invalid command-line option" ;; + 246) echo "Node.js: Internal JavaScript Parse Error" ;; + 247) echo "Node.js: Fatal internal error" ;; + 248) echo "Node.js: Invalid C++ addon / N-API failure" ;; + 249) echo "Node.js: Inspector error" ;; + 254) echo "npm/pnpm/yarn: Unknown fatal error" ;; - # --- Python / pip / uv --- - 210) echo "Python: Virtualenv / uv environment missing or broken" ;; - 211) echo "Python: Dependency resolution failed" ;; - 212) echo "Python: Installation aborted (permissions or EXTERNALLY-MANAGED)" ;; + # --- Python / pip / uv --- + 210) echo "Python: Virtualenv / uv environment missing or broken" ;; + 211) echo "Python: Dependency resolution failed" ;; + 212) echo "Python: Installation aborted (permissions or EXTERNALLY-MANAGED)" ;; - # --- PostgreSQL --- - 231) echo "PostgreSQL: Connection failed (server not running / wrong socket)" ;; - 232) echo "PostgreSQL: Authentication failed (bad user/password)" ;; - 233) echo "PostgreSQL: Database does not exist" ;; - 234) echo "PostgreSQL: Fatal error in query / syntax" ;; + # --- PostgreSQL --- + 231) echo "PostgreSQL: Connection failed (server not running / wrong socket)" ;; + 232) echo "PostgreSQL: Authentication failed (bad user/password)" ;; + 233) echo "PostgreSQL: Database does not exist" ;; + 234) echo "PostgreSQL: Fatal error in query / syntax" ;; - # --- MySQL / MariaDB --- - 241) echo "MySQL/MariaDB: Connection failed (server not running / wrong socket)" ;; - 242) echo "MySQL/MariaDB: Authentication failed (bad user/password)" ;; - 243) echo "MySQL/MariaDB: Database does not exist" ;; - 244) echo "MySQL/MariaDB: Fatal error in query / syntax" ;; + # --- MySQL / MariaDB --- + 241) echo "MySQL/MariaDB: Connection failed (server not running / wrong socket)" ;; + 242) echo "MySQL/MariaDB: Authentication failed (bad user/password)" ;; + 243) echo "MySQL/MariaDB: Database does not exist" ;; + 244) echo "MySQL/MariaDB: Fatal error in query / syntax" ;; - # --- MongoDB --- - 251) echo "MongoDB: Connection failed (server not running)" ;; - 252) echo "MongoDB: Authentication failed (bad user/password)" ;; - 253) echo "MongoDB: Database not found" ;; - 254) echo "MongoDB: Fatal query error" ;; + # --- MongoDB --- + 251) echo "MongoDB: Connection failed (server not running)" ;; + 252) echo "MongoDB: Authentication failed (bad user/password)" ;; + 253) echo "MongoDB: Database not found" ;; + 254) echo "MongoDB: Fatal query error" ;; - # --- Proxmox Custom Codes --- - 200) echo "Custom: Failed to create lock file" ;; - 203) echo "Custom: Missing CTID variable" ;; - 204) echo "Custom: Missing PCT_OSTYPE variable" ;; - 205) echo "Custom: Invalid CTID (<100)" ;; - 209) echo "Custom: Container creation failed" ;; - 210) echo "Custom: Cluster not quorate" ;; - 214) echo "Custom: Not enough storage space" ;; - 215) echo "Custom: Container ID not listed" ;; - 216) echo "Custom: RootFS entry missing in config" ;; - 217) echo "Custom: Storage does not support rootdir" ;; - 220) echo "Custom: Unable to resolve template path" ;; - 222) echo "Custom: Template download failed after 3 attempts" ;; - 223) echo "Custom: Template not available after download" ;; - 231) echo "Custom: LXC stack upgrade/retry failed" ;; + # --- Proxmox Custom Codes --- + 200) echo "Custom: Failed to create lock file" ;; + 203) echo "Custom: Missing CTID variable" ;; + 204) echo "Custom: Missing PCT_OSTYPE variable" ;; + 205) echo "Custom: Invalid CTID (<100)" ;; + 209) echo "Custom: Container creation failed" ;; + 210) echo "Custom: Cluster not quorate" ;; + 214) echo "Custom: Not enough storage space" ;; + 215) echo "Custom: Container ID not listed" ;; + 216) echo "Custom: RootFS entry missing in config" ;; + 217) echo "Custom: Storage does not support rootdir" ;; + 220) echo "Custom: Unable to resolve template path" ;; + 222) echo "Custom: Template download failed after 3 attempts" ;; + 223) echo "Custom: Template not available after download" ;; + 231) echo "Custom: LXC stack upgrade/retry failed" ;; - # --- Default --- - *) echo "Unknown error" ;; - esac + # --- Default --- + *) echo "Unknown error" ;; + esac } # === Error handler ============================================================ error_handler() { - local exit_code=${1:-$?} - local command=${2:-${BASH_COMMAND:-unknown}} - local line_number=${BASH_LINENO[0]:-unknown} + local exit_code=${1:-$?} + local command=${2:-${BASH_COMMAND:-unknown}} + local line_number=${BASH_LINENO[0]:-unknown} - command="${command//\$STD/}" + command="${command//\$STD/}" - if [[ "$exit_code" -eq 0 ]]; then - return 0 - fi + if [[ "$exit_code" -eq 0 ]]; then + return 0 + fi - local explanation - explanation="$(explain_exit_code "$exit_code")" + local explanation + explanation="$(explain_exit_code "$exit_code")" - printf "\e[?25h" + printf "\e[?25h" + + # Use msg_error if available, fallback to echo + if declare -f msg_error >/dev/null 2>&1; then + msg_error "in line ${line_number}: exit code ${exit_code} (${explanation}): while executing command ${command}" + else echo -e "\n${RD}[ERROR]${CL} in line ${RD}${line_number}${CL}: exit code ${RD}${exit_code}${CL} (${explanation}): while executing command ${YWB}${command}${CL}\n" + fi - if [[ -n "${DEBUG_LOGFILE:-}" ]]; then - { - echo "------ ERROR ------" - echo "Timestamp : $(date '+%Y-%m-%d %H:%M:%S')" - echo "Exit Code : $exit_code ($explanation)" - echo "Line : $line_number" - echo "Command : $command" - echo "-------------------" - } >>"$DEBUG_LOGFILE" + if [[ -n "${DEBUG_LOGFILE:-}" ]]; then + { + echo "------ ERROR ------" + echo "Timestamp : $(date '+%Y-%m-%d %H:%M:%S')" + echo "Exit Code : $exit_code ($explanation)" + echo "Line : $line_number" + echo "Command : $command" + echo "-------------------" + } >>"$DEBUG_LOGFILE" + fi + + if [[ -n "${SILENT_LOGFILE:-}" && -s "$SILENT_LOGFILE" ]]; then + echo "--- Last 20 lines of silent log ---" + tail -n 20 "$SILENT_LOGFILE" + echo "-----------------------------------" + + if [[ -n "${CTID:-}" ]]; then + local HOST_LOG="/tmp/install-$(date +%Y%m%d_%H%M%S)_${SESSION_ID:-error}.log" + if pct push $CTID "$SILENT_LOGFILE" "$HOST_LOG" 2>/dev/null; then + if declare -f msg_custom >/dev/null 2>&1; then + msg_custom "✓" "${GN}" "Full log saved to host: ${HOST_LOG}" + else + echo -e "${GN}✓ Full log saved to host:${CL} ${BL}${HOST_LOG}${CL}" + fi + else + if declare -f msg_custom >/dev/null 2>&1; then + msg_custom "📋" "${YW}" "Full log path in container: ${SILENT_LOGFILE}" + else + echo -e "${YW}Full log path in container:${CL} ${BL}${SILENT_LOGFILE}${CL}" + fi + fi + else + if declare -f msg_custom >/dev/null 2>&1; then + msg_custom "📋" "${YW}" "Full log: ${SILENT_LOGFILE}" + else + echo -e "${YW}Full log:${CL} ${BL}${SILENT_LOGFILE}${CL}" + fi fi + fi - if [[ -n "${SILENT_LOGFILE:-}" && -s "$SILENT_LOGFILE" ]]; then - echo "--- Last 20 lines of silent log ($SILENT_LOGFILE) ---" - tail -n 20 "$SILENT_LOGFILE" - echo "---------------------------------------------------" - fi - - exit "$exit_code" + exit "$exit_code" } # === Exit handler ============================================================= on_exit() { - local exit_code=$? - [[ -n "${lockfile:-}" && -e "$lockfile" ]] && rm -f "$lockfile" - exit "$exit_code" + local exit_code=$? + [[ -n "${lockfile:-}" && -e "$lockfile" ]] && rm -f "$lockfile" + exit "$exit_code" } # === Signal handlers ========================================================== on_interrupt() { + if declare -f msg_error >/dev/null 2>&1; then + msg_error "Interrupted by user (SIGINT)" + else echo -e "\n${RD}Interrupted by user (SIGINT)${CL}" - exit 130 + fi + exit 130 } on_terminate() { + if declare -f msg_error >/dev/null 2>&1; then + msg_error "Terminated by signal (SIGTERM)" + else echo -e "\n${RD}Terminated by signal (SIGTERM)${CL}" - exit 143 + fi + exit 143 } # === Init traps =============================================================== catch_errors() { - set -Ee -o pipefail - if [ "${STRICT_UNSET:-0}" = "1" ]; then - set -u - fi - trap 'error_handler' ERR - trap on_exit EXIT - trap on_interrupt INT - trap on_terminate TERM + set -Ee -o pipefail + if [ "${STRICT_UNSET:-0}" = "1" ]; then + set -u + fi + trap 'error_handler' ERR + trap on_exit EXIT + trap on_interrupt INT + trap on_terminate TERM } From dd34f2d8b3c5b9365ed426f7fb955c7df21dbf7d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 4 Nov 2025 17:03:02 +0100 Subject: [PATCH 1696/1733] Fix npm global module version detection in setup_nodejs Replaces direct npm list checks with STD-prefixed commands and output parsing to improve reliability when detecting installed global module versions in the setup_nodejs function. --- misc/tools.func | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index ee94e3d22..6e687d527 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -3419,8 +3419,8 @@ function setup_nodejs() { fi # Check if the module is already installed - if npm list -g --depth=0 "$MODULE_NAME" >/dev/null 2>&1; then - MODULE_INSTALLED_VERSION="$(npm list -g --depth=0 "$MODULE_NAME" | grep "$MODULE_NAME@" | awk -F@ '{print $2}' | tr -d '[:space:]')" + if $STD npm list -g --depth=0 "$MODULE_NAME" 2>&1 | grep -q "$MODULE_NAME@"; then + MODULE_INSTALLED_VERSION="$($STD npm list -g --depth=0 "$MODULE_NAME" 2>&1 | grep "$MODULE_NAME@" | awk -F@ '{print $2}' | tr -d '[:space:]')" if [[ "$MODULE_REQ_VERSION" != "latest" && "$MODULE_REQ_VERSION" != "$MODULE_INSTALLED_VERSION" ]]; then msg_info "Updating $MODULE_NAME from v$MODULE_INSTALLED_VERSION to v$MODULE_REQ_VERSION" if ! $STD npm install -g "${MODULE_NAME}@${MODULE_REQ_VERSION}" 2>/dev/null; then From 27bb9e519236a3693bb0884023d4c4b9c178b79c Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 4 Nov 2025 17:10:28 +0100 Subject: [PATCH 1697/1733] Improve install log handling for containers Enhances the build and error handler scripts to better manage installation logs. On install failure, the log is now copied from the container to the host for easier debugging. The error handler now saves the log inside the container's /root directory for later retrieval, improving traceability and support. --- misc/build.func | 7 ++++++- misc/error_handler.func | 21 ++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/misc/build.func b/misc/build.func index a2270e845..29e18d9ef 100644 --- a/misc/build.func +++ b/misc/build.func @@ -2518,7 +2518,12 @@ EOF' # Run application installer if ! lxc-attach -n "$CTID" -- bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/install/${var_install}.sh)"; then - exit $? + local exit_code=$? + # Try to copy installation log from container before exiting + if [[ -n "$CTID" && -n "${SESSION_ID:-}" ]]; then + pct pull "$CTID" "/root/.install-${SESSION_ID}.log" "/tmp/install-${SESSION_ID}.log" 2>/dev/null || true + fi + exit $exit_code fi } diff --git a/misc/error_handler.func b/misc/error_handler.func index 2599d59b0..5aa38e5e1 100644 --- a/misc/error_handler.func +++ b/misc/error_handler.func @@ -119,22 +119,17 @@ error_handler() { tail -n 20 "$SILENT_LOGFILE" echo "-----------------------------------" - if [[ -n "${CTID:-}" ]]; then - local HOST_LOG="/tmp/install-$(date +%Y%m%d_%H%M%S)_${SESSION_ID:-error}.log" - if pct push $CTID "$SILENT_LOGFILE" "$HOST_LOG" 2>/dev/null; then - if declare -f msg_custom >/dev/null 2>&1; then - msg_custom "✓" "${GN}" "Full log saved to host: ${HOST_LOG}" - else - echo -e "${GN}✓ Full log saved to host:${CL} ${BL}${HOST_LOG}${CL}" - fi + # Copy log to container home for later retrieval (if running inside container via pct exec) + if [[ -d /root ]]; then + local container_log="/root/.install-${SESSION_ID:-error}.log" + cp "$SILENT_LOGFILE" "$container_log" 2>/dev/null || true + if declare -f msg_custom >/dev/null 2>&1; then + msg_custom "📋" "${YW}" "Log saved to: ${container_log}" else - if declare -f msg_custom >/dev/null 2>&1; then - msg_custom "📋" "${YW}" "Full log path in container: ${SILENT_LOGFILE}" - else - echo -e "${YW}Full log path in container:${CL} ${BL}${SILENT_LOGFILE}${CL}" - fi + echo -e "${YW}Log saved to:${CL} ${BL}${container_log}${CL}" fi else + # Running on host - show local path if declare -f msg_custom >/dev/null 2>&1; then msg_custom "📋" "${YW}" "Full log: ${SILENT_LOGFILE}" else From 96339e216adada1fde9a278db425e52d1d23dfd6 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 4 Nov 2025 17:52:53 +0100 Subject: [PATCH 1698/1733] fix typo --- misc/tools.func | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 6e687d527..99d1ee286 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -4263,8 +4263,7 @@ function setup_uv() { msg_info "Setup uv $LATEST_VERSION" fi - # Download - local UV_URL="https://github.com/astral-sh/uv/releases/download/v${LATEST_VERSION}/${UV_TAR}" + local UV_URL="https://github.com/astral-sh/uv/releases/download/${LATEST_VERSION}/${UV_TAR}" $STD curl -fsSL "$UV_URL" -o "$TMP_DIR/uv.tar.gz" || { msg_error "Failed to download uv from $UV_URL" From b429113018a70b2a41ddea630b017166b46e7145 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Tue, 4 Nov 2025 18:10:52 +0100 Subject: [PATCH 1699/1733] Update tools.func --- misc/tools.func | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 99d1ee286..11dedc84c 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -4276,8 +4276,14 @@ function setup_uv() { return 1 } - # Install uv binary - $STD install -m 755 "$TMP_DIR/uv/uv" "$UV_BIN" || { + # Find and install uv binary (tarball extracts to uv-VERSION-ARCH/ directory) + local UV_BINARY=$(find "$TMP_DIR" -name "uv" -type f -executable | head -n1) + if [[ ! -f "$UV_BINARY" ]]; then + msg_error "Could not find uv binary in extracted tarball" + return 1 + fi + + $STD install -m 755 "$UV_BINARY" "$UV_BIN" || { msg_error "Failed to install uv binary" return 1 } From c9a5b893dbf69abc24b192f3c0829d71ade2ecdf Mon Sep 17 00:00:00 2001 From: tremor021 Date: Tue, 4 Nov 2025 18:17:57 +0100 Subject: [PATCH 1700/1733] Infisical: increase resources --- ct/infisical.sh | 2 +- frontend/public/json/infisical.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/infisical.sh b/ct/infisical.sh index 9af6940af..389e69d79 100644 --- a/ct/infisical.sh +++ b/ct/infisical.sh @@ -9,7 +9,7 @@ APP="Infisical" var_tags="${var_tags:-auth}" var_cpu="${var_cpu:-2}" var_ram="${var_ram:-2048}" -var_disk="${var_disk:-4}" +var_disk="${var_disk:-6}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" diff --git a/frontend/public/json/infisical.json b/frontend/public/json/infisical.json index 777b22ea8..8bb58ba87 100644 --- a/frontend/public/json/infisical.json +++ b/frontend/public/json/infisical.json @@ -21,7 +21,7 @@ "resources": { "cpu": 2, "ram": 2048, - "hdd": 4, + "hdd": 6, "os": "Debian", "version": "13" } From eb7f0542f3e3a491395ec59103b67581bcbd5fbd Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 4 Nov 2025 17:18:16 +0000 Subject: [PATCH 1701/1733] Update .app files --- ct/headers/nginxproxymanager | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/nginxproxymanager diff --git a/ct/headers/nginxproxymanager b/ct/headers/nginxproxymanager new file mode 100644 index 000000000..d68d0c9d8 --- /dev/null +++ b/ct/headers/nginxproxymanager @@ -0,0 +1,6 @@ + _ __ _ ____ __ ___ + / | / /___ _(_)___ _ __ / __ \_________ _ ____ __ / |/ /___ _____ ____ _____ ____ _____ + / |/ / __ `/ / __ \| |/_/ / /_/ / ___/ __ \| |/_/ / / / / /|_/ / __ `/ __ \/ __ `/ __ `/ _ \/ ___/ + / /| / /_/ / / / / /> < / ____/ / / /_/ /> Date: Tue, 4 Nov 2025 19:33:15 +0100 Subject: [PATCH 1702/1733] Update frontend build script for OpenSSL and sass --- install/nginxproxymanager-install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install/nginxproxymanager-install.sh b/install/nginxproxymanager-install.sh index 734b7e60a..833437c3f 100644 --- a/install/nginxproxymanager-install.sh +++ b/install/nginxproxymanager-install.sh @@ -112,6 +112,7 @@ cp -r /opt/nginxproxymanager/global/* /app/global msg_ok "Set up Environment" msg_info "Building Frontend" +export NODE_OPTIONS=--openssl-legacy-provide cd /opt/nginxproxymanager/frontend # Replace node-sass with sass in package.json before installation sed -i 's/"node-sass".*$/"sass": "^1.92.1",/g' package.json From 084787634f2086561684bf346037163f4aceb542 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Tue, 4 Nov 2025 19:51:45 +0100 Subject: [PATCH 1703/1733] Update Node.js version from 22 to 21 --- install/nginxproxymanager-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/nginxproxymanager-install.sh b/install/nginxproxymanager-install.sh index 833437c3f..f437bc246 100644 --- a/install/nginxproxymanager-install.sh +++ b/install/nginxproxymanager-install.sh @@ -52,7 +52,7 @@ $STD apt update $STD apt -y install openresty msg_ok "Installed Openresty" -NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs +NODE_VERSION="21" NODE_MODULE="yarn" setup_nodejs RELEASE=$(curl -fsSL https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/releases/latest | grep "tag_name" | From 0869762826fb5d38257cc19cdcf7a9fe8b48d266 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Tue, 4 Nov 2025 20:04:30 +0100 Subject: [PATCH 1704/1733] Update Node.js version from 21 to 20 --- install/nginxproxymanager-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/nginxproxymanager-install.sh b/install/nginxproxymanager-install.sh index f437bc246..6c0833d15 100644 --- a/install/nginxproxymanager-install.sh +++ b/install/nginxproxymanager-install.sh @@ -52,7 +52,7 @@ $STD apt update $STD apt -y install openresty msg_ok "Installed Openresty" -NODE_VERSION="21" NODE_MODULE="yarn" setup_nodejs +NODE_VERSION="20" NODE_MODULE="yarn" setup_nodejs RELEASE=$(curl -fsSL https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/releases/latest | grep "tag_name" | From 8dacfff359a262408613f2cb2eac0a154fed66d8 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Tue, 4 Nov 2025 21:05:40 +0100 Subject: [PATCH 1705/1733] fix yarn --- install/nginxproxymanager-install.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/install/nginxproxymanager-install.sh b/install/nginxproxymanager-install.sh index 6c0833d15..d94916078 100644 --- a/install/nginxproxymanager-install.sh +++ b/install/nginxproxymanager-install.sh @@ -52,7 +52,7 @@ $STD apt update $STD apt -y install openresty msg_ok "Installed Openresty" -NODE_VERSION="20" NODE_MODULE="yarn" setup_nodejs +NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs RELEASE=$(curl -fsSL https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/releases/latest | grep "tag_name" | @@ -116,8 +116,8 @@ export NODE_OPTIONS=--openssl-legacy-provide cd /opt/nginxproxymanager/frontend # Replace node-sass with sass in package.json before installation sed -i 's/"node-sass".*$/"sass": "^1.92.1",/g' package.json -$STD yarn install --network-timeout 600000 -$STD yarn build +$STD node --openssl-legacy-provider $(which yarn) install --network-timeout 600000 +$STD node --openssl-legacy-provider $(which yarn) build cp -r /opt/nginxproxymanager/frontend/dist/* /app/frontend cp -r /opt/nginxproxymanager/frontend/app-images/* /app/frontend/images msg_ok "Built Frontend" From 279d6fd3fde57b146829949c4fa065c4a1b89e2c Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Tue, 4 Nov 2025 21:19:33 +0100 Subject: [PATCH 1706/1733] Refactor nginxproxymanager-install.sh script --- install/nginxproxymanager-install.sh | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/install/nginxproxymanager-install.sh b/install/nginxproxymanager-install.sh index d94916078..7ea76e30a 100644 --- a/install/nginxproxymanager-install.sh +++ b/install/nginxproxymanager-install.sh @@ -54,10 +54,6 @@ msg_ok "Installed Openresty" NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs -RELEASE=$(curl -fsSL https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/releases/latest | - grep "tag_name" | - awk '{print substr($2, 3, length($2)-4) }') - fetch_and_deploy_gh_release "nginxproxymanager" "NginxProxyManager/nginx-proxy-manager" msg_info "Setting up Environment" @@ -112,7 +108,7 @@ cp -r /opt/nginxproxymanager/global/* /app/global msg_ok "Set up Environment" msg_info "Building Frontend" -export NODE_OPTIONS=--openssl-legacy-provide +export NODE_OPTIONS=--openssl-legacy-provider cd /opt/nginxproxymanager/frontend # Replace node-sass with sass in package.json before installation sed -i 's/"node-sass".*$/"sass": "^1.92.1",/g' package.json From 24985771daeebd4086ac1ab45a044932bf0cf543 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Tue, 4 Nov 2025 21:26:03 +0100 Subject: [PATCH 1707/1733] Fetch latest Nginx Proxy Manager release version --- install/nginxproxymanager-install.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/install/nginxproxymanager-install.sh b/install/nginxproxymanager-install.sh index 7ea76e30a..57f1e82c4 100644 --- a/install/nginxproxymanager-install.sh +++ b/install/nginxproxymanager-install.sh @@ -54,6 +54,10 @@ msg_ok "Installed Openresty" NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs +RELEASE=$(curl -fsSL https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/releases/latest | + grep "tag_name" | + awk '{print substr($2, 3, length($2)-4) }') + fetch_and_deploy_gh_release "nginxproxymanager" "NginxProxyManager/nginx-proxy-manager" msg_info "Setting up Environment" From aad01da01d5455b8f51ddd3d5a5ec44f53936f0c Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Tue, 4 Nov 2025 21:39:23 +0100 Subject: [PATCH 1708/1733] revert yarn --- install/nginxproxymanager-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/nginxproxymanager-install.sh b/install/nginxproxymanager-install.sh index 57f1e82c4..a16de6af1 100644 --- a/install/nginxproxymanager-install.sh +++ b/install/nginxproxymanager-install.sh @@ -116,8 +116,8 @@ export NODE_OPTIONS=--openssl-legacy-provider cd /opt/nginxproxymanager/frontend # Replace node-sass with sass in package.json before installation sed -i 's/"node-sass".*$/"sass": "^1.92.1",/g' package.json -$STD node --openssl-legacy-provider $(which yarn) install --network-timeout 600000 -$STD node --openssl-legacy-provider $(which yarn) build +$STD yarn install --network-timeout 600000 +$STD yarn build cp -r /opt/nginxproxymanager/frontend/dist/* /app/frontend cp -r /opt/nginxproxymanager/frontend/app-images/* /app/frontend/images msg_ok "Built Frontend" From e4e20bc95965ca50667723ef5c4934ba223a890a Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 5 Nov 2025 08:44:52 +0100 Subject: [PATCH 1709/1733] remove global folder as removed upstream --- install/nginxproxymanager-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/install/nginxproxymanager-install.sh b/install/nginxproxymanager-install.sh index a16de6af1..d38e0ef06 100644 --- a/install/nginxproxymanager-install.sh +++ b/install/nginxproxymanager-install.sh @@ -108,7 +108,6 @@ fi mkdir -p /app/global /app/frontend/images cp -r /opt/nginxproxymanager/backend/* /app -cp -r /opt/nginxproxymanager/global/* /app/global msg_ok "Set up Environment" msg_info "Building Frontend" From 732eb55a5171d05fbf85a995b69997a972352902 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 5 Nov 2025 08:53:30 +0100 Subject: [PATCH 1710/1733] Update NODE_OPTIONS for frontend build --- install/nginxproxymanager-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/nginxproxymanager-install.sh b/install/nginxproxymanager-install.sh index d38e0ef06..d6a0779d8 100644 --- a/install/nginxproxymanager-install.sh +++ b/install/nginxproxymanager-install.sh @@ -111,7 +111,7 @@ cp -r /opt/nginxproxymanager/backend/* /app msg_ok "Set up Environment" msg_info "Building Frontend" -export NODE_OPTIONS=--openssl-legacy-provider +export NODE_OPTIONS="--max_old_space_size=1024 --openssl-legacy-provider" cd /opt/nginxproxymanager/frontend # Replace node-sass with sass in package.json before installation sed -i 's/"node-sass".*$/"sass": "^1.92.1",/g' package.json From b530b00ace9a79e23e9dcbec2b0b5a57a2f41de7 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 5 Nov 2025 09:01:26 +0100 Subject: [PATCH 1711/1733] Remove copying of app-images in install script --- install/nginxproxymanager-install.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/install/nginxproxymanager-install.sh b/install/nginxproxymanager-install.sh index d6a0779d8..710728e57 100644 --- a/install/nginxproxymanager-install.sh +++ b/install/nginxproxymanager-install.sh @@ -118,7 +118,6 @@ sed -i 's/"node-sass".*$/"sass": "^1.92.1",/g' package.json $STD yarn install --network-timeout 600000 $STD yarn build cp -r /opt/nginxproxymanager/frontend/dist/* /app/frontend -cp -r /opt/nginxproxymanager/frontend/app-images/* /app/frontend/images msg_ok "Built Frontend" msg_info "Initializing Backend" From d2b922b2fa70e18bcec66edcf33fbbaaa3614b52 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 5 Nov 2025 09:05:55 +0100 Subject: [PATCH 1712/1733] fix: images location --- install/nginxproxymanager-install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install/nginxproxymanager-install.sh b/install/nginxproxymanager-install.sh index 710728e57..afd56781b 100644 --- a/install/nginxproxymanager-install.sh +++ b/install/nginxproxymanager-install.sh @@ -118,6 +118,7 @@ sed -i 's/"node-sass".*$/"sass": "^1.92.1",/g' package.json $STD yarn install --network-timeout 600000 $STD yarn build cp -r /opt/nginxproxymanager/frontend/dist/* /app/frontend +cp -r /opt/nginxproxymanager/frontend/public/images/* /app/frontend/images msg_ok "Built Frontend" msg_info "Initializing Backend" From e96eb2b802a00299ae6ec8f61aac39aaa1ea02b1 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 5 Nov 2025 09:26:40 +0100 Subject: [PATCH 1713/1733] refactor --- ct/nginxproxymanager.sh | 102 ++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 56 deletions(-) diff --git a/ct/nginxproxymanager.sh b/ct/nginxproxymanager.sh index 1dbc9e91f..7ee575320 100644 --- a/ct/nginxproxymanager.sh +++ b/ct/nginxproxymanager.sh @@ -42,65 +42,47 @@ function update_script() { fi NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs - # export NODE_OPTIONS="--openssl-legacy-provider" RELEASE=$(curl -fsSL https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4) }') - ################ - RELEASE="2.13" - ################ - - - msg_info "Downloading NPM v${RELEASE}" - curl -fsSL "https://codeload.github.com/NginxProxyManager/nginx-proxy-manager/tar.gz/v${RELEASE}" | tar -xz - cd nginx-proxy-manager-"${RELEASE}" || exit - msg_ok "Downloaded NPM v${RELEASE}" - - msg_info "Building Frontend" - ( - sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" backend/package.json - sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" frontend/package.json - cd ./frontend || exit - # Replace node-sass with sass in package.json before installation - sed -i 's/"node-sass".*$/"sass": "^1.92.1",/g' package.json - $STD yarn install --network-timeout 600000 - $STD yarn build - ) - msg_ok "Built Frontend" + fetch_and_deploy_gh_release "nginxproxymanager" "NginxProxyManager/nginx-proxy-manager" msg_info "Stopping Services" systemctl stop openresty systemctl stop npm msg_ok "Stopped Services" - msg_info "Cleaning Old Files" - rm -rf /app \ + msg_info "Cleaning old files" + $STD rm -rf /app \ /var/www/html \ /etc/nginx \ /var/log/nginx \ /var/lib/nginx \ - "$STD" /var/cache/nginx - msg_ok "Cleaned Old Files" + /var/cache/nginx + msg_ok "Cleaned old files" msg_info "Setting up Environment" ln -sf /usr/bin/python3 /usr/bin/python - ln -sf /opt/certbot/bin/certbot /usr/local/bin/certbot ln -sf /usr/local/openresty/nginx/sbin/nginx /usr/sbin/nginx ln -sf /usr/local/openresty/nginx/ /etc/nginx - sed -i 's+^daemon+#daemon+g' docker/rootfs/etc/nginx/nginx.conf - NGINX_CONFS=$(find "$(pwd)" -type f -name "*.conf") + sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" /opt/nginxproxymanager/backend/package.json + sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" /opt/nginxproxymanager/frontend/package.json + sed -i 's+^daemon+#daemon+g' /opt/nginxproxymanager/docker/rootfs/etc/nginx/nginx.conf + NGINX_CONFS=$(find /opt/nginxproxymanager -type f -name "*.conf") for NGINX_CONF in $NGINX_CONFS; do sed -i 's+include conf.d+include /etc/nginx/conf.d+g' "$NGINX_CONF" done + mkdir -p /var/www/html /etc/nginx/logs - cp -r docker/rootfs/var/www/html/* /var/www/html/ - cp -r docker/rootfs/etc/nginx/* /etc/nginx/ - cp docker/rootfs/etc/letsencrypt.ini /etc/letsencrypt.ini - cp docker/rootfs/etc/logrotate.d/nginx-proxy-manager /etc/logrotate.d/nginx-proxy-manager + cp -r /opt/nginxproxymanager/docker/rootfs/var/www/html/* /var/www/html/ + cp -r /opt/nginxproxymanager/docker/rootfs/etc/nginx/* /etc/nginx/ + cp /opt/nginxproxymanager/docker/rootfs/etc/letsencrypt.ini /etc/letsencrypt.ini + cp /opt/nginxproxymanager/docker/rootfs/etc/logrotate.d/nginx-proxy-manager /etc/logrotate.d/nginx-proxy-manager ln -sf /etc/nginx/nginx.conf /etc/nginx/conf/nginx.conf rm -f /etc/nginx/conf.d/dev.conf + mkdir -p /tmp/nginx/body \ /run/nginx \ /data/nginx \ @@ -117,27 +99,33 @@ function update_script() { /var/lib/nginx/cache/public \ /var/lib/nginx/cache/private \ /var/cache/nginx/proxy_temp + chmod -R 777 /var/cache/nginx chown root /tmp/nginx - echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" {print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf);" >/etc/nginx/conf.d/include/resolvers.conf - if [ ! -f /data/nginx/dummycert.pem ] || [ ! -f /data/nginx/dummykey.pem ]; then - $STD openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 -subj "/O=Nginx Proxy Manager/OU=Dummy Certificate/CN=localhost" -keyout /data/nginx/dummykey.pem -out /data/nginx/dummycert.pem - fi - mkdir -p /app/global /app/frontend/images - cp -r frontend/dist/* /app/frontend - cp -r frontend/app-images/* /app/frontend/images - cp -r backend/* /app - cp -r global/* /app/global - # Update Certbot and plugins in virtual environment - if [ -d /opt/certbot ]; then - $STD /opt/certbot/bin/pip install --upgrade pip setuptools wheel - $STD /opt/certbot/bin/pip install --upgrade certbot certbot-dns-cloudflare + echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" {print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf);" >/etc/nginx/conf.d/include/resolvers.conf + + if [ ! -f /data/nginx/dummycert.pem ] || [ ! -f /data/nginx/dummykey.pem ]; then + openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 -subj "/O=Nginx Proxy Manager/OU=Dummy Certificate/CN=localhost" -keyout /data/nginx/dummykey.pem -out /data/nginx/dummycert.pem &>/dev/null fi - msg_ok "Setup Environment" + + mkdir -p /app/frontend/images + cp -r /opt/nginxproxymanager/backend/* /app + msg_ok "Set up Environment" + + msg_info "Building Frontend" + export NODE_OPTIONS="--max_old_space_size=1024 --openssl-legacy-provider" + cd /opt/nginxproxymanager/frontend + # Replace node-sass with sass in package.json before installation + sed -E -i 's/"node-sass" *: *"([^"]*)"/"sass": "\1"/g' package.json + $STD yarn install --network-timeout 600000 + $STD yarn build + cp -r /opt/nginxproxymanager/frontend/dist/* /app/frontend + cp -r /opt/nginxproxymanager/frontend/public/images/* /app/frontend/images + msg_ok "Built Frontend" msg_info "Initializing Backend" - $STD rm -rf /app/config/default.json + rm -rf /app/config/default.json if [ ! -f /app/config/production.json ]; then cat <<'EOF' >/app/config/production.json { @@ -153,23 +141,25 @@ function update_script() { } EOF fi - cd /app || exit - export NODE_OPTIONS="--openssl-legacy-provider" + cd /app $STD yarn install --network-timeout 600000 msg_ok "Initialized Backend" + + msg_info "Updating Certbot" + if [ -d /opt/certbot ]; then + $STD /opt/certbot/bin/pip install --upgrade pip setuptools wheel + $STD /opt/certbot/bin/pip install --upgrade certbot certbot-dns-cloudflare + fi + msg_ok "Updated Certbot" msg_info "Starting Services" sed -i 's/user npm/user root/g; s/^pid/#pid/g' /usr/local/openresty/nginx/conf/nginx.conf - sed -i 's/su npm npm/su root root/g' /etc/logrotate.d/nginx-proxy-manager - sed -i 's/include-system-site-packages = false/include-system-site-packages = true/g' /opt/certbot/pyvenv.cfg + sed -r -i 's/^([[:space:]]*)su npm npm/\1#su npm npm/g;' /etc/logrotate.d/nginx-proxy-manager systemctl enable -q --now openresty systemctl enable -q --now npm + systemctl restart openresty msg_ok "Started Services" - msg_info "Cleaning up" - rm -rf ~/nginx-proxy-manager-* - msg_ok "Cleaned" - msg_ok "Updated successfully!" exit } From 321162cbcb501d9cef282af319c0527350ef8a51 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 5 Nov 2025 09:28:31 +0100 Subject: [PATCH 1714/1733] update --- ct/nginxproxymanager.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/nginxproxymanager.sh b/ct/nginxproxymanager.sh index 7ee575320..d3d310fb1 100644 --- a/ct/nginxproxymanager.sh +++ b/ct/nginxproxymanager.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) +# Copyright (c) 2021-2025 Community-Script ORG +# Author: tteck (tteckster) | Co-Author: CrazyWolf13 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://nginxproxymanager.com/ From 734faf5bdaa175bf578a46c7b659d0f429e19865 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 5 Nov 2025 09:30:10 +0100 Subject: [PATCH 1715/1733] Install OpenResty and update Certbot --- ct/nginxproxymanager.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ct/nginxproxymanager.sh b/ct/nginxproxymanager.sh index d3d310fb1..616ac3add 100644 --- a/ct/nginxproxymanager.sh +++ b/ct/nginxproxymanager.sh @@ -146,6 +146,16 @@ EOF msg_ok "Initialized Backend" msg_info "Updating Certbot" + curl -fsSL "https://openresty.org/package/pubkey.gpg" | gpg --dearmor -o /etc/apt/trusted.gpg.d/openresty.gpg + cat <<'EOF' >/etc/apt/sources.list.d/openresty.sources +Types: deb +URIs: http://openresty.org/package/debian/ +Suites: bookworm +Components: openresty +Signed-By: /etc/apt/trusted.gpg.d/openresty.gpg +EOF + $STD apt update + $STD apt -y install openresty if [ -d /opt/certbot ]; then $STD /opt/certbot/bin/pip install --upgrade pip setuptools wheel $STD /opt/certbot/bin/pip install --upgrade certbot certbot-dns-cloudflare From 92325b5430d89e78d5d05b53547b00917ebdac9e Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 5 Nov 2025 09:31:04 +0100 Subject: [PATCH 1716/1733] fix sed --- install/nginxproxymanager-install.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/install/nginxproxymanager-install.sh b/install/nginxproxymanager-install.sh index afd56781b..5f046298a 100644 --- a/install/nginxproxymanager-install.sh +++ b/install/nginxproxymanager-install.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash -# Copyright (c) 2021-2025 tteck -# Author: tteck (tteckster) +# Copyright (c) 2021-2025 Community-Scripts ORG +# Author: tteck (tteckster) | Co-Author: CrazyWolf13 # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE # Source: https://nginxproxymanager.com/ @@ -106,7 +106,7 @@ if [ ! -f /data/nginx/dummycert.pem ] || [ ! -f /data/nginx/dummykey.pem ]; then openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 -subj "/O=Nginx Proxy Manager/OU=Dummy Certificate/CN=localhost" -keyout /data/nginx/dummykey.pem -out /data/nginx/dummycert.pem &>/dev/null fi -mkdir -p /app/global /app/frontend/images +mkdir -p /app/frontend/images cp -r /opt/nginxproxymanager/backend/* /app msg_ok "Set up Environment" @@ -114,7 +114,7 @@ msg_info "Building Frontend" export NODE_OPTIONS="--max_old_space_size=1024 --openssl-legacy-provider" cd /opt/nginxproxymanager/frontend # Replace node-sass with sass in package.json before installation -sed -i 's/"node-sass".*$/"sass": "^1.92.1",/g' package.json +sed -E -i 's/"node-sass" *: *"([^"]*)"/"sass": "\1"/g' package.json $STD yarn install --network-timeout 600000 $STD yarn build cp -r /opt/nginxproxymanager/frontend/dist/* /app/frontend From e7439ac1ef4c41ae0c4534d5a2b174018c3f338a Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 5 Nov 2025 09:33:01 +0100 Subject: [PATCH 1717/1733] update: ressource --- ct/nginxproxymanager.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ct/nginxproxymanager.sh b/ct/nginxproxymanager.sh index 616ac3add..e43ca1aee 100644 --- a/ct/nginxproxymanager.sh +++ b/ct/nginxproxymanager.sh @@ -8,8 +8,8 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV APP="Nginx Proxy Manager" var_tags="${var_tags:-proxy}" var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-1024}" -var_disk="${var_disk:-4}" +var_ram="${var_ram:-2048}" +var_disk="${var_disk:-8}" var_os="${var_os:-debian}" var_version="${var_version:-13}" var_unprivileged="${var_unprivileged:-1}" @@ -114,7 +114,7 @@ function update_script() { msg_ok "Set up Environment" msg_info "Building Frontend" - export NODE_OPTIONS="--max_old_space_size=1024 --openssl-legacy-provider" + export NODE_OPTIONS="--max_old_space_size=2048 --openssl-legacy-provider" cd /opt/nginxproxymanager/frontend # Replace node-sass with sass in package.json before installation sed -E -i 's/"node-sass" *: *"([^"]*)"/"sass": "\1"/g' package.json From 82bc8cef8df26ccc6385b5cca986766ae145aa53 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 5 Nov 2025 09:33:20 +0100 Subject: [PATCH 1718/1733] Increase NODE_OPTIONS max_old_space_size to 2048 --- install/nginxproxymanager-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/nginxproxymanager-install.sh b/install/nginxproxymanager-install.sh index 5f046298a..8626d20ca 100644 --- a/install/nginxproxymanager-install.sh +++ b/install/nginxproxymanager-install.sh @@ -111,7 +111,7 @@ cp -r /opt/nginxproxymanager/backend/* /app msg_ok "Set up Environment" msg_info "Building Frontend" -export NODE_OPTIONS="--max_old_space_size=1024 --openssl-legacy-provider" +export NODE_OPTIONS="--max_old_space_size=2048 --openssl-legacy-provider" cd /opt/nginxproxymanager/frontend # Replace node-sass with sass in package.json before installation sed -E -i 's/"node-sass" *: *"([^"]*)"/"sass": "\1"/g' package.json From 74fed3232303a2b52eb8f94e466604d5c18440f4 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 5 Nov 2025 09:38:42 +0100 Subject: [PATCH 1719/1733] fix: update --- ct/nginxproxymanager.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ct/nginxproxymanager.sh b/ct/nginxproxymanager.sh index e43ca1aee..38b9315b6 100644 --- a/ct/nginxproxymanager.sh +++ b/ct/nginxproxymanager.sh @@ -146,8 +146,10 @@ EOF msg_ok "Initialized Backend" msg_info "Updating Certbot" - curl -fsSL "https://openresty.org/package/pubkey.gpg" | gpg --dearmor -o /etc/apt/trusted.gpg.d/openresty.gpg - cat <<'EOF' >/etc/apt/sources.list.d/openresty.sources + [ -f /etc/apt/trusted.gpg.d/openresty-archive-keyring.gpg ] && rm -f /etc/apt/trusted.gpg.d/openresty-archive-keyring.gpg + [ -f /etc/apt/sources.list.d/openresty.list ] && rm -f /etc/apt/sources.list.d/openresty.list + [ ! -f /etc/apt/trusted.gpg.d/openresty.gpg ] && curl -fsSL https://openresty.org/package/pubkey.gpg | gpg --dearmor --yes -o /etc/apt/trusted.gpg.d/openresty.gpg + [ ! -f /etc/apt/sources.list.d/openresty.sources ] && cat <<'EOF' >/etc/apt/sources.list.d/openresty.sources Types: deb URIs: http://openresty.org/package/debian/ Suites: bookworm From 519119fe2fe745e3f449bce8d972b93bc280c0d4 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 5 Nov 2025 09:44:32 +0100 Subject: [PATCH 1720/1733] reorder --- install/nginxproxymanager-install.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/install/nginxproxymanager-install.sh b/install/nginxproxymanager-install.sh index 8626d20ca..cb12b5902 100644 --- a/install/nginxproxymanager-install.sh +++ b/install/nginxproxymanager-install.sh @@ -162,9 +162,6 @@ WantedBy=multi-user.target EOF msg_ok "Created Service" -motd_ssh -customize - msg_info "Starting Services" sed -i 's/user npm/user root/g; s/^pid/#pid/g' /usr/local/openresty/nginx/conf/nginx.conf sed -r -i 's/^([[:space:]]*)su npm npm/\1#su npm npm/g;' /etc/logrotate.d/nginx-proxy-manager @@ -172,6 +169,9 @@ systemctl enable -q --now openresty systemctl enable -q --now npm msg_ok "Started Services" +motd_ssh +customize + msg_info "Cleaning up" systemctl restart openresty $STD apt -y autoremove From b0610bd8a8dae10f242355689a5195de56013b9d Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 5 Nov 2025 10:34:21 +0100 Subject: [PATCH 1721/1733] Update tools.func --- misc/tools.func | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index 11dedc84c..e70f601e2 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -3286,6 +3286,9 @@ function setup_nodejs() { local NODE_VERSION="${NODE_VERSION:-22}" local NODE_MODULE="${NODE_MODULE:-}" + # ALWAYS clean up legacy installations first (nvm, etc.) to prevent conflicts + cleanup_legacy_install "nodejs" + # Get currently installed version local CURRENT_NODE_VERSION="" CURRENT_NODE_VERSION=$(is_tool_installed "nodejs" 2>/dev/null) || true @@ -3319,9 +3322,6 @@ function setup_nodejs() { msg_info "Setup Node.js $NODE_VERSION" fi - # Clean up legacy installations (nvm, etc.) - cleanup_legacy_install "nodejs" - # Remove ALL Debian nodejs packages BEFORE adding NodeSource repo if dpkg -l 2>/dev/null | grep -qE "^ii.*(nodejs|libnode|node-cjs|node-acorn|node-balanced|node-brace|node-minimatch|node-undici|node-xtend|node-corepack)"; then msg_info "Removing Debian-packaged Node.js and dependencies" From 2981adf62f3aa11bce4649f24f1977c03a199c02 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 5 Nov 2025 10:36:15 +0100 Subject: [PATCH 1722/1733] Update version in package.json files to $RELEASE --- install/nginxproxymanager-install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/nginxproxymanager-install.sh b/install/nginxproxymanager-install.sh index cb12b5902..0f18a74e6 100644 --- a/install/nginxproxymanager-install.sh +++ b/install/nginxproxymanager-install.sh @@ -64,8 +64,8 @@ msg_info "Setting up Environment" ln -sf /usr/bin/python3 /usr/bin/python ln -sf /usr/local/openresty/nginx/sbin/nginx /usr/sbin/nginx ln -sf /usr/local/openresty/nginx/ /etc/nginx -sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" /opt/nginxproxymanager/backend/package.json -sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" /opt/nginxproxymanager/frontend/package.json +sed -i "s|\"version\": \"2.0.0\"|\"version\": \"$RELEASE\"|" /opt/nginxproxymanager/backend/package.json +sed -i "s|\"version\": \"2.0.0\"|\"version\": \"$RELEASE\"|" /opt/nginxproxymanager/frontend/package.json sed -i 's+^daemon+#daemon+g' /opt/nginxproxymanager/docker/rootfs/etc/nginx/nginx.conf NGINX_CONFS=$(find /opt/nginxproxymanager -type f -name "*.conf") for NGINX_CONF in $NGINX_CONFS; do From ca28f570ffe9b6d7f68b3b897e05a717918d2898 Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 5 Nov 2025 10:36:30 +0100 Subject: [PATCH 1723/1733] Update version in package.json files to $RELEASE --- ct/nginxproxymanager.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ct/nginxproxymanager.sh b/ct/nginxproxymanager.sh index 38b9315b6..6eb627a4d 100644 --- a/ct/nginxproxymanager.sh +++ b/ct/nginxproxymanager.sh @@ -67,8 +67,8 @@ function update_script() { ln -sf /usr/bin/python3 /usr/bin/python ln -sf /usr/local/openresty/nginx/sbin/nginx /usr/sbin/nginx ln -sf /usr/local/openresty/nginx/ /etc/nginx - sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" /opt/nginxproxymanager/backend/package.json - sed -i "s|\"version\": \"0.0.0\"|\"version\": \"$RELEASE\"|" /opt/nginxproxymanager/frontend/package.json + sed -i "s|\"version\": \"2.0.0\"|\"version\": \"$RELEASE\"|" /opt/nginxproxymanager/backend/package.json + sed -i "s|\"version\": \"2.0.0\"|\"version\": \"$RELEASE\"|" /opt/nginxproxymanager/frontend/package.json sed -i 's+^daemon+#daemon+g' /opt/nginxproxymanager/docker/rootfs/etc/nginx/nginx.conf NGINX_CONFS=$(find /opt/nginxproxymanager -type f -name "*.conf") for NGINX_CONF in $NGINX_CONFS; do From dec609fb6a74b644ede3c1e913d808c0d0f3d4c5 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Wed, 5 Nov 2025 10:57:41 +0100 Subject: [PATCH 1724/1733] Add success messages to legacy cleanup steps Added explicit success messages after removing legacy installations for nvm, rbenv, rustup, and Go workspace in the cleanup_legacy_install function. Also updated ensure_apt_working to use the $STD variable for apt update commands for consistency. --- misc/tools.func | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index e70f601e2..de0b2d50d 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -118,6 +118,7 @@ cleanup_legacy_install() { msg_info "Removing legacy nvm installation" rm -rf "$HOME/.nvm" "$HOME/.npm" "$HOME/.bower" "$HOME/.config/yarn" 2>/dev/null || true sed -i '/NVM_DIR/d' "$HOME/.bashrc" "$HOME/.profile" 2>/dev/null || true + msg_ok "Legacy nvm installation removed" fi ;; ruby) @@ -125,6 +126,7 @@ cleanup_legacy_install() { msg_info "Removing legacy rbenv installation" rm -rf "$HOME/.rbenv" 2>/dev/null || true sed -i '/rbenv/d' "$HOME/.bashrc" "$HOME/.profile" 2>/dev/null || true + msg_ok "Legacy rbenv installation removed" fi ;; rust) @@ -132,6 +134,7 @@ cleanup_legacy_install() { msg_info "Removing legacy rustup installation" rm -rf "$HOME/.cargo" "$HOME/.rustup" 2>/dev/null || true sed -i '/cargo/d' "$HOME/.bashrc" "$HOME/.profile" 2>/dev/null || true + msg_ok "Legacy rustup installation removed" fi ;; go | golang) @@ -139,6 +142,7 @@ cleanup_legacy_install() { msg_info "Removing legacy Go workspace" # Keep user code, just remove GOPATH env sed -i '/GOPATH/d' "$HOME/.bashrc" "$HOME/.profile" 2>/dev/null || true + msg_ok "Legacy Go workspace cleaned" fi ;; esac @@ -1129,13 +1133,13 @@ ensure_apt_working() { cleanup_orphaned_sources # Try to update package lists - if ! apt update -qq 2>/dev/null; then + if ! $STD apt update; then # More aggressive cleanup rm -f /etc/apt/sources.list.d/*.sources 2>/dev/null || true cleanup_orphaned_sources # Try again - if ! apt update -qq 2>/dev/null; then + if ! $STD apt update; then msg_error "Cannot update package lists - APT is critically broken" return 1 fi From 2e07fb6f60abf0017feb2dff98f0cfbc867a762c Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 5 Nov 2025 12:49:47 +0100 Subject: [PATCH 1725/1733] Refactor: Open WebUI --- ct/openwebui.sh | 66 ++++++++++++++++++++++++ frontend/public/json/openwebui.json | 44 ++++++++++++++++ install/openwebui-install.sh | 78 +++++++++++++++++++++++++++++ 3 files changed, 188 insertions(+) create mode 100644 ct/openwebui.sh create mode 100644 frontend/public/json/openwebui.json create mode 100644 install/openwebui-install.sh diff --git a/ct/openwebui.sh b/ct/openwebui.sh new file mode 100644 index 000000000..ec1122dbe --- /dev/null +++ b/ct/openwebui.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +# Copyright (c) 2021-2025 tteck +# Author: tteck | Co-Author: havardthom | Co-Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://openwebui.com/ + +APP="Open WebUI" +var_tags="${var_tags:-ai;interface}" +var_cpu="${var_cpu:-4}" +var_ram="${var_ram:-8192}" +var_disk="${var_disk:-25}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /root/.open-webui ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + if [ -x "/usr/bin/ollama" ]; then + msg_info "Updating Ollama" + OLLAMA_VERSION=$(ollama -v | awk '{print $NF}') + RELEASE=$(curl -s https://api.github.com/repos/ollama/ollama/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4)}') + if [ "$OLLAMA_VERSION" != "$RELEASE" ]; then + msg_info "Stopping Service" + systemctl stop ollama + msg_ok "Stopped Service" + curl -fsSLO -C - https://ollama.com/download/ollama-linux-amd64.tgz + rm -rf /usr/lib/ollama + rm -rf /usr/bin/ollama + tar -C /usr -xzf ollama-linux-amd64.tgz + rm -rf ollama-linux-amd64.tgz + msg_info "Starting Service" + systemctl start ollama + msg_info "Started Service" + msg_ok "Ollama updated to version $RELEASE" + else + msg_ok "Ollama is already up to date." + fi + fi + + msg_info "Restarting Open WebUI to initiate update" + systemctl restart open-webui + msg_ok "Updated successfully!" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:8080${CL}" diff --git a/frontend/public/json/openwebui.json b/frontend/public/json/openwebui.json new file mode 100644 index 000000000..a7c5891fb --- /dev/null +++ b/frontend/public/json/openwebui.json @@ -0,0 +1,44 @@ +{ + "name": "Open WebUI", + "slug": "openwebui", + "categories": [ + 20 + ], + "date_created": "2024-10-24", + "type": "ct", + "updateable": true, + "privileged": false, + "interface_port": 8080, + "documentation": "https://docs.openwebui.com/", + "website": "https://openwebui.com/", + "logo": "https://cdn.jsdelivr.net/gh/selfhst/icons/webp/open-webui.webp", + "config_path": "/root/.env", + "description": "OpenWebUI is a self-hosted, web-based interface that allows you to run AI models entirely offline. It integrates with various LLM runners, such as OpenAI and Ollama, and supports features like markdown and LaTeX rendering, model management, and voice/video calls. It also offers multilingual support and the ability to generate images using APIs like DALL-E or ComfyUI", + "install_methods": [ + { + "type": "default", + "script": "ct/openwebui.sh", + "resources": { + "cpu": 4, + "ram": 8192, + "hdd": 25, + "os": "debian", + "version": "13" + } + } + ], + "default_credentials": { + "username": null, + "password": null + }, + "notes": [ + { + "text": "Script contains optional installation of Ollama.", + "type": "info" + }, + { + "text": "Initial run of the application/container can take some time, depending on your host speed, as the application is installed/updated at runtime. Please be patient!", + "type": "warning" + } + ] +} diff --git a/install/openwebui-install.sh b/install/openwebui-install.sh new file mode 100644 index 000000000..ad43ac7c3 --- /dev/null +++ b/install/openwebui-install.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 tteck +# Author: tteck | Co-Author: havardthom | Co-Author: Slaviša Arežina (tremor021) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://openwebui.com/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt install -y ffmpeg +msg_ok "Installed Dependencies" + +USE_UVX="YES" PYTHON_VERSION="3.12" setup_uv + +read -r -p "${TAB3}Would you like to add Ollama? " prompt +if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then + msg_info "Installing Ollama" + curl -fsSLO -C - https://ollama.com/download/ollama-linux-amd64.tgz + tar -C /usr -xzf ollama-linux-amd64.tgz + rm -rf ollama-linux-amd64.tgz + cat </etc/systemd/system/ollama.service +[Unit] +Description=Ollama Service +After=network-online.target + +[Service] +Type=exec +ExecStart=/usr/bin/ollama serve +Environment=HOME=$HOME +Environment=OLLAMA_HOST=0.0.0.0 +Restart=always +RestartSec=3 + +[Install] +WantedBy=multi-user.target +EOF + systemctl enable -q --now ollama + echo "ENABLE_OLLAMA_API=true" >/root/.env + msg_ok "Installed Ollama" +fi + +msg_info "Creating Service" +cat </etc/systemd/system/open-webui.service +[Unit] +Description=Open WebUI Service +After=network.target + +[Service] +Type=simple +EnvironmentFile=-/root/.env +Environment=DATA_DIR=/root/.open-webui +ExecStart=/usr/local/bin/uvx --python 3.12 open-webui@latest serve +WorkingDirectory=/root +Restart=on-failure +RestartSec=5 +User=root + +[Install] +WantedBy=multi-user.target +EOF +systemctl enable -q --now open-webui +msg_ok "Created Service" + +motd_ssh +customize + +msg_info "Cleaning up" +$STD apt -y autoremove +$STD apt -y autoclean +$STD apt -y clean +msg_ok "Cleaned" From e5febaf1d7a30f49db6d5cea8e49947f1cf6f472 Mon Sep 17 00:00:00 2001 From: tremor021 Date: Wed, 5 Nov 2025 13:28:51 +0100 Subject: [PATCH 1726/1733] Open WebUI: VE>VED --- ct/openwebui.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ct/openwebui.sh b/ct/openwebui.sh index ec1122dbe..33b315297 100644 --- a/ct/openwebui.sh +++ b/ct/openwebui.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func) +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) # Copyright (c) 2021-2025 tteck # Author: tteck | Co-Author: havardthom | Co-Author: Slaviša Arežina (tremor021) # License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE From c5b8d0e4b267e8e5e21d2d2d26985d6ddfaf304f Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 5 Nov 2025 15:39:08 +0100 Subject: [PATCH 1727/1733] Delete install/nginxproxymanager-install.sh --- install/nginxproxymanager-install.sh | 180 --------------------------- 1 file changed, 180 deletions(-) delete mode 100644 install/nginxproxymanager-install.sh diff --git a/install/nginxproxymanager-install.sh b/install/nginxproxymanager-install.sh deleted file mode 100644 index 0f18a74e6..000000000 --- a/install/nginxproxymanager-install.sh +++ /dev/null @@ -1,180 +0,0 @@ -#!/usr/bin/env bash - -# Copyright (c) 2021-2025 Community-Scripts ORG -# Author: tteck (tteckster) | Co-Author: CrazyWolf13 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://nginxproxymanager.com/ - -source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" -color -verb_ip6 -catch_errors -setting_up_container -network_check -update_os - -msg_info "Installing Dependencies" -$STD apt update -$STD apt -y install \ - ca-certificates \ - apache2-utils \ - logrotate \ - build-essential \ - git -msg_ok "Installed Dependencies" - -msg_info "Installing Python Dependencies" -$STD apt install -y \ - python3 \ - python3-dev \ - python3-pip \ - python3-venv \ - python3-cffi -msg_ok "Installed Python Dependencies" - -msg_info "Setting up Certbot" -$STD python3 -m venv /opt/certbot -$STD /opt/certbot/bin/pip install --upgrade pip setuptools wheel -$STD /opt/certbot/bin/pip install certbot certbot-dns-cloudflare -ln -sf /opt/certbot/bin/certbot /usr/local/bin/certbot -msg_ok "Set up Certbot" - -msg_info "Installing Openresty" -curl -fsSL "https://openresty.org/package/pubkey.gpg" | gpg --dearmor -o /etc/apt/trusted.gpg.d/openresty.gpg -cat <<'EOF' >/etc/apt/sources.list.d/openresty.sources -Types: deb -URIs: http://openresty.org/package/debian/ -Suites: bookworm -Components: openresty -Signed-By: /etc/apt/trusted.gpg.d/openresty.gpg -EOF -$STD apt update -$STD apt -y install openresty -msg_ok "Installed Openresty" - -NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs - -RELEASE=$(curl -fsSL https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/releases/latest | - grep "tag_name" | - awk '{print substr($2, 3, length($2)-4) }') - -fetch_and_deploy_gh_release "nginxproxymanager" "NginxProxyManager/nginx-proxy-manager" - -msg_info "Setting up Environment" -ln -sf /usr/bin/python3 /usr/bin/python -ln -sf /usr/local/openresty/nginx/sbin/nginx /usr/sbin/nginx -ln -sf /usr/local/openresty/nginx/ /etc/nginx -sed -i "s|\"version\": \"2.0.0\"|\"version\": \"$RELEASE\"|" /opt/nginxproxymanager/backend/package.json -sed -i "s|\"version\": \"2.0.0\"|\"version\": \"$RELEASE\"|" /opt/nginxproxymanager/frontend/package.json -sed -i 's+^daemon+#daemon+g' /opt/nginxproxymanager/docker/rootfs/etc/nginx/nginx.conf -NGINX_CONFS=$(find /opt/nginxproxymanager -type f -name "*.conf") -for NGINX_CONF in $NGINX_CONFS; do - sed -i 's+include conf.d+include /etc/nginx/conf.d+g' "$NGINX_CONF" -done - -mkdir -p /var/www/html /etc/nginx/logs -cp -r /opt/nginxproxymanager/docker/rootfs/var/www/html/* /var/www/html/ -cp -r /opt/nginxproxymanager/docker/rootfs/etc/nginx/* /etc/nginx/ -cp /opt/nginxproxymanager/docker/rootfs/etc/letsencrypt.ini /etc/letsencrypt.ini -cp /opt/nginxproxymanager/docker/rootfs/etc/logrotate.d/nginx-proxy-manager /etc/logrotate.d/nginx-proxy-manager -ln -sf /etc/nginx/nginx.conf /etc/nginx/conf/nginx.conf -rm -f /etc/nginx/conf.d/dev.conf - -mkdir -p /tmp/nginx/body \ - /run/nginx \ - /data/nginx \ - /data/custom_ssl \ - /data/logs \ - /data/access \ - /data/nginx/default_host \ - /data/nginx/default_www \ - /data/nginx/proxy_host \ - /data/nginx/redirection_host \ - /data/nginx/stream \ - /data/nginx/dead_host \ - /data/nginx/temp \ - /var/lib/nginx/cache/public \ - /var/lib/nginx/cache/private \ - /var/cache/nginx/proxy_temp - -chmod -R 777 /var/cache/nginx -chown root /tmp/nginx - -echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" {print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf);" >/etc/nginx/conf.d/include/resolvers.conf - -if [ ! -f /data/nginx/dummycert.pem ] || [ ! -f /data/nginx/dummykey.pem ]; then - openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 -subj "/O=Nginx Proxy Manager/OU=Dummy Certificate/CN=localhost" -keyout /data/nginx/dummykey.pem -out /data/nginx/dummycert.pem &>/dev/null -fi - -mkdir -p /app/frontend/images -cp -r /opt/nginxproxymanager/backend/* /app -msg_ok "Set up Environment" - -msg_info "Building Frontend" -export NODE_OPTIONS="--max_old_space_size=2048 --openssl-legacy-provider" -cd /opt/nginxproxymanager/frontend -# Replace node-sass with sass in package.json before installation -sed -E -i 's/"node-sass" *: *"([^"]*)"/"sass": "\1"/g' package.json -$STD yarn install --network-timeout 600000 -$STD yarn build -cp -r /opt/nginxproxymanager/frontend/dist/* /app/frontend -cp -r /opt/nginxproxymanager/frontend/public/images/* /app/frontend/images -msg_ok "Built Frontend" - -msg_info "Initializing Backend" -rm -rf /app/config/default.json -if [ ! -f /app/config/production.json ]; then - cat <<'EOF' >/app/config/production.json -{ - "database": { - "engine": "knex-native", - "knex": { - "client": "sqlite3", - "connection": { - "filename": "/data/database.sqlite" - } - } - } -} -EOF -fi -cd /app -$STD yarn install --network-timeout 600000 -msg_ok "Initialized Backend" - -msg_info "Creating Service" -cat <<'EOF' >/lib/systemd/system/npm.service -[Unit] -Description=Nginx Proxy Manager -After=network.target -Wants=openresty.service - -[Service] -Type=simple -Environment=NODE_ENV=production -ExecStartPre=-mkdir -p /tmp/nginx/body /data/letsencrypt-acme-challenge -ExecStart=/usr/bin/node index.js --abort_on_uncaught_exception --max_old_space_size=250 -WorkingDirectory=/app -Restart=on-failure - -[Install] -WantedBy=multi-user.target -EOF -msg_ok "Created Service" - -msg_info "Starting Services" -sed -i 's/user npm/user root/g; s/^pid/#pid/g' /usr/local/openresty/nginx/conf/nginx.conf -sed -r -i 's/^([[:space:]]*)su npm npm/\1#su npm npm/g;' /etc/logrotate.d/nginx-proxy-manager -systemctl enable -q --now openresty -systemctl enable -q --now npm -msg_ok "Started Services" - -motd_ssh -customize - -msg_info "Cleaning up" -systemctl restart openresty -$STD apt -y autoremove -$STD apt -y autoclean -$STD apt -y clean -msg_ok "Cleaned" From b03921eedfc96cef52f9918dc881974fd984f0aa Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 5 Nov 2025 15:39:20 +0100 Subject: [PATCH 1728/1733] Delete ct/nginxproxymanager.sh --- ct/nginxproxymanager.sh | 186 ---------------------------------------- 1 file changed, 186 deletions(-) delete mode 100644 ct/nginxproxymanager.sh diff --git a/ct/nginxproxymanager.sh b/ct/nginxproxymanager.sh deleted file mode 100644 index 6eb627a4d..000000000 --- a/ct/nginxproxymanager.sh +++ /dev/null @@ -1,186 +0,0 @@ -#!/usr/bin/env bash -source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) -# Copyright (c) 2021-2025 Community-Script ORG -# Author: tteck (tteckster) | Co-Author: CrazyWolf13 -# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE -# Source: https://nginxproxymanager.com/ - -APP="Nginx Proxy Manager" -var_tags="${var_tags:-proxy}" -var_cpu="${var_cpu:-2}" -var_ram="${var_ram:-2048}" -var_disk="${var_disk:-8}" -var_os="${var_os:-debian}" -var_version="${var_version:-13}" -var_unprivileged="${var_unprivileged:-1}" - -header_info "$APP" -variables -color -catch_errors - -function update_script() { - header_info - check_container_storage - check_container_resources - if [[ ! -f /lib/systemd/system/npm.service ]]; then - msg_error "No ${APP} Installation Found!" - exit - fi - - if command -v node &>/dev/null; then - CURRENT_NODE_VERSION=$(node --version | cut -d'v' -f2 | cut -d'.' -f1) - if [[ "$CURRENT_NODE_VERSION" != "22" ]]; then - systemctl stop openresty - apt-get purge -y nodejs npm - apt-get autoremove -y - rm -rf /usr/local/bin/node /usr/local/bin/npm - rm -rf /usr/local/lib/node_modules - rm -rf ~/.npm - rm -rf /root/.npm - fi - fi - - NODE_VERSION="22" NODE_MODULE="yarn" setup_nodejs - - RELEASE=$(curl -fsSL https://api.github.com/repos/NginxProxyManager/nginx-proxy-manager/releases/latest | - grep "tag_name" | - awk '{print substr($2, 3, length($2)-4) }') - - fetch_and_deploy_gh_release "nginxproxymanager" "NginxProxyManager/nginx-proxy-manager" - - msg_info "Stopping Services" - systemctl stop openresty - systemctl stop npm - msg_ok "Stopped Services" - - msg_info "Cleaning old files" - $STD rm -rf /app \ - /var/www/html \ - /etc/nginx \ - /var/log/nginx \ - /var/lib/nginx \ - /var/cache/nginx - msg_ok "Cleaned old files" - - msg_info "Setting up Environment" - ln -sf /usr/bin/python3 /usr/bin/python - ln -sf /usr/local/openresty/nginx/sbin/nginx /usr/sbin/nginx - ln -sf /usr/local/openresty/nginx/ /etc/nginx - sed -i "s|\"version\": \"2.0.0\"|\"version\": \"$RELEASE\"|" /opt/nginxproxymanager/backend/package.json - sed -i "s|\"version\": \"2.0.0\"|\"version\": \"$RELEASE\"|" /opt/nginxproxymanager/frontend/package.json - sed -i 's+^daemon+#daemon+g' /opt/nginxproxymanager/docker/rootfs/etc/nginx/nginx.conf - NGINX_CONFS=$(find /opt/nginxproxymanager -type f -name "*.conf") - for NGINX_CONF in $NGINX_CONFS; do - sed -i 's+include conf.d+include /etc/nginx/conf.d+g' "$NGINX_CONF" - done - - mkdir -p /var/www/html /etc/nginx/logs - cp -r /opt/nginxproxymanager/docker/rootfs/var/www/html/* /var/www/html/ - cp -r /opt/nginxproxymanager/docker/rootfs/etc/nginx/* /etc/nginx/ - cp /opt/nginxproxymanager/docker/rootfs/etc/letsencrypt.ini /etc/letsencrypt.ini - cp /opt/nginxproxymanager/docker/rootfs/etc/logrotate.d/nginx-proxy-manager /etc/logrotate.d/nginx-proxy-manager - ln -sf /etc/nginx/nginx.conf /etc/nginx/conf/nginx.conf - rm -f /etc/nginx/conf.d/dev.conf - - mkdir -p /tmp/nginx/body \ - /run/nginx \ - /data/nginx \ - /data/custom_ssl \ - /data/logs \ - /data/access \ - /data/nginx/default_host \ - /data/nginx/default_www \ - /data/nginx/proxy_host \ - /data/nginx/redirection_host \ - /data/nginx/stream \ - /data/nginx/dead_host \ - /data/nginx/temp \ - /var/lib/nginx/cache/public \ - /var/lib/nginx/cache/private \ - /var/cache/nginx/proxy_temp - - chmod -R 777 /var/cache/nginx - chown root /tmp/nginx - - echo resolver "$(awk 'BEGIN{ORS=" "} $1=="nameserver" {print ($2 ~ ":")? "["$2"]": $2}' /etc/resolv.conf);" >/etc/nginx/conf.d/include/resolvers.conf - - if [ ! -f /data/nginx/dummycert.pem ] || [ ! -f /data/nginx/dummykey.pem ]; then - openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 -subj "/O=Nginx Proxy Manager/OU=Dummy Certificate/CN=localhost" -keyout /data/nginx/dummykey.pem -out /data/nginx/dummycert.pem &>/dev/null - fi - - mkdir -p /app/frontend/images - cp -r /opt/nginxproxymanager/backend/* /app - msg_ok "Set up Environment" - - msg_info "Building Frontend" - export NODE_OPTIONS="--max_old_space_size=2048 --openssl-legacy-provider" - cd /opt/nginxproxymanager/frontend - # Replace node-sass with sass in package.json before installation - sed -E -i 's/"node-sass" *: *"([^"]*)"/"sass": "\1"/g' package.json - $STD yarn install --network-timeout 600000 - $STD yarn build - cp -r /opt/nginxproxymanager/frontend/dist/* /app/frontend - cp -r /opt/nginxproxymanager/frontend/public/images/* /app/frontend/images - msg_ok "Built Frontend" - - msg_info "Initializing Backend" - rm -rf /app/config/default.json - if [ ! -f /app/config/production.json ]; then - cat <<'EOF' >/app/config/production.json -{ - "database": { - "engine": "knex-native", - "knex": { - "client": "sqlite3", - "connection": { - "filename": "/data/database.sqlite" - } - } - } -} -EOF - fi - cd /app - $STD yarn install --network-timeout 600000 - msg_ok "Initialized Backend" - - msg_info "Updating Certbot" - [ -f /etc/apt/trusted.gpg.d/openresty-archive-keyring.gpg ] && rm -f /etc/apt/trusted.gpg.d/openresty-archive-keyring.gpg - [ -f /etc/apt/sources.list.d/openresty.list ] && rm -f /etc/apt/sources.list.d/openresty.list - [ ! -f /etc/apt/trusted.gpg.d/openresty.gpg ] && curl -fsSL https://openresty.org/package/pubkey.gpg | gpg --dearmor --yes -o /etc/apt/trusted.gpg.d/openresty.gpg - [ ! -f /etc/apt/sources.list.d/openresty.sources ] && cat <<'EOF' >/etc/apt/sources.list.d/openresty.sources -Types: deb -URIs: http://openresty.org/package/debian/ -Suites: bookworm -Components: openresty -Signed-By: /etc/apt/trusted.gpg.d/openresty.gpg -EOF - $STD apt update - $STD apt -y install openresty - if [ -d /opt/certbot ]; then - $STD /opt/certbot/bin/pip install --upgrade pip setuptools wheel - $STD /opt/certbot/bin/pip install --upgrade certbot certbot-dns-cloudflare - fi - msg_ok "Updated Certbot" - - msg_info "Starting Services" - sed -i 's/user npm/user root/g; s/^pid/#pid/g' /usr/local/openresty/nginx/conf/nginx.conf - sed -r -i 's/^([[:space:]]*)su npm npm/\1#su npm npm/g;' /etc/logrotate.d/nginx-proxy-manager - systemctl enable -q --now openresty - systemctl enable -q --now npm - systemctl restart openresty - msg_ok "Started Services" - - msg_ok "Updated successfully!" - exit -} - -start -build_container -description - -msg_ok "Completed Successfully!\n" -echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" -echo -e "${INFO}${YW} Access it using the following URL:${CL}" -echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:81${CL}" From e351a46391621aeef3698f234d5fc8966d69841b Mon Sep 17 00:00:00 2001 From: Tobias <96661824+CrazyWolf13@users.noreply.github.com> Date: Wed, 5 Nov 2025 15:39:38 +0100 Subject: [PATCH 1729/1733] Delete ct/headers/nginxproxymanager --- ct/headers/nginxproxymanager | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 ct/headers/nginxproxymanager diff --git a/ct/headers/nginxproxymanager b/ct/headers/nginxproxymanager deleted file mode 100644 index d68d0c9d8..000000000 --- a/ct/headers/nginxproxymanager +++ /dev/null @@ -1,6 +0,0 @@ - _ __ _ ____ __ ___ - / | / /___ _(_)___ _ __ / __ \_________ _ ____ __ / |/ /___ _____ ____ _____ ____ _____ - / |/ / __ `/ / __ \| |/_/ / /_/ / ___/ __ \| |/_/ / / / / /|_/ / __ `/ __ \/ __ `/ __ `/ _ \/ ___/ - / /| / /_/ / / / / /> < / ____/ / / /_/ /> Date: Wed, 5 Nov 2025 14:40:22 +0000 Subject: [PATCH 1730/1733] Update .app files --- ct/headers/openwebui | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ct/headers/openwebui diff --git a/ct/headers/openwebui b/ct/headers/openwebui new file mode 100644 index 000000000..0097a279b --- /dev/null +++ b/ct/headers/openwebui @@ -0,0 +1,6 @@ + ____ _ __ __ __ ______ + / __ \____ ___ ____ | | / /__ / /_ / / / / _/ + / / / / __ \/ _ \/ __ \ | | /| / / _ \/ __ \/ / / // / +/ /_/ / /_/ / __/ / / / | |/ |/ / __/ /_/ / /_/ // / +\____/ .___/\___/_/ /_/ |__/|__/\___/_.___/\____/___/ + /_/ From 9d999d8cacec52fb3c53ed8e5cf309bf58d6d36e Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 6 Nov 2025 09:02:17 +0100 Subject: [PATCH 1731/1733] Add Omada Controller install and container scripts Introduces ct/omada.sh for Proxmox container setup and install/omada-install.sh for Omada Controller installation. Scripts handle dependency installation, MongoDB and Java setup, and automate Omada Controller deployment. --- ct/omada.sh | 73 ++++++++++++++++++++++++++++++++++++++++ install/omada-install.sh | 63 ++++++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 ct/omada.sh create mode 100644 install/omada-install.sh diff --git a/ct/omada.sh b/ct/omada.sh new file mode 100644 index 000000000..576797b71 --- /dev/null +++ b/ct/omada.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash +source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVED/main/misc/build.func) +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://www.tp-link.com/us/support/download/omada-software-controller/ + +APP="Omada" +var_tags="${var_tags:-tp-link;controller}" +var_cpu="${var_cpu:-2}" +var_ram="${var_ram:-3072}" +var_disk="${var_disk:-8}" +var_os="${var_os:-debian}" +var_version="${var_version:-13}" +var_unprivileged="${var_unprivileged:-1}" + +header_info "$APP" +variables +color +catch_errors + +function update_script() { + header_info + check_container_storage + check_container_resources + if [[ ! -d /opt/tplink ]]; then + msg_error "No ${APP} Installation Found!" + exit + fi + + msg_info "Updating MongoDB" + if lscpu | grep -q 'avx'; then + MONGO_VERSION="8.0" setup_mongodb + else + msg_warn "No AVX detected: Using older MongoDB 4.4" + MONGO_VERSION="4.4" setup_mongodb + fi + + msg_info "Checking if right Azul Zulu Java is installed" + java_version=$(java -version 2>&1 | awk -F[\"_] '/version/ {print $2}') + if [[ "$java_version" =~ ^1\.8\.* ]]; then + $STD apt remove --purge -y zulu8-jdk + $STD apt -y install zulu21-jre-headless + msg_ok "Updated Azul Zulu Java to 21" + else + msg_ok "Azul Zulu Java 21 already installed" + fi + + msg_info "Updating Omada Controller" + OMADA_URL=$(curl -fsSL "https://support.omadanetworks.com/en/download/software/omada-controller/" | + grep -o 'https://static\.tp-link\.com/upload/software/[^"]*linux_x64[^"]*\.deb' | + head -n1) + OMADA_PKG=$(basename "$OMADA_URL") + if [ -z "$OMADA_PKG" ]; then + msg_error "Could not retrieve Omada package – server may be down." + exit + fi + curl -fsSL "$OMADA_URL" -o "$OMADA_PKG" + export DEBIAN_FRONTEND=noninteractive + $STD dpkg -i "$OMADA_PKG" + rm -f "$OMADA_PKG" + msg_ok "Updated successfully!" + exit +} + +start +build_container +description + +msg_ok "Completed Successfully!\n" +echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}" +echo -e "${INFO}${YW} Access it using the following URL:${CL}" +echo -e "${TAB}${GATEWAY}${BGN}https://${IP}:8043${CL}" diff --git a/install/omada-install.sh b/install/omada-install.sh new file mode 100644 index 000000000..305c9fc9e --- /dev/null +++ b/install/omada-install.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash + +# Copyright (c) 2021-2025 tteck +# Author: tteck (tteckster) +# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE +# Source: https://www.tp-link.com/us/support/download/omada-software-controller/ + +source /dev/stdin <<<"$FUNCTIONS_FILE_PATH" +color +verb_ip6 +catch_errors +setting_up_container +network_check +update_os + +msg_info "Installing Dependencies" +$STD apt install -y jsvc +msg_ok "Installed Dependencies" + +msg_info "Checking CPU Features" +if lscpu | grep -q 'avx'; then + MONGODB_VERSION="8.0" + msg_ok "AVX detected: Using MongoDB 8.0" + MONGO_VERSION="8.0" setup_mongodb +else + MONGO_VERSION="4.4" setup_mongodb +fi + +msg_info "Installing Azul Zulu Java" +curl -fsSL "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0xB1998361219BD9C9" -o "/etc/apt/trusted.gpg.d/zulu-repo.asc" +curl -fsSL "https://cdn.azul.com/zulu/bin/zulu-repo_1.0.0-3_all.deb" -o zulu-repo.deb +$STD dpkg -i zulu-repo.deb +$STD apt update +$STD apt -y install zulu21-jre-headless +msg_ok "Installed Azul Zulu Java" + + +if ! dpkg -l | grep -q 'libssl1.1'; then + msg_info "Installing libssl (if needed)" + curl -fsSL "https://security.debian.org/debian-security/pool/updates/main/o/openssl/libssl1.1_1.1.1w-0+deb11u4_amd64.deb" -o "/tmp/libssl.deb" + $STD dpkg -i /tmp/libssl.deb + rm -f /tmp/libssl.deb + msg_ok "Installed libssl1.1" +fi + +msg_info "Installing Omada Controller" +OMADA_URL=$(curl -fsSL "https://support.omadanetworks.com/en/download/software/omada-controller/" | + grep -o 'https://static\.tp-link\.com/upload/software/[^"]*linux_x64[^"]*\.deb' | + head -n1) +OMADA_PKG=$(basename "$OMADA_URL") +curl -fsSL "$OMADA_URL" -o "$OMADA_PKG" +$STD dpkg -i "$OMADA_PKG" +msg_ok "Installed Omada Controller" + +motd_ssh +customize + +msg_info "Cleaning up" +rm -rf "$OMADA_PKG" zulu-repo.deb +$STD apt -y autoremove +$STD apt -y autoclean +$STD apt -y clean +msg_ok "Cleaned" From 5552a2beb4e73b085af32ac6a37d8c293a3372fc Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 6 Nov 2025 09:29:37 +0100 Subject: [PATCH 1732/1733] Refactor Omada install and improve repo suite mapping Refactored omada-install.sh to use setup_java and setup_mongodb functions, simplifying Java and MongoDB installation logic. Improved manage_tool_repository in tools.func to provide explicit suite mapping and fallbacks for newer or unknown Debian and Ubuntu releases, enhancing compatibility with future distributions. Minor comment translations and cleanups included. --- install/omada-install.sh | 28 ++++++++-------------- misc/tools.func | 50 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 56 insertions(+), 22 deletions(-) diff --git a/install/omada-install.sh b/install/omada-install.sh index 305c9fc9e..7ffc14fb7 100644 --- a/install/omada-install.sh +++ b/install/omada-install.sh @@ -17,31 +17,21 @@ msg_info "Installing Dependencies" $STD apt install -y jsvc msg_ok "Installed Dependencies" -msg_info "Checking CPU Features" if lscpu | grep -q 'avx'; then - MONGODB_VERSION="8.0" - msg_ok "AVX detected: Using MongoDB 8.0" MONGO_VERSION="8.0" setup_mongodb else MONGO_VERSION="4.4" setup_mongodb fi -msg_info "Installing Azul Zulu Java" -curl -fsSL "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0xB1998361219BD9C9" -o "/etc/apt/trusted.gpg.d/zulu-repo.asc" -curl -fsSL "https://cdn.azul.com/zulu/bin/zulu-repo_1.0.0-3_all.deb" -o zulu-repo.deb -$STD dpkg -i zulu-repo.deb -$STD apt update -$STD apt -y install zulu21-jre-headless -msg_ok "Installed Azul Zulu Java" +JAVA_VERSION="21" setup_java - -if ! dpkg -l | grep -q 'libssl1.1'; then - msg_info "Installing libssl (if needed)" - curl -fsSL "https://security.debian.org/debian-security/pool/updates/main/o/openssl/libssl1.1_1.1.1w-0+deb11u4_amd64.deb" -o "/tmp/libssl.deb" - $STD dpkg -i /tmp/libssl.deb - rm -f /tmp/libssl.deb - msg_ok "Installed libssl1.1" -fi +# if ! dpkg -l | grep -q 'libssl1.1'; then +# msg_info "Installing libssl (if needed)" +# curl -fsSL "https://security.debian.org/debian-security/pool/updates/main/o/openssl/libssl1.1_1.1.1w-0+deb11u4_amd64.deb" -o "/tmp/libssl.deb" +# $STD dpkg -i /tmp/libssl.deb +# rm -f /tmp/libssl.deb +# msg_ok "Installed libssl1.1" +# fi msg_info "Installing Omada Controller" OMADA_URL=$(curl -fsSL "https://support.omadanetworks.com/en/download/software/omada-controller/" | @@ -56,7 +46,7 @@ motd_ssh customize msg_info "Cleaning up" -rm -rf "$OMADA_PKG" zulu-repo.deb +rm -rf "$OMADA_PKG" $STD apt -y autoremove $STD apt -y autoclean $STD apt -y clean diff --git a/misc/tools.func b/misc/tools.func index de0b2d50d..c67e4de58 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -450,7 +450,51 @@ manage_tool_repository() { # Setup repository local distro_codename distro_codename=$(awk -F= '/^VERSION_CODENAME=/{print $2}' /etc/os-release) - suite=$(get_fallback_suite "$distro_id" "$distro_codename" "$repo_url") + + # Suite mapping with fallback for newer releases not yet supported by upstream + if [[ "$distro_id" == "debian" ]]; then + case "$distro_codename" in + trixie | forky | sid) + # Testing/unstable releases fallback to latest stable suite + suite="bookworm" + ;; + bookworm) + suite="bookworm" + ;; + bullseye) + suite="bullseye" + ;; + *) + # Unknown release: fallback to latest stable suite + msg_warn "Unknown Debian release '${distro_codename}', using bookworm" + suite="bookworm" + ;; + esac + elif [[ "$distro_id" == "ubuntu" ]]; then + case "$distro_codename" in + oracular | plucky) + # Newer releases fallback to latest LTS + suite="noble" + ;; + noble) + suite="noble" + ;; + jammy) + suite="jammy" + ;; + focal) + suite="focal" + ;; + *) + # Unknown release: fallback to latest LTS + msg_warn "Unknown Ubuntu release '${distro_codename}', using noble" + suite="noble" + ;; + esac + else + # For other distros, try generic fallback + suite=$(get_fallback_suite "$distro_id" "$distro_codename" "$repo_url") + fi repo_component="main" [[ "$distro_id" == "ubuntu" ]] && repo_component="multiverse" @@ -4188,12 +4232,12 @@ function setup_uv() { local TMP_DIR=$(mktemp -d) local CACHED_VERSION - # Trap für TMP Cleanup + # trap for TMP Cleanup trap "rm -rf '$TMP_DIR'" EXIT CACHED_VERSION=$(get_cached_version "uv") - # Architektur-Detection + # Architecture Detection local ARCH=$(uname -m) local OS_TYPE="" local UV_TAR="" From e6b42a2b86161c2afe04c9428864e526c599efb1 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 6 Nov 2025 10:07:10 +0100 Subject: [PATCH 1733/1733] Update tools.func --- misc/tools.func | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/misc/tools.func b/misc/tools.func index c67e4de58..b55f1de1b 100644 --- a/misc/tools.func +++ b/misc/tools.func @@ -2791,8 +2791,10 @@ function setup_java() { fi # Validate INSTALLED_VERSION is not empty if matched - local JDK_COUNT=$(dpkg -l 2>/dev/null | grep -c "temurin-.*-jdk" || echo "0") - if [[ -z "$INSTALLED_VERSION" && "$JDK_COUNT" -gt 0 ]]; then + local JDK_COUNT + JDK_COUNT=$(dpkg -l 2>/dev/null | grep -c "temurin-.*-jdk" || echo "0") + JDK_COUNT=${JDK_COUNT//[^0-9]/} # Remove any non-numeric characters + if [[ -z "$INSTALLED_VERSION" && "${JDK_COUNT:-0}" -gt 0 ]]; then msg_warn "Found Temurin JDK but cannot determine version" INSTALLED_VERSION="0" fi